Implement global variables

This commit is contained in:
Nikita Lisitsa 2026-05-08 21:38:44 +03:00
parent 393db4b912
commit 9337490f5b
19 changed files with 319 additions and 183 deletions

View file

@ -260,12 +260,12 @@ int main(int argc, char ** argv)
for (auto const & resolve : pcontext.foreign_resolve) for (auto const & resolve : pcontext.foreign_resolve)
{ {
auto fptr = jit::load_foreign(resolve.name); auto fptr = jit::load_foreign(resolve.name);
std::copy_n((std::uint8_t const *)(&fptr), 8, pcontext.code.data() + resolve.offset); std::copy_n((std::uint8_t const *)(&fptr), 8, pcontext.storage.storage.data() + resolve.offset);
} }
auto executable = jit::make_host_executable(pcontext.code); auto executable = jit::make_host_executable(pcontext.storage);
auto entry_point = (void(*)())(executable.data.get() + pcontext.entry_point); auto entry_point = (void(*)())(executable.get() + pcontext.entry_point);
entry_point(); entry_point();
} }
} }

View file

@ -1,72 +1,15 @@
func add1(x : u32) -> u32:
return x + 1u
func sub1(x : u32) -> u32:
return x - 1u
func add_or_sub(add : bool) -> (u32 -> u32):
if add:
return add1
else:
return sub1
foreign func putchar(c: i32) -> i32
func print(c: u8): func print(c: u8):
foreign func putchar(c: i32) -> i32
putchar(c as i32) putchar(c as i32)
func test1():
print('H')
print('e')
print('l')
print('l')
print('o')
print(',')
print(' ')
print('w')
print('o')
print('r')
print('l')
print('d')
print('!')
print('\n')
func b() -> i32:
return 300
func test() -> i32: func test() -> i32:
if false: global mut x = 0
func b() -> i32: x += 1
return 200 return x
return b()
else:
return b()
struct vec2: print('0' + (test() as u8))
x : f32 print('0' + (test() as u8))
y : f32 print('0' + (test() as u8))
print('0' + (test() as u8))
struct vecX: print('0' + (test() as u8))
a: f32
b: f32
c: f64
func test2():
//let v = vec2(1.0, 2.0)
vec2(1.0, 2.0)
//test2()
mut v = vecX(6.0, 9.0, 4.0l)
v = vecX(1.0, 2.0, 5.0l)
v.a = 1.0
//v.b = 2.0
v.c = 5.0l
print('0' + (v.b as u8))
print('\n') print('\n')
//func test1():
// let str = ['H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n']
// mut i = 0
// while i < 14:
// print(str[i])

View file

@ -18,6 +18,7 @@ namespace pslang::ast
std::string name; std::string name;
ast::location location; ast::location location;
variable_declaration * constant_node = nullptr; variable_declaration * constant_node = nullptr;
variable_declaration * global_node = nullptr;
variable_base * variable_node = nullptr; variable_base * variable_node = nullptr;
function_definition * function_node = nullptr; function_definition * function_node = nullptr;
foreign_function_declaration * foreign_function_node = nullptr; foreign_function_declaration * foreign_function_node = nullptr;

View file

@ -21,6 +21,7 @@ namespace pslang::ast
struct variable_declaration struct variable_declaration
: variable_base : variable_base
{ {
bool global;
expression_ptr initializer; expression_ptr initializer;
}; };

View file

@ -22,6 +22,7 @@ namespace pslang::ast
std::unordered_map<std::string, foreign_function_declaration *> foreign_functions; std::unordered_map<std::string, foreign_function_declaration *> foreign_functions;
std::unordered_map<std::string, struct_definition *> structs; std::unordered_map<std::string, struct_definition *> structs;
std::unordered_map<std::string, variable_base *> variables; std::unordered_map<std::string, variable_base *> variables;
std::unordered_map<std::string, variable_declaration *> globals;
std::unordered_map<std::string, variable_declaration *> constants; std::unordered_map<std::string, variable_declaration *> constants;
bool contains_transitive(std::string const & name) const bool contains_transitive(std::string const & name) const
@ -30,6 +31,7 @@ namespace pslang::ast
|| (functions.count(name) > 0) || (functions.count(name) > 0)
|| (foreign_functions.count(name) > 0) || (foreign_functions.count(name) > 0)
|| (structs.count(name) > 0) || (structs.count(name) > 0)
|| (globals.count(name) > 0)
|| (constants.count(name) > 0) || (constants.count(name) > 0)
; ;
} }
@ -48,6 +50,7 @@ namespace pslang::ast
|| (foreign_functions.count(name) > 0) || (foreign_functions.count(name) > 0)
|| (structs.count(name) > 0) || (structs.count(name) > 0)
|| (variables.count(name) > 0) || (variables.count(name) > 0)
|| (globals.count(name) > 0)
|| (constants.count(name) > 0) || (constants.count(name) > 0)
; ;
} }
@ -195,6 +198,12 @@ namespace pslang::ast
return; return;
} }
if (auto jt = it->globals.find(identifier.name); jt != it->globals.end())
{
identifier.global_node = jt->second;
return;
}
if (!crossed_function_scope) if (!crossed_function_scope)
{ {
if (auto jt = it->variables.find(identifier.name); jt != it->variables.end()) if (auto jt = it->variables.find(identifier.name); jt != it->variables.end())
@ -326,6 +335,8 @@ namespace pslang::ast
if (variable_declaration.category == value_category::compile_time) if (variable_declaration.category == value_category::compile_time)
scopes.back().constants[variable_declaration.name] = &variable_declaration; scopes.back().constants[variable_declaration.name] = &variable_declaration;
else if (variable_declaration.global)
scopes.back().globals[variable_declaration.name] = &variable_declaration;
else else
scopes.back().variables[variable_declaration.name] = &variable_declaration; scopes.back().variables[variable_declaration.name] = &variable_declaration;
} }

