Add sizeof & alignof builtins
This commit is contained in:
parent
2a3e2171b0
commit
800de413cf
13 changed files with 178 additions and 95 deletions
|
|
@ -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*)
|
||||
|
|
|
|||
29
libs/ast/include/pslang/ast/builtins.hpp
Normal file
29
libs/ast/include/pslang/ast/builtins.hpp
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?)
|
||||
|
|
|
|||
9
spec.txt
9
spec.txt
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue