Search in sources :

Example 6 with SnippetReflectionProvider

use of org.graalvm.compiler.api.replacements.SnippetReflectionProvider in project graal by oracle.

the class UnsafeAutomaticSubstitutionProcessor method processUnsafeArrayBaseOffsetInvoke.

/**
 * Process call to {@link sun.misc.Unsafe#arrayBaseOffset(Class)}. The matching logic below
 * applies to the following code pattern:
 *
 * <code> static final long arrayBaseOffsets = Unsafe.getUnsafe().arrayBaseOffset(byte[].class); </code>
 */
private void processUnsafeArrayBaseOffsetInvoke(ResolvedJavaType type, Invoke unsafeArrayBaseOffsetInvoke) {
    SnippetReflectionProvider snippetReflectionProvider = GraalAccess.getOriginalSnippetReflection();
    List<String> unsuccessfulReasons = new ArrayList<>();
    Class<?> arrayClass = null;
    ValueNode arrayClassArgument = unsafeArrayBaseOffsetInvoke.callTarget().arguments().get(1);
    if (arrayClassArgument.isJavaConstant()) {
        arrayClass = snippetReflectionProvider.asObject(Class.class, arrayClassArgument.asJavaConstant());
    } else {
        unsuccessfulReasons.add("The first argument of the call to Unsafe.arrayBaseOffset() is not a constant.");
    }
    /*
         * If the value returned by the call to Unsafe.arrayBaseOffset() is stored into a field then
         * that must be the offset field.
         */
    ResolvedJavaField offsetField = extractValueStoreField(unsafeArrayBaseOffsetInvoke.asNode(), unsuccessfulReasons);
    if (arrayClass != null && offsetField != null) {
        Class<?> finalArrayClass = arrayClass;
        Supplier<ComputedValueField> supplier = () -> new ComputedValueField(offsetField, null, Kind.ArrayBaseOffset, finalArrayClass, null, true);
        if (tryAutomaticRecomputation(offsetField, Kind.ArrayBaseOffset, supplier)) {
            reportSuccessfulAutomaticRecomputation(Kind.ArrayBaseOffset, offsetField, arrayClass.getCanonicalName());
        }
    } else {
        reportUnsuccessfulAutomaticRecomputation(type, unsafeArrayBaseOffsetInvoke, Kind.ArrayBaseOffset, unsuccessfulReasons);
    }
}
Also used : SnippetReflectionProvider(org.graalvm.compiler.api.replacements.SnippetReflectionProvider) ArrayList(java.util.ArrayList) ValueNode(org.graalvm.compiler.nodes.ValueNode) ResolvedJavaField(jdk.vm.ci.meta.ResolvedJavaField)

Example 7 with SnippetReflectionProvider

use of org.graalvm.compiler.api.replacements.SnippetReflectionProvider in project graal by oracle.

the class ComputedValueField method readValue.

