Implement computing struct layout (size, alignment, field offsets) in type checking
This commit is contained in:
parent
0d6b491fd4
commit
8c0b371fdb
4 changed files with 119 additions and 8 deletions
|
|
@ -15,6 +15,8 @@ foreign func putchar(c: i32) -> i32
|
||||||
func print(c: u8):
|
func print(c: u8):
|
||||||
putchar(c as i32)
|
putchar(c as i32)
|
||||||
|
|
||||||
|
let x = 0
|
||||||
|
|
||||||
func test1():
|
func test1():
|
||||||
print('H')
|
print('H')
|
||||||
print('e')
|
print('e')
|
||||||
|
|
@ -42,9 +44,20 @@ func test() -> i32:
|
||||||
else:
|
else:
|
||||||
return b()
|
return b()
|
||||||
|
|
||||||
print('O')
|
struct interval:
|
||||||
print('K')
|
min: f32
|
||||||
print('\n')
|
max: f32
|
||||||
|
|
||||||
|
struct box2f:
|
||||||
|
x: interval
|
||||||
|
y: interval
|
||||||
|
|
||||||
|
struct weird:
|
||||||
|
a: i32
|
||||||
|
b: u8
|
||||||
|
c: f32 -> f32
|
||||||
|
d: f64
|
||||||
|
e: f16
|
||||||
|
|
||||||
//func test1():
|
//func test1():
|
||||||
// let str = ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n']
|
// let str = ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n']
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,24 @@
|
||||||
namespace pslang::ast
|
namespace pslang::ast
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct field_layout
|
||||||
|
{
|
||||||
|
std::size_t offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct field_definition
|
struct field_definition
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
ast::type_ptr type;
|
ast::type_ptr type;
|
||||||
ast::location location;
|
ast::location location;
|
||||||
|
|
||||||
|
field_layout layout = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct struct_layout
|
||||||
|
{
|
||||||
|
std::size_t size = 0;
|
||||||
|
std::size_t alignment = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct struct_definition
|
struct struct_definition
|
||||||
|
|
@ -24,6 +37,8 @@ namespace pslang::ast
|
||||||
std::vector<field_definition> fields;
|
std::vector<field_definition> fields;
|
||||||
ast::location prelude_location;
|
ast::location prelude_location;
|
||||||
ast::location location;
|
ast::location location;
|
||||||
|
|
||||||
|
struct_layout layout = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct field_access
|
struct field_access
|
||||||
|
|
|
||||||
|
|
@ -382,13 +382,13 @@ namespace pslang::ast
|
||||||
put_indent(out, options);
|
put_indent(out, options);
|
||||||
out << "field { name = \"" << node.name << "\", type = ";
|
out << "field { name = \"" << node.name << "\", type = ";
|
||||||
print(out, *node.type);
|
print(out, *node.type);
|
||||||
out << " }\n";
|
out << ", offset = " << node.layout.offset << " }\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(struct_definition const & node)
|
void apply(struct_definition const & node)
|
||||||
{
|
{
|
||||||
put_indent(out, options);
|
put_indent(out, options);
|
||||||
out << "struct { name = \"" << node.name << "\" }\n";
|
out << "struct { name = \"" << node.name << "\", size = " << node.layout.size << ", align = " << node.layout.alignment << " }\n";
|
||||||
for (auto const & field : node.fields)
|
for (auto const & field : node.fields)
|
||||||
child(field);
|
child(field);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <pslang/ast/error.hpp>
|
#include <pslang/ast/error.hpp>
|
||||||
#include <pslang/types/print.hpp>
|
#include <pslang/types/print.hpp>
|
||||||
#include <pslang/types/type.hpp>
|
#include <pslang/types/type.hpp>
|
||||||
|
#include <pslang/types/type_visitor.hpp>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
@ -35,6 +36,10 @@ namespace pslang::ast
|
||||||
types::type_ptr type;
|
types::type_ptr type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ast::struct_definition * node;
|
||||||
|
bool layout_being_computed = false;
|
||||||
|
bool layout_ready = false;
|
||||||
|
|
||||||
std::vector<field_data> fields;
|
std::vector<field_data> fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -45,11 +50,85 @@ namespace pslang::ast
|
||||||
std::unordered_map<std::string, struct_data> structs;
|
std::unordered_map<std::string, struct_data> structs;
|
||||||
|
|
||||||
bool is_function_scope = false;
|
bool is_function_scope = false;
|
||||||
bool is_global_scope = false;
|
|
||||||
|
|
||||||
types::type_ptr expected_return_type = nullptr;
|
types::type_ptr expected_return_type = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void compute_layout(struct_data & data, std::vector<scope> & scopes);
|
||||||
|
|
||||||
|
struct size_and_alignment
|
||||||
|
{
|
||||||
|
std::size_t size;
|
||||||
|
std::size_t alignment;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct field_layout_visitor
|
||||||
|
: types::const_visitor<field_layout_visitor>
|
||||||
|
{
|
||||||
|
std::vector<scope> & scopes;
|
||||||
|
|
||||||
|
using const_visitor::apply;
|
||||||
|
|
||||||
|
size_and_alignment apply(types::unit_type const &)
|
||||||
|
{
|
||||||
|
return {.size = 1, .alignment = 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_and_alignment apply(types::primitive_type const & type)
|
||||||
|
{
|
||||||
|
auto size = types::builtin_type_size(type);
|
||||||
|
return {.size = size, .alignment = size};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_and_alignment apply(types::array_type const & type)
|
||||||
|
{
|
||||||
|
auto base = apply(*type.element_type);
|
||||||
|
return {.size = base.size * type.size, .alignment = base.alignment};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_and_alignment apply(types::function_type const & type)
|
||||||
|
{
|
||||||
|
return {.size = 8, .alignment = 8};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_and_alignment apply(types::named_type const & type)
|
||||||
|
{
|
||||||
|
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it)
|
||||||
|
if (auto jt = it->structs.find(type.name); jt != it->structs.end())
|
||||||
|
{
|
||||||
|
// TODO: better error message (including the resursive inclusion path)
|
||||||
|
if (!jt->second.layout_ready && jt->second.layout_being_computed)
|
||||||
|
throw validation_error("Recursive structs are not allowed", jt->second.node->location);
|
||||||
|
|
||||||
|
compute_layout(jt->second, scopes);
|
||||||
|
|
||||||
|
auto & layout = jt->second.node->layout;
|
||||||
|
return {.size = layout.size, .alignment = layout.alignment};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unknown type \"" + type.name + "\"");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void compute_layout(struct_data & data, std::vector<scope> & scopes)
|
||||||
|
{
|
||||||
|
if (data.layout_ready)
|
||||||
|
return;
|
||||||
|
|
||||||
|
data.layout_being_computed = true;
|
||||||
|
auto & layout = data.node->layout;
|
||||||
|
for (std::size_t i = 0; i < data.fields.size(); ++i)
|
||||||
|
{
|
||||||
|
auto field_layout = field_layout_visitor{{}, scopes}.apply(*data.fields[i].type);
|
||||||
|
layout.alignment = std::max(layout.alignment, field_layout.alignment);
|
||||||
|
layout.size = ((layout.size + field_layout.alignment - 1) / field_layout.alignment) * field_layout.alignment;
|
||||||
|
data.node->fields[i].layout.offset = layout.size;
|
||||||
|
layout.size += field_layout.size;
|
||||||
|
}
|
||||||
|
layout.size = ((layout.size + layout.alignment - 1) / layout.alignment) * layout.alignment;
|
||||||
|
data.layout_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
struct resolve_types_visitor
|
struct resolve_types_visitor
|
||||||
: type_visitor<resolve_types_visitor>
|
: type_visitor<resolve_types_visitor>
|
||||||
{
|
{
|
||||||
|
|
@ -179,12 +258,13 @@ namespace pslang::ast
|
||||||
resolve_types(scopes, *node.type);
|
resolve_types(scopes, *node.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(struct_definition const & node)
|
void apply(struct_definition & node)
|
||||||
{
|
{
|
||||||
for (auto const & field : node.fields)
|
for (auto const & field : node.fields)
|
||||||
apply(field);
|
apply(field);
|
||||||
|
|
||||||
auto & data = scopes.back().structs[node.name];
|
auto & data = scopes.back().structs[node.name];
|
||||||
|
data.node = &node;
|
||||||
for (auto const & field : node.fields)
|
for (auto const & field : node.fields)
|
||||||
data.fields.push_back({.name = field.name, .type = get_type(*field.type)});
|
data.fields.push_back({.name = field.name, .type = get_type(*field.type)});
|
||||||
}
|
}
|
||||||
|
|
@ -787,6 +867,9 @@ namespace pslang::ast
|
||||||
populate_globals(scopes, node);
|
populate_globals(scopes, node);
|
||||||
for (auto const & statement : node.statements)
|
for (auto const & statement : node.statements)
|
||||||
apply(*statement);
|
apply(*statement);
|
||||||
|
|
||||||
|
for (auto & struct_data : scopes.back().structs)
|
||||||
|
compute_layout(struct_data.second, scopes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -795,7 +878,7 @@ namespace pslang::ast
|
||||||
void check_and_infer_types(statement_list_ptr & statements)
|
void check_and_infer_types(statement_list_ptr & statements)
|
||||||
{
|
{
|
||||||
std::vector<scope> scopes;
|
std::vector<scope> scopes;
|
||||||
scopes.emplace_back().is_global_scope = true;
|
scopes.emplace_back();
|
||||||
check_visitor visitor{{}, {}, scopes};
|
check_visitor visitor{{}, {}, scopes};
|
||||||
visitor.apply(*statements);
|
visitor.apply(*statements);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue