blob: 2a964563b9da033c0d8a00b05b5ce130066bed65 [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.
#ifndef MPACT_SIM_DECODER_PROTO_INSTRUCTION_ENCODING_H_
#define MPACT_SIM_DECODER_PROTO_INSTRUCTION_ENCODING_H_
#include <cstdint>
#include <string>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "mpact/sim/decoder/proto_constraint_expression.h"
#include "mpact/sim/decoder/proto_format_contexts.h"
#include "src/google/protobuf/descriptor.h"
namespace proto2 {
class FieldDescriptor;
}
namespace mpact {
namespace sim {
namespace decoder {
namespace proto_fmt {
enum class ConstraintType : int { kEq = 0, kNe, kLt, kLe, kGt, kGe, kHas };
inline std::string GetOpText(ConstraintType op) {
switch (op) {
case ConstraintType::kEq:
return "==";
case ConstraintType::kNe:
return "!=";
case ConstraintType::kLt:
return "<";
case ConstraintType::kLe:
return "<=";
case ConstraintType::kGt:
return ">";
case ConstraintType::kGe:
return ">=";
case ConstraintType::kHas:
return "";
}
}
// Struct to store information about an encoding constraint for an instruction.
struct ProtoConstraint {
// Parsing context.
FieldConstraintCtx* ctx;
// The proto field descriptor for which the constraint applies.
const google::protobuf::FieldDescriptor* field_descriptor;
// The constraint type.
ConstraintType op;
// If non-null, the expression that applies to the constraint.
const ProtoConstraintExpression* expr;
// If the value is compatible with int64_t, the value of the expression. This
// is filled in later when the expression is evaluated for decoding purposes.
int64_t value;
// If non-null, points to a constraint that has to be true before one can
// evaluate this constraint.
ProtoConstraint* depends_on;
// Constructors.
ProtoConstraint(FieldConstraintCtx* ctx,
const google::protobuf::FieldDescriptor* field_descriptor,
ConstraintType op, const ProtoConstraintExpression* expr,
int64_t value, ProtoConstraint* depends_on)
: ctx(ctx),
field_descriptor(field_descriptor),
op(op),
expr(expr),
value(value),
depends_on(depends_on) {}
ProtoConstraint(FieldConstraintCtx* ctx,
const google::protobuf::FieldDescriptor* field_descriptor,
ConstraintType op)
: ProtoConstraint(ctx, field_descriptor, op, nullptr, 0, nullptr) {}
// Copy constructor.
ProtoConstraint(const ProtoConstraint& rhs) {
this->ctx = rhs.ctx;
this->field_descriptor = rhs.field_descriptor;
this->op = rhs.op;
this->expr = rhs.expr != nullptr ? rhs.expr->Clone() : nullptr;
this->value = rhs.value;
this->depends_on = rhs.depends_on;
}
};
// Struct to store information about a setter for an instruction encoding.
struct ProtoSetter {
// Proto setter context.
SetterDefCtx* ctx;
// The name of the object that is set.
std::string name;
// The field that will provide the type and value of the object.
const google::protobuf::FieldDescriptor* field_descriptor;
// Default value of the object if the field descriptor is not valid.
IfNotCtx* if_not;
// If non-null, points to constraint that has to be true in order to access
// the value of the field described by field_descriptor.
ProtoConstraint* depends_on;
};
class ProtoInstructionGroup;
// This class captures all the encoding constraints and the setters for one
// instruction in an instruction group.
class ProtoInstructionEncoding {
public:
ProtoInstructionEncoding(std::string name, ProtoInstructionGroup* parent);
ProtoInstructionEncoding(const ProtoInstructionEncoding& rhs);
ProtoInstructionEncoding() = delete;
ProtoInstructionEncoding& operator=(
const ProtoInstructionEncoding& encoding) = delete;
~ProtoInstructionEncoding();
// Adds a value setter that is executed when the instruction is successfully
// decoded. This is used to make values, such as register numbers, immediate
// values, etc., that could be stored in a nested one_of submessage, available
// at known names.
absl::Status AddSetter(
SetterDefCtx* ctx, const std::string& name,
const google::protobuf::FieldDescriptor* field_descriptor,
const std::vector<const google::protobuf::FieldDescriptor*>&
one_of_fields,
IfNotCtx* if_not);
// Adds an encoding constraint for the current instruction. Encoding
// constraints provide constraints on values of proto message fields that
// have to be satisfied in order for the instruction to match.
absl::Status AddConstraint(
FieldConstraintCtx* ctx, ConstraintType op,
const google::protobuf::FieldDescriptor* field_descriptor,
const std::vector<const google::protobuf::FieldDescriptor*>&
one_of_fields,
const ProtoConstraintExpression* expr);
// Call when the setters and constraints have been added in order to generate
// the setter code into the setter_code_ variable.
void GenerateSetterCode();
// Get setter code, substituting 'message_name' for '$' in the text.
std::string GetSetterCode(absl::string_view message_name, int indent) const;
// Getters.
const std::string& name() const { return name_; }
ProtoInstructionGroup* instruction_group() const {
return instruction_group_;
}
std::vector<ProtoConstraint*>& equal_constraints() {
return equal_constraints_;
}
std::vector<ProtoConstraint*>& other_constraints() {
return other_constraints_;
}
absl::flat_hash_map<std::string, ProtoConstraint*>& has_constraints() {
return has_constraints_;
}
std::string setter_code() const { return setter_code_; }
private:
// Returns a pointer to the constraint for field_descriptor if it exists. If
// it does not exist, it creates it and adds it to the has_constraints and
// returns a pointer to the new constraint. If depends_on is not nullptr,
// then it is required that the depends_on constraint exists in the
// has_constraints_ map. This is checked by searching for the full_name of
// the field_descriptor in the depends_on constraint.
ProtoConstraint* AddHasConstraint(
const google::protobuf::FieldDescriptor* field_descriptor,
ProtoConstraint* depends_on);
// Instruction name.
std::string name_;
// Parent instruction group.
ProtoInstructionGroup* instruction_group_ = nullptr;
// Setter code for this encoding.
std::string setter_code_;
// Map from setter names to the setter structs.
absl::btree_map<std::string, ProtoSetter*> setter_map_;
// Map from one_of descriptor to field.
absl::flat_hash_map<const google::protobuf::OneofDescriptor*,
const google::protobuf::FieldDescriptor*>
oneof_field_map_;
// "equal-to" field constraints.
std::vector<ProtoConstraint*> equal_constraints_;
// All other constraints.
std::vector<ProtoConstraint*> other_constraints_;
// Has Constraints, these are required one_of members that other constraints
// may depend on.
absl::flat_hash_map<std::string, ProtoConstraint*> has_constraints_;
};
} // namespace proto_fmt
} // namespace decoder
} // namespace sim
} // namespace mpact
#endif // MPACT_SIM_DECODER_PROTO_INSTRUCTION_ENCODING_H_