Skip to content

Commit

Permalink
Format stable lambda names by parsing the bytecode directly.
Browse files Browse the repository at this point in the history
  • Loading branch information
pecimuth authored and Christian Wimmer committed Jul 25, 2024
1 parent 02d7ccc commit 42220b9
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 357 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@
import jdk.graal.compiler.hotspot.HotSpotGraphBuilderPhase;
import jdk.graal.compiler.java.BytecodeParser;
import jdk.graal.compiler.java.GraphBuilderPhase;
import jdk.graal.compiler.java.StableMethodNameFormatter;
import jdk.graal.compiler.lir.asm.CompilationResultBuilderFactory;
import jdk.graal.compiler.lir.phases.LIRSuites;
import jdk.graal.compiler.loop.phases.ConvertDeoptimizeToGuardPhase;
Expand Down Expand Up @@ -511,7 +510,7 @@ protected void assertEquals(StructuredGraph expected,
boolean checkConstants,
boolean addGaphsToDebugContext) {
DebugContext debug = actual.getDebug();
actual.getOptimizationLog().emit(new StableMethodNameFormatter(getDefaultGraphBuilderPhase(), getProviders(), debug));
actual.getOptimizationLog().emit();

String expectedString = getCanonicalGraphString(expected, excludeVirtual, checkConstants);
String actualString = getCanonicalGraphString(actual, excludeVirtual, checkConstants);
Expand Down Expand Up @@ -1271,7 +1270,7 @@ protected CompilationResult compile(ResolvedJavaMethod installedCodeOwner, Struc
Request<CompilationResult> request = new Request<>(graphToCompile, installedCodeOwner, getProviders(), getBackend(), getDefaultGraphBuilderSuite(), getOptimisticOptimizations(),
graphToCompile.getProfilingInfo(), suites, createLIRSuites(options), compilationResult, CompilationResultBuilderFactory.Default, null, null, true);
CompilationResult result = GraalCompiler.compile(request);
graphToCompile.getOptimizationLog().emit(new StableMethodNameFormatter(getDefaultGraphBuilderPhase(), getProviders(), graphToCompile.getDebug()));
graphToCompile.getOptimizationLog().emit();
return result;
} catch (Throwable e) {
throw debug.handle(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,47 +31,27 @@
import static org.junit.Assert.fail;

import java.math.BigInteger;
import java.util.Collections;

import org.junit.Test;
import org.objectweb.asm.Type;

import jdk.graal.compiler.api.runtime.GraalJVMCICompiler;
import jdk.graal.compiler.core.test.TestGraphBuilderPhase;
import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.debug.DebugContext.Builder;
import jdk.graal.compiler.hotspot.meta.HotSpotJITClassInitializationPlugin;
import jdk.graal.compiler.java.LambdaUtils;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.phases.OptimisticOptimizations;
import jdk.graal.compiler.phases.util.Providers;
import jdk.graal.compiler.runtime.RuntimeProvider;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.runtime.JVMCI;

public class LambdaStableNameTest {
private String findStableLambdaName(ResolvedJavaType type) {
OptionValues options = new OptionValues(OptionValues.newOptionMap());
DebugContext debug = new Builder(options, Collections.emptyList()).build();
GraalJVMCICompiler compiler = (GraalJVMCICompiler) JVMCI.getRuntime().getCompiler();
Providers providers = compiler.getGraalRuntime().getCapability(RuntimeProvider.class).getHostBackend().getProviders();
final HotSpotJITClassInitializationPlugin initializationPlugin = new HotSpotJITClassInitializationPlugin();
return LambdaUtils.findStableLambdaName(initializationPlugin, providers, type, options, debug, this,
config -> new TestGraphBuilderPhase.Instance(providers, config, OptimisticOptimizations.NONE, null));
}

@Test
public void checkStableLamdaNameForRunnableAndAutoCloseable() {
String s = "a string";
Runnable r = s::hashCode;
ResolvedJavaType rType = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(r.getClass());

String name = findStableLambdaName(rType);
String name = LambdaUtils.findStableLambdaName(rType);
assertLambdaName(name);

AutoCloseable ac = s::hashCode;
ResolvedJavaType acType = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(ac.getClass());
String acName = findStableLambdaName(acType);
String acName = LambdaUtils.findStableLambdaName(acType);
assertEquals("Both stable lambda names are the same as they reference the same method", name, acName);

String myName = Type.getInternalName(getClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ protected HotSpotCompilationRequestResult runCompilation(DebugContext debug, Hot
}
}

ProfileReplaySupport result = ProfileReplaySupport.profileReplayPrologue(debug, graalRuntime.getHostProviders(), entryBCI, method, profileProvider, profileSaveFilter);
ProfileReplaySupport result = ProfileReplaySupport.profileReplayPrologue(debug, entryBCI, method, profileProvider, profileSaveFilter);
try {
return compilation.run(debug);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public CompilationResult compileHelper(CompilationResultBuilderFactory crbf, Com
null,
this,
true));
graph.getOptimizationLog().emit(new HotSpotStableMethodNameFormatter(providers, graph.getDebug()));
graph.getOptimizationLog().emit();
if (!isOSR) {
profilingInfo.setCompilerIRSize(StructuredGraph.class, graph.getNodeCount());
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.options.OptionType;
import jdk.graal.compiler.phases.schedule.SchedulePhase;
import jdk.graal.compiler.phases.util.Providers;
import jdk.graal.compiler.util.json.JsonParser;
import jdk.graal.compiler.util.json.JsonPrettyWriter;
import jdk.vm.ci.meta.ResolvedJavaMethod;
Expand Down Expand Up @@ -153,11 +152,11 @@ public Boolean getExpectedResult() {
* either throw an exception or emit a warning in this case.
* </p>
*/
public static ProfileReplaySupport profileReplayPrologue(DebugContext debug, Providers providers, int entryBCI, ResolvedJavaMethod method,
public static ProfileReplaySupport profileReplayPrologue(DebugContext debug, int entryBCI, ResolvedJavaMethod method,
StableProfileProvider profileProvider, TypeFilter profileSaveFilter) {
if (SaveProfiles.getValue(debug.getOptions()) || LoadProfiles.getValue(debug.getOptions()) != null) {
LambdaNameFormatter lambdaNameFormatter = new LambdaNameFormatter() {
private final StableMethodNameFormatter stableFormatter = new HotSpotStableMethodNameFormatter(providers, debug, true);
private final StableMethodNameFormatter stableFormatter = new StableMethodNameFormatter(true);

@Override
public boolean isLambda(ResolvedJavaMethod m) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,23 @@
*/
package jdk.graal.compiler.java;

import static jdk.graal.compiler.bytecode.Bytecodes.INVOKEDYNAMIC;
import static jdk.graal.compiler.bytecode.Bytecodes.INVOKEINTERFACE;
import static jdk.graal.compiler.bytecode.Bytecodes.INVOKESPECIAL;
import static jdk.graal.compiler.bytecode.Bytecodes.INVOKESTATIC;
import static jdk.graal.compiler.bytecode.Bytecodes.INVOKEVIRTUAL;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.nodes.Invoke;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.graphbuilderconf.ClassInitializationPlugin;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.phases.OptimisticOptimizations;
import jdk.graal.compiler.phases.tiers.HighTierContext;
import jdk.graal.compiler.phases.util.Providers;

import jdk.graal.compiler.bytecode.BytecodeStream;
import jdk.graal.compiler.util.Digest;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.JavaMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;

Expand All @@ -57,12 +53,6 @@ public final class LambdaUtils {
public static final String SERIALIZATION_TEST_LAMBDA_CLASS_SPLIT_PATTERN = "\\$\\$Lambda";
public static final String ADDRESS_PREFIX = ".0x";

private static GraphBuilderConfiguration buildLambdaParserConfig(ClassInitializationPlugin cip) {
GraphBuilderConfiguration.Plugins plugins = new GraphBuilderConfiguration.Plugins(new InvocationPlugins());
plugins.setClassInitializationPlugin(cip);
return GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
}

private LambdaUtils() {
}

Expand All @@ -82,35 +72,17 @@ private LambdaUtils() {
* parameters are Object types) and serves as a wrapper that casts parameters to specialized
* types and calls an original method.
*
* @param cip plugin to
* {@link ClassInitializationPlugin#loadReferencedType(GraphBuilderContext, jdk.vm.ci.meta.ConstantPool, int, int)
* load} new types
* @param providers providers to use when processing the lambda code
* @param lambdaType the lambda type to analyze
* @param options options to use when analyzing the lamda code
* @param debug debug context to nest the analysis into
* @param ctx context to use for the
* {@link DebugContext#scope(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)}
* @return stable name for the lambda class
*/
@SuppressWarnings("try")
public static String findStableLambdaName(ClassInitializationPlugin cip, Providers providers, ResolvedJavaType lambdaType, OptionValues options, DebugContext debug, Object ctx,
Function<GraphBuilderConfiguration, GraphBuilderPhase.Instance> graphBuilderSupplier)
throws RuntimeException {
public static String findStableLambdaName(ResolvedJavaType lambdaType) {
ResolvedJavaMethod[] lambdaProxyMethods = Arrays.stream(lambdaType.getDeclaredMethods(false)).filter(m -> !m.isBridge() && m.isPublic()).toArray(ResolvedJavaMethod[]::new);
/*
* Take only the first method to build a graph, because the graph for all other methods will
* be the same.
* Take only the first method to find invoked methods, because the result would be the same
* for all other methods.
*/
StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(lambdaProxyMethods[0]).build();
try (DebugContext.Scope ignored = debug.scope("Lambda target method analysis", graph, lambdaType, ctx)) {
GraphBuilderPhase.Instance lambdaParserPhase = graphBuilderSupplier.apply(buildLambdaParserConfig(cip));
HighTierContext context = new HighTierContext(providers, null, OptimisticOptimizations.NONE);
lambdaParserPhase.apply(graph, context);
} catch (Throwable e) {
throw debug.handle(e);
}
List<ResolvedJavaMethod> invokedMethods = StreamSupport.stream(graph.getInvokes().spliterator(), false).map(Invoke::getTargetMethod).collect(Collectors.toList());
List<JavaMethod> invokedMethods = findInvokedMethods(lambdaProxyMethods[0]);
if (invokedMethods.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append("Lambda without a target invoke: ").append(lambdaType.toClassName());
Expand All @@ -122,6 +94,37 @@ public static String findStableLambdaName(ClassInitializationPlugin cip, Provide
return createStableLambdaName(lambdaType, invokedMethods);
}

/**
* Finds the methods invoked in the bytecode of the provided method.
*
* @param method the method whose bytecode is parsed
* @return the list of invoked methods
*/
public static List<JavaMethod> findInvokedMethods(ResolvedJavaMethod method) {
ConstantPool constantPool = method.getConstantPool();
List<JavaMethod> invokedMethods = new ArrayList<>();
for (BytecodeStream stream = new BytecodeStream(method.getCode()); stream.currentBCI() < stream.endBCI(); stream.next()) {
int opcode = stream.currentBC();
int cpi;
switch (opcode) {
case INVOKEVIRTUAL: // fall through
case INVOKESPECIAL: // fall through
case INVOKESTATIC: // fall through
case INVOKEINTERFACE:
cpi = stream.readCPI();
invokedMethods.add(constantPool.lookupMethod(cpi, opcode, method));
break;
case INVOKEDYNAMIC:
cpi = stream.readCPI4();
invokedMethods.add(constantPool.lookupMethod(cpi, opcode, method));
break;
default:
break;
}
}
return invokedMethods;
}

/**
* Checks if the passed type is lambda class type based on set flags and the type name.
*
Expand All @@ -138,15 +141,15 @@ public static boolean isLambdaName(String name) {
return isLambdaClassName(name) && lambdaMatcher(name).find();
}

private static String createStableLambdaName(ResolvedJavaType lambdaType, List<ResolvedJavaMethod> targetMethods) {
private static String createStableLambdaName(ResolvedJavaType lambdaType, List<JavaMethod> targetMethods) {
final String lambdaName = lambdaType.getName();
assert lambdaMatcher(lambdaName).find() : "Stable name should be created for lambda types: " + lambdaName;

Matcher m = lambdaMatcher(lambdaName);
StringBuilder sb = new StringBuilder();
targetMethods.forEach((targetMethod) -> sb.append(targetMethod.format("%H.%n(%P)%R")));
// Take parameter types of constructor into consideration, see GR-52837
for (ResolvedJavaMethod ctor : lambdaType.getDeclaredConstructors()) {
for (JavaMethod ctor : lambdaType.getDeclaredConstructors()) {
sb.append(ctor.format("%P"));
}
return m.replaceFirst(Matcher.quoteReplacement(LAMBDA_CLASS_NAME_SUBSTRING + ADDRESS_PREFIX + Digest.digestAsHex(sb.toString()) + ";"));
Expand Down
Loading

0 comments on commit 42220b9

Please sign in to comment.