Skip to content

Commit f8326f0

Browse files
committed
Performance improvements of parser logic
1 parent 060e45b commit f8326f0

4 files changed

Lines changed: 54 additions & 48 deletions

File tree

TODO.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
This is a short but not complete list of things that need to be done or should be thought about.
44

5-
- Create PHI for merge nodes on the iDom of that node, as the
6-
PHI must be visible in all incoming nodes to be set. So the iDom
7-
of the merge node is the common ancestor of all incoming nodes,
8-
and this is the best place to create the PHI.
9-
- Compute the CFG Dominator Tree at frame level instead of graph
10-
level, as we need the dominator tree fully computed while we
11-
construct the IR graph. The frames are already there, so we can
12-
use them to compute the dominator tree and store it in the frame.
5+
- Implement correct handling of memory flow while scheduling nodes
6+
- ClassInit, Put Field / Put Status / WriteArray should not be part of the CFG and should be scheduled
7+
based on memory and data flow
8+
- Analysis context should be recorded in terms of type access / method invocation, so these
9+
references can also be resolved during analysis to build a full type hierarchy
10+

src/main/java/de/mirkosertic/metair/ir/Method.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,19 @@
1212

1313
public class Method extends TupleNode {
1414

15-
private final Map<ClassDesc, RuntimeclassReference> runtimeclassReferences;
1615
private final Map<MethodTypeDesc, MethodType> methodtypeReferences;
1716
private final Map<MethodHandleDesc, MethodHandle> methodHandles;
1817

1918
public final List<Value> methodArguments;
2019

2120
Method() {
2221
this.methodArguments = new ArrayList<>();
23-
this.runtimeclassReferences = new HashMap<>();
2422
this.methodtypeReferences = new HashMap<>();
2523
this.methodHandles = new HashMap<>();
2624

2725
registerAs("default", this);
2826
}
2927

30-
public RuntimeclassReference defineRuntimeclassReference(final ClassDesc type) {
31-
return runtimeclassReferences.computeIfAbsent(type, key -> {
32-
final RuntimeclassReference r = new RuntimeclassReference(key);
33-
r.use(Method.this, DefinedByUse.INSTANCE);
34-
return r;
35-
});
36-
}
37-
3828
public MethodType defineMethodType(final MethodTypeDesc type) {
3929
return methodtypeReferences.computeIfAbsent(type, key -> {
4030
final MethodType r = new MethodType(key);

src/main/java/de/mirkosertic/metair/ir/MethodAnalyzer.java

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import java.util.HashSet;
6060
import java.util.List;
6161
import java.util.Map;
62+
import java.util.Objects;
6263
import java.util.Optional;
6364
import java.util.Queue;
6465
import java.util.Set;
@@ -444,22 +445,30 @@ private void step2ComputeTopologicalOrder() {
444445
currentPath.add(frames[0]);
445446
final Set<Frame> marked = new HashSet<>();
446447
marked.add(frames[0]);
447-
while (!currentPath.isEmpty()) {
448-
final Frame currentNode = currentPath.peek();
449-
final List<Frame> forwardNodes = new ArrayList<>();
450-
451-
// This could be improved if we have to successor list directly...
452-
final Frame[] frames = getFrames();
453-
for (final Frame frame : frames) {
454-
// Maybe null due to unreachable statements in bytecode
455-
if (frame != null) {
456-
for (final FrameCFGEdge edge : frame.predecessors) {
457-
if (edge.fromIndex() == currentNode.elementIndex && edge.flowType() == FlowType.FORWARD) {
458-
forwardNodes.add(frame);
448+
449+
final Map<Frame, List<Frame>> successors = new HashMap<>();
450+
for (final Frame frame : frames) {
451+
if (frame != null) {
452+
453+
for (final FrameCFGEdge edge : frame.predecessors) {
454+
if (edge.flowType() == FlowType.FORWARD) {
455+
final Frame fromFrame = frames[edge.fromIndex()];
456+
if (fromFrame == null) {
457+
illegalState("fromFrame == null !, for " + edge);
459458
}
459+
final List<Frame> successorsList = successors.computeIfAbsent(fromFrame, k -> new ArrayList<>());
460+
successorsList.add(frame);
460461
}
461462
}
462463
}
464+
}
465+
466+
while (!currentPath.isEmpty()) {
467+
final Frame currentNode = currentPath.peek();
468+
469+
final List<Frame> forwardNodes;
470+
471+
forwardNodes = Objects.requireNonNullElseGet(successors.get(currentNode), ArrayList::new);
463472

464473
// We sort by index in the code model to make this reproducible...
465474
forwardNodes.sort(Comparator.comparingInt(o -> o.elementIndex));
@@ -701,7 +710,7 @@ private void step4FollowCFGAndInterpret(final CodeModel code) {
701710

702711
for (final Frame frame : topologicalOrder) {
703712
boolean isExceptionHandler = false;
704-
final CodeElement frameElement = cm.elementList().get(frame.elementIndex);
713+
final CodeElement frameElement = frame.codeElement;
705714
if (frameElement instanceof final LabelTarget labelTarget) {
706715
// Check is this is an exception handler
707716
isExceptionHandler = exceptionHandlers.contains(labelTarget.label());
@@ -1225,7 +1234,7 @@ protected void parse_INVOKEDYNAMIC(final InvokeDynamicInstruction node, final Fr
12251234
}
12261235
final List<Value> bootstrapArguments = new ArrayList<>();
12271236

1228-
final Value invokerType = ir.defineRuntimeclassReference(owner);
1237+
final Value invokerType = outgoing.control.defineRuntimeclassReference(owner);
12291238
final ClassDesc lookupOwner = ClassDesc.of(MethodHandles.Lookup.class.getName());
12301239

12311240
// Default bootstrap arguments
@@ -1256,7 +1265,7 @@ protected void parse_INVOKEDYNAMIC(final InvokeDynamicInstruction node, final Fr
12561265
break;
12571266
}
12581267
}
1259-
final Value bootstrapInvocation = new InvokeStatic(bootstrapMethod.owner(), ir.defineRuntimeclassReference(bootstrapMethod.owner()), bootstrapMethod.methodName(), bootstrapMethodType, bootstrapArguments);
1268+
final Value bootstrapInvocation = new InvokeStatic(bootstrapMethod.owner(), outgoing.control.defineRuntimeclassReference(bootstrapMethod.owner()), bootstrapMethod.methodName(), bootstrapMethodType, bootstrapArguments);
12601269

12611270
final List<Value> dynamicArguments = new ArrayList<>();
12621271
for (int i = 0; i < expectedarguments; i++) {
@@ -1635,7 +1644,7 @@ private void parse_INVOKESTATIC(final ClassDesc owner, final String methodName,
16351644
final Status outgoing = frame.copyIncomingToOutgoing();
16361645
assertMinimumStackSize(outgoing, args.length);
16371646

1638-
final RuntimeclassReference runtimeClass = ir.defineRuntimeclassReference(owner);
1647+
final RuntimeclassReference runtimeClass = outgoing.control.defineRuntimeclassReference(owner);
16391648
final ClassInitialization init = new ClassInitialization(runtimeClass);
16401649

16411650
final List<Value> arguments = new ArrayList<>();
@@ -1718,7 +1727,6 @@ protected void parse_IF_ICMP_OP(final BranchInstruction node, final Frame frame,
17181727
final If next = new If(numericCondition);
17191728

17201729
outgoing.control = outgoing.control.controlFlowsTo(next, FlowType.FORWARD);
1721-
outgoing.memory = outgoing.memory.memoryFlowsTo(next);
17221730
frame.entryPoint = next;
17231731

17241732
final int codeElementIndex = labelToIndex.get(node.target());
@@ -1742,7 +1750,6 @@ protected void parse_IF_NUMERIC_X(final BranchInstruction node, final Frame fram
17421750
final If next = new If(numericCondition);
17431751

17441752
outgoing.control = outgoing.control.controlFlowsTo(next, FlowType.FORWARD);
1745-
outgoing.memory = outgoing.memory.memoryFlowsTo(next);
17461753
frame.entryPoint = next;
17471754

17481755
final int codeElementIndex = labelToIndex.get(node.target());
@@ -1766,7 +1773,6 @@ protected void parse_IF_REFERENCETEST_X(final BranchInstruction node, final Fram
17661773
final If next = new If(referenceCondition);
17671774

17681775
outgoing.control = outgoing.control.controlFlowsTo(next, FlowType.FORWARD);
1769-
outgoing.memory = outgoing.memory.memoryFlowsTo(next);
17701776
frame.entryPoint = next;
17711777

17721778
final int codeElementIndex = labelToIndex.get(node.target());
@@ -1791,7 +1797,6 @@ protected void parse_IF_ACMP_OP(final BranchInstruction node, final Frame frame,
17911797
final If next = new If(condition);
17921798

17931799
outgoing.control = outgoing.control.controlFlowsTo(next, FlowType.FORWARD);
1794-
outgoing.memory = outgoing.memory.memoryFlowsTo(next);
17951800
frame.entryPoint = next;
17961801

17971802
final int codeElementIndex = labelToIndex.get(node.target());
@@ -1856,7 +1861,7 @@ private Value constantToValue(final Node control, final ConstantDesc constantDes
18561861
case final Long l -> control.definePrimitiveLong(l);
18571862
case final Float f -> control.definePrimitiveFloat(f);
18581863
case final Double d -> control.definePrimitiveDouble(d);
1859-
case final ClassDesc classDesc -> ir.defineRuntimeclassReference(classDesc);
1864+
case final ClassDesc classDesc -> control.defineRuntimeclassReference(classDesc);
18601865
case final MethodTypeDesc mtd -> ir.defineMethodType(mtd);
18611866
case final MethodHandleDesc mh -> ir.defineMethodHandle(mh);
18621867
case null, default -> {
@@ -1935,7 +1940,7 @@ private void parse_PUTFIELD(final ClassDesc owner, final ClassDesc fieldType, fi
19351940
private void parse_GETSTATIC(final ClassDesc owner, final ClassDesc fieldType, final String fieldName, final Frame frame) {
19361941
final Status outgoing = frame.copyIncomingToOutgoing();
19371942

1938-
final RuntimeclassReference ri = ir.defineRuntimeclassReference(owner);
1943+
final RuntimeclassReference ri = outgoing.control.defineRuntimeclassReference(owner);
19391944
final ClassInitialization init = new ClassInitialization(ri);
19401945

19411946
outgoing.memory = outgoing.memory.memoryFlowsTo(init);
@@ -1948,12 +1953,12 @@ private void parse_GETSTATIC(final ClassDesc owner, final ClassDesc fieldType, f
19481953
}
19491954

19501955
private void parse_PUTSTATIC(final ClassDesc owner, final ClassDesc fieldType, final String fieldName, final Frame frame) {
1951-
final RuntimeclassReference ri = ir.defineRuntimeclassReference(owner);
1952-
final ClassInitialization init = new ClassInitialization(ri);
1953-
19541956
final Status outgoing = frame.copyIncomingToOutgoing();
19551957
assertMinimumStackSize(outgoing, 1);
19561958

1959+
final RuntimeclassReference ri = outgoing.control.defineRuntimeclassReference(owner);
1960+
final ClassInitialization init = new ClassInitialization(ri);
1961+
19571962
outgoing.memory = outgoing.memory.memoryFlowsTo(init);
19581963

19591964
final Value v = outgoing.pop();
@@ -1965,11 +1970,11 @@ private void parse_PUTSTATIC(final ClassDesc owner, final ClassDesc fieldType, f
19651970
}
19661971

19671972
private void parse_NEW(final ClassDesc type, final Frame frame) {
1968-
final RuntimeclassReference ri = ir.defineRuntimeclassReference(type);
1969-
final ClassInitialization init = new ClassInitialization(ri);
1970-
19711973
final Status outgoing = frame.copyIncomingToOutgoing();
19721974

1975+
final RuntimeclassReference ri = outgoing.control.defineRuntimeclassReference(type);
1976+
final ClassInitialization init = new ClassInitialization(ri);
1977+
19731978
final New n = new New(init);
19741979

19751980
outgoing.control = outgoing.control.controlFlowsTo(init, FlowType.FORWARD);
@@ -2025,7 +2030,7 @@ private void parse_CHECKCAST(final ClassDesc typeToCheck, final Frame frame) {
20252030
final Status outgoing = frame.copyIncomingToOutgoing();
20262031

20272032
final Value objectToCheck = outgoing.peek();
2028-
final RuntimeclassReference expectedType = ir.defineRuntimeclassReference(typeToCheck);
2033+
final RuntimeclassReference expectedType = outgoing.control.defineRuntimeclassReference(typeToCheck);
20292034

20302035
final ClassInitialization classInit = new ClassInitialization(expectedType);
20312036
outgoing.control = outgoing.control.controlFlowsTo(classInit, FlowType.FORWARD);
@@ -2039,7 +2044,7 @@ private void parse_INSTANCEOF(final ClassDesc typeToCheck, final Frame frame) {
20392044
assertMinimumStackSize(outgoing, 1);
20402045

20412046
final Value objectToCheck = outgoing.pop();
2042-
final RuntimeclassReference expectedType = ir.defineRuntimeclassReference(typeToCheck);
2047+
final RuntimeclassReference expectedType = outgoing.control.defineRuntimeclassReference(typeToCheck);
20432048

20442049
final ClassInitialization classInit = new ClassInitialization(expectedType);
20452050
outgoing.control = outgoing.control.controlFlowsTo(classInit, FlowType.FORWARD);

src/main/java/de/mirkosertic/metair/ir/Node.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package de.mirkosertic.metair.ir;
22

3+
import java.lang.constant.ClassDesc;
34
import java.lang.constant.ConstantDesc;
45
import java.util.ArrayList;
6+
import java.util.HashMap;
57
import java.util.HashSet;
68
import java.util.List;
9+
import java.util.Map;
710
import java.util.Set;
811

912
public abstract class Node {
@@ -20,10 +23,12 @@ protected static void illegalArgument(final String message) {
2023
// Incoming uses
2124
protected final List<UseEdge> uses;
2225
protected final Set<Node> usedBy;
26+
private final Map<ClassDesc, RuntimeclassReference> runtimeclassReferences;
2327

2428
protected Node() {
2529
this.uses = new ArrayList<>();
2630
this.usedBy = new HashSet<>();
31+
this.runtimeclassReferences = new HashMap<>();
2732
}
2833

2934
public Node controlFlowsTo(final Node target, final FlowType type) {
@@ -95,6 +100,14 @@ public PHI definePHI(final ConstantDesc type) {
95100
return p;
96101
}
97102

103+
public RuntimeclassReference defineRuntimeclassReference(final ClassDesc type) {
104+
return runtimeclassReferences.computeIfAbsent(type, key -> {
105+
final RuntimeclassReference r = new RuntimeclassReference(key);
106+
r.use(this, DefinedByUse.INSTANCE);
107+
return r;
108+
});
109+
}
110+
98111
public boolean isDataUsedMultipleTimes() {
99112
int x = 0;
100113
for (final Node user : usedBy) {

0 commit comments

Comments
 (0)