From bf7416a3ef9f0b787dcaebf816f3f3e6e385ff42 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 13 Aug 2026 22:55:06 +0200 Subject: Implement fixed-size integers --- include/elna/boot/ast.h | 75 ++++++++++++++++- include/elna/boot/dependency.h | 6 +- include/elna/boot/driver.h | 16 ++++ include/elna/boot/materialization.h | 41 +++++++++ include/elna/boot/name_analysis.h | 40 ++++----- include/elna/boot/result.h | 162 +++++++++++++++++++++++++++++++----- include/elna/boot/symbol.h | 19 ++--- include/elna/boot/validation.h | 11 ++- include/elna/gcc/elna-builtins.h | 2 + include/elna/gcc/elna-tree.h | 2 - 10 files changed, 312 insertions(+), 62 deletions(-) create mode 100644 include/elna/boot/materialization.h (limited to 'include') diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 1ea17fb..2645026 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -62,6 +62,55 @@ namespace elna::boot plus }; + enum class integer_sign + { + _unsigned, + unmarked, + negative + }; + + template + struct literal_type_id; + + template<> + struct literal_type_id + { + static constexpr int value = 1; + }; + + template<> + struct literal_type_id + { + static constexpr int value = 2; + }; + + template<> + struct literal_type_id + { + static constexpr int value = 3; + }; + + template<> + struct literal_type_id + { + static constexpr int value = 4; + }; + + template<> + struct literal_type_id + { + static constexpr int value = 5; + }; + + template<> + struct literal_type_id + { + static constexpr int value = 6; + }; + + template + concept literal_type = requires { literal_type_id::value; }; + class variable_declaration; class procedure_declaration; class type_declaration; @@ -95,7 +144,7 @@ namespace elna::boot class dereference_expression; class designator_expression; class literal_expression; - template + template class literal; class defer_statement; class empty_statement; @@ -488,6 +537,16 @@ namespace elna::boot { public: literal_expression *is_literal() override; + + virtual int tag() const = 0; + + template + literal *is_a() + { + return tag() == literal_type_id::value + ? static_cast *>(this) + : nullptr; + } }; /** @@ -893,14 +952,17 @@ namespace elna::boot ~unit() override; }; - template + template class literal : public literal_expression { public: T value; + const integer_sign wants_signed; + const bool has_explicit_size; - literal(const source_position position, const T& value) - : node(position), value(value) + literal(const source_position position, const T& value, const integer_sign wants_signed, + const bool has_explicit_size = true) + : node(position), value(value), wants_signed(wants_signed), has_explicit_size(has_explicit_size) { } @@ -908,6 +970,11 @@ namespace elna::boot { visitor->visit(this); } + + int tag() const override + { + return literal_type_id::value; + } }; class defer_statement : public statement diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h index b3c1155..854c661 100644 --- a/include/elna/boot/dependency.h +++ b/include/elna/boot/dependency.h @@ -37,7 +37,7 @@ namespace elna::boot dependency() = default; }; - dependency read_source(std::istream& entry_point); + dependency read_source(std::istream& entry_point, const target_info& target); std::filesystem::path build_path(const std::vector& segments); error_list analyze_semantics(std::unique_ptr& tree, symbol_bag& bag, const target_info& target); @@ -54,8 +54,8 @@ namespace elna::boot using iterator = std::unordered_map::iterator; using const_iterator = std::unordered_map::const_iterator; - explicit dependency_state(T custom) - : globals(builtin_symbol_table()), custom(custom) + explicit dependency_state(T custom, const target_info& target) + : globals(builtin_symbol_table(target)), custom(custom) { } diff --git a/include/elna/boot/driver.h b/include/elna/boot/driver.h index 60d40fb..2503e39 100644 --- a/include/elna/boot/driver.h +++ b/include/elna/boot/driver.h @@ -54,4 +54,20 @@ namespace elna::boot */ void normalize_newlines(std::string& string); std::optional escape_string(const char *escape); + + template + std::optional, integer_sign>> + parse_integer(const char *text, const int base = 10) + { + using unsigned_t = std::make_unsigned_t; + errno = 0; + std::uint64_t result = strtoull(text, nullptr, base); + + if (errno == ERANGE || result > std::numeric_limits::max()) + { + return std::nullopt; + } + return std::make_pair(static_cast(result), + std::is_signed_v ? integer_sign::unmarked : integer_sign::_unsigned); + } } diff --git a/include/elna/boot/materialization.h b/include/elna/boot/materialization.h new file mode 100644 index 0000000..21a217d --- /dev/null +++ b/include/elna/boot/materialization.h @@ -0,0 +1,41 @@ +/* Literal sign folding. + 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 +. */ + +#pragma once + +#include "elna/boot/ast.h" + +namespace elna::boot +{ + class materialization_error final : public error + { + public: + materialization_error(const source_position position); + + std::string what() const override; + }; + + class materialization_visitor final : public walking_visitor, public error_container + { + const target_info& target; + + public: + explicit materialization_visitor(const target_info& target); + + void visit(literal *literal) override; + }; +} diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index f6663ea..9e10fab 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -36,7 +36,13 @@ namespace elna::boot class declaration_error final : public error { public: - enum class kind { undeclared_type, undeclared_trait, undeclared_symbol, local_export }; + enum class kind + { + undeclared_type, + undeclared_trait, + undeclared_symbol, + local_export + }; struct redefinition { std::optional original; @@ -57,17 +63,27 @@ namespace elna::boot * \c const qualifier used incorrectly — wrong position or * duplicate. */ - class const_qualifier_error final : public error + class name_analysis_error final : public error { public: - enum class kind { array_position, duplicate }; + enum class kind + { + array_position, + duplicate + }; + struct not_initialized + { + std::vector identifiers; + }; + using payload_type = std::variant; - const_qualifier_error(const source_position position, kind error_kind); + name_analysis_error(const source_position position, payload_type payload); + std::optional> note() const override; std::string what() const override; private: - kind error_kind; + payload_type payload; }; /** @@ -95,17 +111,6 @@ namespace elna::boot payload_type payload; }; - class not_initialized_error final : public error - { - std::vector identifiers; - - public: - not_initialized_error(const source_position position, std::vector identifiers); - - std::string what() const override; - std::optional> note() const override; - }; - /** * Origin of a field in a composite type. */ @@ -183,9 +188,6 @@ namespace elna::boot public: forward_table unresolved; - explicit declaration_visitor(); - - void visit(import_declaration *) override; void visit(unit *unit) override; void visit(type_declaration *declaration) override; void visit(procedure_declaration *declaration) override; diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index ee73f33..03d589e 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -273,40 +273,117 @@ namespace elna::boot integer_literal(const integer_literal& that); ~integer_literal(); + /** + * \param that Summand. + * \return Sum of \c this and \p that, or \c nullopt on overflow. + */ std::optional add(const integer_literal& that) const; + + /** + * \param that Subtrahend. + * \return Difference of \c this and \p that, or \c nullopt on overflow. + */ std::optional sub(const integer_literal& that) const; + + /** + * \return Product of \c this and \p that, or \c nullopt on overflow. + */ std::optional mul(const integer_literal& that) const; + + /** + * \param that Factor. + * \return Quotient of \c this and \p that, or \c nullopt if \p that is zero. + */ std::optional div(const integer_literal& that) const; + + /** + * \param that Divisor. + * \return Remainder of \c this divided by \p that, or \c nullopt if \p that is zero. + */ std::optional mod(const integer_literal& that) const; + + /** + * \return Arithmetic negation, or \c nullopt if unsigned or overflowing. + */ std::optional neg() const; + + /** + * \return Signed negation of an unsigned magnitude, or \c nullopt on overflow. + */ + std::optional negate() const; + + /** + * \param that Bit count. + * \return \c this shifted left by \p that bits, wrapping within the current size. + */ std::optional shl(const integer_literal& that) const; + + /** + * \param that Bit count. + * \return \c this shifted right by \p that bits, or \c nullopt if the shift is out of range. + */ std::optional shr(const integer_literal& that) const; + /** + * \param that Operand. + * \return Bitwise OR of \c this and \p that. + */ integer_literal operator|(const integer_literal& that) const; + + /** + * \param that Operand. + * \return Bitwise AND of \c this and \p that. + */ integer_literal operator&(const integer_literal& that) const; + + /** + * \param that Operand. + * \return Bitwise XOR of \c this and \p that. + */ integer_literal operator^(const integer_literal& that) const; + + /** + * \return Bitwise complement of \c this. + */ integer_literal operator~() const; + /** + * \param that Comparand. + * \return Whether \c this and \p that hold the same value. + */ bool operator==(const integer_literal& that) const; - std::weak_ordering operator<=>(const integer_literal& that) const; + + /// \overload template bool operator==(U that) const requires(is_unsigned_v && sizeof(U) <= sizeof(unsigned long int)) { return mpz_cmp_ui(this->raw, that) == 0; } + + /// \overload template bool operator==(U that) const requires(is_signed_v && sizeof(U) <= sizeof(signed long int)) { return mpz_cmp_si(this->raw, that) == 0; } + + /** + * \param that Comparand. + * \return Ordering of \c this relative to \p that. + */ + std::weak_ordering operator<=>(const integer_literal& that) const; + + /// \overload template std::weak_ordering operator<=>(U that) const requires(is_unsigned_v && sizeof(U) <= sizeof(unsigned long int)) { return mpz_cmp_ui(this->raw, that) <=> 0; } + + /// \overload template std::weak_ordering operator<=>(U that) const requires(is_signed_v && sizeof(U) <= sizeof(signed long int)) @@ -317,10 +394,21 @@ namespace elna::boot integer_literal& operator=(integer_literal&& that) noexcept; integer_literal& operator=(const integer_literal& that); + /// \return Whether the literal is signed. bool is_signed() const; + + /// \return Storage size in bytes. std::size_t size() const; + + /** + * \param base Base. + * \return String representation in the given \p base. + */ std::string to_string(const std::uint8_t base = 10U) const; + /// \return Whether the stored value is negative. + bool is_negative() const; + /** * Exports the stored value as a host \p T. * @@ -359,10 +447,29 @@ namespace elna::boot * returned. * * \param target_size Size of the target type in bytes. + * \param target_signed Signedness of the target type. * \return Whether the value fits and has been changed. */ + bool fit_into(bool target_signed, const std::size_t target_size); + + /// \overload bool fit_into(const std::size_t target_size); + /** + * Cast the value to the given size and signedness, discarding bits if + * needed and reinterpreting bits on sign change. + * + * \param target_signed Result signedness. + * \param target_size Result size. + * \return Cast result. + */ + integer_literal cast_to(bool target_signed, std::size_t target_size) const; + + /** + * \tparam T Initializer type. + * \param initial Initial literal value. + * \return A literal constructed from the host value \p initial. + */ template static integer_literal from(T initial) requires is_integral @@ -374,6 +481,29 @@ namespace elna::boot return result; } + /** + * Constructs a literal of \p size bytes from \p initial inheriting its + * signedness. + * + * \tparam T Initializer type. + * \return A literal of \p size bytes constructed from \p initial, or \c nullopt on overflow. + */ + template + static std::optional from(std::size_t size, T initial) + requires is_integral + { + integer_literal result{ std::is_signed_v, size }; + + mpz_import(result.raw, 1, 1, sizeof(T), 0, 0, &initial); + + return std::move(result).check(); + } + + /** + * \param lhs Left hand side. + * \param lhs Right hand side. + * Swaps the contents of \p lhs and \p rhs. + */ friend void swap(integer_literal& lhs, integer_literal& rhs) noexcept; private: @@ -382,11 +512,9 @@ namespace elna::boot mpz_t raw; integer_literal(bool is_signed, std::size_t size); - bool fits_in(const std::size_t bits) const; + bool fits_in(bool starget_signed, const std::size_t bits) const; std::optional check() &&; - bool is_negative() const; bool is_negative_minimum(const std::size_t bits) const; - bool is_negative_minimum() const; std::size_t bits() const; template @@ -474,9 +602,7 @@ namespace elna::boot } } - /** - * \overload - */ + /// \overload const_iterator find(const key_type& key) const { auto search_result = this->index_map.find(key); @@ -509,9 +635,7 @@ namespace elna::boot return { this->payload.begin() + insert_result.first->second, insert_result.second }; } - /** - * \overload - */ + /// \overload std::pair insert(const key_type& key, mapped_type&& value) { auto insert_result = this->index_map.emplace(key, this->payload.size()); @@ -532,9 +656,7 @@ namespace elna::boot return this->payload.begin(); } - /** - * \overload - */ + /// \overload const_iterator begin() const { return this->payload.cbegin(); @@ -550,9 +672,7 @@ namespace elna::boot return this->payload.end(); } - /** - * \overload - */ + /// \overload const_iterator end() const { return this->payload.cend(); @@ -577,9 +697,7 @@ namespace elna::boot return this->payload[this->index_map.find(key)->second].second; } - /** - * \overload - */ + /// \overload const mapped_type& operator[](const key_type& key) const { return this->payload[this->index_map.find(key)->second].second; @@ -605,13 +723,15 @@ namespace elna::boot std::size_t alignment; }; + constexpr std::size_t target_integer_count = 5; + /** * Target machine information, populated by the compiler backend glue layer. */ struct target_info { - type_properties int_properties; - type_properties word_properties; + std::array int_properties; + std::array word_properties; type_properties pointer_properties; type_properties char_properties; type_properties float_properties; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index bd07a7e..f048016 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -123,7 +123,7 @@ namespace elna::boot const std::string name; type referent; - explicit alias_type(const std::string& name); + explicit alias_type(const std::string& name, type referent = type()); }; struct pointer_type @@ -158,8 +158,9 @@ namespace elna::boot struct primitive_type { const std::string identifier; + const type_properties properties; - explicit primitive_type(const std::string& identifier); + primitive_type(const std::string& identifier, const type_properties& properties); }; struct record_type @@ -243,9 +244,7 @@ namespace elna::boot return this->entries.begin(); } - /** - * \overload - */ + /// \overload const_iterator begin() const { return this->entries.cbegin(); @@ -261,9 +260,7 @@ namespace elna::boot return this->entries.end(); } - /** - * \overload - */ + /// \overload const_iterator end() const { return this->entries.cend(); @@ -405,7 +402,7 @@ namespace elna::boot std::shared_ptr is_variable() override; }; - std::shared_ptr builtin_symbol_table(); + std::shared_ptr builtin_symbol_table(const target_info& target); /** * Symbol bag contains: @@ -520,9 +517,7 @@ namespace elna::boot */ type resolve_underlying_type(const type& alias); - /** - * \overload - */ + /// \overload type resolve_underlying_type(const std::shared_ptr& alias); /** diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h index 4a256fa..171ac1d 100644 --- a/include/elna/boot/validation.h +++ b/include/elna/boot/validation.h @@ -49,7 +49,7 @@ namespace elna::boot * Validates: * - case label uniqueness */ - class validation_visitor final : public walking_visitor, public error_container + class validation_visitor final : public empty_visitor, public error_container { symbol_bag& bag; evaluator constant_evaluator; @@ -60,5 +60,14 @@ namespace elna::boot void visit(unit *unit) override; void visit(procedure_declaration *declaration) override; void visit(case_statement *statement) override; + + void visit(assign_statement *) override; + void visit(if_statement *) override; + void visit(while_statement *) override; + void visit(repeat_statement *) override; + void visit(for_statement *) override; + void visit(defer_statement *) override; + void visit(empty_statement *) override; + void visit(procedure_call *) override; }; } diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h index 846a8db..9f24b00 100644 --- a/include/elna/gcc/elna-builtins.h +++ b/include/elna/gcc/elna-builtins.h @@ -27,6 +27,8 @@ along with GCC; see the file COPYING3. If not see namespace elna::gcc { + const elna::boot::target_info& get_host_target(); + void init_ttree(); std::shared_ptr builtin_symbol_table(); diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index 8f7e306..504b14c 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -60,8 +60,6 @@ namespace elna::gcc tree build_slice(tree slice_type, tree ptr, tree length); tree build_enumeration_type(const std::vector& members); - const elna::boot::target_info& get_host_target(); - tree extract_constant(tree expression); tree constant_to_tree(const boot::constant_value& value, const std::shared_ptr& symbols, tree type = NULL_TREE); -- cgit v1.2.3