@Override
public JavaConstant readValue(JavaConstant receiver) {
    if (constantValue != null) {
        return constantValue;
    }
    if (kind == RecomputeFieldValue.Kind.None || kind == RecomputeFieldValue.Kind.Manual) {
        return ReadableJavaField.readFieldValue(GraalAccess.getOriginalProviders().getConstantReflection(), original, receiver);
    }
    JavaConstant result = valueCache.get(receiver);
    if (result != null) {
        return result;
    }
    SnippetReflectionProvider originalSnippetReflection = GraalAccess.getOriginalSnippetReflection();
    switch(kind) {
        case ArrayBaseOffset:
            constantValue = asConstant(ConfigurationValues.getObjectLayout().getArrayBaseOffset(JavaKind.fromJavaClass(targetClass.getComponentType())));
            return constantValue;
        case ArrayIndexScale:
            constantValue = asConstant(ConfigurationValues.getObjectLayout().getArrayIndexScale(JavaKind.fromJavaClass(targetClass.getComponentType())));
            return constantValue;
        case ArrayIndexShift:
            constantValue = asConstant(ConfigurationValues.getObjectLayout().getArrayIndexShift(JavaKind.fromJavaClass(targetClass.getComponentType())));
            return constantValue;
        case NewInstance:
            try {
                Constructor<?> constructor = targetClass.getDeclaredConstructor();
                constructor.setAccessible(true);
                result = originalSnippetReflection.forObject(constructor.newInstance());
            } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException ex) {
                throw shouldNotReachHere("Error performing field recomputation for alias " + annotated.format("%H.%n"), ex);
            }
            break;
        case AtomicFieldUpdaterOffset:
            result = computeAtomicFieldUpdaterOffset(receiver);
            break;
        case TranslateFieldOffset:
            result = translateFieldOffset(receiver, targetClass);
            break;
        case Custom:
            try {
                Constructor<?>[] constructors = targetClass.getDeclaredConstructors();
                if (constructors.length != 1) {
                    throw shouldNotReachHere("The " + CustomFieldValueComputer.class.getSimpleName() + " class " + targetClass.getName() + " has more than one constructor");
                }
                Constructor<?> constructor = constructors[0];
                Object[] constructorArgs = new Object[constructor.getParameterCount()];
                for (int i = 0; i < constructorArgs.length; i++) {
                    constructorArgs[i] = configurationValue(constructor.getParameterTypes()[i]);
                }
                constructor.setAccessible(true);
                CustomFieldValueComputer computer = (CustomFieldValueComputer) constructor.newInstance(constructorArgs);
                Object receiverValue = receiver == null ? null : originalSnippetReflection.asObject(Object.class, receiver);
                result = originalSnippetReflection.forBoxed(annotated.getJavaKind(), computer.compute(original, annotated, receiverValue));
                assert result.getJavaKind() == annotated.getJavaKind();
            } catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) {
                throw shouldNotReachHere("Error performing field recomputation for alias " + annotated.format("%H.%n"), ex);
            }
            break;
        default:
            throw shouldNotReachHere("Field recomputation of kind " + kind + " specified by alias " + annotated.format("%H.%n") + " not yet supported");
    }
    valueCache.put(receiver, result);
    return result;
}
Also used : CustomFieldValueComputer(com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer) Constructor(java.lang.reflect.Constructor) JavaConstant(jdk.vm.ci.meta.JavaConstant) InvocationTargetException(java.lang.reflect.InvocationTargetException) SnippetReflectionProvider(org.graalvm.compiler.api.replacements.SnippetReflectionProvider)

Example 8 with SnippetReflectionProvider

use of org.graalvm.compiler.api.replacements.SnippetReflectionProvider in project graal by oracle.

the class ComputedValueField method computeAtomicFieldUpdaterOffset.

private JavaConstant computeAtomicFieldUpdaterOffset(JavaConstant receiver) {
    assert !Modifier.isStatic(original.getModifiers());
    assert receiver.isNonNull();
    /*
         * Explanation: java.util.concurrent is full of objects and classes that cache the offset of
         * particular fields in the JDK. Here, Atomic<X>FieldUpdater implementation objects cache
         * the offset of some specified field. Which field? Oh...only Doug Lea knows that. We have
         * to search the declaring class for a field that has the same "unsafe" offset as the cached
         * offset in this atomic updater object.
         */
    ResolvedJavaField tclassField = findField(original.getDeclaringClass(), "tclass");
    SnippetReflectionProvider originalSnippetReflection = GraalAccess.getOriginalSnippetReflection();
    Class<?> tclass = originalSnippetReflection.asObject(Class.class, ReadableJavaField.readFieldValue(GraalAccess.getOriginalProviders().getConstantReflection(), tclassField, receiver));
    return translateFieldOffset(receiver, tclass);
}
Also used : SnippetReflectionProvider(org.graalvm.compiler.api.replacements.SnippetReflectionProvider) ResolvedJavaField(jdk.vm.ci.meta.ResolvedJavaField)

Example 9 with SnippetReflectionProvider

use of org.graalvm.compiler.api.replacements.SnippetReflectionProvider in project graal by oracle.

the class IntrinsifyMethodHandlesInvocationPlugin method processInvokeWithMethodHandle.

