Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ private static RuntimeScalar unsignedResult(BigInteger value) {
}

private static boolean hasNativeInteger(RuntimeScalar scalar) {
return scalar.type == RuntimeScalarType.INTEGER && !(scalar.value instanceof BigInteger);
// Perl's ordinary bitwise operators preserve an unsigned 64-bit result.
// Values with the high bit set are consequently stored as BigInteger so
// their decimal representation remains positive. They nevertheless
// contain exactly one machine word, and BigInteger.longValue() retains
// those bits. Keeping them on the native path avoids allocating several
// BigIntegers for every operation in bit-packed workloads.
return scalar.type == RuntimeScalarType.INTEGER && scalar.value instanceof Number;
}

private static RuntimeScalar unsignedResult(long value) {
Expand Down Expand Up @@ -56,6 +62,16 @@ private static RuntimeScalar unsignedShiftRight(BigInteger value, long shift) {
return unsignedResult(value.shiftRight((int) shift));
}

private static RuntimeScalar unsignedShiftLeft(long value, long shift) {
if (shift >= 64) return RuntimeScalarCache.scalarZero;
return unsignedResult(value << (int) shift);
}

private static RuntimeScalar unsignedShiftRight(long value, long shift) {
if (shift >= 64) return RuntimeScalarCache.scalarZero;
return unsignedResult(value >>> (int) shift);
}

private static BigInteger exactInteger(RuntimeScalar scalar) {
return scalar.type == RuntimeScalarType.INTEGER && scalar.value instanceof BigInteger
? (BigInteger) scalar.value : null;
Expand Down Expand Up @@ -352,6 +368,10 @@ public static RuntimeScalar bitwiseNot(RuntimeScalar runtimeScalar) {
* @return A new RuntimeScalar with the result of the bitwise NOT operation.
*/
public static RuntimeScalar bitwiseNotBinary(RuntimeScalar runtimeScalar) {
if (hasNativeInteger(runtimeScalar)) {
return unsignedResult(~((Number) runtimeScalar.value).longValue())
.propagateTaint(runtimeScalar);
}
return unsignedResult(unsignedValue(runtimeScalar).xor(UV_MASK))
.propagateTaint(runtimeScalar);
}
Expand Down Expand Up @@ -526,10 +546,11 @@ public static RuntimeScalar shiftLeft(RuntimeScalar runtimeScalar, RuntimeScalar
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER
&& exactInteger(arg2) == null) {
long shift = arg2.getLong();
long value = runtimeScalar.getLong();
if (shift >= 0) {
return unsignedShiftLeft(unsignedValue(runtimeScalar), shift);
return unsignedShiftLeft(value, shift);
} else if (shift != Long.MIN_VALUE) {
return unsignedShiftRight(unsignedValue(runtimeScalar), -shift);
return unsignedShiftRight(value, -shift);
}
return RuntimeScalarCache.scalarZero;
}
Expand Down Expand Up @@ -615,10 +636,11 @@ public static RuntimeScalar shiftRight(RuntimeScalar runtimeScalar, RuntimeScala
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER
&& exactInteger(arg2) == null) {
long shift = arg2.getLong();
long value = runtimeScalar.getLong();
if (shift >= 0) {
return unsignedShiftRight(unsignedValue(runtimeScalar), shift);
return unsignedShiftRight(value, shift);
} else if (shift != Long.MIN_VALUE) {
return unsignedShiftLeft(unsignedValue(runtimeScalar), -shift);
return unsignedShiftLeft(value, -shift);
}
return RuntimeScalarCache.scalarZero;
}
Expand Down
17 changes: 17 additions & 0 deletions src/test/resources/unit/bitwise_native_word_fastpath.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use strict;
use warnings;

use Test::More tests => 4;

# Ordinary numeric bitwise operations keep an unsigned result when bit 63 is
# set. Reusing that result must preserve the low word through native bitwise
# and shift operations.
my $all_bits = ~0;
is(($all_bits & 0xffff_ffff), 4_294_967_295,
'AND accepts an unsigned word result');
is(($all_bits ^ $all_bits), 0,
'XOR accepts an unsigned word result');
is(($all_bits >> 32), 4_294_967_295,
'right shift accepts an unsigned word result');
is((($all_bits << 1) | 1), '18446744073709551615',
'chained operations retain unsigned word semantics');
Loading