diff options
| -rw-r--r-- | boot/evaluator.cc | 232 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 82 | ||||
| -rw-r--r-- | gcc/gcc/elna-diagnostic.cc | 6 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 321 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 41 | ||||
| -rw-r--r-- | gcc/gcc/elna1.cc | 27 | ||||
| -rw-r--r-- | include/elna/boot/evaluator.h | 61 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 3 | ||||
| -rw-r--r-- | include/elna/gcc/elna-builtins.h | 14 | ||||
| -rw-r--r-- | include/elna/gcc/elna-diagnostic.h | 2 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 14 | ||||
| -rw-r--r-- | include/elna/gcc/elna-tree.h | 9 | ||||
| -rw-r--r-- | testsuite/runnable/record_layout.elna | 55 |
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. |
