Skip to content

Commit abd76a6

Browse files
committed
Support runtime compilation of pod factory methods.
1 parent 8a73b9c commit abd76a6

File tree

2 files changed

+146
-22
lines changed

2 files changed

+146
-22
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodFactorySubstitutionMethod.java

Lines changed: 142 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,34 @@
3232
import org.graalvm.compiler.debug.DebugContext;
3333
import org.graalvm.compiler.debug.GraalError;
3434
import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
35+
import org.graalvm.compiler.nodes.FixedNode;
36+
import org.graalvm.compiler.nodes.FixedWithNextNode;
37+
import org.graalvm.compiler.nodes.FrameState;
38+
import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
3539
import org.graalvm.compiler.nodes.StructuredGraph;
40+
import org.graalvm.compiler.nodes.UnreachableBeginNode;
41+
import org.graalvm.compiler.nodes.UnwindNode;
3642
import org.graalvm.compiler.nodes.ValueNode;
3743
import org.graalvm.compiler.nodes.calc.AddNode;
38-
import org.graalvm.compiler.nodes.java.AbstractNewObjectNode;
3944
import org.graalvm.compiler.nodes.java.ArrayLengthNode;
45+
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
4046

4147
import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
42-
import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess;
4348
import com.oracle.graal.pointsto.meta.HostedProviders;
49+
import com.oracle.svm.core.annotate.DeoptTest;
50+
import com.oracle.svm.core.graal.nodes.DeoptEntryBeginNode;
51+
import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
52+
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
4453
import com.oracle.svm.core.graal.nodes.SubstrateNewHybridInstanceNode;
54+
import com.oracle.svm.core.graal.nodes.TestDeoptimizeNode;
4555
import com.oracle.svm.core.heap.Pod;
4656
import com.oracle.svm.core.heap.Pod.RuntimeSupport.PodFactory;
57+
import com.oracle.svm.core.meta.SharedMethod;
4758
import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod;
59+
import com.oracle.svm.hosted.nodes.DeoptProxyNode;
4860
import com.oracle.svm.hosted.phases.HostedGraphKit;
4961

62+
import jdk.vm.ci.meta.JavaKind;
5063
import jdk.vm.ci.meta.ResolvedJavaField;
5164
import jdk.vm.ci.meta.ResolvedJavaMethod;
5265
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -77,32 +90,25 @@ final class PodFactorySubstitutionMethod extends CustomSubstitutionMethod {
7790
super(original);
7891
}
7992

93+
@Override
94+
public boolean allowRuntimeCompilation() {
95+
return true;
96+
}
97+
8098
@Override
8199
public int getModifiers() {
82100
return super.getModifiers() & ~Modifier.NATIVE;
83101
}
84102

