50
50
import jdk .graal .compiler .nodes .FixedWithNextNode ;
51
51
import jdk .graal .compiler .nodes .FrameState ;
52
52
import jdk .graal .compiler .nodes .GraphState ;
53
+ import jdk .graal .compiler .nodes .Invoke ;
53
54
import jdk .graal .compiler .nodes .NodeView ;
55
+ import jdk .graal .compiler .nodes .ReturnNode ;
54
56
import jdk .graal .compiler .nodes .StructuredGraph ;
55
57
import jdk .graal .compiler .nodes .ValueNode ;
56
58
import jdk .graal .compiler .nodes .ValuePhiNode ;
57
59
import jdk .graal .compiler .nodes .ValueProxyNode ;
58
60
import jdk .graal .compiler .nodes .calc .MinMaxNode ;
59
61
import jdk .graal .compiler .nodes .extended .FixedValueAnchorNode ;
62
+ import jdk .graal .compiler .nodes .java .MethodCallTargetNode ;
60
63
import jdk .graal .compiler .nodes .spi .CoreProviders ;
61
64
import jdk .graal .compiler .nodes .spi .SimplifierTool ;
62
65
import jdk .graal .compiler .nodes .type .StampTool ;
72
75
import jdk .graal .compiler .vector .nodes .simd .SimdStamp ;
73
76
import jdk .graal .compiler .vector .replacements .vectorapi .nodes .VectorAPIMacroNode ;
74
77
import jdk .graal .compiler .vector .replacements .vectorapi .nodes .VectorAPISinkNode ;
78
+ import jdk .vm .ci .meta .JavaKind ;
75
79
import jdk .vm .ci .meta .ResolvedJavaType ;
80
+ import org .graalvm .collections .Equivalence ;
76
81
77
82
/**
78
83
* Expands {@link VectorAPIMacroNode}s to SIMD operations if they are supported by the target
@@ -167,6 +172,36 @@ private static class ConnectedComponent {
167
172
private ArrayList <ConstantNode > constants ;
168
173
/** Unboxable vector values as inputs to other nodes in this component. */
169
174
private ArrayList <ValueNode > unboxes ;
175
+
176
+ /**
177
+ * Record locations at which nodes in this component need to be materialized due to
178
+ * unexpected usage. This cuts the usages off the component, allows the connected component
179
+ * to be expanded. For example, a macro node {@code v} is used as an argument to a call:
180
+ *
181
+ * <pre>
182
+ * {@code
183
+ * IntVector v;
184
+ * consume(v);
185
+ * }
186
+ * </pre>
187
+ *
188
+ * In general, the escape of {@code v} disallows its expansion. However, if it seems that
189
+ * the call {@code consume(v)} happens rarely, we may manually box the vector instance, so
190
+ * that the pseudocode snippet changes to:
191
+ *
192
+ * <pre>
193
+ * {@code
194
+ * IntVector v;
195
+ * IntVector v1 = new IntVector;
196
+ * v.intoArray(v1.payload);
197
+ * consume(v1);
198
+ * }
199
+ * </pre>
200
+ *
201
+ * This disconnects {@code v} from the call to {@code consume}, allows it to be expanded.
202
+ */
203
+ private ArrayList <VectorAPIMacroNode > boxes ;
204
+
170
205
/**
171
206
* A map from each node in this component to the corresponding SIMD stamp. The keys of this
172
207
* map can be used to iterate over all the nodes in this component, i.e., the union of
@@ -187,6 +222,7 @@ private static class ConnectedComponent {
187
222
this .proxies = new ArrayList <>();
188
223
this .constants = new ArrayList <>();
189
224
this .unboxes = new ArrayList <>();
225
+ this .boxes = new ArrayList <>();
190
226
this .simdStamps = EconomicMap .create ();
191
227
}
192
228
@@ -400,6 +436,11 @@ private static Iterable<ConnectedComponent> buildConnectedComponents(StructuredG
400
436
* on the heap.
401
437
*/
402
438
continue ;
439
+ } else if (node instanceof VectorAPIMacroNode macro && shouldBox (macro , usage , context )) {
440
+ // Manually box the vector node to disconnect the unexpected usage from the
441
+ // ConnectedComponent
442
+ component .boxes .add (macro );
443
+ continue ;
403
444
} else {
404
445
/*
405
446
* Some other usage, this would force materialization of the vector object.
@@ -495,6 +536,8 @@ private static void expandComponents(StructuredGraph graph, HighTierContext cont
495
536
496
537
/* Expand unboxing operations that are inputs to the component. */
497
538
unboxComponentInputs (graph , context , component , expanded );
539
+ /* Box the nodes that escape to make the component expandable */
540
+ boxComponentOutputs (graph , context , component , expanded , vectorArch );
498
541
/* Expand, starting from sinks and recursing upwards through inputs. */
499
542
for (VectorAPISinkNode sink : component .sinks ) {
500
543
expandRecursivelyUpwards (graph , context , expanded , component .simdStamps , sink , vectorArch );
@@ -554,6 +597,79 @@ private static void unboxComponentInputs(StructuredGraph graph, CoreProviders pr
554
597
component .unboxes .clear ();
555
598
}
556
599
600
+ private static void boxComponentOutputs (StructuredGraph graph , CoreProviders providers , ConnectedComponent component , NodeMap <ValueNode > expanded , VectorArchitecture vectorArch ) {
601
+ GraalError .guarantee (component .canExpand , "should only place box nodes once we know the component can expand" );
602
+ for (VectorAPIMacroNode macro : component .boxes ) {
603
+ expandRecursivelyUpwards (graph , providers , expanded , component .simdStamps , macro , vectorArch );
604
+ ValueNode expandedDef = expanded .get (macro );
605
+ GraalError .guarantee (expandedDef != null , "must be expanded %s" , macro );
606
+ ResolvedJavaType boxType = macro .stamp (NodeView .DEFAULT ).javaType (providers .getMetaAccess ());
607
+ EconomicSet <Node > uses = EconomicSet .create (Equivalence .DEFAULT );
608
+ uses .addAll (macro .usages ());
609
+ for (Node use : uses ) {
610
+ // For a use, this operation might replace it with a clone that has the macro input
611
+ // fixed. As a result, we need to collect the uses here instead of recording them
612
+ // while constructing the connected components.
613
+ if (!shouldBox (macro , use , providers )) {
614
+ continue ;
615
+ }
616
+
617
+ if (use instanceof FixedNode successor ) {
618
+ // If the usage is a FixedNode, box the macro there and replace the macro input
619
+ // with the allocated box instance
620
+ ValueNode boxedMacro = VectorAPIBoxingUtils .boxVector (boxType , successor , expandedDef , providers );
621
+ successor .replaceAllInputs (macro , boxedMacro );
622
+ } else {
623
+ // The pattern here looks similar to macro -> MethodCallTarget -> Invoke. As a
624
+ // result, we need to clone a MethodCallTarget for each of its Invoke output,
625
+ // then replace the macro in the cloned MethodCallTarget with a boxed vector
626
+ // instance.
627
+ GraalError .guarantee (use instanceof MethodCallTargetNode , "unexpected use %s" , use );
628
+ EconomicSet <Node > successors = EconomicSet .create ();
629
+ successors .addAll (use .usages ());
630
+ for (Node successor : successors ) {
631
+ FixedNode fixedSuccessor = (FixedNode ) successor ;
632
+ ValueNode useCloned = (ValueNode ) use .copyWithInputs ();
633
+ ValueNode boxedMacro = VectorAPIBoxingUtils .boxVector (boxType , fixedSuccessor , expandedDef , providers );
634
+ useCloned .replaceAllInputs (macro , boxedMacro );
635
+ useCloned = graph .addOrUniqueWithInputs (useCloned );
636
+ fixedSuccessor .replaceAllInputs (use , useCloned );
637
+ }
638
+ }
639
+ }
640
+
641
+ graph .getDebug ().dump (DebugContext .VERY_DETAILED_LEVEL , graph , "after boxing %s for %s" , macro , component );
642
+ }
643
+
644
+ graph .getDebug ().dump (DebugContext .DETAILED_LEVEL , graph , "after boxing all escapes of %s" , component );
645
+ }
646
+
647
+ /**
648
+ * If a {@link VectorAPIMacroNode} has a usage {@code use} that cannot be expanded. We try to
649
+ * see if it is profitable to box {@code macro} at {@code use}, disconnecting {@code use} from
650
+ * the {@link ConnectedComponent}, allowing it to be expanded.
651
+ */
652
+ private static boolean shouldBox (VectorAPIMacroNode macro , Node use , CoreProviders providers ) {
653
+ if (use instanceof MethodCallTargetNode method ) {
654
+ /*
655
+ * If a macro node is used in a method call that appears to be uncommon, we can manually
656
+ * box the vector, disconnecting the method call from the connected component. The
657
+ * conservative heuristics now is that a method returning a throwable is likely
658
+ * uncommon. Revisit and expand the heuristic if the need arises.
659
+ */
660
+ ResolvedJavaType throwableType = providers .getMetaAccess ().lookupJavaType (Throwable .class );
661
+ if (method .returnKind () == JavaKind .Object && throwableType .isAssignableFrom (method .returnStamp ().getTrustedStamp ().javaType (providers .getMetaAccess ())) &&
662
+ method .usages ().filter (n -> !(n instanceof Invoke )).isEmpty ()) {
663
+ return true ;
664
+ }
665
+ } else if (use instanceof ReturnNode returnNode && returnNode .result ().equals (macro )) {
666
+ // If a macro node is returned, we can also box the vector there
667
+ return true ;
668
+ }
669
+
670
+ return false ;
671
+ }
672
+
557
673
private static void anchorAndUnboxInput (StructuredGraph graph , CoreProviders providers , ValueNode usage , ValueNode unboxableInput , FixedNode insertionPoint , NodeMap <ValueNode > expanded ) {
558
674
anchorAndUnboxInput (graph , providers , usage , unboxableInput , insertionPoint , expanded , -1 );
559
675
}
0 commit comments