From dea1c177cd3592cc24fd15ad446a676da2e4da28 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 25 Jul 2026 23:33:22 +0200 Subject: Support #offset trait at compile time --- boot/evaluator.cc | 232 +++++++++++++++++++++++++++++------------------------- 1 file changed, 126 insertions(+), 106 deletions(-) (limited to 'boot') diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 40a6533..bb50bd3 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -18,12 +18,115 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/evaluator.h" #include "elna/boot/ast.h" -#include #include #include +#include namespace elna::boot { + std::optional get_type_properties(const type& subject, const target_info& target) + { + auto resolved = resolve_underlying_type(subject); + + if (is_primitive_type(resolved, "Int") + || resolved.get() != nullptr) + { + return target.int_properties; + } + else if (is_primitive_type(resolved, "Word")) + { + return target.word_properties; + } + else if (is_primitive_type(resolved, "Char")) + { + return target.char_properties; + } + else if (is_primitive_type(resolved, "Float")) + { + return target.float_properties; + } + else if (is_primitive_type(resolved, "Bool")) + { + return target.bool_properties; + } + else if (resolved.get() != nullptr) + { + return type_properties{ + .size = target.pointer_properties.size + target.word_properties.size, + .alignment = target.pointer_properties.alignment + }; + } + else if (is_any_pointer_type(resolved)) + { + return target.pointer_properties; + } + else if (auto array = resolved.get()) + { + if (auto element = get_type_properties(array->base, target)) + { + return type_properties{ + .size = array->size * element->size, + .alignment = element->alignment + }; + } + } + else if (auto record = resolved.get()) + { + auto record_layout = layout_record(record, target); + if (record_layout.has_value()) + { + return type_properties{ + .size = record_layout.value().size, + .alignment = record_layout.value().alignment + }; + } + } + return std::nullopt; + } + + std::optional layout_record(const std::shared_ptr& subject, + const target_info& target) + { + std::size_t current_offset{ 0 }; + std::size_t size{ 0 }; + std::size_t alignment{ 1 }; + ordered_map result_map; + std::vector chain; + + for (auto current_record = subject; current_record != nullptr;) + { + chain.push_back(current_record.get()); + if (current_record->base.empty()) + { + break; + } + current_record = resolve_underlying_type(current_record->base).get(); + } + for (auto const *current_record : std::views::reverse(chain)) + { + for (auto const& field : current_record->fields) + { + auto props = get_type_properties(field.second, target); + if (!props.has_value()) + { + return std::nullopt; + } + size = (size + props->alignment - 1) & ~(props->alignment - 1); + size += props->size; + alignment = std::max(alignment, props->alignment); + current_offset = (current_offset + props->alignment - 1) & ~(props->alignment - 1); + + result_map.insert(field.first, current_offset); + current_offset += props->size; + } + } + return record_properties{ + .offset_map = std::move(result_map), + .size = (size + alignment - 1) & ~(alignment - 1), + .alignment = alignment + }; + } + evaluator::evaluator(symbol_bag& bag, const target_info& target, const std::map& evaluated_initializers) : bag(bag), target(target), evaluated_initializers(evaluated_initializers) @@ -434,114 +537,14 @@ namespace elna::boot std::optional evaluator::evaluate_traits_size(const type& subject) { - type const resolved = resolve_underlying_type(subject); - - if (is_primitive_type(resolved, "Int") - || resolved.get() != nullptr) - { - return target.int_size; - } - else if (is_primitive_type(resolved, "Word")) - { - return target.word_size; - } - else if (is_primitive_type(resolved, "Char")) - { - return target.char_size; - } - else if (is_primitive_type(resolved, "Float")) - { - return target.float_size; - } - else if (is_primitive_type(resolved, "Bool")) - { - return target.bool_size; - } - else if (resolved.get() != nullptr) - { - return target.pointer_size + target.word_size; - } - else if (is_primitive_type(resolved, "Pointer") - || resolved.get() != nullptr) - { - return target.pointer_size; - } - else if (auto array = resolved.get()) - { - if (auto element_size = evaluate_traits_size(array->base)) - { - return array->size * element_size.value(); - } - } - return std::nullopt; + auto props = get_type_properties(subject, this->target); + return props ? std::optional{ props->size } : std::nullopt; } std::optional evaluator::evaluate_traits_alignment(const type& subject) { - type const resolved = resolve_underlying_type(subject); - - if (is_primitive_type(resolved, "Int")) - { - return target.int_alignment; - } - if (is_primitive_type(resolved, "Word")) - { - return target.word_alignment; - } - if (is_primitive_type(resolved, "Char")) - { - return target.char_alignment; - } - if (is_primitive_type(resolved, "Float")) - { - return target.float_alignment; - } - if (is_primitive_type(resolved, "Pointer")) - { - return target.pointer_alignment; - } - if (resolved.get() != nullptr) - { - return target.pointer_alignment; - } - if (auto array = resolved.get()) - { - // The alignment of the array equals that of its element. - return evaluate_traits_alignment(array->base); - } - if (auto record = resolved.get()) - { - // An empty record has alignment 1. - std::optional by_field_alignment = std::accumulate( - std::begin(record->fields), std::end(record->fields), std::make_optional(1), - [this](std::optional max_alignment, auto& field) { - std::optional field_alignment = evaluate_traits_alignment(field.second); - - return field_alignment.has_value() && max_alignment.has_value() - ? std::make_optional(std::max(field_alignment.value(), max_alignment.value())) - : std::nullopt; - } - ); - if (!by_field_alignment.has_value()) - { - return by_field_alignment; - } - std::size_t max_alignment = by_field_alignment.value(); - - if (!record->base.empty()) - { - if (auto base_alignment = evaluate_traits_alignment(record->base)) - { - max_alignment = std::max(base_alignment.value(), max_alignment); - } - else - { - return std::nullopt; - } - } - return max_alignment; - } - return std::nullopt; + auto props = get_type_properties(subject, this->target); + return props ? std::optional{ props->alignment } : std::nullopt; } std::optional evaluator::evaluate_traits(traits_expression& subject) @@ -622,7 +625,24 @@ namespace elna::boot return constant_value{ static_cast(enumeration->members.size()) }; } } - // #offset(T, field) stays in codegen - needs GCC record layout. + else if (subject.name.name() == "offset") + { + auto *field = subject.arguments.at(1)->is_named(); + auto record = resolve_underlying_type(subject.types.front()).get(); + if (field == nullptr || record == nullptr) + { + return std::nullopt; + } + if (auto record_layout = layout_record(record, this->target)) + { + auto field_search = record_layout.value().offset_map.find(field->name); + + if (field_search != std::cend(record_layout.value().offset_map)) + { + return constant_value{ static_cast(field_search->second) }; + } + } + } return std::nullopt; } } -- cgit v1.2.3