aboutsummaryrefslogtreecommitdiff
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
parent4f89a02e03b056d0c55108a8c9194126ef4ee811 (diff)
downloadelna-dea1c177cd3592cc24fd15ad446a676da2e4da28.tar.gz
Support #offset trait at compile time
-rw-r--r--boot/evaluator.cc232
-rw-r--r--gcc/gcc/elna-builtins.cc82
-rw-r--r--gcc/gcc/elna-diagnostic.cc6
-rw-r--r--gcc/gcc/elna-generic.cc321
-rw-r--r--gcc/gcc/elna-tree.cc41
-rw-r--r--gcc/gcc/elna1.cc27
-rw-r--r--include/elna/boot/evaluator.h61
-rw-r--r--include/elna/boot/result.h3
-rw-r--r--include/elna/gcc/elna-builtins.h14
-rw-r--r--include/elna/gcc/elna-diagnostic.h2
-rw-r--r--include/elna/gcc/elna-generic.h14
-rw-r--r--include/elna/gcc/elna-tree.h9
-rw-r--r--testsuite/runnable/record_layout.elna55
13 files changed, 505 insertions, 362 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;
}
}
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index 6ea8874..226de4a 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -15,8 +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 <algorithm>
-
#include "elna/gcc/elna-builtins.h"
#include "elna/gcc/elna1.h"
#include "stor-layout.h"
@@ -41,7 +39,7 @@ namespace elna::gcc
}
static
- tree declare_builtin_type(std::shared_ptr<symbol_table> symbol_table, const char *name, tree type)
+ tree declare_builtin_type(const std::shared_ptr<symbol_table>& symbol_table, const char *name, tree type)
{
tree identifier = get_identifier(name);
tree type_declaration = build_decl(UNKNOWN_LOCATION, TYPE_DECL, identifier, type);
@@ -65,21 +63,44 @@ namespace elna::gcc
return builtin_table;
}
- tree build_composite_type(const boot::ordered_map<boot::type>& fields,
- tree composite_type_node, std::shared_ptr<symbol_table> symbols)
+ static tree build_composite_type(const boot::ordered_map<boot::type>& fields,
+ tree composite_type_node, const std::shared_ptr<symbol_table>& symbols)
{
- for (auto& field : fields)
+ for (const auto& field : fields)
{
tree rewritten_field = get_inner_alias(field.second, symbols);
tree field_declaration = build_field(UNKNOWN_LOCATION,
composite_type_node, field.first, rewritten_field);
TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration);
}
- layout_type(composite_type_node);
return composite_type_node;
}
- tree build_procedure_type(const boot::procedure_type& procedure, std::shared_ptr<symbol_table> symbols)
+ static bool layout_record_type(tree record_type, const std::shared_ptr<boot::record_type>& subject)
+ {
+ layout_type(record_type);
+ const auto& target = get_host_target();
+ auto record_layout = boot::layout_record(subject, target);
+
+ if (!record_layout.has_value())
+ {
+ return false;
+ }
+ for (tree field = TYPE_FIELDS(record_type); field != NULL_TREE; field = TREE_CHAIN(field))
+ {
+ std::string field_name(IDENTIFIER_POINTER(DECL_NAME(field)));
+
+ DECL_FIELD_OFFSET(field) = size_int(record_layout.value().offset_map.find(field_name)->second);
+ DECL_FIELD_BIT_OFFSET(field) = bitsize_zero_node;
+ }
+ TYPE_SIZE_UNIT(record_type) = size_int(record_layout.value().size);
+ TYPE_SIZE(record_type) = bitsize_int(record_layout.value().size * BITS_PER_UNIT);
+
+ return true;
+ }
+
+ static tree build_procedure_type(const boot::procedure_type& procedure,
+ const std::shared_ptr<symbol_table>& symbols)
{
std::vector<tree> parameter_types(procedure.parameters.size());
@@ -93,14 +114,15 @@ namespace elna::gcc
{
return_type = get_inner_alias(procedure.return_type.proper_type, symbols);
}
- return build_function_type_array(return_type, procedure.parameters.size(), parameter_types.data());
+ return build_function_type_array(return_type,
+ static_cast<int>(procedure.parameters.size()), parameter_types.data());
}
- tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols)
+ tree get_inner_alias(const boot::type& type, const std::shared_ptr<symbol_table>& symbols)
{
if (auto reference = type.get<boot::primitive_type>())
{
- auto looked_up = symbols->lookup(reference->identifier);
+ tree looked_up = symbols->lookup(reference->identifier);
gcc_assert(looked_up != NULL_TREE);
return TREE_TYPE(looked_up);
@@ -122,7 +144,10 @@ namespace elna::gcc
}
}
build_composite_type(reference->fields, composite_type_node, symbols);
-
+ if (!layout_record_type(composite_type_node, reference))
+ {
+ return error_mark_node;
+ }
return composite_type_node;
}
else if (auto reference = type.get<boot::enumeration_type>())
@@ -154,7 +179,7 @@ namespace elna::gcc
}
else if (auto reference = type.get<boot::procedure_type>())
{
- auto procedure = build_procedure_type(*reference, symbols);
+ tree procedure = build_procedure_type(*reference, symbols);
return build_pointer_type(procedure);
}
@@ -170,14 +195,19 @@ namespace elna::gcc
return error_mark_node;
}
- tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
- std::shared_ptr<symbol_table> symbols)
+ tree handle_symbol(const std::string& symbol_name,
+ const std::shared_ptr<boot::alias_type>& reference,
+ const std::shared_ptr<symbol_table>& symbols)
{
tree looked_up = symbols->lookup(symbol_name);
if (looked_up == NULL_TREE)
{
tree type_tree = get_inner_alias(reference->referent, symbols);
+ if (type_tree == error_mark_node)
+ {
+ return error_mark_node;
+ }
looked_up = build_decl(UNKNOWN_LOCATION, TYPE_DECL,
get_identifier(symbol_name.c_str()), type_tree);
@@ -197,7 +227,7 @@ namespace elna::gcc
}
void declare_procedure(const std::string& name, const boot::procedure_info& info,
- std::shared_ptr<symbol_table> symbols)
+ const std::shared_ptr<symbol_table>& symbols)
{
tree declaration_type = gcc::build_procedure_type(info.symbol, symbols);
tree fndecl = build_fn_decl(name.c_str(), declaration_type);
@@ -230,37 +260,39 @@ namespace elna::gcc
}
DECL_ARGUMENTS(fndecl) = argument_chain;
TREE_ADDRESSABLE(fndecl) = 1;
- DECL_EXTERNAL(fndecl) = info.is_extern();
- TREE_PUBLIC(fndecl) = info.exported;
+ DECL_EXTERNAL(fndecl) = static_cast<unsigned>(info.is_extern());
+ TREE_PUBLIC(fndecl) = static_cast<unsigned>(info.exported);
}
tree declare_variable(const std::string& name, const boot::variable_info& info,
- std::shared_ptr<symbol_table> symbols)
+ const std::shared_ptr<symbol_table>& symbols)
{
- auto *variable_type = get_inner_alias(info.symbol, symbols);
+ tree variable_type = get_inner_alias(info.symbol, symbols);
tree declaration_tree = build_decl(UNKNOWN_LOCATION, VAR_DECL, get_identifier(name.c_str()), variable_type);
TREE_ADDRESSABLE(declaration_tree) = 1;
- DECL_EXTERNAL(declaration_tree) = info.is_extern;
- TREE_PUBLIC(declaration_tree) = info.exported;
+ DECL_EXTERNAL(declaration_tree) = static_cast<unsigned>(info.is_extern);
+ TREE_PUBLIC(declaration_tree) = static_cast<unsigned>(info.exported);
symbols->enter(name, declaration_tree);
return declaration_tree;
}
- void declare_type(const std::string& name, const boot::type_info& info, std::shared_ptr<symbol_table> symbols)
+ static void declare_type(const std::string& name,
+ const boot::type_info& info, const std::shared_ptr<symbol_table>& symbols)
{
// The top level symbol table has basic (builtin) types in it which are not aliases.
if (auto alias_type = info.symbol.get<boot::alias_type>())
{
tree type_declaration = handle_symbol(name, alias_type, symbols);
- TREE_PUBLIC(type_declaration) = info.exported;
+ TREE_PUBLIC(type_declaration) = static_cast<unsigned>(info.exported);
}
}
- void rewrite_symbol_table(std::shared_ptr<boot::symbol_table> info_table, std::shared_ptr<symbol_table> symbols)
+ void rewrite_symbol_table(const std::shared_ptr<boot::symbol_table>& info_table,
+ const std::shared_ptr<symbol_table>& symbols)
{
for (auto& [symbol_name, symbol_info] : *info_table)
{
diff --git a/gcc/gcc/elna-diagnostic.cc b/gcc/gcc/elna-diagnostic.cc
index 630db0c..3dcdb98 100644
--- a/gcc/gcc/elna-diagnostic.cc
+++ b/gcc/gcc/elna-diagnostic.cc
@@ -26,7 +26,7 @@ namespace elna::gcc
linemap_add(line_table, LC_ENTER, 0, filename, 1);
}
- linemap_guard::linemap_guard(const std::filesystem::path filename)
+ linemap_guard::linemap_guard(const std::filesystem::path& filename)
{
const char *filename_pointer = ggc_strdup(filename.native().c_str());
@@ -35,7 +35,7 @@ namespace elna::gcc
linemap_guard::~linemap_guard()
{
- linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
+ linemap_add(line_table, LC_LEAVE, 0, nullptr, 0);
}
location_t get_location(const boot::source_position *position)
@@ -65,7 +65,7 @@ namespace elna::gcc
return make_location(caret, start, end);
}
- std::string print_aggregate_name(tree type, const std::string& kind_name)
+ static std::string print_aggregate_name(tree type, const std::string& kind_name)
{
if (TYPE_IDENTIFIER(type) == NULL_TREE)
{
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 43739fb..7491372 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -36,9 +36,9 @@ along with GCC; see the file COPYING3. If not see
namespace elna::gcc
{
- generic_visitor::generic_visitor(std::shared_ptr<symbol_table> symbol_table,
+ generic_visitor::generic_visitor(const std::shared_ptr<symbol_table>& symbol_table,
boot::symbol_bag bag, const boot::target_info& target)
- : bag(bag), symbols(symbol_table), target(target)
+ : bag(std::move(bag)), symbols(symbol_table), target(target)
, const_evaluator(std::make_unique<boot::evaluator>(this->bag, this->target,
this->evaluated_initializers))
{
@@ -58,7 +58,8 @@ namespace elna::gcc
argument_trees->quick_push(this->current_expression);
}
this->current_expression = fold_build_call_array_loc(call_location, TREE_TYPE(symbol_type),
- procedure_address, vec_safe_length(argument_trees), vec_safe_address(argument_trees));
+ procedure_address, static_cast<int>(vec_safe_length(argument_trees)),
+ vec_safe_address(argument_trees));
}
void generic_visitor::build_assert_builtin(location_t call_location,
@@ -343,6 +344,94 @@ namespace elna::gcc
finish_function(fndecl);
}
+ tree generic_visitor::build_equality(boot::binary_expression *expression, tree left, tree right)
+ {
+ tree_code equality_code;
+
+ if (expression->operation() == boot::binary_operator::equals)
+ {
+ equality_code = EQ_EXPR;
+ }
+ else if (expression->operation() == boot::binary_operator::not_equals)
+ {
+ equality_code = NE_EXPR;
+ }
+ else
+ {
+ gcc_unreachable();
+ }
+ return build_equality_comparison(get_location(&expression->position()), left, right,
+ expression->lhs().type_decoration, equality_code);
+ }
+
+ tree generic_visitor::build_equality_comparison(location_t loc, tree left, tree right,
+ const boot::type& left_type, tree_code equality_code)
+ {
+ tree_code combination_code = equality_code == EQ_EXPR
+ ? TRUTH_ANDIF_EXPR : TRUTH_ORIF_EXPR;
+
+ if (left_type.get<boot::slice_type>() != nullptr)
+ {
+ tree lhs_ptr_field = TYPE_FIELDS(TREE_TYPE(left));
+ tree lhs_length_field = TREE_CHAIN(lhs_ptr_field);
+
+ tree lhs_length = build3(COMPONENT_REF, TREE_TYPE(lhs_length_field),
+ left, lhs_length_field, NULL_TREE);
+ tree lhs_ptr = build3(COMPONENT_REF, TREE_TYPE(lhs_ptr_field),
+ left, lhs_ptr_field, NULL_TREE);
+
+ tree rhs_length = build3(COMPONENT_REF, TREE_TYPE(lhs_length_field),
+ right, lhs_length_field, NULL_TREE);
+ tree rhs_ptr = build3(COMPONENT_REF, TREE_TYPE(lhs_ptr_field),
+ right, lhs_ptr_field, NULL_TREE);
+
+ tree element_size = fold_convert(elna_word_type_node,
+ size_in_bytes(TREE_TYPE(TREE_TYPE(lhs_ptr_field))));
+ tree byte_length = build2(MULT_EXPR, elna_word_type_node, lhs_length, element_size);
+
+ tree length_equality = build2(equality_code, elna_bool_type_node, lhs_length, rhs_length);
+ tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp", integer_type_node,
+ lhs_ptr, rhs_ptr, byte_length);
+ tree equals_zero = build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node);
+
+ return build2(combination_code, elna_bool_type_node, length_equality, equals_zero);
+ }
+ else if (left_type.get<boot::record_type>() != nullptr
+ || left_type.get<boot::array_type>() != nullptr)
+ {
+ tree size = fold_convert(elna_word_type_node,
+ size_in_bytes(TREE_TYPE(left)));
+ tree lhs_addr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(left)), left);
+ tree rhs_addr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(right)), right);
+ tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp",
+ integer_type_node, lhs_addr, rhs_addr, size);
+ return build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node);
+ }
+ else
+ {
+ return build2_loc(loc, equality_code, elna_bool_type_node, left, right);
+ }
+ }
+
+ std::pair<tree, tree> generic_visitor::build_loop_head(tree control_variable_declaration,
+ tree limit, tree_code comparison, location_t check_location)
+ {
+ // Put a label in front of the loop condition.
+ tree check_label = create_artificial_label(check_location);
+ tree check_statement = build1_loc(check_location, LABEL_EXPR, void_type_node, check_label);
+ append_statement(check_statement);
+
+ // Build the condition and jump if the condition isn't met.
+ tree condition = fold_build2(comparison, elna_bool_type_node, control_variable_declaration, limit);
+ tree end_label = create_artificial_label(UNKNOWN_LOCATION);
+ tree goto_end = build1(GOTO_EXPR, void_type_node, end_label);
+ tree condition_statement = build3_loc(check_location, COND_EXPR, void_type_node,
+ condition, NULL_TREE, goto_end);
+ append_statement(condition_statement);
+
+ return { check_label, end_label };
+ }
+
void generic_visitor::enter_scope()
{
this->symbols = std::make_shared<symbol_table>(this->symbols);
@@ -396,7 +485,7 @@ namespace elna::gcc
cgraph_node::finalize_function(fndecl, true);
}
- static tree constant_to_tree(const boot::constant_value& constant_value)
+ static tree constant_to_tree(const boot::constant_value& constant_value, tree type = NULL_TREE)
{
if (std::holds_alternative<std::int32_t>(constant_value))
{
@@ -411,10 +500,12 @@ namespace elna::gcc
{
auto real_value = std::get<double>(constant_value);
REAL_VALUE_TYPE real;
- HOST_WIDE_INT target_bits[(sizeof(double) + sizeof(HOST_WIDE_INT) - 1)
- / sizeof(HOST_WIDE_INT)];
- std::memcpy(target_bits, &real_value, sizeof(real_value));
- real_from_target(&real, target_bits, REAL_MODE_FORMAT(TYPE_MODE(elna_float_type_node)));
+ constexpr std::size_t bits_size = (sizeof(double) + sizeof(HOST_WIDE_INT) - 1) / sizeof(HOST_WIDE_INT);
+ std::array<HOST_WIDE_INT, bits_size> target_bits;
+
+ 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)));
+
return build_real(elna_float_type_node, real);
}
else if (std::holds_alternative<bool>(constant_value))
@@ -431,11 +522,55 @@ namespace elna::gcc
}
else if (std::holds_alternative<boot::constant_aggregate<boot::ordered_map>>(constant_value))
{
- return NULL_TREE; // Compound constants are not lowered to trees yet.
+ // NOLINTNEXTLINE(readability-simplify-boolean-expr)
+ if (type == NULL_TREE || !RECORD_OR_UNION_TYPE_P(type))
+ {
+ return NULL_TREE;
+ }
+ const auto& aggregate = std::get<boot::constant_aggregate<boot::ordered_map>>(constant_value);
+ const auto& fields = *aggregate;
+ vec<constructor_elt, va_gc> *tree_arguments = nullptr;
+
+ for (const auto& [field_name, field_value] : fields)
+ {
+ tree field_decl = find_field_by_name(UNKNOWN_LOCATION, type, field_name);
+ if (field_decl == error_mark_node)
+ {
+ return NULL_TREE;
+ }
+ tree value_tree = constant_to_tree(field_value, TREE_TYPE(field_decl));
+ if (value_tree == NULL_TREE)
+ {
+ return NULL_TREE;
+ }
+ CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, value_tree);
+ }
+ return build_constructor(type, tree_arguments);
}
else if (std::holds_alternative<boot::constant_aggregate<std::vector>>(constant_value))
{
- return NULL_TREE; // Compound constants are not lowered to trees yet.
+ if (type == NULL_TREE || TREE_CODE(type) != ARRAY_TYPE)
+ {
+ return NULL_TREE;
+ }
+ const auto& aggregate = std::get<boot::constant_aggregate<std::vector>>(constant_value);
+ const auto& elements = *aggregate;
+ tree domain = TYPE_DOMAIN(type);
+ tree index = TYPE_MIN_VALUE(domain);
+ tree element_type = TREE_TYPE(type);
+ vec<constructor_elt, va_gc> *tree_arguments = nullptr;
+
+ for (const auto& element : elements)
+ {
+ tree element_tree = constant_to_tree(element, element_type);
+ if (element_tree == NULL_TREE)
+ {
+ return NULL_TREE;
+ }
+ CONSTRUCTOR_APPEND_ELT(tree_arguments, index, element_tree);
+ index = int_const_binop(PLUS_EXPR, index, elna_word_one_node);
+ }
+ return build_constructor(type, tree_arguments);
}
return NULL_TREE;
}
@@ -506,75 +641,6 @@ namespace elna::gcc
this->current_expression = build_constructor(slice_type, elms);
}
- tree generic_visitor::build_equality(boot::binary_expression *expression, tree left, tree right)
- {
- tree_code equality_code;
-
- if (expression->operation() == boot::binary_operator::equals)
- {
- equality_code = EQ_EXPR;
- }
- else if (expression->operation() == boot::binary_operator::not_equals)
- {
- equality_code = NE_EXPR;
- }
- else
- {
- gcc_unreachable();
- }
- return build_equality_comparison(get_location(&expression->position()), left, right,
- expression->lhs().type_decoration, equality_code);
- }
-
- tree generic_visitor::build_equality_comparison(location_t loc, tree left, tree right,
- const boot::type& left_type, tree_code equality_code)
- {
- tree_code combination_code = equality_code == EQ_EXPR
- ? TRUTH_ANDIF_EXPR : TRUTH_ORIF_EXPR;
-
- if (left_type.get<boot::slice_type>() != nullptr)
- {
- tree lhs_ptr_field = TYPE_FIELDS(TREE_TYPE(left));
- tree lhs_length_field = TREE_CHAIN(lhs_ptr_field);
-
- tree lhs_length = build3(COMPONENT_REF, TREE_TYPE(lhs_length_field),
- left, lhs_length_field, NULL_TREE);
- tree lhs_ptr = build3(COMPONENT_REF, TREE_TYPE(lhs_ptr_field),
- left, lhs_ptr_field, NULL_TREE);
-
- tree rhs_length = build3(COMPONENT_REF, TREE_TYPE(lhs_length_field),
- right, lhs_length_field, NULL_TREE);
- tree rhs_ptr = build3(COMPONENT_REF, TREE_TYPE(lhs_ptr_field),
- right, lhs_ptr_field, NULL_TREE);
-
- tree element_size = fold_convert(elna_word_type_node,
- size_in_bytes(TREE_TYPE(TREE_TYPE(lhs_ptr_field))));
- tree byte_length = build2(MULT_EXPR, elna_word_type_node, lhs_length, element_size);
-
- tree length_equality = build2(equality_code, elna_bool_type_node, lhs_length, rhs_length);
- tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp", integer_type_node,
- lhs_ptr, rhs_ptr, byte_length);
- tree equals_zero = build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node);
-
- return build2(combination_code, elna_bool_type_node, length_equality, equals_zero);
- }
- else if (left_type.get<boot::record_type>() != nullptr
- || left_type.get<boot::array_type>() != nullptr)
- {
- tree size = fold_convert(elna_word_type_node,
- size_in_bytes(TREE_TYPE(left)));
- tree lhs_addr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(left)), left);
- tree rhs_addr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(right)), right);
- tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp",
- integer_type_node, lhs_addr, rhs_addr, size);
- return build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node);
- }
- else
- {
- return build2_loc(loc, equality_code, elna_bool_type_node, left, right);
- }
- }
-
void generic_visitor::visit(boot::binary_expression *expression)
{
expression->lhs().accept(this);
@@ -746,7 +812,7 @@ namespace elna::gcc
boot::evaluator constant_evaluator(this->bag, this->target, evaluated_initializers);
if (auto constant_value = constant_evaluator.evaluate(*declaration->initializer))
{
- tree folded = constant_to_tree(constant_value.value());
+ tree folded = constant_to_tree(constant_value.value(), TREE_TYPE(declaration_tree));
if (folded != NULL_TREE)
{
initializer_tree = folded;
@@ -779,7 +845,8 @@ namespace elna::gcc
if (lang_hooks.decls.global_bindings_p())
{
- TREE_STATIC(declaration_tree) = !variable_identifier.exported() && !declaration->is_extern;
+ TREE_STATIC(declaration_tree) =
+ static_cast<unsigned>(!variable_identifier.exported() && !declaration->is_extern);
varpool_node::get_create(declaration_tree);
varpool_node::finalize_decl(declaration_tree);
}
@@ -788,7 +855,7 @@ namespace elna::gcc
DECL_CONTEXT(declaration_tree) = current_function_decl;
f_binding_level->names = chainon(f_binding_level->names, declaration_tree);
- auto *declaration_statement = build1_loc(declaration_location, DECL_EXPR,
+ tree declaration_statement = build1_loc(declaration_location, DECL_EXPR,
void_type_node, declaration_tree);
append_statement(declaration_statement);
}
@@ -797,7 +864,7 @@ namespace elna::gcc
void generic_visitor::visit(boot::named_expression *expression)
{
- auto symbol = this->symbols->lookup(expression->name);
+ tree symbol = this->symbols->lookup(expression->name);
if (symbol == NULL_TREE)
{
@@ -851,66 +918,27 @@ namespace elna::gcc
}
}
- bool generic_visitor::expect_trait_type_only(boot::traits_expression *trait)
- {
- if (trait->arguments.size() != 1)
- {
- error_at(get_location(&trait->position()), "Trait '%s' expects 1 argument, got %lu",
- trait->name.name().c_str(), trait->arguments.size());
- this->current_expression = error_mark_node;
- return false;
- }
- this->current_expression = get_inner_alias(trait->types.front(), this->symbols);
-
- return this->current_expression != error_mark_node;
- }
-
void generic_visitor::visit(boot::traits_expression *trait)
{
location_t trait_location = get_location(&trait->position());
- if (trait->name == "size")
+ if (trait->name == "size" || trait->name == "alignment"
+ || trait->name == "min" || trait->name == "max")
{
- if (expect_trait_type_only(trait))
+ if (trait->arguments.size() != 1)
{
- if (auto value = this->const_evaluator->evaluate_traits(*trait))
- {
- this->current_expression = constant_to_tree(*value);
- }
- else
- {
- this->current_expression = fold_convert_loc(trait_location,
- elna_word_type_node, size_in_bytes(this->current_expression));
- }
+ error_at(trait_location, "Trait '%s' expects 1 argument, got %lu",
+ trait->name.name().c_str(), trait->arguments.size());
+ this->current_expression = error_mark_node;
+ return;
}
- }
- else if (trait->name == "alignment")
- {
- if (expect_trait_type_only(trait))
+ if (auto value = this->const_evaluator->evaluate_traits(*trait))
{
- if (auto value = this->const_evaluator->evaluate_traits(*trait))
- {
- this->current_expression = constant_to_tree(*value);
- }
- else
- {
- this->current_expression = build_int_cstu(elna_word_type_node,
- TYPE_ALIGN_UNIT(this->current_expression));
- }
+ this->current_expression = constant_to_tree(*value);
}
- }
- else if (trait->name == "min" || trait->name == "max")
- {
- if (expect_trait_type_only(trait))
+ else
{
- if (auto value = this->const_evaluator->evaluate_traits(*trait))
- {
- this->current_expression = constant_to_tree(*value);
- }
- else
- {
- this->current_expression = error_mark_node;
- }
+ this->current_expression = error_mark_node;
}
}
else if (trait->name == "offset")
@@ -922,7 +950,6 @@ namespace elna::gcc
this->current_expression = error_mark_node;
return;
}
- this->current_expression = get_inner_alias(trait->types.front(), this->symbols);
auto *field_type = trait->arguments.at(1)->is_named();
if (field_type == nullptr)
@@ -933,12 +960,9 @@ namespace elna::gcc
this->current_expression = error_mark_node;
return;
}
- tree field_declaration = find_field_by_name(trait_location, this->current_expression, field_type->name);
-
- if (field_declaration != error_mark_node)
+ if (auto value = this->const_evaluator->evaluate_traits(*trait))
{
- this->current_expression = fold_convert_loc(trait_location, elna_word_type_node,
- byte_position(field_declaration));
+ this->current_expression = constant_to_tree(*value);
}
else
{
@@ -1099,25 +1123,6 @@ namespace elna::gcc
return decl;
}
- std::pair<tree, tree> generic_visitor::build_loop_head(tree control_variable_declaration, tree limit,
- tree_code comparison, location_t check_location)
- {
- // Put a label in front of the loop condition.
- tree check_label = create_artificial_label(check_location);
- tree check_statement = build1_loc(check_location, LABEL_EXPR, void_type_node, check_label);
- append_statement(check_statement);
-
- // Build the condition and jump if the condition isn't met.
- tree condition = fold_build2(comparison, elna_bool_type_node, control_variable_declaration, limit);
- tree end_label = create_artificial_label(UNKNOWN_LOCATION);
- tree goto_end = build1(GOTO_EXPR, void_type_node, end_label);
- tree condition_statement = build3_loc(check_location, COND_EXPR, void_type_node,
- condition, NULL_TREE, goto_end);
- append_statement(condition_statement);
-
- return { check_label, end_label };
- }
-
void generic_visitor::visit(boot::repeat_statement *statement)
{
enter_scope();
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index c47bb94..e8a7daa 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -25,6 +25,29 @@ along with GCC; see the file COPYING3. If not see
namespace elna::gcc
{
+ const elna::boot::target_info& get_host_target()
+ {
+ static const elna::boot::target_info info = []{
+ elna::boot::target_info target_info;
+
+ target_info.int_properties.size = TYPE_PRECISION(elna_int_type_node) / BITS_PER_UNIT;
+ target_info.int_properties.alignment = TYPE_ALIGN_UNIT(elna_int_type_node);
+ target_info.word_properties.size = TYPE_PRECISION(elna_word_type_node) / BITS_PER_UNIT;
+ target_info.word_properties.alignment = TYPE_ALIGN_UNIT(elna_word_type_node);
+ target_info.pointer_properties.size = TYPE_PRECISION(ptr_type_node) / BITS_PER_UNIT;
+ target_info.pointer_properties.alignment = TYPE_ALIGN_UNIT(ptr_type_node);
+ target_info.char_properties.size = TYPE_PRECISION(elna_char_type_node) / BITS_PER_UNIT;
+ target_info.char_properties.alignment = TYPE_ALIGN_UNIT(elna_char_type_node);
+ target_info.float_properties.size = TYPE_PRECISION(elna_float_type_node) / BITS_PER_UNIT;
+ target_info.float_properties.alignment = TYPE_ALIGN_UNIT(elna_float_type_node);
+ target_info.bool_properties.size = (TYPE_PRECISION(elna_bool_type_node) + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
+ target_info.bool_properties.alignment = TYPE_ALIGN_UNIT(elna_bool_type_node);
+
+ return target_info;
+ }();
+ return info;
+ }
+
bool is_integral_type(tree type)
{
gcc_assert(TYPE_P(type));
@@ -74,7 +97,7 @@ namespace elna::gcc
void defer(tree statement_tree)
{
- defer_scope new_defer{ statement_tree, alloc_stmt_list() };
+ defer_scope new_defer{ .defer_block = statement_tree, .try_statements = alloc_stmt_list() };
vec_safe_insert(f_binding_level->defers, 0, new_defer);
}
@@ -87,9 +110,9 @@ namespace elna::gcc
defer_scope *defer_iterator = f_binding_level->defers->begin();
tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node,
defer_iterator->try_statements, defer_iterator->defer_block);
- int i;
+ int index;
- FOR_EACH_VEC_ELT_FROM(*f_binding_level->defers, i, defer_iterator, 1)
+ FOR_EACH_VEC_ELT_FROM(*f_binding_level->defers, index, defer_iterator, 1)
{
append_to_statement_list(defer_tree, &defer_iterator->try_statements);
defer_tree = build2(TRY_FINALLY_EXPR, void_type_node,
@@ -98,7 +121,7 @@ namespace elna::gcc
return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), f_binding_level->statement_list, defer_tree);
}
- tree build_field(location_t location, tree record_type, const std::string name, tree type)
+ tree build_field(location_t location, tree record_type, const std::string& name, tree type)
{
tree field_declaration = build_decl(location,
FIELD_DECL, get_identifier(name.c_str()), type);
@@ -174,7 +197,7 @@ namespace elna::gcc
return type;
}
tree field_declaration = TYPE_FIELDS(type);
-
+ // NOLINTNEXTLINE(readability-simplify-boolean-expr)
if (!RECORD_OR_UNION_TYPE_P(type))
{
error_at(expression_location, "Type '%s' does not have a field named '%s'",
@@ -211,12 +234,12 @@ namespace elna::gcc
tree build_enumeration_type(const std::vector<std::string>& members)
{
tree composite_type_node = make_node(ENUMERAL_TYPE);
- const tree base_type = integer_type_node;
+ tree base_type = integer_type_node;
TREE_TYPE(composite_type_node) = base_type;
ENUM_IS_SCOPED(composite_type_node) = 1;
- tree *pp = &TYPE_VALUES(composite_type_node);
+ tree *members_pointer = &TYPE_VALUES(composite_type_node);
std::size_t order{ 1 };
for (const std::string& member : members)
@@ -231,8 +254,8 @@ namespace elna::gcc
TYPE_MAX_VALUE(composite_type_node) = DECL_INITIAL(member_declaration);
- *pp = build_tree_list(member_name, member_declaration);
- pp = &TREE_CHAIN(*pp);
+ *members_pointer = build_tree_list(member_name, member_declaration);
+ members_pointer = &TREE_CHAIN(*members_pointer);
}
TYPE_MIN_VALUE(composite_type_node) = DECL_INITIAL(TREE_VALUE(TYPE_VALUES(composite_type_node)));
TYPE_UNSIGNED(composite_type_node) = TYPE_UNSIGNED(base_type);
diff --git a/gcc/gcc/elna1.cc b/gcc/gcc/elna1.cc
index fde2adc..8eb103b 100644
--- a/gcc/gcc/elna1.cc
+++ b/gcc/gcc/elna1.cc
@@ -52,29 +52,6 @@ union GTY ((desc("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
/* Language hooks. */
-static const elna::boot::target_info& get_host_target()
-{
- static const elna::boot::target_info info = []{
- elna::boot::target_info target_info;
-
- target_info.int_size = TYPE_PRECISION(elna_int_type_node) / BITS_PER_UNIT;
- target_info.int_alignment = TYPE_ALIGN_UNIT(elna_int_type_node);
- target_info.word_size = TYPE_PRECISION(elna_word_type_node) / BITS_PER_UNIT;
- target_info.word_alignment = TYPE_ALIGN_UNIT(elna_word_type_node);
- target_info.pointer_size = TYPE_PRECISION(ptr_type_node) / BITS_PER_UNIT;
- target_info.pointer_alignment = TYPE_ALIGN_UNIT(ptr_type_node);
- target_info.char_size = TYPE_PRECISION(elna_char_type_node) / BITS_PER_UNIT;
- target_info.char_alignment = TYPE_ALIGN_UNIT(elna_char_type_node);
- target_info.float_size = TYPE_PRECISION(elna_float_type_node) / BITS_PER_UNIT;
- target_info.float_alignment = TYPE_ALIGN_UNIT(elna_float_type_node);
- target_info.bool_size = TYPE_PRECISION(elna_bool_type_node) / BITS_PER_UNIT;
- target_info.bool_alignment = TYPE_ALIGN_UNIT(elna_bool_type_node);
-
- return target_info;
- }();
- return info;
-}
-
static bool elna_langhook_init(void)
{
build_common_tree_nodes(false);
@@ -148,7 +125,7 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha
outcome_bag.add_import(cached_import->second);
}
}
- outcome.errors() = analyze_semantics(outcome.tree, outcome_bag, get_host_target());
+ outcome.errors() = analyze_semantics(outcome.tree, outcome_bag, elna::gcc::get_host_target());
}
if (outcome.has_errors())
{
@@ -172,7 +149,7 @@ static void elna_langhook_parse_file(void)
{
linemap_add(line_table, LC_ENTER, 0, in_fnames[i], 1);
elna::gcc::generic_visitor generic_visitor{ state.custom,
- state.find(in_fnames[i])->second, get_host_target() };
+ state.find(in_fnames[i])->second, elna::gcc::get_host_target() };
outcome.tree->accept(&generic_visitor);
linemap_add(line_table, LC_LEAVE, 0, NULL, 0);
}
diff --git a/include/elna/boot/evaluator.h b/include/elna/boot/evaluator.h
index 015a2c7..2d832bf 100644
--- a/include/elna/boot/evaluator.h
+++ b/include/elna/boot/evaluator.h
@@ -30,22 +30,35 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
/**
+ * Size and alignment of a single type on the target machine.
+ */
+ struct type_properties
+ {
+ std::size_t size{0};
+ std::size_t alignment{0};
+ };
+
+ /**
+ * Size, alignment and offset of each field of a record.
+ */
+ struct record_properties
+ {
+ ordered_map<std::size_t> offset_map;
+ std::size_t size;
+ std::size_t alignment;
+ };
+
+ /**
* Target machine information, populated by the compiler backend glue layer.
*/
struct target_info
{
- std::size_t int_size{0};
- std::size_t int_alignment{0};
- std::size_t word_size{0};
- std::size_t word_alignment{0};
- std::size_t pointer_size{0};
- std::size_t pointer_alignment{0};
- std::size_t char_size{0};
- std::size_t char_alignment{0};
- std::size_t float_size{0};
- std::size_t float_alignment{0};
- std::size_t bool_size{0};
- std::size_t bool_alignment{0};
+ type_properties int_properties;
+ type_properties word_properties;
+ type_properties pointer_properties;
+ type_properties char_properties;
+ type_properties float_properties;
+ type_properties bool_properties;
};
template<template<typename, typename> typename C, template<typename> typename Alloc = std::allocator>
@@ -85,27 +98,43 @@ namespace elna::boot
{
}
- constant_value& operator*()
+ Container& operator*()
{
return *this->container;
}
- constant_value& operator*() const
+ const Container& operator*() const
{
return *this->container;
}
- constant_value *operator->()
+ Container *operator->()
{
return this->container.get();
}
- constant_value *operator->() const
+ const Container *operator->() const
{
return this->container.get();
}
};
+ std::optional<type_properties> get_type_properties(const type& subject, const target_info& target);
+
+ /**
+ * Computes the layout of a record type at compile time.
+ *
+ * Walks fields in layout order (base chain, root to leaf), aligning
+ * each field to its natural alignment. Uses \p target for size and
+ * alignment values so that the computation is backend-independent.
+ *
+ * \param subject The record type to lay out.
+ * \param target Target type information (sizes, alignments).
+ * \return Record layout, or std::nullopt if any field cannot be layed out.
+ */
+ std::optional<record_properties> layout_record(const std::shared_ptr<record_type>& subject,
+ const target_info& target);
+
/**
* Called on-demand.
*/
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index 1f22baf..0719151 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -26,7 +26,6 @@ along with GCC; see the file COPYING3. If not see
#include <variant>
#include <vector>
#include <unordered_map>
-#include <ranges>
namespace elna::boot
{
@@ -217,7 +216,7 @@ namespace elna::boot
}
else
{
- return this->payload.at(search_result->second);
+ return this->payload.begin() + search_result->second;
}
}
diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h
index 0cdf519..846a8db 100644
--- a/include/elna/gcc/elna-builtins.h
+++ b/include/elna/gcc/elna-builtins.h
@@ -30,12 +30,14 @@ namespace elna::gcc
void init_ttree();
std::shared_ptr<symbol_table> builtin_symbol_table();
- void rewrite_symbol_table(std::shared_ptr<boot::symbol_table> info_table, std::shared_ptr<symbol_table> symbols);
- tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
- std::shared_ptr<symbol_table> symbols);
- tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols);
+ void rewrite_symbol_table(const std::shared_ptr<boot::symbol_table>& info_table,
+ const std::shared_ptr<symbol_table>& symbols);
+ tree handle_symbol(const std::string& symbol_name,
+ const std::shared_ptr<boot::alias_type>& reference,
+ const std::shared_ptr<symbol_table>& symbols);
+ tree get_inner_alias(const boot::type& type, const std::shared_ptr<symbol_table>& symbols);
void declare_procedure(const std::string& name, const boot::procedure_info& info,
- std::shared_ptr<symbol_table> symbols);
+ const std::shared_ptr<symbol_table>& symbols);
tree declare_variable(const std::string& name, const boot::variable_info& info,
- std::shared_ptr<symbol_table> symbols);
+ const std::shared_ptr<symbol_table>& symbols);
}
diff --git a/include/elna/gcc/elna-diagnostic.h b/include/elna/gcc/elna-diagnostic.h
index 31e180e..2cb30f5 100644
--- a/include/elna/gcc/elna-diagnostic.h
+++ b/include/elna/gcc/elna-diagnostic.h
@@ -33,7 +33,7 @@ namespace elna::gcc
struct linemap_guard
{
explicit linemap_guard(const char *filename);
- explicit linemap_guard(const std::filesystem::path filename);
+ explicit linemap_guard(const std::filesystem::path& filename);
linemap_guard(const linemap_guard&) = delete;
linemap_guard(linemap_guard&&) = delete;
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index 727c646..27fdb43 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -42,6 +42,12 @@ namespace elna::gcc
std::map<std::string, boot::expression *> evaluated_initializers;
std::unique_ptr<boot::evaluator> const_evaluator;
+ static tree build_equality(boot::binary_expression *expression, tree left, tree right);
+ static tree build_equality_comparison(location_t loc, tree left, tree right,
+ const boot::type& left_type, tree_code equality_code);
+ static std::pair<tree, tree> build_loop_head(tree control_variable_declaration,
+ tree limit, tree_code comparison, location_t check_location);
+
void enter_scope();
tree leave_scope();
@@ -51,9 +57,6 @@ namespace elna::gcc
tree make_if_branch(boot::conditional_statements& branch, tree next,
tree goto_append = NULL_TREE);
- tree build_equality(boot::binary_expression *expression, tree left, tree right);
- tree build_equality_comparison(location_t loc, tree left, tree right,
- const boot::type& left_type, tree_code equality_code);
void build_case_integral(boot::case_statement *statement, tree condition_expression);
void build_case_general(boot::case_statement *statement, tree condition_expression);
void build_procedure_call(location_t call_location,
@@ -61,16 +64,13 @@ namespace elna::gcc
bool build_builtin_procedures(boot::procedure_call *call);
void build_assert_builtin(location_t call_location, const std::vector<boot::expression *>& arguments);
- bool expect_trait_type_only(boot::traits_expression *trait);
void visit_statements(const std::vector<boot::statement *>& statements);
bool assert_constant(location_t expression_location);
tree declare_local_variable(const boot::identifier& name,
const boot::variable_info& info, tree initial_value);
- std::pair<tree, tree> build_loop_head(tree control_variable_declaration, tree limit,
- tree_code comparison, location_t check_location);
public:
- generic_visitor(std::shared_ptr<symbol_table> symbol_table,
+ generic_visitor(const std::shared_ptr<symbol_table>& symbol_table,
elna::boot::symbol_bag bag, const elna::boot::target_info& target);
void visit(boot::procedure_declaration *declaration) override;
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index ac97e41..a989656 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -17,8 +17,6 @@ along with GCC; see the file COPYING3. If not see
#pragma once
-#include <forward_list>
-
#include "config.h"
#include "system.h"
#include "coretypes.h"
@@ -28,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
#include "fold-const.h"
#include "elna/boot/ast.h"
+#include "elna/boot/evaluator.h"
#include "elna/boot/symbol.h"
#include "elna/gcc/elna1.h"
@@ -60,12 +59,14 @@ namespace elna::gcc
tree chain_defer();
tree do_pointer_arithmetic(boot::binary_operator binary_operator,
- tree left, tree right, location_t expression_location);
- tree build_field(location_t location, tree record_type, const std::string name, tree type);
+ tree left, tree right, location_t operation_location);
+ tree build_field(location_t location, tree record_type, const std::string& name, tree type);
tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name);
tree build_static_array_type(tree type, const std::uint64_t size);
tree build_enumeration_type(const std::vector<std::string>& members);
+ const elna::boot::target_info& get_host_target();
+
tree extract_constant(tree expression);
template<typename... Args>
diff --git a/testsuite/runnable/record_layout.elna b/testsuite/runnable/record_layout.elna
new file mode 100644
index 0000000..180d7bc
--- /dev/null
+++ b/testsuite/runnable/record_layout.elna
@@ -0,0 +1,55 @@
+type
+ IntChar = record
+ x: Int;
+ y: Char
+ end
+ CharInt = record
+ c: Char;
+ i: Int
+ end
+ Base = record
+ x: Int
+ end
+ Derived = record(Base)
+ y: Char
+ end
+ ArrayRecord = record
+ a: [3]Int;
+ c: Char
+ end
+
+proc f()
+var
+ int_size: const Word := #size(Int)
+
+ int_char_size: const Word := #size(IntChar)
+ int_char_y_offset: const Word := #offset(IntChar, y)
+ char_int_size: const Word := #size(CharInt)
+ char_int_c_offset: const Word := #offset(CharInt, c)
+ char_int_i_offset: const Word := #offset(CharInt, i)
+ derived_size: const Word := #size(Derived)
+ derived_x_offset: const Word := #offset(Derived, x)
+ derived_y_offset: const Word := #offset(Derived, y)
+ array_record_size: const Word := #size(ArrayRecord)
+ array_record_a_offset: const Word := #offset(ArrayRecord, a)
+ array_record_c_offset: const Word := #offset(ArrayRecord, c)
+begin
+ assert(int_char_y_offset = int_size);
+ assert(int_char_size = 2u * int_size);
+
+ assert(char_int_c_offset = 0u);
+ assert(char_int_i_offset = int_size);
+ assert(char_int_size = 2u * int_size);
+
+ assert(derived_x_offset = 0u);
+ assert(derived_y_offset = int_size);
+ assert(derived_size = 2u * int_size);
+
+ assert(array_record_size = 4u * int_size);
+ assert(array_record_a_offset = 0u);
+ assert(array_record_c_offset = 3u * int_size)
+return
+
+begin
+ f()
+end.