aboutsummaryrefslogtreecommitdiff
path: root/boot/evaluator.cc
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-25 23:33:22 +0200
committerEugen Wissner <belka@caraus.de>2026-07-26 02:45:19 +0200
commitdea1c177cd3592cc24fd15ad446a676da2e4da28 (patch)
treeca29eefe25858ca49d376614ecf6ddda9e144299 /boot/evaluator.cc
parent4f89a02e03b056d0c55108a8c9194126ef4ee811 (diff)
downloadelna-dea1c177cd3592cc24fd15ad446a676da2e4da28.tar.gz
Support #offset trait at compile time
Diffstat (limited to 'boot/evaluator.cc')
-rw-r--r--boot/evaluator.cc232
1 files changed, 126 insertions, 106 deletions
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 <numeric>
#include <algorithm>
#include <limits>
+#include <ranges>
namespace elna::boot
{
+ std::optional<type_properties> get_type_properties(const type& subject, const target_info& target)
+ {
+ auto resolved = resolve_underlying_type(subject);
+
+ if (is_primitive_type(resolved, "Int")
+ || resolved.get<enumeration_type>() != 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<slice_type>() != 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<array_type>())
+ {
+ 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<record_type>())
+ {
+ 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<record_properties> layout_record(const std::shared_ptr<record_type>& subject,
+ const target_info& target)
+ {
+ std::size_t current_offset{ 0 };
+ std::size_t size{ 0 };
+ std::size_t alignment{ 1 };
+ ordered_map<std::size_t> result_map;
+ std::vector<const record_type *> 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<record_type>();
+ }
+ 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<std::string, expression*>& evaluated_initializers)
: bag(bag), target(target), evaluated_initializers(evaluated_initializers)
@@ -434,114 +537,14 @@ namespace elna::boot
std::optional<std::size_t> evaluator::evaluate_traits_size(const type& subject)
{
- type const resolved = resolve_underlying_type(subject);
-
- if (is_primitive_type(resolved, "Int")
- || resolved.get<enumeration_type>() != 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<slice_type>() != nullptr)
- {
- return target.pointer_size + target.word_size;
- }
- else if (is_primitive_type(resolved, "Pointer")
- || resolved.get<pointer_type>() != nullptr)
- {
- return target.pointer_size;
- }
- else if (auto array = resolved.get<array_type>())
- {
- 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<std::size_t> 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<pointer_type>() != nullptr)
- {
- return target.pointer_alignment;
- }
- if (auto array = resolved.get<array_type>())
- {
- // The alignment of the array equals that of its element.
- return evaluate_traits_alignment(array->base);
- }
- if (auto record = resolved.get<record_type>())
- {
- // An empty record has alignment 1.
- std::optional<std::size_t> by_field_alignment = std::accumulate(
- std::begin(record->fields), std::end(record->fields), std::make_optional<std::size_t>(1),
- [this](std::optional<std::size_t> max_alignment, auto& field) {
- std::optional<std::size_t> 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<constant_value> evaluator::evaluate_traits(traits_expression& subject)
@@ -622,7 +625,24 @@ namespace elna::boot
return constant_value{ static_cast<std::int32_t>(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<record_type>();
+ 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<std::uint32_t>(field_search->second) };
+ }
+ }
+ }
return std::nullopt;
}
}