diff options
29 files changed, 589 insertions, 453 deletions
@@ -1,2 +1,3 @@ build/ jit/ +.cache/ diff --git a/boot/ast.cc b/boot/ast.cc index 9b21f56..287b7aa 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -186,7 +186,7 @@ namespace elna::boot __builtin_unreachable(); } - void empty_visitor::visit(literal<double> *) + void empty_visitor::visit(literal<float_literal> *) { __builtin_unreachable(); } @@ -509,7 +509,7 @@ namespace elna::boot { } - void walking_visitor::visit(literal<double> *) + void walking_visitor::visit(literal<float_literal> *) { } diff --git a/boot/evaluator.cc b/boot/evaluator.cc index d43ef11..1d10346 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -237,9 +237,9 @@ namespace elna::boot { return constant_value{ integer_subject->value }; } - else if (auto *double_subject = subject.is_a<double>()) + else if (auto *float_subject = subject.is_a<float_literal>()) { - return constant_value{ double_subject->value }; + return constant_value{ float_subject->value }; } else if (auto *boolean_subject = subject.is_a<bool>()) { @@ -395,7 +395,7 @@ namespace elna::boot } return std::nullopt; } - if constexpr (std::is_same_v<T, double>) + if constexpr (std::is_same_v<T, float_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 (is_integral<T>) + if constexpr (is_integral_v<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 (is_integral<T>) + if constexpr (is_integral_v<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 (is_integral<T>) + if constexpr (is_integral_v<T>) { T result; return __builtin_mul_overflow(lhs, rhs, &result) @@ -498,7 +498,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (std::is_floating_point_v<T>) + else if constexpr (std::is_same_v<T, float_literal>) { if (auto result = add_overflow(lhs, rhs)) { @@ -514,7 +514,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (std::is_floating_point_v<T>) + else if constexpr (std::is_same_v<T, float_literal>) { if (auto result = sub_overflow(lhs, rhs)) { @@ -530,7 +530,7 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (std::is_floating_point_v<T>) + else if constexpr (std::is_same_v<T, float_literal>) { if (auto result = mul_overflow(lhs, rhs)) { @@ -546,12 +546,9 @@ namespace elna::boot return constant_value{ result.value() }; } } - else if constexpr (std::is_floating_point_v<T>) + else if constexpr (std::is_same_v<T, float_literal>) { - if (rhs != static_cast<T>(0)) - { - return constant_value{ lhs / rhs }; - } + return constant_value{ lhs / rhs }; } return std::nullopt; case remainder: @@ -625,25 +622,25 @@ namespace elna::boot case not_equals: return constant_value{ lhs != rhs }; case less: - if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>) + if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs < rhs }; } return std::nullopt; case greater: - if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>) + if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs > rhs }; } return std::nullopt; case less_equal: - if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>) + if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs <= rhs }; } return std::nullopt; case greater_equal: - if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>) + if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>) { return constant_value{ lhs >= rhs }; } @@ -789,23 +786,23 @@ namespace elna::boot { type const resolved = resolve_underlying_type(subject.types.front()); - if (auto resolved_primitive = resolved.get<primitive_type>()) + if (is_integer_type(resolved)) { - if (resolved_primitive->identifier.starts_with("Int")) - { - const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT; - const integer_literal one = integer_literal::from<std::ptrdiff_t>( - resolved_primitive->properties.size, 1U).value(); + const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>(); + const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT; + const integer_literal one = integer_literal::from<std::ptrdiff_t>( + resolved_primitive->properties.size, 1U).value(); - return constant_value{ - one.shl(integer_literal::from<std::size_t>(bits - 1U)).value() - }; - } - if (resolved_primitive->identifier.starts_with("Word")) - { - return constant_value{ integer_literal::from<std::size_t>( - resolved_primitive->properties.size, 0U).value() }; - } + return constant_value{ + one.shl(integer_literal::from<std::size_t>(bits - 1U)).value() + }; + } + if (is_word_type(resolved)) + { + const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>(); + + return constant_value{ integer_literal::from<std::size_t>( + resolved_primitive->properties.size, 0U).value() }; } if (is_primitive_type(resolved, "Char")) { @@ -815,9 +812,13 @@ namespace elna::boot { return constant_value{ false }; } - if (is_primitive_type(resolved, "Float")) + if (is_primitive_type(resolved, "Single")) { - return constant_value{ -std::numeric_limits<double>::max() }; + return constant_value{ float_literal::from(std::numeric_limits<float>::min()) }; + } + if (is_primitive_type(resolved, "Double")) + { + return constant_value{ float_literal::from(std::numeric_limits<double>::min()) }; } if (auto enumeration = resolved.get<enumeration_type>()) { @@ -828,26 +829,25 @@ namespace elna::boot { type const resolved = resolve_underlying_type(subject.types.front()); - if (auto resolved_primitive = resolved.get<primitive_type>()) + if (is_integer_type(resolved)) { - if (resolved_primitive->identifier.starts_with("Int")) - { - const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT; - const integer_literal one = integer_literal::from<std::ptrdiff_t>( - resolved_primitive->properties.size, 1U).value(); - const std::optional<integer_literal> minimum = one.shl( - integer_literal::from<std::size_t>(bits - 1U)); + const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>(); + const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT; + const integer_literal one = integer_literal::from<std::ptrdiff_t>( + resolved_primitive->properties.size, 1U).value(); + const std::optional<integer_literal> minimum = one.shl( + integer_literal::from<std::size_t>(bits - 1U)); - return constant_value{ ~minimum.value() }; - } - if (resolved_primitive->identifier.starts_with("Word")) - { - // All bits set. - std::optional<integer_literal> zero = integer_literal::from<std::size_t>( - resolved_primitive->properties.size, 0U); + return constant_value{ ~minimum.value() }; + } + if (is_word_type(resolved)) + { + const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>(); + // All bits set. + std::optional<integer_literal> zero = integer_literal::from<std::size_t>( + resolved_primitive->properties.size, 0U); - return constant_value{ ~zero.value() }; - } + return constant_value{ ~zero.value() }; } if (is_primitive_type(resolved, "Char")) { @@ -857,9 +857,13 @@ namespace elna::boot { return constant_value{ true }; } - if (is_primitive_type(resolved, "Float")) + if (is_primitive_type(resolved, "Single")) + { + return constant_value{ float_literal::from(std::numeric_limits<float>::max()) }; + } + if (is_primitive_type(resolved, "Double")) { - return constant_value{ std::numeric_limits<double>::max() }; + return constant_value{ float_literal::from(std::numeric_limits<double>::max()) }; } if (auto enumeration = resolved.get<enumeration_type>()) { diff --git a/boot/lexer.ll b/boot/lexer.ll index 888c17d..eb1bbd3 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see } while (0); #include <sstream> +#include <cmath> #include "parser.hh" #undef YY_DECL @@ -50,6 +51,7 @@ HIGIT [0-9a-fA-F] HEXADECIMAL 0[xX]{HIGIT}+ BIGIT [01] NATURAL [1-9][[:digit:]]* +EXPONENT [eE][+-]?[[:digit:]]+ %% %{ @@ -325,31 +327,17 @@ to { REJECT; } } -[[:digit:]]+\.[[:digit:]]+([eE][+-]?[[:digit:]]+)? { +[[:digit:]]+(\.[[:digit:]]+{EXPONENT}?|{EXPONENT}) { errno = 0; double result = strtod(yytext, NULL); - if (errno == ERANGE) - { - REJECT; - } - else - { - return yy::parser::make_FLOAT(result, this->location); - } + return yy::parser::make_DOUBLE(result, this->location); } -[[:digit:]]+[eE][+-]?[[:digit:]]+ { +[[:digit:]]+(\.[[:digit:]]+{EXPONENT}?|{EXPONENT})f { errno = 0; - double result = strtod(yytext, NULL); + float result = strtof(yytext, NULL); - if (errno == ERANGE) - { - REJECT; - } - else - { - return yy::parser::make_FLOAT(result, this->location); - } + return yy::parser::make_SINGLE(result, this->location); } '([^'\\\n]|\\.|\\\n)+' { std::optional<std::uint8_t> result = parse_byte_literal(yytext); diff --git a/boot/materialization.cc b/boot/materialization.cc index 8c436b3..4b58ac2 100644 --- a/boot/materialization.cc +++ b/boot/materialization.cc @@ -19,14 +19,22 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - materialization_error::materialization_error(const source_position position) - : diagnostic(position) + materialization_error::materialization_error(const source_position position, kind error_kind) + : diagnostic(position), m_kind(error_kind) { } std::string materialization_error::what() const { - return "Integer literal overflows"; + switch (this->m_kind) + { + using enum kind; + case integer_overflow: + return "Integer literal overflows"; + case real_overflow: + return "Real literal overflows"; + } + __builtin_unreachable(); } materialization_visitor::materialization_visitor(const target_info& target) @@ -46,12 +54,14 @@ namespace elna::boot if (!literal->has_explicit_size && !literal->value.fit_into(true, target.int_properties.front().size)) { - add_error<materialization_error>(literal->position()); + add_error<materialization_error>(literal->position(), + materialization_error::kind::integer_overflow); } } else { - add_error<materialization_error>(literal->position()); + add_error<materialization_error>(literal->position(), + materialization_error::kind::integer_overflow); } break; case unmarked: @@ -62,7 +72,8 @@ namespace elna::boot if (!literal->value.fit_into(true, target_size)) { - add_error<materialization_error>(literal->position()); + add_error<materialization_error>(literal->position(), + materialization_error::kind::integer_overflow); } break; } @@ -70,9 +81,19 @@ namespace elna::boot if (!literal->has_explicit_size && !literal->value.fit_into(false, target.word_properties.front().size)) { - add_error<materialization_error>(literal->position()); + add_error<materialization_error>(literal->position(), + materialization_error::kind::integer_overflow); } break; } } + + void materialization_visitor::visit(literal<float_literal> *literal) + { + if (!literal->value.is_finite()) + { + add_error<materialization_error>(literal->position(), + materialization_error::kind::real_overflow); + } + } } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 4b69f9c..1ad66a8 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -912,9 +912,11 @@ namespace elna::boot this->current_type = type(); } - void name_analysis_visitor::visit(literal<double> *literal) + void name_analysis_visitor::visit(literal<float_literal> *literal) { - literal->type_decoration = lookup_primitive_type("Float"); + literal->type_decoration = literal->value.format() == float_literal::format_kind::binary32 + ? lookup_primitive_type("Single") + : lookup_primitive_type("Double"); this->current_type = type(); } diff --git a/boot/parser.yy b/boot/parser.yy index 411922d..c45fdbc 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -82,7 +82,8 @@ along with GCC; see the file COPYING3. If not see %token <std::pair<std::uint8_t, elna::boot::integer_sign>> INTEGER8 WORD8 %token <std::pair<std::uint16_t, elna::boot::integer_sign>> INTEGER16 WORD16 %token <std::pair<std::uint32_t, elna::boot::integer_sign>> INTEGER32 WORD32 -%token <double> FLOAT +%token <float> SINGLE +%token <double> DOUBLE %token <std::uint32_t> CHARACTER %token <std::string> STRING %token <bool> BOOLEAN @@ -298,9 +299,15 @@ literal: $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } - | FLOAT + | SINGLE { - $$ = new boot::literal<double>(boot::make_position(@$), $1, boot::integer_sign::unmarked); + $$ = new boot::literal<boot::float_literal>(boot::make_position(@$), + boot::float_literal::from($1), boot::integer_sign::unmarked); + } + | DOUBLE + { + $$ = new boot::literal<boot::float_literal>(boot::make_position(@$), + boot::float_literal::from($1), boot::integer_sign::unmarked); } | BOOLEAN { diff --git a/boot/result.cc b/boot/result.cc index 3970b1f..d25b85b 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -18,6 +18,8 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/result.h" #include <algorithm> +#include <charconv> +#include <cmath> #include <cstring> #include <numeric> @@ -467,6 +469,85 @@ namespace elna::boot return mpz_sgn(this->raw) < 0; } + float_literal::float_literal(format_kind binary_format, double value) + : m_format(binary_format), raw(value) + { + } + + float_literal float_literal::rounded(format_kind binary_format, double value) + { + if (binary_format == format_kind::binary32) + { + return float_literal(format_kind::binary32, static_cast<float>(value)); + } + return float_literal(binary_format, value); + } + + float_literal float_literal::operator+(const float_literal& that) const + { + return rounded(this->m_format, this->raw + that.raw); + } + + float_literal float_literal::operator-(const float_literal& that) const + { + return rounded(this->m_format, this->raw - that.raw); + } + + float_literal float_literal::operator*(const float_literal& that) const + { + return rounded(this->m_format, this->raw * that.raw); + } + + float_literal float_literal::operator/(const float_literal& that) const + { + return rounded(this->m_format, this->raw / that.raw); + } + + float_literal float_literal::operator-() const + { + return rounded(this->m_format, -this->raw); + } + + bool float_literal::operator==(const float_literal& that) const + { + return this->raw == that.raw; + } + + std::partial_ordering float_literal::operator<=>(const float_literal& that) const + { + return this->raw <=> that.raw; + } + + float_literal::format_kind float_literal::format() const + { + return this->m_format; + } + + std::string float_literal::to_string() const + { + constexpr std::size_t shortest_round_trip = 24; + std::array<char, shortest_round_trip + 4> buffer; + + auto float_chars = std::to_chars(buffer.begin(), buffer.end(), this->raw); + + return std::string(buffer.begin(), float_chars.ptr); + } + + float_literal float_literal::cast_to(format_kind binary_format) const + { + return rounded(binary_format, this->raw); + } + + double float_literal::value() const + { + return this->raw; + } + + bool float_literal::is_finite() const + { + return std::isfinite(this->raw); + } + std::size_t constant_value_hash::operator()(const elna::boot::constant_value& value) const noexcept { return std::visit([](auto&& alternative) -> std::size_t { @@ -541,3 +622,9 @@ std::size_t std::hash<elna::boot::integer_literal>::operator()(const elna::boot: } return 0; } + +std::size_t std::hash<elna::boot::float_literal>::operator()(const elna::boot::float_literal& key) const noexcept +{ + // -0.0 and 0.0 compare equal, so their hashes must agree. + return std::hash<double>{}(key == 0.0 ? 0.0 : key.value()); +} diff --git a/boot/symbol.cc b/boot/symbol.cc index 05eea31..fdf6a65 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -326,8 +326,10 @@ namespace elna::boot const type pointer = type(std::make_shared<primitive_type>("Pointer", target.pointer_properties)); result->enter("Pointer", std::make_shared<type_info>(pointer)); - result->enter("Float", - std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float", target.float_properties)))); + result->enter("Single", + std::make_shared<type_info>(type(std::make_shared<primitive_type>("Single", target.single_properties)))); + result->enter("Double", + std::make_shared<type_info>(type(std::make_shared<primitive_type>("Double", target.double_properties)))); const type boolean = type(std::make_shared<primitive_type>("Bool", target.bool_properties)); result->enter("Bool", std::make_shared<type_info>(boolean)); @@ -462,22 +464,41 @@ namespace elna::boot return false; } - bool is_numeric_type(const type& checked) + bool is_integer_type(const type& checked) { - return is_integral_type(checked) - || is_primitive_type(checked, "Float"); + if (auto primitive_checked = checked.get<primitive_type>()) + { + return primitive_checked->identifier.starts_with("Int"); + } + return false; } - bool is_integral_type(const type& checked) + bool is_word_type(const type& checked) { if (auto primitive_checked = checked.get<primitive_type>()) { - return primitive_checked->identifier.starts_with("Int") - || primitive_checked->identifier.starts_with("Word"); + return primitive_checked->identifier.starts_with("Word"); } return false; } + bool is_float_type(const type& checked) + { + return is_primitive_type(checked, "Single") + || is_primitive_type(checked, "Double"); + } + + bool is_numeric_type(const type& checked) + { + return is_integral_type(checked) + || is_float_type(checked); + } + + bool is_integral_type(const type& checked) + { + return is_integer_type(checked) || is_word_type(checked); + } + bool is_discrete_type(const type& checked) { return is_integral_type(checked) @@ -495,7 +516,7 @@ namespace elna::boot bool is_scalar_type(const type& checked) { return is_discrete_type(checked) - || is_primitive_type(checked, "Float") + || is_float_type(checked) || is_any_pointer_type(checked) || checked.get<enumeration_type>() != nullptr; } diff --git a/boot/type_check.cc b/boot/type_check.cc index d830763..2d82579 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -833,7 +833,7 @@ namespace elna::boot auto operation = expression->operation(); type const resolved = resolve_underlying_type(expression->operand().type_decoration); - if (operation == unary_operator::plus) + if (operation == unary_operator::plus || operation == unary_operator::minus) { if (!is_numeric_type(resolved)) { @@ -842,17 +842,6 @@ namespace elna::boot type_mismatch_error::unary{ .operation = operation }); } } - else if (operation == unary_operator::minus) - { - auto signed_primitive = resolved.get<primitive_type>(); - if ((signed_primitive == nullptr || !signed_primitive->identifier.starts_with("Int")) - && !is_primitive_type(resolved, "Float")) - { - add_error<type_mismatch_error>(expression->position(), - expression->operand().type_decoration, - type_mismatch_error::unary{ .operation = operation }); - } - } else if (operation == unary_operator::negation) { if (is_primitive_type(resolved, "Bool")) @@ -933,10 +922,12 @@ namespace elna::boot || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved); break; case division: - case remainder: case multiplication: valid = is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved; break; + case remainder: + valid = is_integral_type(lhs_resolved) && lhs_resolved == rhs_resolved; + break; case less: case greater: case less_equal: @@ -1032,7 +1023,7 @@ namespace elna::boot const type resolved = resolve_underlying_type(trait->type_decoration); if (resolved.get<enumeration_type>() == nullptr - && !is_primitive_type(resolved, "Float") + && !is_float_type(resolved) && !is_discrete_type(resolved)) { add_error<trait_error>(trait->name.position(), trait->name.name(), @@ -22,11 +22,11 @@ load_lib elna-dg.exp # Initialize dg. dg-init -# Main loop. -# Single-module .elna files and multi-module directories, -# both exactly one level below the category directories. -elna-dg-runtest-single [lsort [glob -nocomplain $srcdir/$subdir/*/*.elna]] -elna-dg-runtest-multi [lsort [glob -nocomplain -type d $srcdir/$subdir/*/*]] +# Main loop. Single-module tests are files one level below the category +# directories, multi-module tests are directories one level below any +# category directory, with a sut.elna as the primary module. +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*/*.elna]] "" "" +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*/*/sut.elna]] "" "" # All done. dg-finish diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 95ffb2f..9552abb 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -52,7 +52,8 @@ namespace elna::gcc }, .pointer_properties = get_host_numeric_properties(ptr_type_node), .char_properties = get_host_numeric_properties(elna_char_type_node), - .float_properties = get_host_numeric_properties(elna_float_type_node), + .single_properties = get_host_numeric_properties(float_type_node), + .double_properties = get_host_numeric_properties(double_type_node), .bool_properties = { .size = static_cast<std::size_t>( (TYPE_PRECISION(elna_bool_type_node) + BITS_PER_UNIT - 1) / BITS_PER_UNIT), @@ -72,7 +73,6 @@ namespace elna::gcc TYPE_STRING_FLAG(elna_char_type_node) = 1; elna_pointer_type_node = ptr_type_node; - elna_float_type_node = double_type_node; elna_bool_type_node = boolean_type_node; elna_bool_true_node = boolean_true_node; @@ -109,7 +109,8 @@ namespace elna::gcc declare_builtin_type(builtin_table, "Char", elna_char_type_node); declare_builtin_type(builtin_table, "Bool", elna_bool_type_node); declare_builtin_type(builtin_table, "Pointer", elna_pointer_type_node); - declare_builtin_type(builtin_table, "Float", elna_float_type_node); + declare_builtin_type(builtin_table, "Single", float_type_node); + declare_builtin_type(builtin_table, "Double", double_type_node); return builtin_table; } diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 323ec09..fb898d5 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -29,7 +29,6 @@ along with GCC; see the file COPYING3. If not see #include "gimplify.h" #include "dumpfile.h" #include "stringpool.h" -#include "realmpfr.h" #include "fold-const.h" #include "langhooks.h" @@ -490,34 +489,29 @@ namespace elna::gcc this->symbols, literal_type); } - void generic_visitor::visit(boot::literal<double> *literal) + void generic_visitor::visit(boot::literal<boot::float_literal> *literal) { - REAL_VALUE_TYPE real_value1; - - mpfr_t number; - mpfr_init2(number, SIGNIFICAND_BITS); - mpfr_set_d(number, literal->value, MPFR_RNDN); - - real_from_mpfr(&real_value1, number, double_type_node, MPFR_RNDN); - - this->current_expression = build_real(double_type_node, real_value1); - - mpfr_clear(number); + tree literal_type = get_inner_alias(literal->type_decoration, this->symbols); + this->current_expression = constant_to_tree(boot::constant_value{ literal->value }, + this->symbols, literal_type); } void generic_visitor::visit(boot::literal<bool> *boolean) { - this->current_expression = constant_to_tree(boot::constant_value{ boolean->value }, this->symbols); + this->current_expression = constant_to_tree(boot::constant_value{ boolean->value }, + this->symbols, elna_bool_type_node); } void generic_visitor::visit(boot::literal<std::uint32_t> *character) { - this->current_expression = constant_to_tree(boot::constant_value{ character->value }, this->symbols); + this->current_expression = constant_to_tree(boot::constant_value{ character->value }, + this->symbols, elna_char_type_node); } void generic_visitor::visit(boot::literal<std::nullptr_t> *) { - this->current_expression = constant_to_tree(boot::constant_value{ std::nullptr_t{} }, this->symbols); + this->current_expression = constant_to_tree(boot::constant_value{ std::nullptr_t{} }, + this->symbols, elna_pointer_type_node); } void generic_visitor::visit(boot::literal<std::string> *string) @@ -579,7 +573,8 @@ namespace elna::gcc break; case division: this->current_expression = fold_build2_loc(expression_location, - TRUNC_DIV_EXPR, left_type, left, right); + SCALAR_FLOAT_TYPE_P(left_type) ? RDIV_EXPR : TRUNC_DIV_EXPR, + left_type, left, right); break; case remainder: this->current_expression = fold_build2_loc(expression_location, diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index 5284e42..e4c8a20 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -15,9 +15,6 @@ 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/>. */ -#include <array> -#include <cstring> - #include "elna/gcc/elna-diagnostic.h" #include "elna/gcc/elna-tree.h" #include "elna/gcc/elna1.h" @@ -25,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "function.h" #include "stor-layout.h" #include "diagnostic-core.h" +#include "realmpfr.h" namespace elna::gcc { @@ -251,6 +249,8 @@ namespace elna::gcc tree constant_to_tree(const boot::constant_value& constant_value, const std::shared_ptr<symbol_table>& symbols, tree type) { + gcc_assert(type != NULL_TREE); + if (std::holds_alternative<boot::integer_literal>(constant_value)) { auto literal_value = std::get<boot::integer_literal>(constant_value); @@ -272,17 +272,20 @@ namespace elna::gcc return NULL_TREE; } } - else if (std::holds_alternative<double>(constant_value)) + else if (std::holds_alternative<boot::float_literal>(constant_value)) { - auto real_value = std::get<double>(constant_value); + auto literal = std::get<boot::float_literal>(constant_value); + + mpfr_t number; + mpfr_init2(number, SIGNIFICAND_BITS); + mpfr_set_d(number, literal.value(), MPFR_RNDN); + REAL_VALUE_TYPE real; - constexpr std::size_t bits_size = (sizeof(double) + sizeof(long) - 1) / sizeof(long); - std::array<long, bits_size> target_bits; + real_from_mpfr(&real, number, type, MPFR_RNDN); - std::memcpy(target_bits.data(), &real_value, sizeof(real_value)); - real_from_target(&real, target_bits.data(), REAL_MODE_FORMAT(TYPE_MODE(elna_float_type_node))); + mpfr_clear(number); - return build_real(elna_float_type_node, real); + return build_real(type, real); } else if (std::holds_alternative<bool>(constant_value)) { diff --git a/gcc/testlib/elna-dg.exp b/gcc/testlib/elna-dg.exp index 03620e3..773dd54 100644 --- a/gcc/testlib/elna-dg.exp +++ b/gcc/testlib/elna-dg.exp @@ -16,7 +16,9 @@ load_lib gcc-dg.exp +# # Define elna callbacks for dg.exp. +# proc elna-dg-test { prog do_what extra_tool_flags } { set result \ @@ -31,236 +33,3 @@ proc elna-dg-test { prog do_what extra_tool_flags } { proc elna-dg-prune { system text } { return [gcc-dg-prune $system $text] } - -# Utility routines. - -# -# Escapes a directive argument so that it reaches dejagnu intact. -# -# dg.exp evaluates each extracted directive as a Tcl script -# ("catch $op"), which performs one round of backslash, command and -# variable substitution inside the double-quoted argument. Quoting the -# affected characters here means the argument dejagnu receives is -# exactly what the test author wrote, so @Error messages keep their -# regular expression semantics: "\[2\]" matches literal brackets and -# "[2]" stays a character class. -# -proc elna-escape-directive { text } { - return [string map [list \ - "\\" "\\\\" \ - "\[" "\\\[" \ - "\]" "\\\]" \ - "\$" "\\\$" \ - "\"" "\\\""] $text] -} - -# -# Replaces an elna-native directive in the line with its dg-* equivalent, -# escaping the directive argument for dejagnu's Tcl evaluation. -# -# line_var - name of the variable holding the line in the caller. -# directive - elna directive name, e.g. @Error. -# dg_name - dejagnu directive name, e.g. dg-error. -# -proc elna-convert-directive { line_var directive dg_name } { - upvar 1 $line_var line - - if { [regexp -indices "\\(\\*\\s*$directive\\s+(.*?)\\s*\\*\\)" $line whole argument] } { - set escaped [elna-escape-directive \ - [string range $line [lindex $argument 0] [lindex $argument 1]]] - set prefix [string range $line 0 [expr {[lindex $whole 0] - 1}]] - set suffix [string range $line [expr {[lindex $whole 1] + 1}] end] - - set line "${prefix}(* { $dg_name \"$escaped\" } *)$suffix" - } -} - -# -# This procedure copies a test file from the source tree to the -# build directory while rewriting elna-native directives to dejagnu -# dg-* equivalents. -# -# Directives (inside (* ... *) comments): -# -# (* @Error message *) Expected compiler error on this line. -# (* @Flags args *) Extra compiler flags. -# -# base - absolute path to the source directory, e.g. .../testsuite/elna.dg -# test - relative path from testsuite, e.g. fail_compilation/file_name.elna -# -# -# Creates a directory at $path, handling the case where a regular -# file already exists at that location (leftover from a prior -# interrupted run). -# -proc elna-ensure-dir { path } { - if { [file exists $path] && ![file isdirectory $path] } { - file delete $path - } - file mkdir $path -} - -proc elna-convert-test { base test { extra_lines "" } } { - set type [file dirname $test] - set target $test - - # Opens a file in the build directory for writing and the source - # file for reading. - elna-ensure-dir $type - set fin [open $base/$target r] - set fout [open $target w] - - # Reads the source test file line by line and replace directives with - # Elna equivalents using a regular expression. - while { [gets $fin line] >= 0 } { - elna-convert-directive line @Error dg-error - elna-convert-directive line @Flags dg-additional-options - puts $fout $line - } - - close $fin - - # Append caller-supplied extra directives (e.g. dg-additional-sources). - foreach line $extra_lines { - puts $fout $line - } - - # Suppress excess output so stray diagnostics do not fail the test. - puts $fout "(* { dg-prune-output .* } *)" - - # Emit category-specific expectation. - set category [lindex [file split $test] 0] - switch $category { - compilable { - # Assert that an output file was produced. - puts $fout "(* { dg-final { output-exists } } *)" - } - fail_compilation { - # Assert that no output file was produced. - puts $fout "(* { dg-final { output-exists-not } } *)" - } - # Other cases are handled automatically. - } - - close $fout - - # Reaches up one stack frame (into the runtest proc) and links the - # caller's cleanup_extra_files variable to the local name cleanups. - upvar 1 cleanup_extra_files cleanups - # This ensures the converted file gets deleted after the test runs. - lappend cleanups $target - - return $target -} - -# -# Handles a multi-module test directory. -# -# Requires sut.elna (system under test) as the primary module; -# all other .elna files are passed as dg-additional-sources. -# -# base - absolute path to the source directory. -# test - relative path from testsuite, e.g. group/test_name. -# -proc elna-handle-multimodule { base test } { - upvar 1 cleanup_extra_files cleanups - - set dir "$base/$test" - - # Find all .elna files recursively inside the test directory. - set modules [lsort [find $dir *.elna]] - if { [llength $modules] == 0 } { - return "" - } - - # Allocate sut.elna as the primary module, collect the rest. - set main_module "" - set extra_sources "" - foreach module $modules { - if { [file tail $module] == "sut.elna" } { - set main_module $module - } else { - lappend extra_sources $module - } - } - if { $main_module == "" } { - error "multi-module test directory \"$test\" must contain sut.elna" - } - - # Build extra directives: -I for imports and dg-additional-sources. - set extra_lines "" - lappend extra_lines "(* { dg-additional-options \"-I$dir\" } *)" - elna-ensure-dir $test - foreach src $extra_sources { - set src_rel [string range $src [expr {[string length $dir] + 1}] end] - file copy $src $test - lappend cleanups [file join $test $src_rel] - lappend extra_lines "(* { dg-additional-sources \"$src_rel\" } *)" - } - - # Convert the primary module, passing the extra directives. - set main_rel [file join $test [file tail $main_module]] - return [elna-convert-test $base $main_rel $extra_lines] -} - -# -# Runs a converted test file through dejagnu and cleans up afterwards. -# -proc elna-dg-run-one { target type cleanup_extra_files } { - global dg-do-what-default - - switch $type { - runnable { set dg-do-what-default "run" } - default { set dg-do-what-default "compile" } - } - - dg-test $target "" "" - file delete $target - foreach srcfile $cleanup_extra_files { - file delete $srcfile - } -} - -# -# Converts and runs single-module .elna test files. -# -proc elna-dg-runtest-single { testcases } { - global dg-do-what-default - set saved-dg-do-what-default ${dg-do-what-default} - - foreach test $testcases { - set type [file tail [file dirname $test]] - set name [file tail $test] - set base [file dirname [file dirname $test]] - set cleanup_extra_files "" - - set target [elna-convert-test $base $type/$name] - elna-dg-run-one $target $type $cleanup_extra_files - } - - set dg-do-what-default ${saved-dg-do-what-default} -} - -# -# Converts and runs multi-module test directories. -# -proc elna-dg-runtest-multi { testcases } { - global dg-do-what-default - set saved-dg-do-what-default ${dg-do-what-default} - - foreach test $testcases { - set type [file tail [file dirname $test]] - set name [file tail $test] - set base [file dirname [file dirname $test]] - set cleanup_extra_files "" - - set target [elna-handle-multimodule $base $type/$name] - if { $target == "" } { - continue - } - - elna-dg-run-one $target $type $cleanup_extra_files - } - - set dg-do-what-default ${saved-dg-do-what-default} -} diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index a05233c..8425b54 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -79,7 +79,7 @@ namespace elna::boot }; template<> - struct literal_type_id<double> + struct literal_type_id<float_literal> { static constexpr int value = 2; }; @@ -187,7 +187,7 @@ namespace elna::boot virtual void visit(field_access_expression *) = 0; virtual void visit(dereference_expression *) = 0; virtual void visit(literal<integer_literal> *) = 0; - virtual void visit(literal<double> *) = 0; + virtual void visit(literal<float_literal> *) = 0; virtual void visit(literal<bool> *) = 0; virtual void visit(literal<std::uint32_t> *) = 0; virtual void visit(literal<std::nullptr_t> *) = 0; @@ -236,7 +236,7 @@ namespace elna::boot [[noreturn]] void visit(field_access_expression *) override; [[noreturn]] void visit(dereference_expression *) override; [[noreturn]] void visit(literal<integer_literal> *) override; - [[noreturn]] void visit(literal<double> *) override; + [[noreturn]] void visit(literal<float_literal> *) override; [[noreturn]] void visit(literal<bool> *) override; [[noreturn]] void visit(literal<std::uint32_t> *) override; [[noreturn]] void visit(literal<std::nullptr_t> *) override; @@ -283,7 +283,7 @@ namespace elna::boot void visit(field_access_expression *expression) override; void visit(dereference_expression *expression) override; void visit(literal<integer_literal> *) override; - void visit(literal<double> *) override; + void visit(literal<float_literal> *) override; void visit(literal<bool> *) override; void visit(literal<std::uint32_t> *) override; void visit(literal<std::nullptr_t> *) override; diff --git a/include/elna/boot/materialization.h b/include/elna/boot/materialization.h index 2c571c5..f3c720e 100644 --- a/include/elna/boot/materialization.h +++ b/include/elna/boot/materialization.h @@ -24,9 +24,18 @@ namespace elna::boot class materialization_error final : public diagnostic { public: - materialization_error(const source_position position); + enum class kind + { + integer_overflow, + real_overflow + }; + + materialization_error(const source_position position, kind error_kind); std::string what() const override; + + private: + kind m_kind; }; class materialization_visitor final : public walking_visitor, public diagnostic_container @@ -37,5 +46,6 @@ namespace elna::boot explicit materialization_visitor(const target_info& target); void visit(literal<integer_literal> *literal) override; + void visit(literal<float_literal> *literal) override; }; } diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 91b669c..50d9fe6 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -173,7 +173,7 @@ namespace elna::boot void visit(for_statement *statement) override; void visit(literal<integer_literal> *literal) override; - void visit(literal<double> *literal) override; + void visit(literal<float_literal> *literal) override; void visit(literal<bool> *literal) override; void visit(literal<std::uint32_t> *literal) override; void visit(literal<std::nullptr_t> *literal) override; diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index ccc95ca..b34c4d4 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -273,12 +273,24 @@ namespace elna::boot /** * 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. + * Contrary to \c std::is_integral_v characters and booleans do not count. * * \tparam T The examined type. */ template<typename T> - inline constexpr bool is_integral = is_signed_v<T> || is_unsigned_v<T>; + inline constexpr bool is_integral_v = is_signed_v<T> || is_unsigned_v<T>; + + /** + * Checks whether \p T is a \c float or \c double. + * + * Contrary to \c is_floating_point_v it considers only 32 and 64 bit + * floating point numbers. + * + * \tparam T The examined type. + */ + template<typename T> + inline constexpr bool is_floating_point_v = std::is_same_v<T, float> + || std::is_same_v<T, double>; struct integer_literal { @@ -337,69 +349,39 @@ namespace elna::boot */ 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; - /// \overload - template<typename U> - bool operator==(U that) const - requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int)) + template<typename T> + bool operator==(T that) const + requires(is_unsigned_v<T> && sizeof(T) <= 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)) + template<typename T> + bool operator==(T that) const + requires(is_signed_v<T> && sizeof(T) <= 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)) + template<typename T> + std::weak_ordering operator<=>(T that) const + requires(is_unsigned_v<T> && sizeof(T) <= 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)) + template<typename T> + std::weak_ordering operator<=>(T that) const + requires(is_signed_v<T> && sizeof(T) <= sizeof(signed long int)) { return mpz_cmp_si(this->raw, that) <=> 0; } @@ -433,7 +415,7 @@ namespace elna::boot */ template<typename T> std::optional<T> try_to() const - requires is_integral<T> + requires is_integral_v<T> { if constexpr (is_signed_v<T>) { @@ -443,10 +425,6 @@ namespace elna::boot { return to_unsigned<T>(); } - else - { - static_assert("integer_literal::to expected only integral types"); - } } /** @@ -485,7 +463,7 @@ namespace elna::boot */ template<typename T> static integer_literal from(T initial) - requires is_integral<T> + requires is_integral_v<T> { integer_literal result{ std::is_signed_v<T>, sizeof(T) }; @@ -503,7 +481,7 @@ namespace elna::boot */ template<typename T> static std::optional<integer_literal> from(std::size_t size, T initial) - requires is_integral<T> + requires is_integral_v<T> { integer_literal result{ std::is_signed_v<T>, size }; @@ -567,6 +545,122 @@ namespace elna::boot } }; + struct float_literal + { + enum class format_kind + { + binary32, + binary64 + }; + + /** + * \tparam T Initializer type. + * \param initial Initial literal value. + * \return A literal constructed from the host value \p initial. + */ + template<typename T> + static float_literal from(T initial) + requires is_floating_point_v<T> + { + if constexpr (std::is_same_v<T, float>) + { + return float_literal(format_kind::binary32, initial); + } + else if constexpr (std::is_same_v<T, double>) + { + return float_literal(format_kind::binary64, initial); + } + } + + float_literal operator+(const float_literal& that) const; + float_literal operator-(const float_literal& that) const; + float_literal operator*(const float_literal& that) const; + float_literal operator/(const float_literal& that) const; + float_literal operator-() const; + + bool operator==(const float_literal& that) const; + + template<typename T> + bool operator==(T that) const + requires is_floating_point_v<T> + { + return this->raw == that; + } + + std::partial_ordering operator<=>(const float_literal& that) const; + + template<typename T> + bool operator<=>(T that) const + requires is_floating_point_v<T> + { + return this->raw <=> that; + } + + /// \return Stored floating point number format. + format_kind format() const; + + /** + * \return The stored value as a host \c double. + * + * Binary32 values are exactly representable as \c double, so this + * accessor is always lossless. + */ + double value() const; + + /** + * \return Whether the stored value is finite. + */ + bool is_finite() const; + + /** + * \return String representation. + */ + std::string to_string() const; + + /** + * Exports the stored value as a host \p T. + * + * This function tries to shrink or extend the value to fit into the + * target type if possible. + * + * \tparam Target type. + * \return The converted value, or \c std::nullopt if not enough precision. + */ + template<typename T> + std::optional<T> try_to() const + requires is_floating_point_v<T> + { + if constexpr (std::is_same_v<T, float>) + { + float single = static_cast<float>(this->raw); + + return static_cast<double>(single) == this->raw + ? std::optional(single) + : std::nullopt; + } + else if constexpr (std::is_same_v<T, double>) + { + return std::optional(static_cast<T>(this->raw)); + } + } + + /** + * Cast the value to the given size, reducing precision if needed. + * + * \param binary_format Result size. + * \return Cast result. + */ + float_literal cast_to(format_kind binary_format) const; + + private: + float_literal(format_kind binary_format, double value); + + static float_literal rounded(format_kind binary_format, double value); + + format_kind m_format; + double raw; + }; + /** * An associative container that contains key-value pairs with unique keys. * Keys preserve the insertion order. @@ -747,7 +841,8 @@ namespace elna::boot std::array<type_properties, target_integer_count> word_properties; type_properties pointer_properties; type_properties char_properties; - type_properties float_properties; + type_properties single_properties; + type_properties double_properties; type_properties bool_properties; }; @@ -762,7 +857,7 @@ namespace elna::boot */ using constant_value = std::variant< integer_literal, - double, + float_literal, bool, std::uint32_t, std::nullptr_t, @@ -907,3 +1002,9 @@ struct std::hash<elna::boot::integer_literal> { std::size_t operator()(const elna::boot::integer_literal& key) const noexcept; }; + +template<> +struct std::hash<elna::boot::float_literal> +{ + std::size_t operator()(const elna::boot::float_literal& key) const noexcept; +}; diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 5817b38..1caba83 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -540,8 +540,33 @@ namespace elna::boot bool is_primitive_type(const type& checked, const std::string& name); /** + * Checks whether the given type is a fixed-size integer. + * + * \param checked The type to check. + * \return Whether the type is a fixed-size integer. + */ + bool is_integer_type(const type& checked); + + /** + * Checks whether the given type is a fixed-size word. + * + * \param checked The type to check. + * \return Whether the type is a fixed-size word. + */ + bool is_word_type(const type& checked); + + /** + * Checks whether the given type is a floating point primitive type + * (\c Single or \c Double). + * + * \param checked The type to check. + * \return Whether the type is a floating point primitive. + */ + bool is_float_type(const type& checked); + + /** * Checks whether the given type is a numeric primitive type - * (\c Int, \c Word, or \c Float). + * (\c Int, \c Word, \c Single, or \c Double). * * \param checked The type to check. * \return Whether the type is a numeric primitive. diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 2d65a26..fd54ba5 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<boot::integer_literal> *literal) override; - void visit(boot::literal<double> *literal) override; + void visit(boot::literal<boot::float_literal> *literal) override; void visit(boot::literal<bool> *boolean) override; void visit(boot::literal<std::uint32_t> *character) override; void visit(boot::literal<std::nullptr_t> *) override; diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index 504b14c..f201e64 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -62,7 +62,7 @@ namespace elna::gcc 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); + const std::shared_ptr<symbol_table>& symbols, tree type); template<typename... Args> tree call_built_in(location_t call_location, const char *name, tree return_type, Args... arguments) diff --git a/include/elna/gcc/elna1.h b/include/elna/gcc/elna1.h index 96043f9..44a0c88 100644 --- a/include/elna/gcc/elna1.h +++ b/include/elna/gcc/elna1.h @@ -24,7 +24,6 @@ enum elna_tree_index ELNA_TI_CHAR_TYPE, ELNA_TI_BOOL_TYPE, ELNA_TI_POINTER_TYPE, - ELNA_TI_FLOAT_TYPE, ELNA_TI_BOOL_TRUE, ELNA_TI_BOOL_FALSE, ELNA_TI_WORD_ONE, @@ -41,7 +40,6 @@ extern std::vector<std::string> elna_include_dirs; #define elna_char_type_node elna_global_trees[ELNA_TI_CHAR_TYPE] #define elna_bool_type_node elna_global_trees[ELNA_TI_BOOL_TYPE] #define elna_pointer_type_node elna_global_trees[ELNA_TI_POINTER_TYPE] -#define elna_float_type_node elna_global_trees[ELNA_TI_FLOAT_TYPE] #define elna_bool_true_node elna_global_trees[ELNA_TI_BOOL_TRUE] #define elna_bool_false_node elna_global_trees[ELNA_TI_BOOL_FALSE] #define elna_word_one_node elna_global_trees[ELNA_TI_WORD_ONE] diff --git a/rakelib/gcc.rake b/rakelib/gcc.rake index 7b6bf44..3d6cf85 100644 --- a/rakelib/gcc.rake +++ b/rakelib/gcc.rake @@ -91,10 +91,48 @@ def download_and_pipe(url, target, command) end end +# Makes a link from the source tree in the GCC tree. def link_frontend(source, destination) File.symlink Pathname.new(source).relative_path_from(destination), (destination + File.basename(source)) end +# Replaces an elna-native directive in the line with its dg-* equivalent, +# escaping the directive argument for dejagnu's Tcl evaluation. +def convert_directive(line, directive, dg_name) + line.sub(/\(\*\s*#{Regexp.escape directive}\s+(.*?)\s*\*\)/) do + escaped = Regexp.last_match(1).gsub(/[\\\[\]$"]/) { |character| "\\#{character}" } + "(* { #{dg_name} \"#{escaped}\" } *)" + end +end + +def convert_test(source, target, category, extra_lines = []) + File.open(target, 'wb') do |output| + File.foreach(source) do |line| + line = convert_directive(line, '@Error', 'dg-error') + line = convert_directive(line, '@Flags', 'dg-additional-options') + + output.puts line + end + + # Runnable tests compile, link and execute. + output.puts '(* { dg-do run } *)' if category == 'runnable' + + extra_lines.each { |line| output.puts line } + + # Suppress excess output so stray diagnostics do not fail the test. + output.puts '(* { dg-prune-output .* } *)' + + case category + when 'compilable' + # Assert that an output file was produced. + output.puts '(* { dg-final { output-exists } } *)' + when 'fail_compilation' + # Assert that no output file was produced. + output.puts '(* { dg-final { output-exists-not } } *)' + end + end +end + namespace :gcc do # Dependencies. GCC_VERSION = "16.1.0" @@ -139,7 +177,7 @@ namespace :gcc do FileList['boot', 'include', 'COPYING3', 'README.md', 'gcc/gcc', 'gcc/*.in', 'gcc/lang*'].each do |file| link_frontend file, source_destination end - FileList['testsuite/*', 'gcc/dg.exp', 'README.md'].each do |file| + FileList['gcc/dg.exp', 'README.md'].each do |file| link_frontend file, test_destination end destination = GCC_TREE + 'gcc/testsuite/lib' @@ -180,8 +218,52 @@ namespace :gcc do sh 'make', 'install', chdir: HOST_GCC end + desc 'Convert the testsuite into dejagnu format' + task :convert do + destination = GCC_TREE + 'gcc/testsuite/elna.dg' + destination.mkpath + source = Pathname.new 'testsuite' + + # Regenerate the category directories from scratch: this removes + # converted tests whose sources are gone and adds new ones. + %w[compilable fail_compilation runnable].each do |category| + category_destination = destination + category + category_source = source + category + + rm_rf category_destination + mkdir_p category_destination + + category_source.each_child do |test_path| + test_target = category_destination + test_path.basename + + # Multi-module tests: directories containing a sut.elna (system under + # test) and any helper files, compiled and linked together. + if test_path.directory? + main = test_path + 'sut.elna' + raise %(multi-module test directory \"#{test_path}\" must contain sut.elna) unless main + + test_target.mkdir + modules = test_path.children - [main] + + modules.each do |extra| + cp_r extra, test_target + end + extra_sources = test_target.glob("**/*.elna") + .map { |extra| %("#{extra.relative_path_from test_target}") } + + convert_test main, (test_target + 'sut.elna'), category, [ + "(* { dg-additional-options \"-I#{test_target.expand_path}\" } *)", + "(* { dg-additional-sources #{extra_sources * ' '} } *)" + ] + else + convert_test test_path, test_target, category + end + end + end + end + desc 'Run tests' - task :check do + task check: 'gcc:convert' do log_file = Pathname.new(HOST_GCC) + 'gcc/testsuite/elna/elna.log' sh 'make', 'check-elna', chdir: File.join(HOST_GCC, 'gcc') diff --git a/testsuite/compilable/double_literals.elna b/testsuite/compilable/double_literals.elna new file mode 100644 index 0000000..9e8a73d --- /dev/null +++ b/testsuite/compilable/double_literals.elna @@ -0,0 +1,9 @@ +proc f() +var + x: const Double := 3.14 + y: const Double := 1e10 + z: const Double := 4.567e8 + t: const Double := 2.5E-3 +return + +end. diff --git a/testsuite/compilable/float_literals.elna b/testsuite/compilable/float_literals.elna deleted file mode 100644 index 77af6b1..0000000 --- a/testsuite/compilable/float_literals.elna +++ /dev/null @@ -1,9 +0,0 @@ -proc f() -var - x: const Float := 3.14 - y: const Float := 1e10 - z: const Float := 4.567e8 - t: const Float := 2.5E-3 -return - -end. diff --git a/testsuite/compilable/single_literals.elna b/testsuite/compilable/single_literals.elna new file mode 100644 index 0000000..ac03623 --- /dev/null +++ b/testsuite/compilable/single_literals.elna @@ -0,0 +1,9 @@ +proc f() +var + x: const Single := 3.14f + y: const Single := 1e10f + z: const Single := 4.567e8f + t: const Single := 2.5E-3f +return + +end. diff --git a/testsuite/fail_compilation/float_remainder.elna b/testsuite/fail_compilation/float_remainder.elna new file mode 100644 index 0000000..ec6c9e2 --- /dev/null +++ b/testsuite/fail_compilation/float_remainder.elna @@ -0,0 +1,8 @@ +proc f() +var + x: Double +begin + x := 5.0 % 2.0 (* @Error Invalid operands of type 'Double' and 'Double' for operator % *) +return + +end. diff --git a/testsuite/runnable/float_arithmetic.elna b/testsuite/runnable/float_arithmetic.elna new file mode 100644 index 0000000..73072f2 --- /dev/null +++ b/testsuite/runnable/float_arithmetic.elna @@ -0,0 +1,13 @@ +begin + assert(6.0 / 2.0 = 3.0); + assert(5.0 / 2.0 = 2.5); + assert(3.0 * 0.5 = 1.5); + assert(-3.0 + 1.0 = -2.0); + assert(10.0 - 4.0 = 6.0); + + assert(6.0f / 2.0f = 3.0f); + assert(5.0f / 2.0f = 2.5f); + assert(3.0f * 0.5f = 1.5f); + assert(1.0f / 4.0f = 0.25f); + assert(-3.14f < 0.0f) +end. |
