/* Miscellaneous types used across stage boundaries. Copyright (C) 2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "elna/boot/result.h" #include #include namespace elna::boot { location::location(const std::size_t line, const std::size_t column) : m_line(line), m_column(column) { } std::size_t location::line() const { return this->m_line; } std::size_t location::column() const { return this->m_column; } bool location::available() const { return this->m_line != 0 || this->m_column != 0; } source_position::source_position(location start, location end) : m_start(start), m_end(end) { } const location& source_position::start() const { return this->m_start; } const location& source_position::end() const { return this->m_end; } bool source_position::is_span() const { return this->m_start != this->m_end; } error::error(const source_position position) : position(position) { } std::deque>& error_container::errors() { return m_errors; } bool error_container::has_errors() const { return !m_errors.empty(); } identifier::identifier(const std::string& name, const source_position& position) : m_name(name), m_position(position) { } const std::string& identifier::name() const { return this->m_name; } const source_position& identifier::position() const { return this->m_position; } bool identifier::operator==(const identifier& that) const { return this->m_name == that; } bool identifier::operator==(std::string_view that) const { return this->m_name == that; } identifier_definition::identifier_definition(const std::string& name, const source_position& position, const bool exported) : m_identifier(name, position), m_exported(exported) { } const std::string& identifier_definition::name() const { return this->m_identifier.name(); } const identifier& identifier_definition::id() const { return this->m_identifier; } bool identifier_definition::exported() const { return this->m_exported; } integer_literal::integer_literal(bool is_signed, std::size_t size) : m_signed(is_signed), m_size(size) { mpz_init(this->raw); } integer_literal::integer_literal(integer_literal&& that) noexcept : m_signed(that.is_signed()), m_size(that.size()) { mpz_init(this->raw); mpz_swap(this->raw, that.raw); } integer_literal::integer_literal(const integer_literal& that) : m_signed(that.is_signed()), m_size(that.size()) { mpz_init(this->raw); mpz_set(this->raw, that.raw); } integer_literal::~integer_literal() { mpz_clear(this->raw); } std::optional integer_literal::add(const integer_literal& that) const { integer_literal result = *this; mpz_add(result.raw, this->raw, that.raw); return std::move(result).check(); } std::optional integer_literal::sub(const integer_literal& that) const { integer_literal result = *this; mpz_sub(result.raw, this->raw, that.raw); return std::move(result).check(); } std::optional integer_literal::mul(const integer_literal& that) const { integer_literal result = *this; mpz_mul(result.raw, this->raw, that.raw); return std::move(result).check(); } std::optional integer_literal::div(const integer_literal& that) const { if (mpz_cmp_ui(that.raw, 0U) == 0) { return std::nullopt; } else { integer_literal result = *this; mpz_div(result.raw, this->raw, that.raw); return std::make_optional(std::move(result)); } } std::optional integer_literal::mod(const integer_literal& that) const { if (mpz_cmp_ui(that.raw, 0U) == 0) { return std::nullopt; } else { integer_literal result = *this; mpz_mod(result.raw, this->raw, that.raw); return std::make_optional(std::move(result)); } } std::optional integer_literal::neg() const { if (!is_signed() || is_negative_minimum()) { return std::nullopt; } integer_literal result = *this; mpz_neg(result.raw, this->raw); return result; } std::optional integer_literal::shl(const integer_literal& that) const { if (that >= bits()) { return std::nullopt; } integer_literal result = *this; mpz_mul_2exp(result.raw, this->raw, static_cast(mpz_get_ui(that.raw))); return std::move(result).check(); } std::optional integer_literal::shr(const integer_literal& that) const { integer_literal result = *this; if (that >= bits()) { mpz_set_si(result.raw, *this > 0 ? 0 : -1); } else { mpz_fdiv_q_2exp(result.raw, this->raw, static_cast(mpz_get_ui(that.raw))); } return std::make_optional(std::move(result)); } integer_literal integer_literal::operator|(const integer_literal& that) const { integer_literal result = *this; mpz_ior(result.raw, this->raw, that.raw); return result; } integer_literal integer_literal::operator&(const integer_literal& that) const { integer_literal result = *this; mpz_and(result.raw, this->raw, that.raw); return result; } integer_literal integer_literal::operator^(const integer_literal& that) const { integer_literal result = *this; mpz_xor(result.raw, this->raw, that.raw); return result; } integer_literal integer_literal::operator~() const { integer_literal result = *this; mpz_com(result.raw, this->raw); return result; } bool integer_literal::operator==(const integer_literal& that) const { return mpz_cmp(this->raw, that.raw) == 0; } std::weak_ordering integer_literal::operator<=>(const integer_literal& that) const { return mpz_cmp(this->raw, that.raw) <=> 0; } integer_literal& integer_literal::operator=(integer_literal&& that) noexcept { swap(*this, that); return *this; } integer_literal& integer_literal::operator=(const integer_literal& that) { if (this != &that) { integer_literal temp(that); swap(*this, temp); } return *this; } std::optional integer_literal::to_signed() const { if (!is_signed()) { return std::nullopt; } if (is_negative_minimum(sizeof(std::ptrdiff_t) * CHAR_BIT)) { return std::numeric_limits::min(); } auto [written, rop] = export_to_words(); if (written > 1 || rop < 0) { return std::nullopt; } return mpz_sgn(this->raw) < 0 ? -rop : rop; } std::optional integer_literal::to_unsigned() const { if (is_signed()) { return std::nullopt; } auto [written, rop] = export_to_words(); return written > 1 ? std::nullopt : std::make_optional(rop); } bool integer_literal::fit_into(const std::size_t target_size) { if (fits_in(target_size * CHAR_BIT)) { this->m_size = target_size; return true; } return false; } bool integer_literal::is_signed() const { return this->m_signed; } std::size_t integer_literal::size() const { return this->m_size; } std::string integer_literal::to_string(const std::uint8_t base) const { // +1 sign, +1 null terminator const size_t buffer_size = mpz_sizeinbase(this->raw, static_cast(base)) + 2; std::string result(buffer_size, '\0'); mpz_get_str(result.data(), base, this->raw); result.resize(std::strlen(result.c_str())); return result; } void swap(integer_literal& lhs, integer_literal& rhs) noexcept { mpz_swap(lhs.raw, rhs.raw); std::swap(lhs.m_signed, rhs.m_signed); std::swap(lhs.m_size, rhs.m_size); } bool integer_literal::fits_in(const std::size_t bits) const { std::size_t required_bits = mpz_sizeinbase(this->raw, 2); if (!is_negative_minimum(bits)) { ++required_bits; // Add one bit for the sign. } return required_bits <= bits && (mpz_sgn(this->raw) >= 0 || is_signed()); } std::optional integer_literal::check() && { return fits_in(bits()) ? std::make_optional(std::move(*this)) : std::nullopt; } bool integer_literal::is_negative_minimum(const std::size_t bits) const { return mpz_sgn(this->raw) < 0 && mpz_scan1(this->raw, 0) == bits - 1; } bool integer_literal::is_negative_minimum() const { return is_negative_minimum(bits()); } std::size_t integer_literal::bits() const { return size() * CHAR_BIT; } std::size_t constant_value_hash::operator()(const elna::boot::constant_value& value) const noexcept { return std::visit([](auto&& alternative) -> std::size_t { using T = std::decay_t; return std::hash{}(alternative); }, value); } hash_accumulator hash_accumulator::operator+(const std::size_t& that) const { hash_accumulator result{}; result.m_seed ^= that + hash_accumulator::golden_ratio // NOLINTNEXTLINE(readability-magic-numbers) + (this->m_seed << 6) + (this->m_seed >> 2); return result; } std::size_t hash_accumulator::seed() const { return this->m_seed; } } std::size_t std::hash::operator()( const elna::boot::identifier& key) const noexcept { return std::hash{}(key.name()); } std::size_t std::hash::operator()( const elna::boot::global_address& key) const noexcept { return std::hash{}(key.name); } std::size_t std::hash>::operator()( const elna::boot::constant_aggregate& key) const noexcept { const elna::boot::constant_value_hash hasher{}; auto hash = std::accumulate(key->begin(), key->end(), elna::boot::hash_accumulator{}, [&hasher](const auto& accumulator, const auto& element) { return accumulator + hasher(element); }); return hash.seed(); } std::size_t std::hash>::operator()( const elna::boot::constant_aggregate& key) const noexcept { const elna::boot::constant_value_hash hasher{}; auto hash = std::accumulate(key->begin(), key->end(), elna::boot::hash_accumulator{}, [&hasher](const auto& accumulator, const auto& element) { return accumulator + std::hash{}(element.first) + hasher(element.second); }); return hash.seed(); } std::size_t std::hash::operator()(const elna::boot::integer_literal& key) const noexcept { if (key.is_signed()) { if (auto converted = key.to_signed()) { return std::hash{}(*converted); } } else { if (auto converted = key.to_unsigned()) { return std::hash{}(*converted); } } return 0; }