Skip to content

Commit 2a55a90

Browse files
committed
GROOVY-12065: Implement peephole optimization for bytecode generation
1 parent c92ba5f commit 2a55a90

10 files changed

Lines changed: 1041 additions & 74 deletions

src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.codehaus.groovy.ast.GenericsType;
3333
import org.codehaus.groovy.ast.InnerClassNode;
3434
import org.codehaus.groovy.ast.InterfaceHelperClassNode;
35+
import org.codehaus.groovy.ast.IntersectionTypeClassNode;
3536
import org.codehaus.groovy.ast.MethodNode;
3637
import org.codehaus.groovy.ast.ModuleNode;
3738
import org.codehaus.groovy.ast.PackageNode;
@@ -56,7 +57,6 @@
5657
import org.codehaus.groovy.ast.expr.Expression;
5758
import org.codehaus.groovy.ast.expr.FieldExpression;
5859
import org.codehaus.groovy.ast.expr.GStringExpression;
59-
import org.codehaus.groovy.ast.IntersectionTypeClassNode;
6060
import org.codehaus.groovy.ast.expr.LambdaExpression;
6161
import org.codehaus.groovy.ast.expr.ListExpression;
6262
import org.codehaus.groovy.ast.expr.MapEntryExpression;
@@ -103,6 +103,7 @@
103103
import org.codehaus.groovy.classgen.asm.MopWriter;
104104
import org.codehaus.groovy.classgen.asm.OperandStack;
105105
import org.codehaus.groovy.classgen.asm.OptimizingStatementWriter;
106+
import org.codehaus.groovy.classgen.asm.PeepholeOptimizingMethodVisitor;
106107
import org.codehaus.groovy.classgen.asm.WriterController;
107108
import org.codehaus.groovy.classgen.asm.WriterControllerFactory;
108109
import org.codehaus.groovy.control.SourceUnit;
@@ -622,11 +623,15 @@ protected void visitConstructorOrMethod(final MethodNode node, final boolean isC
622623
receiver = parameters[0]; // non-static method or inner class ctor
623624
parameters = Arrays.copyOfRange(parameters, 1, parameters.length);
624625
}
625-
MethodVisitor mv = classVisitor.visitMethod(
626-
node.getModifiers() | (isVargs(parameters) ? ACC_VARARGS : 0), node.getName(),
627-
BytecodeHelper.getMethodDescriptor(node.getReturnType(), parameters),
628-
BytecodeHelper.getGenericsMethodSignature(node),
629-
buildExceptions(node.getExceptions()));
626+
627+
MethodVisitor mv = new PeepholeOptimizingMethodVisitor(
628+
classVisitor.visitMethod(
629+
node.getModifiers() | (isVargs(parameters) ? ACC_VARARGS : 0),
630+
node.getName(),
631+
BytecodeHelper.getMethodDescriptor(node.getReturnType(), parameters),
632+
BytecodeHelper.getGenericsMethodSignature(node),
633+
buildExceptions(node.getExceptions())));
634+
630635
controller.setMethodVisitor(mv);
631636
controller.resetLineNumber();
632637

