Add sizeof & alignof builtins

This commit is contained in:
Nikita Lisitsa 2026-04-03 00:04:12 +03:00
parent 2a3e2171b0
commit 800de413cf
13 changed files with 178 additions and 95 deletions

View file

@ -670,8 +670,7 @@ func unlock_mutex(mutex: mutex mut*):
func make_default_scene() -> scene:
let object_count = 8ul
// No sizeof yet...
let objects = allocate(object_count * 20ul * 4ul) as object mut*
let objects = allocate(object_count * sizeof(object)) as object mut*
// Filling all objects in the same function overflows the max stack size
// dictated by arm64 limitations (add immediate is bound by 12 bits => 4Kb)
@ -814,8 +813,8 @@ func main():
mut report_count = 0u
let thread_count = 9ul
let threads = allocate(thread_count * 8ul) as thread mut*
let threads_data = allocate(thread_count * 104ul) as thread_data mut*
let threads = allocate(thread_count * sizeof(thread)) as thread mut*
let threads_data = allocate(thread_count * sizeof(thread_data)) as thread_data mut*
mut th = 0ul
while th < thread_count:
@ -825,7 +824,7 @@ func main():
data.camera = camera
data.ystart = th
data.ystep = thread_count
data.done_mutex = allocate(64ul) as mutex mut*
data.done_mutex = allocate(sizeof(mutex)) as mutex mut*
create_mutex(data.done_mutex)
data.done = 0ul
threads[th] = create_thread(thread_func, data as unit mut*)

View file

@ -0,0 +1,29 @@
#pragma once
#include <pslang/ast/expression_fwd.hpp>
#include <pslang/ast/type_fwd.hpp>
#include <pslang/ast/location.hpp>
#include <pslang/types/type_fwd.hpp>
namespace pslang::ast
{
struct sizeof_operator
{
expression_ptr expression;
type_ptr type;
location location;
types::type_ptr inferred_source_type = nullptr;
types::type_ptr inferred_type = nullptr;
};
struct alignof_operator
{
expression_ptr expression;
type_ptr type;
location location;
types::type_ptr inferred_source_type = nullptr;
types::type_ptr inferred_type = nullptr;
};
}

View file

@ -9,6 +9,7 @@
#include <pslang/ast/array.hpp>
#include <pslang/ast/struct.hpp>
#include <pslang/ast/control.hpp>
#include <pslang/ast/builtins.hpp>
#include <pslang/ast/expression_fwd.hpp>
#include <pslang/types/type_fwd.hpp>
@ -42,7 +43,9 @@ namespace pslang::ast
array,
array_access,
field_access,
if_expression
if_expression,
sizeof_operator,
alignof_operator
>;
struct expression

View file

@ -13,53 +13,8 @@ namespace pslang::ast
{
using const_expression_visitor::apply;
template <typename T>
location apply(primitive_literal_base<T> const & node)
{
return node.location;
}
location apply(identifier const & node)
{
return node.location;
}
location apply(unary_operation const & node)
{
return node.location;
}
location apply(binary_operation const & node)
{
return node.location;
}
location apply(cast_operation const & node)
{
return node.location;
}
location apply(function_call const & node)
{
return node.location;
}
location apply(array const & node)
{
return node.location;
}
location apply(array_access const & node)
{
return node.location;
}
location apply(field_access const & node)
{
return node.location;
}
location apply(if_expression const & node)
template <typename Node>
location apply(Node const & node)
{
return node.location;
}
@ -76,47 +31,8 @@ namespace pslang::ast
return std::make_unique<types::type>(types::primitive_type{types::primitive_type_base<T>{}});
}
types::type_ptr apply(identifier const & node)
{
return node.inferred_type;
}
types::type_ptr apply(unary_operation const & node)
{
return node.inferred_type;
}
types::type_ptr apply(binary_operation const & node)
{
return node.inferred_type;
}
types::type_ptr apply(cast_operation const & node)
{
return node.inferred_type;
}
types::type_ptr apply(function_call const & node)
{
return node.inferred_type;
}
types::type_ptr apply(array const & node)
{
return node.inferred_type;
}
types::type_ptr apply(array_access const & node)
{
return node.inferred_type;
}
types::type_ptr apply(field_access const & node)
{
return node.inferred_type;
}
types::type_ptr apply(if_expression const & node)
template <typename Node>
types::type_ptr apply(Node const & node)
{
return node.inferred_type;
}

View file

@ -367,6 +367,38 @@ namespace pslang::ast
child(*node.if_true);
child(*node.if_false);
}
void apply(sizeof_operator const & node)
{
put_indent(out, options);
if (node.expression)
{
out << "sizeof\n";
child(*node.expression);
}
else
{
out << "sizeof ";
print(out, *node.type);
out << "\n";
}
}
void apply(alignof_operator const & node)
{
put_indent(out, options);
if (node.expression)
{
out << "alignof\n";
child(*node.expression);
}
else
{
out << "sizeof ";
print(out, *node.type);
out << "\n";
}
}
};
struct statement_print_visitor

View file

