Added new source/destination, and resource specification in the .isa files. This specification allows you to specify an operand (resource) as [name]. This translates into an enum entry of k_Name_. This operand/resource specification is used in cases where it may genereate multiple operands or resourse references. For instance, in RiscV push/pop instructions a field 'rlist' is used to specify a value in the range [4..15] that represents different length lists of registers. Instead of having to specify the instruction explicitly for each possible value of 'rlist', the [rlist] specification will allow new GetResource/GetSource/GetDestination calls in the encoding interface to return multiple source/destination/resource operands during the decoding of the instruction. PiperOrigin-RevId: 692247798 Change-Id: Ie6a08d2df349046a1ccd98c4acf8025a968451d0
diff --git a/mpact/sim/decoder/InstructionSet.g4 b/mpact/sim/decoder/InstructionSet.g4 index 431863c..f26e41e 100644 --- a/mpact/sim/decoder/InstructionSet.g4 +++ b/mpact/sim/decoder/InstructionSet.g4
@@ -312,7 +312,16 @@ ; opcode_operands - : pred=IDENT? (':' source=ident_list? ( ':' dest_list? )? )? + : pred=IDENT? (':' source=source_list? ( ':' dest_list? )? )? + ; + +source_list + : source_operand (',' source_operand)* + ; + +source_operand + : source=IDENT + | '[' array_source=IDENT ']' ; // Destination operands may include a latency. @@ -322,7 +331,8 @@ ; dest_operand - : dest=IDENT ( '(' (expression | wildcard='*' ) ')' )? + : (dest=IDENT | '[' array_dest=IDENT ']') + ( '(' (expression | wildcard='*' ) ')' )? ; // Special rules for generator instruction descriptions. @@ -441,7 +451,7 @@ // x[2]: x is acquired starting at cycle 2 through the instruction latency. resource_item - : name=IDENT + : ( name=IDENT | '[' array_name=IDENT ']' ) ('[' (begin_cycle=expression)? ('..' end_cycle=expression? )? ']')? ; @@ -499,7 +509,7 @@ OPCODES : 'opcodes'; OVERRIDE : 'override'; NAMESPACE : 'namespace'; -RESOURCES: 'resources'; +RESOURCES : 'resources'; SEMFUNC : 'semfunc'; SLOT : 'slot'; SLOTS : 'slots';
diff --git a/mpact/sim/decoder/instruction.cc b/mpact/sim/decoder/instruction.cc index c9a7309..6232b11 100644 --- a/mpact/sim/decoder/instruction.cc +++ b/mpact/sim/decoder/instruction.cc
@@ -185,8 +185,8 @@ } end_expr = result.value(); } - auto *new_ref = - new ResourceReference(ref->resource, ref->dest_op, begin_expr, end_expr); + auto *new_ref = new ResourceReference(ref->resource, ref->is_array, + ref->dest_op, begin_expr, end_expr); return new_ref; }
diff --git a/mpact/sim/decoder/instruction_set.cc b/mpact/sim/decoder/instruction_set.cc index f3bb740..f2fe00c 100644 --- a/mpact/sim/decoder/instruction_set.cc +++ b/mpact/sim/decoder/instruction_set.cc
@@ -169,12 +169,24 @@ // OpcodeEnum opcode, // SourceOpEnum source, // int source_no) = 0; +// virtual std::vector<SourceOperandInterface *> GetSources( +// SlotEnum slot, +// int entry, +// OpcodeEnum opcode, +// ListSourceOpEnum list_source_op, +// int source_no) = 0; // virtual DestinationOperandInterface *GetDestination(int latency, // SlotEnum slot, // int entry, // OpcodeEnum opcode, // DestOpEnum dest, // int dest_no) = 0; +// virtual std::vector<DestinationOperandInterface *> GetDestinations( +// SlotEnum slot, +// int entry, +// OpcodeEnum opcode, +// ListDestOpEnum dest_op, +// int dest_no) = 0; // virtual int GetLatency(SlotEnum slot, int entry, OpcodeEnum opcode, // DestOpEnum dest) = 0; // virtual ResourceOperandInterface *GetSimpleResourceOperand( @@ -438,7 +450,9 @@ // Btree sets to sort by name. absl::btree_set<std::string> predicate_operands; absl::btree_set<std::string> source_operands; + absl::btree_set<std::string> list_source_operands; absl::btree_set<std::string> dest_operands; + absl::btree_set<std::string> list_dest_operands; absl::btree_set<std::string> dest_latency; // Insert PascalCase operand names into the sets to select unique names. for (auto const *slot : slot_order_) { @@ -450,11 +464,19 @@ if (!opcode->predicate_op_name().empty()) { predicate_operands.insert(ToPascalCase(opcode->predicate_op_name())); } - for (auto const &source_op_name : opcode->source_op_name_vec()) { - source_operands.insert(ToPascalCase(source_op_name)); + for (auto const &source_op : opcode->source_op_vec()) { + if (source_op.is_array) { + list_source_operands.insert(ToPascalCase(source_op.name)); + } else { + source_operands.insert(ToPascalCase(source_op.name)); + } } for (auto const *dest_op : opcode->dest_op_vec()) { - dest_operands.insert(dest_op->pascal_case_name()); + if (dest_op->is_array()) { + list_dest_operands.insert(dest_op->pascal_case_name()); + } else { + dest_operands.insert(dest_op->pascal_case_name()); + } if (dest_op->expression() == nullptr) { dest_latency.insert(dest_op->pascal_case_name()); } @@ -483,6 +505,17 @@ absl::StrAppend(&h_output, " kPastMaxValue = ", src_count, ",\n" " };\n\n"); + // Create enum for list source operands. + absl::StrAppend(&h_output, " enum class ListSourceOpEnum {\n"); + int list_src_count = 0; + absl::StrAppend(&h_output, " kNone = ", list_src_count++, ",\n"); + for (auto const &source_name : list_source_operands) { + absl::StrAppend(&h_output, " k", source_name, " = ", list_src_count++, + ",\n"); + } + absl::StrAppend(&h_output, " kPastMaxValue = ", list_src_count, + ",\n" + " };\n\n"); // Create enum for destination operands. absl::StrAppend(&h_output, " enum class DestOpEnum {\n"); int dst_count = 0; @@ -493,7 +526,17 @@ absl::StrAppend(&h_output, " kPastMaxValue = ", dst_count, ",\n" " };\n\n"); - + // Create enum for list destination operands. + absl::StrAppend(&h_output, " enum class ListDestOpEnum {\n"); + int list_dst_count = 0; + absl::StrAppend(&h_output, " kNone = ", list_dst_count++, ",\n"); + for (auto const &dest_name : list_dest_operands) { + absl::StrAppend(&h_output, " k", dest_name, " = ", list_dst_count++, + ",\n"); + } + absl::StrAppend(&h_output, " kPastMaxValue = ", list_dst_count, + ",\n" + " };\n\n"); // Emit opcode enumeration type. absl::StrAppend(&h_output, " enum class OpcodeEnum {\n" @@ -540,14 +583,14 @@ } absl::StrAppend(&h_output, " kPastMaxValue = ", resource_count, "\n };\n\n"); - + // Complex resource enumeration type. absl::StrAppend(&h_output, " enum class ComplexResourceEnum {\n" " kNone = 0,\n"); resource_count = 1; name_set.clear(); for (auto const &[unused, resource_ptr] : resource_factory_->resource_map()) { - if (!resource_ptr->is_simple()) { + if (!resource_ptr->is_simple() && !resource_ptr->is_array()) { name_set.insert(resource_ptr->pascal_name()); } } @@ -556,7 +599,22 @@ } absl::StrAppend(&h_output, " kPastMaxValue = ", resource_count, "\n };\n\n"); - + // List complex resource enumeration type. + absl::StrAppend(&h_output, + " enum class ListComplexResourceEnum {\n" + " kNone = 0,\n"); + resource_count = 1; + name_set.clear(); + for (auto const &[unused, resource_ptr] : resource_factory_->resource_map()) { + if (!resource_ptr->is_simple() && resource_ptr->is_array()) { + name_set.insert(resource_ptr->pascal_name()); + } + } + for (auto const &name : name_set) { + absl::StrAppend(&h_output, " k", name, " = ", resource_count++, ",\n"); + } + absl::StrAppend(&h_output, " kPastMaxValue = ", resource_count, + "\n };\n\n"); // Emit instruction attribute types. absl::StrAppend(&h_output, " enum class AttributeEnum {\n"); int attribute_count = 0;
diff --git a/mpact/sim/decoder/instruction_set_contexts.h b/mpact/sim/decoder/instruction_set_contexts.h index 2838810..3ae2e15 100644 --- a/mpact/sim/decoder/instruction_set_contexts.h +++ b/mpact/sim/decoder/instruction_set_contexts.h
@@ -1,3 +1,17 @@ +// Copyright 2024 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. + #ifndef MPACT_SIM_DECODER_INSTRUCTION_SET_CONTEXTS_H_ #define MPACT_SIM_DECODER_INSTRUCTION_SET_CONTEXTS_H_
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc index 579f96c..0374561 100644 --- a/mpact/sim/decoder/instruction_set_visitor.cc +++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -21,7 +21,6 @@ #include <iostream> #include <istream> #include <memory> -#include <new> #include <optional> #include <string> #include <utility> @@ -1285,9 +1284,20 @@ auto *factory = slot->instruction_set()->resource_factory(); DestinationOperand *dest_op = nullptr; // Extract the text from the resource reference. - std::string ident_text = resource_item->name->getText(); + std::string ident_text; + bool is_array; + if (resource_item->name != nullptr) { + ident_text = resource_item->name->getText(); + is_array = false; + } else { + // Prepend the name with [] so that it doesn't conflict with a non-array + // resource of the same name. + ident_text = resource_item->array_name->getText(); + is_array = true; + } dest_op = inst->GetDestOp(ident_text); auto *resource = factory->GetOrInsertResource(ident_text); + resource->set_is_array(is_array); // Compute begin and end values. TemplateExpression *begin_expr; TemplateExpression *end_expr; @@ -1346,7 +1356,8 @@ {resource_item->end_cycle, context_file_map_.at(slot->ctx())}); end_expr = VisitExpression(resource_item->end_cycle, slot, inst); } - auto *ref = new ResourceReference(resource, dest_op, begin_expr, end_expr); + auto *ref = + new ResourceReference(resource, is_array, dest_op, begin_expr, end_expr); return std::optional<ResourceReference *>(ref); } @@ -1574,9 +1585,17 @@ } if (ctx->source != nullptr) { int instance = 0; - for (auto *ident : ctx->source->IDENT()) { - std::string name = ident->getText(); - child->opcode()->AppendSourceOpName(name); + for (auto *source_op : ctx->source->source_operand()) { + std::string name; + bool is_array; + if (source_op->source != nullptr) { + name = source_op->source->getText(); + is_array = false; + } else { + name = source_op->array_source->getText(); + is_array = true; + } + child->opcode()->AppendSourceOp(name, is_array); parent->opcode()->op_locator_map().insert( std::make_pair(name, OperandLocator(op_spec_number, 's', instance))); instance++; @@ -1585,7 +1604,15 @@ if (ctx->dest_list() != nullptr) { int instance = 0; for (auto *dest_op : ctx->dest_list()->dest_operand()) { - std::string ident = dest_op->dest->getText(); + std::string ident; + bool is_array; + if (dest_op->dest != nullptr) { + ident = dest_op->dest->getText(); + is_array = false; + } else { + ident = dest_op->array_dest->getText(); + is_array = true; + } // The latency of the destination operand is either specified by an // expression, by '*' (wildcard), or omitted, in which case it // defaults to 1. @@ -1593,14 +1620,15 @@ context_file_map_.insert( {dest_op->expression(), context_file_map_.at(slot->ctx())}); child->opcode()->AppendDestOp( - ident, VisitExpression(dest_op->expression(), slot, child)); + ident, is_array, + VisitExpression(dest_op->expression(), slot, child)); } else if (dest_op->wildcard != nullptr) { - child->opcode()->AppendDestOp(ident); + child->opcode()->AppendDestOp(ident, is_array); } else if (slot->default_latency() != nullptr) { - child->opcode()->AppendDestOp(ident, + child->opcode()->AppendDestOp(ident, is_array, slot->default_latency()->DeepCopy()); } else { - child->opcode()->AppendDestOp(ident, new TemplateConstant(1)); + child->opcode()->AppendDestOp(ident, is_array, new TemplateConstant(1)); } parent->opcode()->op_locator_map().insert( std::make_pair(ident, OperandLocator(op_spec_number, 'd', instance))); @@ -2146,32 +2174,54 @@ absl::StrAppend( &output, " virtual ResourceOperandInterface *GetSimpleResourceOperand", "(SlotEnum slot, int entry, OpcodeEnum opcode, SimpleResourceVector " - "&resource_vec, int end) = 0;\n"); + "&resource_vec, int end) { return nullptr;}\n"); absl::StrAppend( - &output, " virtual ResourceOperandInterface *GetComplexResourceOperand", + &output, + " virtual ResourceOperandInterface * " + "GetComplexResourceOperand", "(SlotEnum slot, int entry, OpcodeEnum opcode, ComplexResourceEnum " - "resource_op, int begin, int end) = 0;\n"); + "resource_op, int begin, int end) { return {}; }\n"); + absl::StrAppend( + &output, + " virtual std::vector<ResourceOperandInterface *> " + "GetComplexResourceOperands", + "(SlotEnum slot, int entry, OpcodeEnum opcode, ComplexResourceEnum " + "resource_op, int begin, int end) { return {}; }\n"); // For each operand type, declare the pure virtual method that returns the // given operand. absl::StrAppend(&output, " virtual PredicateOperandInterface *GetPredicate" "(SlotEnum slot, int entry, OpcodeEnum opcode, PredOpEnum " - "pred_op) = 0;\n"); + "pred_op) { return nullptr; }\n"); absl::StrAppend(&output, " virtual SourceOperandInterface *GetSource" "(SlotEnum slot, int entry, OpcodeEnum opcode, SourceOpEnum " - "source_op, int source_no) = 0;\n"); + "source_op, int source_no) { return nullptr;}\n"); + absl::StrAppend( + &output, + " virtual std::vector<SourceOperandInterface *> GetSources" + "(SlotEnum slot, int entry, OpcodeEnum opcode, ListSourceOpEnum " + "list_source_op, int source_no) { return {};}\n"); absl::StrAppend(&output, " virtual DestinationOperandInterface *GetDestination" "(SlotEnum slot, int entry, OpcodeEnum opcode, " - "DestOpEnum dest_op, int dest_no, int latency)" - "= 0;\n"); + "DestOpEnum list_dest_op, int dest_no, int latency)" + " { return nullptr; }\n"); + absl::StrAppend( + &output, + " virtual std::vector<DestinationOperandInterface *> GetDestinations" + "(SlotEnum slot, int entry, OpcodeEnum opcode, " + "ListDestOpEnum dest_op, int dest_no, const std::vector<int> &latency)" + " { return {}; };\n"); // Destination operand latency getter for destination operands with '*' // as latency. absl::StrAppend( &output, " virtual int GetLatency(SlotEnum slot, int entry, OpcodeEnum " - "opcode, DestOpEnum dest_op, int dest_no) = 0;\n"); + "opcode, DestOpEnum dest_op, int dest_no) { return 0; };\n", + " virtual std::vector<int> GetLatency(SlotEnum slot, int entry, " + "OpcodeEnum " + "opcode, ListDestOpEnum dest_op, int dest_no) { return {0}; }\n"); absl::StrAppend(&output, "};\n\n"); absl::StrAppend(
diff --git a/mpact/sim/decoder/opcode.cc b/mpact/sim/decoder/opcode.cc index bdc6bcf..aaed476 100644 --- a/mpact/sim/decoder/opcode.cc +++ b/mpact/sim/decoder/opcode.cc
@@ -15,7 +15,6 @@ #include "mpact/sim/decoder/opcode.h" #include <functional> -#include <new> #include <string> #include <utility> @@ -45,19 +44,19 @@ dest_op_map_.clear(); } -void Opcode::AppendSourceOpName(absl::string_view op_name) { - source_op_name_vec_.emplace_back(op_name); +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) { - auto *op = new DestinationOperand(std::string(op_name)); +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, +void Opcode::AppendDestOp(absl::string_view op_name, bool is_array, TemplateExpression *expression) { - auto *op = new DestinationOperand(std::string(op_name), 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)); } @@ -108,15 +107,15 @@ 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 &op_name : opcode->source_op_name_vec()) { - new_opcode->AppendSourceOpName(op_name); + 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()); + 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 @@ -124,7 +123,8 @@ // recursively folded into constant nodes. auto result = dest_op->expression()->Evaluate(args); if (result.ok()) { - new_opcode->AppendDestOp(dest_op->name(), result.value()); + new_opcode->AppendDestOp(dest_op->name(), dest_op->is_array(), + result.value()); } else { delete new_opcode; return absl::InternalError(absl::StrCat(
diff --git a/mpact/sim/decoder/opcode.h b/mpact/sim/decoder/opcode.h index 2e51bc4..ad327aa 100644 --- a/mpact/sim/decoder/opcode.h +++ b/mpact/sim/decoder/opcode.h
@@ -50,26 +50,31 @@ class DestinationOperand { public: // Operand latency is defined by the expression. - DestinationOperand(std::string name, TemplateExpression *expression) + DestinationOperand(std::string name, bool is_array, + TemplateExpression *expression) : name_(std::move(name)), pascal_case_name_(ToPascalCase(name_)), - expression_(expression) {} + expression_(expression), + is_array_(is_array) {} // Operand latency is a constant. - DestinationOperand(std::string name, int latency) + DestinationOperand(std::string name, bool is_array, int latency) : name_(std::move(name)), pascal_case_name_(ToPascalCase(name_)), - expression_(new TemplateConstant(latency)) {} + expression_(new TemplateConstant(latency)), + is_array_(is_array) {} // This constructor is used when the destination operand latency is specified // as '*' - meaning that it will be computed at the time of decode. - explicit DestinationOperand(std::string name) + explicit DestinationOperand(std::string name, bool is_array) : name_(std::move(name)), pascal_case_name_(ToPascalCase(name_)), - expression_(nullptr) {} + expression_(nullptr), + is_array_(is_array) {} ~DestinationOperand() { delete expression_; } const std::string &name() const { return name_; } const std::string &pascal_case_name() const { return pascal_case_name_; } TemplateExpression *expression() const { return expression_; } + bool is_array() const { return is_array_; } bool HasLatency() const { return expression_ != nullptr; } absl::StatusOr<int> GetLatency() const { if (expression_ == nullptr) return -1; @@ -90,6 +95,14 @@ std::string name_; std::string pascal_case_name_; TemplateExpression *expression_; + bool is_array_ = false; +}; + +struct SourceOperand { + std::string name; + bool is_array; + SourceOperand(std::string name_, bool is_array_) + : name(std::move(name_)), is_array(is_array_) {} }; // This struct is used to specify the location of an operand within an @@ -145,18 +158,22 @@ struct ResourceReference { Resource *resource; + bool is_array; DestinationOperand *dest_op; TemplateExpression *begin_expression; TemplateExpression *end_expression; - ResourceReference(Resource *resource_, DestinationOperand *dest_op_, + ResourceReference(Resource *resource_, bool is_array_, + DestinationOperand *dest_op_, TemplateExpression *begin_expr_, TemplateExpression *end_expr_) : resource(resource_), + is_array(is_array_), dest_op(dest_op_), begin_expression(begin_expr_), end_expression(end_expr_) {} ResourceReference(const ResourceReference &rhs) { resource = rhs.resource; + is_array = rhs.is_array; dest_op = rhs.dest_op; begin_expression = rhs.begin_expression->DeepCopy(); end_expression = rhs.end_expression->DeepCopy(); @@ -187,9 +204,10 @@ // to get the Predicate, Source and Destination operand interfaces (defined // in .../sim/generic/operand_interfaces.h. The implementation of these // methods will be left to the user of this generator tool. - void AppendSourceOpName(absl::string_view op_name); - void AppendDestOp(absl::string_view op_name, TemplateExpression *expression); - void AppendDestOp(absl::string_view op_name); + void AppendSourceOp(absl::string_view op_name, bool is_array); + void AppendDestOp(absl::string_view op_name, bool is_array, + TemplateExpression *expression); + void AppendDestOp(absl::string_view op_name, bool is_array); DestinationOperand *GetDestOp(absl::string_view op_name); // Append child opcode specification. void AppendChild(Opcode *op) { child_ = op; } @@ -210,8 +228,8 @@ void set_predicate_op_name(absl::string_view op_name) { predicate_op_name_ = op_name; } - const std::vector<std::string> &source_op_name_vec() const { - return source_op_name_vec_; + const std::vector<SourceOperand> &source_op_vec() const { + return source_op_vec_; } const std::vector<DestinationOperand *> &dest_op_vec() const { return dest_op_vec_; @@ -226,7 +244,7 @@ Opcode *child_ = nullptr; Opcode *parent_ = nullptr; std::string predicate_op_name_; - std::vector<std::string> source_op_name_vec_; + std::vector<SourceOperand> source_op_vec_; std::vector<DestinationOperand *> dest_op_vec_; absl::flat_hash_map<std::string, DestinationOperand *> dest_op_map_; std::string name_;
diff --git a/mpact/sim/decoder/resource.h b/mpact/sim/decoder/resource.h index 68364e6..6567b15 100644 --- a/mpact/sim/decoder/resource.h +++ b/mpact/sim/decoder/resource.h
@@ -47,6 +47,8 @@ void set_is_simple(bool value) { is_simple_ = value; } bool is_multi_valued() const { return is_multi_valued_; } void set_is_multi_valued(bool value) { is_multi_valued_ = value; } + bool is_array() const { return is_array_; } + void set_is_array(bool value) { is_array_ = value; } const std::string &name() const { return name_; } const std::string &pascal_name() const { return pascal_name_; } @@ -57,6 +59,7 @@ std::string pascal_name_; bool is_multi_valued_ = false; bool is_simple_ = true; + bool is_array_ = false; }; // Resource factory class. This is used so that there's a single registry of
diff --git a/mpact/sim/decoder/slot.cc b/mpact/sim/decoder/slot.cc index 941cb9c..2b5edf0 100644 --- a/mpact/sim/decoder/slot.cc +++ b/mpact/sim/decoder/slot.cc
@@ -383,12 +383,18 @@ if (!simple_refs.empty()) { // First gather the resource references into a single vector, then request // the resource operands for all the resource references in that vector. - std::string sep = ""; absl::StrAppend(&output, " std::vector<SimpleResourceEnum> hold_vec = {"); for (auto const *simple : simple_refs) { - absl::StrAppend(&output, sep, "\n SimpleResourceEnum::k", - simple->resource->pascal_name(), ", "); + std::string resource_name; + if (simple->is_array) { + resource_name = absl::StrCat("SimpleResourceEnum::k", + simple->resource->pascal_name()); + } else { + resource_name = absl::StrCat("SimpleResourceEnum::k", + simple->resource->pascal_name()); + } + absl::StrAppend(&output, "\n ", resource_name, ", "); } absl::StrAppend(&output, "};\n\n" @@ -416,15 +422,27 @@ &output, "#error Unable to get value of begin or end expression\n"); continue; } - absl::StrAppend(&output, - " res_op = enc->GetComplexResourceOperand(slot, entry, ", - opcode_enum, ", ComplexResourceEnum::k", - complex->resource->pascal_name(), ", "); - absl::StrAppend(&output, *begin, ", ", *end, ");\n"); - absl::StrAppend(&output, - " if (res_op != nullptr) {\n" - " inst->AppendResourceHold(res_op);\n" - " }\n"); + if (complex->is_array) { + absl::StrAppend( + &output, + " auto res_op_vec = enc->GetComplexResourceOperands(slot, entry, ", + opcode_enum, ", ListComplexResourceEnum::k", + complex->resource->pascal_name(), *begin, ", ", *end, ");\n"); + absl::StrAppend(&output, + " for (auto res_op : res_op_vec) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); + } else { + absl::StrAppend( + &output, " res_op = enc->GetComplexResourceOperand(slot, entry, ", + opcode_enum, ", ComplexResourceEnum::k", + complex->resource->pascal_name(), ", "); + absl::StrAppend(&output, *begin, ", ", *end, ");\n"); + absl::StrAppend(&output, + " if (res_op != nullptr) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); + } } // Get all the simple resources that need to be reserved, then all the complex @@ -473,8 +491,15 @@ for (auto iter = latency_map.lower_bound(latency); iter != latency_map.upper_bound(latency); ++iter) { auto *simple = iter->second; - absl::StrAppend(&output, sep, "\n SimpleResourceEnum::k", - simple->resource->pascal_name(), ","); + std::string resource_name; + if (simple->is_array) { + resource_name = absl::StrCat("SimpleResourceEnum::k", + simple->resource->pascal_name()); + } else { + resource_name = absl::StrCat("SimpleResourceEnum::k", + simple->resource->pascal_name()); + } + absl::StrAppend(&output, sep, "\n ", resource_name, ","); } absl::StrAppend( &output, @@ -504,16 +529,29 @@ // Get the integer values from the begin and end expression values. int *begin = std::get_if<int>(&begin_value.value()); int *end = std::get_if<int>(&end_value.value()); - absl::StrAppend( - &output, - " res_op = enc->GetComplexResourceOperand(ComplexResourceEnum::k", - complex->resource->pascal_name(), ", ResourceArgumentEnum::k"); - absl::StrAppend(&output, "None, slot, entry, ", *begin, ", ", *end, - ");\n"); - absl::StrAppend(&output, - " if (res != nullptr) {\n" - " inst->AppendResourceAcquire(res_op);\n" - " }\n"); + if (complex->is_array) { + absl::StrAppend(&output, + " auto res_op_vec = " + "enc->GetComplexResourceOperands(slot, entry, ", + opcode_enum, ", ListComplexResourceEnum::k", + complex->resource->pascal_name(), *begin, ", ", *end, + ");\n"); + absl::StrAppend(&output, + " for (auto res_op : res_op_vec) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); + } else { + absl::StrAppend( + &output, + " res_op = enc->GetComplexResourceOperand(slot, entry, ", + opcode_enum, ", ComplexResourceEnum::k", + complex->resource->pascal_name(), ", "); + absl::StrAppend(&output, *begin, ", ", *end, ");\n"); + absl::StrAppend(&output, + " if (res_op != nullptr) {\n" + " inst->AppendResourceHold(res_op);\n" + " }\n"); + } } } absl::StrAppend(&output, " };\n\n"); @@ -586,39 +624,76 @@ } // Generate code to set the instruction's source operands. int source_no = 0; - for (const auto &src_name : inst->opcode()->source_op_name_vec()) { - std::string src_op_enum = - absl::StrCat("SourceOpEnum::k", ToPascalCase(src_name)); - absl::StrAppend(&output, " inst->AppendSource(enc->GetSource", - "(slot_, entry, ", opcode_enum, ", ", src_op_enum, ", ", - source_no++, "));\n"); + for (const auto &src_op : inst->opcode()->source_op_vec()) { + // If the source operand is an array, then we need to iterate over the + // vector of operands that GetSources returns. + if (src_op.is_array) { + std::string src_op_enum = + absl::StrCat("ListSourceOpEnum::k", ToPascalCase(src_op.name)); + absl::StrAppend( + &output, + " {\n" + " auto vec = enc->GetSources", + "(slot_, entry, ", opcode_enum, ", ", src_op_enum, ", ", + source_no, + ");\n" + " for (auto *op : vec) inst->AppendSource(op);\n" + " }\n"); + } else { + std::string src_op_enum = + absl::StrCat("SourceOpEnum::k", ToPascalCase(src_op.name)); + absl::StrAppend(&output, " inst->AppendSource(enc->GetSource", + "(slot_, entry, ", opcode_enum, ", ", src_op_enum, + ", ", source_no++, "));\n"); + } } // Generate code to set the instruction's destination operands. int dest_no = 0; for (auto const *dst_op : inst->opcode()->dest_op_vec()) { - std::string dest_op_enum = - absl::StrCat("DestOpEnum::k", dst_op->pascal_case_name()); + std::string dest_op_enum; + if (dst_op->is_array()) { + dest_op_enum = + absl::StrCat("ListDestOpEnum::k", dst_op->pascal_case_name()); + } else { + dest_op_enum = + absl::StrCat("DestOpEnum::k", dst_op->pascal_case_name()); + } + std::string latency; if (dst_op->expression() == nullptr) { + latency = absl::StrCat("enc->GetLatency(slot_, entry, ", opcode_enum, + ", ", dest_op_enum, ", ", dest_no, ")"); + } else { + auto result = dst_op->GetLatency(); + if (!result.ok()) { + absl::StrAppend(&output, + "#error \"Failed to get latency for operand '", + dst_op->name(), "'\""); + dest_no++; + continue; + } + latency = absl::StrCat(result.value()); + // If the operand is an array, then the latency is a vector. + if (dst_op->is_array()) { + latency = absl::StrCat("{", latency, "}"); + } + } + if (dst_op->is_array()) { + absl::StrAppend( + &output, + " {\n" + " auto vec = enc->GetDestinations", + "(slot_, entry, ", opcode_enum, ", ", dest_op_enum, ", ", dest_no, + ", ", latency, + ");\n" + " for (auto *op : vec) inst->AppendDestination(op);\n" + " }"); + } else { absl::StrAppend( &output, " inst->AppendDestination(enc->GetDestination(", "slot_, entry, ", opcode_enum, ", ", dest_op_enum, ", ", dest_no, - ", enc->GetLatency(slot_, entry, ", opcode_enum, ", ", - dest_op_enum, " , ", dest_no, ")));\n"); + ", ", latency, "));\n"); dest_no++; - continue; } - auto result = dst_op->GetLatency(); - if (!result.ok()) { - absl::StrAppend(&output, - "#error \"Failed to get latency for operand '", - dst_op->name(), "'\""); - dest_no++; - continue; - } - absl::StrAppend(&output, - " inst->AppendDestination(enc->GetDestination", - "(slot_, entry, ", opcode_enum, ", ", dest_op_enum, - ", ", dest_no, ", ", result.value(), "));\n"); dest_no++; } absl::StrAppend(&output, " });\n\n");
diff --git a/mpact/sim/decoder/slot.h b/mpact/sim/decoder/slot.h index 33fb85b..888b584 100644 --- a/mpact/sim/decoder/slot.h +++ b/mpact/sim/decoder/slot.h
@@ -135,7 +135,9 @@ absl::btree_map<std::string, ResourceDetailsCtx *> &resource_spec_map() { return resource_spec_map_; } - + absl::btree_map<std::string, IdentListCtx *> &resource_array_ref_map() { + return resource_array_ref_map_; + } const absl::btree_map<std::string, TemplateExpression *> &attribute_map() { return attribute_map_; } @@ -194,6 +196,7 @@ absl::flat_hash_map<std::string, TemplateExpression *> constant_map_; // Named resource specifiers. absl::btree_map<std::string, ResourceDetailsCtx *> resource_spec_map_; + absl::btree_map<std::string, IdentListCtx *> resource_array_ref_map_; // Default instruction attributes. absl::btree_map<std::string, TemplateExpression *> attribute_map_; };
diff --git a/mpact/sim/decoder/test/BUILD b/mpact/sim/decoder/test/BUILD index cee5d5a..bcef866 100644 --- a/mpact/sim/decoder/test/BUILD +++ b/mpact/sim/decoder/test/BUILD
@@ -14,7 +14,7 @@ # This contains the test projects for the decoder. -load("//mpact/sim/decoder:mpact_sim_isa.bzl", "mpact_cc_library", "mpact_cc_test", "mpact_isa_decoder", "mpact_proto_fmt_decoder") +load("//mpact/sim/decoder:mpact_sim_isa.bzl", "mpact_bin_fmt_decoder", "mpact_cc_library", "mpact_cc_test", "mpact_isa_decoder", "mpact_proto_fmt_decoder") package( default_applicable_licenses = ["//:license"], @@ -86,6 +86,7 @@ deps = [ "//mpact/sim/decoder:isa_parser", "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest", @@ -490,3 +491,82 @@ "@com_google_googletest//:gtest_main", ], ) + +cc_library( + name = "push_pop_instructions", + srcs = [ + "push_pop.cc", + ], + hdrs = [ + "push_pop.h", + ], + deps = [ + "//mpact/sim/generic:instruction", + ], +) + +cc_library( + name = "push_pop_decoder", + srcs = [ + "push_pop_decoder.cc", + "push_pop_encoding.cc", + ], + hdrs = [ + "push_pop_decoder.h", + "push_pop_encoding.h", + ], + deps = [ + ":push_pop_bin_fmt", + ":push_pop_isa", + "//mpact/sim/generic:arch_state", + "//mpact/sim/generic:core", + "//mpact/sim/generic:instruction", + "//mpact/sim/generic:type_helpers", + "//mpact/sim/util/memory", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/log", + ], +) + +mpact_isa_decoder( + name = "push_pop_isa", + src = "push_pop.isa", + includes = [], + isa_name = "PushPopInst", + prefix = "push_pop_inst", + deps = [ + ":push_pop_instructions", + "@com_google_absl//absl/functional:bind_front", + ], +) + +mpact_bin_fmt_decoder( + name = "push_pop_bin_fmt", + src = "push_pop.bin_fmt", + decoder_name = "PushPopInst", + includes = [], + prefix = "push_pop_inst", + deps = [ + ":push_pop_isa", + ], +) + +mpact_cc_test( + name = "array_operand_test", + size = "small", + srcs = [ + "array_operand_test.cc", + ], + deps = [ + ":push_pop_decoder", + ":push_pop_isa", + "//mpact/sim/generic:arch_state", + "//mpact/sim/generic:core", + "//mpact/sim/util/memory", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +)
diff --git a/mpact/sim/decoder/test/array_operand_test.cc b/mpact/sim/decoder/test/array_operand_test.cc new file mode 100644 index 0000000..c0bdb3f --- /dev/null +++ b/mpact/sim/decoder/test/array_operand_test.cc
@@ -0,0 +1,148 @@ +#include <sys/types.h> + +#include <cstdint> +#include <memory> + +#include "absl/log/check.h" +#include "absl/strings/str_cat.h" +#include "googlemock/include/gmock/gmock.h" // IWYU pragma: keep +#include "googletest/include/gtest/gtest.h" +#include "mpact/sim/decoder/test/push_pop_decoder.h" +#include "mpact/sim/decoder/test/push_pop_inst_enums.h" +#include "mpact/sim/generic/arch_state.h" +#include "mpact/sim/generic/data_buffer.h" +#include "mpact/sim/generic/register.h" +#include "mpact/sim/util/memory/flat_demand_memory.h" + +// This file tests that the "array" or "list" valued operands used in the .isa +// description is handled correctly by the decoder. This test case uses the +// push/pop isa and decoder. + +namespace { + +using ::mpact::sim::decoder::test::OpcodeEnum; +using ::mpact::sim::decoder::test::PushPopDecoder; +using ::mpact::sim::generic::DataBuffer; +using ::mpact::sim::util::FlatDemandMemory; + +using TestRegister = ::mpact::sim::generic::Register<uint32_t>; + +constexpr uint64_t kBase = 0x1000; + +// A test state, since ArchState cannot be instantiated directly. +class TestState : public ::mpact::sim::generic::ArchState { + public: + TestState() : ::mpact::sim::generic::ArchState("TestState") {} +}; + +// Helper functions to generate encoding push and pop instructions with +// different rlist and spimm values. +static uint16_t GeneratePushInstruction(uint8_t rlist, uint8_t spimm) { + uint16_t inst = 0b101'11000'0000'00'10; + return inst | ((rlist & 0xf) << 4) | ((spimm & 0x3) << 2); +} + +static uint16_t GeneratePopInstruction(uint8_t rlist, uint8_t spimm) { + uint16_t inst = 0b101'11010'0000'00'10; + return inst | ((rlist & 0xf) << 4) | ((spimm & 0x3) << 2); +} + +// Test fixture. This adds registers to the state and writes instructions to +// memory. +class ArrayOperandTest : public ::testing::Test { + protected: + ArrayOperandTest() { + state_ = std::make_unique<TestState>(); + memory_ = std::make_unique<FlatDemandMemory>(); + decoder_ = std::make_unique<PushPopDecoder>(state_.get(), memory_.get()); + // Add registers to the state. + for (int i = 1; i < 32; i++) { + state_->AddRegister(new TestRegister(state_.get(), absl::StrCat("x", i))); + } + // Write instructions to memory - 16 each of pushes and pops. The first + // 4 of each are illegal instructions since rlist < 4. + DataBuffer *db = state_->db_factory()->Allocate<uint16_t>(1); + for (int i = 0; i < 16; i++) { + db->Set<uint16_t>(0, GeneratePushInstruction(i, i & 0x3)); + memory_->Store(kBase + i * db->size<uint8_t>(), db); + } + for (int i = 0; i < 16; i++) { + db->Set<uint16_t>(0, GeneratePopInstruction(i, i & 0x3)); + memory_->Store(kBase + 16 * db->size<uint8_t>() + i * db->size<uint8_t>(), + db); + } + db->DecRef(); + } + + std::unique_ptr<TestState> state_; + std::unique_ptr<FlatDemandMemory> memory_; + std::unique_ptr<PushPopDecoder> decoder_; +}; + +// Verify that the push instructions are decoded correctly. +TEST_F(ArrayOperandTest, PushInstructionDecoding) { + for (int i = 0; i < 16; i++) { + auto *inst = decoder_->DecodeInstruction(kBase + i * 2); + CHECK_NE(inst, nullptr); + if (i < 4) { + EXPECT_EQ(inst->opcode(), *OpcodeEnum::kNone); + } else { + EXPECT_EQ(inst->opcode(), *OpcodeEnum::kPush); + } + inst->DecRef(); + } +} + +// Verify that the pop instructions are decoded correctly. +TEST_F(ArrayOperandTest, PopInstructionDecoding) { + for (int i = 0; i < 16; i++) { + auto *inst = decoder_->DecodeInstruction(kBase + 16 * 2 + i * 2); + CHECK_NE(inst, nullptr); + if (i < 4) { + EXPECT_EQ(inst->opcode(), *OpcodeEnum::kNone); + } else { + EXPECT_EQ(inst->opcode(), *OpcodeEnum::kPop); + } + inst->DecRef(); + } +} + +// Verify that the push instructions have the correct number of source and +// destination operands. +TEST_F(ArrayOperandTest, PushOperands) { + for (int i = 4; i < 16; i++) { + auto *inst = decoder_->DecodeInstruction(kBase + i * 2); + CHECK_NE(inst, nullptr); + EXPECT_EQ(inst->opcode(), *OpcodeEnum::kPush) + << inst->AsString() << " for instruction " << i; + // Push instructions have 3 source operands (x2, spimm6, rlist) in addition + // to the list of registers to be pushed. The number of registers pushed + // is determined by the rlist field, 4 -> 1 register, 5 -> 2 registers, + // etc., up to 14 -> 11 registers. Then for 15 -> 13 registers. + EXPECT_EQ(inst->SourcesSize(), 3 + (i - 3) + (i == 15 ? 1 : 0)); + // There should only be a single destination operand, x2. + EXPECT_EQ(inst->DestinationsSize(), 1); + inst->DecRef(); + } +} + +// Verify that the pop instructions have the correct number of source and +// destination operands. +TEST_F(ArrayOperandTest, PopOperands) { + for (int i = 4; i < 16; i++) { + auto *inst = decoder_->DecodeInstruction(kBase + 16 * 2 + i * 2); + CHECK_NE(inst, nullptr); + EXPECT_EQ(inst->opcode(), *OpcodeEnum::kPop) + << inst->AsString() << " for instruction " << i; + // Pop instructions have 3 source operands (x2, spimm6, rlist). + EXPECT_EQ(inst->SourcesSize(), 3); + // The number of destination operands is x2 and the list of registers to be + // popped from the stack. The number of registers is determined by the + // rlist field, 4 -> 1 register, 5 -> 2 registers, etc., up to 14 -> 11 + // registers. Then for 15 -> 13 registers. + EXPECT_EQ(inst->DestinationsSize(), 1 + (i - 3) + (i == 15 ? 1 : 0)); + inst->DecRef(); + } +} + +} // namespace
diff --git a/mpact/sim/decoder/test/opcode_test.cc b/mpact/sim/decoder/test/opcode_test.cc index d0a234f..fba903c 100644 --- a/mpact/sim/decoder/test/opcode_test.cc +++ b/mpact/sim/decoder/test/opcode_test.cc
@@ -17,8 +17,10 @@ #include <memory> #include <string> +#include "absl/status/status.h" #include "absl/status/statusor.h" -#include "googlemock/include/gmock/gmock.h" +#include "absl/strings/str_cat.h" +#include "googlemock/include/gmock/gmock.h" // IWYU pragma: keep #include "googletest/include/gtest/gtest.h" #include "mpact/sim/decoder/instruction_set.h" @@ -57,7 +59,7 @@ EXPECT_STREQ(opcode_->name().c_str(), kOpcodeName0); EXPECT_EQ(opcode_->value(), 1); EXPECT_STREQ(opcode_->predicate_op_name().c_str(), ""); - EXPECT_EQ(opcode_->source_op_name_vec().size(), 0); + EXPECT_EQ(opcode_->source_op_vec().size(), 0); EXPECT_EQ(opcode_->dest_op_vec().size(), 0); } @@ -88,9 +90,9 @@ TEST_F(OpcodeTest, SourceOperandNames) { for (int indx = 0; indx < 3; indx++) { std::string source_op_name = absl::StrCat("SourceOp", indx); - opcode_->AppendSourceOpName(source_op_name); - EXPECT_EQ(opcode_->source_op_name_vec().size(), indx + 1); - EXPECT_STREQ(opcode_->source_op_name_vec()[indx].c_str(), + opcode_->AppendSourceOp(source_op_name, /*is_array=*/false); + EXPECT_EQ(opcode_->source_op_vec().size(), indx + 1); + EXPECT_STREQ(opcode_->source_op_vec()[indx].name.c_str(), source_op_name.c_str()); } } @@ -100,10 +102,10 @@ for (int indx = 0; indx < 2; indx++) { std::string dest_op_name = absl::StrCat("DestOp", indx); if (indx == 0) { - opcode_->AppendDestOp(dest_op_name); + opcode_->AppendDestOp(dest_op_name, /*is_array=*/false); } else if (indx == 1) { // Using nullptr - the value isn't checked upon append. - opcode_->AppendDestOp(dest_op_name, nullptr); + opcode_->AppendDestOp(dest_op_name, /*is_array=*/false, nullptr); } EXPECT_EQ(opcode_->dest_op_vec().size(), indx + 1); EXPECT_STREQ(opcode_->dest_op_vec()[indx]->name().c_str(),
diff --git a/mpact/sim/decoder/test/push_pop.bin_fmt b/mpact/sim/decoder/test/push_pop.bin_fmt new file mode 100644 index 0000000..21642f2 --- /dev/null +++ b/mpact/sim/decoder/test/push_pop.bin_fmt
@@ -0,0 +1,47 @@ +// Copyright 2024 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. + +// This file defines the push and pop instructions for a test ISA to test +// "array" or "list" operands. + +decoder PushPopInst { + namespace mpact::sim::decoder::test; + opcode_enum = "OpcodeEnum"; + includes { + #include "mpact/sim/decoder/test/push_pop_inst_decoder.h" + } + PushPop; +} + +format Inst16Format[16] { + fields: + unsigned func3[3]; + unsigned bits[11]; + unsigned op[2]; +} + +format PType[16] : Inst16Format { + fields: + unsigned func8[8]; + unsigned rlist[4]; + unsigned spimm[2]; + unsigned op[2]; + overlays: + unsigned spimm6[6] = spimm, 0b0000; +} + +instruction group PushPop[16] : Inst16Format { + push : PType : func8 == 0b101'11000, rlist > 3, op == 0b10; + pop : PType : func8 == 0b101'11010, rlist > 3, op == 0b10; +} \ No newline at end of file
diff --git a/mpact/sim/decoder/test/push_pop.cc b/mpact/sim/decoder/test/push_pop.cc new file mode 100644 index 0000000..b7ac9f7 --- /dev/null +++ b/mpact/sim/decoder/test/push_pop.cc
@@ -0,0 +1,33 @@ +// Copyright 2024 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/test/push_pop.h" + +#include "mpact/sim/generic/instruction.h" + +namespace mpact { +namespace sim { +namespace decoder { +namespace test { + +using ::mpact::sim::generic::Instruction; + +void Illegal(const Instruction *inst) { /*empty*/ } +void Push(const Instruction *inst) { /*empty*/ } +void Pop(const Instruction *inst) { /*empty*/ } + +} // namespace test +} // namespace decoder +} // namespace sim +} // namespace mpact
diff --git a/mpact/sim/decoder/test/push_pop.h b/mpact/sim/decoder/test/push_pop.h new file mode 100644 index 0000000..01523d1 --- /dev/null +++ b/mpact/sim/decoder/test/push_pop.h
@@ -0,0 +1,36 @@ +// Copyright 2024 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. + +#ifndef MPACT_SIM_DECODER_TEST_PUSH_POP_H_ +#define MPACT_SIM_DECODER_TEST_PUSH_POP_H_ + +#include "mpact/sim/generic/instruction.h" + +namespace mpact { +namespace sim { +namespace decoder { +namespace test { + +using ::mpact::sim::generic::Instruction; + +void Illegal(const Instruction *inst); +void Push(const Instruction *inst); +void Pop(const Instruction *inst); + +} // namespace test +} // namespace decoder +} // namespace sim +} // namespace mpact + +#endif // MPACT_SIM_DECODER_TEST_PUSH_POP_H_
diff --git a/mpact/sim/decoder/test/push_pop.isa b/mpact/sim/decoder/test/push_pop.isa new file mode 100644 index 0000000..de79edf --- /dev/null +++ b/mpact/sim/decoder/test/push_pop.isa
@@ -0,0 +1,42 @@ +// Copyright 2024 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. + +// This file defines the isa for the push/pop example decoder. + +disasm widths = {-18}; + +isa PushPopInst { + namespace mpact::sim::decoder::test; + slots { push_pop_inst; } +} + +slot push_pop_inst { + includes { + #include "mpact/sim/decoder/test/push_pop.h" + } + default size = 2; + default opcode = + disasm: "IllegalInstruction", + semfunc: "&Illegal"; + opcodes { + push{ : x2, spimm6, rlist, [rlist] : x2 }, + resources: {x2, [rlist] : x2}, + disasm: "push", + semfunc: "&Push"; + pop{ : x2, spimm6, rlist : x2, [rlist] }, + resources: { x2 : x2, [rlist]}, + disasm: "pop", + semfunc: "&Pop"; + } +} \ No newline at end of file
diff --git a/mpact/sim/decoder/test/push_pop_decoder.cc b/mpact/sim/decoder/test/push_pop_decoder.cc new file mode 100644 index 0000000..d2b3596 --- /dev/null +++ b/mpact/sim/decoder/test/push_pop_decoder.cc
@@ -0,0 +1,57 @@ +// Copyright 2024 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/test/push_pop_decoder.h" + +#include <sys/types.h> + +#include <cstdint> + +#include "mpact/sim/decoder/test/push_pop_encoding.h" +#include "mpact/sim/decoder/test/push_pop_inst_decoder.h" +#include "mpact/sim/decoder/test/push_pop_inst_enums.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/util/memory/memory_interface.h" + +namespace mpact::sim::decoder::test { + +using ::mpact::sim::generic::Instruction; +using ::mpact::sim::util::MemoryInterface; + +PushPopDecoder::PushPopDecoder(ArchState *state, MemoryInterface *memory) + : state_(state), memory_(memory) { + push_pop_isa_factory_ = new PushPopIsaFactory(); + push_pop_isa_ = new PushPopInstInstructionSet(state, push_pop_isa_factory_); + push_pop_encoding_ = new PushPopEncoding(state_); + inst_db_ = state_->db_factory()->Allocate<uint16_t>(1); +} + +PushPopDecoder::~PushPopDecoder() { + inst_db_->DecRef(); + delete push_pop_encoding_; + delete push_pop_isa_; + delete push_pop_isa_factory_; +} + +Instruction *PushPopDecoder::DecodeInstruction(uint64_t address) { + memory_->Load(address, inst_db_, nullptr, nullptr); + uint16_t iword = inst_db_->Get<uint16_t>(0); + push_pop_encoding_->ParseInstruction(iword); + auto *instruction = push_pop_isa_->Decode(address, push_pop_encoding_); + instruction->set_opcode( + *push_pop_encoding_->GetOpcode(SlotEnum::kPushPopInst, 0)); + return instruction; +} + +} // namespace mpact::sim::decoder::test
diff --git a/mpact/sim/decoder/test/push_pop_decoder.h b/mpact/sim/decoder/test/push_pop_decoder.h new file mode 100644 index 0000000..821d362 --- /dev/null +++ b/mpact/sim/decoder/test/push_pop_decoder.h
@@ -0,0 +1,73 @@ +// Copyright 2024 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. +#ifndef MPACT_SIM_DECODER_TEST_PUSH_POP_DECODER_H_ +#define MPACT_SIM_DECODER_TEST_PUSH_POP_DECODER_H_ + +#include <cstdint> +#include <memory> + +#include "mpact/sim/decoder/test/push_pop_encoding.h" +#include "mpact/sim/decoder/test/push_pop_inst_decoder.h" +#include "mpact/sim/decoder/test/push_pop_inst_enums.h" +#include "mpact/sim/generic/arch_state.h" +#include "mpact/sim/generic/data_buffer.h" +#include "mpact/sim/generic/decoder_interface.h" +#include "mpact/sim/generic/instruction.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/memory_interface.h" + +// This file defines the decoder class for the push/pop isa test case. + +namespace mpact::sim::decoder::test { + +using ::mpact::sim::generic::operator*; // NOLINT + +using ::mpact::sim::generic::ArchState; +using ::mpact::sim::generic::DataBuffer; +using ::mpact::sim::util::MemoryInterface; + +class PushPopIsaFactory : public PushPopInstInstructionSetFactory { + public: + std::unique_ptr<PushPopInstSlot> CreatePushPopInstSlot( + ArchState *state) override { + return std::make_unique<PushPopInstSlot>(state); + } +}; + +class PushPopDecoder : public generic::DecoderInterface { + public: + PushPopDecoder(ArchState *state, MemoryInterface *memory); + PushPopDecoder() = delete; + ~PushPopDecoder() override; + + generic::Instruction *DecodeInstruction(uint64_t address) override; + int GetNumOpcodes() const override { return *OpcodeEnum::kPastMaxValue; } + const char *GetOpcodeName(int index) const override { + return kOpcodeNames[index]; + } + + PushPopEncoding *push_pop_encoding() const { return push_pop_encoding_; } + + private: + ArchState *state_; + MemoryInterface *memory_; + PushPopEncoding *push_pop_encoding_; + PushPopIsaFactory *push_pop_isa_factory_; + PushPopInstInstructionSet *push_pop_isa_; + DataBuffer *inst_db_; +}; + +} // namespace mpact::sim::decoder::test + +#endif // MPACT_SIM_DECODER_TEST_PUSH_POP_DECODER_H_
diff --git a/mpact/sim/decoder/test/push_pop_encoding.cc b/mpact/sim/decoder/test/push_pop_encoding.cc new file mode 100644 index 0000000..487e8c2 --- /dev/null +++ b/mpact/sim/decoder/test/push_pop_encoding.cc
@@ -0,0 +1,232 @@ +// Copyright 2024 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/test/push_pop_encoding.h" + +#include <cstdint> +#include <string> +#include <utility> +#include <vector> + +#include "mpact/sim/decoder/test/push_pop_inst_bin_decoder.h" +#include "mpact/sim/decoder/test/push_pop_inst_enums.h" +#include "mpact/sim/generic/arch_state.h" +#include "mpact/sim/generic/immediate_operand.h" +#include "mpact/sim/generic/operand_interface.h" +#include "mpact/sim/generic/register.h" +#include "mpact/sim/generic/type_helpers.h" + +namespace mpact::sim::decoder::test { + +using ::mpact::sim::generic::operator*; // NOLINT + +using ::mpact::sim::generic::ArchState; +using ::mpact::sim::generic::ImmediateOperand; +using ::mpact::sim::generic::SourceOperandInterface; +using TestRegister = ::mpact::sim::generic::Register<uint32_t>; + +template <typename RegType> +inline RegType *GetRegister(ArchState *state, std::string name) { + auto iter = state->registers()->find(name); + if (iter == state->registers()->end()) { + return nullptr; + } + return static_cast<RegType *>(iter->second); +} + +// Generic helper functions to create register operands. +template <typename RegType> +inline DestinationOperandInterface *GetRegisterDestinationOp(ArchState *state, + std::string name, + int latency) { + auto *reg = GetRegister<RegType>(state, name); + return reg->CreateDestinationOperand(latency); +} + +template <typename RegType> +inline DestinationOperandInterface *GetRegisterDestinationOp( + ArchState *state, std::string name, int latency, std::string op_name) { + auto *reg = GetRegister<RegType>(state, name); + return reg->CreateDestinationOperand(latency, op_name); +} + +template <typename RegType> +inline SourceOperandInterface *GetRegisterSourceOp(ArchState *state, + std::string name) { + auto *reg = GetRegister<RegType>(state, name); + auto *op = reg->CreateSourceOperand(); + return op; +} + +template <typename RegType> +inline SourceOperandInterface *GetRegisterSourceOp(ArchState *state, + std::string name, + std::string op_name) { + auto *reg = GetRegister<RegType>(state, name); + auto *op = reg->CreateSourceOperand(op_name); + return op; +} + +PushPopEncoding::PushPopEncoding(ArchState *state) : state_(state) { + source_op_getters_.insert(std::make_pair( + *SourceOpEnum::kRlist, [this]() -> SourceOperandInterface * { + return new generic::ImmediateOperand<uint32_t>( + p_type::ExtractRlist(inst_word_)); + })); + + source_op_getters_.insert(std::make_pair( + *SourceOpEnum::kSpimm6, [this]() -> SourceOperandInterface * { + return new generic::ImmediateOperand<uint32_t>( + p_type::ExtractSpimm6(inst_word_)); + })); + + source_op_getters_.insert( + std::make_pair(*SourceOpEnum::kX2, [this]() -> SourceOperandInterface * { + return GetRegisterSourceOp<TestRegister>(state_, "x2"); + })); + + list_source_op_getters_.insert(std::make_pair( + *ListSourceOpEnum::kRlist, + [this]() -> std::vector<SourceOperandInterface *> { + std::vector<SourceOperandInterface *> result; + auto rlist = p_type::ExtractRlist(inst_word_); + // Get the value of 'rlist', and add source operands accordingly. + if (rlist < 4) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x1")); + if (rlist == 4) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x8")); + if (rlist == 5) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x9")); + if (rlist == 6) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x18")); + if (rlist == 7) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x19")); + if (rlist == 8) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x20")); + if (rlist == 9) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x21")); + if (rlist == 10) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x22")); + if (rlist == 11) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x23")); + if (rlist == 12) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x24")); + if (rlist == 13) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x25")); + if (rlist == 14) return result; + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x26")); + result.push_back(GetRegisterSourceOp<TestRegister>(state_, "x26")); + return result; + })); + + dest_op_getters_.insert(std::make_pair( + *DestOpEnum::kX2, [this](int latency) -> DestinationOperandInterface * { + return GetRegisterDestinationOp<TestRegister>(state_, "x2", latency); + })); + + list_dest_op_getters_.insert( + std::make_pair(*ListDestOpEnum::kRlist, + [this](const std::vector<int> &latency) + -> std::vector<DestinationOperandInterface *> { + std::vector<DestinationOperandInterface *> result; + // Get the value of 'rlist', and add destination operands + // accordingly. + auto rlist = p_type::ExtractRlist(inst_word_); + int size = latency.size(); + if (rlist < 4) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x1", latency[result.size() % size])); + if (rlist == 4) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x8", latency[result.size() % size])); + if (rlist == 5) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x9", latency[result.size() % size])); + if (rlist == 6) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x18", latency[result.size() % size])); + if (rlist == 7) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x19", latency[result.size() % size])); + if (rlist == 8) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x20", latency[result.size() % size])); + if (rlist == 9) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x21", latency[result.size() % size])); + if (rlist == 10) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x22", latency[result.size() % size])); + if (rlist == 11) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x23", latency[result.size() % size])); + if (rlist == 12) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x24", latency[result.size() % size])); + if (rlist == 13) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x25", latency[result.size() % size])); + if (rlist == 14) return result; + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x26", latency[result.size() % size])); + result.push_back(GetRegisterDestinationOp<TestRegister>( + state_, "x26", latency[result.size() % size])); + return result; + })); +} + +void PushPopEncoding::ParseInstruction(uint16_t inst_word) { + inst_word_ = inst_word; + opcode_ = DecodePushPop(inst_word_); +} + +SourceOperandInterface *PushPopEncoding::GetSource(SlotEnum, int, OpcodeEnum, + SourceOpEnum op, + int source_no) { + auto iter = source_op_getters_.find(*op); + if (iter == source_op_getters_.end()) { + return nullptr; + } + return iter->second(); +} + +std::vector<SourceOperandInterface *> PushPopEncoding::GetSources( + SlotEnum, int, OpcodeEnum, ListSourceOpEnum op, int source_no) { + auto iter = list_source_op_getters_.find(*op); + if (iter == list_source_op_getters_.end()) { + return {}; + } + return iter->second(); +} + +DestinationOperandInterface *PushPopEncoding::GetDestination( + SlotEnum, int, OpcodeEnum, DestOpEnum op, int dest_no, int latency) { + auto iter = dest_op_getters_.find(*op); + if (iter == dest_op_getters_.end()) { + return nullptr; + } + return iter->second(latency); +} + +std::vector<DestinationOperandInterface *> PushPopEncoding::GetDestinations( + SlotEnum, int, OpcodeEnum, ListDestOpEnum op, int dest_no, + const std::vector<int> &latency) { + auto iter = list_dest_op_getters_.find(*op); + if (iter == list_dest_op_getters_.end()) { + return {}; + } + return iter->second(latency); +} + +} // namespace mpact::sim::decoder::test
diff --git a/mpact/sim/decoder/test/push_pop_encoding.h b/mpact/sim/decoder/test/push_pop_encoding.h new file mode 100644 index 0000000..4dec95e --- /dev/null +++ b/mpact/sim/decoder/test/push_pop_encoding.h
@@ -0,0 +1,84 @@ +// Copyright 2024 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. + +#ifndef MPACT_SIM_DECODER_TEST_PUSH_POP_ENCODING_H_ +#define MPACT_SIM_DECODER_TEST_PUSH_POP_ENCODING_H_ + +#include <cstdint> +#include <vector> + +#include "absl/container/flat_hash_map.h" +#include "absl/functional/any_invocable.h" +#include "mpact/sim/decoder/test/push_pop_inst_decoder.h" +#include "mpact/sim/decoder/test/push_pop_inst_enums.h" +#include "mpact/sim/generic/arch_state.h" +#include "mpact/sim/generic/operand_interface.h" + +// This file defines the encoding class for the push/pop test case isa. +namespace mpact::sim::decoder::test { + +using ::mpact::sim::generic::ArchState; +using ::mpact::sim::generic::DestinationOperandInterface; +using ::mpact::sim::generic::SourceOperandInterface; + +class PushPopEncoding : public PushPopInstEncodingBase { + public: + explicit PushPopEncoding(ArchState *state); + ~PushPopEncoding() override = default; + + void ParseInstruction(uint16_t inst_word); + OpcodeEnum GetOpcode(SlotEnum slot, int entry) override { return opcode_; } + OpcodeEnum opcode() const { return opcode_; } + // Return a single source operand for the given slot, entry, opcode, and + // source operand enum. + SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op, + int source_no) override; + // Expand the ListSourceOpEnum into a vector of source operands. + std::vector<SourceOperandInterface *> GetSources(SlotEnum, int, OpcodeEnum, + ListSourceOpEnum op, + int source_no) override; + // Return a single destination operand for the given slot, entry, opcode, + // destination operand enum, and latency. + DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum, + DestOpEnum op, int dest_no, + int latency) override; + // Expand the ListDestOpEnum into a vector of destination operands. + std::vector<DestinationOperandInterface *> GetDestinations( + SlotEnum, int, OpcodeEnum, ListDestOpEnum op, int dest_no, + const std::vector<int> &latency) override; + + private: + using SourceOpGetterMap = + absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface *()>>; + using DestOpGetterMap = absl::flat_hash_map< + int, absl::AnyInvocable<DestinationOperandInterface *(int)>>; + using ListSourceOpGetterMap = absl::flat_hash_map< + int, absl::AnyInvocable<std::vector<SourceOperandInterface *>()>>; + using ListDestOpGetterMap = absl::flat_hash_map< + int, absl::AnyInvocable<std::vector<DestinationOperandInterface *>( + const std::vector<int> &)>>; + + ArchState *state_; + OpcodeEnum opcode_; + uint16_t inst_word_; + + SourceOpGetterMap source_op_getters_; + DestOpGetterMap dest_op_getters_; + ListSourceOpGetterMap list_source_op_getters_; + ListDestOpGetterMap list_dest_op_getters_; +}; + +} // namespace mpact::sim::decoder::test + +#endif // MPACT_SIM_DECODER_TEST_PUSH_POP_ENCODING_H_
diff --git a/mpact/sim/decoder/test/testfiles/example.isa b/mpact/sim/decoder/test/testfiles/example.isa index 521b2af..3a6cc47 100644 --- a/mpact/sim/decoder/test/testfiles/example.isa +++ b/mpact/sim/decoder/test/testfiles/example.isa
@@ -42,6 +42,8 @@ cvtfs{(pred : sy : dest(base + 1))}; adds{(pred : sy, sx : dest(base))}, attributes: {five = 1, six}; + push{(pred : [rlist]: )}; + pop{(pred : : [rlist])}; addf{(pred : sy, sx : dest(base))}; subf{(pred : sy, sx : dest(base))}; mulf{(pred : sy, sx : dest(mult_plus_2))};
diff --git a/mpact/sim/generic/BUILD b/mpact/sim/generic/BUILD index 11730ac..d38e144 100644 --- a/mpact/sim/generic/BUILD +++ b/mpact/sim/generic/BUILD
@@ -96,6 +96,7 @@ ":program_error", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/status", "@com_google_absl//absl/strings",
diff --git a/mpact/sim/generic/arch_state.cc b/mpact/sim/generic/arch_state.cc index c38c62a..4a82638 100644 --- a/mpact/sim/generic/arch_state.cc +++ b/mpact/sim/generic/arch_state.cc
@@ -17,8 +17,15 @@ #include <string> #include <utility> +#include "absl/container/flat_hash_set.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "mpact/sim/generic/component.h" +#include "mpact/sim/generic/data_buffer.h" #include "mpact/sim/generic/fifo.h" +#include "mpact/sim/generic/function_delay_line.h" +#include "mpact/sim/generic/operand_interface.h" +#include "mpact/sim/generic/program_error.h" #include "mpact/sim/generic/register.h" namespace mpact {