Bit shifts
Jose E. Marchesi
jemarch@gnu.org
Wed Feb 4 11:07:00 GMT 2026
Allright, so I propose we settle on the following behavior, implemented
by the patch below:
* In standard Algol 68
- ABS (b) < bits_width results in the expected result.
- ABS (b) = bits_width results in all bits set to zero.
- ABS (b) > bits_width is undefined.
* In GNU Algol 68
- ABS (b) < bits_width results in the expected result.
- ABS (b) >= bits_width results in all bits set to zero.
Note that our behavior is:
1. Conformant and backwards compatible.
2. Matches well with the similar extension of skip of integral, bits,
real, etc values to always yield zeroes.
3. Intuitive.
Any objections?
diff --git a/gcc/algol68/a68-low-bits.cc b/gcc/algol68/a68-low-bits.cc
index cfe84fbff33..7ec058ee1b0 100644
--- a/gcc/algol68/a68-low-bits.cc
+++ b/gcc/algol68/a68-low-bits.cc
@@ -258,25 +258,37 @@ a68_bits_subset (tree bits1, tree bits2)
bits2);
}
-/* Rotate the bits in BITS SHIFT bits to the left if SHIFT is positive, or ABS
- (SHIFT) bits to the right if SHIFT is negative.
+/* Rotate the bits in BITS according to the value of SHIFT:
- A run-time error is raised if the count overflows the BITS value. */
+ - If ABS(SHIFT) >= bits_width, the result is all bits clear.
+ - If SHIFT is positive, BITS gets shifted SHIFT bits to the right.
+ - If SHIFT is negative, BITS gets shifted ABS(SHIFT) bits to the left.
+*/
tree
-a68_bits_shift (tree shift, tree bits)
+a68_bits_shift (NODE_T *p, tree shift, tree bits)
{
shift = save_expr (shift);
bits = save_expr (bits);
- return fold_build3 (COND_EXPR,
- TREE_TYPE (bits),
- fold_build2 (GE_EXPR, TREE_TYPE (shift),
- shift, build_int_cst (TREE_TYPE (shift), 0)),
- fold_build2 (LSHIFT_EXPR, TREE_TYPE (bits),
- bits, shift),
- fold_build2 (RSHIFT_EXPR, TREE_TYPE (bits),
- bits,
- fold_build1 (ABS_EXPR, TREE_TYPE (shift), shift)));
+
+ tree shift_type = TREE_TYPE (shift);
+ tree bits_type = TREE_TYPE (bits);
+ tree abs_shift = save_expr (fold_build1 (ABS_EXPR, TREE_TYPE (shift), shift));
+
+ tree shifted_right = fold_build2 (RSHIFT_EXPR, bits_type, bits, abs_shift);
+ tree shifted_left = fold_build2 (LSHIFT_EXPR, bits_type, bits, abs_shift);
+
+ tree shifted_bits = fold_build3 (COND_EXPR, TREE_TYPE (bits),
+ fold_build2 (GE_EXPR, shift_type,
+ shift, build_zero_cst (shift_type)),
+ shifted_right, shifted_left);
+
+ return fold_build3_loc (a68_get_node_location (p),
+ COND_EXPR,
+ TREE_TYPE (bits),
+ fold_build2 (LT_EXPR, TREE_TYPE (abs_shift),
+ abs_shift, a68_bits_width (bits_type)),
+ shifted_bits, build_zero_cst (bits_type));
}
/* Given two bits values, build an expression that calculates whether A = B. */
diff --git a/gcc/algol68/a68-low-prelude.cc b/gcc/algol68/a68-low-prelude.cc
index 9bc03cff48f..44abd5fe74d 100644
--- a/gcc/algol68/a68-low-prelude.cc
+++ b/gcc/algol68/a68-low-prelude.cc
@@ -1085,7 +1085,9 @@ a68_lower_shl3 (NODE_T *p, LOW_CTX_T ctx)
{
tree bits = a68_lower_tree (SUB (p), ctx);
tree shift = a68_lower_tree (NEXT (NEXT (SUB (p))), ctx);
- return a68_bits_shift (shift, bits);
+ return a68_bits_shift (p,
+ fold_build1 (NEGATE_EXPR, TREE_TYPE (shift), shift),
+ bits);
}
tree
@@ -1093,9 +1095,7 @@ a68_lower_shr3 (NODE_T *p, LOW_CTX_T ctx)
{
tree bits = a68_lower_tree (SUB (p), ctx);
tree shift = a68_lower_tree (NEXT (NEXT (SUB (p))), ctx);
- return a68_bits_shift (fold_build1 (NEGATE_EXPR,
- TREE_TYPE (shift), shift),
- bits);
+ return a68_bits_shift (p, shift, bits);
}
tree
diff --git a/gcc/algol68/a68.h b/gcc/algol68/a68.h
index 3e3442f668a..9dcb14600a2 100644
--- a/gcc/algol68/a68.h
+++ b/gcc/algol68/a68.h
@@ -534,7 +534,7 @@ tree a68_bits_ior (tree bits1, tree bits2);
tree a68_bits_xor (tree bits1, tree bits2);
tree a68_bits_elem (NODE_T *p, tree pos, tree bits);
tree a68_bits_subset (tree bits1, tree bits2);
-tree a68_bits_shift (tree shift, tree bits);
+tree a68_bits_shift (NODE_T *p, tree shift, tree bits);
tree a68_bits_eq (tree a, tree b, location_t loc = UNKNOWN_LOCATION);
tree a68_bits_ne (tree a, tree b, location_t loc = UNKNOWN_LOCATION);
tree a68_bits_set (MOID_T *m, tree bits, tree numbit, location_t loc = UNKNOWN_LOCATION);
diff --git a/gcc/algol68/ga68.texi b/gcc/algol68/ga68.texi
index 1d40530c3f4..811f34f7d9f 100644
--- a/gcc/algol68/ga68.texi
+++ b/gcc/algol68/ga68.texi
@@ -2799,16 +2799,22 @@ in the elements of the given bits operands.
@deftypefn Operator {} {@B{SHL}} {= (@B{l} @B{bits} a, @B{int} n) @B{l} @B{bits}}
@deftypefnx Operator {} {@B{UP}} {= (@B{l} @B{bits} a, @B{int} n) @B{l} @B{bits}}
-Dyadic operator that yields the given bits operand shifted @code{n}
-positions to the left. Extra elements introduced on the right are
-initialized to @code{@B{false}}.
+Dyadic operator that yields the given bits operand shifted @code{ABS
+n} positions to the left if @code{n >= 0} or @code{ABS n} positions to
+the right if @code{n < 0}. Extra elements introduced on the right or
+left are initialized to @code{@B{false}}. If @code{ABS n >
+L_bits_width} then the resulting bits value has all bits set to
+@code{false}.
@end deftypefn
@deftypefn Operator {} {@B{SHR}} {= (@B{l} @B{bits} a, @B{int} n) @B{l} @B{bits}}
@deftypefnx Operator {} {@B{DOWN}} {= (@B{l} @B{bits} a, @B{int} n) @B{l} @B{bits}}
-Dyadic operator that yields the given bits operand shifted @code{n}
-positions to the right. Extra elements introduced on the left are
-initialized to @code{@B{false}}.
+Dyadic operator that yields the given bits operand shifted @code{ABS
+n} positions to the right if @code{n >= 0} or @code{ABS n} positions
+to the left if @code{n < 0}. Extra elements introduced on the right
+or left are initialized to @code{@B{false}}. If @code{ABS n >
+L_bits_width} then the resulting bits value has all bits set to
+@code{false}.
@end deftypefn
@subsection Relational
diff --git a/gcc/testsuite/algol68/execute/bits-shift-1.a68 b/gcc/testsuite/algol68/execute/bits-shift-1.a68
new file mode 100644
index 00000000000..9e3220d9190
--- /dev/null
+++ b/gcc/testsuite/algol68/execute/bits-shift-1.a68
@@ -0,0 +1,9 @@
+begin int first_bit = 2**31;
+ int myshift = 29;
+
+ bits b1 = BIN (first_bit) SHR 29;
+ bits b2 = BIN (first_bit) SHR myshift;
+
+ assert (b1 = 2r100);
+ assert (b2 = 2r100)
+end
diff --git a/gcc/testsuite/algol68/execute/bits-shift-2.a68 b/gcc/testsuite/algol68/execute/bits-shift-2.a68
new file mode 100644
index 00000000000..e946b978728
--- /dev/null
+++ b/gcc/testsuite/algol68/execute/bits-shift-2.a68
@@ -0,0 +1,26 @@
+{ Shifting by L_bits_width results in all bits being zero. }
+
+begin assert (short short 16rffff SHR short_short_bits_width = short short 16r0);
+ assert (short 16rffff SHR short_bits_width = short 16r0);
+ assert (16rffff SHR bits_width = 16r0);
+ assert (long 16rffff SHR long_bits_width = long 16r0);
+ assert (long long 16rffff SHR long_long_bits_width = long long 16r0);
+
+ assert (short short 16rffff SHR -short_short_bits_width = short short 16r0);
+ assert (short 16rffff SHR -short_bits_width = short 16r0);
+ assert (16rffff SHR -bits_width = 16r0);
+ assert (long 16rffff SHR -long_bits_width = long 16r0);
+ assert (long long 16rffff SHR -long_long_bits_width = long long 16r0);
+
+ assert (short short 16rffff SHL short_short_bits_width = short short 16r0);
+ assert (short 16rffff SHL short_bits_width = short 16r0);
+ assert (16rffff SHL bits_width = 16r0);
+ assert (long 16rffff SHL long_bits_width = long 16r0);
+ assert (long long 16rffff SHL long_long_bits_width = long long 16r0);
+
+ assert (short short 16rffff SHL -short_short_bits_width = short short 16r0);
+ assert (short 16rffff SHL -short_bits_width = short 16r0);
+ assert (16rffff SHL -bits_width = 16r0);
+ assert (long 16rffff SHL -long_bits_width = long 16r0);
+ assert (long long 16rffff SHL -long_long_bits_width = long long 16r0)
+end
diff --git a/gcc/testsuite/algol68/execute/bits-shift-3.a68 b/gcc/testsuite/algol68/execute/bits-shift-3.a68
new file mode 100644
index 00000000000..b4536dc4368
--- /dev/null
+++ b/gcc/testsuite/algol68/execute/bits-shift-3.a68
@@ -0,0 +1,26 @@
+{ Shifting by > L_bits_width results in all bits being zero. }
+
+begin assert (short short 16rffff SHR (short_short_bits_width + 1) = short short 16r0);
+ assert (short 16rffff SHR (short_bits_width + 1) = short 16r0);
+ assert (16rffff SHR (bits_width + 1) = 16r0);
+ assert (long 16rffff SHR (long_bits_width + 1) = long 16r0);
+ assert (long long 16rffff SHR (long_long_bits_width + 1) = long long 16r0);
+
+ assert (short short 16rffff SHR -(short_short_bits_width + 1) = short short 16r0);
+ assert (short 16rffff SHR -(short_bits_width + 1) = short 16r0);
+ assert (16rffff SHR -(bits_width + 1) = 16r0);
+ assert (long 16rffff SHR -(long_bits_width + 1) = long 16r0);
+ assert (long long 16rffff SHR -(long_long_bits_width + 1) = long long 16r0);
+
+ assert (short short 16rffff SHL (short_short_bits_width + 1) = short short 16r0);
+ assert (short 16rffff SHL (short_bits_width + 1) = short 16r0);
+ assert (16rffff SHL (bits_width + 1) = 16r0);
+ assert (long 16rffff SHL (long_bits_width + 1) = long 16r0);
+ assert (long long 16rffff SHL (long_long_bits_width + 1) = long long 16r0);
+
+ assert (short short 16rffff SHL -(short_short_bits_width + 1) = short short 16r0);
+ assert (short 16rffff SHL -(short_bits_width + 1) = short 16r0);
+ assert (16rffff SHL -(bits_width + 1) = 16r0);
+ assert (long 16rffff SHL -(long_bits_width + 1) = long 16r0);
+ assert (long long 16rffff SHL -(long_long_bits_width + 1) = long long 16r0)
+end
More information about the Algol68
mailing list