blob: 83a435b12e1d04b3f6c90501be431059aab9ddbb [file]
// Copyright 2025 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_UTIL_ASM_TEST_RISCV_BIN_SETTERS_H_
#define MPACT_SIM_UTIL_ASM_TEST_RISCV_BIN_SETTERS_H_
#include <cstdint>
#include <initializer_list>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "mpact/sim/util/asm/opcode_assembler_interface.h"
#include "mpact/sim/util/asm/resolver_interface.h"
#include "mpact/sim/util/asm/test/riscv_getter_helpers.h"
#include "re2/re2.h"
namespace mpact {
namespace sim {
namespace riscv {
using ::mpact::sim::util::assembler::RelocationInfo;
using ::mpact::sim::util::assembler::ResolverInterface;
constexpr std::initializer_list<const std::pair<absl::string_view, uint64_t>>
kRegisterList = {
{"x0", 0}, {"x1", 1}, {"x2", 2}, {"x3", 3}, {"x4", 4},
{"x5", 5}, {"x6", 6}, {"x7", 7}, {"x8", 8}, {"x9", 9},
{"x10", 10}, {"x11", 11}, {"x12", 12}, {"x13", 13}, {"x14", 14},
{"x15", 15}, {"x16", 16}, {"x17", 17}, {"x18", 18}, {"x19", 19},
{"x20", 20}, {"x21", 21}, {"x22", 22}, {"x23", 23}, {"x24", 24},
{"x25", 25}, {"x26", 26}, {"x27", 27}, {"x28", 28}, {"x29", 29},
{"x30", 30}, {"x31", 31}, {"zero", 0}, {"ra", 1}, {"sp", 2},
{"gp", 3}, {"tp", 4}, {"t0", 5}, {"t1", 6}, {"t2", 7},
{"s0", 8}, {"s1", 9}, {"a0", 10}, {"a1", 11}, {"a2", 12},
{"a3", 13}, {"a4", 14}, {"a5", 15}, {"a6", 16}, {"a7", 17},
{"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22},
{"s7", 23}, {"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27},
{"t3", 28}, {"t4", 29}, {"t5", 30}, {"t6", 31}};
template <typename T>
absl::StatusOr<T> SimpleTextToInt(absl::string_view op_text,
ResolverInterface* resolver) {
T value;
static RE2 hex_re("^\\s*0x([0-9a-fA-F]+)\\s*$");
static RE2 dec_re("^\\s*(-?[0-9]+)\\s*$");
static RE2 relo_re("^\\s*\\%[a-zA-Z0-9_]+\\s*\\(([a-zA-Z0-9_]+)\\s*\\)\\s*$");
static RE2 symbol_re("^\\s*([a-zA-Z0-9_]+)\\s*$");
std::string str;
std::string text(op_text);
// First see if the operand is a relocation function, and extract the text
// argument. A relocation function is on the form of %name(arg).
if (RE2::FullMatch(op_text, relo_re, &str)) {
text = str;
}
// Extract the hex immediate.
if (RE2::FullMatch(text, hex_re, &str)) {
if (absl::SimpleHexAtoi(str, &value)) return value;
return absl::InvalidArgumentError(
absl::StrCat("Invalid hexadecimal immediate: ", text));
}
// Extract the decimal immediate.
if (RE2::FullMatch(text, dec_re, &str)) {
if (absl::SimpleAtoi(str, &value)) return value;
return absl::InvalidArgumentError(
absl::StrCat("Invalid decimal immediate: ", text));
}
// Extract the symbol.
if (RE2::FullMatch(text, symbol_re, &str)) {
if (resolver != nullptr) {
auto res = resolver->Resolve(str);
if (!res.ok()) {
return res.status();
}
return static_cast<T>(res.value());
}
}
return absl::InvalidArgumentError(absl::StrCat("Invalid argument: ", text));
}
using ValueMap = absl::flat_hash_map<absl::string_view, uint64_t>;
template <typename Enum, typename Map, typename Encoder>
void AddRiscvSourceOpBinSetters(Map& map) {
Insert(map, *Enum::kIImm12,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
auto res = SimpleTextToInt<int32_t>(text, resolver);
if (!res.ok()) return res.status();
return Encoder::IType::InsertImm12(res.value(), 0ULL);
});
Insert(map, *Enum::kIUimm6,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
auto res = SimpleTextToInt<uint32_t>(text, resolver);
if (!res.ok()) return res.status();
return Encoder::RSType::InsertRUimm6(res.value(), 0ULL);
});
Insert(map, *Enum::kJImm12,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
auto res = SimpleTextToInt<int32_t>(text, resolver);
if (!res.ok()) return res.status();
return Encoder::IType::InsertImm12(res.value(), 0ULL);
});
Insert(map, *Enum::kJImm20,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
auto res = SimpleTextToInt<int32_t>(text, resolver);
if (!res.ok()) return res.status();
uint32_t delta = res.value() - address;
auto value = Encoder::JType::InsertJImm(delta, 0ULL);
return value;
});
Insert(map, *Enum::kRs1,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
static ValueMap map(kRegisterList);
auto iter = map.find(text);
if (iter == map.end()) {
return absl::InvalidArgumentError(
absl::StrCat("Invalid source operand: ", text));
}
return Encoder::RSType::InsertRs1(iter->second, 0ULL);
});
Insert(map, *Enum::kRs2,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
static ValueMap map(kRegisterList);
auto iter = map.find(text);
if (iter == map.end()) {
return absl::InvalidArgumentError(
absl::StrCat("Invalid source operand: ", text));
}
return Encoder::SType::InsertRs2(iter->second, 0ULL);
});
Insert(map, *Enum::kSImm12,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
auto res = SimpleTextToInt<uint32_t>(text, resolver);
if (!res.ok()) return res.status();
return Encoder::SType::InsertSImm(res.value(), 0ULL);
});
Insert(map, *Enum::kUImm20,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
auto res = SimpleTextToInt<uint32_t>(text, resolver);
if (!res.ok()) return res.status();
return Encoder::UType::InsertUImm(res.value(), 0ULL);
});
}
template <typename Enum, typename Map, typename Encoder>
void AddRiscvDestOpBinSetters(Map& map) {
Insert(map, *Enum::kRd,
[](uint64_t address, absl::string_view text,
ResolverInterface* resolver) -> absl::StatusOr<uint64_t> {
static ValueMap map(kRegisterList);
auto iter = map.find(text);
if (iter == map.end()) {
return absl::InvalidArgumentError(
absl::StrCat("Invalid destination operand: ", text));
}
return Encoder::RSType::InsertRd(iter->second, 0ULL);
});
}
namespace internal {
absl::Status RelocateAddiIImm12(uint64_t address, absl::string_view text,
ResolverInterface* resolver,
std::vector<RelocationInfo>& relocations);
absl::Status RelocateJJImm20(uint64_t address, absl::string_view text,
ResolverInterface* resolver,
std::vector<RelocationInfo>& relocations);
absl::Status RelocateJrJImm12(uint64_t address, absl::string_view text,
ResolverInterface* resolver,
std::vector<RelocationInfo>& relocations);
absl::Status RelocateLuiUImm20(uint64_t address, absl::string_view text,
ResolverInterface* resolver,
std::vector<RelocationInfo>& relocations);
absl::Status RelocateSdSImm12(uint64_t address, absl::string_view text,
ResolverInterface* resolver,
std::vector<RelocationInfo>& relocations);
} // namespace internal
template <typename OpcodeEnum, typename SourceOpEnum, typename Map>
void AddRiscvSourceOpRelocationSetters(Map& map) {
Insert(map, OpcodeEnum::kAddi, SourceOpEnum::kIImm12,
internal::RelocateAddiIImm12);
Insert(map, OpcodeEnum::kJal, SourceOpEnum::kJImm20,
internal::RelocateJJImm20);
Insert(map, OpcodeEnum::kJ, SourceOpEnum::kJImm20, internal::RelocateJJImm20);
Insert(map, OpcodeEnum::kJr, SourceOpEnum::kJImm12,
internal::RelocateJrJImm12);
Insert(map, OpcodeEnum::kLui, SourceOpEnum::kUImm20,
internal::RelocateLuiUImm20);
Insert(map, OpcodeEnum::kSd, SourceOpEnum::kSImm12,
internal::RelocateSdSImm12);
}
} // namespace riscv
} // namespace sim
} // namespace mpact
#endif // MPACT_SIM_UTIL_ASM_TEST_RISCV_BIN_SETTERS_H_