The attached change to src/pmc/bigint.pmc fixes a bug in shl/shr when
given a negative shift: The shift amount argument to the underlying GMP
operations is unsigned, so GMP tries consumes a huge amount of memory.
The patch also adds four tests for negative shifts.
The src/pmc/scalar.pmc changes promote the result to BigInt on
overflow for shl (but not shr yet). Comments would be greatly
appreciated. I have two questions in particular: (1) Is there a
cleaner way to do the code-sharing? and (2) Should the code throw an
error if attempting to morph a non-numeric type (and if so, what's the
best way of detecting that)? TIA.
If there are no objections, I will commit the negative shift fix
first, then work on finishing BigInt promotion by adding overflow
signalling (see PARROT_ERRORS_OVERFLOW_FLAG) and making shr behave
symmetrically.
-- Bob Rogers
http://rgrjr.dyndns.org/
* src/pmc/bigint.pmc:
+ (bigint_bitwise_shl_bigint_int, bigint_bitwise_shr_bigint_int):
Handle negative shift values by shifting in the opposite
direction. This fixes a bug where GMP would run out of memory; the
shift amount is unsigned.
* src/pmc/scalar.pmc:
+ (bitwise_shl_int, i_bitwise_shl_int): Promote to BigInt on
overflow. [Arguably, this should throw an error rather than morph
SELF from a non-numeric type.]
+ (bitwise_shl): Use bitwise_shl_int.
+ (i_bitwise_shl): Use i_bitwise_shl_int.
* t/pmc/bigint.t:
+ Four new tests for negative bignum shifts, and one new shl overflow
test. [Still need to tackle shr overflow.]
Diffs between last version checked in and current workfile(s):
Index: src/pmc/bigint.pmc
===================================================================
--- src/pmc/bigint.pmc (revision 18426)
+++ src/pmc/bigint.pmc (working copy)
@@ -267,13 +267,19 @@
bigint_bitwise_shl_bigint_int(Interp *interp, PMC* self,
INTVAL value, PMC *dest)
{
- mpz_mul_2exp(BN(dest), BN(self), value);
+ if (value >= 0)
+ mpz_mul_2exp(BN(dest), BN(self), value);
+ else
+ mpz_tdiv_q_2exp(BN(dest), BN(self), -value);
}
static void
bigint_bitwise_shr_bigint_int(Interp *interp, PMC* self,
INTVAL value, PMC *dest)
{
- mpz_tdiv_q_2exp(BN(dest), BN(self), value);
+ if (value >= 0)
+ mpz_tdiv_q_2exp(BN(dest), BN(self), value);
+ else
+ mpz_mul_2exp(BN(dest), BN(self), -value);
}
#else /* ifdef PARROT_HAS_GMP */
Index: src/pmc/scalar.pmc
===================================================================
--- src/pmc/scalar.pmc (revision 18426)
+++ src/pmc/scalar.pmc (working copy)
@@ -20,6 +20,9 @@
#include "parrot/parrot.h"
+extern PMC*
+Parrot_BigInt_bitwise_shl_int(Interp *interp, PMC* pmc, INTVAL value, PMC*
dest);
+
pmclass scalar abstract noinit {
/*
@@ -1083,38 +1086,51 @@
*/
- PMC* bitwise_shl(PMC* value, PMC* dest) {
- INTVAL result;
-
- result = DYNSELF.get_integer() << VTABLE_get_integer(INTERP, value);
- if (!dest)
- dest = pmc_new(INTERP, SELF->vtable->base_type);
- VTABLE_set_integer_native(INTERP, dest, result);
- return dest;
+ PMC* bitwise_shl(PMC* shift_value, PMC* dest) {
+ Parrot_scalar_bitwise_shl_int(interp, SELF,
+ VTABLE_get_integer(INTERP, shift_value),
+ dest);
}
- PMC* bitwise_shl_int(INTVAL value, PMC* dest) {
- INTVAL result;
+ PMC* bitwise_shl_int(INTVAL shift_amount, PMC* dest) {
+ INTVAL base = DYNSELF.get_integer();
+ INTVAL result = base << shift_amount;
- result = DYNSELF.get_integer() << value;
- if (!dest)
- dest = pmc_new(INTERP, SELF->vtable->base_type);
- VTABLE_set_integer_native(INTERP, dest, result);
+ if ((result >> shift_amount) != base) {
+ /* Overflow; must promote dest or SELF to bigint. */
+ if (dest)
+ VTABLE_morph(INTERP, dest, enum_class_BigInt);
+ else
+ dest = pmc_new(INTERP, enum_class_BigInt);
+ VTABLE_set_integer_native(INTERP, dest, base);
+ Parrot_BigInt_bitwise_shl_int(interp, dest, shift_amount, dest);
+ }
+ else {
+ if (!dest)
+ dest = pmc_new(INTERP, SELF->vtable->base_type);
+ VTABLE_set_integer_native(INTERP, dest, result);
+ }
return dest;
}
- void i_bitwise_shl(PMC* value) {
- INTVAL result;
-
- result = DYNSELF.get_integer() << VTABLE_get_integer(INTERP, value);
- DYNSELF.set_integer_native(result);
+ void i_bitwise_shl(PMC* shift_value) {
+ Parrot_scalar_i_bitwise_shl_int(interp, SELF,
+ VTABLE_get_integer(INTERP,
shift_value));
}
- void i_bitwise_shl_int(INTVAL value) {
- INTVAL result;
+ void i_bitwise_shl_int(INTVAL shift_amount) {
+ INTVAL base = DYNSELF.get_integer();
+ INTVAL result = base << shift_amount;
- result = DYNSELF.get_integer() << value;
- DYNSELF.set_integer_native(result);
+ if ((result >> shift_amount) != base) {
+ /* Overflow. */
+ VTABLE_morph(INTERP, SELF, enum_class_BigInt);
+ DYNSELF.set_integer_native(base);
+ Parrot_BigInt_bitwise_shl_int(interp, SELF, shift_amount, SELF);
+ }
+ else {
+ DYNSELF.set_integer_native(result);
+ }
}
/*
Index: t/pmc/bigint.t
===================================================================
--- t/pmc/bigint.t (revision 18426)
+++ t/pmc/bigint.t (working copy)
@@ -25,7 +25,7 @@
=cut
if ( $PConfig{gmp} ) {
- plan tests => 36;
+ plan tests => 41;
}
else {
plan skip_all => "No BigInt Lib configured";
@@ -759,6 +759,26 @@
102400000000000
OUT
+pir_output_is( <<'CODE', <<'OUT', "shl_bigint with a negative shift" );
+## cf the shr_bigint case.
+.sub main :main
+ new $P0, .BigInt
+ set $P0, 8
+ new $P1, .BigInt
+ set $P1, -2
+ new $P2, .BigInt
+ shl $P2, $P0, $P1
+ say $P2
+ set $P0, "102400000000000"
+ set $P1, -10
+ shl $P2, $P0, $P1
+ say $P2
+.end
+CODE
+2
+100000000000
+OUT
+
pasm_output_is( <<'CODE', <<'OUT', "shl_int" );
new P0, .BigInt
set P0, 2
@@ -788,6 +808,66 @@
102400000000000
OUT
+pir_output_is( <<'CODE', <<'OUT', "shl_int with a negative shift" );
+## cf the shr_int case.
+.sub main :main
+ new $P0, .BigInt
+ set $P0, 4
+ new $P1, .Integer
+ set $P1, -1
+ new $P2, .BigInt
+ shl $P2, $P0, $P1
+ say $P2
+ set $P0, "200000000000"
+ set $P1, -1
+ shl $P2, $P0, $P1
+ say $P2
+ set $P0, "102400000000000"
+ set $P1, -10
+ shl $P2, $P0, $P1
+ say $P2
+.end
+CODE
+2
+100000000000
+100000000000
+OUT
+
+pir_output_is( <<'CODE', <<'OUT', "shl_int promotes Integer to Bigint" );
+.sub main :main
+ new $P0, .Integer
+ set $P0, 1000001
+ new $P1, .Integer
+ set $P1, 10
+ new $P2, .Integer
+ ## shift by 10 bits . . .
+ shl $P2, $P0, $P1
+ $S2 = typeof $P2
+ print $S2
+ print ' '
+ say $P2
+ ## then by 20 bits . . .
+ $P1 = 20
+ new $P3, .Integer
+ $P3 = 1000001
+ shl $P3, $P0, $P1
+ $S2 = typeof $P3
+ print $S2
+ print ' '
+ say $P3
+ ## then by another 20 bits (total 30) in place.
+ shl $P2, $P2, $P1
+ $S2 = typeof $P2
+ print $S2
+ print ' '
+ say $P2
+.end
+CODE
+Integer 1024001024
+BigInt 1048577048576
+BigInt 1073742897741824
+OUT
+
pasm_output_is( <<'CODE', <<'OUT', "shr_bigint" );
new P0, .BigInt
set P0, 8
@@ -810,6 +890,26 @@
100000000000
OUT
+pir_output_is( <<'CODE', <<'OUT', "shr_bigint with a negative shift" );
+## cf the shl_bigint case.
+.sub main :main
+ new $P0, .BigInt
+ set $P0, 2
+ new $P1, .BigInt
+ set $P1, -2
+ new $P2, .BigInt
+ shr $P2, $P0, $P1
+ say $P2
+ set $P0, "100000000000"
+ set $P1, -10
+ shr $P2, $P0, $P1
+ say $P2
+.end
+CODE
+8
+102400000000000
+OUT
+
pasm_output_is( <<'CODE', <<'OUT', "shr_int" );
new P0, .BigInt
set P0, 4
@@ -839,6 +939,30 @@
100000000000
OUT
+pir_output_is( <<'CODE', <<'OUT', "shr_int with a negative shift" );
+## cf the shl_int case.
+.sub main :main
+ new $P0, .BigInt
+ set $P0, 2
+ new $P1, .Integer
+ set $P1, -1
+ new $P2, .BigInt
+ shr $P2, $P0, $P1
+ say $P2
+ set $P0, "100000000000"
+ set $P1, -1
+ shr $P2, $P0, $P1
+ say $P2
+ set $P1, -10
+ shr $P2, $P0, $P1
+ say $P2
+.end
+CODE
+4
+200000000000
+102400000000000
+OUT
+
pir_output_is( <<'CODE', <<'OUT', "BUG #34949 gt" );
.sub main :main
.local pmc b
End of diffs.