aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/elna/boot/ast.h75
-rw-r--r--include/elna/boot/dependency.h6
-rw-r--r--include/elna/boot/driver.h16
-rw-r--r--include/elna/boot/materialization.h41
-rw-r--r--include/elna/boot/name_analysis.h40
-rw-r--r--include/elna/boot/result.h162
-rw-r--r--include/elna/boot/symbol.h19
-rw-r--r--include/elna/boot/validation.h11
-rw-r--r--include/elna/gcc/elna-builtins.h2
-rw-r--r--include/elna/gcc/elna-tree.h2
10 files changed, 312 insertions, 62 deletions
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<typename T>
+ struct literal_type_id;
+
+ template<>
+ struct literal_type_id<integer_literal>
+ {
+ static constexpr int value = 1;
+ };
+
+ template<>
+ struct literal_type_id<double>
+ {
+ static constexpr int value = 2;
+ };
+
+ template<>
+ struct literal_type_id<bool>
+ {
+ static constexpr int value = 3;
+ };
+
+ template<>
+ struct literal_type_id<unsigned char>
+ {
+ static constexpr int value = 4;
+ };
+
+ template<>
+ struct literal_type_id<std::nullptr_t>
+ {
+ static constexpr int value = 5;
+ };
+
+ template<>
+ struct literal_type_id<std::string>
+ {
+ static constexpr int value = 6;
+ };
+
+ template<typename T>
+ concept literal_type = requires { literal_type_id<T>::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<typename T>
+ template<literal_type T>
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_type T>
+ literal<T> *is_a()
+ {
+ return tag() == literal_type_id<T>::value
+ ? static_cast<literal<T > *>(this)
+ : nullptr;
+ }
};
/**
@@ -893,14 +952,17 @@ namespace elna::boot
~unit() override;
};
- template<typename T>
+ template<literal_type T>
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<T>::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<std::string>& segments);
error_list analyze_semantics(std::unique_ptr<unit>& tree, symbol_bag& bag,
const target_info& target);
@@ -54,8 +54,8 @@ namespace elna::boot
using iterator = std::unordered_map<std::filesystem::path, symbol_bag>::iterator;
using const_iterator = std::unordered_map<std::filesystem::path, symbol_bag>::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<std::string> escape_string(const char *escape);
+
+ template<typename T>
+ std::optional<std::pair<std::make_unsigned_t<T>, integer_sign>>
+ parse_integer(const char *text, const int base = 10)
+ {
+ using unsigned_t = std::make_unsigned_t<T>;
+ errno = 0;
+ std::uint64_t result = strtoull(text, nullptr, base);
+
+ if (errno == ERANGE || result > std::numeric_limits<unsigned_t>::max())
+ {
+ return std::nullopt;
+ }
+ return std::make_pair(static_cast<unsigned_t>(result),
+ std::is_signed_v<T> ? 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
+<http://www.gnu.org/licenses/>. */
+
+#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<integer_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<source_position> 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<identifier> identifiers;
+ };
+ using payload_type = std::variant<not_initialized, kind>;
- const_qualifier_error(const source_position position, kind error_kind);
+ name_analysis_error(const source_position position, payload_type payload);
+ std::optional<std::pair<std::string, source_position>> 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<identifier> identifiers;
-
- public:
- not_initialized_error(const source_position position, std::vector<identifier> identifiers);
-
- std::string what() const override;
- std::optional<std::pair<std::string, source_position>> 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<integer_literal> add(const integer_literal& that) const;
+
+ /**
+ * \param that Subtrahend.
+ * \return Difference of \c this and \p that, or \c nullopt on overflow.
+ */
std::optional<integer_literal> sub(const integer_literal& that) const;
+
+ /**
+ * \return Product of \c this and \p that, or \c nullopt on overflow.
+ */
std::optional<integer_literal> 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<integer_literal> 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<integer_literal> mod(const integer_literal& that) const;
+
+ /**
+ * \return Arithmetic negation, or \c nullopt if unsigned or overflowing.
+ */
std::optional<integer_literal> neg() const;
+
+ /**
+ * \return Signed negation of an unsigned magnitude, or \c nullopt on overflow.
+ */
+ std::optional<integer_literal> negate() const;
+
+ /**
+ * \param that Bit count.
+ * \return \c this shifted left by \p that bits, wrapping within the current size.
+ */
std::optional<integer_literal> 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<integer_literal> 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<typename U>
bool operator==(U that) const
requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int))
{
return mpz_cmp_ui(this->raw, that) == 0;
}
+
+ /// \overload
template<typename U>
bool operator==(U that) const
requires(is_signed_v<U> && 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<typename U>
std::weak_ordering operator<=>(U that) const
requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int))
{
return mpz_cmp_ui(this->raw, that) <=> 0;
}
+
+ /// \overload
template<typename U>
std::weak_ordering operator<=>(U that) const
requires(is_signed_v<U> && 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<typename T>
static integer_literal from(T initial)
requires is_integral<T>
@@ -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<typename T>
+ static std::optional<integer_literal> from(std::size_t size, T initial)
+ requires is_integral<T>
+ {
+ integer_literal result{ std::is_signed_v<T>, 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<integer_literal> 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<typename T>
@@ -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<iterator, bool> 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<type_properties, target_integer_count> int_properties;
+ std::array<type_properties, target_integer_count> 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<variable_info> is_variable() override;
};
- std::shared_ptr<symbol_table> builtin_symbol_table();
+ std::shared_ptr<symbol_table> 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_type>& 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<symbol_table> 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<std::string>& 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<symbol_table>& symbols, tree type = NULL_TREE);