Implement global variables
This commit is contained in:
parent
393db4b912
commit
9337490f5b
19 changed files with 319 additions and 183 deletions
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ namespace pslang::ast
|
|||
struct variable_declaration
|
||||
: variable_base
|
||||
{
|
||||
bool global;
|
||||
expression_ptr initializer;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace pslang::ast
|
|||
std::unordered_map<std::string, foreign_function_declaration *> foreign_functions;
|
||||
std::unordered_map<std::string, struct_definition *> structs;
|
||||
std::unordered_map<std::string, variable_base *> variables;
|
||||
std::unordered_map<std::string, variable_declaration *> globals;
|
||||
std::unordered_map<std::string, variable_declaration *> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ namespace pslang::ir
|
|||
struct alloc
|
||||
{};
|
||||
|
||||
struct global
|
||||
{
|
||||
std::vector<std::uint8_t> initializer;
|
||||
};
|
||||
|
||||
struct copy
|
||||
{
|
||||
node_ref source;
|
||||
|
|
@ -126,6 +131,7 @@ namespace pslang::ir
|
|||
label,
|
||||
literal,
|
||||
alloc,
|
||||
global,
|
||||
copy,
|
||||
load,
|
||||
store,
|
||||
|
|
|
|||
|
|
@ -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::variable_base const *, node_ref> variables;
|
||||
std::unordered_map<ast::variable_declaration const *, node_ref> 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::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 result = apply(*node.initializer);
|
||||
if (before == last())
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ namespace pslang::ir
|
|||
return false;
|
||||
}
|
||||
|
||||
bool operator()(global const &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator()(store const &)
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -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<std::uint32_t>(value);
|
||||
}
|
||||
out << std::dec;
|
||||
}
|
||||
|
||||
void operator()(copy const & instruction)
|
||||
{
|
||||
out << "copy $" << get_index(instruction.source);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/jit/blob.hpp>
|
||||
#include <pslang/jit/storage.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/jit/abi.hpp>
|
||||
#include <pslang/jit/storage.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
|
@ -28,7 +29,9 @@ namespace pslang::jit
|
|||
};
|
||||
|
||||
jit::abi abi;
|
||||
std::vector<std::uint8_t> code = {};
|
||||
|
||||
binary_storage storage = {};
|
||||
|
||||
std::unordered_map<ast::function_definition const *, std::int32_t> symbols = {};
|
||||
std::int32_t entry_point = 0;
|
||||
std::vector<foreign_resolve_info> foreign_resolve = {};
|
||||
|
|
|
|||
28
libs/jit/include/pslang/jit/storage.hpp
Normal file
28
libs/jit/include/pslang/jit/storage.hpp
Normal 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();
|
||||
|
||||
}
|
||||
|
|
@ -115,6 +115,30 @@ namespace pslang::jit::aarch64
|
|||
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
|
||||
{
|
||||
program_context & pcontext;
|
||||
|
|
@ -128,24 +152,24 @@ namespace pslang::jit::aarch64
|
|||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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<void *>(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<types::struct_type>(*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<types::struct_type>(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<types::array_type>(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<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)
|
||||
{
|
||||
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});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,17 +14,41 @@
|
|||
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__)
|
||||
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<std::uint8_t>(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<std::uint8_t>(ptr, [total_size](void * ptr){ munmap(ptr, total_size); });
|
||||
#else
|
||||
throw std::runtime_error("Host-executable modules are not supported for this platform");
|
||||
#endif
|
||||
|
|
|
|||
32
libs/jit/source/storage.cpp
Normal file
32
libs/jit/source/storage.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ template <typename T>
|
|||
%token const
|
||||
%token let
|
||||
%token mut
|
||||
%token global
|
||||
%token if
|
||||
%token else
|
||||
%token then
|
||||
|
|
@ -192,6 +193,7 @@ template <typename T>
|
|||
%type <ast::function_declaration::argument> function_declaration_single_argument
|
||||
%type <ast::type_ptr> function_return_type
|
||||
%type <ast::variable_declaration> variable_declaration
|
||||
%type <bool> optional_global
|
||||
%type <ast::value_category> variable_keyword
|
||||
%type <ast::type> type_expression
|
||||
%type <types::primitive_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<ast::expression>($4)}; }
|
||||
| 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 assignment expression { $$ = ast::variable_declaration{{$2, $3, nullptr, @$}, $1, std::make_unique<ast::expression>($5)}; }
|
||||
| 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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue