--- old/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java 2019-03-28 11:24:02.000000000 -0700 +++ new/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java 2019-03-28 11:24:02.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, 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 @@ -23,6 +23,8 @@ package jdk.vm.ci.hotspot; import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM; +import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; + import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.code.InvalidInstalledCodeException; import jdk.vm.ci.meta.JavaKind; @@ -30,56 +32,104 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; /** - * Implementation of {@link InstalledCode} for code installed as an nmethod. - * - * When a {@link HotSpotNmethod} dies, it triggers unloading of the nmethod unless - * {@link #isDefault() == true}. + * Implementation of {@link InstalledCode} for code installed as an {@code nmethod}. The address of + * the {@code nmethod} is stored in {@link InstalledCode#address} and the value of + * {@code nmethod::verified_entry_point()} is in {@link InstalledCode#entryPoint}. */ public class HotSpotNmethod extends HotSpotInstalledCode { /** - * This (indirect) Method* reference is safe since class redefinition preserves all methods - * associated with nmethods in the code cache. + * This (indirect) {@code Method*} reference is safe since class redefinition preserves all + * methods associated with nmethods in the code cache. */ - private final HotSpotResolvedJavaMethod method; + private final HotSpotResolvedJavaMethodImpl method; + /** + * Specifies whether the {@code nmethod} associated with this object is the code executed by + * default HotSpot linkage when a normal Java call to {@link #method} is made. That is, does + * {@code this.method.metadataHandle->_code} == {@code this.address}. If not, then the + * {@code nmethod} can only be invoked via a reference to this object. An example of this is the + * trampoline mechanism used by Truffle: https://goo.gl/LX88rZ. + */ private final boolean isDefault; - public HotSpotNmethod(HotSpotResolvedJavaMethod method, String name, boolean isDefault) { + /** + * Determines whether this object is in the oops table of the nmethod. + *

+ * If this object is in the oops table, the VM uses the oops table entry to update this object's + * {@link #address} and {@link #entryPoint} fields when the state of the nmethod changes. The + * nmethod will be unloadable when this object dies. + *

+ * Otherwise, the nmethod's unloadability is not changed when this object dies. + */ + boolean inOopsTable() { + return compileIdSnapshot != 0; + } + + /** + * If this field is 0, this object is in the oops table of the nmethod. Otherwise, the value of + * the field records the nmethod's compile identifier. This value is used to confirm an entry in + * the code cache retrieved by {@link #address} is indeed the nmethod represented by this + * object. + * + * @see #inOopsTable + */ + private final long compileIdSnapshot; + + HotSpotNmethod(HotSpotResolvedJavaMethodImpl method, String name, boolean isDefault, long compileId) { super(name); this.method = method; this.isDefault = isDefault; + boolean inOopsTable = !IS_IN_NATIVE_IMAGE && !isDefault; + this.compileIdSnapshot = inOopsTable ? 0L : compileId; + assert inOopsTable || compileId != 0L : this; } /** * Determines if the nmethod associated with this object is the compiled entry point for - * {@link #getMethod()}. If {@code false}, then the nmethod is unloaded when the VM determines - * this object has died. + * {@link #getMethod()}. */ public boolean isDefault() { return isDefault; } + @Override + public boolean isValid() { + if (compileIdSnapshot != 0L) { + compilerToVM().updateHotSpotNmethod(this); + } + return super.isValid(); + } + public ResolvedJavaMethod getMethod() { return method; } @Override public void invalidate() { - compilerToVM().invalidateInstalledCode(this); + compilerToVM().invalidateHotSpotNmethod(this); } @Override - public String toString() { - return String.format("InstalledNmethod[method=%s, codeBlob=0x%x, isDefault=%b, name=%s]", method, getAddress(), isDefault, name); + public long getAddress() { + if (compileIdSnapshot != 0L) { + compilerToVM().updateHotSpotNmethod(this); + } + return super.getAddress(); } - protected boolean checkThreeObjectArgs() { - assert method.getSignature().getParameterCount(!method.isStatic()) == 3; - assert method.getSignature().getParameterKind(0) == JavaKind.Object; - assert method.getSignature().getParameterKind(1) == JavaKind.Object; - assert !method.isStatic() || method.getSignature().getParameterKind(2) == JavaKind.Object; - return true; + @Override + public long getEntryPoint() { + if (compileIdSnapshot != 0L) { + return 0; + } + return super.getEntryPoint(); + } + + @Override + public String toString() { + return String.format("HotSpotNmethod[method=%s, codeBlob=0x%x, isDefault=%b, name=%s, inOopsTable=%s]", + method, getAddress(), isDefault, name, inOopsTable()); } private boolean checkArgs(Object... args) { @@ -98,8 +148,11 @@ @Override public Object executeVarargs(Object... args) throws InvalidInstalledCodeException { + if (IS_IN_NATIVE_IMAGE) { + throw new HotSpotJVMCIUnsupportedOperationError("Cannot execute nmethod via mirror in native image"); + } assert checkArgs(args); - return compilerToVM().executeInstalledCode(args, this); + return compilerToVM().executeHotSpotNmethod(args, this); } @Override