From 00d8f0fe524d0f4ceee80a5e99573e35f78d5f11 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Tue, 6 Jan 2026 12:11:56 +0300 Subject: [PATCH] Aarch64 jit compiler wip: implement integer & floating-point type conversions --- apps/interpreter/source/main.cpp | 6 +- examples/jit_test.psl | 12 +--- .../jit/arch/aarch64/instruction_builder.hpp | 19 ++++++ libs/jit/source/arch/aarch64/compiler.cpp | 59 +++++++++++++++++++ .../arch/aarch64/instruction_builder.cpp | 31 ++++++++++ 5 files changed, 114 insertions(+), 13 deletions(-) diff --git a/apps/interpreter/source/main.cpp b/apps/interpreter/source/main.cpp index 45f425b..48b888d 100644 --- a/apps/interpreter/source/main.cpp +++ b/apps/interpreter/source/main.cpp @@ -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; } } diff --git a/examples/jit_test.psl b/examples/jit_test.psl index 2f9beda..04c8f30 100644 --- a/examples/jit_test.psl +++ b/examples/jit_test.psl @@ -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 diff --git a/libs/jit/include/pslang/jit/arch/aarch64/instruction_builder.hpp b/libs/jit/include/pslang/jit/arch/aarch64/instruction_builder.hpp index 45d8c99..12f2e15 100644 --- a/libs/jit/include/pslang/jit/arch/aarch64/instruction_builder.hpp +++ b/libs/jit/include/pslang/jit/arch/aarch64/instruction_builder.hpp @@ -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); diff --git a/libs/jit/source/arch/aarch64/compiler.cpp b/libs/jit/source/arch/aarch64/compiler.cpp index 2dcbde7..fd44e57 100644 --- a/libs/jit/source/arch/aarch64/compiler.cpp +++ b/libs/jit/source/arch/aarch64/compiler.cpp @@ -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(node.lhs.get()); diff --git a/libs/jit/source/arch/aarch64/instruction_builder.cpp b/libs/jit/source/arch/aarch64/instruction_builder.cpp index 9c200a3..ebfc136 100644 --- a/libs/jit/source/arch/aarch64/instruction_builder.cpp +++ b/libs/jit/source/arch/aarch64/instruction_builder.cpp @@ -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));