8245505: Prelink j.l.ref.Reference when loading AOT library

Reviewed-by: dlong, kvn
This commit is contained in:
Igor Veresov 2020-05-28 11:36:39 -07:00
parent b189d0b918
commit 23ce03d2ca
5 changed files with 142 additions and 55 deletions

View File

@ -366,24 +366,30 @@ void AOTCodeHeap::publish_aot(const methodHandle& mh, AOTMethodData* method_data
}
}
void AOTCodeHeap::link_primitive_array_klasses() {
void AOTCodeHeap::link_klass(const Klass* klass) {
ResourceMark rm;
assert(klass != NULL, "Should be given a klass");
AOTKlassData* klass_data = (AOTKlassData*) os::dll_lookup(_lib->dl_handle(), klass->signature_name());
if (klass_data != NULL) {
// Set both GOT cells, resolved and initialized klass pointers.
// _got_index points to second cell - resolved klass pointer.
_klasses_got[klass_data->_got_index-1] = (Metadata*)klass; // Initialized
_klasses_got[klass_data->_got_index ] = (Metadata*)klass; // Resolved
if (PrintAOT) {
tty->print_cr("[Found %s in %s]", klass->internal_name(), _lib->name());
}
}
}
void AOTCodeHeap::link_known_klasses() {
for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) {
BasicType t = (BasicType)i;
if (is_java_primitive(t)) {
const Klass* arr_klass = Universe::typeArrayKlassObj(t);
AOTKlassData* klass_data = (AOTKlassData*) os::dll_lookup(_lib->dl_handle(), arr_klass->signature_name());
if (klass_data != NULL) {
// Set both GOT cells, resolved and initialized klass pointers.
// _got_index points to second cell - resolved klass pointer.
_klasses_got[klass_data->_got_index-1] = (Metadata*)arr_klass; // Initialized
_klasses_got[klass_data->_got_index ] = (Metadata*)arr_klass; // Resolved
if (PrintAOT) {
tty->print_cr("[Found %s in %s]", arr_klass->internal_name(), _lib->name());
}
}
link_klass(arr_klass);
}
}
link_klass(SystemDictionary::Reference_klass());
}
void AOTCodeHeap::register_stubs() {
@ -590,9 +596,7 @@ void AOTCodeHeap::link_global_lib_symbols() {
link_stub_routines_symbols();
link_os_symbols();
link_graal_runtime_symbols();
// Link primitive array klasses.
link_primitive_array_klasses();
link_known_klasses();
}
}

View File

