Skip to content
Merged
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 @@ -47,8 +47,9 @@ public class InterpreterState {
ThreadLocal.withInitial(() -> new RuntimeScalar("main"));
private static final ThreadLocal<Deque<InterpreterFrame>> frameStack =
ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<Integer>> pcStack =
ThreadLocal.withInitial(ArrayDeque::new);
// Use ArrayList of mutable int holders for O(1) PC updates (no pop/push overhead)
private static final ThreadLocal<ArrayList<int[]>> pcStack =
ThreadLocal.withInitial(ArrayList::new);

/**
* Push a new interpreter frame onto the stack.
Expand All @@ -60,7 +61,7 @@ public class InterpreterState {
*/
public static void push(InterpretedCode code, String packageName, String subroutineName) {
frameStack.get().push(new InterpreterFrame(code, packageName, subroutineName));
pcStack.get().push(0);
pcStack.get().add(new int[]{0}); // Mutable holder for PC
}

/**
Expand All @@ -73,17 +74,16 @@ public static void pop() {
stack.pop();
}

Deque<Integer> pcs = pcStack.get();
ArrayList<int[]> pcs = pcStack.get();
if (!pcs.isEmpty()) {
pcs.pop();
pcs.removeLast();
}
}

public static void setCurrentPc(int pc) {
Deque<Integer> pcs = pcStack.get();
ArrayList<int[]> pcs = pcStack.get();
if (!pcs.isEmpty()) {
pcs.pop();
pcs.push(pc);
pcs.getLast()[0] = pc; // Direct mutation, no allocation
}
}

Expand All @@ -109,7 +109,12 @@ public static List<InterpreterFrame> getStack() {
}

public static List<Integer> getPcStack() {
return new ArrayList<>(pcStack.get());
ArrayList<int[]> pcs = pcStack.get();
ArrayList<Integer> result = new ArrayList<>(pcs.size());
for (int[] holder : pcs) {
result.add(holder[0]);
}
return result;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,17 @@ public class BitwiseOperators {
* @return A new RuntimeScalar with the result of the bitwise AND operation.
*/
public static RuntimeScalar bitwiseAnd(RuntimeScalar runtimeScalar, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip all checks (defined, looksLikeNumber, tied)
int t1 = runtimeScalar.type;
int t2 = arg2.type;
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER) {
long result = ((int) runtimeScalar.value) & ((int) arg2.value);
return new RuntimeScalar(result);
}

// Fetch tied scalars once to avoid redundant FETCH calls
RuntimeScalar val1 = runtimeScalar.type == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() : runtimeScalar;
RuntimeScalar val2 = arg2.type == RuntimeScalarType.TIED_SCALAR ? arg2.tiedFetch() : arg2;
RuntimeScalar val1 = t1 == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() : runtimeScalar;
RuntimeScalar val2 = t2 == RuntimeScalarType.TIED_SCALAR ? arg2.tiedFetch() : arg2;

// Check for uninitialized values and generate warnings
if (!val1.getDefinedBoolean()) {
Expand Down Expand Up @@ -69,9 +77,17 @@ public static RuntimeScalar bitwiseAndBinary(RuntimeScalar runtimeScalar, Runtim
* @return A new RuntimeScalar with the result of the bitwise OR operation.
*/
public static RuntimeScalar bitwiseOr(RuntimeScalar runtimeScalar, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip all checks (looksLikeNumber, tied)
int t1 = runtimeScalar.type;
int t2 = arg2.type;
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER) {
long result = ((int) runtimeScalar.value) | ((int) arg2.value);
return new RuntimeScalar(result);
}

// Fetch tied scalars once to avoid redundant FETCH calls
RuntimeScalar val1 = runtimeScalar.type == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() : runtimeScalar;
RuntimeScalar val2 = arg2.type == RuntimeScalarType.TIED_SCALAR ? arg2.tiedFetch() : arg2;
RuntimeScalar val1 = t1 == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() : runtimeScalar;
RuntimeScalar val2 = t2 == RuntimeScalarType.TIED_SCALAR ? arg2.tiedFetch() : arg2;

// In Perl, if either operand is a reference or doesn't look like a number, use string operations
if (!ScalarUtils.looksLikeNumber(val1) || !ScalarUtils.looksLikeNumber(val2)) {
Expand Down Expand Up @@ -112,9 +128,17 @@ public static RuntimeScalar bitwiseOrBinary(RuntimeScalar runtimeScalar, Runtime
* @return A new RuntimeScalar with the result of the bitwise XOR operation.
*/
public static RuntimeScalar bitwiseXor(RuntimeScalar runtimeScalar, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip all checks (looksLikeNumber, tied)
int t1 = runtimeScalar.type;
int t2 = arg2.type;
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER) {
long result = ((int) runtimeScalar.value) ^ ((int) arg2.value);
return new RuntimeScalar(result);
}

// Fetch tied scalars once to avoid redundant FETCH calls
RuntimeScalar val1 = runtimeScalar.type == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() : runtimeScalar;
RuntimeScalar val2 = arg2.type == RuntimeScalarType.TIED_SCALAR ? arg2.tiedFetch() : arg2;
RuntimeScalar val1 = t1 == RuntimeScalarType.TIED_SCALAR ? runtimeScalar.tiedFetch() : runtimeScalar;
RuntimeScalar val2 = t2 == RuntimeScalarType.TIED_SCALAR ? arg2.tiedFetch() : arg2;

// Use numeric XOR only if BOTH operands look like numbers
// For everything else (strings, blessed objects, references, etc.), use string XOR
Expand Down Expand Up @@ -320,6 +344,18 @@ public static RuntimeScalar bitwiseNotDot(RuntimeScalar runtimeScalar) {
* @return A new RuntimeScalar with the result of the left shift operation.
*/
public static RuntimeScalar shiftLeft(RuntimeScalar runtimeScalar, RuntimeScalar arg2) {
// Fast path: both INTEGER with non-negative shift < 32
int t1 = runtimeScalar.type;
int t2 = arg2.type;
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER) {
int shift = (int) arg2.value;
if (shift >= 0 && shift < 32) {
long unsignedValue = ((int) runtimeScalar.value) & 0xFFFFFFFFL;
long result = (unsignedValue << shift) & 0xFFFFFFFFL;
return new RuntimeScalar(result);
}
}

// Check for uninitialized values and generate warnings
// Use getDefinedBoolean() to handle tied scalars correctly
if (!runtimeScalar.getDefinedBoolean()) {
Expand Down Expand Up @@ -390,6 +426,18 @@ public static RuntimeScalar shiftLeft(RuntimeScalar runtimeScalar, RuntimeScalar
* @return A new RuntimeScalar with the result of the right shift operation.
*/
public static RuntimeScalar shiftRight(RuntimeScalar runtimeScalar, RuntimeScalar arg2) {
// Fast path: both INTEGER with non-negative shift < 32
int t1 = runtimeScalar.type;
int t2 = arg2.type;
if (t1 == RuntimeScalarType.INTEGER && t2 == RuntimeScalarType.INTEGER) {
int shift = (int) arg2.value;
if (shift >= 0 && shift < 32) {
long unsignedValue = ((int) runtimeScalar.value) & 0xFFFFFFFFL;
long result = unsignedValue >>> shift;
return new RuntimeScalar(result);
}
}

// Check for uninitialized values and generate warnings
// Use getDefinedBoolean() to handle tied scalars correctly
if (!runtimeScalar.getDefinedBoolean()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public class CompareOperators {
* @return A RuntimeScalar representing a boolean value (true if arg1 < arg2).
*/
public static RuntimeScalar lessThan(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber()
if (arg1.type == RuntimeScalarType.INTEGER && arg2.type == RuntimeScalarType.INTEGER) {
return getScalarBoolean((int) arg1.value < (int) arg2.value);
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -54,6 +59,11 @@ public static RuntimeScalar lessThan(RuntimeScalar arg1, RuntimeScalar arg2) {
* @return A RuntimeScalar representing a boolean value (true if arg1 <= arg2).
*/
public static RuntimeScalar lessThanOrEqual(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber()
if (arg1.type == RuntimeScalarType.INTEGER && arg2.type == RuntimeScalarType.INTEGER) {
return getScalarBoolean((int) arg1.value <= (int) arg2.value);
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -87,6 +97,11 @@ public static RuntimeScalar lessThanOrEqual(RuntimeScalar arg1, RuntimeScalar ar
* @return A RuntimeScalar representing a boolean value (true if arg1 > arg2).
*/
public static RuntimeScalar greaterThan(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber()
if (arg1.type == RuntimeScalarType.INTEGER && arg2.type == RuntimeScalarType.INTEGER) {
return getScalarBoolean((int) arg1.value > (int) arg2.value);
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -120,6 +135,11 @@ public static RuntimeScalar greaterThan(RuntimeScalar arg1, RuntimeScalar arg2)
* @return A RuntimeScalar representing a boolean value (true if arg1 >= arg2).
*/
public static RuntimeScalar greaterThanOrEqual(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber()
if (arg1.type == RuntimeScalarType.INTEGER && arg2.type == RuntimeScalarType.INTEGER) {
return getScalarBoolean((int) arg1.value >= (int) arg2.value);
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -184,6 +204,11 @@ public static RuntimeScalar equalTo(RuntimeScalar arg1, int arg2) {
* @return A RuntimeScalar representing a boolean value (true if arg1 == arg2).
*/
public static RuntimeScalar equalTo(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber()
if (arg1.type == RuntimeScalarType.INTEGER && arg2.type == RuntimeScalarType.INTEGER) {
return getScalarBoolean((int) arg1.value == (int) arg2.value);
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -217,6 +242,11 @@ public static RuntimeScalar equalTo(RuntimeScalar arg1, RuntimeScalar arg2) {
* @return A RuntimeScalar representing a boolean value (true if arg1 != arg2).
*/
public static RuntimeScalar notEqualTo(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber()
if (arg1.type == RuntimeScalarType.INTEGER && arg2.type == RuntimeScalarType.INTEGER) {
return getScalarBoolean((int) arg1.value != (int) arg2.value);
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/perlonjava/runtime/operators/MathOperators.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ public static RuntimeScalar add(RuntimeScalar arg1, int arg2) {
* @return A new RuntimeScalar representing the sum.
*/
public static RuntimeScalar add(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber(), type checks
if (arg1.type == INTEGER && arg2.type == INTEGER) {
int a = (int) arg1.value;
int b = (int) arg2.value;
try {
return getScalarInt(Math.addExact(a, b));
} catch (ArithmeticException ignored) {
return new RuntimeScalar((double) a + (double) b);
}
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -117,6 +128,17 @@ public static RuntimeScalar subtract(RuntimeScalar arg1, int arg2) {
* @return A new RuntimeScalar representing the difference.
*/
public static RuntimeScalar subtract(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getNumber(), type checks
if (arg1.type == INTEGER && arg2.type == INTEGER) {
int a = (int) arg1.value;
int b = (int) arg2.value;
try {
return getScalarInt(Math.subtractExact(a, b));
} catch (ArithmeticException ignored) {
return new RuntimeScalar((double) a - (double) b);
}
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down Expand Up @@ -151,6 +173,17 @@ public static RuntimeScalar subtract(RuntimeScalar arg1, RuntimeScalar arg2) {
* @return A new RuntimeScalar representing the product.
*/
public static RuntimeScalar multiply(RuntimeScalar arg1, RuntimeScalar arg2) {
// Fast path: both INTEGER - skip blessedId check, getDefinedBoolean(), getNumber()
if (arg1.type == INTEGER && arg2.type == INTEGER) {
int a = (int) arg1.value;
int b = (int) arg2.value;
try {
return getScalarInt(Math.multiplyExact(a, b));
} catch (ArithmeticException ignored) {
return new RuntimeScalar((double) a * (double) b);
}
}

// Prepare overload context and check if object is eligible for overloading
int blessId = blessedId(arg1);
int blessId2 = blessedId(arg2);
Expand Down