diff options
| -rw-r--r-- | boot/ast.cc | 4 | ||||
| -rw-r--r-- | boot/evaluator.cc | 101 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 2 | ||||
| -rw-r--r-- | boot/parser.yy | 2 | ||||
| -rw-r--r-- | boot/result.cc | 270 | ||||
| -rw-r--r-- | boot/validation.cc | 2 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 5 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 5 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 6 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 2 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 183 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 2 |
12 files changed, 471 insertions, 113 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 7f34243..2a90d0e 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -186,7 +186,7 @@ namespace elna::boot __builtin_unreachable(); } - void empty_visitor::visit(literal<std::uint32_t> *) + void empty_visitor::visit(literal<integer_literal> *) { __builtin_unreachable(); } @@ -513,7 +513,7 @@ namespace elna::boot { } - void walking_visitor::visit(literal<std::uint32_t> *) + void walking_visitor::visit(literal<integer_literal> *) { } diff --git a/boot/evaluator.cc b/boot/evaluator.cc index b49a9ec..bff727d 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -218,7 +218,7 @@ namespace elna::boot } if (is_primitive_type(decoration, "Word")) { - return constant_value{ static_cast<literal<std::uint32_t>&>(subject).value }; + return constant_value{ static_cast<literal<integer_literal>&>(subject).value }; } if (is_primitive_type(decoration, "Float")) { @@ -279,9 +279,9 @@ namespace elna::boot } position = static_cast<std::size_t>(*int_index); } - else if (auto *word_index = std::get_if<std::uint32_t>(&index.value())) + else if (auto *word_index = std::get_if<integer_literal>(&index.value())) { - position = *word_index; + position = word_index->to<std::uint32_t>(); } else { @@ -416,7 +416,7 @@ namespace elna::boot return std::visit([](const auto& value) -> std::optional<constant_value> { using T = std::decay_t<decltype(value)>; - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T> || std::is_same_v<T, integer_literal>) { return constant_value{ ~value }; } @@ -433,7 +433,7 @@ namespace elna::boot template<typename T> static std::optional<T> add_overflow(T lhs, T rhs) { - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T>) { T result; return __builtin_add_overflow(lhs, rhs, &result) @@ -446,7 +446,7 @@ namespace elna::boot template<typename T> static std::optional<T> sub_overflow(T lhs, T rhs) { - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T>) { T result; return __builtin_sub_overflow(lhs, rhs, &result) @@ -459,7 +459,7 @@ namespace elna::boot template<typename T> static std::optional<T> mul_overflow(T lhs, T rhs) { - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T>) { T result; return __builtin_mul_overflow(lhs, rhs, &result) @@ -506,7 +506,14 @@ namespace elna::boot { using enum binary_operator; case sum: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.add(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (is_arithmetic<T>) { if (auto result = add_overflow(lhs, rhs)) { @@ -515,7 +522,14 @@ namespace elna::boot } return std::nullopt; case subtraction: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.sub(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (is_arithmetic<T>) { if (auto result = sub_overflow(lhs, rhs)) { @@ -524,7 +538,14 @@ namespace elna::boot } return std::nullopt; case multiplication: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.mul(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (is_arithmetic<T>) { if (auto result = mul_overflow(lhs, rhs)) { @@ -533,7 +554,14 @@ namespace elna::boot } return std::nullopt; case division: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.div(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (is_arithmetic<T>) { if (rhs != static_cast<T>(0)) { @@ -542,7 +570,14 @@ namespace elna::boot } return std::nullopt; case remainder: - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.mod(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (is_integral<T>) { if (rhs != static_cast<T>(0)) { @@ -552,21 +587,21 @@ namespace elna::boot return std::nullopt; case disjunction: case bitwise_disjunction: - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs | rhs }; } return std::nullopt; case conjunction: case bitwise_conjunction: - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs & rhs }; } return std::nullopt; case exclusive_disjunction: case bitwise_exclusive_disjunction: - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_integral<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs ^ rhs }; } @@ -590,7 +625,14 @@ namespace elna::boot } return std::nullopt; case shift_left: - if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.shl(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (is_integral<T>) { if (rhs < 0 || static_cast<std::make_unsigned_t<T>>(rhs) >= std::numeric_limits<T>::digits) { @@ -600,7 +642,14 @@ namespace elna::boot } return std::nullopt; case shift_right: - if constexpr (std::is_integral_v<T>) + if constexpr (std::is_same_v<T, integer_literal>) + { + if (auto result = lhs.shr(rhs)) + { + return constant_value{ result.value() }; + } + } + else if constexpr (std::is_integral_v<T>) { return constant_value{ lhs >> rhs }; } @@ -610,25 +659,25 @@ namespace elna::boot case not_equals: return constant_value{ lhs != rhs }; case less: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_arithmetic<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs < rhs }; } return std::nullopt; case greater: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_arithmetic<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs > rhs }; } return std::nullopt; case less_equal: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_arithmetic<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs <= rhs }; } return std::nullopt; case greater_equal: - if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) + if constexpr (is_arithmetic<T> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs >= rhs }; } @@ -690,14 +739,14 @@ namespace elna::boot { if (auto size = evaluate_traits_size(subject.types.front())) { - return constant_value{ static_cast<std::uint32_t>(size.value()) }; + return constant_value{ integer_literal::from(static_cast<std::uint32_t>(size.value())) }; } } else if (subject.name.name() == "alignment") { if (auto alignment = evaluate_traits_alignment(subject.types.front())) { - return constant_value{ static_cast<std::uint32_t>(alignment.value()) }; + return constant_value{ integer_literal::from(static_cast<std::uint32_t>(alignment.value())) }; } } else if (subject.name.name() == "min") @@ -710,7 +759,7 @@ namespace elna::boot } if (is_primitive_type(resolved, "Word")) { - return constant_value{ static_cast<std::uint32_t>(0) }; + return constant_value{ integer_literal::from(static_cast<std::uint32_t>(0)) }; } if (is_primitive_type(resolved, "Char")) { @@ -739,7 +788,7 @@ namespace elna::boot } if (is_primitive_type(resolved, "Word")) { - return constant_value{ std::numeric_limits<std::uint32_t>::max() }; + return constant_value{ integer_literal::from(std::numeric_limits<std::uint32_t>::max()) }; } if (is_primitive_type(resolved, "Char")) { @@ -772,7 +821,7 @@ namespace elna::boot if (field_search != std::cend(record_layout.value().offset_map)) { - return constant_value{ static_cast<std::uint32_t>(field_search->second) }; + return constant_value{ integer_literal::from(static_cast<std::uint32_t>(field_search->second)) }; } } } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 89353e9..b8cd83e 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -795,7 +795,7 @@ namespace elna::boot this->current_type = literal->type_decoration; } - void name_analysis_visitor::visit(literal<std::uint32_t> *literal) + void name_analysis_visitor::visit(literal<integer_literal> *literal) { literal->type_decoration = lookup_primitive_type("Word"); this->current_type = literal->type_decoration; diff --git a/boot/parser.yy b/boot/parser.yy index 4fdbf84..85fc454 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -236,7 +236,7 @@ procedure_return: | "return" { $$ = nullptr; } literal: INTEGER { $$ = new boot::literal<std::int32_t>(boot::make_position(@$), $1); } - | WORD { $$ = new boot::literal<std::uint32_t>(boot::make_position(@$), $1); } + | WORD { $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), boot::integer_literal::from($1)); } | FLOAT { $$ = new boot::literal<double>(boot::make_position(@$), $1); } | BOOLEAN { $$ = new boot::literal<bool>(boot::make_position(@$), $1); } | CHARACTER { $$ = new boot::literal<unsigned char>(boot::make_position(@$), $1.at(0)); } diff --git a/boot/result.cc b/boot/result.cc index 0eb5c12..f9f57db 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -91,22 +91,9 @@ namespace elna::boot return this->m_position; } - std::strong_ordering identifier::operator<=>(const identifier& that) const + bool identifier::operator==(const identifier& that) const { - auto comparison = this->m_name.compare(that.name()); - - if (comparison < 0) - { - return std::strong_ordering::less; - } - else if (comparison > 0) - { - return std::strong_ordering::greater; - } - else - { - return std::strong_ordering::equal; - } + return this->m_name == that; } bool identifier::operator==(std::string_view that) const @@ -114,11 +101,6 @@ namespace elna::boot return this->m_name == that; } - bool identifier::operator!=(std::string_view that) const - { - return !(*this == that); - } - identifier_definition::identifier_definition(const std::string& name, const source_position& position, const bool exported) : m_identifier(name, position), m_exported(exported) @@ -140,6 +122,231 @@ namespace elna::boot 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 + { + mpz_init(this->raw); + mpz_swap(this->raw, that.raw); + } + + integer_literal::integer_literal(const integer_literal& that) + { + mpz_init(this->raw); + mpz_set(this->raw, that.raw); + } + + integer_literal::~integer_literal() + { + mpz_clear(this->raw); + } + + std::optional<integer_literal> 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> 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> 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> 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> 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> 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> 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<mp_bitcnt_t>(mpz_get_ui(that.raw))); + + return std::move(result).check(); + } + + std::optional<integer_literal> 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<mp_bitcnt_t>(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 + { + if (this != &that) + { + mpz_swap(this->raw, that.raw); + } + return *this; + } + + integer_literal& integer_literal::operator=(const integer_literal& that) + { + if (this != &that) + { + mpz_set(this->raw, that.raw); + } + return *this; + } + + bool integer_literal::is_signed() const + { + return this->m_signed; + } + + std::size_t integer_literal::size() const + { + return this->m_size; + } + + std::optional<integer_literal> integer_literal::check() && + { + std::size_t required_bits = mpz_sizeinbase(this->raw, 2); + + if (!is_negative_minimum()) + { + ++required_bits; // Add one bit for the sign. + } + if (required_bits > bits() || (mpz_sgn(this->raw) < 0 && !is_signed())) + { + return std::nullopt; + } + else + { + return std::move(*this); + } + } + + bool integer_literal::is_negative_minimum() const + { + return mpz_sgn(this->raw) < 0 && mpz_scan1(this->raw, 0) == bits() - 1; + } + + 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 { @@ -149,24 +356,6 @@ namespace elna::boot }, value); } - bool constant_value_hash::operator()(const elna::boot::constant_value& lhs, - const elna::boot::constant_value& rhs) const noexcept - { - return std::visit([](auto&& first, auto&& second) -> bool { - using T = std::decay_t<decltype(first)>; - using U = std::decay_t<decltype(second)>; - - if constexpr (std::is_same_v<T, U>) - { - return first == second; - } - else - { - return false; - } - }, lhs, rhs); - } - hash_accumulator hash_accumulator::operator+(const std::size_t& that) const { hash_accumulator result{}; @@ -219,3 +408,8 @@ std::size_t std::hash<elna::boot::constant_aggregate<elna::boot::ordered_map>>:: return hash.seed(); } + +std::size_t std::hash<elna::boot::integer_literal>::operator()(const elna::boot::integer_literal& key) const noexcept +{ + return std::hash<std::uint32_t>{}(key.to<std::uint32_t>()); +} diff --git a/boot/validation.cc b/boot/validation.cc index ccc8865..dc4b5b0 100644 --- a/boot/validation.cc +++ b/boot/validation.cc @@ -131,7 +131,7 @@ namespace elna::boot void validation_visitor::visit(case_statement *statement) { walking_visitor::visit(statement); - std::unordered_map<constant_value, source_position, constant_value_hash, constant_value_hash> seen; + std::unordered_map<constant_value, source_position, constant_value_hash> seen; for (const auto& case_block : statement->cases) { for (auto *label : case_block.labels) diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 1104cf1..6da3f99 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -483,9 +483,10 @@ namespace elna::gcc this->current_expression = constant_to_tree(boot::constant_value{ literal->value }, this->symbols); } - void generic_visitor::visit(boot::literal<std::uint32_t> *literal) + void generic_visitor::visit(boot::literal<boot::integer_literal> *literal) { - this->current_expression = constant_to_tree(boot::constant_value{ literal->value }, this->symbols); + this->current_expression = constant_to_tree( + boot::constant_value{ literal->value }, this->symbols); } void generic_visitor::visit(boot::literal<double> *literal) diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index 7862eaa..4b3ef81 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -287,9 +287,10 @@ namespace elna::gcc { return build_int_cst(elna_int_type_node, std::get<std::int32_t>(constant_value)); } - else if (std::holds_alternative<std::uint32_t>(constant_value)) + else if (std::holds_alternative<boot::integer_literal>(constant_value)) { - return build_int_cstu(elna_word_type_node, std::get<std::uint32_t>(constant_value)); + return build_int_cstu(elna_word_type_node, + std::get<boot::integer_literal>(constant_value).to<std::uint32_t>()); } else if (std::holds_alternative<double>(constant_value)) { diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index cc5878b..0eda1a3 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -139,7 +139,7 @@ namespace elna::boot virtual void visit(field_access_expression *) = 0; virtual void visit(dereference_expression *) = 0; virtual void visit(literal<std::int32_t> *) = 0; - virtual void visit(literal<std::uint32_t> *) = 0; + virtual void visit(literal<integer_literal> *) = 0; virtual void visit(literal<double> *) = 0; virtual void visit(literal<bool> *) = 0; virtual void visit(literal<unsigned char> *) = 0; @@ -189,7 +189,7 @@ namespace elna::boot [[noreturn]] void visit(field_access_expression *) override; [[noreturn]] void visit(dereference_expression *) override; [[noreturn]] void visit(literal<std::int32_t> *) override; - [[noreturn]] void visit(literal<std::uint32_t> *) override; + [[noreturn]] void visit(literal<integer_literal> *) override; [[noreturn]] void visit(literal<double> *) override; [[noreturn]] void visit(literal<bool> *) override; [[noreturn]] void visit(literal<unsigned char> *) override; @@ -237,7 +237,7 @@ namespace elna::boot void visit(field_access_expression *expression) override; void visit(dereference_expression *expression) override; void visit(literal<std::int32_t> *) override; - void visit(literal<std::uint32_t> *) override; + void visit(literal<integer_literal> *) override; void visit(literal<double> *) override; void visit(literal<bool> *) override; void visit(literal<unsigned char> *) override; diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 626e9f2..5d43f67 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -158,7 +158,7 @@ namespace elna::boot void visit(for_statement *statement) override; void visit(literal<std::int32_t> *literal) override; - void visit(literal<std::uint32_t> *literal) override; + void visit(literal<integer_literal> *literal) override; void visit(literal<double> *literal) override; void visit(literal<bool> *literal) override; void visit(literal<unsigned char> *literal) override; diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 42c96f2..3e8dfdf 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -29,6 +29,8 @@ along with GCC; see the file COPYING3. If not see #include <vector> #include <unordered_map> +#include <gmp.h> + namespace elna::boot { /** @@ -143,7 +145,7 @@ namespace elna::boot { } - auto operator<=>(const return_declaration&) const = default; + bool operator==(const return_declaration&) const = default; T proper_type{}; bool no_return{ false }; @@ -156,9 +158,8 @@ namespace elna::boot const std::string& name() const; const source_position& position() const; - std::strong_ordering operator<=>(const identifier& that) const; + bool operator==(const identifier& that) const; bool operator==(std::string_view that) const; - bool operator!=(std::string_view that) const; private: std::string m_name; @@ -180,6 +181,138 @@ namespace elna::boot }; /** + * Checks whether \p T is a signed, std::int*_t type. + * + * \tparam T The examined type. + * + * \see is_unsigned + */ + template<typename T> + inline constexpr bool is_signed = std::is_same_v<T, std::int8_t> + || std::is_same_v<T, std::int16_t> + || std::is_same_v<T, std::int32_t> + || std::is_same_v<T, std::int64_t>; + + /** + * Checks whether \p T is a signed, std::uint*_t type. + * + * \tparam T The examined type. + * + * \see is_signed + */ + template<typename T> + inline constexpr bool is_unsigned = std::is_same_v<T, std::uint8_t> + || std::is_same_v<T, std::uint16_t> + || std::is_same_v<T, std::uint32_t> + || std::is_same_v<T, std::uint64_t>; + + /** + * Checks whether \p T is any of std::int*_t or std::uint*_t types. + * + * Contrary to \c std::is_integral characters and booleans do not count. + * + * \tparam T The examined type. + */ + template<typename T> + inline constexpr bool is_integral = is_signed<T> || is_unsigned<T>; + + /** + * Checks whether \p T is an integral or floating point type. + * + * \tparam T The examined type. + * + * \see is_integral + */ + template<typename T> + inline constexpr bool is_arithmetic = is_integral<T> || std::is_floating_point_v<T>; + + struct integer_literal + { + integer_literal(integer_literal&& that) noexcept; + integer_literal(const integer_literal& that); + ~integer_literal(); + + std::optional<integer_literal> add(const integer_literal& that) const; + std::optional<integer_literal> sub(const integer_literal& that) const; + std::optional<integer_literal> mul(const integer_literal& that) const; + std::optional<integer_literal> div(const integer_literal& that) const; + std::optional<integer_literal> mod(const integer_literal& that) const; + std::optional<integer_literal> neg() const; + std::optional<integer_literal> shl(const integer_literal& that) const; + std::optional<integer_literal> shr(const integer_literal& that) const; + + integer_literal operator|(const integer_literal& that) const; + integer_literal operator&(const integer_literal& that) const; + integer_literal operator^(const integer_literal& that) const; + integer_literal operator~() const; + + bool operator==(const integer_literal& that) const; + std::weak_ordering operator<=>(const integer_literal& that) const; + template<typename U> + bool operator==(U that) const + requires(is_unsigned<U> && sizeof(U) <= sizeof(unsigned long int)) + { + return mpz_cmp_ui(this->raw, that) == 0; + } + template<typename U> + bool operator==(U that) const + requires(is_signed<U> && sizeof(U) <= sizeof(signed long int)) + { + return mpz_cmp_si(this->raw, that) == 0; + } + template<typename U> + std::weak_ordering operator<=>(U that) const + requires(is_unsigned<U> && sizeof(U) <= sizeof(unsigned long int)) + { + return mpz_cmp_ui(this->raw, that) <=> 0; + } + template<typename U> + std::weak_ordering operator<=>(U that) const + requires(is_signed<U> && sizeof(U) <= sizeof(signed long int)) + { + return mpz_cmp_si(this->raw, that) <=> 0; + } + + integer_literal& operator=(integer_literal&& that) noexcept; + integer_literal& operator=(const integer_literal& that); + + bool is_signed() const; + std::size_t size() const; + + template<typename T> + T to() const + requires is_integral<T> + { + T result; + + mpz_export(&result, nullptr, 1, sizeof(T), 0, 0, this->raw); + + return result; + } + + template<typename T> + static integer_literal from(T initial) + requires is_integral<T> + { + integer_literal result{ std::is_signed_v<T>, sizeof(T) }; + + mpz_import(result.raw, 1, 1, sizeof(T), 0, 0, &initial); + + return result; + } + + private: + bool m_signed; + std::size_t m_size; + mpz_t raw; + + integer_literal(bool is_signed, std::size_t size); + std::optional<integer_literal> check() &&; + bool is_negative_minimum() const; + std::size_t bits() const; + }; + + /** * An associative container that contains key-value pairs with unique keys. * Keys preserve the insertion order. */ @@ -202,9 +335,9 @@ namespace elna::boot using iterator = std::vector<value_type>::iterator; using const_iterator = std::vector<value_type>::const_iterator; - auto operator<=>(const ordered_map& that) const + bool operator==(const ordered_map& that) const { - return this->payload <=> that.payload; + return this->payload == that.payload; } /** @@ -357,7 +490,7 @@ namespace elna::boot { std::string name; - auto operator<=>(const global_address&) const = default; + bool operator==(const global_address&) const = default; }; template<template<typename, typename> typename C, template<typename> typename Alloc = std::allocator> @@ -371,7 +504,7 @@ namespace elna::boot */ using constant_value = std::variant< std::int32_t, - std::uint32_t, + integer_literal, double, bool, unsigned char, @@ -387,7 +520,6 @@ namespace elna::boot struct constant_value_hash { std::size_t operator()(const constant_value& value) const noexcept; - bool operator()(const constant_value& lhs, const constant_value& rhs) const noexcept; }; /** @@ -433,34 +565,9 @@ namespace elna::boot return this->container.get(); } - bool operator==(const constant_aggregate<std::vector>& that) const - requires std::is_same_v<Container, std::vector<constant_value, Alloc<constant_value>>> - { - if (this->container->size() != that.container->size()) - { - return false; - } - return std::ranges::equal(*this->container, *that.container, constant_value_hash{}); - } - - bool operator==(const constant_aggregate<ordered_map>& that) const - requires std::is_same_v<Container, ordered_map<constant_value, Alloc<constant_value>>> - { - if (this->container->size() != that.container->size()) - { - return false; - } - const constant_value_hash comparator{}; - - return std::ranges::equal(*this->container, *that.container, - [comparator](const auto& lhs, const auto& rhs) { - return lhs.first == rhs.first && comparator(lhs.second, rhs.second); - }); - } - - bool operator!=(const constant_aggregate& that) const + bool operator==(const constant_aggregate<C, Alloc>& that) const { - return !(*this == that); + return *this->container == *that.container; } }; @@ -540,3 +647,9 @@ struct std::hash<elna::boot::constant_aggregate<elna::boot::ordered_map>> { std::size_t operator()(const elna::boot::constant_aggregate<elna::boot::ordered_map>& key) const noexcept; }; + +template<> +struct std::hash<elna::boot::integer_literal> +{ + std::size_t operator()(const elna::boot::integer_literal& key) const noexcept; +}; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index b5ba5e0..944c4d0 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -74,7 +74,7 @@ namespace elna::gcc void visit(boot::procedure_call *call) override; void visit(boot::cast_expression *expression) override; void visit(boot::literal<std::int32_t> *literal) override; - void visit(boot::literal<std::uint32_t> *literal) override; + void visit(boot::literal<boot::integer_literal> *literal) override; void visit(boot::literal<double> *literal) override; void visit(boot::literal<bool> *boolean) override; void visit(boot::literal<unsigned char> *character) override; |
