Author: rgrjr
Date: Sun May  6 19:32:12 2007
New Revision: 18444

Modified:
   trunk/src/pmc/scalar.pmc
   trunk/t/pmc/bigint.t

Log:
* src/pmc/scalar.pmc:
   + (bitwise_left_shift_internal):  New internal function that takes a
     signed shift amount, and promotes to BigInt (or throws) on
     left-shift overflow.
   + (bitwise_shr, bitwise_shr_int, i_bitwise_shr, i_bitwise_shr_int,
     bitwise_shl, bitwise_shl_int, i_bitwise_shl, i_bitwise_shl_int):
     Use bitwise_left_shift_internal for correctness & symmetry.
* t/pmc/bigint.t:
   + Three new Integer => BigInt promotion tests.


Modified: trunk/src/pmc/scalar.pmc
==============================================================================
--- trunk/src/pmc/scalar.pmc    (original)
+++ trunk/src/pmc/scalar.pmc    Sun May  6 19:32:12 2007
@@ -20,6 +20,60 @@
 
 #include "parrot/parrot.h"
 
+extern PMC *
+Parrot_BigInt_bitwise_shl_int(Interp *interp, PMC* pmc, INTVAL value, PMC* 
dest);
+
+static PMC *
+bitwise_left_shift_internal(Interp *interp, PMC *self,
+                            PMC *dest, INTVAL shift_amount)
+{
+    /* This handles both left and right shifts; the sign of shift_amount
+       controls the direction, shifting left if positive and right if negative.
+       If we get an error (which can only happen for left shifts) and
+       PARROT_ERRORS_OVERFLOW_FLAG is set, then we throw an error, else the
+       destination is promoted to a BigInt before shifting.  If dest == self,
+       then the shift is done in place.  */
+    INTVAL base = VTABLE_get_integer(interp, self);
+    INTVAL result;
+    int overflow_p = 0;
+
+    /* Compute the shift. */
+    if (shift_amount >= 0) {
+        /* Left shift. */
+        result = base << shift_amount;
+        overflow_p = (result >> shift_amount) != base;
+    }
+    else {
+        /* Right shift.  By definition, this can never overflow. */
+        result = base >> -shift_amount;
+    }
+
+    /* Store the result. */
+    if (! overflow_p) {
+        if (!dest)
+            dest = pmc_new(interp, self->vtable->base_type);
+        VTABLE_set_integer_native(interp, dest, result);
+    }
+    else if (PARROT_ERRORS_test(interp, PARROT_ERRORS_OVERFLOW_FLAG)) {
+        real_exception(interp, NULL, ERR_OVERFLOW, "Integer overflow");
+    }
+    else {
+        /* Overflow; must promote dest to BigInt, and do a BigInt shift. */
+        if (dest)
+            VTABLE_morph(interp, dest, enum_class_BigInt);
+        else {
+            /* Arguably, we should throw an error here if dest == self and self
+             * is not a numeric type.  However, that would require a firmer
+             * definition of "numeric type."
+             */
+            dest = pmc_new(interp, enum_class_BigInt);
+        }
+        VTABLE_set_integer_native(interp, dest, base);
+        Parrot_BigInt_bitwise_shl_int(interp, dest, shift_amount, dest);
+    }
+    return dest;
+}
+
 pmclass scalar abstract noinit {
 
 /*
@@ -966,49 +1020,38 @@
 =item C<PMC* bitwise_shr_int(INTVAL value, PMC *dest)>
 
 Returns in C<*dest> the arithmetic shift right of the scalar by C<value>.
+A negative C<value> shifts left.
+The destination may become a C<BigInt> as a result (but only if the shift 
amount
+is less than zero).
 
 =item C<void i_bitwise_shr(PMC *value)>
 
 =item C<void i_bitwise_shr_int(INTVAL value)>
 
-Inplace shift right.
+Inplace shift right.  A negative C<value> shifts left.
+SELF may become a C<BigInt> as a result (but only if the shift amount is less
+than zero).
 
 =cut
 
 */
 
     PMC* bitwise_shr(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;
+        return bitwise_left_shift_internal(INTERP, SELF, dest,
+                                           -VTABLE_get_integer(INTERP, value));
     }
 
     PMC* bitwise_shr_int(INTVAL value, PMC* dest) {
-        INTVAL result;
-
-        result = DYNSELF.get_integer() >> value;
-        if (!dest)
-            dest = pmc_new(INTERP, SELF->vtable->base_type);
-        VTABLE_set_integer_native(INTERP, dest, result);
-        return dest;
+        return bitwise_left_shift_internal(INTERP, SELF, dest, -value);
     }
 
     void i_bitwise_shr(PMC* value) {
-        INTVAL result;
-
-        result = DYNSELF.get_integer() >> VTABLE_get_integer(INTERP, value);
-        DYNSELF.set_integer_native(result);
+        bitwise_left_shift_internal(INTERP, SELF, SELF,
+                                    -VTABLE_get_integer(INTERP, value));
     }
 
     void i_bitwise_shr_int(INTVAL value) {
-        INTVAL result;
-
-        result = DYNSELF.get_integer() >> value;
-        DYNSELF.set_integer_native(result);
+        bitwise_left_shift_internal(INTERP, SELF, SELF, -value);
     }
 
 /*
@@ -1071,50 +1114,36 @@
 
 =item C<PMC* bitwise_shl_int(INTVAL value, PMC *dest)>
 
-Returns in C<*dest> the shift left of the scalar by C<*value>.
+Returns in C<*dest> the shift left of the scalar by C<*value>.  A negative
+C<value> shifts right.  The destination may become a C<BigInt> as a result.
 
 =item C<void i_bitwise_shl(PMC *value)>
 
 =item C<void i_bitwise_shl_int(INTVAL value)>
 
-Inplace shift left.
+Inplace shift left.  A negative C<value> shifts right.  SELF may become a
+C<BigInt> as a result.
 
 =cut
 
 */
 
     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;
+        return bitwise_left_shift_internal(INTERP, SELF, dest,
+                                           VTABLE_get_integer(INTERP, value));
     }
 
     PMC* bitwise_shl_int(INTVAL value, PMC* dest) {
-        INTVAL result;
-
-        result = DYNSELF.get_integer() << value;
-        if (!dest)
-            dest = pmc_new(INTERP, SELF->vtable->base_type);
-        VTABLE_set_integer_native(INTERP, dest, result);
-        return dest;
+        return bitwise_left_shift_internal(INTERP, SELF, dest, value);
     }
 
     void i_bitwise_shl(PMC* value) {
-        INTVAL result;
-
-        result = DYNSELF.get_integer() << VTABLE_get_integer(INTERP, value);
-        DYNSELF.set_integer_native(result);
+        bitwise_left_shift_internal(INTERP, SELF, SELF,
+                                    VTABLE_get_integer(INTERP, value));
     }
 
     void i_bitwise_shl_int(INTVAL value) {
-        INTVAL result;
-
-        result = DYNSELF.get_integer() << value;
-        DYNSELF.set_integer_native(result);
+        bitwise_left_shift_internal(INTERP, SELF, SELF, value);
     }
 
 /*

Modified: trunk/t/pmc/bigint.t
==============================================================================
--- trunk/t/pmc/bigint.t        (original)
+++ trunk/t/pmc/bigint.t        Sun May  6 19:32:12 2007
@@ -25,7 +25,7 @@
 =cut
 
 if ( $PConfig{gmp} ) {
-    plan tests => 40;
+    plan tests => 43;
 }
 else {
     plan skip_all => "No BigInt Lib configured";
@@ -833,6 +833,107 @@
 100000000000
 OUT
 
+pir_output_is( <<'CODE', <<'OUT', "shl_int and i_shl_int promote 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
+
+pir_output_like( <<'CODE', <<'OUT', "shl_int throws an error when promotion is 
disabled");
+.include "errors.pasm"
+.sub main :main
+   errorson .PARROT_ERRORS_OVERFLOW_FLAG
+   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
+.end
+CODE
+/Integer 1024001024
+Integer overflow
+current instr/
+OUT
+
+pir_output_is( <<'CODE', <<'OUT', "shr_int and i_shr_int with a neg shift 
promote 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 . . .
+   shr $P2, $P0, $P1
+   $S2 = typeof $P2
+   print $S2
+   print ' '
+   say $P2
+   ## then by 20 bits . . .
+   $P1 = -20
+   new $P3, .Integer
+   $P3 = 1000001
+   shr $P3, $P0, $P1
+   $S2 = typeof $P3
+   print $S2
+   print ' '
+   say $P3
+   ## then by another 20 bits (total 30) in place.
+   shr $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

Reply via email to