From 38fd23b50ebaff38321496f06bf31ac6dd0416a0 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 30 Mar 2026 12:04:34 +0300 Subject: [PATCH] Add left/right shift operators --- libs/ast/include/pslang/ast/operation.hpp | 8 ++++++++ libs/ast/source/type_check.cpp | 15 +++++++++++++++ libs/interpreter/source/eval.cpp | 3 +++ libs/ir/source/print.cpp | 6 ++++++ .../jit/arch/aarch64/instruction_builder.hpp | 9 +++++++++ libs/jit/source/arch/aarch64/compiler.cpp | 10 ++++++++++ .../source/arch/aarch64/instruction_builder.cpp | 15 +++++++++++++++ libs/parser/rules/pslang.l | 2 ++ libs/parser/rules/pslang.y | 5 +++++ plans.txt | 5 +++++ 10 files changed, 78 insertions(+) diff --git a/libs/ast/include/pslang/ast/operation.hpp b/libs/ast/include/pslang/ast/operation.hpp index 898b71e..5d54796 100644 --- a/libs/ast/include/pslang/ast/operation.hpp +++ b/libs/ast/include/pslang/ast/operation.hpp @@ -24,6 +24,8 @@ namespace pslang::ast binary_or, logical_or, logical_xor, + left_shift, + right_shift, equals, not_equals, less, @@ -107,6 +109,12 @@ namespace pslang::ast case binary_operation_type::logical_xor: out << "xor"; break; + case binary_operation_type::left_shift: + out << "left shift"; + break; + case binary_operation_type::right_shift: + out << "right shift"; + break; case binary_operation_type::equals: out << "equals"; break; diff --git a/libs/ast/source/type_check.cpp b/libs/ast/source/type_check.cpp index f13f3f8..a73c2bc 100644 --- a/libs/ast/source/type_check.cpp +++ b/libs/ast/source/type_check.cpp @@ -442,6 +442,21 @@ namespace pslang::ast return; } break; + case binary_operation_type::left_shift: + case binary_operation_type::right_shift: + if (both_integers) + { + if (!types::is_unsigned_integer_type(*arg2_type)) + { + std::ostringstream os; + os << "Shift amount must be an unsigned integer type, but got "; + ast::print(os, *arg2_type); + throw type_error(os.str(), ast::get_location(*node.arg2)); + } + node.inferred_type = arg1_type; + return; + } + break; case binary_operation_type::equals: if (equal || both_integers) { diff --git a/libs/interpreter/source/eval.cpp b/libs/interpreter/source/eval.cpp index 95e5ef1..af41637 100644 --- a/libs/interpreter/source/eval.cpp +++ b/libs/interpreter/source/eval.cpp @@ -255,6 +255,9 @@ namespace pslang::interpreter return primitive_value(primitive_value_base{static_cast(arg1.value ^ arg2.value)}); } break; + case ast::binary_operation_type::left_shift: + case ast::binary_operation_type::right_shift: + throw std::runtime_error("Not implemented"); case ast::binary_operation_type::equals: return primitive_value(primitive_value_base{arg1.value == arg2.value}); case ast::binary_operation_type::not_equals: diff --git a/libs/ir/source/print.cpp b/libs/ir/source/print.cpp index ddf4c2f..a75d53b 100644 --- a/libs/ir/source/print.cpp +++ b/libs/ir/source/print.cpp @@ -68,6 +68,12 @@ namespace pslang::ir case ast::binary_operation_type::logical_xor: out << "xor"; break; + case ast::binary_operation_type::left_shift: + out << "shl"; + break; + case ast::binary_operation_type::right_shift: + out << "shr"; + break; case ast::binary_operation_type::equals: out << "eq"; break; 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 0c8e308..63993e4 100644 --- a/libs/jit/include/pslang/jit/arch/aarch64/instruction_builder.hpp +++ b/libs/jit/include/pslang/jit/arch/aarch64/instruction_builder.hpp @@ -137,6 +137,15 @@ namespace pslang::jit::aarch64 // Compute the value of unsigned division (@reg_src1 / @reg_src2) and store the result in @reg_dst void udiv_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst); + // Compute the value of logical left bit shift (@reg_src1 << @reg_src2) and store the result in @reg_dst + void lsl_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst); + + // Compute the value of logical (zero-extended) right bit shift (@reg_src1 >> @reg_src2) and store the result in @reg_dst + void lsr_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst); + + // Compute the value of arithmetic (sign-extended) right bit shift (@reg_src1 >> @reg_src2) and store the result in @reg_dst + void asr_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst); + // Compare the values of @reg_src1 and @reg_src2 and set the flags void cmp_reg(std::uint8_t reg_src1, std::uint8_t reg_src2); diff --git a/libs/jit/source/arch/aarch64/compiler.cpp b/libs/jit/source/arch/aarch64/compiler.cpp index 41ab352..13ca073 100644 --- a/libs/jit/source/arch/aarch64/compiler.cpp +++ b/libs/jit/source/arch/aarch64/compiler.cpp @@ -466,6 +466,16 @@ namespace pslang::jit::aarch64 case ast::binary_operation_type::logical_xor: builder.xor_reg(0, 1, 0); break; + case ast::binary_operation_type::left_shift: + builder.lsl_reg(0, 1, 0); + break; + case ast::binary_operation_type::right_shift: + extend(0, arg1_type); + if (types::is_unsigned_integer_type(*arg1_type)) + builder.lsr_reg(0, 1, 0); + else + builder.asr_reg(0, 1, 0); + break; case ast::binary_operation_type::equals: if (is_fp) { diff --git a/libs/jit/source/arch/aarch64/instruction_builder.cpp b/libs/jit/source/arch/aarch64/instruction_builder.cpp index 29fc226..af33297 100644 --- a/libs/jit/source/arch/aarch64/instruction_builder.cpp +++ b/libs/jit/source/arch/aarch64/instruction_builder.cpp @@ -175,6 +175,21 @@ namespace pslang::jit::aarch64 do_push(0x9ac00800u | (reg_dst & REG_MASK) | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16)); } + void instruction_builder::lsl_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst) + { + do_push(0x9ac02000u | (reg_dst & REG_MASK) | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16)); + } + + void instruction_builder::lsr_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst) + { + do_push(0x9ac02400u | (reg_dst & REG_MASK) | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16)); + } + + void instruction_builder::asr_reg(std::uint8_t reg_src1, std::uint8_t reg_src2, std::uint8_t reg_dst) + { + do_push(0x9ac02800u | (reg_dst & REG_MASK) | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16)); + } + void instruction_builder::cmp_reg(std::uint8_t reg_src1, std::uint8_t reg_src2) { do_push(0xeb20601fu | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16)); diff --git a/libs/parser/rules/pslang.l b/libs/parser/rules/pslang.l index a13213d..074249a 100644 --- a/libs/parser/rules/pslang.l +++ b/libs/parser/rules/pslang.l @@ -74,6 +74,8 @@ f64 { return bp::make_f64(ctx.location); } "|" { return bp::make_vertical_bar(ctx.location); } "^" { return bp::make_circumflex(ctx.location); } "!" { return bp::make_exclamation(ctx.location); } +"<<" { return bp::make_left_shift(ctx.location); } +">>" { return bp::make_right_shift(ctx.location); } "==" { return bp::make_equals(ctx.location); } "!=" { return bp::make_not_equals(ctx.location); } "<" { return bp::make_less(ctx.location); } diff --git a/libs/parser/rules/pslang.y b/libs/parser/rules/pslang.y index ff9c914..c256dbf 100644 --- a/libs/parser/rules/pslang.y +++ b/libs/parser/rules/pslang.y @@ -96,6 +96,8 @@ template %token double_vertical_bar "||" %token circumflex "^" %token exclamation "!" +%token left_shift "<<" +%token right_shift ">>" %token equals "==" %token not_equals "!=" %token less "<" @@ -156,6 +158,7 @@ template %left ampersand double_ampersand vertical_bar double_vertical_bar circumflex %left equals not_equals less greater less_equals greater_equals %nonassoc as +%nonassoc left_shift right_shift %left plus minus %precedence UMINUS %left asterisk slash percent @@ -311,6 +314,8 @@ expression | expression less_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::less_equals, std::make_unique($1), std::make_unique($3), @$ }; } | expression greater_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique($1), std::make_unique($3), @$ }; } | expression as type_expression { $$ = ast::cast_operation{ std::make_unique($1), std::make_unique($3), @$ }; } +| expression left_shift expression { $$ = ast::binary_operation{ast::binary_operation_type::left_shift, std::make_unique($1), std::make_unique($3), @$ }; } +| expression right_shift expression { $$ = ast::binary_operation{ast::binary_operation_type::right_shift, std::make_unique($1), std::make_unique($3), @$ }; } | minus expression %prec UMINUS { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique($2), @$ }; } | expression plus expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique($1), std::make_unique($3), @$ }; } | expression minus expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique($1), std::make_unique($3), @$ }; } diff --git a/plans.txt b/plans.txt index 81f03f1..44cc6f8 100644 --- a/plans.txt +++ b/plans.txt @@ -31,3 +31,8 @@ General backlog: * Replace std::runtime_error with appropriate custom exception types * Replace std::ostringstream with std::format (need support for std::format in type/ast printing) * TEST COVERAGE!!! + +Platform-dependent operations backlog: +* Division by zero (returns 0 on arm64, triggers an interrupt on x86) - declare as UB? +* Floating-point <-> integer conversion (is rounding the same on all platforms?) +* Shifts (bit count is taken modulo integer size on arm64, x86 - ???)