@ -217,7 +217,8 @@ class AOTCodeHeap : public CodeHeap {
void link_graal_runtime_symbols();
void link_global_lib_symbols();
void link_primitive_array_klasses();
void link_klass(const Klass* klass);
void link_known_klasses();
void publish_aot(const methodHandle& mh, AOTMethodData* method_data, int code_id);

View File

@ -121,7 +121,7 @@ public class ReplaceConstantNodesPhaseTest extends HotSpotGraalCompilerTest {
new EliminateRedundantInitializationPhase().apply(graph, highTierContext);
new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext);
new LoadJavaMirrorWithKlassPhase(config).apply(graph, highTierContext);
new ReplaceConstantNodesPhase(false).apply(graph, highTierContext);
new ReplaceConstantNodesPhase(true, false).apply(graph, highTierContext);
Assert.assertEquals(expectedInits, graph.getNodes().filter(InitializeKlassNode.class).count());
Assert.assertEquals(expectedResolves, graph.getNodes().filter(ResolveConstantNode.class).count());
Assert.assertEquals(expectedLoads, graph.getNodes().filter(LoadConstantIndirectlyNode.class).count());

View File

@ -59,6 +59,7 @@ import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.common.LoweringPhase;
import org.graalvm.compiler.phases.common.inlining.InliningPhase;
import org.graalvm.compiler.phases.tiers.HighTierContext;
import org.graalvm.compiler.phases.tiers.LowTierContext;
import org.graalvm.compiler.phases.tiers.MidTierContext;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.phases.tiers.SuitesCreator;
@ -100,7 +101,11 @@ public class HotSpotSuitesProvider extends SuitesProviderBase {
highTierLowering.add(new FinalizeProfileNodesPhase(HotSpotAOTProfilingPlugin.Options.TierAInvokeInlineeNotifyFreqLog.getValue(options)));
}
ListIterator<BasePhase<? super MidTierContext>> midTierLowering = ret.getMidTier().findPhase(LoweringPhase.class);
midTierLowering.add(new ReplaceConstantNodesPhase());
midTierLowering.add(new ReplaceConstantNodesPhase(true));
// Replace possible constants after GC barrier expansion.
ListIterator<BasePhase<? super LowTierContext>> lowTierLowering = ret.getLowTier().findPhase(LoweringPhase.class);
lowTierLowering.add(new ReplaceConstantNodesPhase(false));
// Replace inlining policy
if (Inline.getValue(options)) {

View File

@ -28,6 +28,7 @@ import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.stri
import static org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode.getLoadMethodCountersNodes;
import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes;
import java.lang.ref.Reference;
import java.util.HashSet;
import java.util.List;
@ -82,6 +83,7 @@ import jdk.vm.ci.meta.ResolvedJavaType;
public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
private final boolean verifyFingerprints;
private final boolean allowResolution;
static Class<?> characterCacheClass = Character.class.getDeclaredClasses()[0];
static Class<?> byteCacheClass = Byte.class.getDeclaredClasses()[0];
@ -92,6 +94,7 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
static class ClassInfo {
private ResolvedJavaType stringType;
private ResolvedJavaType referenceType;
private final HashSet<ResolvedJavaType> builtIns = new HashSet<>();
ClassInfo(MetaAccessProvider metaAccessProvider) {
@ -113,6 +116,7 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
builtIns.add(metaAccessProvider.lookupJavaType(longCacheClass));
stringType = metaAccessProvider.lookupJavaType(String.class);
referenceType = metaAccessProvider.lookupJavaType(Reference.class);
}
}
@ -313,8 +317,10 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
* @param graph
* @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
* resolution.
* @return return true if all usages of the node have been replaced
*/
private static void tryToReplaceWithExisting(StructuredGraph graph, ConstantNode node) {
private static boolean tryToReplaceWithExisting(StructuredGraph graph, ConstantNode node) {
boolean allUsagesReplaced = true;
ScheduleResult schedule = graph.getLastSchedule();
NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap();
BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap();
@ -347,16 +353,20 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
for (Block d : blockToExisting.getKeys()) {
if (strictlyDominates(d, b)) {
use.replaceFirstInput(node, blockToExisting.get(d));
replaced = true;
break;
}
}
}
if (!replaced && allUsagesReplaced) {
allUsagesReplaced = false;
}
}
return allUsagesReplaced;
}
/**
* Replace the uses of a constant with either {@link LoadConstantIndirectlyNode} or
* {@link ResolveConstantNode}.
* Replace the uses of a constant with {@link ResolveConstantNode}.
*
* @param graph
* @param stateMapper
@ -366,30 +376,63 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
private static void replaceWithResolution(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node, ClassInfo classInfo) {
HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass();
ValueNode replacement;
if (type.isArray() && type.getComponentType().isPrimitive()) {
// Special case for primitive arrays. The AOT runtime pre-resolves them, so we may
// omit the resolution call.
FixedWithNextNode fixedReplacement;
if (classInfo.builtIns.contains(type)) {
// Special case of klass constants that come from {@link BoxingSnippets}.
fixedReplacement = graph.add(new ResolveConstantNode(node, HotSpotConstantLoadAction.INITIALIZE));
} else {
fixedReplacement = graph.add(new ResolveConstantNode(node));
}
insertReplacement(graph, stateMapper, node, fixedReplacement);
node.replaceAtUsages(fixedReplacement, n -> !isReplacementNode(n));
}
/**
* Replace the uses of a constant with either {@link LoadConstantIndirectlyNode} if possible.
*
* @param graph
* @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
* resolution.
* @return return true if all usages of the node have been replaced
*/
private static boolean replaceWithLoad(StructuredGraph graph, ConstantNode node, ClassInfo classInfo) {
HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass();
ValueNode replacement = null;
if ((type.isArray() && type.getComponentType().isPrimitive()) || type.equals(classInfo.referenceType)) {
// Special case for primitive arrays and j.l.ref.Reference.
// The AOT runtime pre-resolves them, so we may omit the resolution call.
replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node));
} else if (type.equals(topMethodHolder) || (type.isAssignableFrom(topMethodHolder) && !type.isInterface())) {
// If it's a supertype of or the same class that declares the top method, we are
// guaranteed to have it resolved already. If it's an interface, we just test for
// equality.
replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node));
} else {
FixedWithNextNode fixedReplacement;
if (classInfo.builtIns.contains(type)) {
// Special case of klass constants that come from {@link BoxingSnippets}.
fixedReplacement = graph.add(new ResolveConstantNode(node, HotSpotConstantLoadAction.INITIALIZE));
} else {
fixedReplacement = graph.add(new ResolveConstantNode(node));
}
insertReplacement(graph, stateMapper, node, fixedReplacement);
replacement = fixedReplacement;
}
node.replaceAtUsages(replacement, n -> !isReplacementNode(n));
if (replacement != null) {
node.replaceAtUsages(replacement, n -> !isReplacementNode(n));
return true;
}
return false;
}
/**
* Verify that {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} has a valid
* fingerprint.
*
* @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType}.
*/
private void verifyFingerprint(ConstantNode node) {
HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
if (type != null) {
assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants";
if (verifyFingerprints && checkForBadFingerprint(type)) {
throw new GraalError("Type with bad fingerprint: " + type);
}
}
}
/**
@ -400,17 +443,11 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
* @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
* resolution.
*/
private void handleHotSpotMetaspaceConstant(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node, ClassInfo classInfo) {
private static void handleHotSpotMetaspaceConstant(StructuredGraph graph, FrameStateMapperClosure stateMapper, ConstantNode node, ClassInfo classInfo) {
HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
if (type != null) {
if (verifyFingerprints && checkForBadFingerprint(type)) {
throw new GraalError("Type with bad fingerprint: " + type);
}
assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants";
tryToReplaceWithExisting(graph, node);
if (anyUsagesNeedReplacement(node)) {
if (!tryToReplaceWithExisting(graph, node) && !replaceWithLoad(graph, node, classInfo)) {
replaceWithResolution(graph, stateMapper, node, classInfo);
}
} else {
@ -418,6 +455,24 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
}
}
/**
* Replace {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} with a load. This
* variant handles only constants that don't require resolution.
*
* @param graph
* @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
* resolution.
*/
private static void handleHotSpotMetaspaceConstantWithoutResolution(StructuredGraph graph, ConstantNode node, ClassInfo classInfo) {
HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
if (type != null) {
replaceWithLoad(graph, node, classInfo);
} else {
throw new GraalError("Unsupported metaspace constant type: " + type);
}
}
/**
* Replace an object constant with an indirect load {@link ResolveConstantNode}. Currently we
* support only strings.
@ -482,6 +537,7 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
*
* @param graph
* @param stateMapper
* @param classInfo
*/
private void replaceKlassesAndObjects(StructuredGraph graph, FrameStateMapperClosure stateMapper, ClassInfo classInfo) {
new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false);
@ -489,6 +545,7 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
for (ConstantNode node : getConstantNodes(graph)) {
Constant constant = node.asConstant();
if (constant instanceof HotSpotMetaspaceConstant && anyUsagesNeedReplacement(node)) {
verifyFingerprint(node);
handleHotSpotMetaspaceConstant(graph, stateMapper, node, classInfo);
} else if (constant instanceof HotSpotObjectConstant && anyUsagesNeedReplacement(node)) {
handleHotSpotObjectConstant(graph, stateMapper, node, classInfo);
@ -496,18 +553,37 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
}
}
/**
* Replace well-known klass constants with indirect loads.
*
* @param graph
* @param classInfo
*/
private static void replaceKlassesWithoutResolution(StructuredGraph graph, ClassInfo classInfo) {
for (ConstantNode node : getConstantNodes(graph)) {
Constant constant = node.asConstant();
if (constant instanceof HotSpotMetaspaceConstant && anyUsagesNeedReplacement(node)) {
handleHotSpotMetaspaceConstantWithoutResolution(graph, node, classInfo);
}
}
}
@Override
protected void run(StructuredGraph graph, CoreProviders context) {
FrameStateMapperClosure stateMapper = new FrameStateMapperClosure(graph);
ReentrantNodeIterator.apply(stateMapper, graph.start(), null);
if (allowResolution) {
FrameStateMapperClosure stateMapper = new FrameStateMapperClosure(graph);
ReentrantNodeIterator.apply(stateMapper, graph.start(), null);
// Replace LoadMethodCountersNode with ResolveMethodAndLoadCountersNode, expose klass
// constants.
replaceLoadMethodCounters(graph, stateMapper, context);
// Replace LoadMethodCountersNode with ResolveMethodAndLoadCountersNode, expose klass
// constants.
replaceLoadMethodCounters(graph, stateMapper, context);
// Replace object and klass constants (including the ones added in the previous pass) with
// resolution nodes.
replaceKlassesAndObjects(graph, stateMapper, new ClassInfo(context.getMetaAccess()));
// Replace object and klass constants (including the ones added in the previous pass)
// with resolution nodes.
replaceKlassesAndObjects(graph, stateMapper, new ClassInfo(context.getMetaAccess()));
} else {
replaceKlassesWithoutResolution(graph, new ClassInfo(context.getMetaAccess()));
}
}
@Override
@ -515,11 +591,12 @@ public class ReplaceConstantNodesPhase extends BasePhase<CoreProviders> {
return false;
}
public ReplaceConstantNodesPhase() {
this(true);
public ReplaceConstantNodesPhase(boolean allowResolution) {
this(allowResolution, true);
}
public ReplaceConstantNodesPhase(boolean verifyFingerprints) {
public ReplaceConstantNodesPhase(boolean allowResolution, boolean verifyFingerprints) {
this.allowResolution = allowResolution;
this.verifyFingerprints = verifyFingerprints;
}
}