blob: 500538819fd3b194bfce41b18d3616f6c1fd0085 [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/format.h"
#include <algorithm>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/numeric/bits.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "antlr4-runtime/Token.h"
#include "mpact/sim/decoder/format_name.h"
#include "mpact/sim/decoder/overlay.h"
namespace mpact {
namespace sim {
namespace decoder {
namespace bin_format {
using ::mpact::sim::machine_description::instruction_set::ToSnakeCase;
FieldOrFormat::~FieldOrFormat() {
if (is_field_) {
delete field_;
}
field_ = nullptr;
format_ = nullptr;
}
// The equality operator compares to verify that the field/format definitions
// are equivalent, i.e., refers to the same bits.
bool FieldOrFormat::operator==(const FieldOrFormat &rhs) const {
if (is_field_ != rhs.is_field_) return false;
if (is_field_) {
if (high_ != rhs.high_) return false;
if (size_ != rhs.size_) return false;
} else {
if (format_ != rhs.format_) return false;
}
return true;
}
bool FieldOrFormat::operator!=(const FieldOrFormat &rhs) const {
return !(*this == rhs);
}
using ::mpact::sim::machine_description::instruction_set::ToPascalCase;
Format::Format(std::string name, int width, BinEncodingInfo *encoding_info)
: Format(name, width, "", encoding_info) {}
Format::Format(std::string name, int width, std::string base_format_name,
BinEncodingInfo *encoding_info)
: name_(name),
base_format_name_(base_format_name),
declared_width_(width),
encoding_info_(encoding_info) {}
Format::~Format() {
// for (auto &[unused, field_ptr] : field_map_) {
// delete field_ptr;
// }
field_map_.clear();
for (auto &[unused, overlay_ptr] : overlay_map_) {
delete overlay_ptr;
}
overlay_map_.clear();
for (auto *field : field_vec_) {
delete field;
}
field_vec_.clear();
}
// Add a field to the current format with the given width and signed/unsigned
// attribute.
absl::Status Format::AddField(std::string name, bool is_signed, int width) {
// Make sure that the name isn't used already in the format.
if (field_map_.contains(name)) {
return absl::InvalidArgumentError(
absl::StrCat("Field '", name, "' already defined"));
}
auto field = new Field(name, is_signed, width, this);
field_vec_.push_back(new FieldOrFormat(field));
field_map_.insert(std::make_pair(name, field));
return absl::OkStatus();
}
// Add a format reference field - name of another format - to the current
// format. This will be resolved once all the formats have been parsed.
void Format::AddFormatReferenceField(std::string format_alias,
std::string format_name, int size,
antlr4::Token *ctx) {
field_vec_.push_back(new FieldOrFormat(format_alias, format_name, size, ctx));
}
// Add an overlay to the current format. An overlay is a named alias for a
// not necessarily contiguous nor in order collection of bits in the format.
absl::StatusOr<Overlay *> Format::AddFieldOverlay(std::string name,
bool is_signed, int width) {
// Make sure that the name isn't used already in the format.
if (overlay_map_.contains(name)) {
return absl::InvalidArgumentError(
absl::StrCat("Overlay '", name, "' already defined as an overlay"));
}
if (field_map_.contains(name)) {
return absl::InvalidArgumentError(
absl::StrCat("Overlay '", name, "' already defined as a field"));
}
auto overlay = new Overlay(name, is_signed, width, this);
overlay_map_.insert(std::make_pair(name, overlay));
return overlay;
}
// Return the named field if it exists, nullptr otherwise.
Field *Format::GetField(absl::string_view field_name) const {
auto iter = field_map_.find(field_name);
if (iter == field_map_.end()) return nullptr;
return iter->second;
}
// Return the named field if it exists, nullptr otherwise.
Overlay *Format::GetOverlay(absl::string_view overlay_name) const {
auto iter = overlay_map_.find(overlay_name);
if (iter == overlay_map_.end()) return nullptr;
return iter->second;
}
// Return the string containing the integer type used to contain the current
// format. If it is greater than 128 bits, will use a byte array (int8_t *).
// If it is 65 to 128 bits, will use absl::[u]int128.
std::string Format::GetUIntType(int bitwidth) const {
if (bitwidth > 128) return "uint8_t *";
if (bitwidth > 64) return "absl::uint128";
return absl::StrCat("uint", GetIntTypeBitWidth(bitwidth), "_t");
}
std::string Format::GetIntType(int bitwidth) const {
if (bitwidth > 128) return "int8_t *";
if (bitwidth > 64) return "absl::int128";
return absl::StrCat("int", GetIntTypeBitWidth(bitwidth), "_t");
}
// Return the int type byte width (1, 2, 4, 8, 16) or (-1 if it's bigger), of
// the integer type that would fit this format.
int Format::GetIntTypeBitWidth(int bitwidth) const {
auto shift = absl::bit_width(static_cast<unsigned>(bitwidth)) - 1;
if (absl::popcount(static_cast<unsigned>(bitwidth)) > 1) shift++;
shift = std::max(shift, 3);
if (shift > 7) return -1;
return 1 << shift;
}
// Once all the formats have been read in, this method is called to check the
// format and update any widths that depended on other formats being read in.
absl::Status Format::ComputeAndCheckFormatWidth() {
// If there is a base format name, look up that format, verify that the widths
// are the same.
if (!base_format_name_.empty()) {
auto *base_format = encoding_info_->GetFormat(base_format_name_);
if (base_format == nullptr) {
return absl::InternalError(
absl::StrCat("Format ", name(), " refers to undefined base format ",
base_format_name_));
}
if (base_format->declared_width() != declared_width_) {
return absl::InternalError(absl::StrCat(
"Format ", name_, " (", declared_width_,
") differs in width from base format ", base_format->name(), " (",
base_format->declared_width(), ")"));
}
base_format_ = base_format;
base_format_->derived_formats_.push_back(this);
}
if (computed_width_ == 0) {
// Go through the list of fields/format references. Get the declared widths
// of the formats and add to the computed width. Signal error if the
// computed width differs from the declared width.
for (auto *field_or_format : field_vec_) {
// Field.
if (field_or_format->is_field()) {
auto *field = field_or_format->field();
field->high = declared_width_ - computed_width_ - 1;
field->low = field->high - field->width + 1;
computed_width_ += field->width;
extractors_.insert(std::make_pair(field->name, field_or_format));
continue;
}
// Format;
auto *format = field_or_format->format();
if (format == nullptr) {
std::string fmt_name = field_or_format->format_name();
format = encoding_info_->GetFormat(fmt_name);
if (format == nullptr) {
return absl::InternalError(absl::StrCat(
"Format '", name(), "' refers to undefined format ", fmt_name));
}
field_or_format->set_format(format);
}
field_or_format->set_high(declared_width_ - computed_width_ - 1);
computed_width_ += format->declared_width() * field_or_format->size();
extractors_.insert(
std::make_pair(field_or_format->format_alias(), field_or_format));
}
if (computed_width_ != declared_width_) {
return absl::InternalError(absl::StrCat(
"Format '", name_, "' declared width (", declared_width_,
") differs from computed width (", computed_width_, ")"));
}
}
for (auto &[name, overlay_ptr] : overlay_map_) {
auto status = overlay_ptr->ComputeHighLow();
if (!status.ok()) return status;
overlay_extractors_.insert(std::make_pair(name, overlay_ptr));
}
// Set the type names.
int_type_name_ = GetIntType(declared_width_);
uint_type_name_ = absl::StrCat("u", int_type_name_);
return absl::OkStatus();
}
// The extractor functions in the generated code are all generated within a
// namespace specific to the format they're associated with. However, extractors
// that don't conflict in the bits they select may be promoted to be generated
// in the base format namespace. This method is used to propagate such
// potential promotions upward in the inheritance tree.
void Format::PropagateExtractorsUp() {
for (auto *fmt : derived_formats_) {
fmt->PropagateExtractorsUp();
}
if (base_format_ != nullptr) {
// Try to propagate extractors up the inheritance tree.
for (auto const &[name, field_or_format_ptr] : extractors_) {
// Ignore those that have a nullptr, they have already failed to be
// promoted.
if (field_or_format_ptr == nullptr) continue;
auto iter = base_format_->extractors_.find(name);
// If it isn't in the parent, add it.
if (iter == base_format_->extractors_.end()) {
base_format_->extractors_.insert(
std::make_pair(name, field_or_format_ptr));
} else if (iter->second == nullptr) {
// Can't promote it, a previous attempt failed.
continue;
} else if (*field_or_format_ptr != *(iter->second)) {
// If the base extractor refers to a different object, fail the
// promotion.
base_format_->extractors_[name] = nullptr;
}
}
for (auto const &[name, overlay_ptr] : overlay_extractors_) {
// Ignore those that have a nullptr, they have already failed to be
// promoted.
if (overlay_ptr == nullptr) continue;
auto iter = base_format_->overlay_extractors_.find(name);
// If it isn't in the parent, add it.
if (iter == base_format_->overlay_extractors_.end()) {
base_format_->overlay_extractors_.insert(
std::make_pair(name, overlay_ptr));
} else if (iter->second == nullptr) {
// Previous attempt fail, don't promote.
continue;
} else if (*overlay_ptr != *(iter->second)) {
// If the base format extractor refers to a different overlay type,
// fail the promotion.
base_format_->overlay_extractors_[name] = nullptr;
}
}
}
}
// This is the counterpart to the previous method and cleans up extractors that
// were attempted to be promoted, but couldn't be due to conflicts with others,
// e.g., two fields were named the same in different formats but referred to
// different bits.
void Format::PropagateExtractorsDown() {
// Remove the extractor entries with null ptrs and any extractors that
// have been promoted.
auto e_iter = extractors_.begin();
while (e_iter != extractors_.end()) {
auto cur = e_iter++;
// Failed promotion from derived format extractors.
if (cur->second == nullptr) {
extractors_.erase(cur);
continue;
}
// If the name exists in overlay extractors, erase both.
if (overlay_extractors_.find(cur->first) != overlay_extractors_.end()) {
overlay_extractors_.erase(cur->first);
extractors_.erase(cur);
continue;
}
}
// Remove the overlay extractor entries with null ptrs.
auto o_iter = overlay_extractors_.begin();
while (o_iter != overlay_extractors_.end()) {
auto cur = o_iter++;
// Failed promotion from derived format extractors.
if (cur->second == nullptr) {
overlay_extractors_.erase(cur);
continue;
}
// If the name exists in overlay extractors, erase both.
if (extractors_.find(cur->first) != extractors_.end()) {
extractors_.erase(cur->first);
overlay_extractors_.erase(cur);
continue;
}
}
for (auto *fmt : derived_formats_) {
fmt->PropagateExtractorsDown();
}
}
// Returns true if the current format, or a base format, contains an
// extractor for field 'name'.
bool Format::HasExtract(const std::string &name) const {
auto iter = extractors_.find(name);
if ((iter != extractors_.end()) && (iter->second != nullptr)) return true;
if (base_format_ != nullptr) return base_format_->HasExtract(name);
return false;
}
// Returns true if the current format, or a base format, contains an
// extractor for overlay 'name'.
bool Format::HasOverlayExtract(const std::string &name) const {
auto iter = overlay_extractors_.find(name);
if ((iter != overlay_extractors_.end()) && (iter->second != nullptr)) {
return true;
}
if (base_format_ != nullptr) return base_format_->HasOverlayExtract(name);
return false;
}
std::string Format::GeneratePackedStructFieldExtractor(
const Field *field) const {
std::string h_output;
int width = field->width;
std::string return_type = GetUIntType(width);
std::string signature = absl::StrCat("inline ", return_type, " Extract",
ToPascalCase(field->name), "(");
if (declared_width_ < 64) {
absl::StrAppend(&signature, GetUIntType(declared_width_), " value) {\n");
} else {
absl::StrAppend(&signature, "const uint8_t *value) {\n");
}
absl::StrAppend(&h_output, signature);
// Now start the body.
std::string union_type = absl::StrCat("const ", ToSnakeCase(name()),
"::Union", ToPascalCase(name()));
absl::StrAppend(&h_output, " ", union_type,
" *packed_union;\n"
" packed_union = reinterpret_cast<",
union_type, "*>(",
declared_width_ > 64 ? "value);\n" : "&value);\n",
" return packed_union->", ToSnakeCase(name()), ".",
field->name, ";\n}\n\n");
return h_output;
}
// This method generates the C++ code for the field extractors for the current
// format.
std::string Format::GenerateFieldExtractor(const Field *field) const {
std::string h_output;
int return_width = GetIntTypeBitWidth(field->width);
std::string result_type_name =
field->is_signed ? GetIntType(return_width) : GetUIntType(return_width);
std::string argument_type_name = GetUIntType(computed_width_);
std::string signature = absl::StrCat(
result_type_name, " Extract", ToPascalCase(field->name), "(",
computed_width_ > 128 ? "const " : "", argument_type_name, " value)");
absl::StrAppend(&h_output, "inline ", signature, " {\n");
// Generate extraction function. For fields it's a simple shift and mask if
// the source format width <= 64 bits. Slightly more involved with format
// <= 128 bits. For larger formats use the templated extract helper function.
std::string expr;
if (declared_width_ <= 64) {
uint64_t mask = (1ULL << field->width) - 1;
if (field->low == 0) {
expr = absl::StrCat("value & 0x", absl::Hex(mask));
} else {
expr = absl::StrCat(" (value >> ", field->low, ") & 0x", absl::Hex(mask));
}
} else if (declared_width_ <= 128) {
absl::StrAppend(&h_output,
" absl::uint128 mask = 1;\n"
" mask = (mask << ",
field->width, ") - 1;\n");
if (field->low == 0) {
expr = absl::StrCat("value & mask");
} else {
expr = absl::StrCat(" (value >> ", field->low, ") & mask");
}
} else {
// For format width > 128 bits, use the templated extract helper function.
int byte_size = (declared_width_ + 7) / 8;
expr = absl::StrCat("internal::ExtractBits<", result_type_name, ">(value, ",
byte_size, ", ", field->high, ", ", field->width, ")");
}
// Add sign-extension if the field is signed.
std::string sign_extension;
if (field->is_signed) {
int shift = return_width - field->width;
sign_extension =
absl::StrCat(" ", result_type_name, " result = (", expr, ") << ",
shift, ";\n result = result >> ", shift, ";\n");
expr = "result";
}
if (declared_width_ <= 64) {
absl::StrAppend(&h_output, sign_extension, " return ", expr, ";\n}\n\n");
} else if ((declared_width_ <= 128) && (return_width <= 64)) {
absl::StrAppend(&h_output, sign_extension, " return absl::Uint128Low64(",
expr, ");\n}\n\n");
} else {
absl::StrAppend(&h_output, sign_extension, " return ", expr, ";\n}\n\n");
}
// If the parent format size is not a power of two, also create an extractor
// that takes a uint8_t * parameter.
if ((declared_width_ <= 128) &&
(absl::popcount(static_cast<unsigned>(declared_width_)) > 1)) {
absl::StrAppend(&h_output, "inline ", result_type_name, " Extract",
ToPascalCase(field->name), "(const uint8_t *value) {\n");
int byte_size = (declared_width_ + 7) / 8;
absl::StrAppend(&h_output, " return internal::ExtractBits<",
result_type_name, ">(value, ", byte_size, ", ", field->high,
", ", field->width, ");\n}\n\n");
}
return h_output;
}
std::string Format::GeneratePackedStructFieldInserter(
const Field *field) const {
std::string h_output;
std::string field_type_name;
std::string inst_word_type_name;
if (computed_width_ <= 64) {
inst_word_type_name = GetUIntType(computed_width_);
} else {
inst_word_type_name = "uint8_t *";
}
field_type_name = GetUIntType(field->width);
std::string union_type =
absl::StrCat(ToSnakeCase(name()), "::Union", ToPascalCase(name()));
absl::StrAppend(&h_output, "static inline ", inst_word_type_name, " Insert",
ToPascalCase(field->name), "(", field_type_name, " value, ",
inst_word_type_name,
" inst_word) {\n"
" ",
union_type,
" *packed_union;\n"
" packed_union = reinterpret_cast<",
union_type, "*>(",
(computed_width_ <= 64 ? "&inst_word" : "inst_word"),
");\n"
" packed_union->",
ToSnakeCase(name()), ".", field->name,
" = value;\n"
" return inst_word;\n"
"}\n\n");
return h_output;
}
// This method generates the C++ code for field inserters for the current
// format. That is, the generated code will take the value of a field and
// insert it into the right place in the instruction word.
std::string Format::GenerateFieldInserter(const Field *field) const {
std::string h_output;
std::string field_type_name;
std::string inst_word_type_name = GetUIntType(computed_width_);
if (declared_width_ <= 128) {
field_type_name = inst_word_type_name;
} else {
field_type_name = GetUIntType(field->width);
}
absl::StrAppend(&h_output, "static inline ", inst_word_type_name, " Insert",
ToPascalCase(field->name), "(", field_type_name, " value, ",
inst_word_type_name, " inst_word) {\n");
if (declared_width_ <= 64) {
uint64_t mask = ((1ULL << field->width) - 1) << field->low;
std::string shift;
if (field->low != 0) {
shift = absl::StrCat(" << ", field->low);
}
absl::StrAppend(&h_output, " inst_word = (inst_word & ~0x",
absl::Hex(mask), "ULL)", " | ((value", shift, ") & 0x",
absl::Hex(mask),
"ULL);\n"
" return inst_word;\n"
"}\n");
} else if (declared_width_ <= 128) {
absl::StrAppend(&h_output,
" absl::uint128 mask = 1;\n"
" mask = (mask << ",
field->width, ") - 1;\n");
if (field->low != 0) {
absl::StrAppend(&h_output, " mask = mask << ", field->low, ";\n");
}
absl::StrAppend(&h_output,
" inst_word = (inst_word & ~mask) | (value & mask);\n"
" return inst_word;\n"
"}\n");
} else if (field->width <= 128) {
int byte_size = (declared_width_ + 7) / 8;
absl::StrAppend(&h_output, " internal::InsertBits(inst_word, ", byte_size,
", ", field->high, ", ", field->width,
", value);\n"
" return inst_word;\n"
"}\n");
} else {
absl::StrAppend(&h_output,
" LOG(FATAL) << \" Support for fields > 128 bits not "
"implemented - "
"yet.\";\n"
" return 0;\n}\n");
}
return h_output;
}
// This method generates the C++ code for overlay inserters for the current
// format. That is, the generated code will take the value of an overlay and
// insert its components into the right places in the instruction word.
std::string Format::GenerateOverlayInserter(Overlay *overlay) const {
std::string h_output;
std::string result_type_name = GetUIntType(computed_width_);
std::string overlay_type_name;
if (computed_width_ <= 128) {
overlay_type_name = result_type_name;
} else {
overlay_type_name = GetUIntType(overlay->declared_width());
}
absl::StrAppend(&h_output, "static inline ", result_type_name, " Insert",
ToPascalCase(overlay->name()), "(", overlay_type_name,
" value, ", result_type_name, " inst_word) {\n");
// Mark error if either the overlay or the format is > 64 bits.
if (overlay->declared_width() > 128) {
absl::StrAppend(&h_output,
" LOG(FATAL) << \" Support for overlays > 128 bits "
"not implemented - "
"yet.\";\n"
" return 0;\n}\n");
return h_output;
}
bool use_mask_variable = false;
int remaining = overlay->declared_width();
int byte_size = (declared_width_ + 7) / 8;
if (declared_width_ <= 128) {
absl::StrAppend(&h_output, " ", result_type_name, " tmp;\n");
// Track the leftmost bit in the overlay.
if (declared_width_ > 64) {
absl::StrAppend(&h_output, " absl::uint128 mask;\n");
use_mask_variable = true;
}
} else {
absl::StrAppend(&h_output, " ", overlay_type_name, " tmp;\n");
if (overlay->declared_width() > 64) {
absl::StrAppend(&h_output, " absl::uint128 mask;\n");
use_mask_variable = true;
}
}
for (auto &bits_or_field : overlay->component_vec()) {
int width = bits_or_field->width();
// Ignore the bit fields in the overlay.
if (bits_or_field->high() < 0) {
remaining -= width;
continue;
}
std::string shift;
if (remaining - width > 0) {
shift = absl::StrCat(" >> ", remaining - width);
}
if (use_mask_variable) {
absl::StrAppend(&h_output,
" mask = 1;\n"
" mask = (mask << ",
width, ") - 1;\n");
absl::StrAppend(&h_output, " tmp = (value ", shift, ") & mask;\n");
} else {
uint64_t mask = ((1ULL << width) - 1);
// Extract the bits from the overlay value for the current component.
absl::StrAppend(&h_output, " tmp = (value ", shift, ") & 0x",
absl::Hex(mask), "ULL;\n");
}
shift.clear();
if (bits_or_field->low() != 0) {
shift = absl::StrCat(" << ", bits_or_field->low());
}
if (declared_width_ <= 128) {
absl::StrAppend(&h_output, " inst_word |= (tmp ", shift, ");\n");
} else {
absl::StrAppend(&h_output, " internal::InsertBits(inst_word, ",
byte_size, ", ", bits_or_field->high(), ", ", width,
", tmp);\n");
}
remaining -= width;
}
absl::StrAppend(&h_output, " return inst_word;\n}\n");
return h_output;
}
std::string Format::GeneratePackedStructFormatInserter(
std::string_view format_alias, const Format *format, int high,
int size) const {
std::string h_output;
std::string inst_word_type_name;
if (computed_width_ <= 64) {
inst_word_type_name = GetUIntType(computed_width_);
} else {
inst_word_type_name = "uint8_t *";
}
std::string format_type_name = GetUIntType(format->declared_width());
std::string union_type =
absl::StrCat(ToSnakeCase(name()), "::Union", ToPascalCase(name()));
absl::StrAppend(&h_output, "static inline ", inst_word_type_name, "Insert",
ToPascalCase(format_alias), "(", format_type_name, " value, ",
inst_word_type_name,
" inst_word) {\n"
" ",
union_type,
" *packed_union;\n"
" packed_union = reinterpret_cast<",
union_type, "*>(",
(computed_width_ <= 64 ? "&inst_word" : "inst_word"),
");\n"
" packed_union->",
ToSnakeCase(name()), ".", format_alias,
" = value;\n"
" return inst_word;\n"
"}\n\n");
return h_output;
}
// This method generates the C++ code for format inserters for the current
// format. That is, the generated code will take the value of a format and
// insert it into the right place in the instruction word.
std::string Format::GenerateFormatInserter(std::string_view format_alias,
const Format *format, int high,
int size) const {
if (size > 1) {
return GenerateReplicatedFormatInserter(format_alias, format, high, size);
}
return GenerateSingleFormatInserter(format_alias, format, high);
}
std::string Format::GenerateReplicatedFormatInserter(
std::string_view format_alias, const Format *format, int high,
int size) const {
std::string h_output;
std::string target_type_name = GetUIntType(declared_width_);
std::string format_type_name;
if (declared_width_ <= 128) {
format_type_name = target_type_name;
} else {
format_type_name = GetUIntType(format->declared_width());
}
absl::StrAppend(&h_output, "static inline ", target_type_name, " Insert",
ToPascalCase(format_alias), "(", "int index, ",
format_type_name, " value, ", target_type_name,
" inst_word) {\n");
if (format->declared_width() > 128) {
absl::StrAppend(&h_output,
" LOG(FATAL) << \" Support for formats > 128 bits not "
"implemented - "
"yet.\";\n"
" return 0;\n}\n");
return h_output;
}
int width = format->declared_width();
int low = high - width + 1;
if (declared_width_ <= 64) {
uint64_t mask = (1ULL << width) - 1;
absl::StrAppend(&h_output, " int low = ", low, " - (index * ", width,
");\n"
" return (inst_word & (~0x",
absl::Hex(mask), "ULL << low))", " | ((value << low) & (0x",
absl::Hex(mask), "ULL << low));\n}\n");
} else if (declared_width_ <= 128) {
absl::StrAppend(
&h_output, " int low = ", low, " - (index * ", width,
");\n"
" absl::uint128 mask = 1;\n"
" mask = (mask << ",
width,
") - 1;\n"
" mask <<= low;\n"
" return (inst_word & ~mask) | (value << low) & mask;\n}\n");
} else {
int byte_size = (declared_width_ + 7) / 8;
absl::StrAppend(&h_output, " internal::InsertBits(inst_word, ", byte_size,
", ", high, " - (index * ", width, "), ", width,
", value);\n"
" return inst_word;\n}\n");
}
return h_output;
}
std::string Format::GenerateSingleFormatInserter(std::string_view format_alias,
const Format *format,
int high) const {
std::string h_output;
std::string target_type_name = GetUIntType(declared_width_);
std::string format_type_name;
if (declared_width_ <= 128) {
format_type_name = target_type_name;
} else {
format_type_name = GetUIntType(format->declared_width());
}
absl::StrAppend(&h_output, "static inline ", target_type_name, " Insert",
ToPascalCase(format_alias), "(", format_type_name, " value, ",
target_type_name, " inst_word) {\n");
if (format->declared_width() > 128) {
absl::StrAppend(&h_output,
" LOG(FATAL) << \" Support for formats > 128 bits not "
"implemented - "
"yet.\";\n"
" return 0;\n}\n");
return h_output;
}
int width = format->declared_width();
int low = high - width + 1;
std::string shift;
if (low != 0) {
shift = absl::StrCat(" << ", low);
}
if (declared_width_ <= 64) {
uint64_t mask = ((1ULL << width) - 1) << low;
absl::StrAppend(&h_output, " return (inst_word & (~0x", absl::Hex(mask),
"ULL))", " | ((value ", shift, ") & 0x", absl::Hex(mask),
"ULL);\n}\n");
} else if (declared_width_ <= 128) {
absl::StrAppend(&h_output,
" absl::uint128 mask = 1;\n"
" mask = (mask << ",
width, ") - 1;\n");
if (low > 0) {
absl::StrAppend(&h_output, " mask = mask << ", low, ";\n");
}
absl::StrAppend(&h_output, " return (inst_word & ~mask) | (value ", shift,
") & mask;\n}\n");
} else {
int byte_size = (declared_width_ + 7) / 8;
absl::StrAppend(&h_output, " internal::InsertBits(inst_word, ", byte_size,
", ", high, ", ", width,
", value);\n"
" return inst_word;\n}\n");
}
return h_output;
}
std::string Format::GeneratePackedStructFormatExtractor(
absl::string_view format_alias, const Format *format, int high,
int size) const {
std::string h_output;
int width = format->declared_width();
std::string return_type = GetUIntType(width);
std::string signature = absl::StrCat("inline ", return_type, " Extract",
ToPascalCase(format_alias), "(");
if (declared_width_ < 64) {
absl::StrAppend(&signature, GetUIntType(declared_width_), " value) {\n");
} else {
absl::StrAppend(&signature, "const uint8_t *value) {\n");
}
absl::StrAppend(&h_output, signature);
// Now start the body.
std::string union_type = absl::StrCat("const ", ToSnakeCase(name()),
"::Union", ToPascalCase(name()));
absl::StrAppend(&h_output, " ", union_type,
" *packed_union;\n"
" packed_union = reinterpret_cast<",
union_type, " *>(",
declared_width_ > 64 ? "value);\n" : "&value);\n",
" return packed_union->", ToSnakeCase(name()), ".",
format_alias, ";\n}\n\n");
return h_output;
}
// This method generates the format extractors for the current format (for
// when a format contains other formats).
std::string Format::GenerateFormatExtractor(absl::string_view format_alias,
const Format *format, int high,
int size) const {
std::string h_output; // For each format generate an extractor.
int width = format->declared_width();
// An extraction can only be for 128 bits or less.
if (width > 128) {
encoding_info_->error_listener()->semanticError(
nullptr,
absl::StrCat("Cannot generate a format extractor for format '",
format->name(), "': format is wider than 128 bits"));
return "";
}
std::string return_type = GetUIntType(width);
std::string signature = absl::StrCat("inline ", return_type, " Extract",
ToPascalCase(format_alias), "(");
if (declared_width_ <= 128) {
// If the source format is <= 128 bits, then use an int type.
std::string arg_type = GetUIntType(declared_width_);
absl::StrAppend(&signature, arg_type, " value");
} else {
// Otherwise use a pointer to uint8_t type.
absl::StrAppend(&signature, "const uint8_t *value");
}
// If the format has multiple instances add an index parameter.
if (size > 1) {
absl::StrAppend(&signature, ", int index");
}
absl::StrAppend(&signature, ")");
// Now start the body.
absl::StrAppend(&h_output, signature, " {\n");
std::string expr;
if (declared_width_ <= 128) {
// If the source format can be stored in a uint128 or smaller.
int low = high - width + 1;
std::string shift;
if (size > 1) {
shift = absl::StrCat("(", low, " + (index - 1) * ", width, ")");
} else {
shift = absl::StrCat(low);
}
if (declared_width_ <= 64) {
uint64_t mask = (1ULL << width) - 1;
expr =
absl::StrCat("(value >> ", shift, ") & 0x", absl::Hex(mask), ";\n");
absl::StrAppend(&h_output, " return ", expr, ";\n}\n\n");
} else {
absl::StrAppend(&h_output,
" absl::uint128 mask = 1;\n"
" mask = (mask << ",
width, ") - 1;\n");
expr = absl::StrCat("(value >> ", shift, ") & mask");
if (width <= 64) {
absl::StrAppend(&h_output, " return absl::Uint128Low64(", expr,
");\n}\n\n");
} else {
absl::StrAppend(&h_output, " return ", expr, ";\n}\n\n");
}
}
} else {
// If the source format is stored in uint8_t[].
int byte_size = (declared_width_ + 7) / 8;
expr = absl::StrCat("internal::ExtractBits<", return_type, ">(value, ",
byte_size, ", ", high);
if (size > 1) {
absl::StrAppend(&expr, " - (index * ", width, ")");
}
absl::StrAppend(&expr, ", ", width, ")");
absl::StrAppend(&h_output, " return ", expr, ";\n}\n\n");
}
// If the parent format size is not a power of two, also create an
// extractor that takes a uint8_t * parameter.
if ((declared_width_ <= 128) &&
(absl::popcount(static_cast<unsigned>(declared_width_)) > 1)) {
absl::StrAppend(&h_output, "inline ", return_type, " Extract",
ToPascalCase(format_alias), "(const uint8_t *value) {\n");
int byte_size = (declared_width_ + 7) / 8;
expr = absl::StrCat("internal::ExtractBits<", return_type, ">(value, ",
byte_size, ", ", high);
if (size > 1) {
absl::StrAppend(&expr, " - (index * ", width, ")");
}
absl::StrAppend(&expr, ", ", width, ")");
absl::StrAppend(&h_output, " return ", expr, ";\n}\n\n");
}
return h_output;
}
std::string Format::GeneratePackedStructOverlayExtractor(
Overlay *overlay) const {
std::string h_output;
std::string arg_type;
if (declared_width_ > 128) {
arg_type = "const uint8_t *";
} else {
arg_type = GetUIntType(declared_width_);
}
std::string return_type = overlay->is_signed()
? GetIntType(overlay->declared_width())
: GetUIntType(overlay->declared_width());
std::string signature =
absl::StrCat("inline ", return_type, " Extract",
ToPascalCase(overlay->name()), "(", arg_type, " value)");
absl::StrAppend(&h_output, signature, " {\n ", return_type, " result;\n",
overlay->WritePackedStructValueExtractor("value", "result"));
if (overlay->is_signed()) {
int shift = GetIntTypeBitWidth(overlay->declared_width()) -
overlay->declared_width();
absl::StrAppend(&h_output, " result = result << ", shift,
";\n"
" result = result >> ",
shift, ";\n");
}
absl::StrAppend(&h_output,
" return result;\n"
"}\n\n");
return h_output;
}
// Generates the C++ code for the overlay extractors in the current format.
std::string Format::GenerateOverlayExtractor(Overlay *overlay) const {
std::string h_output;
std::string return_type = overlay->is_signed()
? GetIntType(overlay->declared_width())
: GetUIntType(overlay->declared_width());
std::string arg_type = GetUIntType(declared_width_);
std::string signature =
absl::StrCat("inline ", return_type, " Extract",
ToPascalCase(overlay->name()), "(", arg_type, " value)");
// Generate definition.
absl::StrAppend(&h_output, signature,
" {\n"
" ",
return_type, " result;\n");
if (declared_width_ <= 64) {
absl::StrAppend(&h_output,
overlay->WriteSimpleValueExtractor("value", "result"));
} else {
absl::StrAppend(&h_output, overlay->WriteComplexValueExtractor(
"value", "result", return_type));
}
if (overlay->is_signed()) {
int shift = GetIntTypeBitWidth(overlay->declared_width()) -
overlay->declared_width();
absl::StrAppend(&h_output, " result = result << ", shift,
";\n"
" result = result >> ",
shift, ";\n");
}
if ((declared_width_ > 64) && (overlay->declared_width() <= 64)) {
absl::StrAppend(&h_output,
" return UInt128Low64(result();\n"
"}\n\n");
} else {
absl::StrAppend(&h_output,
" return result;\n"
"}\n\n");
}
return h_output;
}
// Top level function called to generate all the inserters for this format.
std::string Format::GenerateInserters() const {
std::string class_output;
std::string h_output;
if (extractors_.empty() && overlay_extractors_.empty()) {
return h_output;
}
absl::StrAppend(&h_output, "struct ", ToPascalCase(name()), " {\n\n");
// First fields and formats.
std::string inserter;
for (auto &[unused, field_or_format_ptr] : extractors_) {
if (field_or_format_ptr->is_field()) {
if (layout() == Layout::kPackedStruct) {
inserter =
GeneratePackedStructFieldInserter(field_or_format_ptr->field());
} else {
inserter = GenerateFieldInserter(field_or_format_ptr->field());
}
absl::StrAppend(&h_output, inserter);
} else {
if (layout() == Layout::kPackedStruct) {
inserter = GeneratePackedStructFormatInserter(
field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
field_or_format_ptr->high(), field_or_format_ptr->size());
} else {
inserter = GenerateFormatInserter(
field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
field_or_format_ptr->high(), field_or_format_ptr->size());
}
absl::StrAppend(&h_output, inserter);
}
}
// Next the overlays.
for (auto &[unused, overlay_ptr] : overlay_extractors_) {
auto inserter = GenerateOverlayInserter(overlay_ptr);
absl::StrAppend(&h_output, inserter);
}
absl::StrAppend(&h_output, "}; // struct ", ToPascalCase(name()), "\n\n");
return h_output;
}
std::string Format::GeneratePackedStructTypes() const {
std::string h_output;
// First the struct.
absl::StrAppend(&h_output, "struct Packed", ToPascalCase(name()), " {\n");
for (auto it = field_vec_.rbegin(); it != field_vec_.rend(); ++it) {
auto *component = *it;
if (component->is_field()) {
int width = component->field()->width;
std::string field_type = component->field()->is_signed
? GetIntType(width)
: GetUIntType(width);
absl::StrAppend(&h_output, " ", field_type, " ",
component->field()->name, " : ",
component->field()->width, ";\n");
} else {
absl::StrAppend(&h_output, " ",
GetUIntType(component->format()->declared_width()), " ",
component->format_alias(), " : ",
component->format()->declared_width(), ";\n");
}
}
absl::StrAppend(&h_output, "} ABSL_ATTRIBUTE_PACKED;\n\n");
// Next the union.
int num_bytes = (declared_width_ + 7) / 8;
absl::StrAppend(&h_output, "union Union", ToPascalCase(name()),
" {\n"
" Packed",
ToPascalCase(name()), " ", ToSnakeCase(name()),
";\n"
" uint8_t bytes[",
num_bytes, "];\n");
// If it is 64 bits or less, add an unsigned integer value type.
if (declared_width_ <= 64) {
absl::StrAppend(&h_output, " ", GetUIntType(declared_width_), " value;\n");
}
absl::StrAppend(&h_output, "};\n\n");
return h_output;
}
// Top level function called to generate all the extractors for this format.
Extractors Format::GenerateExtractors() const {
Extractors extractors;
if (extractors_.empty() && overlay_extractors_.empty()) {
return extractors;
}
extractors.class_output =
absl::StrCat("class ", ToPascalCase(name()), " {\n public:\n", " ",
ToPascalCase(name()), "() = default;\n\n");
// Use a separate namespace for each format.
extractors.h_output =
absl::StrCat("namespace ", ToSnakeCase(name()), " {\n\n");
extractors.types_output =
absl::StrCat("namespace ", ToSnakeCase(name()), " {\n\n");
std::string get_size = absl::StrCat("constexpr int k", ToPascalCase(name()),
"Size = ", declared_width(), ";\n\n");
absl::StrAppend(&extractors.h_output, get_size);
absl::StrAppend(&extractors.class_output, "static ", get_size);
// If this format has a packed struct layout, generate the types required.
if (layout() == Layout::kPackedStruct) {
absl::StrAppend(&extractors.types_output, GeneratePackedStructTypes());
}
// First fields and formats.
for (auto &[unused, field_or_format_ptr] : extractors_) {
if (field_or_format_ptr->is_field()) {
std::string extractor;
if (layout() == Layout::kPackedStruct) {
extractor =
GeneratePackedStructFieldExtractor(field_or_format_ptr->field());
} else {
extractor = GenerateFieldExtractor(field_or_format_ptr->field());
}
absl::StrAppend(&extractors.h_output, extractor);
absl::StrAppend(&extractors.class_output, "static ", extractor);
} else {
std::string extractor;
if (layout() == Layout::kPackedStruct) {
extractor = GeneratePackedStructFormatExtractor(
field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
field_or_format_ptr->high(), field_or_format_ptr->size());
} else {
extractor = GenerateFormatExtractor(
field_or_format_ptr->format_alias(), field_or_format_ptr->format(),
field_or_format_ptr->high(), field_or_format_ptr->size());
}
absl::StrAppend(&extractors.h_output, extractor);
absl::StrAppend(&extractors.class_output, "static ", extractor);
}
}
// Then the overlays.
for (auto &[unused, overlay_ptr] : overlay_extractors_) {
std::string extractor;
if (layout() == Layout::kPackedStruct) {
extractor = GeneratePackedStructOverlayExtractor(overlay_ptr);
} else {
extractor = GenerateOverlayExtractor(overlay_ptr);
}
absl::StrAppend(&extractors.h_output, extractor);
absl::StrAppend(&extractors.class_output, "static ", extractor);
}
absl::StrAppend(&extractors.h_output, "} // namespace ", ToSnakeCase(name()),
"\n\n");
absl::StrAppend(&extractors.types_output, "} // namespace ",
ToSnakeCase(name()), "\n\n");
absl::StrAppend(&extractors.class_output, "};\n\n");
return extractors;
}
bool Format::IsDerivedFrom(const Format *format) {
if (format == this) return true;
if (base_format_ == nullptr) return false;
if (base_format_ == format) return true;
return base_format_->IsDerivedFrom(format);
}
} // namespace bin_format
} // namespace decoder
} // namespace sim
} // namespace mpact