@SuppressWarnings("try")
private void processInvokeWithMethodHandle(GraphBuilderContext b, BytecodeProvider bytecodeProvider, ResolvedJavaMethod methodHandleMethod, ValueNode[] methodHandleArguments) {
    Plugins graphBuilderPlugins = new Plugins(((ReplacementsImpl) originalProviders.getReplacements()).getGraphBuilderPlugins());
    registerInvocationPlugins(graphBuilderPlugins.getInvocationPlugins(), bytecodeProvider);
    graphBuilderPlugins.prependParameterPlugin(new MethodHandlesParameterPlugin(methodHandleArguments));
    graphBuilderPlugins.clearInlineInvokePlugins();
    graphBuilderPlugins.prependInlineInvokePlugin(new MethodHandlesInlineInvokePlugin());
    graphBuilderPlugins.prependNodePlugin(new MethodHandlePlugin(originalProviders.getConstantReflection().getMethodHandleAccess(), false));
    /* We do all the word type rewriting because parameters to the lambda can be word types. */
    SnippetReflectionProvider originalSnippetReflection = GraalAccess.getOriginalSnippetReflection();
    WordOperationPlugin wordOperationPlugin = new WordOperationPlugin(originalSnippetReflection, new WordTypes(originalProviders.getMetaAccess(), FrameAccess.getWordKind()));
    graphBuilderPlugins.appendInlineInvokePlugin(wordOperationPlugin);
    graphBuilderPlugins.appendTypePlugin(wordOperationPlugin);
    graphBuilderPlugins.appendTypePlugin(new TrustedInterfaceTypePlugin());
    graphBuilderPlugins.appendNodePlugin(wordOperationPlugin);
    GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getSnippetDefault(graphBuilderPlugins);
    GraphBuilderPhase.Instance graphBuilder = new GraphBuilderPhase.Instance(originalProviders.getMetaAccess(), originalProviders.getStampProvider(), originalProviders.getConstantReflection(), originalProviders.getConstantFieldProvider(), graphBuilderConfig, OptimisticOptimizations.NONE, null);
    DebugContext debug = b.getDebug();
    StructuredGraph graph = new StructuredGraph.Builder(b.getOptions(), debug).method(toOriginal(methodHandleMethod)).build();
    try (DebugContext.Scope s = debug.scope("IntrinsifyMethodHandles", graph)) {
        graphBuilder.apply(graph);
        /*
             * We do not care about the improved type information from Pi nodes, so we just delete
             * them to simplify our graph.
             */
        for (PiNode pi : graph.getNodes(PiNode.TYPE)) {
            pi.replaceAndDelete(pi.object());
        }
        /*
             * Support for MethodHandle that adapt the input type to a more generic type, i.e., a
             * MethodHandle that does a dynamic type check on a parameter.
             */
        for (UnaryOpLogicNode node : graph.getNodes().filter(UnaryOpLogicNode.class).filter(v -> v instanceof IsNullNode || v instanceof InstanceOfNode)) {
            ValueNode value = node.getValue();
            if (value instanceof ParameterNode) {
                /*
                     * We just assume that the InstanceOfNode or IsNullNode are used in an If and
                     * the true-successor is actually the branch we want. If that assumption is
                     * wrong, nothing bad happens - we will just continue to report the invocation
                     * as unsupported because the updated stamp for the parameter will not simplify
                     * the graph.
                     */
                if (node instanceof InstanceOfNode) {
                    InstanceOfNode inst = (InstanceOfNode) node;
                    TypeReference typeRef = inst.type();
                    value.setStamp(new ObjectStamp(typeRef.getType(), typeRef.isExact(), !inst.allowsNull(), false));
                } else {
                    assert node instanceof IsNullNode;
                    ResolvedJavaType type = value.stamp(NodeView.DEFAULT).javaType(originalProviders.getMetaAccess());
                    value.setStamp(new ObjectStamp(type, false, /* non-null */
                    true, false));
                }
            }
        }
        /*
             * The canonicalizer converts unsafe field accesses for get/set method handles back to
             * high-level field load and store nodes.
             */
        new CanonicalizerPhase().apply(graph, new PhaseContext(originalProviders));
        for (FixedGuardNode guard : graph.getNodes(FixedGuardNode.TYPE)) {
            if (guard.next() instanceof AccessFieldNode && guard.condition() instanceof IsNullNode && guard.isNegated() && ((IsNullNode) guard.condition()).getValue() == ((AccessFieldNode) guard.next()).object()) {
                /*
                     * Method handles to load and stores fields have null checks. Remove them, since
                     * the null check is implicitly done by the field access.
                     */
                GraphUtil.removeFixedWithUnusedInputs(guard);
            }
        }
        debug.dump(DebugContext.VERY_DETAILED_LEVEL, graph, "Final intrinisfication graph");
        /*
             * After parsing (and recursive inlining during parsing), the graph must contain only
             * one invocation (and therefore only one MethodCallTargetNode), plus the parameters,
             * constants, start, and return nodes.
             */
        Node singleFunctionality = null;
        ReturnNode singleReturn = null;
        for (Node node : graph.getNodes()) {
            if (node == graph.start() || node instanceof ParameterNode || node instanceof ConstantNode || node instanceof FrameState) {
                /* Ignore the allowed framework around the nodes we care about. */
                continue;
            } else if (node instanceof Invoke) {
                /* We check the MethodCallTargetNode, so we can ignore the invoke. */
                continue;
            } else if ((node instanceof MethodCallTargetNode || node instanceof LoadFieldNode || node instanceof StoreFieldNode) && singleFunctionality == null) {
                singleFunctionality = node;
                continue;
            } else if (node instanceof ReturnNode && singleReturn == null) {
                singleReturn = (ReturnNode) node;
                continue;
            }
            throw new UnsupportedFeatureException("Invoke with MethodHandle argument could not be reduced to at most a single call: " + methodHandleMethod.format("%H.%n(%p)"));
        }
        if (singleFunctionality instanceof MethodCallTargetNode) {
            MethodCallTargetNode singleCallTarget = (MethodCallTargetNode) singleFunctionality;
            assert singleReturn.result() == null || singleReturn.result() == singleCallTarget.invoke();
            /*
                 * Replace the originalTarget with the replacementTarget. Note that the
                 * replacementTarget node belongs to a different graph than originalTarget, so we
                 * need to match parameter back to the original graph and allocate a new
                 * MethodCallTargetNode for the original graph.
                 */
            ValueNode[] replacedArguments = new ValueNode[singleCallTarget.arguments().size()];
            for (int i = 0; i < replacedArguments.length; i++) {
                replacedArguments[i] = lookup(b, methodHandleArguments, singleCallTarget.arguments().get(i));
            }
            b.handleReplacedInvoke(singleCallTarget.invokeKind(), lookup(singleCallTarget.targetMethod()), replacedArguments, false);
        } else if (singleFunctionality instanceof LoadFieldNode) {
            LoadFieldNode fieldLoad = (LoadFieldNode) singleFunctionality;
            b.addPush(b.getInvokeReturnType().getJavaKind(), LoadFieldNode.create(null, lookup(b, methodHandleArguments, fieldLoad.object()), lookup(fieldLoad.field())));
        } else if (singleFunctionality instanceof StoreFieldNode) {
            StoreFieldNode fieldStore = (StoreFieldNode) singleFunctionality;
            b.add(new StoreFieldNode(lookup(b, methodHandleArguments, fieldStore.object()), lookup(fieldStore.field()), lookup(b, methodHandleArguments, fieldStore.value())));
        } else if (singleReturn.result() != null) {
            /* Replace the invocation with he constant result. */
            JavaConstant constantResult = singleReturn.result().asJavaConstant();
            assert b.getInvokeReturnType().getJavaKind() == constantResult.getJavaKind();
            b.addPush(constantResult.getJavaKind(), ConstantNode.forConstant(lookup(constantResult), universeProviders.getMetaAccess()));
        } else {
            /* No invoke and no return value, so nothing to do. */
            assert b.getInvokeReturnType().getJavaKind() == JavaKind.Void;
        }
    } catch (Throwable ex) {
        throw debug.handle(ex);
    }
}
Also used : ObjectStamp(org.graalvm.compiler.core.common.type.ObjectStamp) MethodCallTargetNode(org.graalvm.compiler.nodes.java.MethodCallTargetNode) AccessFieldNode(org.graalvm.compiler.nodes.java.AccessFieldNode) ReturnNode(org.graalvm.compiler.nodes.ReturnNode) FixedGuardNode(org.graalvm.compiler.nodes.FixedGuardNode) PiNode(org.graalvm.compiler.nodes.PiNode) UnaryOpLogicNode(org.graalvm.compiler.nodes.UnaryOpLogicNode) ValueNode(org.graalvm.compiler.nodes.ValueNode) IsNullNode(org.graalvm.compiler.nodes.calc.IsNullNode) FloatingNode(org.graalvm.compiler.nodes.calc.FloatingNode) LoadFieldNode(org.graalvm.compiler.nodes.java.LoadFieldNode) ConstantNode(org.graalvm.compiler.nodes.ConstantNode) InstanceOfNode(org.graalvm.compiler.nodes.java.InstanceOfNode) ParameterNode(org.graalvm.compiler.nodes.ParameterNode) StoreFieldNode(org.graalvm.compiler.nodes.java.StoreFieldNode) Node(org.graalvm.compiler.graph.Node) WordTypes(org.graalvm.compiler.word.WordTypes) JavaConstant(jdk.vm.ci.meta.JavaConstant) PiNode(org.graalvm.compiler.nodes.PiNode) FrameState(org.graalvm.compiler.nodes.FrameState) UnaryOpLogicNode(org.graalvm.compiler.nodes.UnaryOpLogicNode) ResolvedJavaType(jdk.vm.ci.meta.ResolvedJavaType) Invoke(org.graalvm.compiler.nodes.Invoke) PhaseContext(org.graalvm.compiler.phases.tiers.PhaseContext) SnippetReflectionProvider(org.graalvm.compiler.api.replacements.SnippetReflectionProvider) ConstantNode(org.graalvm.compiler.nodes.ConstantNode) StructuredGraph(org.graalvm.compiler.nodes.StructuredGraph) ParameterNode(org.graalvm.compiler.nodes.ParameterNode) TypeReference(org.graalvm.compiler.core.common.type.TypeReference) GraphBuilderPhase(org.graalvm.compiler.java.GraphBuilderPhase) InvocationPlugins(org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins) Plugins(org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins) StoreFieldNode(org.graalvm.compiler.nodes.java.StoreFieldNode) UnsupportedFeatureException(com.oracle.graal.pointsto.constraints.UnsupportedFeatureException) GraphBuilderConfiguration(org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration) DebugContext(org.graalvm.compiler.debug.DebugContext) LoadFieldNode(org.graalvm.compiler.nodes.java.LoadFieldNode) TrustedInterfaceTypePlugin(com.oracle.svm.core.graal.phases.TrustedInterfaceTypePlugin) FixedGuardNode(org.graalvm.compiler.nodes.FixedGuardNode) ReturnNode(org.graalvm.compiler.nodes.ReturnNode) MethodCallTargetNode(org.graalvm.compiler.nodes.java.MethodCallTargetNode) IsNullNode(org.graalvm.compiler.nodes.calc.IsNullNode) AccessFieldNode(org.graalvm.compiler.nodes.java.AccessFieldNode) ValueNode(org.graalvm.compiler.nodes.ValueNode) CanonicalizerPhase(org.graalvm.compiler.phases.common.CanonicalizerPhase) InstanceOfNode(org.graalvm.compiler.nodes.java.InstanceOfNode) MethodHandlePlugin(org.graalvm.compiler.replacements.MethodHandlePlugin) WordOperationPlugin(org.graalvm.compiler.word.WordOperationPlugin)

