package com.oracle.svm.hosted.c.function;
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.AddNode;
import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
import org.graalvm.compiler.nodes.extended.StateSplitProxyNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.Isolate;
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.word.WordBase;
import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.c.function.CEntryPointActions;
import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters;
import com.oracle.svm.core.c.function.CEntryPointSetup;
import com.oracle.svm.core.graal.GraalFeature;
import com.oracle.svm.core.graal.nodes.CEntryPointEnterNode;
import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode;
import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode.LeaveAction;
import com.oracle.svm.core.graal.nodes.CEntryPointPrologueBailoutNode;
import com.oracle.svm.core.graal.nodes.CEntryPointUtilityNode;
import com.oracle.svm.core.graal.nodes.CEntryPointUtilityNode.UtilityAction;
import com.oracle.svm.core.graal.nodes.DeadEndNode;
import com.oracle.svm.core.graal.nodes.ReadReservedRegister;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
@AutomaticFeature
public class CEntryPointSupport implements GraalFeature {
@Override
public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, InvocationPlugins invocationPlugins, boolean analysis, boolean hosted) {
registerEntryPointActionsPlugins(invocationPlugins);
registerCurrentIsolatePlugins(invocationPlugins);
}
private static void registerEntryPointActionsPlugins(InvocationPlugins plugins) {
Registration r = new Registration(plugins, CEntryPointActions.class);
r.register1("enterCreateIsolate", CEntryPointCreateIsolateParameters.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode parameters) {
b.addPush(JavaKind.Int, CEntryPointEnterNode.createIsolate(parameters));
return true;
}
});
r.register2("enterAttachThread", Isolate.class, boolean.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode isolate, ValueNode ensureJavaThreadNode) {
if (!ensureJavaThreadNode.isConstant()) {
b.bailout("Parameter ensureJavaThread of enterAttachThread must be a compile time constant");
}
b.addPush(JavaKind.Int, CEntryPointEnterNode.attachThread(isolate, ensureJavaThreadNode.asJavaConstant().asInt() != 0));
return true;
}
});
r.register1("enter", IsolateThread.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode thread) {
b.addPush(JavaKind.Int, CEntryPointEnterNode.enter(thread));
return true;
}
});
r.register1("enterIsolate", Isolate.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode isolate) {
b.addPush(JavaKind.Int, CEntryPointEnterNode.enterIsolate(isolate, false));
return true;
}
});
r.register1("enterIsolateFromCrashHandler", Isolate.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode isolate) {
b.addPush(JavaKind.Int, CEntryPointEnterNode.enterIsolate(isolate, true));
return true;
}
});
InvocationPlugin bailoutPlugin = new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
b.add(new CEntryPointPrologueBailoutNode(value));
return true;
}
};
r.register1("bailoutInPrologue", WordBase.class, bailoutPlugin);
r.register1("bailoutInPrologue", long.class, bailoutPlugin);
r.register1("bailoutInPrologue", double.class, bailoutPlugin);
r.register1("bailoutInPrologue", boolean.class, bailoutPlugin);
r.register0("bailoutInPrologue", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
b.add(new CEntryPointPrologueBailoutNode(null));
return true;
}
});
r.register0("leave", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
StateSplitProxyNode proxy = new StateSplitProxyNode(null);
b.add(proxy);
b.setStateAfter(proxy);
b.addPush(JavaKind.Int, new CEntryPointLeaveNode(LeaveAction.Leave));
return true;
}
});
r.register0("leaveDetachThread", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
StateSplitProxyNode proxy = new StateSplitProxyNode(null);
b.add(proxy);
b.setStateAfter(proxy);
b.addPush(JavaKind.Int, new CEntryPointLeaveNode(LeaveAction.DetachThread));
return true;
}
});
r.register0("leaveTearDownIsolate", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
StateSplitProxyNode proxy = new StateSplitProxyNode(null);
b.add(proxy);
b.setStateAfter(proxy);
b.addPush(JavaKind.Int, new CEntryPointLeaveNode(LeaveAction.TearDownIsolate));
return true;
}
});
r.register2("failFatally", int.class, CCharPointer.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg1, ValueNode arg2) {
b.add(new CEntryPointUtilityNode(UtilityAction.FailFatally, arg1, arg2));
DeadEndNode deadEndNode = b.add(new DeadEndNode());
AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(deadEndNode);
if (prevBegin != null && prevBegin.predecessor() instanceof IfNode) {
((IfNode) prevBegin.predecessor()).setProbability(prevBegin, BranchProbabilityNode.LUDICROUSLY_SLOW_PATH_PROBABILITY);
}
return true;
}
});
r.register1("isCurrentThreadAttachedTo", Isolate.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode isolate) {
b.addPush(JavaKind.Boolean, new CEntryPointUtilityNode(UtilityAction.IsAttached, isolate));
return true;
}
});
}
private static void registerCurrentIsolatePlugins(InvocationPlugins plugins) {
Registration r = new Registration(plugins, CurrentIsolate.class);
r.register0("getCurrentThread", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
if (SubstrateOptions.MultiThreaded.getValue()) {
b.addPush(JavaKind.Object, ReadReservedRegister.createReadIsolateThreadNode(b.getGraph()));
} else if (SubstrateOptions.SpawnIsolates.getValue()) {
ValueNode heapBase = b.add(ReadReservedRegister.createReadHeapBaseNode(b.getGraph()));
ConstantNode addend = b.add(ConstantNode.forIntegerKind(FrameAccess.getWordKind(), CEntryPointSetup.SINGLE_ISOLATE_TO_SINGLE_THREAD_ADDEND));
b.addPush(JavaKind.Object, new AddNode(heapBase, addend));
} else {
b.addPush(JavaKind.Object, ConstantNode.forIntegerKind(FrameAccess.getWordKind(), CEntryPointSetup.SINGLE_THREAD_SENTINEL.rawValue()));
}
return true;
}
});
r.register0("getIsolate", new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
if (SubstrateOptions.SpawnIsolates.getValue()) {
b.addPush(JavaKind.Object, ReadReservedRegister.createReadHeapBaseNode(b.getGraph()));
} else {
b.addPush(JavaKind.Object, ConstantNode.forIntegerKind(FrameAccess.getWordKind(), CEntryPointSetup.SINGLE_ISOLATE_SENTINEL.rawValue()));
}
return true;
}
});
}
}