diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java b/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java index d2f9abc7b..f4625f480 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java @@ -47,8 +47,9 @@ public class InterpreterState { ThreadLocal.withInitial(() -> new RuntimeScalar("main")); private static final ThreadLocal> frameStack = ThreadLocal.withInitial(ArrayDeque::new); - private static final ThreadLocal> pcStack = - ThreadLocal.withInitial(ArrayDeque::new); + // Use ArrayList of mutable int holders for O(1) PC updates (no pop/push overhead) + private static final ThreadLocal> pcStack = + ThreadLocal.withInitial(ArrayList::new); /** * Push a new interpreter frame onto the stack. @@ -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 } /** @@ -73,17 +74,16 @@ public static void pop() { stack.pop(); } - Deque pcs = pcStack.get(); + ArrayList pcs = pcStack.get(); if (!pcs.isEmpty()) { - pcs.pop(); + pcs.removeLast(); } } public static void setCurrentPc(int pc) { - Deque pcs = pcStack.get(); + ArrayList pcs = pcStack.get(); if (!pcs.isEmpty()) { - pcs.pop(); - pcs.push(pc); + pcs.getLast()[0] = pc; // Direct mutation, no allocation } } @@ -109,7 +109,12 @@ public static List getStack() { } public static List getPcStack() { - return new ArrayList<>(pcStack.get()); + ArrayList pcs = pcStack.get(); + ArrayList result = new ArrayList<>(pcs.size()); + for (int[] holder : pcs) { + result.add(holder[0]); + } + return result; } /** diff --git a/src/main/java/org/perlonjava/runtime/operators/BitwiseOperators.java b/src/main/java/org/perlonjava/runtime/operators/BitwiseOperators.java index 7594efb48..036247a21 100644 --- a/src/main/java/org/perlonjava/runtime/operators/BitwiseOperators.java +++ b/src/main/java/org/perlonjava/runtime/operators/BitwiseOperators.java @@ -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()) { @@ -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)) { @@ -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 @@ -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()) { @@ -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()) { diff --git a/src/main/java/org/perlonjava/runtime/operators/CompareOperators.java b/src/main/java/org/perlonjava/runtime/operators/CompareOperators.java index 402ad5c0c..8d96dbfe5 100644 --- a/src/main/java/org/perlonjava/runtime/operators/CompareOperators.java +++ b/src/main/java/org/perlonjava/runtime/operators/CompareOperators.java @@ -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); @@ -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); @@ -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); @@ -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); @@ -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); @@ -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); diff --git a/src/main/java/org/perlonjava/runtime/operators/MathOperators.java b/src/main/java/org/perlonjava/runtime/operators/MathOperators.java index e0bb87701..41a792ad4 100644 --- a/src/main/java/org/perlonjava/runtime/operators/MathOperators.java +++ b/src/main/java/org/perlonjava/runtime/operators/MathOperators.java @@ -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); @@ -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); @@ -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);