Aarch64 jit compiler wip: implement integer & floating-point type conversions

This commit is contained in:
Nikita Lisitsa 2026-01-06 12:11:56 +03:00
parent 12a4f64aed
commit 00d8f0fe52
5 changed files with 114 additions and 13 deletions

View file

@ -169,9 +169,9 @@ int main(int argc, char ** argv)
for (auto const & module : modules)
{
// TODO: remove, testing-only code; should execute entry point instead
auto offset = module.code.symbol_table.at("pow");
auto fptr = (float(*)(float, unsigned))(module.code.memory.data.get() + offset);
auto x = fptr(1.5f, 12);
auto offset = module.code.symbol_table.at("test");
auto fptr = (int(*)())(module.code.memory.data.get() + offset);
auto x = fptr();
std::cout << "Result: " << std::boolalpha << x << std::endl;
}
}

View file

@ -1,10 +1,2 @@
func pow(x : f32, n : u32) -> f32:
mut r = 1.0
mut f = x // x^k
mut k = 1u
while k <= n:
if (n & k) != 0u:
r = r * f
f = f * f
k = k + k
return r
func test() -> i32:
return 3.141592h as i32

View file

@ -147,6 +147,25 @@ namespace pslang::jit::aarch64
void fcmp(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t mode);
// If @op is 0, move the value of a floating-point register @reg_src in format @mode into a general register @reg_dst
// If @op is 1, move the value of a general register @reg_src into a floating-point register @reg_dst in format @mode
// NB: mode = 2 (single precision) isn't supported
void fmov(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode, std::uint8_t op);
// Convert signed integer in floating-point register @reg_src into floating-point format @mode and
// store in floating-point register @reg_dst
void scvtf(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode);
// Convert unsigned integer in floating-point register @reg_src into floating-point format @mode and
// store in floating-point register @reg_dst
void ucvtf(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode);
// Convert a value from floating-point register @reg_src in format @mode into a signed integer in @reg_dst
void fcvtns(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode);
// Convert a value from floating-point register @reg_src in format @mode into a unsigned integer in @reg_dst
void fcvtnu(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode);
// Return from a subroutine, taking the return address from register @reg
void ret(std::uint8_t reg = 30);

View file

@ -528,6 +528,65 @@ namespace pslang::jit::aarch64
}
}
void apply(ast::cast_operation const & node)
{
auto src_type = ast::get_type(*node.expression);
auto dst_type = node.inferred_type;
apply(*node.expression);
if (types::equal(*src_type, *dst_type))
return;
if (types::is_integer_type(*src_type))
{
if (types::is_integer_type(*dst_type))
{
extend(0, dst_type);
}
else if (types::is_floating_point_type(*dst_type))
{
auto dst_mode = fp_mode_for(*dst_type);
if (types::is_signed_integer_type(*src_type))
{
builder.fmov(0, 0, 3, 1);
builder.scvtf(0, 0, 3);
if (dst_mode != 3)
builder.fcvt(0, 3, 0, dst_mode);
}
else if (types::is_unsigned_integer_type(*src_type))
{
builder.fmov(0, 0, 3, 1);
builder.ucvtf(0, 0, 3);
if (dst_mode != 3)
builder.fcvt(0, 3, 0, dst_mode);
}
}
}
else if (types::is_floating_point_type(*src_type))
{
auto src_mode = fp_mode_for(*src_type);
if (types::is_integer_type(*dst_type))
{
if (types::is_signed_integer_type(*dst_type))
{
builder.fcvtns(0, 0, src_mode);
extend(0, dst_type);
}
else if (types::is_unsigned_integer_type(*dst_type))
{
builder.fcvtnu(0, 0, src_mode);
extend(0, dst_type);
}
}
else if (types::is_floating_point_type(*dst_type))
{
auto dst_mode = fp_mode_for(*dst_type);
builder.fcvt(0, src_mode, 0, dst_mode);
}
}
}
void apply(ast::assignment const & node)
{
auto identifier = std::get_if<ast::identifier>(node.lhs.get());

View file

@ -209,6 +209,37 @@ namespace pslang::jit::aarch64
do_push(0x1e202000u | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16) | (((mode & 0x3u) ^ 0x2u) << 22));
}
void instruction_builder::fmov(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode, std::uint8_t op)
{
do_push(0x9e260000u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5) | (((mode & 0x3u) ^ 0x2u) << 22) | ((op & 0x1u) << 16));
}
void instruction_builder::scvtf(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode)
{
if (mode == 1)
do_push(0x5e79d800u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5));
else
do_push(0x5e21d800u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5) | ((mode & 0x1u) << 22));
}
void instruction_builder::ucvtf(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode)
{
if (mode == 1)
do_push(0x7e79d800u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5));
else
do_push(0x7e21d800u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5) | ((mode & 0x1u) << 22));
}
void instruction_builder::fcvtns(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode)
{
do_push(0x9e200000u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5) | (((mode & 0x3u) ^ 0x2u) << 22));
}
void instruction_builder::fcvtnu(std::uint8_t reg_src, std::uint8_t reg_dst, std::uint8_t mode)
{
do_push(0x9e210000u | (reg_dst & REG_MASK) | ((reg_src & REG_MASK) << 5) | (((mode & 0x3u) ^ 0x2u) << 22));
}
void instruction_builder::ret(std::uint8_t reg)
{
do_push(0xd65f0000u | ((reg & REG_MASK) << 5));