@ -288,6 +288,22 @@ namespace pslang::ast
apply(*if_expression.if_false);
}
void apply(sizeof_operator const & sizeof_operator)
{
if (sizeof_operator.expression)
apply(*sizeof_operator.expression);
if (sizeof_operator.type)
apply(*sizeof_operator.type);
}
void apply(alignof_operator const & alignof_operator)
{
if (alignof_operator.expression)
apply(*alignof_operator.expression);
if (alignof_operator.type)
apply(*alignof_operator.type);
}
void apply(expression_ptr const & expression_ptr)
{
apply(*expression_ptr);

View file

@ -818,6 +818,46 @@ namespace pslang::ast
node.inferred_type = true_type;
}
void apply(sizeof_operator & node)
{
if (node.expression && node.type)
throw invalid_ast_error("sizeof node cannot have both an expression and a type", node.location);
if (node.expression)
{
apply(*node.expression);
node.inferred_source_type = get_type(*node.expression);
}
if (node.type)
{
resolve_types(*node.type);
node.inferred_source_type = get_type(*node.type);
}
node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}});
}
void apply(alignof_operator & node)
{
if (node.expression && node.type)
throw invalid_ast_error("alignof node cannot have both an expression and a type", node.location);
if (node.expression)
{
apply(*node.expression);
node.inferred_source_type = get_type(*node.expression);
}
if (node.type)
{
resolve_types(*node.type);
node.inferred_source_type = get_type(*node.type);
}
node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}});
}
void apply(expression_ptr const & node)
{
apply(*node);

View file

@ -691,6 +691,16 @@ namespace pslang::interpreter
throw std::runtime_error("Not implemented");
}
value eval_impl(context & context, ast::sizeof_operator const & sizeof_operator)
{
throw std::runtime_error("Not implemented");
}
value eval_impl(context & context, ast::alignof_operator const & alignof_operator)
{
throw std::runtime_error("Not implemented");
}
value eval_impl(context & context, ast::expression_ptr const & expression)
{
return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression);
@ -784,6 +794,16 @@ namespace pslang::interpreter
throw std::runtime_error("Not implemented");
}
value * eval_ref_impl(context & context, ast::sizeof_operator const & sizeof_operator)
{
throw std::runtime_error("Not implemented");
}
value * eval_ref_impl(context & context, ast::alignof_operator const & alignof_operator)
{
throw std::runtime_error("Not implemented");
}
value * eval_ref_impl(context & context, ast::expression_ptr const & expression)
{
return std::visit([&](auto const & expression){ return eval_ref_impl(context, expression); }, *expression);

View file

@ -513,6 +513,18 @@ namespace pslang::ir
return result;
}
node_ref apply(ast::sizeof_operator const & node)
{
mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_size(*node.inferred_source_type)}}}, node.inferred_type);
return last();
}
node_ref apply(ast::alignof_operator const & node)
{
mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_alignment(*node.inferred_source_type)}}}, node.inferred_type);
return last();
}
// Statements
node_ref apply(ast::expression_ptr const & node)

View file

@ -39,6 +39,8 @@ return { return bp::make_return(ctx.location); }
struct { return bp::make_struct(ctx.location); }
true { return bp::make_true(ctx.location); }
false { return bp::make_false(ctx.location); }
sizeof { return bp::make_sizeof(ctx.location); }
alignof { return bp::make_alignof(ctx.location); }
unit { return bp::make_unit(ctx.location); }
bool { return bp::make_bool(ctx.location); }

View file

@ -150,6 +150,8 @@ template <typename T>
%token struct
%token true
%token false
%token sizeof
%token alignof
%token unit
%token bool
@ -355,6 +357,8 @@ expression
| asterisk expression %prec DEREFERENCE { $$ = ast::unary_operation{ast::unary_operation_type::dereference, std::make_unique<ast::expression>($2), @$ }; }
| postfix_expression { $$ = $1; }
| if expression then expression else expression { $$ = ast::if_expression{ std::make_unique<ast::expression>($2), std::make_unique<ast::expression>($4), std::make_unique<ast::expression>($6), @$ }; }
| sizeof lparen type_expression rparen { $$ = ast::sizeof_operator{ nullptr, std::make_unique<ast::type>($3), @$ }; }
| alignof lparen type_expression rparen { $$ = ast::alignof_operator{ nullptr, std::make_unique<ast::type>($3), @$ }; }
;
postfix_expression

View file

@ -1,5 +1,6 @@
Future plans:
* Globals (requires a separate mmaped segment in JIT compiler)
* Merge ast types and values (helps for sizeof/alignof builtins, also paves the path for metaprogramming)
* Function overloading: separate functions from values (again) in interpreter, allow casting to specific function type to take function value
* Const propagation: annotate expression AST nodes that are computable in compile-time
* Generic parameters: can be either values or `t : type`, but always compile-time
@ -27,11 +28,11 @@ Optimizer plans:
General backlog:
* Mutually recursive structs (relevant only with pointers)
* Empty array expression
* Calling functions as methods
* Replace std::runtime_error with appropriate custom exception types
* Replace std::ostringstream with std::format (need support for std::format in type/ast printing)
* TEST COVERAGE!!!
Platform-dependent operations backlog:
* Division by zero (returns 0 on arm64, triggers an interrupt on x86) - declare as UB?
* Floating-point <-> integer conversion (is rounding the same on all platforms?)

View file

@ -139,6 +139,15 @@ Assignment:
x = 15 // requires x to be a mut variable
*p = 15 // p must be a pointer to mut
Special built-ins:
typeof(value)
sizeof(type)
sizeof(value) // same as sizeof(typeof(value))
alignof(type)
alignof(value) // same as alignof(typeof(value))
offsetof(struct type, field)
offsetof(field access expression)
======== FLOW CONTROL ========
Flow control: