GraalVM Support for Valhalla#2
Conversation
…tion fail for TestMethodHandle::test2.
…t bytecodes if Valhalla is not enabled.
…r non-assignable types.
…s a dead node for a node that was already process.
… case a placeholder is deleted at a call target occurring multiple times, the chained placeholder will occur in the call target. It shouldn't be deleted.
…erTool during unproxify.
…We shouldn't rely on an InlineType node as it can be canonicalized to constant null.
… increased to correctly pop the return address in x64.
…stamp as the original node to not lose any information. Add comment.
|
We need a way to propagate the Scalarization nodes for method arguments during inlining or inserted during merge processing in PEA through the graph. If that is not easily possible, I think we should move to the implementation of C2 and remove all changes from PEA. My solution would be: Value objects from outside a compilation unit can be encountered with the following nodes with stamp value object: They are non-larval and can be scalarized. Value objects within a compilation unit are normally virtual during PEA but will be materialized in case the constructor is not inlined. So value objects can also be scalarized if they are receiver of an Invoke node with a constructor call. If all constructors are inlined, it is virtual anyway during PEA. A special case are OSR arguments as they may still be larval. Scalarizarion nodes should be made floating and anchored at the earliest possible position. We should rely on location identities as this is not necessary and allows GVN. Value objects are immutable, so it does not make any difference when a field is read, as long as it is non-larval. As a consequence the input to a Scalarization node inserted during parsing should always be virtual. We don't need any local aliases as discussed below, as we can just update the materialized state and insert the field values. Progress can be found in the following branch: --------------------------------old stuff---------------------------------- --------------------------------old stuff---------------------------------- --------------------------------old stuff---------------------------------- As the Invoke node may get inlined we need a new node special for OSR arguments that indicates that it can be scalarized at this position. All in all this helps us to keep the compilation for economy mode fast and simple. All the magic of further optimizations happen during PEA. With local alias we mean when we do merge processing during PEA, we need to merge the local aliases as well, like we also merge object states a Scalarizeable node at the following position:
a Scalarization node for:
a InlineType node for:
PEA should do the following actions on the following nodes (new nodes e.g. for scalarization, are inserted after the currently processed node):
Scalarization node:
Scalarizeable node:
If node with IsNull node:
Invoke node with init call:
Invoke with non-scalarized value object return
LoadField node with value class field:
StoreFieldNode:
When we scalarize inputs of the nodes above we should do that before processing this node with virtualize. Further optimizations are to make Scalarization nodes part of ReadElimination phase. In case a LoadField is above, a MultiValue node can be replaced with it. --------------------------------old stuff---------------------------------- E.g. for the following code (suppose read elimination is not executed), we get the following non-optimal code. This is because the scalarized version is currently not propagated. int notInlined (ValueClass scalarizedObject) { } int lateInlined (ValueClass nonScalarizedObject) { int demo (ValueClass nonScalarizedObject) { non-optimal code optimal code should be |
| } | ||
|
|
||
| @Override | ||
| public boolean handleLoadIndexed(GraphBuilderContext b, ValueNode array, ValueNode index, GuardingNode boundsCheck, JavaKind elementKind) { |
There was a problem hiding this comment.
Create a new node similar to LoadFlatField node which encapsulates the load indexed logic, instead of already inserting null checks etc. what normally is done during lowering in JIT mode.
| * deleting this node. | ||
| */ | ||
| @NodeInfo(cycles = CYCLES_UNKNOWN, cyclesRationale = "We don't know statically how many, and which, objects we are gonna scalarize.", size = SIZE_UNKNOWN, sizeRationale = "We don't know statically how much code for which scalarization has to be generated.") | ||
| public class ScalarizationNode extends FixedWithNextNode implements MemoryAccess, Virtualizable, MultiValue, IterableNodeType, Simplifiable { |
There was a problem hiding this comment.
Make this node floating and without a memory access, scalarization of value objects accesses immutable data, which does not depend on when we load the fields, as long as the value object is non-larval. Making this node floating allows it to be GVNed and reduces code size.
There was a problem hiding this comment.
else if (node instanceof OSRLocal) {
alias node with object state where the materialized value points to the node
} else if (node instanceof ParameterNode) {
scalarize
} else if (node instanceof Invoke invoke && StampTool.isInlineType(invoke.getReceiver()) && invoke.getTargetMethod().isConstructor()) {
// NewInstance node should have been virtualized
assert getAlias(node.getReceiver()) instanceof VirtualInstanceNode;
scalarize receiver and set entries in existing object state, anchor the Scalarization node to the Invoke node
}
There was a problem hiding this comment.
if (StampTool.isNullableInlineType(node)) {
scalarize
// although the scalarized values are inserted below this node, as we process in reverse post-order we are allowed to create a virtual object and alias this node with the new virtual object.
}
There was a problem hiding this comment.
if (StampTool.isNullableInlineType(node)) {
if (getAlias(node) instanceof VirtualInstanceNode) {
set field initialized in object state
if object state becomes non-larval, scalarize
}
}
There was a problem hiding this comment.
else if (node instanceof LoadFieldNode load) {
if (getAlias(load.object()) instanceof VirtualInstanceNode) {
scalarize load.object();
call virtualize
}
} else if (node instanceof StoreFieldNode store) {
scalarize store.value()
} else if (node instanceof IfNode ifNode &&ifNode.condition() instanceof IsNullNode isNullNode && StampTool.isNullableInlineTypeNode(isNullNode.getValue())) {
scalarize isNullNode.getValue()
}
| } | ||
|
|
||
| private boolean mergeObjectStates(int resultObject, int[] sourceObjects, PartialEscapeBlockState<?>[] states, int currentScalarizationDepth, | ||
| List<JavaType> visited) { |
There was a problem hiding this comment.
no need for a visited list as in C2, at some point we will terminate, as value object fields are final so we can't land in a recursion.
No description provided.