Add left/right shift operators

This commit is contained in:
Nikita Lisitsa 2026-03-30 12:04:34 +03:00
parent 138bddf187
commit 38fd23b50e
10 changed files with 78 additions and 0 deletions

View file

@ -24,6 +24,8 @@ namespace pslang::ast
binary_or, binary_or,
logical_or, logical_or,
logical_xor, logical_xor,
left_shift,
right_shift,
equals, equals,
not_equals, not_equals,
less, less,
@ -107,6 +109,12 @@ namespace pslang::ast
case binary_operation_type::logical_xor: case binary_operation_type::logical_xor:
out << "xor"; out << "xor";
break; 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: case binary_operation_type::equals:
out << "equals"; out << "equals";
break; break;

View file

@ -442,6 +442,21 @@ namespace pslang::ast
return; return;
} }
break; 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: case binary_operation_type::equals:
if (equal || both_integers) if (equal || both_integers)
{ {

View file

@ -255,6 +255,9 @@ namespace pslang::interpreter
return primitive_value(primitive_value_base<T>{static_cast<T>(arg1.value ^ arg2.value)}); return primitive_value(primitive_value_base<T>{static_cast<T>(arg1.value ^ arg2.value)});
} }
break; 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: case ast::binary_operation_type::equals:
return primitive_value(primitive_value_base<bool>{arg1.value == arg2.value}); return primitive_value(primitive_value_base<bool>{arg1.value == arg2.value});
case ast::binary_operation_type::not_equals: case ast::binary_operation_type::not_equals:

View file

@ -68,6 +68,12 @@ namespace pslang::ir
case ast::binary_operation_type::logical_xor: case ast::binary_operation_type::logical_xor:
out << "xor"; out << "xor";
break; 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: case ast::binary_operation_type::equals:
out << "eq"; out << "eq";
break; break;

View file

@ -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 // 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); 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 // 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); void cmp_reg(std::uint8_t reg_src1, std::uint8_t reg_src2);

View file

@ -466,6 +466,16 @@ namespace pslang::jit::aarch64
case ast::binary_operation_type::logical_xor: case ast::binary_operation_type::logical_xor:
builder.xor_reg(0, 1, 0); builder.xor_reg(0, 1, 0);
break; 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: case ast::binary_operation_type::equals:
if (is_fp) if (is_fp)
{ {

View file

@ -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)); 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) 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)); do_push(0xeb20601fu | ((reg_src1 & REG_MASK) << 5) | ((reg_src2 & REG_MASK) << 16));

View file

@ -74,6 +74,8 @@ f64 { return bp::make_f64(ctx.location); }
"|" { return bp::make_vertical_bar(ctx.location); } "|" { return bp::make_vertical_bar(ctx.location); }
"^" { return bp::make_circumflex(ctx.location); } "^" { return bp::make_circumflex(ctx.location); }
"!" { return bp::make_exclamation(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_equals(ctx.location); }
"!=" { return bp::make_not_equals(ctx.location); } "!=" { return bp::make_not_equals(ctx.location); }
"<" { return bp::make_less(ctx.location); } "<" { return bp::make_less(ctx.location); }

View file

@ -96,6 +96,8 @@ template <typename T>
%token double_vertical_bar "||" %token double_vertical_bar "||"
%token circumflex "^" %token circumflex "^"
%token exclamation "!" %token exclamation "!"
%token left_shift "<<"
%token right_shift ">>"
%token equals "==" %token equals "=="
%token not_equals "!=" %token not_equals "!="
%token less "<" %token less "<"
@ -156,6 +158,7 @@ template <typename T>
%left ampersand double_ampersand vertical_bar double_vertical_bar circumflex %left ampersand double_ampersand vertical_bar double_vertical_bar circumflex
%left equals not_equals less greater less_equals greater_equals %left equals not_equals less greater less_equals greater_equals
%nonassoc as %nonassoc as
%nonassoc left_shift right_shift
%left plus minus %left plus minus
%precedence UMINUS %precedence UMINUS
%left asterisk slash percent %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<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; } | expression less_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::less_equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression greater_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; } | expression greater_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression as type_expression { $$ = ast::cast_operation{ std::make_unique<ast::expression>($1), std::make_unique<ast::type>($3), @$ }; } | expression as type_expression { $$ = ast::cast_operation{ std::make_unique<ast::expression>($1), std::make_unique<ast::type>($3), @$ }; }
| expression left_shift expression { $$ = ast::binary_operation{ast::binary_operation_type::left_shift, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression right_shift expression { $$ = ast::binary_operation{ast::binary_operation_type::right_shift, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| minus expression %prec UMINUS { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique<ast::expression>($2), @$ }; } | minus expression %prec UMINUS { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique<ast::expression>($2), @$ }; }
| expression plus expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; } | expression plus expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression minus expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; } | expression minus expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }

View file

@ -31,3 +31,8 @@ General backlog:
* Replace std::runtime_error with appropriate custom exception types * Replace std::runtime_error with appropriate custom exception types
* Replace std::ostringstream with std::format (need support for std::format in type/ast printing) * Replace std::ostringstream with std::format (need support for std::format in type/ast printing)
* TEST COVERAGE!!! * 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 - ???)