Example 10 with SnippetReflectionProvider

use of org.graalvm.compiler.api.replacements.SnippetReflectionProvider in project graal by oracle.

the class UnsafeAutomaticSubstitutionProcessor method processUnsafeArrayIndexScaleInvoke.

/**
 * Process call to {@link sun.misc.Unsafe#arrayIndexScale(Class)}. The matching logic below
 * applies to the following code pattern:
 *
 * <code> static final long byteArrayIndexScale = Unsafe.getUnsafe().arrayIndexScale(byte[].class); </code>
 */
private void processUnsafeArrayIndexScaleInvoke(ResolvedJavaType type, Invoke unsafeArrayIndexScale, StructuredGraph clinitGraph) {
    SnippetReflectionProvider snippetReflectionProvider = GraalAccess.getOriginalSnippetReflection();
    List<String> unsuccessfulReasons = new ArrayList<>();
    Class<?> arrayClass = null;
    ValueNode arrayClassArgument = unsafeArrayIndexScale.callTarget().arguments().get(1);
    if (arrayClassArgument.isJavaConstant()) {
        arrayClass = snippetReflectionProvider.asObject(Class.class, arrayClassArgument.asJavaConstant());
    } else {
        unsuccessfulReasons.add("The first argument of the call to Unsafe.arrayIndexScale() is not a constant.");
    }
    /*
         * If the value returned by the call to Unsafe.unsafeArrayIndexScale() is stored into a
         * field then that must be the offset field.
         */
    ResolvedJavaField indexScaleField = extractValueStoreField(unsafeArrayIndexScale.asNode(), unsuccessfulReasons);
    boolean indexScaleComputed = false;
    boolean indexShiftComputed = false;
    if (arrayClass != null) {
        if (indexScaleField != null) {
            Class<?> finalArrayClass = arrayClass;
            Supplier<ComputedValueField> supplier = () -> new ComputedValueField(indexScaleField, null, Kind.ArrayIndexScale, finalArrayClass, null, true);
            if (tryAutomaticRecomputation(indexScaleField, Kind.ArrayIndexScale, supplier)) {
                reportSuccessfulAutomaticRecomputation(Kind.ArrayIndexScale, indexScaleField, arrayClass.getCanonicalName());
                indexScaleComputed = true;
                /* Try substitution for the array index shift computation if present. */
                indexShiftComputed = processArrayIndexShiftFromField(type, indexScaleField, arrayClass, clinitGraph);
            }
        } else {
            /*
                 * The index scale is not stored into a field, it might be used to compute the index
                 * shift.
                 */
            indexShiftComputed = processArrayIndexShiftFromLocal(type, unsafeArrayIndexScale, arrayClass);
        }
    }
    if (!indexScaleComputed && !indexShiftComputed) {
        reportUnsuccessfulAutomaticRecomputation(type, unsafeArrayIndexScale, Kind.ArrayIndexScale, unsuccessfulReasons);
    }
}
Also used : SnippetReflectionProvider(org.graalvm.compiler.api.replacements.SnippetReflectionProvider) ArrayList(java.util.ArrayList) ValueNode(org.graalvm.compiler.nodes.ValueNode) ResolvedJavaField(jdk.vm.ci.meta.ResolvedJavaField)