85103
@Override
86104
public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) {
87-
UniverseMetaAccess metaAccess = (UniverseMetaAccess) providers.getMetaAccess();
88105
HostedGraphKit kit = new HostedGraphKit(debug, providers, method);
106+
boolean isDeoptTarget = (method instanceof SharedMethod) && ((SharedMethod) method).isDeoptTarget();
89107

90-
ValueNode[] originalArgs = kit.loadArguments(method.toParameterTypes()).toArray(ValueNode.EMPTY_ARRAY);
91-
92-
ResolvedJavaType podType = metaAccess.lookupJavaType(Pod.class);
93-
ValueNode podObject = kit.maybeCreateExplicitNullCheck(kit.createLoadField(originalArgs[0], findField(method.getDeclaringClass(), "pod")));
94-
ValueNode sizeWithoutRefMap = kit.createLoadField(podObject, findField(podType, "fieldsSizeWithoutRefMap"));
95-
ValueNode refMapArray = kit.createLoadField(podObject, findField(podType, "referenceMap"));
96-
ValueNode refMapLength = kit.append(new ArrayLengthNode(refMapArray));
97-
ValueNode hybridArrayLength = kit.append(AddNode.add(sizeWithoutRefMap, refMapLength));
98-
99-
PodFactory annotation = method.getDeclaringClass().getAnnotation(PodFactory.class);
100-
ResolvedJavaType podConcreteType = metaAccess.lookupJavaType(annotation.podClass());
101-
ResolvedJavaType elementType = metaAccess.lookupJavaType(byte.class);
102-
103-
AbstractNewObjectNode instance = kit.append(new SubstrateNewHybridInstanceNode(podConcreteType, elementType, hybridArrayLength));
104-
kit.createInvokeWithExceptionAndUnwind(Pod.class, "initInstanceRefMap", InvokeKind.Virtual, podObject, instance);
105-
108+
ResolvedJavaType factoryType = method.getDeclaringClass();
109+
PodFactory annotation = factoryType.getAnnotation(PodFactory.class);
110+
ResolvedJavaType podConcreteType = kit.getMetaAccess().lookupJavaType(annotation.podClass());
111+
ResolvedJavaType elementType = kit.getMetaAccess().lookupJavaType(byte.class);
106112
ResolvedJavaMethod targetCtor = null;
107113
for (ResolvedJavaMethod ctor : podConcreteType.getSuperclass().getDeclaredConstructors()) {
108114
if (parameterTypesMatch(method, ctor)) {
@@ -112,12 +118,126 @@ public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method,
112118
}
113119
GraalError.guarantee(targetCtor != null, "Matching constructor not found: " + getSignature());
114120

121+
/*
122+
* The graph must be safe for runtime compilation and so for compilation as a deoptimization
123+
* target. We must be careful to use values only from the frame state, so we keep the
124+
* allocated instance in a local and load it after each step during which a deopt can occur.
125+
*/
126+
int instanceLocal = method.getSignature().getParameterCount(true);
127+
int nextDeoptIndex = startMethod(kit, isDeoptTarget, 0);
128+
instantiatePod(kit, factoryType, podConcreteType, elementType, instanceLocal);
129+
if (isAnnotationPresent(DeoptTest.class)) {
130+
kit.append(new TestDeoptimizeNode());
131+
}
132+
nextDeoptIndex = initInstanceRefMap(kit, method, isDeoptTarget, nextDeoptIndex, instanceLocal);
133+
nextDeoptIndex = invokeConstructor(kit, method, isDeoptTarget, nextDeoptIndex, targetCtor, instanceLocal);
134+
135+
kit.createReturn(kit.loadLocal(instanceLocal, JavaKind.Object), JavaKind.Object);
136+
return kit.finalizeGraph();
137+
}
138+
139+
private static int startMethod(HostedGraphKit kit, boolean isDeoptTarget, int nextDeoptIndex) {
140+
if (!isDeoptTarget) {
141+
return nextDeoptIndex;
142+
}
143+
FrameState initialState = kit.getGraph().start().stateAfter();
144+
return appendDeoptWithExceptionUnwind(kit, initialState, initialState.bci, nextDeoptIndex);
145+
}
146+
147+
private static void instantiatePod(HostedGraphKit kit, ResolvedJavaType factoryType, ResolvedJavaType podConcreteType, ResolvedJavaType elementType, int instanceLocal) {
148+
ResolvedJavaType podType = kit.getMetaAccess().lookupJavaType(Pod.class);
149+
ValueNode pod = loadPod(kit, factoryType);
150+
ValueNode sizeWithoutRefMap = kit.createLoadField(pod, findField(podType, "fieldsSizeWithoutRefMap"));
151+
ValueNode refMapArray = kit.createLoadField(pod, findField(podType, "referenceMap"));
152+
ValueNode refMapLength = kit.append(new ArrayLengthNode(refMapArray));
153+
ValueNode hybridArrayLength = kit.append(AddNode.add(sizeWithoutRefMap, refMapLength));
154+
ValueNode instance = kit.append(new SubstrateNewHybridInstanceNode(podConcreteType, elementType, hybridArrayLength));
155+
kit.storeLocal(instanceLocal, JavaKind.Object, instance);
156+
}
157+
158+
private static int initInstanceRefMap(HostedGraphKit kit, ResolvedJavaMethod method, boolean isDeoptTarget, int nextDeoptIndex, int instanceLocal) {
159+
ValueNode instance = kit.loadLocal(instanceLocal, JavaKind.Object);
160+
ResolvedJavaMethod initInstanceRefMap = kit.findMethod(Pod.class, "initInstanceRefMap", false);
161+
ValueNode pod = loadPod(kit, method.getDeclaringClass());
162+
return invokeWithDeoptAndExceptionUnwind(kit, isDeoptTarget, nextDeoptIndex, initInstanceRefMap, InvokeKind.Virtual, pod, instance);
163+
}
164+
165+
private static int invokeConstructor(HostedGraphKit kit, ResolvedJavaMethod method, boolean isDeoptTarget, int nextDeoptIndex, ResolvedJavaMethod targetCtor, int instanceLocal) {
166+
ValueNode instance = kit.loadLocal(instanceLocal, JavaKind.Object);
167+
ValueNode[] originalArgs = kit.loadArguments(method.toParameterTypes()).toArray(ValueNode.EMPTY_ARRAY);
115168
ValueNode[] invokeArgs = Arrays.copyOf(originalArgs, originalArgs.length);
116169
invokeArgs[0] = instance;
117-
kit.createInvokeWithExceptionAndUnwind(targetCtor, InvokeKind.Special, kit.getFrameState(), kit.bci(), invokeArgs);
170+
return invokeWithDeoptAndExceptionUnwind(kit, isDeoptTarget, nextDeoptIndex, targetCtor, InvokeKind.Special, invokeArgs);
171+
}
118172

119-
kit.createReturn(instance, instance.getStackKind());
120-
return kit.finalizeGraph();
173+
private static ValueNode loadPod(HostedGraphKit kit, ResolvedJavaType declaringClass) {
174+
ValueNode receiver = kit.loadLocal(0, JavaKind.Object);
175+
return kit.maybeCreateExplicitNullCheck(kit.createLoadField(receiver, findField(declaringClass, "pod")));
176+
}
177+
178+
/** @see com.oracle.svm.hosted.phases.HostedGraphBuilderPhase */
179+
private static int invokeWithDeoptAndExceptionUnwind(HostedGraphKit kit, boolean isDeoptTarget, int initialNextDeoptIndex, ResolvedJavaMethod target, InvokeKind invokeKind, ValueNode... args) {
180+
InvokeWithExceptionNode invoke = kit.startInvokeWithException(target, invokeKind, kit.getFrameState(), kit.bci(), args);
181+
kit.exceptionPart();
182+
ExceptionObjectNode exception = kit.exceptionObject();
183+
184+
if (!isDeoptTarget) {
185+
kit.append(new UnwindNode(exception));
186+
kit.endInvokeWithException();
187+
return initialNextDeoptIndex;
188+
}
189+
190+
int nextDeoptIndex = initialNextDeoptIndex;
191+
192+
// Exception during invoke
193+
194+
var exceptionDeopt = kit.add(new DeoptEntryNode());
195+
exceptionDeopt.setStateAfter(exception.stateAfter().duplicate());
196+
var exceptionDeoptBegin = kit.add(new DeoptEntryBeginNode());
197+
int exceptionDeoptIndex = nextDeoptIndex++;
198+
ValueNode exceptionProxy = createDeoptProxy(kit, exceptionDeoptIndex, exceptionDeopt, exception);
199+
var unwind = kit.append(new UnwindNode(exceptionProxy));
200+
exception.setNext(exceptionDeopt);
201+
exceptionDeopt.setNext(exceptionDeoptBegin);
202+
exceptionDeoptBegin.setNext(unwind);
203+
204+
var exceptionDeoptExceptionEdge = kit.add(new UnreachableBeginNode());
205+
exceptionDeoptExceptionEdge.setNext(kit.add(new LoweredDeadEndNode()));
206+
exceptionDeopt.setExceptionEdge(exceptionDeoptExceptionEdge);
207+
208+
// Deopt entry after invoke without exception
209+
210+
kit.noExceptionPart();
211+
nextDeoptIndex = appendDeoptWithExceptionUnwind(kit, invoke.stateAfter(), invoke.stateAfter().bci, nextDeoptIndex);
212+
kit.endInvokeWithException();
213+
214+
return nextDeoptIndex;
215+
}
216+
217+
/** @see com.oracle.svm.hosted.phases.HostedGraphBuilderPhase */
218+
private static int appendDeoptWithExceptionUnwind(HostedGraphKit kit, FrameState state, int exceptionBci, int nextDeoptIndex) {
219+
var entry = kit.add(new DeoptEntryNode());
220+
entry.setStateAfter(state.duplicate());
221+
var begin = kit.append(new DeoptEntryBeginNode());
222+
((FixedWithNextNode) begin.predecessor()).setNext(entry);
223+
entry.setNext(begin);
224+
225+
ExceptionObjectNode exception = kit.add(new ExceptionObjectNode(kit.getMetaAccess()));
226+
entry.setExceptionEdge(exception);
227+
var exState = kit.getFrameState().copy();
228+
exState.clearStack();
229+
exState.push(JavaKind.Object, exception);
230+
exState.setRethrowException(true);
231+
exception.setStateAfter(exState.create(exceptionBci, exception));
232+
exception.setNext(kit.add(new UnwindNode(exception)));
233+
234+
// Ensure later nodes see values from potential deoptimization
235+
kit.getFrameState().insertProxies(value -> createDeoptProxy(kit, nextDeoptIndex, entry, value));
236+
return nextDeoptIndex + 1;
237+
}
238+
239+
private static ValueNode createDeoptProxy(HostedGraphKit kit, int nextDeoptIndex, FixedNode deoptTarget, ValueNode value) {
240+
return kit.getGraph().addOrUniqueWithInputs(DeoptProxyNode.create(value, deoptTarget, nextDeoptIndex));
121241
}
122242

123243
private static boolean parameterTypesMatch(ResolvedJavaMethod method, ResolvedJavaMethod ctor) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/PodSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.graalvm.nativeimage.hosted.Feature;
5050

5151
import com.oracle.svm.core.annotate.AutomaticFeature;
52+
import com.oracle.svm.core.annotate.DeoptTest;
5253
import com.oracle.svm.core.annotate.Hybrid;
5354
import com.oracle.svm.core.heap.Pod;
5455
import com.oracle.svm.core.heap.Pod.Builder;
@@ -234,6 +235,9 @@ private static Constructor<?> generatePodFactory(Class<?> podClass, Class<?> fac
234235
for (Method method : factoryInterface.getMethods()) {
235236
int methodAccess = ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC;
236237
var impl = writer.visitMethod(methodAccess, method.getName(), Type.getMethodDescriptor(method), null, null);
238+
if (method.isAnnotationPresent(DeoptTest.class)) {
239+
impl.visitAnnotation(Type.getDescriptor(DeoptTest.class), true).visitEnd();
240+
}
237241
impl.visitCode(); // substituted with custom graph
238242
impl.visitInsn(ACONST_NULL);
239243
impl.visitInsn(ARETURN);

0 commit comments

Comments
 (0)