@@ -678,10 +683,10 @@ protected void visitConstructorOrMethod(final MethodNode node, final boolean isC
678683
mv.visitMaxs(0, 0);
679684
} catch (Throwable t) {
680685
Writer writer = null;
681-
if (mv instanceof TraceMethodVisitor) {
686+
if (mv instanceof TraceMethodVisitor tmv) {
682687
writer = new StringBuilderWriter();
683688
PrintWriter p = new PrintWriter(writer);
684-
((TraceMethodVisitor) mv).p.print(p);
689+
tmv.p.print(p);
685690
p.flush();
686691
}
687692
StringBuilder message = new StringBuilder(64);
@@ -2329,11 +2334,12 @@ public void visitListExpression(final ListExpression expression) {
23292334
while (index<size) {
23302335
String methodName = "$createListEntry_" + controller.getNextHelperMethodIndex();
23312336
methods.add(methodName);
2332-
mv = controller.getClassVisitor().visitMethod(
2333-
ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC,
2334-
methodName,
2335-
"([Ljava/lang/Object;)V",
2336-
null, null);
2337+
mv = new PeepholeOptimizingMethodVisitor(
2338+
controller.getClassVisitor().visitMethod(
2339+
ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC,
2340+
methodName,
2341+
"([Ljava/lang/Object;)V",
2342+
null, null));
23372343
controller.setMethodVisitor(mv);
23382344
mv.visitCode();
23392345
int methodBlockSize = Math.min(size-index, maxInit);

src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import static org.objectweb.asm.Opcodes.ICONST_3;
7070
import static org.objectweb.asm.Opcodes.ICONST_4;
7171
import static org.objectweb.asm.Opcodes.ICONST_5;
72+
import static org.objectweb.asm.Opcodes.ICONST_M1;
7273
import static org.objectweb.asm.Opcodes.IFEQ;
7374
import static org.objectweb.asm.Opcodes.IFNE;
7475
import static org.objectweb.asm.Opcodes.ILOAD;
@@ -249,6 +250,9 @@ public static String[] getClassInternalNames(ClassNode[] names) {
249250
*/
250251
public static void pushConstant(MethodVisitor mv, int value) {
251252
switch (value) {
253+
case -1:
254+
mv.visitInsn(ICONST_M1);
255+
break;
252256
case 0:
253257
mv.visitInsn(ICONST_0);
254258
break;

src/main/java/org/codehaus/groovy/classgen/asm/MopWriter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ protected void generateMopCalls(final LinkedList<MethodNode> methods, final bool
180180
Parameter[] parameters = method.getParameters();
181181
String mopName = getMopMethodName(method, useThis);
182182
String signature = BytecodeHelper.getMethodDescriptor(returnType, parameters);
183-
MethodVisitor mv = controller.getClassVisitor().visitMethod(ACC_PUBLIC | ACC_SYNTHETIC, mopName, signature, null, null);
183+
MethodVisitor mv = new PeepholeOptimizingMethodVisitor(
184+
controller.getClassVisitor().visitMethod(
185+
ACC_PUBLIC | ACC_SYNTHETIC, mopName, signature, null, null));
184186
controller.setMethodVisitor(mv);
185187

186188
int stackIndex = 0;

src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java

Lines changed: 6 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,10 @@
4949
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveVoid;
5050
import static org.objectweb.asm.Opcodes.ACONST_NULL;
5151
import static org.objectweb.asm.Opcodes.ALOAD;
52-
import static org.objectweb.asm.Opcodes.BIPUSH;
5352
import static org.objectweb.asm.Opcodes.CHECKCAST;
5453
import static org.objectweb.asm.Opcodes.D2F;
5554
import static org.objectweb.asm.Opcodes.D2I;
5655
import static org.objectweb.asm.Opcodes.D2L;
57-
import static org.objectweb.asm.Opcodes.DCONST_0;
58-
import static org.objectweb.asm.Opcodes.DCONST_1;
5956
import static org.objectweb.asm.Opcodes.DUP;
6057
import static org.objectweb.asm.Opcodes.DUP2;
6158
import static org.objectweb.asm.Opcodes.DUP2_X1;
@@ -64,25 +61,18 @@
6461
import static org.objectweb.asm.Opcodes.F2D;
6562
import static org.objectweb.asm.Opcodes.F2I;
6663
import static org.objectweb.asm.Opcodes.F2L;
67-
import static org.objectweb.asm.Opcodes.FCONST_0;
68-
import static org.objectweb.asm.Opcodes.FCONST_1;
69-
import static org.objectweb.asm.Opcodes.FCONST_2;
7064
import static org.objectweb.asm.Opcodes.GETSTATIC;
7165
import static org.objectweb.asm.Opcodes.I2B;
7266
import static org.objectweb.asm.Opcodes.I2C;
7367
import static org.objectweb.asm.Opcodes.I2D;
7468
import static org.objectweb.asm.Opcodes.I2F;
7569
import static org.objectweb.asm.Opcodes.I2L;
7670
import static org.objectweb.asm.Opcodes.I2S;
77-
import static org.objectweb.asm.Opcodes.ICONST_0;
78-
import static org.objectweb.asm.Opcodes.ICONST_1;
7971
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
8072
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
8173
import static org.objectweb.asm.Opcodes.L2D;
8274
import static org.objectweb.asm.Opcodes.L2F;
8375
import static org.objectweb.asm.Opcodes.L2I;
84-
import static org.objectweb.asm.Opcodes.LCONST_0;
85-
import static org.objectweb.asm.Opcodes.LCONST_1;
8676
import static org.objectweb.asm.Opcodes.NEW;
8777
import static org.objectweb.asm.Opcodes.POP;
8878
import static org.objectweb.asm.Opcodes.POP2;
@@ -165,11 +155,7 @@ public void castToBool(final int mark, final boolean emptyDefault) {
165155
MethodVisitor mv = controller.getMethodVisitor();
166156
if (mark == size) {
167157
// no element, so use emptyDefault
168-
if (emptyDefault) {
169-
mv.visitIntInsn(BIPUSH, 1);
170-
} else {
171-
mv.visitIntInsn(BIPUSH, 0);
172-
}
158+
mv.visitLdcInsn(emptyDefault ? 1 : 0);
173159
stack.add(null);
174160
} else if (mark == size - 1) {
175161
ClassNode last = stack.get(size - 1);
@@ -585,42 +571,11 @@ private static void pushPrimitiveConstant(final MethodVisitor mv, final Object v
585571
boolean isChar = isPrimitiveChar(type);
586572
if (isInt || isShort || isByte || isChar) {
587573
int val = isInt ? (Integer) value : isShort ? (Short) value : isChar ? (Character) value : (Byte) value;
588-
BytecodeHelper.pushConstant(mv, val);
589-
} else if (isPrimitiveLong(type)) {
590-
if ((Long) value == 0L) {
591-
mv.visitInsn(LCONST_0);
592-
} else if ((Long) value == 1L) {
593-
mv.visitInsn(LCONST_1);
594-
} else {
595-
mv.visitLdcInsn(value);
596-
}
597-
} else if (isPrimitiveFloat(type)) {
598-
// GROOVY-9797: Use Float.equals to differentiate between positive and negative zero
599-
if (value.equals(0f)) {
600-
mv.visitInsn(FCONST_0);
601-
} else if ((Float) value == 1f) {
602-
mv.visitInsn(FCONST_1);
603-
} else if ((Float) value == 2f) {
604-
mv.visitInsn(FCONST_2);
605-
} else {
606-
mv.visitLdcInsn(value);
607-
}
608-
} else if (isPrimitiveDouble(type)) {
609-
// GROOVY-9797: Use Double.equals to differentiate between positive and negative zero
610-
if (value.equals(0d)) {
611-
mv.visitInsn(DCONST_0);
612-
} else if ((Double) value == 1d) {
613-
mv.visitInsn(DCONST_1);
614-
} else {
615-
mv.visitLdcInsn(value);
616-
}
574+
mv.visitLdcInsn(val);
575+
} else if (isPrimitiveLong(type) || isPrimitiveFloat(type) || isPrimitiveDouble(type)) {
576+
mv.visitLdcInsn(value);
617577
} else if (isPrimitiveBoolean(type)) {
618-
boolean b = (Boolean) value;
619-
if (b) {
620-
mv.visitInsn(ICONST_1);
621-
} else {
622-
mv.visitInsn(ICONST_0);
623-
}
578+
mv.visitLdcInsn((Boolean) value ? 1 : 0);
624579
} else {
625580
mv.visitLdcInsn(value);
626581
}
@@ -729,7 +684,7 @@ public void load(final ClassNode type, final int idx) {
729684
*/
730685
public void pushBool(final boolean value) {
731686
MethodVisitor mv = controller.getMethodVisitor();
732-
mv.visitInsn(value ? ICONST_1 : ICONST_0);
687+
mv.visitLdcInsn(value ? 1 : 0);
733688
push(ClassHelper.boolean_TYPE);
734689
}
735690

0 commit comments

Comments
 (0)