Aggregations

SnippetReflectionProvider (org.graalvm.compiler.api.replacements.SnippetReflectionProvider)17 ResolvedJavaField (jdk.vm.ci.meta.ResolvedJavaField)5 ValueNode (org.graalvm.compiler.nodes.ValueNode)5 StructuredGraph (org.graalvm.compiler.nodes.StructuredGraph)4 ArrayList (java.util.ArrayList)3 WordTypes (org.graalvm.compiler.word.WordTypes)3 UnsupportedFeatureException (com.oracle.graal.pointsto.constraints.UnsupportedFeatureException)2 AnalysisMethod (com.oracle.graal.pointsto.meta.AnalysisMethod)2 HostedProviders (com.oracle.graal.pointsto.meta.HostedProviders)2 SubstrateForeignCallsProvider (com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider)2 SubstrateLoweringProvider (com.oracle.svm.core.graal.meta.SubstrateLoweringProvider)2 SubstrateSnippetReflectionProvider (com.oracle.svm.core.graal.meta.SubstrateSnippetReflectionProvider)2 SubstrateStampProvider (com.oracle.svm.core.graal.meta.SubstrateStampProvider)2 GraalFeature (com.oracle.svm.graal.hosted.GraalFeature)2 InvocationTargetException (java.lang.reflect.InvocationTargetException)2 JavaConstant (jdk.vm.ci.meta.JavaConstant)2 MetaAccessProvider (jdk.vm.ci.meta.MetaAccessProvider)2 ForeignCallsProvider (org.graalvm.compiler.core.common.spi.ForeignCallsProvider)2 DebugContext (org.graalvm.compiler.debug.DebugContext)2 GraphBuilderPhase (org.graalvm.compiler.java.GraphBuilderPhase)2