View file

@ -256,6 +256,10 @@ namespace pslang::ast
{ {
node.inferred_type = node.constant_node->inferred_type; node.inferred_type = node.constant_node->inferred_type;
} }
else if (node.global_node)
{
node.inferred_type = node.global_node->inferred_type;
}
else if (node.variable_node) else if (node.variable_node)
{ {
node.inferred_type = node.variable_node->inferred_type; node.inferred_type = node.variable_node->inferred_type;
@ -1010,6 +1014,8 @@ namespace pslang::ast
{ {
if (identifier->variable_node) if (identifier->variable_node)
return identifier->variable_node->category; return identifier->variable_node->category;
else if (identifier->global_node)
return identifier->global_node->category;
else if (identifier->function_node || identifier->foreign_function_node) else if (identifier->function_node || identifier->foreign_function_node)
return ast::value_category::constant; return ast::value_category::constant;
else else

View file

@ -24,6 +24,11 @@ namespace pslang::ir
struct alloc struct alloc
{}; {};
struct global
{
std::vector<std::uint8_t> initializer;
};
struct copy struct copy
{ {
node_ref source; node_ref source;
@ -126,6 +131,7 @@ namespace pslang::ir
label, label,
literal, literal,
alloc, alloc,
global,
copy, copy,
load, load,
store, store,

View file

@ -16,6 +16,7 @@ namespace pslang::ir
{ {
std::unordered_map<ast::function_definition const *, std::pair<node_ref, node_ref>> functions; std::unordered_map<ast::function_definition const *, std::pair<node_ref, node_ref>> functions;
std::unordered_map<ast::variable_base const *, node_ref> variables; std::unordered_map<ast::variable_base const *, node_ref> variables;
std::unordered_map<ast::variable_declaration const *, node_ref> globals;
struct scope struct scope
{ {
@ -104,6 +105,10 @@ namespace pslang::ir
// Temporary hack: replace reading constants with their ininitialization code // Temporary hack: replace reading constants with their ininitialization code
return apply(*node.constant_node->initializer); return apply(*node.constant_node->initializer);
} }
else if (node.global_node)
{
return lcontext.globals.at(node.global_node);
}
else if (node.variable_node) else if (node.variable_node)
{ {
return lcontext.variables.at(node.variable_node); return lcontext.variables.at(node.variable_node);
@ -678,6 +683,24 @@ namespace pslang::ir
node_ref apply(ast::variable_declaration const & node) node_ref apply(ast::variable_declaration const & node)
{ {
if (node.global)
{
mcontext.nodes->emplace_back(global{.initializer = {0}}, std::make_unique<types::type>(types::primitive_type{types::bool_type{}}));
auto guard = last();
mcontext.nodes->emplace_back(global{.initializer = {}}, node.inferred_type);
auto result = last();
mcontext.nodes->emplace_back(jump_if_nonzero{.condition = guard, .target = {}});
auto jump = last();
mcontext.nodes->emplace_back(literal{ast::bool_literal{true}}, std::make_unique<types::type>(types::primitive_type{types::bool_type{}}));
mcontext.nodes->emplace_back(assignment{guard, last()});
auto value = apply(*node.initializer);
mcontext.nodes->emplace_back(assignment{result, value});
mcontext.nodes->emplace_back(label{});
std::get<jump_if_nonzero>(jump->instruction).target = last();
lcontext.globals[&node] = result;
return result;
}
auto before = last(); auto before = last();
auto result = apply(*node.initializer); auto result = apply(*node.initializer);
if (before == last()) if (before == last())

View file

@ -19,6 +19,11 @@ namespace pslang::ir
return false; return false;
} }
bool operator()(global const &)
{
return false;
}
bool operator()(store const &) bool operator()(store const &)
{ {
return false; return false;

View file

@ -210,6 +210,18 @@ namespace pslang::ir
out << "alloc"; out << "alloc";
} }
void operator()(global const & instruction)
{
out << "global" << std::hex;
if (!instruction.initializer.empty()) {
out << ' ';
out << std::setw(2) << std::setfill('0');
for (auto value : instruction.initializer)
out << static_cast<std::uint32_t>(value);
}
out << std::dec;
}
void operator()(copy const & instruction) void operator()(copy const & instruction)
{ {
out << "copy $" << get_index(instruction.source); out << "copy $" << get_index(instruction.source);

View file

@ -1,43 +0,0 @@
#pragma once
#include <cstdint>
#include <memory>
namespace pslang::jit
{
struct blob
{
std::shared_ptr<std::uint8_t> data;
std::size_t size;
blob()
: data{nullptr}
, size{0}
{}
blob(std::shared_ptr<std::uint8_t> data, std::size_t size)
: data{std::move(data)}
, size{size}
{}
blob(blob && other)
: data{std::move(other.data)}
, size{other.size}
{
other.size = 0;
}
blob & operator = (blob && other)
{
if (this == &other)
return *this;
data = std::move(other.data);
size = other.size;
other.size = 0;
return *this;
}
};
}

View file

@ -1,13 +1,13 @@
#pragma once #pragma once
#include <pslang/jit/blob.hpp> #include <pslang/jit/storage.hpp>
#include <vector> #include <memory>
#include <cstdint> #include <cstdint>
namespace pslang::jit namespace pslang::jit
{ {
blob make_host_executable(std::vector<std::uint8_t> const & code); std::shared_ptr<std::uint8_t> make_host_executable(binary_storage const & storage);
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <pslang/jit/abi.hpp> #include <pslang/jit/abi.hpp>
#include <pslang/jit/storage.hpp>
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
@ -28,7 +29,9 @@ namespace pslang::jit
}; };
jit::abi abi; jit::abi abi;
std::vector<std::uint8_t> code = {};
binary_storage storage = {};
std::unordered_map<ast::function_definition const *, std::int32_t> symbols = {}; std::unordered_map<ast::function_definition const *, std::int32_t> symbols = {};
std::int32_t entry_point = 0; std::int32_t entry_point = 0;
std::vector<foreign_resolve_info> foreign_resolve = {}; std::vector<foreign_resolve_info> foreign_resolve = {};

View file

@ -0,0 +1,28 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
namespace pslang::jit
{
struct binary_storage
{
struct range
{
std::size_t begin;
std::size_t end;
};
std::vector<std::uint8_t> storage;
std::vector<range> data;
std::vector<range> code;
std::size_t size() const { return storage.size(); }
std::size_t align();
};
std::size_t native_page_size();
}

View file

@ -115,6 +115,30 @@ namespace pslang::jit::aarch64
return std::nullopt; return std::nullopt;
} }
struct populate_globals_visitor
{
std::vector<std::uint8_t> & storage;
local_context & lcontext;
template <typename Node>
void apply(ir::node_ref, Node const &, types::type_ptr const &)
{}
void apply(ir::node_ref it, ir::global const & node, types::type_ptr const & type)
{
auto size = ast::type_size(*type);
if (node.initializer.size() > size)
throw std::runtime_error("global IR node with initializer larger than type size");
auto alignment = ast::type_alignment(*type);
auto offset = storage.size();
offset = ((offset + (alignment - 1)) / alignment) * alignment;
storage.resize(offset + size);
std::copy(node.initializer.begin(), node.initializer.end(), storage.begin() + offset);
lcontext.nodes[it] = offset;
}
};
struct populate_const_data_visitor struct populate_const_data_visitor
{ {
program_context & pcontext; program_context & pcontext;
@ -128,24 +152,24 @@ namespace pslang::jit::aarch64
{ {
if (auto f16_literal = std::get_if<ast::f16_literal>(&node.value)) if (auto f16_literal = std::get_if<ast::f16_literal>(&node.value))
{ {
lcontext.f16_constants[f16_literal->value.repr] = pcontext.code.size(); lcontext.f16_constants[f16_literal->value.repr] = pcontext.storage.size();
push_bytes(f16_literal->value.repr); push_bytes(f16_literal->value.repr);
} }
else if (auto f32_literal = std::get_if<ast::f32_literal>(&node.value)) else if (auto f32_literal = std::get_if<ast::f32_literal>(&node.value))
{ {
lcontext.f32_constants[f32_literal->value] = pcontext.code.size(); lcontext.f32_constants[f32_literal->value] = pcontext.storage.size();
push_bytes(f32_literal->value); push_bytes(f32_literal->value);
} }
else if (auto f64_literal = std::get_if<ast::f64_literal>(&node.value)) else if (auto f64_literal = std::get_if<ast::f64_literal>(&node.value))
{ {
lcontext.f32_constants[f64_literal->value] = pcontext.code.size(); lcontext.f32_constants[f64_literal->value] = pcontext.storage.size();
push_bytes(f64_literal->value); push_bytes(f64_literal->value);
} }
} }
void apply(ir::extern_symbol const & node, types::type_ptr const &) void apply(ir::extern_symbol const & node, types::type_ptr const &)
{ {
std::int32_t offset = pcontext.code.size(); std::int32_t offset = pcontext.storage.size();
lcontext.extern_symbols[node.name] = offset; lcontext.extern_symbols[node.name] = offset;
pcontext.foreign_resolve.push_back({node.name, offset}); pcontext.foreign_resolve.push_back({node.name, offset});
push_bytes<void *>(nullptr); push_bytes<void *>(nullptr);
@ -157,7 +181,7 @@ namespace pslang::jit::aarch64
{ {
auto begin = (std::uint8_t const *)(&value); auto begin = (std::uint8_t const *)(&value);
auto end = begin + sizeof(value); auto end = begin + sizeof(value);
pcontext.code.insert(pcontext.code.end(), begin, end); pcontext.storage.storage.insert(pcontext.storage.storage.end(), begin, end);
} }
}; };
@ -212,7 +236,7 @@ namespace pslang::jit::aarch64
void operator()(ast::f16_literal const & node) void operator()(ast::f16_literal const & node)
{ {
auto offset = lcontext.f16_constants.at(node.value.repr); auto offset = lcontext.f16_constants.at(node.value.repr);
std::int32_t current = pcontext.code.size(); std::int32_t current = pcontext.storage.size();
builder.ldr_fp_pc(0, 0, (offset - current) / 4); builder.ldr_fp_pc(0, 0, (offset - current) / 4);
builder.fcvt(0, 0b10, 0, 0b01); builder.fcvt(0, 0b10, 0, 0b01);
} }
@ -220,14 +244,14 @@ namespace pslang::jit::aarch64
void operator()(ast::f32_literal const & node) void operator()(ast::f32_literal const & node)
{ {
auto offset = lcontext.f32_constants.at(node.value); auto offset = lcontext.f32_constants.at(node.value);
std::int32_t current = pcontext.code.size(); std::int32_t current = pcontext.storage.size();
builder.ldr_fp_pc(0, 0, (offset - current) / 4); builder.ldr_fp_pc(0, 0, (offset - current) / 4);
} }
void operator()(ast::f64_literal const & node) void operator()(ast::f64_literal const & node)
{ {
auto offset = lcontext.f64_constants.at(node.value); auto offset = lcontext.f64_constants.at(node.value);
std::int32_t current = pcontext.code.size(); std::int32_t current = pcontext.storage.size();
builder.ldr_fp_pc(0, 1, (offset - current) / 4); builder.ldr_fp_pc(0, 1, (offset - current) / 4);
} }
}; };
@ -316,22 +340,28 @@ namespace pslang::jit::aarch64
// but we already allocated stack space for it // but we already allocated stack space for it
} }
void apply(ir::node_ref it, ir::global const & node, types::type_ptr const &)
{
// TODO
throw std::runtime_error("Not implemented");
}
void apply(ir::node_ref it, ir::copy const & node, types::type_ptr const & type) void apply(ir::node_ref it, ir::copy const & node, types::type_ptr const & type)
{ {
// TODO: array/array element copy? // TODO: array/array element copy?
auto size = ast::type_size(*type); auto size = ast::type_size(*type);
auto dst_offset = stack_size - stack_position.at(it); auto dst_address = node_address(it, 0);
auto src_type = node.source->inferred_type; auto src_type = node.source->inferred_type;
auto src_offset = stack_size - stack_position.at(node.source); auto src_address = node_address(node.source, 1);
for (auto field_id : node.path) for (auto field_id : node.path)
{ {
auto const & field = std::get<types::struct_type>(*src_type).node->fields[field_id]; auto const & field = std::get<types::struct_type>(*src_type).node->fields[field_id];
src_type = field.inferred_type; src_type = field.inferred_type;
src_offset += field.layout.offset; src_address.offset += field.layout.offset;
} }
copy_memory(31, src_offset, 31, dst_offset, size, 0); copy_memory(src_address.reg, src_address.offset, dst_address.reg, dst_address.offset, size, 2);
} }
void apply(ir::node_ref it, ir::load const & node, types::type_ptr const & type) void apply(ir::node_ref it, ir::load const & node, types::type_ptr const & type)
@ -339,8 +369,8 @@ namespace pslang::jit::aarch64
// TODO: array/array element load? // TODO: array/array element load?
load(node.ptr, 0); load(node.ptr, 0);
auto size = ast::type_size(*type); auto size = ast::type_size(*type);
auto dst_offset = stack_size - stack_position.at(it); auto dst_address = node_address(it, 1);
copy_memory(0, 0, 31, dst_offset, size, 1); copy_memory(0, 0, dst_address.reg, dst_address.offset, size, 2);
} }
void apply(ir::node_ref, ir::store const & node, types::type_ptr const & type) void apply(ir::node_ref, ir::store const & node, types::type_ptr const & type)
@ -348,8 +378,8 @@ namespace pslang::jit::aarch64
// TODO: array/array element store? // TODO: array/array element store?
load(node.ptr, 0); load(node.ptr, 0);
auto size = ast::type_size(*type); auto size = ast::type_size(*type);
std::int32_t src_offset = stack_size - stack_position.at(node.value); auto src_address = node_address(node.value, 1);
copy_memory(31, src_offset, 0, 0, size, 1); copy_memory(src_address.reg, src_address.offset, 0, 0, size, 2);
} }
void apply(ir::node_ref it, ir::unary_operation const & node, types::type_ptr const & type) void apply(ir::node_ref it, ir::unary_operation const & node, types::type_ptr const & type)
@ -378,8 +408,11 @@ namespace pslang::jit::aarch64
break; break;
case ast::unary_operation_type::address_of: case ast::unary_operation_type::address_of:
case ast::unary_operation_type::mutable_address_of: case ast::unary_operation_type::mutable_address_of:
builder.add_imm(31, 0, stack_size - stack_position.at(node.arg1)); {
store(it, 0); auto arg1_address = node_address(node.arg1, 1);
builder.add_imm(arg1_address.reg, 0, arg1_address.offset);
store(it, 0);
}
break; break;
case ast::unary_operation_type::dereference: case ast::unary_operation_type::dereference:
throw std::runtime_error("Dereference operator mush not be present in compiled IR"); throw std::runtime_error("Dereference operator mush not be present in compiled IR");
@ -617,8 +650,8 @@ namespace pslang::jit::aarch64
{ {
if (types::equal(*array_type->element_type, *pointer_type->referenced_type)) if (types::equal(*array_type->element_type, *pointer_type->referenced_type))
{ {
std::int32_t offset = stack_size - stack_position.at(node.arg1); auto arg1_address = node_address(node.arg1, 1);
builder.add_imm(31, 0, offset); builder.add_imm(arg1_address.reg, 0, arg1_address.offset);
store(it, 0); store(it, 0);
return; return;
} }
@ -698,47 +731,47 @@ namespace pslang::jit::aarch64
void apply(ir::node_ref it, ir::instruction_address const & node, types::type_ptr const &) void apply(ir::node_ref it, ir::instruction_address const & node, types::type_ptr const &)
{ {
lcontext.adr_resolve.emplace_back(pcontext.code.size(), node.target); lcontext.adr_resolve.emplace_back(pcontext.storage.size(), node.target);
builder.adr(0, 0); builder.adr(0, 0);
store(it, 0); store(it, 0);
} }
void apply(ir::node_ref it, ir::extern_symbol const & node, types::type_ptr const &) void apply(ir::node_ref it, ir::extern_symbol const & node, types::type_ptr const &)
{ {
builder.ldr_pc(0, (lcontext.extern_symbols[node.name] - (std::int32_t)pcontext.code.size()) / 4); builder.ldr_pc(0, (lcontext.extern_symbols[node.name] - (std::int32_t)pcontext.storage.size()) / 4);
store(it, 0); store(it, 0);
} }
void apply(ir::node_ref, ir::assignment const & node, types::type_ptr const & type) void apply(ir::node_ref, ir::assignment const & node, types::type_ptr const & type)
{ {
// TODO: array/array element assignment? // TODO: array/array element assignment?
std::size_t src_offset = stack_size - stack_position.at(node.rhs); auto src_address = node_address(node.rhs, 0);
auto dst_type = node.lhs->inferred_type; auto dst_type = node.lhs->inferred_type;
std::size_t dst_offset = stack_size - stack_position.at(node.lhs); auto dst_address = node_address(node.lhs, 1);
for (auto field_id : node.path) for (auto field_id : node.path)
{ {
if (auto struct_type = std::get_if<types::struct_type>(dst_type.get())) if (auto struct_type = std::get_if<types::struct_type>(dst_type.get()))
{ {
auto struct_node = struct_type->node; auto struct_node = struct_type->node;
dst_type = struct_node->fields[field_id].inferred_type; dst_type = struct_node->fields[field_id].inferred_type;
dst_offset += struct_node->fields[field_id].layout.offset; dst_address.offset += struct_node->fields[field_id].layout.offset;
} }
else if (auto array_type = std::get_if<types::array_type>(dst_type.get())) else if (auto array_type = std::get_if<types::array_type>(dst_type.get()))
{ {
dst_type = array_type->element_type; dst_type = array_type->element_type;
dst_offset += field_id * ast::type_size(*array_type->element_type); dst_address.offset += field_id * ast::type_size(*array_type->element_type);
} }
else else
throw std::runtime_error("Unknown object type for field assignment"); throw std::runtime_error("Unknown object type for field assignment");
} }
copy_memory(31, src_offset, 31, dst_offset, ast::type_size(*dst_type), 0); copy_memory(src_address.reg, src_address.offset, dst_address.reg, dst_address.offset, ast::type_size(*dst_type), 0);
} }
void apply(ir::node_ref, ir::jump const & node, types::type_ptr const &) void apply(ir::node_ref, ir::jump const & node, types::type_ptr const &)
{ {
lcontext.branch_resolve.emplace_back(pcontext.code.size(), node.target); lcontext.branch_resolve.emplace_back(pcontext.storage.size(), node.target);
builder.b(0); builder.b(0);
} }
@ -746,7 +779,7 @@ namespace pslang::jit::aarch64
{ {
load(node.condition, 0); load(node.condition, 0);
extend(0, node.condition->inferred_type); extend(0, node.condition->inferred_type);
lcontext.cbranch_resolve.emplace_back(pcontext.code.size(), node.target); lcontext.cbranch_resolve.emplace_back(pcontext.storage.size(), node.target);
builder.cbz(0, 0); builder.cbz(0, 0);
} }
@ -754,7 +787,7 @@ namespace pslang::jit::aarch64
{ {
load(node.condition, 0); load(node.condition, 0);
extend(0, node.condition->inferred_type); extend(0, node.condition->inferred_type);
lcontext.cbranch_resolve.emplace_back(pcontext.code.size(), node.target); lcontext.cbranch_resolve.emplace_back(pcontext.storage.size(), node.target);
builder.cbnz(0, 0); builder.cbnz(0, 0);
} }
@ -777,28 +810,28 @@ namespace pslang::jit::aarch64
if (auto hfa = get_hfa_data(lcontext, argument->inferred_type); hfa && hfa->count <= 4) if (auto hfa = get_hfa_data(lcontext, argument->inferred_type); hfa && hfa->count <= 4)
{ {
// HFA - passed in consecutive FP registers // HFA - passed in consecutive FP registers
std::int32_t base_offset = stack_size - stack_position.at(argument); auto base_address = node_address(argument, 29);
auto fp_mode = fp_mode_for(*hfa->element_type); auto fp_mode = fp_mode_for(*hfa->element_type);
auto size = fp_size(fp_mode); auto size = fp_size(fp_mode);
for (std::size_t i = 0; i < hfa->count; ++i) for (std::size_t i = 0; i < hfa->count; ++i)
builder.ldr_fp(fp_reg++, fp_mode, 31, (base_offset + i * size) / size); builder.ldr_fp(fp_reg++, fp_mode, base_address.reg, (base_address.offset + i * size) / size);
} }
else if (size <= 16) else if (size <= 16)
{ {
// Small struct - passed in up to 2 GP registers // Small struct - passed in up to 2 GP registers
std::int32_t base_offset = stack_size - stack_position.at(argument); auto base_address = node_address(argument, 29);
std::int32_t offset = 0; std::int32_t offset = 0;
while (offset < size) while (offset < size)
{ {
builder.ldr(reg++, 31, (base_offset + offset) / 8); builder.ldr(reg++, base_address.reg, (base_address.offset + offset) / 8);
offset += 8; offset += 8;
} }
} }
else else
{ {
// Large struct - passed by pointer // Large struct - passed by pointer
std::int32_t base_offset = stack_size - stack_position.at(argument); auto base_address = node_address(argument, 29);
builder.add_imm(31, reg++, base_offset); builder.add_imm(base_address.reg, reg++, base_address.offset);
} }
} }
else if (types::is_integer_like_type(*argument->inferred_type)) else if (types::is_integer_like_type(*argument->inferred_type))
@ -885,7 +918,7 @@ namespace pslang::jit::aarch64
void apply(ir::node_ref it, ir::call const & node, types::type_ptr const & type) void apply(ir::node_ref it, ir::call const & node, types::type_ptr const & type)
{ {
apply_call(it, node, type, [&]{ apply_call(it, node, type, [&]{
lcontext.branch_resolve.emplace_back(pcontext.code.size(), node.target); lcontext.branch_resolve.emplace_back(pcontext.storage.size(), node.target);
builder.bl(0); builder.bl(0);
}); });
} }
@ -911,26 +944,26 @@ namespace pslang::jit::aarch64
{} {}
else if (struct_type || array_type) else if (struct_type || array_type)
{ {
auto base_offset = stack_size - stack_position.at(*node.value); auto base_address = node_address(*node.value, 29);
if (auto hfa = get_hfa_data(lcontext, type); hfa && hfa->count <= 4) if (auto hfa = get_hfa_data(lcontext, type); hfa && hfa->count <= 4)
{ {
auto fp_mode = fp_mode_for(*hfa->element_type); auto fp_mode = fp_mode_for(*hfa->element_type);
auto size = fp_size(fp_mode); auto size = fp_size(fp_mode);
// HFA - returned in consecutive FP registers // HFA - returned in consecutive FP registers
for (std::size_t i = 0; i < hfa->count; ++i) for (std::size_t i = 0; i < hfa->count; ++i)
builder.ldr_fp(i, fp_mode, 31, (base_offset + i * size) / size); builder.ldr_fp(i, fp_mode, base_address.reg, (base_address.offset + i * size) / size);
} }
else if (size <= 16) else if (size <= 16)
{ {
// Small struct - returned in x0-x1 registers // Small struct - returned in x0-x1 registers
builder.ldr(0, 31, base_offset / 8); builder.ldr(0, base_address.reg, base_address.offset / 8);
if (size > 8) if (size > 8)
builder.ldr(1, 31, (base_offset + 8) / 8); builder.ldr(1, base_address.reg, (base_address.offset + 8) / 8);
} }
else else
{ {
// Large struct - returned by pointer in x8 register // Large struct - returned by pointer in x8 register
copy_memory(31, base_offset, 8, 0, size, 0); copy_memory(base_address.reg, base_address.offset, 8, 0, size, 0);
} }
} }
else if (types::is_integer_like_type(*type)) else if (types::is_integer_like_type(*type))
@ -999,7 +1032,7 @@ namespace pslang::jit::aarch64
auto it = begin; auto it = begin;
lcontext.nodes[it] = pcontext.code.size(); lcontext.nodes[it] = pcontext.storage.size();
if (stack_size > 0) if (stack_size > 0)
builder.sub_imm(31, 31, stack_size); builder.sub_imm(31, 31, stack_size);
if (lcontext.use_frame_pointer) if (lcontext.use_frame_pointer)
@ -1062,36 +1095,66 @@ namespace pslang::jit::aarch64
for (; it != end; ++it) for (; it != end; ++it)
{ {
// Globals are added in a separate pass before code
if (lcontext.nodes.contains(it))
continue;
// Uncomment to debug per-node instruction generation: // Uncomment to debug per-node instruction generation:
// builder.nop(); builder.nop();
lcontext.nodes[it] = pcontext.code.size(); lcontext.nodes[it] = pcontext.storage.size();
std::visit([&](auto const & instruction){ apply(it, instruction, it->inferred_type); }, it->instruction); std::visit([&](auto const & instruction){ apply(it, instruction, it->inferred_type); }, it->instruction);
} }
} }
private: private:
struct value_address
{
std::uint8_t reg;
std::int32_t offset;
};
value_address node_address(ir::node_ref it, std::uint8_t extra_reg)
{
if (std::holds_alternative<ir::global>(it->instruction))
{
builder.adr(extra_reg, lcontext.nodes.at(it) - static_cast<std::int32_t>(builder.code.size()));
return {extra_reg, 0};
}
else
return {31, stack_size - stack_position.at(it)};
}
void load(ir::node_ref it, std::uint8_t reg) void load(ir::node_ref it, std::uint8_t reg)
{ {
std::int32_t offset = stack_size - stack_position.at(it); auto address = node_address(it, reg);
builder.ldr(reg, 31, offset / 8); if ((address.offset % 8) != 0)
throw std::runtime_error("Node address offset for ldr must be a multiple of 8");
builder.ldr(reg, address.reg, address.offset / 8);
} }
void load_fp(ir::node_ref it, std::uint8_t reg, std::uint8_t mode) void load_fp(ir::node_ref it, std::uint8_t reg, std::uint8_t mode)
{ {
std::int32_t offset = stack_size - stack_position.at(it); auto address = node_address(it, reg);
builder.ldr_fp(reg, mode, 31, offset / fp_size(mode)); auto size = fp_size(mode);
if ((address.offset % size) != 0)
throw std::runtime_error("Node address offset for ldr_fp must be a multiple of size");
builder.ldr_fp(reg, mode, address.reg, address.offset / size);
} }
void store(ir::node_ref it, std::uint8_t reg) void store(ir::node_ref it, std::uint8_t reg)
{ {
std::int32_t offset = stack_size - stack_position.at(it); auto address = node_address(it, reg);
builder.str(reg, 31, offset / 8); if ((address.offset % 8) != 0)
throw std::runtime_error("Node address offset for str must be a multiple of 8");
builder.str(reg, address.reg, address.offset / 8);
} }
void store_fp(ir::node_ref it, std::uint8_t reg, std::uint8_t mode) void store_fp(ir::node_ref it, std::uint8_t reg, std::uint8_t mode)
{ {
std::int32_t offset = stack_size - stack_position.at(it); auto address = node_address(it, reg);
builder.str_fp(reg, mode, 31, offset / fp_size(mode)); auto size = fp_size(mode);
if ((address.offset % size) != 0)
throw std::runtime_error("Node address offset for str_fp must be a multiple of size");
builder.str_fp(reg, mode, address.reg, address.offset / fp_size(mode));
} }
// Sign- or zero-extend the register depending on the exact type // Sign- or zero-extend the register depending on the exact type
@ -1149,7 +1212,17 @@ namespace pslang::jit::aarch64
{ {
local_context lcontext; local_context lcontext;
instruction_builder builder{pcontext.code}; auto data_begin = pcontext.storage.align();
{
populate_globals_visitor visitor{.storage = pcontext.storage.storage, .lcontext = lcontext};
for (auto it = mcontext.nodes->begin(); it != mcontext.nodes->end(); ++it)
std::visit([&](auto const & instruction){ visitor.apply(it, instruction, it->inferred_type); }, it->instruction);
}
auto data_end = pcontext.storage.align();
pcontext.storage.data.push_back({data_begin, data_end});
auto code_begin = data_end;
instruction_builder builder{pcontext.storage.storage};
{ {
populate_const_data_visitor visitor{pcontext, lcontext}; populate_const_data_visitor visitor{pcontext, lcontext};
@ -1159,7 +1232,7 @@ namespace pslang::jit::aarch64
for (auto const & symbol : mcontext.symbols) for (auto const & symbol : mcontext.symbols)
{ {
pcontext.symbols[symbol.first] = pcontext.code.size(); pcontext.symbols[symbol.first] = pcontext.storage.size();
compile_visitor visitor{pcontext, mcontext, lcontext, builder}; compile_visitor visitor{pcontext, mcontext, lcontext, builder};
visitor.compile(symbol.first, symbol.second.begin, symbol.second.end); visitor.compile(symbol.first, symbol.second.begin, symbol.second.end);
} }
@ -1167,13 +1240,16 @@ namespace pslang::jit::aarch64
pcontext.entry_point = lcontext.nodes.at(mcontext.entry_point); pcontext.entry_point = lcontext.nodes.at(mcontext.entry_point);
for (auto const & resolve : lcontext.branch_resolve) for (auto const & resolve : lcontext.branch_resolve)
builder.b_inject(pcontext.code.data() + resolve.offset, (lcontext.nodes.at(resolve.target) - resolve.offset) / 4); builder.b_inject(pcontext.storage.storage.data() + resolve.offset, (lcontext.nodes.at(resolve.target) - resolve.offset) / 4);
for (auto const & resolve : lcontext.cbranch_resolve) for (auto const & resolve : lcontext.cbranch_resolve)
builder.cb_inject(pcontext.code.data() + resolve.offset, (lcontext.nodes.at(resolve.target) - resolve.offset) / 4); builder.cb_inject(pcontext.storage.storage.data() + resolve.offset, (lcontext.nodes.at(resolve.target) - resolve.offset) / 4);
for (auto const & resolve : lcontext.adr_resolve) for (auto const & resolve : lcontext.adr_resolve)
builder.adr_inject(pcontext.code.data() + resolve.offset, lcontext.nodes.at(resolve.target) - resolve.offset); builder.adr_inject(pcontext.storage.storage.data() + resolve.offset, lcontext.nodes.at(resolve.target) - resolve.offset);
auto code_end = pcontext.storage.align();
pcontext.storage.code.push_back({code_begin, code_end});
} }
} }

View file

@ -14,17 +14,41 @@
namespace pslang::jit namespace pslang::jit
{ {
blob make_host_executable(std::vector<std::uint8_t> const & code) std::shared_ptr<std::uint8_t> make_host_executable(binary_storage const & storage)
{ {
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__) || defined(__APPLE__)
auto size = code.size(); auto const page_size = native_page_size();
auto ptr = (std::uint8_t *)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
for (auto const & range : storage.data)
{
if ((range.begin % page_size) != 0)
throw std::runtime_error("Data range start is not aligned to page boundary");
if ((range.end % page_size) != 0)
throw std::runtime_error("Data range end is not aligned to page boundary");
}
for (auto const & range : storage.code)
{
if ((range.begin % page_size) != 0)
throw std::runtime_error("Code range start is not aligned to page boundary");
if ((range.end % page_size) != 0)
throw std::runtime_error("Code range end is not aligned to page boundary");
}
auto total_size = storage.storage.size();
auto ptr = (std::uint8_t *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (ptr == MAP_FAILED) if (ptr == MAP_FAILED)
throw std::system_error(errno, std::generic_category()); throw std::system_error(errno, std::generic_category());
std::copy(code.begin(), code.end(), ptr);
if (mprotect(ptr, size, PROT_READ | PROT_EXEC) != 0) std::copy(storage.storage.begin(), storage.storage.end(), ptr);
throw std::system_error(errno, std::generic_category());
return blob(std::shared_ptr<std::uint8_t>(ptr, [size](void * ptr){ munmap(ptr, size); }), size); for (auto const & range : storage.code)
if (mprotect(ptr + range.begin, range.end - range.begin, PROT_READ | PROT_EXEC) != 0)
throw std::system_error(errno, std::generic_category());
return std::shared_ptr<std::uint8_t>(ptr, [total_size](void * ptr){ munmap(ptr, total_size); });
#else #else
throw std::runtime_error("Host-executable modules are not supported for this platform"); throw std::runtime_error("Host-executable modules are not supported for this platform");
#endif #endif

View file

@ -0,0 +1,32 @@
#include <pslang/jit/storage.hpp>
#ifdef __linux__
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <unistd.h>
#endif
namespace pslang::jit
{
std::size_t binary_storage::align()
{
auto const page_size = native_page_size();
auto aligned_size = ((storage.size() + (page_size - 1)) / page_size) * page_size;
storage.resize(aligned_size);
return storage.size();
}
std::size_t native_page_size()
{
#if defined(__linux__) || defined(__APPLE__)
return getpagesize();
#else
throw std::runtime_error("Native page size is not supported for this platform");
#endif
}
}

View file

@ -26,6 +26,7 @@ using bp = ::pslang::parser::bison::parser;
const { return bp::make_const(ctx.location); } const { return bp::make_const(ctx.location); }
let { return bp::make_let(ctx.location); } let { return bp::make_let(ctx.location); }
mut { return bp::make_mut(ctx.location); } mut { return bp::make_mut(ctx.location); }
global { return bp::make_global(ctx.location); }
if { return bp::make_if(ctx.location); } if { return bp::make_if(ctx.location); }
else { return bp::make_else(ctx.location); } else { return bp::make_else(ctx.location); }
then { return bp::make_then(ctx.location); } then { return bp::make_then(ctx.location); }

View file

@ -137,6 +137,7 @@ template <typename T>
%token const %token const
%token let %token let
%token mut %token mut
%token global
%token if %token if
%token else %token else
%token then %token then
@ -192,6 +193,7 @@ template <typename T>
%type <ast::function_declaration::argument> function_declaration_single_argument %type <ast::function_declaration::argument> function_declaration_single_argument
%type <ast::type_ptr> function_return_type %type <ast::type_ptr> function_return_type
%type <ast::variable_declaration> variable_declaration %type <ast::variable_declaration> variable_declaration
%type <bool> optional_global
%type <ast::value_category> variable_keyword %type <ast::value_category> variable_keyword
%type <ast::type> type_expression %type <ast::type> type_expression
%type <types::primitive_type> primitive_type %type <types::primitive_type> primitive_type
@ -284,8 +286,13 @@ function_return_type
; ;
variable_declaration variable_declaration
: variable_keyword name assignment expression { $$ = ast::variable_declaration{{$1, $2, nullptr, @$}, std::make_unique<ast::expression>($4)}; } : optional_global variable_keyword name assignment expression { $$ = ast::variable_declaration{{$2, $3, nullptr, @$}, $1, std::make_unique<ast::expression>($5)}; }
| variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{{$1, $2, std::make_unique<ast::type>($4), @$}, std::make_unique<ast::expression>($6)}; } | optional_global variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{{$2, $3, std::make_unique<ast::type>($5), @$}, $1, std::make_unique<ast::expression>($7)}; }
;
optional_global
: global { $$ = true; }
| %empty { $$ = false; }
; ;
variable_keyword variable_keyword