Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Fix bigint bitops. This should fix RT#115966 on perl6-p. #169

Merged
merged 1 commit into from

3 participants

@skids

No description provided.

@skids

just adding this comment as an experement to see if that jogs things to do travis-ci

@FROGGS
Owner

I guess you'd have to comit to trigger it. Also, travis only builds or moar atm.

I'll test that PR today in xcase I do not forget, and will merge if there is ne spectest fallout.

@vendethiel

Can't you set up travis to test PRs?

@FROGGS
Owner

It is already set-up to do that.

@FROGGS
Owner

Look at the icon after the title here for example: https://github.com/perl6/nqp/pulls?q=is%3Apr+is%3Aclosed

@FROGGS
Owner

That looks indeed very good:

Test Summary Report
-------------------
t/spec/S03-operators/bit.rakudo.parrot                        (Wstat: 0 Tests: 37 Failed: 0)
  TODO passed:   9-11
Files=859, Tests=32872, 896 wallclock secs (10.78 usr  1.34 sys + 2414.45 cusr 162.07 csys = 2588.64 CPU)
Result: PASS
@FROGGS FROGGS merged commit 8319266 into perl6:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 13, 2014
  1. @skids
This page is out of date. Refresh to see the latest.
Showing with 59 additions and 23 deletions.
  1. +59 −23 src/vm/parrot/ops/nqp_bigint.ops
View
82 src/vm/parrot/ops/nqp_bigint.ops
@@ -103,44 +103,80 @@ static void from_num(FLOATVAL d, mp_int *a) {
mp_shrink(a);
}
+/* Bitops on libtomath (no 2s compliment API) are horrendously inefficient and
+ * really should be hand-coded to work DIGIT-by-DIGIT with in-loop carry
+ * handling. For now we have these fixups.
+ *
+ * The following inverts the bits of a negative bigint, adds 1 to that, and
+ * appends sign-bit extension DIGITs to it to give us a 2s compliment
+ * representation in memory. Do not call it on positive bigints.
+ */
static void grow_and_negate(mp_int *a, int size, mp_int *b) {
int i;
- int actual_size = MAX(size, USED(a));
- mp_zero(b);
+ /* Always add an extra DIGIT so we can tell positive values
+ * with a one in the highest bit apart from negative values.
+ */
+ int actual_size = MAX(size, USED(a)) + 1;
+
+ SIGN(b) = MP_ZPOS;
mp_grow(b, actual_size);
USED(b) = actual_size;
- for (i = 0; i < actual_size; i++) {
+ for (i = 0; i < USED(a); i++) {
DIGIT(b, i) = (~DIGIT(a, i)) & MP_MASK;
}
+ for (; i < actual_size; i++) {
+ DIGIT(b, i) = MP_MASK;
+ }
+ /* Note: This add cannot cause another grow assuming nobody ever
+ * tries to use tommath -0 for anything, and nobody tries to use
+ * this on positive bigints.
+ */
mp_add_d(b, 1, b);
}
-
static void two_complement_bitop(mp_int *a, mp_int *b, mp_int *c,
int (*mp_bitop)(mp_int *, mp_int *, mp_int *)) {
+
mp_int d;
- if (SIGN(a) ^ SIGN(b)) {
- /* exactly one of them is negative, so need to perform
- * some magic. tommath stores a sign bit, but Perl 6 expects
- * 2's complement */
+ mp_int e;
+ mp_int *f;
+ mp_int *g;
+
+ f = a;
+ g = b;
+ if (MP_NEG == SIGN(a)) {
mp_init(&d);
- if (MP_NEG == SIGN(a)) {
- grow_and_negate(a, USED(b), &d);
- mp_bitop(&d, b, c);
- } else {
- grow_and_negate(b, USED(a), &d);
- mp_bitop(a, &d, c);
- }
- if (DIGIT(c, USED(c) - 1) & ((mp_digit)1<<(mp_digit)(DIGIT_BIT - 1))) {
- grow_and_negate(c, c->used, &d);
- mp_copy(&d, c);
- mp_neg(c, c);
+ grow_and_negate(a, USED(b), &d);
+ f = &d;
+ }
+ if (MP_NEG == SIGN(b)) {
+ mp_init(&e);
+ grow_and_negate(b, USED(a), &e);
+ g = &e;
+ }
+ /* f and g now guaranteed to each point to positive bigints containing
+ * a 2s compliment representation of the values in a and b. If either
+ * a or b was negative, the representation is one tomath "digit" longer
+ * than it need be and sign extended.
+ */
+ mp_bitop(f, g, c);
+ if (f == &d) mp_clear(&d);
+ if (g == &e) mp_clear(&e);
+ /* Use the fact that tomath clamps to detect results that should be
+ * signed. If we created extra tomath "digits" and they resulted in
+ * sign bits of 0, they have been clamped away. If the resulting sign
+ * bits were 1, they remain, and c will have more digits than either of
+ * original operands. Note this only works because we do not
+ * support NOR/NAND/NXOR, and so two zero sign bits can never create 1s.
+ */
+ if (USED(c) > MAX(USED(a),USED(b))) {
+ int i;
+ for (i = 0; i < USED(c); i++) {
+ DIGIT(c, i) = (~DIGIT(c, i)) & MP_MASK;
}
- mp_clear(&d);
- } else {
- mp_bitop(a, b, c);
+ mp_add_d(c, 1, c);
+ mp_neg(c, c);
}
-
}
static void two_complement_shl(mp_int *result, mp_int *value, INTVAL count) {
Something went wrong with that request. Please try again.