Skip to content

Commit

Permalink
[GR-39240] Snippets: require top-level profiles.
Browse files Browse the repository at this point in the history
PullRequest: graal/12162
  • Loading branch information
davleopo committed Sep 2, 2022
2 parents b694f0a + 02d51bc commit 6ab8465
Show file tree
Hide file tree
Showing 34 changed files with 380 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@
*/
boolean allowPartialIntrinsicArgumentMismatch() default false;

/**
* Marks a method as known to be missing injected branch probabilities. Normally snippets are
* required to have at least probabilities in their top level method but sometimes this is not
* feasible either because the code is outside of our control or there aren't clear
* probabilities that could be chosen.
*/
boolean allowMissingProbabilities() default false;

/**
* Denotes a snippet parameter representing 0 or more arguments that will be bound during
* snippet template instantiation. During snippet template creation, its value must be an array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ public static void runTest(InvariantsTool tool) {
verifiers.add(new VerifyStatelessPhases());
verifiers.add(new VerifyProfileMethodUsage());
verifiers.add(new VerifyMemoryKillCheck());
verifiers.add(new VerifySnippetProbabilities());

loadVerifiers(verifiers);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.core.test;

import java.lang.annotation.Annotation;

import org.graalvm.collections.EconomicSet;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
import org.graalvm.compiler.api.replacements.Snippet.NonNullParameter;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.CallTargetNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.ParameterNode;
import org.graalvm.compiler.nodes.ProfileData.BranchProbabilityData;
import org.graalvm.compiler.nodes.ProfileData.ProfileSource;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.extended.BranchProbabilityNode;
import org.graalvm.compiler.nodes.spi.CoreProviders;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.phases.VerifyPhase;

import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;

public class VerifySnippetProbabilities extends VerifyPhase<CoreProviders> {

private static final Object[] KNOWN_PROFILE_INTRINSICS = {
BranchProbabilityNode.class, "probability", //
BranchProbabilityNode.class, "unknownProbability", //
GraalDirectives.class, "injectBranchProbability", //
GraalDirectives.class, "injectIterationCount"};

@Override
public boolean checkContract() {
return false;
}

@Override
protected void verify(StructuredGraph graph, CoreProviders context) {
if (!graph.isSubstitution()) {
// only process snippets
return;
}
ResolvedJavaMethod method = graph.method();
Snippet snippet = method.getAnnotation(Snippet.class);
if (snippet.allowMissingProbabilities()) {
// no checks possible
return;
}
EconomicSet<ResolvedJavaMethod> knownIntrinsicMethods = EconomicSet.create();
for (int i = 0; i < KNOWN_PROFILE_INTRINSICS.length; i += 2) {
Class<?> receiverClass = (Class<?>) KNOWN_PROFILE_INTRINSICS[i];
String methodName = (String) KNOWN_PROFILE_INTRINSICS[i + 1];
ResolvedJavaType type = context.getMetaAccess().lookupJavaType(receiverClass);
for (ResolvedJavaMethod typeMethod : type.getDeclaredMethods()) {
if (typeMethod.getName().contains(methodName)) {
knownIntrinsicMethods.add(typeMethod);
}
}
}
boolean[] specialParameters = new boolean[method.getParameters().length];
Annotation[][] parameterAnnotations = graph.method().getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
for (Annotation a : parameterAnnotations[i]) {
Class<? extends Annotation> annotationType = a.annotationType();
if (annotationType == ConstantParameter.class || annotationType == NonNullParameter.class) {
specialParameters[i] = true;
}
}
}
for (Node n : graph.getNodes()) {
if (n instanceof IfNode) {
IfNode ifNode = (IfNode) n;
BranchProbabilityData profile = ifNode.getProfileData();
if (!ProfileSource.isTrusted(profile.getProfileSource())) {
if (isExplodedLoopExit(ifNode)) {
continue;
}
LogicNode ln = ifNode.condition();
boolean found = false;
outer: for (Node input : ln.inputs()) {
if (input instanceof Invoke) {
Invoke invoke = (Invoke) input;
CallTargetNode mc = invoke.callTarget();
if (mc.invokeKind().isDirect()) {
ResolvedJavaMethod targetMethod = mc.targetMethod();
if (knownIntrinsicMethods.contains(targetMethod)) {
found = true;
break;
} else if (targetMethod.getAnnotation(Fold.class) != null) {
// will fold the entire if away, not necessary to verify
found = true;
break;
} else {
// some snippets have complex semantics separated in different
// method in the same class, allow such patterns they will be
// fully inlined
if (targetMethod.getDeclaringClass().equals(method.getDeclaringClass())) {
found = true;
break;
}
// reading folded options in snippets may require some unboxing
for (Node argument : mc.arguments()) {
ValueNode pureArg = GraphUtil.unproxify((ValueNode) argument);
if (pureArg instanceof Invoke && ((Invoke) pureArg).getTargetMethod().getAnnotation(Fold.class) != null) {
found = true;
break outer;
}
}
}
} else {
// abstract / interface methods called in a snippet, most likely due
// to overriden snippet logic that folds later, ignore
found = true;
break;
}
} else if (input instanceof ParameterNode) {
if (specialParameters[((ParameterNode) input).index()]) {
// constant or non null parameter, ignore
found = true;
break;
}
}
}
if (!found) {
throw new VerificationError("Node %s in snippet %s has unknown probability %s (nsp %s) and does not call" +
"BranchProbabilityNode.probabiliy/GraalDirectives.inject<> on any of it's condition inputs.",
ifNode, graph, profile,
ifNode.getNodeSourcePosition());

}
}
}
}
}

private static boolean isExplodedLoopExit(IfNode ifNode) {
LoopExitNode lex = null;
if (ifNode.trueSuccessor() instanceof LoopExitNode) {
lex = (LoopExitNode) ifNode.trueSuccessor();
} else if (ifNode.falseSuccessor() instanceof LoopExitNode) {
lex = (LoopExitNode) ifNode.falseSuccessor();
}
if (lex != null) {
for (FixedNode f : GraphUtil.predecessorIterable(lex.loopBegin().forwardEnd())) {
if (f instanceof Invoke) {
if (((Invoke) f).callTarget().targetMethod().getName().equals("explodeLoop")) {
return true;
}
}
}
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package org.graalvm.compiler.hotspot.amd64;

import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.LIKELY_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;

import org.graalvm.compiler.api.replacements.Snippet;
Expand All @@ -48,23 +50,23 @@ public class AMD64X87MathSnippets implements Snippets {

@Snippet
public static double sin(double input) {
if (Math.abs(input) < PI_4) {
if (probability(LIKELY_PROBABILITY, Math.abs(input) < PI_4)) {
return AMD64X87MathIntrinsicNode.compute(input, UnaryOperation.SIN);
}
return callDouble1(UnaryOperation.SIN.foreignCallSignature, input);
}

@Snippet
public static double cos(double input) {
if (Math.abs(input) < PI_4) {
if (probability(LIKELY_PROBABILITY, Math.abs(input) < PI_4)) {
return AMD64X87MathIntrinsicNode.compute(input, UnaryOperation.COS);
}
return callDouble1(UnaryOperation.COS.foreignCallSignature, input);
}

@Snippet
public static double tan(double input) {
if (Math.abs(input) < PI_4) {
if (probability(LIKELY_PROBABILITY, Math.abs(input) < PI_4)) {
return AMD64X87MathIntrinsicNode.compute(input, UnaryOperation.TAN);
}
return callDouble1(UnaryOperation.TAN.foreignCallSignature, input);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public Templates(OptionValues options, Providers providers) {
public final SnippetTemplate.SnippetInfo implMultiplyToLen = snippet(BigIntegerSnippets.class, "implMultiplyToLen");
}

@Snippet
@Snippet(allowMissingProbabilities = true)
public static int[] implMultiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] zIn) {
int[] zResult = zIn;
int zLen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public Templates(OptionValues options, Providers providers) {
public final SnippetTemplate.SnippetInfo implCompressMultiBlock0 = snippet(DigestBaseSnippets.class, "implCompressMultiBlock0");
}

@Snippet
@Snippet(allowMissingProbabilities = true)
static int implCompressMultiBlock0(Object receiver, byte[] buf, int ofs, int limit,
@Snippet.ConstantParameter ResolvedJavaType receiverType,
@Snippet.ConstantParameter ResolvedJavaType md5type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,14 @@ public Object allocateArrayDynamic(Class<?> elementType,
* knownElementKind.
*/
staticAssert(knownElementKind != JavaKind.Void, "unsupported knownElementKind");
if (knownElementKind == JavaKind.Illegal && probability(SLOW_PATH_PROBABILITY, elementType == null || DynamicNewArrayNode.throwsIllegalArgumentException(elementType, voidClass))) {
DeoptimizeNode.deopt(None, RuntimeConstraint);

if (knownElementKind == JavaKind.Illegal) {
if (probability(SLOW_PATH_PROBABILITY, elementType == null)) {
DeoptimizeNode.deopt(None, RuntimeConstraint);
}
if (probability(SLOW_PATH_PROBABILITY, DynamicNewArrayNode.throwsIllegalArgumentException(elementType, voidClass))) {
DeoptimizeNode.deopt(None, RuntimeConstraint);
}
}

KlassPointer klass = loadKlassFromObject(elementType, arrayKlassOffset(INJECTED_VMCONFIG), CLASS_ARRAY_KLASS_LOCATION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ public Templates(OptionValues options, Group.Factory factory, HotSpotProviders p
this.lowerer = new HotspotG1WriteBarrierLowerer(config, factory);

HotSpotG1WriteBarrierSnippets receiver = new HotSpotG1WriteBarrierSnippets(providers.getRegisters());
g1PreWriteBarrier = snippet(G1WriteBarrierSnippets.class, "g1PreWriteBarrier", null, receiver, SATB_QUEUE_LOG_LOCATION, SATB_QUEUE_MARKING_ACTIVE_LOCATION, SATB_QUEUE_INDEX_LOCATION,
g1PreWriteBarrier = snippet(G1WriteBarrierSnippets.class, "g1PreWriteBarrier", null, receiver, SATB_QUEUE_LOG_LOCATION, SATB_QUEUE_MARKING_ACTIVE_LOCATION,
SATB_QUEUE_INDEX_LOCATION,
SATB_QUEUE_BUFFER_LOCATION);
g1ReferentReadBarrier = snippet(G1WriteBarrierSnippets.class, "g1ReferentReadBarrier", null, receiver, SATB_QUEUE_LOG_LOCATION, SATB_QUEUE_MARKING_ACTIVE_LOCATION,
SATB_QUEUE_INDEX_LOCATION, SATB_QUEUE_BUFFER_LOCATION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
package org.graalvm.compiler.hotspot.replacements;

import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.klassIsArray;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;

import org.graalvm.compiler.hotspot.word.KlassPointer;
import org.graalvm.compiler.nodes.SnippetAnchorNode;
Expand All @@ -35,7 +37,7 @@ public class HotSpotIsArraySnippets extends IsArraySnippets {
@Override
protected boolean classIsArray(Class<?> clazz) {
KlassPointer klass = ClassGetHubNode.readClass(clazz);
if (klass.isNull()) {
if (probability(NOT_FREQUENT_PROBABILITY, klass.isNull())) {
// Class for primitive type
return false;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ public static boolean klassIsArray(KlassPointer klassNonNull) {
*/
final int layoutHelper = readLayoutHelper(klassNonNull);
final int layoutHelperNeutralValue = klassLayoutHelperNeutralValue(INJECTED_VMCONFIG);
return (layoutHelper < layoutHelperNeutralValue);
return layoutHelper < layoutHelperNeutralValue;
}

public static final LocationIdentity ARRAY_KLASS_COMPONENT_MIRROR = NamedLocationIdentity.immutable("ArrayKlass::_component_mirror");
Expand Down
Loading

0 comments on commit 6ab8465

Please sign in to comment.