From 9337490f5b43f91cf864984a3d9e45e95303964b Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 8 May 2026 21:38:44 +0300 Subject: [PATCH] Implement global variables --- apps/interpreter/source/main.cpp | 6 +- examples/jit_test.psl | 75 +------ libs/ast/include/pslang/ast/identifier.hpp | 1 + libs/ast/include/pslang/ast/variable.hpp | 1 + libs/ast/source/resolve_identifiers.cpp | 11 + libs/ast/source/type_check.cpp | 6 + libs/ir/include/pslang/ir/node.hpp | 6 + libs/ir/source/compiler.cpp | 23 +++ libs/ir/source/node.cpp | 5 + libs/ir/source/print.cpp | 12 ++ libs/jit/include/pslang/jit/blob.hpp | 43 ---- libs/jit/include/pslang/jit/executable.hpp | 6 +- .../include/pslang/jit/program_context.hpp | 5 +- libs/jit/include/pslang/jit/storage.hpp | 28 +++ libs/jit/source/arch/aarch64/compiler.cpp | 192 ++++++++++++------ libs/jit/source/executable.cpp | 38 +++- libs/jit/source/storage.cpp | 32 +++ libs/parser/rules/pslang.l | 1 + libs/parser/rules/pslang.y | 11 +- 19 files changed, 319 insertions(+), 183 deletions(-) delete mode 100644 libs/jit/include/pslang/jit/blob.hpp create mode 100644 libs/jit/include/pslang/jit/storage.hpp create mode 100644 libs/jit/source/storage.cpp diff --git a/apps/interpreter/source/main.cpp b/apps/interpreter/source/main.cpp index 047a816..c34861a 100644 --- a/apps/interpreter/source/main.cpp +++ b/apps/interpreter/source/main.cpp @@ -260,12 +260,12 @@ int main(int argc, char ** argv) for (auto const & resolve : pcontext.foreign_resolve) { 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(); } } diff --git a/examples/jit_test.psl b/examples/jit_test.psl index fd736e4..ccae7a2 100644 --- a/examples/jit_test.psl +++ b/examples/jit_test.psl @@ -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): + foreign func putchar(c: i32) -> 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: - if false: - func b() -> i32: - return 200 - return b() - else: - return b() + global mut x = 0 + x += 1 + return x -struct vec2: - x : f32 - y : f32 - -struct vecX: - 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('0' + (test() as u8)) +print('0' + (test() as u8)) +print('0' + (test() as u8)) +print('0' + (test() as u8)) +print('0' + (test() as u8)) 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]) diff --git a/libs/ast/include/pslang/ast/identifier.hpp b/libs/ast/include/pslang/ast/identifier.hpp index c8334f6..ed7cc07 100644 --- a/libs/ast/include/pslang/ast/identifier.hpp +++ b/libs/ast/include/pslang/ast/identifier.hpp @@ -18,6 +18,7 @@ namespace pslang::ast std::string name; ast::location location; variable_declaration * constant_node = nullptr; + variable_declaration * global_node = nullptr; variable_base * variable_node = nullptr; function_definition * function_node = nullptr; foreign_function_declaration * foreign_function_node = nullptr; diff --git a/libs/ast/include/pslang/ast/variable.hpp b/libs/ast/include/pslang/ast/variable.hpp index 2d2f699..d2873a9 100644 --- a/libs/ast/include/pslang/ast/variable.hpp +++ b/libs/ast/include/pslang/ast/variable.hpp @@ -21,6 +21,7 @@ namespace pslang::ast struct variable_declaration : variable_base { + bool global; expression_ptr initializer; }; diff --git a/libs/ast/source/resolve_identifiers.cpp b/libs/ast/source/resolve_identifiers.cpp index 50ecb51..e2813ad 100644 --- a/libs/ast/source/resolve_identifiers.cpp +++ b/libs/ast/source/resolve_identifiers.cpp @@ -22,6 +22,7 @@ namespace pslang::ast std::unordered_map foreign_functions; std::unordered_map structs; std::unordered_map variables; + std::unordered_map globals; std::unordered_map constants; bool contains_transitive(std::string const & name) const @@ -30,6 +31,7 @@ namespace pslang::ast || (functions.count(name) > 0) || (foreign_functions.count(name) > 0) || (structs.count(name) > 0) + || (globals.count(name) > 0) || (constants.count(name) > 0) ; } @@ -48,6 +50,7 @@ namespace pslang::ast || (foreign_functions.count(name) > 0) || (structs.count(name) > 0) || (variables.count(name) > 0) + || (globals.count(name) > 0) || (constants.count(name) > 0) ; } @@ -195,6 +198,12 @@ namespace pslang::ast return; } + if (auto jt = it->globals.find(identifier.name); jt != it->globals.end()) + { + identifier.global_node = jt->second; + return; + } + if (!crossed_function_scope) { 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) scopes.back().constants[variable_declaration.name] = &variable_declaration; + else if (variable_declaration.global) + scopes.back().globals[variable_declaration.name] = &variable_declaration; else scopes.back().variables[variable_declaration.name] = &variable_declaration; } diff --git a/libs/ast/source/type_check.cpp b/libs/ast/source/type_check.cpp index 3192a19..6da8d6d 100644 --- a/libs/ast/source/type_check.cpp +++ b/libs/ast/source/type_check.cpp @@ -256,6 +256,10 @@ namespace pslang::ast { 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) { node.inferred_type = node.variable_node->inferred_type; @@ -1010,6 +1014,8 @@ namespace pslang::ast { if (identifier->variable_node) return identifier->variable_node->category; + else if (identifier->global_node) + return identifier->global_node->category; else if (identifier->function_node || identifier->foreign_function_node) return ast::value_category::constant; else diff --git a/libs/ir/include/pslang/ir/node.hpp b/libs/ir/include/pslang/ir/node.hpp index 705fbd5..b85933d 100644 --- a/libs/ir/include/pslang/ir/node.hpp +++ b/libs/ir/include/pslang/ir/node.hpp @@ -24,6 +24,11 @@ namespace pslang::ir struct alloc {}; + struct global + { + std::vector initializer; + }; + struct copy { node_ref source; @@ -126,6 +131,7 @@ namespace pslang::ir label, literal, alloc, + global, copy, load, store, diff --git a/libs/ir/source/compiler.cpp b/libs/ir/source/compiler.cpp index 8cbee69..5fa70a8 100644 --- a/libs/ir/source/compiler.cpp +++ b/libs/ir/source/compiler.cpp @@ -16,6 +16,7 @@ namespace pslang::ir { std::unordered_map> functions; std::unordered_map variables; + std::unordered_map globals; struct scope { @@ -104,6 +105,10 @@ namespace pslang::ir // Temporary hack: replace reading constants with their ininitialization code return apply(*node.constant_node->initializer); } + else if (node.global_node) + { + return lcontext.globals.at(node.global_node); + } else if (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) { + if (node.global) + { + mcontext.nodes->emplace_back(global{.initializer = {0}}, std::make_unique(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::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->instruction).target = last(); + lcontext.globals[&node] = result; + return result; + } + auto before = last(); auto result = apply(*node.initializer); if (before == last()) diff --git a/libs/ir/source/node.cpp b/libs/ir/source/node.cpp index 9ed1b119..320c835 100644 --- a/libs/ir/source/node.cpp +++ b/libs/ir/source/node.cpp @@ -19,6 +19,11 @@ namespace pslang::ir return false; } + bool operator()(global const &) + { + return false; + } + bool operator()(store const &) { return false; diff --git a/libs/ir/source/print.cpp b/libs/ir/source/print.cpp index a75d53b..6d68365 100644 --- a/libs/ir/source/print.cpp +++ b/libs/ir/source/print.cpp @@ -210,6 +210,18 @@ namespace pslang::ir 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(value); + } + out << std::dec; + } + void operator()(copy const & instruction) { out << "copy $" << get_index(instruction.source); diff --git a/libs/jit/include/pslang/jit/blob.hpp b/libs/jit/include/pslang/jit/blob.hpp deleted file mode 100644 index e026e26..0000000 --- a/libs/jit/include/pslang/jit/blob.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include - -namespace pslang::jit -{ - - struct blob - { - std::shared_ptr data; - std::size_t size; - - blob() - : data{nullptr} - , size{0} - {} - - blob(std::shared_ptr 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; - } - }; - -} diff --git a/libs/jit/include/pslang/jit/executable.hpp b/libs/jit/include/pslang/jit/executable.hpp index 7e67d3f..9e0af99 100644 --- a/libs/jit/include/pslang/jit/executable.hpp +++ b/libs/jit/include/pslang/jit/executable.hpp @@ -1,13 +1,13 @@ #pragma once -#include +#include -#include +#include #include namespace pslang::jit { - blob make_host_executable(std::vector const & code); + std::shared_ptr make_host_executable(binary_storage const & storage); } diff --git a/libs/jit/include/pslang/jit/program_context.hpp b/libs/jit/include/pslang/jit/program_context.hpp index 8ea9240..c8f1121 100644 --- a/libs/jit/include/pslang/jit/program_context.hpp +++ b/libs/jit/include/pslang/jit/program_context.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -28,7 +29,9 @@ namespace pslang::jit }; jit::abi abi; - std::vector code = {}; + + binary_storage storage = {}; + std::unordered_map symbols = {}; std::int32_t entry_point = 0; std::vector foreign_resolve = {}; diff --git a/libs/jit/include/pslang/jit/storage.hpp b/libs/jit/include/pslang/jit/storage.hpp new file mode 100644 index 0000000..914be36 --- /dev/null +++ b/libs/jit/include/pslang/jit/storage.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace pslang::jit +{ + + struct binary_storage + { + struct range + { + std::size_t begin; + std::size_t end; + }; + + std::vector storage; + std::vector data; + std::vector code; + + std::size_t size() const { return storage.size(); } + std::size_t align(); + }; + + std::size_t native_page_size(); + +} diff --git a/libs/jit/source/arch/aarch64/compiler.cpp b/libs/jit/source/arch/aarch64/compiler.cpp index 0d61320..e59e200 100644 --- a/libs/jit/source/arch/aarch64/compiler.cpp +++ b/libs/jit/source/arch/aarch64/compiler.cpp @@ -115,6 +115,30 @@ namespace pslang::jit::aarch64 return std::nullopt; } + struct populate_globals_visitor + { + std::vector & storage; + local_context & lcontext; + + template + 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 { program_context & pcontext; @@ -128,24 +152,24 @@ namespace pslang::jit::aarch64 { if (auto f16_literal = std::get_if(&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); } else if (auto f32_literal = std::get_if(&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); } else if (auto f64_literal = std::get_if(&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); } } 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; pcontext.foreign_resolve.push_back({node.name, offset}); push_bytes(nullptr); @@ -157,7 +181,7 @@ namespace pslang::jit::aarch64 { auto begin = (std::uint8_t const *)(&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) { 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.fcvt(0, 0b10, 0, 0b01); } @@ -220,14 +244,14 @@ namespace pslang::jit::aarch64 void operator()(ast::f32_literal const & node) { 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); } void operator()(ast::f64_literal const & node) { 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); } }; @@ -316,22 +340,28 @@ namespace pslang::jit::aarch64 // 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) { // TODO: array/array element copy? 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_offset = stack_size - stack_position.at(node.source); + auto src_address = node_address(node.source, 1); for (auto field_id : node.path) { auto const & field = std::get(*src_type).node->fields[field_id]; 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) @@ -339,8 +369,8 @@ namespace pslang::jit::aarch64 // TODO: array/array element load? load(node.ptr, 0); auto size = ast::type_size(*type); - auto dst_offset = stack_size - stack_position.at(it); - copy_memory(0, 0, 31, dst_offset, size, 1); + auto dst_address = node_address(it, 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) @@ -348,8 +378,8 @@ namespace pslang::jit::aarch64 // TODO: array/array element store? load(node.ptr, 0); auto size = ast::type_size(*type); - std::int32_t src_offset = stack_size - stack_position.at(node.value); - copy_memory(31, src_offset, 0, 0, size, 1); + auto src_address = node_address(node.value, 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) @@ -378,8 +408,11 @@ namespace pslang::jit::aarch64 break; case ast::unary_operation_type::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; case ast::unary_operation_type::dereference: 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)) { - std::int32_t offset = stack_size - stack_position.at(node.arg1); - builder.add_imm(31, 0, offset); + auto arg1_address = node_address(node.arg1, 1); + builder.add_imm(arg1_address.reg, 0, arg1_address.offset); store(it, 0); return; } @@ -698,47 +731,47 @@ namespace pslang::jit::aarch64 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); store(it, 0); } 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); } void apply(ir::node_ref, ir::assignment const & node, types::type_ptr const & type) { // 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; - 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) { if (auto struct_type = std::get_if(dst_type.get())) { auto struct_node = struct_type->node; 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(dst_type.get())) { 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 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 &) { - lcontext.branch_resolve.emplace_back(pcontext.code.size(), node.target); + lcontext.branch_resolve.emplace_back(pcontext.storage.size(), node.target); builder.b(0); } @@ -746,7 +779,7 @@ namespace pslang::jit::aarch64 { load(node.condition, 0); 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); } @@ -754,7 +787,7 @@ namespace pslang::jit::aarch64 { load(node.condition, 0); 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); } @@ -777,28 +810,28 @@ namespace pslang::jit::aarch64 if (auto hfa = get_hfa_data(lcontext, argument->inferred_type); hfa && hfa->count <= 4) { // 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 size = fp_size(fp_mode); 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) { // 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; while (offset < size) { - builder.ldr(reg++, 31, (base_offset + offset) / 8); + builder.ldr(reg++, base_address.reg, (base_address.offset + offset) / 8); offset += 8; } } else { // Large struct - passed by pointer - std::int32_t base_offset = stack_size - stack_position.at(argument); - builder.add_imm(31, reg++, base_offset); + auto base_address = node_address(argument, 29); + builder.add_imm(base_address.reg, reg++, base_address.offset); } } 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) { 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); }); } @@ -911,26 +944,26 @@ namespace pslang::jit::aarch64 {} 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) { auto fp_mode = fp_mode_for(*hfa->element_type); auto size = fp_size(fp_mode); // HFA - returned in consecutive FP registers 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) { // 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) - builder.ldr(1, 31, (base_offset + 8) / 8); + builder.ldr(1, base_address.reg, (base_address.offset + 8) / 8); } else { // 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)) @@ -999,7 +1032,7 @@ namespace pslang::jit::aarch64 auto it = begin; - lcontext.nodes[it] = pcontext.code.size(); + lcontext.nodes[it] = pcontext.storage.size(); if (stack_size > 0) builder.sub_imm(31, 31, stack_size); if (lcontext.use_frame_pointer) @@ -1062,36 +1095,66 @@ namespace pslang::jit::aarch64 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: - // builder.nop(); - lcontext.nodes[it] = pcontext.code.size(); + builder.nop(); + lcontext.nodes[it] = pcontext.storage.size(); std::visit([&](auto const & instruction){ apply(it, instruction, it->inferred_type); }, it->instruction); } } 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(it->instruction)) + { + builder.adr(extra_reg, lcontext.nodes.at(it) - static_cast(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) { - std::int32_t offset = stack_size - stack_position.at(it); - builder.ldr(reg, 31, offset / 8); + auto address = node_address(it, reg); + 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) { - std::int32_t offset = stack_size - stack_position.at(it); - builder.ldr_fp(reg, mode, 31, offset / fp_size(mode)); + auto address = node_address(it, reg); + 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) { - std::int32_t offset = stack_size - stack_position.at(it); - builder.str(reg, 31, offset / 8); + auto address = node_address(it, reg); + 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) { - std::int32_t offset = stack_size - stack_position.at(it); - builder.str_fp(reg, mode, 31, offset / fp_size(mode)); + auto address = node_address(it, reg); + 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 @@ -1149,7 +1212,17 @@ namespace pslang::jit::aarch64 { 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}; @@ -1159,7 +1232,7 @@ namespace pslang::jit::aarch64 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}; 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); 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) - 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) - 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}); } } diff --git a/libs/jit/source/executable.cpp b/libs/jit/source/executable.cpp index 1366058..4614ef7 100644 --- a/libs/jit/source/executable.cpp +++ b/libs/jit/source/executable.cpp @@ -14,17 +14,41 @@ namespace pslang::jit { - blob make_host_executable(std::vector const & code) + std::shared_ptr make_host_executable(binary_storage const & storage) { #if defined(__linux__) || defined(__APPLE__) - auto size = code.size(); - auto ptr = (std::uint8_t *)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + auto const page_size = native_page_size(); + + 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) throw std::system_error(errno, std::generic_category()); - std::copy(code.begin(), code.end(), ptr); - if (mprotect(ptr, size, PROT_READ | PROT_EXEC) != 0) - throw std::system_error(errno, std::generic_category()); - return blob(std::shared_ptr(ptr, [size](void * ptr){ munmap(ptr, size); }), size); + + std::copy(storage.storage.begin(), storage.storage.end(), ptr); + + 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(ptr, [total_size](void * ptr){ munmap(ptr, total_size); }); #else throw std::runtime_error("Host-executable modules are not supported for this platform"); #endif diff --git a/libs/jit/source/storage.cpp b/libs/jit/source/storage.cpp new file mode 100644 index 0000000..f213ee9 --- /dev/null +++ b/libs/jit/source/storage.cpp @@ -0,0 +1,32 @@ +#include + +#ifdef __linux__ +#include +#endif + +#ifdef __APPLE__ +#include +#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 + } + +} + diff --git a/libs/parser/rules/pslang.l b/libs/parser/rules/pslang.l index 1f3108e..75761e4 100644 --- a/libs/parser/rules/pslang.l +++ b/libs/parser/rules/pslang.l @@ -26,6 +26,7 @@ using bp = ::pslang::parser::bison::parser; const { return bp::make_const(ctx.location); } let { return bp::make_let(ctx.location); } mut { return bp::make_mut(ctx.location); } +global { return bp::make_global(ctx.location); } if { return bp::make_if(ctx.location); } else { return bp::make_else(ctx.location); } then { return bp::make_then(ctx.location); } diff --git a/libs/parser/rules/pslang.y b/libs/parser/rules/pslang.y index b839456..40c92a6 100644 --- a/libs/parser/rules/pslang.y +++ b/libs/parser/rules/pslang.y @@ -137,6 +137,7 @@ template %token const %token let %token mut +%token global %token if %token else %token then @@ -192,6 +193,7 @@ template %type function_declaration_single_argument %type function_return_type %type variable_declaration +%type optional_global %type variable_keyword %type type_expression %type primitive_type @@ -284,8 +286,13 @@ function_return_type ; variable_declaration -: variable_keyword name assignment expression { $$ = ast::variable_declaration{{$1, $2, nullptr, @$}, std::make_unique($4)}; } -| variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{{$1, $2, std::make_unique($4), @$}, std::make_unique($6)}; } +: optional_global variable_keyword name assignment expression { $$ = ast::variable_declaration{{$2, $3, nullptr, @$}, $1, std::make_unique($5)}; } +| optional_global variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{{$2, $3, std::make_unique($5), @$}, $1, std::make_unique($7)}; } +; + +optional_global +: global { $$ = true; } +| %empty { $$ = false; } ; variable_keyword