Skip to content

Commit 50a8e02

Browse files
committed
[GR-45250][GR-45734] Add framework for dataflow analysis on JVMCI provided method bytecode.
1 parent 275b0d9 commit 50a8e02

File tree

18 files changed

+3241
-80
lines changed

18 files changed

+3241
-80
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java

+5
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@
269269
import java.util.function.IntConsumer;
270270
import java.util.function.Supplier;
271271

272+
import jdk.graal.compiler.nodes.graphbuilderconf.MethodParsingPlugin;
272273
import org.graalvm.collections.EconomicMap;
273274
import org.graalvm.collections.Equivalence;
274275
import org.graalvm.word.LocationIdentity;
@@ -1101,6 +1102,10 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start
11011102
graph.recordMethod(method);
11021103
}
11031104

1105+
for (MethodParsingPlugin plugin : graphBuilderConfig.getPlugins().getMethodParsingPlugins()) {
1106+
plugin.execute(method, intrinsicContext);
1107+
}
1108+
11041109
// compute the block map, setup exception handlers and get the entrypoint(s)
11051110
this.blockMap = generateBlockMap();
11061111
this.firstInstructionArray = new FixedWithNextNode[blockMap.getBlockCount()];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.java.dataflow;
26+
27+
import java.util.ArrayList;
28+
import java.util.HashMap;
29+
import java.util.Map;
30+
import java.util.Objects;
31+
import java.util.function.BiFunction;
32+
import java.util.function.Function;
33+
import java.util.function.Predicate;
34+
35+
/**
36+
* Abstract representation of a bytecode execution frame.
37+
*
38+
* @param <T> The abstract representation of values pushed and popped from the operand stack and stored in the local variable table.
39+
*/
40+
public class AbstractFrame<T> {
41+
42+
private final OperandStack<T> operandStack;
43+
private final LocalVariableTable<T> localVariableTable;
44+
45+
public AbstractFrame() {
46+
this.operandStack = new OperandStack<>();
47+
this.localVariableTable = new LocalVariableTable<>();
48+
}
49+
50+
public AbstractFrame(AbstractFrame<T> state) {
51+
this.operandStack = new OperandStack<>(state.operandStack);
52+
this.localVariableTable = new LocalVariableTable<>(state.localVariableTable);
53+
}
54+
55+
public void mergeWith(AbstractFrame<T> other, BiFunction<T, T, T> mergeFunction) throws DataFlowAnalysisException {
56+
operandStack.mergeWith(other.operandStack, mergeFunction);
57+
localVariableTable.mergeWith(other.localVariableTable, mergeFunction);
58+
}
59+
60+
public void transform(Predicate<T> filterFunction, Function<T, T> transformFunction) {
61+
operandStack.transform(filterFunction, transformFunction);
62+
localVariableTable.transform(filterFunction, transformFunction);
63+
}
64+
65+
public OperandStack<T> getOperandStack() {
66+
return operandStack;
67+
}
68+
69+
public LocalVariableTable<T> getLocalVariableTable() {
70+
return localVariableTable;
71+
}
72+
73+
public T getOperand(int depth) {
74+
try {
75+
return operandStack.peek(depth).value;
76+
} catch (DataFlowAnalysisException e) {
77+
throw new RuntimeException(e);
78+
}
79+
}
80+
81+
public T getVariable(int index) {
82+
try {
83+
return localVariableTable.get(index).value;
84+
} catch (DataFlowAnalysisException e) {
85+
throw new RuntimeException(e);
86+
}
87+
}
88+
89+
@Override
90+
public boolean equals(Object o) {
91+
if (this == o) {
92+
return true;
93+
}
94+
if (o == null || getClass() != o.getClass()) {
95+
return false;
96+
}
97+
AbstractFrame<?> that = (AbstractFrame<?>) o;
98+
return Objects.equals(operandStack, that.operandStack) && Objects.equals(localVariableTable, that.localVariableTable);
99+
}
100+
101+
@Override
102+
public int hashCode() {
103+
return Objects.hash(operandStack, localVariableTable);
104+
}
105+
106+
@Override
107+
public String toString() {
108+
return operandStack + "" + localVariableTable;
109+
}
110+
111+
public static final class OperandStack<T> {
112+
113+
private final ArrayList<SizedValue<T>> stack;
114+
115+
public OperandStack() {
116+
this.stack = new ArrayList<>();
117+
}
118+
119+
public OperandStack(OperandStack<T> stack) {
120+
this.stack = new ArrayList<>(stack.stack);
121+
}
122+
123+
public void push(SizedValue<T> value) {
124+
stack.add(value);
125+
}
126+
127+
public SizedValue<T> pop() throws DataFlowAnalysisException {
128+
if (stack.isEmpty()) {
129+
throw new DataFlowAnalysisException("Operand stack is empty");
130+
}
131+
return stack.removeLast();
132+
}
133+
134+
public SizedValue<T> peek(int depth) throws DataFlowAnalysisException {
135+
if (size() <= depth) {
136+
throw new DataFlowAnalysisException("Operand stack doesn't contain enough values");
137+
}
138+
return stack.get(stack.size() - depth - 1);
139+
}
140+
141+
public SizedValue<T> peek() throws DataFlowAnalysisException {
142+
return peek(0);
143+
}
144+
145+
public void clear() {
146+
stack.clear();
147+
}
148+
149+
public int size() {
150+
return stack.size();
151+
}
152+
153+
public void mergeWith(OperandStack<T> other, BiFunction<T, T, T> mergeFunction) throws DataFlowAnalysisException {
154+
if (size() != other.size()) {
155+
throw new DataFlowAnalysisException("Operand stack size mismatch upon merging");
156+
}
157+
for (int i = 0; i < stack.size(); i++) {
158+
SizedValue<T> thisValue = stack.get(i);
159+
SizedValue<T> thatValue = other.stack.get(i);
160+
if (thisValue.size() != thatValue.size()) {
161+
throw new DataFlowAnalysisException("Operand stack value size mismatch upon merging");
162+
}
163+
SizedValue<T> mergedValue = new SizedValue<>(mergeFunction.apply(thisValue.value(), thatValue.value()), thisValue.size());
164+
stack.set(i, mergedValue);
165+
}
166+
}
167+
168+
public void transform(Predicate<T> filterFunction, Function<T, T> transformFunction) {
169+
for (int i = 0; i < stack.size(); i++) {
170+
SizedValue<T> value = stack.get(i);
171+
if (filterFunction.test(value.value())) {
172+
stack.set(i, new SizedValue<>(transformFunction.apply(value.value()), value.size()));
173+
}
174+
}
175+
}
176+
177+
@Override
178+
public boolean equals(Object o) {
179+
if (this == o) {
180+
return true;
181+
}
182+
if (o == null || getClass() != o.getClass()) {
183+
return false;
184+
}
185+
OperandStack<?> that = (OperandStack<?>) o;
186+
return Objects.equals(stack, that.stack);
187+
}
188+
189+
@Override
190+
public int hashCode() {
191+
return Objects.hashCode(stack);
192+
}
193+
194+
@Override
195+
public String toString() {
196+
StringBuilder builder = new StringBuilder();
197+
builder.append("Operand stack:\n");
198+
for (SizedValue<T> value : stack.reversed()) {
199+
builder.append("[").append(value.value()).append("]").append("\n");
200+
}
201+
return builder.toString();
202+
}
203+
}
204+
205+
public static final class LocalVariableTable<T> {
206+
207+
private final Map<Integer, SizedValue<T>> variables;
208+
209+
public LocalVariableTable() {
210+
this.variables = new HashMap<>();
211+
}
212+
213+
public LocalVariableTable(LocalVariableTable<T> localVariableTable) {
214+
this.variables = new HashMap<>(localVariableTable.variables);
215+
}
216+
217+
public void put(int index, SizedValue<T> value) {
218+
variables.put(index, value);
219+
}
220+
221+
public SizedValue<T> get(int index) throws DataFlowAnalysisException {
222+
if (!variables.containsKey(index)) {
223+
throw new DataFlowAnalysisException("Variable table doesn't contain entry for index " + index);
224+
}
225+
return variables.get(index);
226+
}
227+
228+
public void mergeWith(LocalVariableTable<T> other, BiFunction<T, T, T> mergeFunction) {
229+
for (Map.Entry<Integer, SizedValue<T>> entry : variables.entrySet()) {
230+
SizedValue<T> thisValue = entry.getValue();
231+
SizedValue<T> thatValue = other.variables.get(entry.getKey());
232+
if (thatValue != null) {
233+
SizedValue<T> mergedValue = new SizedValue<>(mergeFunction.apply(thisValue.value(), thatValue.value()), thisValue.size());
234+
entry.setValue(mergedValue);
235+
}
236+
}
237+
}
238+
239+
public void transform(Predicate<T> filterFunction, Function<T, T> transformFunction) {
240+
for (Map.Entry<Integer, SizedValue<T>> entry : variables.entrySet()) {
241+
SizedValue<T> value = entry.getValue();
242+
if (filterFunction.test(value.value())) {
243+
entry.setValue(new SizedValue<>(transformFunction.apply(value.value()), value.size()));
244+
}
245+
}
246+
}
247+
248+
@Override
249+
public boolean equals(Object o) {
250+
if (this == o) {
251+
return true;
252+
}
253+
if (o == null || getClass() != o.getClass()) {
254+
return false;
255+
}
256+
LocalVariableTable<?> that = (LocalVariableTable<?>) o;
257+
return Objects.equals(variables, that.variables);
258+
}
259+
260+
@Override
261+
public int hashCode() {
262+
return Objects.hashCode(variables);
263+
}
264+
265+
@Override
266+
public String toString() {
267+
StringBuilder builder = new StringBuilder();
268+
builder.append("Local variable table:\n");
269+
variables.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(
270+
e -> {
271+
Integer varIndex = e.getKey();
272+
SizedValue<T> value = e.getValue();
273+
builder.append(varIndex).append(": ").append(value.value()).append("\n");
274+
}
275+
);
276+
return builder.toString();
277+
}
278+
}
279+
280+
public record SizedValue<T>(T value, Slots size) {
281+
public enum Slots {ONE_SLOT, TWO_SLOTS}
282+
283+
public static <T> SizedValue<T> wrap(T value, Slots size) {
284+
return new SizedValue<>(value, size);
285+
}
286+
}
287+
}

0 commit comments

Comments
 (0)