This commit is contained in:
Jesper Wilhelmsson 2019-12-16 17:43:20 +01:00
commit 83163dbfe6
146 changed files with 10955 additions and 1143 deletions

View File

@ -600,4 +600,5 @@ c16ac7a2eba4e73cb4f7ee9294dd647860eebff0 jdk-14+21
17d242844fc9e7d18b3eac97426490a9c246119e jdk-14+25
288777cf0702914e5266bc1e5d380eed9032ca41 jdk-14+26
2c724dba4c3cf9516b2152e151c9aea66b21b30b jdk-15+0
91a3f092682fc715d991a87eb6ec6f28886d2035 jdk-14+27
63e17cf29bed191ea21020b4648c9cdf893f80f5 jdk-15+1

View File

@ -156,6 +156,12 @@ ifeq ($(call isTargetOs, macosx)+$(DEBUG_LEVEL), true+release)
JRE_IMAGE_HOMEDIR := $(JRE_MACOSX_CONTENTS_DIR)/Home
JDK_BUNDLE_SUBDIR :=
JRE_BUNDLE_SUBDIR :=
# In certain situations, the JDK_IMAGE_DIR points to an image without the
# the symbols and demos. If so, the symobls and demos can be found in a
# separate image. These variables allow for overriding from a custom makefile.
JDK_SYMBOLS_IMAGE_DIR ?= $(JDK_IMAGE_DIR)
JDK_DEMOS_IMAGE_DIR ?= $(JDK_IMAGE_DIR)
JDK_DEMOS_IMAGE_HOMEDIR ?= $(JDK_DEMOS_IMAGE_DIR)/$(JDK_MACOSX_CONTENTS_SUBDIR)/Home
else
JDK_IMAGE_HOMEDIR := $(JDK_IMAGE_DIR)
JRE_IMAGE_HOMEDIR := $(JRE_IMAGE_DIR)
@ -165,6 +171,12 @@ else
JDK_BUNDLE_SUBDIR := $(JDK_BUNDLE_SUBDIR)/$(DEBUG_LEVEL)
JRE_BUNDLE_SUBDIR := $(JRE_BUNDLE_SUBDIR)/$(DEBUG_LEVEL)
endif
# In certain situations, the JDK_IMAGE_DIR points to an image without the
# the symbols and demos. If so, the symobls and demos can be found in a
# separate image. These variables allow for overriding from a custom makefile.
JDK_SYMBOLS_IMAGE_DIR ?= $(JDK_IMAGE_DIR)
JDK_DEMOS_IMAGE_DIR ?= $(JDK_IMAGE_DIR)
JDK_DEMOS_IMAGE_HOMEDIR ?= $(JDK_DEMOS_IMAGE_DIR)
endif
################################################################################
@ -176,13 +188,24 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
# There may be files with spaces in the names, so use ShellFindFiles
# explicitly.
ALL_JDK_FILES := $(call ShellFindFiles, $(JDK_IMAGE_DIR))
ifneq ($(JDK_IMAGE_DIR), $(JDK_SYMBOLS_IMAGE_DIR))
ALL_JDK_SYMBOLS_FILES := $(call ShellFindFiles, $(JDK_SYMBOLS_IMAGE_DIR))
else
ALL_JDK_SYMBOLS_FILES := $(ALL_JDK_FILES)
endif
ifneq ($(JDK_IMAGE_DIR), $(JDK_DEMOS_IMAGE_DIR))
ALL_JDK_DEMOS_FILES := $(call ShellFindFiles, $(JDK_DEMOS_IMAGE_DIR))
else
ALL_JDK_DEMOS_FILES := $(ALL_JDK_FILES)
endif
# Create special filter rules when dealing with unzipped .dSYM directories on
# macosx
ifeq ($(call isTargetOs, macosx), true)
ifeq ($(ZIP_EXTERNAL_DEBUG_SYMBOLS), false)
JDK_SYMBOLS_EXCLUDE_PATTERN := $(addprefix %, \
$(call containing, .dSYM/, $(patsubst $(JDK_IMAGE_DIR)/%, %, $(ALL_JDK_FILES))))
$(call containing, .dSYM/, $(patsubst $(JDK_IMAGE_DIR)/%, %, \
$(ALL_JDK_SYMBOLS_FILES))))
endif
endif
@ -203,12 +226,13 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
$(filter-out \
$(JDK_IMAGE_HOMEDIR)/demo/% \
, \
$(ALL_JDK_FILES) \
$(ALL_JDK_SYMBOLS_FILES) \
) \
) \
$(call FindFiles, $(SYMBOLS_IMAGE_DIR))
TEST_DEMOS_BUNDLE_FILES := $(filter $(JDK_IMAGE_HOMEDIR)/demo/%, $(ALL_JDK_FILES))
TEST_DEMOS_BUNDLE_FILES := $(filter $(JDK_DEMOS_IMAGE_HOMEDIR)/demo/%, \
$(ALL_JDK_DEMOS_FILES))
ALL_JRE_FILES := $(call ShellFindFiles, $(JRE_IMAGE_DIR))
@ -245,15 +269,17 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
LEGACY_TARGETS += $(BUILD_JRE_BUNDLE)
$(eval $(call SetupBundleFile, BUILD_JDK_SYMBOLS_BUNDLE, \
BUNDLE_NAME := $(JDK_SYMBOLS_BUNDLE_NAME), \
FILES := $(JDK_SYMBOLS_BUNDLE_FILES), \
BASE_DIRS := $(JDK_IMAGE_DIR) $(wildcard $(SYMBOLS_IMAGE_DIR)), \
SUBDIR := $(JDK_BUNDLE_SUBDIR), \
UNZIP_DEBUGINFO := true, \
))
ifeq ($(COPY_DEBUG_SYMBOLS), true)
$(eval $(call SetupBundleFile, BUILD_JDK_SYMBOLS_BUNDLE, \
BUNDLE_NAME := $(JDK_SYMBOLS_BUNDLE_NAME), \
FILES := $(JDK_SYMBOLS_BUNDLE_FILES), \
BASE_DIRS := $(JDK_SYMBOLS_IMAGE_DIR) $(wildcard $(SYMBOLS_IMAGE_DIR)), \
SUBDIR := $(JDK_BUNDLE_SUBDIR), \
UNZIP_DEBUGINFO := true, \
))
PRODUCT_TARGETS += $(BUILD_JDK_SYMBOLS_BUNDLE)
PRODUCT_TARGETS += $(BUILD_JDK_SYMBOLS_BUNDLE)
endif
# The demo bundle is only created to support client tests. Ideally it should
# be built with the main test bundle, but since the prerequisites match
@ -261,7 +287,7 @@ ifneq ($(filter product-bundles% legacy-bundles, $(MAKECMDGOALS)), )
$(eval $(call SetupBundleFile, BUILD_TEST_DEMOS_BUNDLE, \
BUNDLE_NAME := $(TEST_DEMOS_BUNDLE_NAME), \
FILES := $(TEST_DEMOS_BUNDLE_FILES), \
BASE_DIRS := $(JDK_IMAGE_DIR), \
BASE_DIRS := $(JDK_DEMOS_IMAGE_DIR), \
SUBDIR := $(JDK_BUNDLE_SUBDIR), \
))

View File

@ -61,17 +61,15 @@ ifeq ($(call isTargetOs, macosx), true)
FILES := $(call FindFiles, $(JRE_IMAGE_DIR)), \
))
$(JDK_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib:
$(call LogInfo, Creating link $(patsubst $(OUTPUTDIR)/%,%,$@))
$(call MakeTargetDir)
$(RM) $@
$(LN) -s ../Home/lib/libjli.dylib $@
$(eval $(call SetupCopyFiles, COPY_LIBJLI_JDK, \
FILES := $(JDK_IMAGE_DIR)/lib/libjli.dylib, \
DEST := $(JDK_MACOSX_CONTENTS_DIR)/MacOS, \
))
$(JRE_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib:
$(call LogInfo, Creating link $(patsubst $(OUTPUTDIR)/%,%,$@))
$(call MakeTargetDir)
$(RM) $@
$(LN) -s ../Home/lib/libjli.dylib $@
$(eval $(call SetupCopyFiles, COPY_LIBJLI_JRE, \
FILES := $(JRE_IMAGE_DIR)/lib/libjli.dylib, \
DEST := $(JRE_MACOSX_CONTENTS_DIR)/MacOS, \
))
$(eval $(call SetupTextFileProcessing, BUILD_JDK_PLIST, \
SOURCE_FILES := $(MACOSX_PLIST_SRC)/JDK-Info.plist, \
@ -97,13 +95,19 @@ ifeq ($(call isTargetOs, macosx), true)
@@VENDOR@@ => $(BUNDLE_VENDOR) , \
))
jdk-bundle: $(COPY_JDK_IMAGE) $(JDK_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib \
$(BUILD_JDK_PLIST)
$(SUPPORT_OUTPUTDIR)/images/_jdk_bundle_attribute_set: $(COPY_JDK_IMAGE)
$(SETFILE) -a B $(dir $(JDK_MACOSX_CONTENTS_DIR))
$(TOUCH) $@
jre-bundle: $(COPY_JRE_IMAGE) $(JRE_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib \
$(BUILD_JRE_PLIST)
$(SUPPORT_OUTPUTDIR)/images/_jre_bundle_attribute_set: $(COPY_JRE_IMAGE)
$(SETFILE) -a B $(dir $(JRE_MACOSX_CONTENTS_DIR))
$(TOUCH) $@
jdk-bundle: $(COPY_JDK_IMAGE) $(COPY_LIBJLI_JDK) \
$(BUILD_JDK_PLIST) $(SUPPORT_OUTPUTDIR)/images/_jdk_bundle_attribute_set
jre-bundle: $(COPY_JRE_IMAGE) $(COPY_LIBJLI_JRE) \
$(BUILD_JRE_PLIST) $(SUPPORT_OUTPUTDIR)/images/_jre_bundle_attribute_set
else # Not macosx

View File

@ -58,6 +58,7 @@ BOOT_MODULES += \
java.rmi \
java.security.sasl \
java.xml \
jdk.incubator.foreign \
jdk.internal.vm.ci \
jdk.jfr \
jdk.management \
@ -162,6 +163,7 @@ DOCS_MODULES += \
jdk.jsobject \
jdk.jshell \
jdk.jstatd \
jdk.incubator.foreign \
jdk.localedata \
jdk.management \
jdk.management.agent \

View File

@ -434,7 +434,7 @@ var getJibProfilesProfiles = function (input, common, data) {
target_cpu: "x64",
dependencies: ["devkit", "cups"],
configure_args: concat(common.configure_args_64bit,
"--with-zlib=system", "--enable-dtrace"),
"--with-zlib=system", "--enable-dtrace", "--enable-deprecated-ports=yes"),
},
"solaris-sparcv9": {
@ -442,7 +442,7 @@ var getJibProfilesProfiles = function (input, common, data) {
target_cpu: "sparcv9",
dependencies: ["devkit", "cups"],
configure_args: concat(common.configure_args_64bit,
"--with-zlib=system", "--enable-dtrace"),
"--with-zlib=system", "--enable-dtrace", "--enable-deprecated-ports=yes"),
},
"windows-x64": {

View File

@ -159,6 +159,108 @@ endef
################################################################################
################################################################################
# Setup a rule for generating a VarHandleMemoryAddress java class
# Param 1 - Variable declaration prefix
# Param 2 - Type with first letter capitalized
define GenerateVarHandleMemoryAddress
$1_Type := $2
$1_FILENAME := $(VARHANDLES_GENSRC_DIR)/VarHandleMemoryAddressAs$$($1_Type)s.java
ifeq ($$($1_Type), Byte)
$1_type := byte
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -Kbyte
endif
ifeq ($$($1_Type), Short)
$1_type := short
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
endif
ifeq ($$($1_Type), Char)
$1_type := char
$1_BoxType := Character
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
endif
ifeq ($$($1_Type), Int)
$1_type := int
$1_BoxType := Integer
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KCAS
$1_ARGS += -KAtomicAdd
$1_ARGS += -KBitwise
endif
ifeq ($$($1_Type), Long)
$1_type := long
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KCAS
$1_ARGS += -KAtomicAdd
$1_ARGS += -KBitwise
endif
ifeq ($$($1_Type), Float)
$1_type := float
$1_BoxType := $$($1_Type)
$1_rawType := int
$1_RawType := Int
$1_RawBoxType := Integer
$1_ARGS += -KCAS
$1_ARGS += -KfloatingPoint
endif
ifeq ($$($1_Type), Double)
$1_type := double
$1_BoxType := $$($1_Type)
$1_rawType := long
$1_RawType := Long
$1_RawBoxType := Long
$1_ARGS += -KCAS
$1_ARGS += -KfloatingPoint
endif
$$($1_FILENAME): $(VARHANDLES_SRC_DIR)/X-VarHandleMemoryAddressView.java.template $(BUILD_TOOLS_JDK)
$$(call MakeDir, $$(@D))
$(RM) $$@
$(TOOL_SPP) -nel -K$$($1_type) \
-Dtype=$$($1_type) -DType=$$($1_Type) -DBoxType=$$($1_BoxType) \
-DrawType=$$($1_rawType) -DRawType=$$($1_RawType) -DRawBoxType=$$($1_RawBoxType) \
$$($1_ARGS) -i$$< -o$$@
GENSRC_VARHANDLES += $$($1_FILENAME)
endef
################################################################################
# List the types to generate source for, with capitalized first letter
VARHANDLES_TYPES := Boolean Byte Short Char Int Long Float Double Reference
$(foreach t, $(VARHANDLES_TYPES), \
@ -169,6 +271,11 @@ VARHANDLES_BYTE_ARRAY_TYPES := Short Char Int Long Float Double
$(foreach t, $(VARHANDLES_BYTE_ARRAY_TYPES), \
$(eval $(call GenerateVarHandleByteArray,VAR_HANDLE_BYTE_ARRAY_$t,$t)))
# List the types to generate source for, with capitalized first letter
VARHANDLES_MEMORY_ADDRESS_TYPES := Byte Short Char Int Long Float Double
$(foreach t, $(VARHANDLES_MEMORY_ADDRESS_TYPES), \
$(eval $(call GenerateVarHandleMemoryAddress,VAR_HANDLE_MEMORY_ADDRESS_$t,$t)))
GENSRC_JAVA_BASE += $(GENSRC_VARHANDLES)
# Include custom extension post hook

View File

@ -219,7 +219,9 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
// Never trust strangely unstable finals: System.out, etc.
return false;
// Even if general trusting is disabled, trust system-built closures in these packages.
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke"))
if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") ||
holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") ||
holder->is_in_package("java/lang"))
return true;
// Trust VM unsafe anonymous classes. They are private API (jdk.internal.misc.Unsafe)
// and can't be serialized, so there is no hacking of finals going on with them.

View File

@ -145,7 +145,7 @@ void ciMethodData::prepare_metadata() {
}
}
void ciMethodData::load_extra_data() {
void ciMethodData::load_remaining_extra_data() {
MethodData* mdo = get_MethodData();
MutexLocker ml(mdo->extra_data_lock());
// Deferred metadata cleaning due to concurrent class unloading.
@ -154,6 +154,14 @@ void ciMethodData::load_extra_data() {
// and no safepoints can introduce more stale metadata.
NoSafepointVerifier no_safepoint;
assert((mdo->data_size() == _data_size) && (mdo->extra_data_size() == _extra_data_size), "sanity, unchanged");
assert(extra_data_base() == (DataLayout*)((address) _data + _data_size), "sanity");
// Copy the extra data once it is prepared (i.e. cache populated, no release of extra data lock anymore)
Copy::disjoint_words_atomic((HeapWord*) mdo->extra_data_base(),
(HeapWord*)((address) _data + _data_size),
(_extra_data_size - mdo->parameters_size_in_bytes()) / HeapWordSize);
// speculative trap entries also hold a pointer to a Method so need to be translated
DataLayout* dp_src = mdo->extra_data_base();
DataLayout* end_src = mdo->args_data_limit();
@ -162,19 +170,7 @@ void ciMethodData::load_extra_data() {
assert(dp_src < end_src, "moved past end of extra data");
assert(((intptr_t)dp_dst) - ((intptr_t)extra_data_base()) == ((intptr_t)dp_src) - ((intptr_t)mdo->extra_data_base()), "source and destination don't match");
// New traps in the MDO may have been added since we copied the
// data (concurrent deoptimizations before we acquired
// extra_data_lock above) or can be removed (a safepoint may occur
// in the prepare_metadata call above) as we translate the copy:
// update the copy as we go.
int tag = dp_src->tag();
size_t entry_size = DataLayout::header_size_in_bytes();
if (tag != DataLayout::no_tag) {
ProfileData* src_data = dp_src->data_in();
entry_size = src_data->size_in_bytes();
}
memcpy(dp_dst, dp_src, entry_size);
switch(tag) {
case DataLayout::speculative_trap_data_tag: {
ciSpeculativeTrapData data_dst(dp_dst);
@ -205,9 +201,31 @@ void ciMethodData::load_data() {
// To do: don't copy the data if it is not "ripe" -- require a minimum #
// of invocations.
// Snapshot the data -- actually, take an approximate snapshot of
// the data. Any concurrently executing threads may be changing the
// data as we copy it.
// Snapshot the data and extra parameter data first without the extra trap and arg info data.
// Those are copied in a second step. Actually, an approximate snapshot of the data is taken.
// Any concurrently executing threads may be changing the data as we copy it.
//
// The first snapshot step requires two copies (data entries and parameter data entries) since
// the MDO is laid out as follows:
//
// data_base: ---------------------------
// | data entries |
// | ... |
// extra_data_base: ---------------------------
// | trap data entries |
// | ... |
// | one arg info data entry |
// | data for each arg |
// | ... |
// args_data_limit: ---------------------------
// | parameter data entries |
// | ... |
// extra_data_limit: ---------------------------
//
// _data_size = extra_data_base - data_base
// _extra_data_size = extra_data_limit - extra_data_base
// total_size = _data_size + _extra_data_size
// args_data_limit = data_base + total_size - parameter_data_size
Copy::disjoint_words_atomic((HeapWord*) mdo,
(HeapWord*) &_orig,
sizeof(_orig) / HeapWordSize);
@ -218,8 +236,15 @@ void ciMethodData::load_data() {
_data = (intptr_t *) arena->Amalloc(total_size);
Copy::disjoint_words_atomic((HeapWord*) mdo->data_base(),
(HeapWord*) _data,
total_size / HeapWordSize);
_data_size / HeapWordSize);
int parameters_data_size = mdo->parameters_size_in_bytes();
if (parameters_data_size > 0) {
// Snapshot the parameter data
Copy::disjoint_words_atomic((HeapWord*) mdo->args_data_limit(),
(HeapWord*) ((address)_data + total_size - parameters_data_size),
parameters_data_size / HeapWordSize);
}
// Traverse the profile data, translating any oops into their
// ci equivalents.
ResourceMark rm;
@ -236,7 +261,9 @@ void ciMethodData::load_data() {
parameters->translate_from(mdo->parameters_type_data());
}
load_extra_data();
assert((DataLayout*) ((address)_data + total_size - parameters_data_size) == args_data_limit(),
"sanity - parameter data starts after the argument data of the single ArgInfoData entry");
load_remaining_extra_data();
// Note: Extra data are all BitData, and do not need translation.
_current_mileage = MethodData::mileage_of(mdo->method());
@ -360,7 +387,7 @@ ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_f
two_free_slots = (MethodData::next_extra(dp)->tag() == DataLayout::no_tag);
return NULL;
case DataLayout::arg_info_data_tag:
return NULL; // ArgInfoData is at the end of extra data section.
return NULL; // ArgInfoData is after the trap data right before the parameter data.
case DataLayout::bit_data_tag:
if (m == NULL && dp->bci() == bci) {
return new ciBitData(dp);
@ -767,7 +794,7 @@ void ciMethodData::print_data_on(outputStream* st) {
break;
case DataLayout::arg_info_data_tag:
data = new ciArgInfoData(dp);
dp = end; // ArgInfoData is at the end of extra data section.
dp = end; // ArgInfoData is after the trap data right before the parameter data.
break;
case DataLayout::speculative_trap_data_tag:
data = new ciSpeculativeTrapData(dp);

View File

@ -462,7 +462,7 @@ private:
ciArgInfoData *arg_info() const;
void prepare_metadata();
void load_extra_data();
void load_remaining_extra_data();
ciProfileData* bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots);
void dump_replay_data_type_helper(outputStream* out, int round, int& count, ProfileData* pdata, ByteSize offset, ciKlass* k);

View File

@ -2079,10 +2079,6 @@ private:
// parameter profiling.
enum { no_parameters = -2, parameters_uninitialized = -1 };
int _parameters_type_data_di;
int parameters_size_in_bytes() const {
ParametersTypeData* param = parameters_type_data();
return param == NULL ? 0 : param->size_in_bytes();
}
// Beginning of the data entries
intptr_t _data[1];
@ -2300,6 +2296,11 @@ public:
return _data_size;
}
int parameters_size_in_bytes() const {
ParametersTypeData* param = parameters_type_data();
return param == NULL ? 0 : param->size_in_bytes();
}
// Accessors
Method* method() const { return _method; }

View File

@ -302,8 +302,6 @@ public class BufferedReader extends Reader {
* (EOF).
*
* @param ignoreLF If true, the next '\n' will be skipped
* @param term Output: Whether a line terminator was encountered
* while reading the line; may be {@code null}.
*
* @return A String containing the contents of the line, not including
* any line-termination characters, or null if the end of the
@ -313,14 +311,13 @@ public class BufferedReader extends Reader {
*
* @throws IOException If an I/O error occurs
*/
String readLine(boolean ignoreLF, boolean[] term) throws IOException {
String readLine(boolean ignoreLF) throws IOException {
StringBuilder s = null;
int startChar;
synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;
if (term != null) term[0] = false;
bufferLoop:
for (;;) {
@ -347,7 +344,6 @@ public class BufferedReader extends Reader {
for (i = nextChar; i < nChars; i++) {
c = cb[i];
if ((c == '\n') || (c == '\r')) {
if (term != null) term[0] = true;
eol = true;
break charLoop;
}
@ -393,7 +389,7 @@ public class BufferedReader extends Reader {
* @see java.nio.file.Files#readAllLines
*/
public String readLine() throws IOException {
return readLine(false, null);
return readLine(false);
}
/**

View File

@ -25,6 +25,7 @@
package java.io;
/**
* A buffered character-input stream that keeps track of line numbers. This
* class defines methods {@link #setLineNumber(int)} and {@link
@ -199,10 +200,9 @@ public class LineNumberReader extends BufferedReader {
*/
public String readLine() throws IOException {
synchronized (lock) {
boolean[] term = new boolean[1];
String l = super.readLine(skipLF, term);
String l = super.readLine(skipLF);
skipLF = false;
if (l != null && term[0])
if (l != null)
lineNumber++;
return l;
}

View File

@ -0,0 +1,494 @@
/*
* Copyright (c) 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
* 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 java.lang.invoke;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
class AddressVarHandleGenerator {
private static final String DEBUG_DUMP_CLASSES_DIR_PROPERTY = "jdk.internal.foreign.ClassGenerator.DEBUG_DUMP_CLASSES_DIR";
private static final boolean DEBUG =
GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.ClassGenerator.DEBUG");
private static final Class<?> BASE_CLASS = VarHandleMemoryAddressBase.class;
private static final HashMap<Class<?>, Class<?>> helperClassCache;
static {
helperClassCache = new HashMap<>();
helperClassCache.put(byte.class, VarHandleMemoryAddressAsBytes.class);
helperClassCache.put(short.class, VarHandleMemoryAddressAsShorts.class);
helperClassCache.put(char.class, VarHandleMemoryAddressAsChars.class);
helperClassCache.put(int.class, VarHandleMemoryAddressAsInts.class);
helperClassCache.put(long.class, VarHandleMemoryAddressAsLongs.class);
helperClassCache.put(float.class, VarHandleMemoryAddressAsFloats.class);
helperClassCache.put(double.class, VarHandleMemoryAddressAsDoubles.class);
}
private static final File DEBUG_DUMP_CLASSES_DIR;
static {
String path = GetPropertyAction.privilegedGetProperty(DEBUG_DUMP_CLASSES_DIR_PROPERTY);
if (path == null) {
DEBUG_DUMP_CLASSES_DIR = null;
} else {
DEBUG_DUMP_CLASSES_DIR = new File(path);
}
}
private static final Unsafe U = Unsafe.getUnsafe();
private final String implClassName;
private final int dimensions;
private final Class<?> carrier;
private final Class<?> helperClass;
private final VarForm form;
AddressVarHandleGenerator(Class<?> carrier, int dims) {
this.dimensions = dims;
this.carrier = carrier;
Class<?>[] components = new Class<?>[dimensions];
Arrays.fill(components, long.class);
this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components);
this.helperClass = helperClassCache.get(carrier);
this.implClassName = helperClass.getName().replace('.', '/') + dimensions;
}
/*
* Generate a VarHandle memory access factory.
* The factory has type (ZJJ[J)VarHandle.
*/
MethodHandle generateHandleFactory() {
Class<?> implCls = generateClass();
try {
Class<?>[] components = new Class<?>[dimensions];
Arrays.fill(components, long.class);
VarForm form = new VarForm(implCls, MemoryAddressProxy.class, carrier, components);
MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class);
MethodHandle constr = MethodHandles.Lookup.IMPL_LOOKUP.findConstructor(implCls, constrType);
constr = MethodHandles.insertArguments(constr, 0, form);
return constr;
} catch (Throwable ex) {
throw new AssertionError(ex);
}
}
/*
* Generate a specialized VarHandle class for given carrier
* and access coordinates.
*/
Class<?> generateClass() {
BinderClassWriter cw = new BinderClassWriter();
if (DEBUG) {
System.out.println("Generating header implementation class");
}
cw.visit(52, ACC_PUBLIC | ACC_SUPER, implClassName, null, Type.getInternalName(BASE_CLASS), null);
//add dimension fields
for (int i = 0; i < dimensions; i++) {
cw.visitField(ACC_PRIVATE | ACC_FINAL, "dim" + i, "J", null, null);
}
addConstructor(cw);
addAccessModeTypeMethod(cw);
addStridesAccessor(cw);
addCarrierAccessor(cw);
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
addAccessModeMethodIfNeeded(mode, cw);
}
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
return defineClass(cw, classBytes);
}
void addConstructor(BinderClassWriter cw) {
MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class);
MethodVisitor mv = cw.visitMethod(0, "<init>", constrType.toMethodDescriptorString(), null, null);
mv.visitCode();
//super call
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(VarForm.class));
mv.visitVarInsn(ILOAD, 2);
mv.visitVarInsn(LLOAD, 3);
mv.visitVarInsn(LLOAD, 5);
mv.visitVarInsn(LLOAD, 7);
mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(BASE_CLASS), "<init>",
MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class).toMethodDescriptorString(), false);
//init dimensions
for (int i = 0 ; i < dimensions ; i++) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 9);
mv.visitLdcInsn(i);
mv.visitInsn(LALOAD);
mv.visitFieldInsn(PUTFIELD, implClassName, "dim" + i, "J");
}
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addAccessModeTypeMethod(BinderClassWriter cw) {
MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessMode.class);
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "accessModeTypeUncached", modeMethType.toMethodDescriptorString(), null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(GETFIELD, Type.getInternalName(VarHandle.AccessMode.class), "at", Type.getDescriptor(VarHandle.AccessType.class));
mv.visitLdcInsn(cw.makeConstantPoolPatch(MemoryAddressProxy.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitLdcInsn(cw.makeConstantPoolPatch(carrier));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
Class<?>[] dims = new Class<?>[dimensions];
Arrays.fill(dims, long.class);
mv.visitLdcInsn(cw.makeConstantPoolPatch(dims));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class[].class));
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(VarHandle.AccessType.class),
"accessModeType", MethodType.methodType(MethodType.class, Class.class, Class.class, Class[].class).toMethodDescriptorString(), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, BinderClassWriter cw) {
String methName = mode.methodName();
MethodType methType = form.getMethodType(mode.at.ordinal())
.insertParameterTypes(0, BASE_CLASS);
try {
MethodType helperType = methType.insertParameterTypes(2, long.class);
if (dimensions > 0) {
helperType = helperType.dropParameterTypes(3, 3 + dimensions);
}
//try to resolve...
String helperMethodName = methName + "0";
MethodHandles.Lookup.IMPL_LOOKUP
.findStatic(helperClass,
helperMethodName,
helperType);
MethodVisitor mv = cw.visitMethod(ACC_STATIC, methName, methType.toMethodDescriptorString(), null, null);
mv.visitAnnotation(Type.getDescriptor(ForceInline.class), true);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // handle impl
mv.visitVarInsn(ALOAD, 1); // receiver
// offset calculation
int slot = 2;
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitFieldInsn(GETFIELD, Type.getInternalName(BASE_CLASS), "offset", "J");
for (int i = 0 ; i < dimensions ; i++) {
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitTypeInsn(CHECKCAST, implClassName);
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
mv.visitVarInsn(LLOAD, slot);
mv.visitInsn(LMUL);
mv.visitInsn(LADD);
slot += 2;
}
for (int i = 2 + dimensions; i < methType.parameterCount() ; i++) {
Class<?> param = methType.parameterType(i);
mv.visitVarInsn(loadInsn(param), slot); // load index
slot += getSlotsForType(param);
}
//call helper
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(helperClass), helperMethodName,
helperType.toMethodDescriptorString(), false);
mv.visitInsn(returnInsn(helperType.returnType()));
mv.visitMaxs(0, 0);
mv.visitEnd();
} catch (ReflectiveOperationException ex) {
//not found, skip
}
}
void addStridesAccessor(BinderClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "strides", "()[J", null, null);
mv.visitCode();
iConstInsn(mv, dimensions);
mv.visitIntInsn(NEWARRAY, T_LONG);
for (int i = 0 ; i < dimensions ; i++) {
mv.visitInsn(DUP);
iConstInsn(mv, i);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
mv.visitInsn(LASTORE);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addCarrierAccessor(BinderClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "carrier", "()Ljava/lang/Class;", null, null);
mv.visitCode();
mv.visitLdcInsn(cw.makeConstantPoolPatch(carrier));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
//where
private Class<?> defineClass(BinderClassWriter cw, byte[] classBytes) {
try {
if (DEBUG_DUMP_CLASSES_DIR != null) {
debugWriteClassToFile(classBytes);
}
Object[] patches = cw.resolvePatches(classBytes);
Class<?> c = U.defineAnonymousClass(BASE_CLASS, classBytes, patches);
return c;
} catch (Throwable e) {
debugPrintClass(classBytes);
throw e;
}
}
// shared code generation helpers
private static int getSlotsForType(Class<?> c) {
if (c == long.class || c == double.class) {
return 2;
}
return 1;
}
/**
* Emits an actual return instruction conforming to the given return type.
*/
private int returnInsn(Class<?> type) {
return switch (LambdaForm.BasicType.basicType(type)) {
case I_TYPE -> Opcodes.IRETURN;
case J_TYPE -> Opcodes.LRETURN;
case F_TYPE -> Opcodes.FRETURN;
case D_TYPE -> Opcodes.DRETURN;
case L_TYPE -> Opcodes.ARETURN;
case V_TYPE -> RETURN;
};
}
private int loadInsn(Class<?> type) {
return switch (LambdaForm.BasicType.basicType(type)) {
case I_TYPE -> Opcodes.ILOAD;
case J_TYPE -> LLOAD;
case F_TYPE -> Opcodes.FLOAD;
case D_TYPE -> Opcodes.DLOAD;
case L_TYPE -> Opcodes.ALOAD;
case V_TYPE -> throw new IllegalStateException("Cannot load void");
};
}
private static void iConstInsn(MethodVisitor mv, int i) {
switch (i) {
case -1, 0, 1, 2, 3, 4, 5:
mv.visitInsn(ICONST_0 + i);
break;
default:
if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, i);
} else {
mv.visitLdcInsn(i);
}
}
}
// debug helpers
private static String debugPrintClass(byte[] classFile) {
ClassReader cr = new ClassReader(classFile);
StringWriter sw = new StringWriter();
cr.accept(new TraceClassVisitor(new PrintWriter(sw)), 0);
return sw.toString();
}
private void debugWriteClassToFile(byte[] classFile) {
File file = new File(DEBUG_DUMP_CLASSES_DIR, implClassName + ".class");
if (DEBUG) {
System.err.println("Dumping class " + implClassName + " to " + file);
}
try {
debugWriteDataToFile(classFile, file);
} catch (Exception e) {
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
}
}
private void debugWriteDataToFile(byte[] data, File file) {
if (file.exists()) {
file.delete();
}
if (file.exists()) {
throw new RuntimeException("Failed to remove pre-existing file " + file);
}
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
if (!parent.exists()) {
throw new RuntimeException("Failed to create " + parent);
}
if (!parent.isDirectory()) {
throw new RuntimeException(parent + " is not a directory");
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(data);
} catch (IOException e) {
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
}
}
static class BinderClassWriter extends ClassWriter {
private final ArrayList<ConstantPoolPatch> cpPatches = new ArrayList<>();
private int curUniquePatchIndex = 0;
BinderClassWriter() {
super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
}
public String makeConstantPoolPatch(Object o) {
int myUniqueIndex = curUniquePatchIndex++;
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + myUniqueIndex;
int index = newConst(cpPlaceholder);
cpPatches.add(new ConstantPoolPatch(index, cpPlaceholder, o));
return cpPlaceholder;
}
public Object[] resolvePatches(byte[] classFile) {
if (cpPatches.isEmpty()) {
return null;
}
int size = ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF);
Object[] patches = new Object[size];
for (ConstantPoolPatch p : cpPatches) {
if (p.index >= size) {
throw new InternalError("Failed to resolve constant pool patch entries");
}
patches[p.index] = p.value;
}
return patches;
}
static class ConstantPoolPatch {
final int index;
final String placeholder;
final Object value;
ConstantPoolPatch(int index, String placeholder, Object value) {
this.index = index;
this.placeholder = placeholder;
this.value = value;
}
@Override
public String toString() {
return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
}
}
}
}

View File

@ -41,6 +41,7 @@ import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import java.lang.reflect.Array;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -1790,6 +1791,44 @@ abstract class MethodHandleImpl {
invokerMethodTypes, callSiteMethodTypes);
}
@Override
public VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides) {
return VarHandles.makeMemoryAddressViewHandle(carrier, alignmentMask, order, offset, strides);
}
@Override
public Class<?> memoryAddressCarrier(VarHandle handle) {
return checkMemAccessHandle(handle).carrier();
}
@Override
public long memoryAddressAlignmentMask(VarHandle handle) {
return checkMemAccessHandle(handle).alignmentMask;
}
@Override
public ByteOrder memoryAddressByteOrder(VarHandle handle) {
return checkMemAccessHandle(handle).be ?
ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
}
@Override
public long memoryAddressOffset(VarHandle handle) {
return checkMemAccessHandle(handle).offset;
}
@Override
public long[] memoryAddressStrides(VarHandle handle) {
return checkMemAccessHandle(handle).strides();
}
private VarHandleMemoryAddressBase checkMemAccessHandle(VarHandle handle) {
if (!(handle instanceof VarHandleMemoryAddressBase)) {
throw new IllegalArgumentException("Not a memory access varhandle: " + handle);
}
return (VarHandleMemoryAddressBase) handle;
}
});
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 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
* 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 java.lang.invoke;
/**
* Base class for memory access var handle implementations.
*/
abstract class VarHandleMemoryAddressBase extends VarHandle {
/** endianness **/
final boolean be;
/** access size (in bytes, computed from var handle carrier type) **/
final long length;
/** access offset (in bytes); must be compatible with {@code alignmentMask} **/
final long offset;
/** alignment constraint (in bytes, expressed as a bit mask) **/
final long alignmentMask;
VarHandleMemoryAddressBase(VarForm form, boolean be, long length, long offset, long alignmentMask) {
super(form);
this.be = be;
this.length = length;
this.offset = offset;
this.alignmentMask = alignmentMask;
}
static IllegalStateException newIllegalStateExceptionForMisalignedAccess(long address) {
return new IllegalStateException("Misaligned access at address: " + address);
}
/**
* Strides used for multi-dimensional access; each stride must be compatible with {@code alignmentMask}.
*/
abstract long[] strides();
abstract Class<?> carrier();
}

View File

@ -25,13 +25,26 @@
package java.lang.invoke;
import sun.invoke.util.Wrapper;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
final class VarHandles {
static ClassValue<ConcurrentMap<Integer, MethodHandle>> ADDRESS_FACTORIES = new ClassValue<>() {
@Override
protected ConcurrentMap<Integer, MethodHandle> computeValue(Class<?> type) {
return new ConcurrentHashMap<>();
}
};
static VarHandle makeFieldHandle(MemberName f, Class<?> refc, Class<?> type, boolean isWriteAllowedOnFinalFields) {
if (!f.isStatic()) {
long foffset = MethodHandleNatives.objectFieldOffset(f);
@ -279,6 +292,43 @@ final class VarHandles {
throw new UnsupportedOperationException();
}
/**
* Creates a memory access VarHandle.
*
* Resulting VarHandle will take a memory address as first argument,
* and a certain number of coordinate {@code long} parameters, depending on the length
* of the {@code strides} argument array.
*
* Coordinates are multiplied with corresponding scale factors ({@code strides}) and added
* to a single fixed offset to compute an effective offset from the given MemoryAddress for the access.
*
* @param carrier the Java carrier type.
* @param alignmentMask alignment requirement to be checked upon access. In bytes. Expressed as a mask.
* @param byteOrder the byte order.
* @param offset a constant offset for the access.
* @param strides the scale factors with which to multiply given access coordinates.
* @return the created VarHandle.
*/
static VarHandle makeMemoryAddressViewHandle(Class<?> carrier, long alignmentMask,
ByteOrder byteOrder, long offset, long[] strides) {
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
}
long size = Wrapper.forPrimitiveType(carrier).bitWidth() / 8;
boolean be = byteOrder == ByteOrder.BIG_ENDIAN;
Map<Integer, MethodHandle> carrierFactory = ADDRESS_FACTORIES.get(carrier);
MethodHandle fac = carrierFactory.computeIfAbsent(strides.length,
dims -> new AddressVarHandleGenerator(carrier, dims)
.generateHandleFactory());
try {
return (VarHandle)fac.invoke(be, size, offset, alignmentMask, strides);
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
// /**
// * A helper program to generate the VarHandleGuards class with a set of
// * static guard methods each of which corresponds to a particular shape and

View File

@ -24,6 +24,8 @@
*/
package java.lang.invoke;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
@ -38,6 +40,8 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE;
final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
static final int ALIGN = $BoxType$.BYTES - 1;
#if[floatingPoint]
@ -529,6 +533,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
@ForceInline
static int index(ByteBuffer bb, int index) {
nioAccess.checkSegment(bb);
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
}
@ -536,7 +541,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static int indexRO(ByteBuffer bb, int index) {
if (UNSAFE.getBoolean(bb, BYTE_BUFFER_IS_READ_ONLY))
throw new ReadOnlyBufferException();
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
return index(bb, index);
}
@ForceInline

View File

@ -0,0 +1,512 @@
/*
* Copyright (c) 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
* 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 java.lang.invoke;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.vm.annotation.ForceInline;
import java.util.Objects;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
#warn
final class VarHandleMemoryAddressAs$Type$s {
static final boolean BE = UNSAFE.isBigEndian();
static final int VM_ALIGN = $BoxType$.BYTES - 1;
#if[floatingPoint]
@ForceInline
static $rawType$ convEndian(boolean big, $type$ v) {
$rawType$ rv = $Type$.$type$ToRaw$RawType$Bits(v);
return big == BE ? rv : $RawBoxType$.reverseBytes(rv);
}
@ForceInline
static $type$ convEndian(boolean big, $rawType$ rv) {
rv = big == BE ? rv : $RawBoxType$.reverseBytes(rv);
return $Type$.$rawType$BitsTo$Type$(rv);
}
#else[floatingPoint]
#if[byte]
@ForceInline
static $type$ convEndian(boolean big, $type$ n) {
return n;
}
#else[byte]
@ForceInline
static $type$ convEndian(boolean big, $type$ n) {
return big == BE ? n : $BoxType$.reverseBytes(n);
}
#end[byte]
#end[floatingPoint]
@ForceInline
static MemoryAddressProxy checkAddress(Object obb, long offset, long length, boolean ro) {
MemoryAddressProxy oo = (MemoryAddressProxy)Objects.requireNonNull(obb);
oo.checkAccess(offset, length, ro);
return oo;
}
@ForceInline
static long offset(MemoryAddressProxy bb, long offset, long alignmentMask) {
long address = offsetNoVMAlignCheck(bb, offset, alignmentMask);
if ((address & VM_ALIGN) != 0) {
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
}
return address;
}
@ForceInline
static long offsetNoVMAlignCheck(MemoryAddressProxy bb, long offset, long alignmentMask) {
long base = bb.unsafeGetOffset();
long address = base + offset;
//note: the offset portion has already been aligned-checked, by construction
if ((base & alignmentMask) != 0) {
throw VarHandleMemoryAddressBase.newIllegalStateExceptionForMisalignedAccess(address);
}
return address;
}
@ForceInline
static $type$ get0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
handle.be);
return $Type$.$rawType$BitsTo$Type$(rawValue);
#else[floatingPoint]
#if[byte]
return UNSAFE.get$Type$(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask));
#else[byte]
return UNSAFE.get$Type$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
handle.be);
#end[byte]
#end[floatingPoint]
}
@ForceInline
static void set0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
$Type$.$type$ToRaw$RawType$Bits(value),
handle.be);
#else[floatingPoint]
#if[byte]
UNSAFE.put$Type$(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
value);
#else[byte]
UNSAFE.put$Type$Unaligned(
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
value,
handle.be);
#end[byte]
#end[floatingPoint]
}
@ForceInline
static $type$ getVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
}
@ForceInline
static void setVolatile0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Volatile(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
@ForceInline
static $type$ getAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
}
@ForceInline
static void setRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
@ForceInline
static $type$ getOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
}
@ForceInline
static void setOpaque0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Opaque(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
#if[CAS]
@ForceInline
static boolean compareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.compareAndSet$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static $type$ compareAndExchange0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static $type$ compareAndExchangeAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static $type$ compareAndExchangeRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static boolean weakCompareAndSetPlain0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Plain(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ expected, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static $type$ getAndSet0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
@ForceInline
static $type$ getAndSetAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
@ForceInline
static $type$ getAndSetRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
#end[CAS]
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ delta) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ delta) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta)));
return expectedValue;
}
#end[AtomicAdd]
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value)));
return expectedValue;
}
@ForceInline
static $type$ getAndBitwiseAnd0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value)));
return expectedValue;
}
@ForceInline
static $type$ getAndBitwiseXor0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorRelease0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorAcquire0(VarHandleMemoryAddressBase handle, Object obb, long base, $type$ value) {
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value)));
return expectedValue;
}
#end[Bitwise]
}

View File

@ -28,7 +28,9 @@ package java.nio;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.ForceInline;
import java.util.Spliterator;
@ -213,13 +215,26 @@ public abstract class Buffer {
// NOTE: hoisted here for speed in JNI GetDirectBufferAddress
long address;
// Used by buffers generated by the memory access API (JEP-370)
final MemorySegmentProxy segment;
// Creates a new buffer with given address and capacity.
//
Buffer(long addr, int cap, MemorySegmentProxy segment) {
this.address = addr;
this.capacity = cap;
this.segment = segment;
}
// Creates a new buffer with the given mark, position, limit, and capacity,
// after checking invariants.
//
Buffer(int mark, int pos, int lim, int cap) { // package-private
Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
if (cap < 0)
throw createCapacityException(cap);
this.capacity = cap;
this.segment = segment;
limit(lim);
position(pos);
if (mark >= 0) {
@ -731,6 +746,13 @@ public abstract class Buffer {
mark = -1;
}
@ForceInline
final void checkSegment() {
if (segment != null) {
segment.checkValidState();
}
}
static {
// setup access to this package in SharedSecrets
SharedSecrets.setJavaNioAccess(
@ -739,6 +761,31 @@ public abstract class Buffer {
public JavaNioAccess.BufferPool getDirectBufferPool() {
return Bits.BUFFER_POOL;
}
@Override
public ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment) {
return new DirectByteBuffer(addr, cap, obj, segment);
}
@Override
public ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment) {
return new HeapByteBuffer(hb, offset, capacity, segment);
}
@Override
public Object getBufferBase(ByteBuffer bb) {
return bb.base();
}
@Override
public long getBufferAddress(ByteBuffer bb) {
return bb.address;
}
@Override
public void checkSegment(Buffer buffer) {
buffer.checkSegment();
}
});
}

View File

@ -28,6 +28,7 @@
package java.nio;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
@ -40,11 +41,11 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
#end[rw]
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb) { // package-private
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb, MemorySegmentProxy segment) { // package-private
#if[rw]
super(-1, 0,
bb.remaining() >> $LG_BYTES_PER_VALUE$,
bb.remaining() >> $LG_BYTES_PER_VALUE$);
bb.remaining() >> $LG_BYTES_PER_VALUE$, segment);
this.bb = bb;
// enforce limit == capacity
int cap = this.capacity();
@ -53,21 +54,21 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
assert (pos <= cap);
address = bb.address;
#else[rw]
super(bb);
super(bb, segment);
#end[rw]
}
ByteBufferAs$Type$Buffer$RW$$BO$(ByteBuffer bb,
int mark, int pos, int lim, int cap,
long addr)
long addr, MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap);
super(mark, pos, lim, cap, segment);
this.bb = bb;
address = addr;
assert address >= bb.address;
#else[rw]
super(bb, mark, pos, lim, cap, addr);
super(bb, mark, pos, lim, cap, addr, segment);
#end[rw]
}
@ -82,7 +83,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
long addr = byteOffset(pos);
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr);
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr, segment);
}
@Override
@ -93,7 +94,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
0,
length,
length,
byteOffset(index));
byteOffset(index), segment);
}
public $Type$Buffer duplicate() {
@ -102,7 +103,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
this.position(),
this.limit(),
this.capacity(),
address);
address, segment);
}
public $Type$Buffer asReadOnlyBuffer() {
@ -112,7 +113,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
this.position(),
this.limit(),
this.capacity(),
address);
address, segment);
#else[rw]
return duplicate();
#end[rw]
@ -130,12 +131,14 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
}
public $type$ get() {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()),
{#if[boB]?true:false});
return $fromBits$(x);
}
public $type$ get(int i) {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)),
{#if[boB]?true:false});
return $fromBits$(x);
@ -153,6 +156,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
public $Type$Buffer put($type$ x) {
#if[rw]
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y,
{#if[boB]?true:false});
@ -164,6 +168,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y,
{#if[boB]?true:false});
@ -237,7 +242,7 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
pos + start,
pos + end,
capacity(),
address);
address, segment);
}
#end[char]

View File

@ -33,6 +33,7 @@ class XXX {
private $type$ get$Type$(long a) {
try {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(null, a, bigEndian);
return $fromBits$(x);
} finally {
@ -61,6 +62,7 @@ class XXX {
private ByteBuffer put$Type$(long a, $type$ x) {
#if[rw]
try {
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(null, a, y, bigEndian);
} finally {
@ -104,13 +106,13 @@ class XXX {
0,
size,
size,
address + off))
address + off, segment))
: ($Type$Buffer)(new ByteBufferAs$Type$Buffer$RW$L(this,
-1,
0,
size,
size,
address + off)));
address + off, segment)));
} else {
return (nativeByteOrder
? ($Type$Buffer)(new Direct$Type$Buffer$RW$U(this,
@ -118,13 +120,13 @@ class XXX {
0,
size,
size,
off))
off, segment))
: ($Type$Buffer)(new Direct$Type$Buffer$RW$S(this,
-1,
0,
size,
size,
off)));
off, segment)));
}
}

View File

@ -30,6 +30,7 @@ package java.nio;
import java.io.FileDescriptor;
import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.VM;
import jdk.internal.ref.Cleaner;
import sun.nio.ch.DirectBuffer;
@ -112,7 +113,7 @@ class Direct$Type$Buffer$RW$$BO$
//
Direct$Type$Buffer$RW$(int cap) { // package-private
#if[rw]
super(-1, 0, cap, cap);
super(-1, 0, cap, cap, null);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
@ -145,8 +146,8 @@ class Direct$Type$Buffer$RW$$BO$
// Invoked to construct a direct ByteBuffer referring to the block of
// memory. A given arbitrary object may also be attached to the buffer.
//
Direct$Type$Buffer(long addr, int cap, Object ob) {
super(-1, 0, cap, cap);
Direct$Type$Buffer(long addr, int cap, Object ob, MemorySegmentProxy segment) {
super(-1, 0, cap, cap, segment);
address = addr;
cleaner = null;
att = ob;
@ -156,7 +157,7 @@ class Direct$Type$Buffer$RW$$BO$
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
//
private Direct$Type$Buffer(long addr, int cap) {
super(-1, 0, cap, cap);
super(-1, 0, cap, cap, null);
address = addr;
cleaner = null;
att = null;
@ -169,15 +170,15 @@ class Direct$Type$Buffer$RW$$BO$
protected Direct$Type$Buffer$RW$(int cap, long addr,
FileDescriptor fd,
Runnable unmapper,
boolean isSync)
boolean isSync, MemorySegmentProxy segment)
{
#if[rw]
super(-1, 0, cap, cap, fd, isSync);
super(-1, 0, cap, cap, fd, isSync, segment);
address = addr;
cleaner = Cleaner.create(this, unmapper);
att = null;
#else[rw]
super(cap, addr, fd, unmapper, isSync);
super(cap, addr, fd, unmapper, isSync, segment);
this.isReadOnly = true;
#end[rw]
}
@ -188,10 +189,10 @@ class Direct$Type$Buffer$RW$$BO$
//
Direct$Type$Buffer$RW$$BO$(DirectBuffer db, // package-private
int mark, int pos, int lim, int cap,
int off)
int off, MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap);
super(mark, pos, lim, cap, segment);
address = db.address() + off;
#if[byte]
cleaner = null;
@ -199,7 +200,7 @@ class Direct$Type$Buffer$RW$$BO$
Object attachment = db.attachment();
att = (attachment == null ? db : attachment);
#else[rw]
super(db, mark, pos, lim, cap, off);
super(db, mark, pos, lim, cap, off, segment);
this.isReadOnly = true;
#end[rw]
}
@ -216,7 +217,7 @@ class Direct$Type$Buffer$RW$$BO$
int rem = (pos <= lim ? lim - pos : 0);
int off = (pos << $LG_BYTES_PER_VALUE$);
assert (off >= 0);
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off);
return new Direct$Type$Buffer$RW$$BO$(this, -1, 0, rem, rem, off, segment);
}
@Override
@ -227,7 +228,7 @@ class Direct$Type$Buffer$RW$$BO$
0,
length,
length,
index);
index, segment);
}
public $Type$Buffer duplicate() {
@ -236,7 +237,7 @@ class Direct$Type$Buffer$RW$$BO$
this.position(),
this.limit(),
this.capacity(),
0);
0, segment);
}
public $Type$Buffer asReadOnlyBuffer() {
@ -246,7 +247,7 @@ class Direct$Type$Buffer$RW$$BO$
this.position(),
this.limit(),
this.capacity(),
0);
0, segment);
#else[rw]
return duplicate();
#end[rw]
@ -264,6 +265,7 @@ class Direct$Type$Buffer$RW$$BO$
public $type$ get() {
try {
checkSegment();
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(nextGetIndex()))));
} finally {
Reference.reachabilityFence(this);
@ -272,6 +274,7 @@ class Direct$Type$Buffer$RW$$BO$
public $type$ get(int i) {
try {
checkSegment();
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(checkIndex(i)))));
} finally {
Reference.reachabilityFence(this);
@ -290,6 +293,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer get($type$[] dst, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(offset, length, dst.length);
int pos = position();
@ -331,6 +335,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, dst.length);
@ -368,6 +373,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($type$ x) {
#if[rw]
try {
checkSegment();
UNSAFE.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x)));
} finally {
Reference.reachabilityFence(this);
@ -381,6 +387,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
try {
checkSegment();
UNSAFE.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x)));
} finally {
Reference.reachabilityFence(this);
@ -393,6 +400,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($Type$Buffer src) {
#if[rw]
checkSegment();
if (src instanceof Direct$Type$Buffer$BO$) {
if (src == this)
throw createSameBufferException();
@ -439,6 +447,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($type$[] src, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(offset, length, src.length);
int pos = position();
@ -480,6 +489,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, src.length);
@ -577,7 +587,7 @@ class Direct$Type$Buffer$RW$$BO$
pos + start,
pos + end,
capacity(),
offset);
offset, segment);
}
#end[char]

View File

@ -28,6 +28,7 @@
package java.nio;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
/**
#if[rw]
@ -58,47 +59,47 @@ class Heap$Type$Buffer$RW$
#end[rw]
*/
Heap$Type$Buffer$RW$(int cap, int lim) { // package-private
Heap$Type$Buffer$RW$(int cap, int lim, MemorySegmentProxy segment) { // package-private
#if[rw]
super(-1, 0, lim, cap, new $type$[cap], 0);
super(-1, 0, lim, cap, new $type$[cap], 0, segment);
/*
hb = new $type$[cap];
offset = 0;
*/
this.address = ARRAY_BASE_OFFSET;
#else[rw]
super(cap, lim);
super(cap, lim, segment);
this.isReadOnly = true;
#end[rw]
}
Heap$Type$Buffer$RW$($type$[] buf, int off, int len) { // package-private
Heap$Type$Buffer$RW$($type$[] buf, int off, int len, MemorySegmentProxy segment) { // package-private
#if[rw]
super(-1, off, off + len, buf.length, buf, 0);
super(-1, off, off + len, buf.length, buf, 0, segment);
/*
hb = buf;
offset = 0;
*/
this.address = ARRAY_BASE_OFFSET;
#else[rw]
super(buf, off, len);
super(buf, off, len, segment);
this.isReadOnly = true;
#end[rw]
}
protected Heap$Type$Buffer$RW$($type$[] buf,
int mark, int pos, int lim, int cap,
int off)
int off, MemorySegmentProxy segment)
{
#if[rw]
super(mark, pos, lim, cap, buf, off);
super(mark, pos, lim, cap, buf, off, segment);
/*
hb = buf;
offset = off;
*/
this.address = ARRAY_BASE_OFFSET + off * ARRAY_INDEX_SCALE;
#else[rw]
super(buf, mark, pos, lim, cap, off);
super(buf, mark, pos, lim, cap, off, segment);
this.isReadOnly = true;
#end[rw]
}
@ -110,7 +111,7 @@ class Heap$Type$Buffer$RW$
0,
rem,
rem,
this.position() + offset);
this.position() + offset, segment);
}
@Override
@ -121,7 +122,7 @@ class Heap$Type$Buffer$RW$
0,
length,
length,
index + offset);
index + offset, segment);
}
public $Type$Buffer duplicate() {
@ -130,7 +131,7 @@ class Heap$Type$Buffer$RW$
this.position(),
this.limit(),
this.capacity(),
offset);
offset, segment);
}
public $Type$Buffer asReadOnlyBuffer() {
@ -140,7 +141,7 @@ class Heap$Type$Buffer$RW$
this.position(),
this.limit(),
this.capacity(),
offset);
offset, segment);
#else[rw]
return duplicate();
#end[rw]
@ -159,20 +160,23 @@ class Heap$Type$Buffer$RW$
#end[byte]
public $type$ get() {
checkSegment();
return hb[ix(nextGetIndex())];
}
public $type$ get(int i) {
checkSegment();
return hb[ix(checkIndex(i))];
}
#if[streamableType]
$type$ getUnchecked(int i) {
return hb[ix(i)];
return hb[ix(i)];
}
#end[streamableType]
public $Type$Buffer get($type$[] dst, int offset, int length) {
checkSegment();
Objects.checkFromIndexSize(offset, length, dst.length);
int pos = position();
if (length > limit() - pos)
@ -183,6 +187,7 @@ class Heap$Type$Buffer$RW$
}
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
checkSegment();
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, dst.length);
System.arraycopy(hb, ix(index), dst, offset, length);
@ -201,6 +206,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($type$ x) {
#if[rw]
checkSegment();
hb[ix(nextPutIndex())] = x;
return this;
#else[rw]
@ -210,6 +216,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
checkSegment();
hb[ix(checkIndex(i))] = x;
return this;
#else[rw]
@ -219,6 +226,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($type$[] src, int offset, int length) {
#if[rw]
checkSegment();
Objects.checkFromIndexSize(offset, length, src.length);
int pos = position();
if (length > limit() - pos)
@ -233,6 +241,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($Type$Buffer src) {
#if[rw]
checkSegment();
if (src instanceof Heap$Type$Buffer) {
if (src == this)
throw createSameBufferException();
@ -264,6 +273,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
#if[rw]
checkSegment();
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, src.length);
System.arraycopy(src, offset, hb, ix(index), length);
@ -276,6 +286,7 @@ class Heap$Type$Buffer$RW$
#if[char]
public $Type$Buffer put(String src, int start, int end) {
checkSegment();
int length = end - start;
Objects.checkFromIndexSize(start, length, src.length());
if (isReadOnly())
@ -327,6 +338,7 @@ class Heap$Type$Buffer$RW$
#if[rw]
public char getChar() {
checkSegment();
return UNSAFE.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
}
@ -338,6 +350,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putChar(char x) {
#if[rw]
checkSegment();
UNSAFE.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
return this;
#else[rw]
@ -347,6 +360,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putChar(int i, char x) {
#if[rw]
checkSegment();
UNSAFE.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
return this;
#else[rw]
@ -364,13 +378,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (CharBuffer)(new ByteBufferAsCharBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -379,10 +393,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public short getShort() {
checkSegment();
return UNSAFE.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
}
public short getShort(int i) {
checkSegment();
return UNSAFE.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
}
@ -390,6 +406,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putShort(short x) {
#if[rw]
checkSegment();
UNSAFE.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
return this;
#else[rw]
@ -399,6 +416,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putShort(int i, short x) {
#if[rw]
checkSegment();
UNSAFE.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
return this;
#else[rw]
@ -416,13 +434,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (ShortBuffer)(new ByteBufferAsShortBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -431,10 +449,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public int getInt() {
checkSegment();
return UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
}
public int getInt(int i) {
checkSegment();
return UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
}
@ -442,6 +462,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putInt(int x) {
#if[rw]
checkSegment();
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian);
return this;
#else[rw]
@ -451,6 +472,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putInt(int i, int x) {
#if[rw]
checkSegment();
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
return this;
#else[rw]
@ -468,13 +490,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (IntBuffer)(new ByteBufferAsIntBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -483,10 +505,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public long getLong() {
checkSegment();
return UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
}
public long getLong(int i) {
checkSegment();
return UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
}
@ -494,6 +518,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putLong(long x) {
#if[rw]
checkSegment();
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian);
return this;
#else[rw]
@ -503,6 +528,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putLong(int i, long x) {
#if[rw]
checkSegment();
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
return this;
#else[rw]
@ -520,13 +546,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (LongBuffer)(new ByteBufferAsLongBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -535,11 +561,13 @@ class Heap$Type$Buffer$RW$
#if[rw]
public float getFloat() {
checkSegment();
int x = UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
return Float.intBitsToFloat(x);
}
public float getFloat(int i) {
checkSegment();
int x = UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
return Float.intBitsToFloat(x);
}
@ -548,6 +576,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putFloat(float x) {
#if[rw]
checkSegment();
int y = Float.floatToRawIntBits(x);
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian);
return this;
@ -558,6 +587,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putFloat(int i, float x) {
#if[rw]
checkSegment();
int y = Float.floatToRawIntBits(x);
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
return this;
@ -576,13 +606,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (FloatBuffer)(new ByteBufferAsFloatBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -591,11 +621,13 @@ class Heap$Type$Buffer$RW$
#if[rw]
public double getDouble() {
checkSegment();
long x = UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
return Double.longBitsToDouble(x);
}
public double getDouble(int i) {
checkSegment();
long x = UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
return Double.longBitsToDouble(x);
}
@ -604,6 +636,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putDouble(double x) {
#if[rw]
checkSegment();
long y = Double.doubleToRawLongBits(x);
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian);
return this;
@ -614,6 +647,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putDouble(int i, double x) {
#if[rw]
checkSegment();
long y = Double.doubleToRawLongBits(x);
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
return this;
@ -632,13 +666,13 @@ class Heap$Type$Buffer$RW$
0,
size,
size,
addr))
addr, segment))
: (DoubleBuffer)(new ByteBufferAsDoubleBuffer$RW$L(this,
-1,
0,
size,
size,
addr)));
addr, segment)));
}
@ -666,7 +700,7 @@ class Heap$Type$Buffer$RW$
pos + start,
pos + end,
capacity(),
offset);
offset, segment);
}
#end[char]

View File

@ -28,6 +28,8 @@ package java.nio;
import java.io.FileDescriptor;
import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
@ -88,21 +90,21 @@ public abstract class MappedByteBuffer
// This should only be invoked by the DirectByteBuffer constructors
//
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
FileDescriptor fd, boolean isSync) {
super(mark, pos, lim, cap);
FileDescriptor fd, boolean isSync, MemorySegmentProxy segment) {
super(mark, pos, lim, cap, segment);
this.fd = fd;
this.isSync = isSync;
}
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
boolean isSync) {
super(mark, pos, lim, cap);
boolean isSync, MemorySegmentProxy segment) {
super(mark, pos, lim, cap, segment);
this.fd = null;
this.isSync = isSync;
}
MappedByteBuffer(int mark, int pos, int lim, int cap) { // package-private
super(mark, pos, lim, cap);
MappedByteBuffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
super(mark, pos, lim, cap, segment);
this.fd = null;
this.isSync = false;
}

View File

@ -35,7 +35,7 @@ class StringCharBuffer // package-private
CharSequence str;
StringCharBuffer(CharSequence s, int start, int end) { // package-private
super(-1, start, end, s.length());
super(-1, start, end, s.length(), null);
int n = s.length();
Objects.checkFromToIndex(start, end, n);
str = s;
@ -68,7 +68,7 @@ class StringCharBuffer // package-private
int limit,
int cap,
int offset) {
super(mark, pos, limit, cap, null, offset);
super(mark, pos, limit, cap, null, offset, null);
str = s;
this.isReadOnly = true;
}

View File

@ -37,6 +37,7 @@ import java.util.stream.$Streamtype$Stream;
#end[streamableType]
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.util.ArraysSupport;
/**
@ -279,17 +280,25 @@ public abstract class $Type$Buffer
// backing array, and array offset
//
$Type$Buffer(int mark, int pos, int lim, int cap, // package-private
$type$[] hb, int offset)
$type$[] hb, int offset, MemorySegmentProxy segment)
{
super(mark, pos, lim, cap);
super(mark, pos, lim, cap, segment);
this.hb = hb;
this.offset = offset;
}
// Creates a new buffer with the given mark, position, limit, and capacity
//
$Type$Buffer(int mark, int pos, int lim, int cap) { // package-private
this(mark, pos, lim, cap, null, 0);
$Type$Buffer(int mark, int pos, int lim, int cap, MemorySegmentProxy segment) { // package-private
this(mark, pos, lim, cap, null, 0, segment);
}
// Creates a new buffer with given base, address and capacity
//
$Type$Buffer($type$[] hb, long addr, int cap, MemorySegmentProxy segment) { // package-private
super(addr, cap, segment);
this.hb = hb;
this.offset = 0;
}
@Override
@ -348,7 +357,7 @@ public abstract class $Type$Buffer
public static $Type$Buffer allocate(int capacity) {
if (capacity < 0)
throw createCapacityException(capacity);
return new Heap$Type$Buffer(capacity, capacity);
return new Heap$Type$Buffer(capacity, capacity, null);
}
/**
@ -393,7 +402,7 @@ public abstract class $Type$Buffer
int offset, int length)
{
try {
return new Heap$Type$Buffer(array, offset, length);
return new Heap$Type$Buffer(array, offset, length, null);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}

View File

@ -26,6 +26,8 @@
package jdk.internal.access;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.Map;
public interface JavaLangInvokeAccess {
@ -106,4 +108,41 @@ public interface JavaLangInvokeAccess {
MethodType[] invokerMethodTypes,
MethodType[] callSiteMethodTypes);
/**
* Returns a var handle view of a given memory address.
* Used by {@code jdk.internal.foreign.LayoutPath} and
* {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle memoryAddressViewVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides);
/**
* Returns the carrier associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
Class<?> memoryAddressCarrier(VarHandle handle);
/**
* Returns the alignment mask associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long memoryAddressAlignmentMask(VarHandle handle);
/**
* Returns the byte order associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
ByteOrder memoryAddressByteOrder(VarHandle handle);
/**
* Returns the offset associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long memoryAddressOffset(VarHandle handle);
/**
* Returns the strides associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long[] memoryAddressStrides(VarHandle handle);
}

View File

@ -25,6 +25,8 @@
package jdk.internal.access;
import jdk.internal.access.foreign.MemorySegmentProxy;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@ -39,4 +41,34 @@ public interface JavaNioAccess {
long getMemoryUsed();
}
BufferPool getDirectBufferPool();
/**
* Constructs a direct ByteBuffer referring to the block of memory starting
* at the given memory address and extending {@code cap} bytes.
* The {@code ob} parameter is an arbitrary object that is attached
* to the resulting buffer.
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
*/
ByteBuffer newDirectByteBuffer(long addr, int cap, Object obj, MemorySegmentProxy segment);
/**
* Constructs an heap ByteBuffer with given backing array, offset, capacity and segment.
* Used by {@code jdk.internal.foreignMemorySegmentImpl}.
*/
ByteBuffer newHeapByteBuffer(byte[] hb, int offset, int capacity, MemorySegmentProxy segment);
/**
* Used by {@code jdk.internal.foreign.Utils}.
*/
Object getBufferBase(ByteBuffer bb);
/**
* Used by {@code jdk.internal.foreign.Utils}.
*/
long getBufferAddress(ByteBuffer bb);
/**
* Used by byte buffer var handle views.
*/
void checkSegment(Buffer buffer);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 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
* 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 jdk.internal.access.foreign;
/**
* This proxy interface is required to allow instances of the {@code MemoryAddress} interface (which is defined inside
* an incubating module) to be accessed from the memory access var handles.
*/
public interface MemoryAddressProxy {
/**
* Check that memory access is within spatial and temporal bounds.
* @throws IllegalStateException if underlying segment has been closed already.
* @throws IndexOutOfBoundsException if access is out-of-bounds.
*/
void checkAccess(long offset, long length, boolean readOnly);
long unsafeGetOffset();
Object unsafeGetBase();
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 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
* 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 jdk.internal.access.foreign;
/**
* This proxy interface is required to allow instances of the {@code MemorySegment} interface (which is defined inside
* an incubating module) to be accessed from the memory access var handles.
*/
public interface MemorySegmentProxy {
void checkValidState();
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 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
* 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 jdk.internal.access.foreign;
/**
* This proxy interface is required to allow instances of the {@code FileChannelImpl.Unmapper} interface (which is a non-public class
* inside the {@code sun.nio.ch} package) to be accessed from the mapped memory segment factory.
*/
public interface UnmapperProxy {
long address();
void unmap();
}

View File

@ -134,7 +134,8 @@ module java.base {
// see make/gensrc/GenModuleInfo.gmk
exports sun.invoke.util to
jdk.compiler;
jdk.compiler,
jdk.incubator.foreign;
exports com.sun.security.ntlm to
java.security.sasl;
exports jdk.internal to
@ -149,7 +150,10 @@ module java.base {
java.naming,
java.rmi,
jdk.jlink,
jdk.net;
jdk.net,
jdk.incubator.foreign;
exports jdk.internal.access.foreign to
jdk.incubator.foreign;
exports jdk.internal.event to
jdk.jfr;
exports jdk.internal.jimage to
@ -202,7 +206,8 @@ module java.base {
jdk.scripting.nashorn,
jdk.scripting.nashorn.shell,
jdk.unsupported,
jdk.internal.vm.ci;
jdk.internal.vm.ci,
jdk.incubator.foreign;
exports jdk.internal.module to
java.instrument,
java.management.rmi,
@ -260,7 +265,8 @@ module java.base {
java.management,
jdk.crypto.cryptoki,
jdk.net,
jdk.sctp;
jdk.sctp,
jdk.incubator.foreign;
exports sun.nio.cs to
jdk.charsets;
exports sun.reflect.annotation to
@ -276,7 +282,8 @@ module java.base {
java.sql.rowset;
exports sun.security.action to
java.desktop,
java.security.jgss;
java.security.jgss,
jdk.incubator.foreign;
exports sun.security.internal.interfaces to
jdk.crypto.cryptoki;
exports sun.security.internal.spec to

View File

@ -42,6 +42,7 @@ import java.nio.channels.NonWritableChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Objects;
import jdk.internal.access.JavaIOFileDescriptorAccess;
import jdk.internal.access.JavaNioAccess;
@ -51,6 +52,8 @@ import jdk.internal.misc.Unsafe;
import jdk.internal.ref.Cleaner;
import jdk.internal.ref.CleanerFactory;
import jdk.internal.access.foreign.UnmapperProxy;
public class FileChannelImpl
extends FileChannel
{
@ -863,27 +866,39 @@ public class FileChannelImpl
// -- Memory-mapped buffers --
private static abstract class Unmapper
implements Runnable
implements Runnable, UnmapperProxy
{
// may be required to close file
private static final NativeDispatcher nd = new FileDispatcherImpl();
private volatile long address;
protected final long size;
protected final int cap;
protected final long cap;
private final FileDescriptor fd;
private final int pagePosition;
private Unmapper(long address, long size, int cap,
FileDescriptor fd)
private Unmapper(long address, long size, long cap,
FileDescriptor fd, int pagePosition)
{
assert (address != 0);
this.address = address;
this.size = size;
this.cap = cap;
this.fd = fd;
this.pagePosition = pagePosition;
}
@Override
public long address() {
return address;
}
@Override
public void run() {
unmap();
}
public void unmap() {
if (address == 0)
return;
unmap0(address, size);
@ -911,9 +926,9 @@ public class FileChannelImpl
static volatile long totalSize;
static volatile long totalCapacity;
public DefaultUnmapper(long address, long size, int cap,
FileDescriptor fd) {
super(address, size, cap, fd);
public DefaultUnmapper(long address, long size, long cap,
FileDescriptor fd, int pagePosition) {
super(address, size, cap, fd, pagePosition);
incrementStats();
}
@ -940,9 +955,9 @@ public class FileChannelImpl
static volatile long totalSize;
static volatile long totalCapacity;
public SyncUnmapper(long address, long size, int cap,
FileDescriptor fd) {
super(address, size, cap, fd);
public SyncUnmapper(long address, long size, long cap,
FileDescriptor fd, int pagePosition) {
super(address, size, cap, fd, pagePosition);
incrementStats();
}
@ -968,11 +983,44 @@ public class FileChannelImpl
cl.clean();
}
private static final int MAP_INVALID = -1;
private static final int MAP_RO = 0;
private static final int MAP_RW = 1;
private static final int MAP_PV = 2;
public MappedByteBuffer map(MapMode mode, long position, long size)
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
if (size > Integer.MAX_VALUE)
throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null"));
int prot = toProt(mode);
Unmapper unmapper = mapInternal(mode, position, size, prot, isSync);
if (unmapper == null) {
// a valid file descriptor is not required
FileDescriptor dummy = new FileDescriptor();
if ((!writable) || (prot == MAP_RO))
return Util.newMappedByteBufferR(0, 0, dummy, null, isSync);
else
return Util.newMappedByteBuffer(0, 0, dummy, null, isSync);
} else if ((!writable) || (prot == MAP_RO)) {
return Util.newMappedByteBufferR((int)unmapper.cap,
unmapper.address + unmapper.pagePosition,
unmapper.fd,
unmapper, isSync);
} else {
return Util.newMappedByteBuffer((int)unmapper.cap,
unmapper.address + unmapper.pagePosition,
unmapper.fd,
unmapper, isSync);
}
}
public Unmapper mapInternal(MapMode mode, long position, long size) throws IOException {
boolean isSync = isSync(Objects.requireNonNull(mode, "Mode is null"));
int prot = toProt(mode);
return mapInternal(mode, position, size, prot, isSync);
}
private Unmapper mapInternal(MapMode mode, long position, long size, int prot, boolean isSync)
throws IOException
{
ensureOpen();
@ -984,35 +1032,8 @@ public class FileChannelImpl
throw new IllegalArgumentException("Negative size");
if (position + size < 0)
throw new IllegalArgumentException("Position + size overflow");
if (size > Integer.MAX_VALUE)
throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE");
int imode;
boolean isSync = false;
if (mode == MapMode.READ_ONLY)
imode = MAP_RO;
else if (mode == MapMode.READ_WRITE)
imode = MAP_RW;
else if (mode == MapMode.PRIVATE)
imode = MAP_PV;
else if (mode == ExtendedMapMode.READ_ONLY_SYNC) {
imode = MAP_RO;
isSync = true;
} else if (mode == ExtendedMapMode.READ_WRITE_SYNC) {
imode = MAP_RW;
isSync = true;
} else {
throw new UnsupportedOperationException();
}
if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable)
throw new NonWritableChannelException();
if (!readable)
throw new NonReadableChannelException();
// reject SYNC request if writeback is not enabled for this platform
if (isSync && !Unsafe.isWritebackEnabled()) {
throw new UnsupportedOperationException();
}
checkMode(mode, prot, isSync);
long addr = -1;
int ti = -1;
try {
@ -1045,13 +1066,7 @@ public class FileChannelImpl
}
if (size == 0) {
addr = 0;
// a valid file descriptor is not required
FileDescriptor dummy = new FileDescriptor();
if ((!writable) || (imode == MAP_RO))
return Util.newMappedByteBufferR(0, 0, dummy, null, isSync);
else
return Util.newMappedByteBuffer(0, 0, dummy, null, isSync);
return null;
}
pagePosition = (int)(position % allocationGranularity);
@ -1059,7 +1074,7 @@ public class FileChannelImpl
mapSize = size + pagePosition;
try {
// If map0 did not throw an exception, the address is valid
addr = map0(imode, mapPosition, mapSize, isSync);
addr = map0(prot, mapPosition, mapSize, isSync);
} catch (OutOfMemoryError x) {
// An OutOfMemoryError may indicate that we've exhausted
// memory so force gc and re-attempt map
@ -1070,7 +1085,7 @@ public class FileChannelImpl
Thread.currentThread().interrupt();
}
try {
addr = map0(imode, mapPosition, mapSize, isSync);
addr = map0(prot, mapPosition, mapSize, isSync);
} catch (OutOfMemoryError y) {
// After a second OOME, fail
throw new IOException("Map failed", y);
@ -1090,29 +1105,53 @@ public class FileChannelImpl
assert (IOStatus.checkAll(addr));
assert (addr % allocationGranularity == 0);
int isize = (int)size;
Unmapper um = (isSync
? new SyncUnmapper(addr, mapSize, isize, mfd)
: new DefaultUnmapper(addr, mapSize, isize, mfd));
if ((!writable) || (imode == MAP_RO)) {
return Util.newMappedByteBufferR(isize,
addr + pagePosition,
mfd,
um,
isSync);
} else {
return Util.newMappedByteBuffer(isize,
addr + pagePosition,
mfd,
um,
isSync);
}
? new SyncUnmapper(addr, mapSize, size, mfd, pagePosition)
: new DefaultUnmapper(addr, mapSize, size, mfd, pagePosition));
return um;
} finally {
threads.remove(ti);
endBlocking(IOStatus.checkAll(addr));
}
}
private boolean isSync(MapMode mode) {
return mode == ExtendedMapMode.READ_ONLY_SYNC ||
mode == ExtendedMapMode.READ_WRITE_SYNC;
}
private int toProt(MapMode mode) {
int prot;
if (mode == MapMode.READ_ONLY) {
prot = MAP_RO;
} else if (mode == MapMode.READ_WRITE) {
prot = MAP_RW;
} else if (mode == MapMode.PRIVATE) {
prot = MAP_PV;
} else if (mode == ExtendedMapMode.READ_ONLY_SYNC) {
prot = MAP_RO;
} else if (mode == ExtendedMapMode.READ_WRITE_SYNC) {
prot = MAP_RW;
} else {
prot = MAP_INVALID;
}
return prot;
}
private void checkMode(MapMode mode, int prot, boolean isSync) {
if (prot == MAP_INVALID) {
throw new UnsupportedOperationException();
}
if ((mode != MapMode.READ_ONLY) && mode != ExtendedMapMode.READ_ONLY_SYNC && !writable)
throw new NonWritableChannelException();
if (!readable)
throw new NonReadableChannelException();
// reject SYNC request if writeback is not enabled for this platform
if (isSync && !Unsafe.isWritebackEnabled()) {
throw new UnsupportedOperationException();
}
}
/**
* Invoked by sun.management.ManagementFactoryHelper to create the management
* interface for mapped buffers.

View File

@ -37,6 +37,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.TerminatingThreadLocal;
import jdk.internal.misc.Unsafe;
import sun.security.action.GetPropertyAction;
@ -416,7 +417,7 @@ public class Util {
long.class,
FileDescriptor.class,
Runnable.class,
boolean.class });
boolean.class, MemorySegmentProxy.class});
ctor.setAccessible(true);
directByteBufferConstructor = ctor;
} catch (ClassNotFoundException |
@ -443,7 +444,7 @@ public class Util {
addr,
fd,
unmapper,
isSync});
isSync, null});
} catch (InstantiationException |
IllegalAccessException |
InvocationTargetException e) {
@ -464,7 +465,7 @@ public class Util {
long.class,
FileDescriptor.class,
Runnable.class,
boolean.class });
boolean.class, MemorySegmentProxy.class });
ctor.setAccessible(true);
directByteBufferRConstructor = ctor;
} catch (ClassNotFoundException |
@ -491,7 +492,7 @@ public class Util {
addr,
fd,
unmapper,
isSync});
isSync, null});
} catch (InstantiationException |
IllegalAccessException |
InvocationTargetException e) {

View File

@ -373,7 +373,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private static List<CipherSuite> getApplicableCipherSuites(
Collection<CipherSuite> allowedCipherSuites,
List<ProtocolVersion> protocols) {
TreeSet<CipherSuite> suites = new TreeSet<>();
LinkedHashSet<CipherSuite> suites = new LinkedHashSet<>();
if (protocols != null && (!protocols.isEmpty())) {
for (CipherSuite suite : allowedCipherSuites) {
if (!suite.isAvailable()) {

View File

@ -224,9 +224,14 @@ public final class Main {
System.gc();
}
HotSpotGC graal_gc = runtime.getGarbageCollector();
int def = graal_gc.ordinal() + 1;
String name = "CollectedHeap::" + graal_gc.name();
HotSpotGC graalGC = runtime.getGarbageCollector();
// Prior to JDK 14, the Graal HotSpotGC enum order matched the JDK CollectedHeap enum
// order, so using the ordinal value worked fine. In JDK 14, CMS was removed on the
// JDK side, so we need a symbolic lookup of the JDK value.
int def = graalGC.ordinal() + 1;
// The GC names are spelled the same in both enums, so no clever remapping is needed
// here.
String name = "CollectedHeap::" + graalGC.name();
int gc = graalHotSpotVMConfig.getConstant(name, Integer.class, def);
BinaryContainer binaryContainer = new BinaryContainer(graalOptions, graalHotSpotVMConfig, graphBuilderConfig, gc, JVM_VERSION);

View File

@ -0,0 +1,178 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteOrder;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
abstract class AbstractLayout implements MemoryLayout {
private final OptionalLong size;
final long alignment;
private final Optional<String> name;
public AbstractLayout(OptionalLong size, long alignment, Optional<String> name) {
this.size = size;
this.alignment = alignment;
this.name = name;
}
Optional<String> optName() {
return name;
}
@Override
public AbstractLayout withName(String name) {
return dup(alignment, Optional.of(name));
}
@Override
public final Optional<String> name() {
return name;
}
abstract AbstractLayout dup(long alignment, Optional<String> name);
@Override
public AbstractLayout withBitAlignment(long alignmentBits) {
checkAlignment(alignmentBits);
return dup(alignmentBits, name);
}
void checkAlignment(long alignmentBitCount) {
if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
(alignmentBitCount < 8)) { //alignment must be greater than 8
throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
}
}
static void checkSize(long size) {
checkSize(size, false);
}
static void checkSize(long size, boolean includeZero) {
if (size < 0 || (!includeZero && size == 0)) {
throw new IllegalArgumentException("Invalid size for layout: " + size);
}
}
@Override
public final long bitAlignment() {
return alignment;
}
@Override
public long bitSize() {
return size.orElseThrow(this::badSizeException);
}
static OptionalLong optSize(MemoryLayout layout) {
return ((AbstractLayout)layout).size;
}
private UnsupportedOperationException badSizeException() {
return new UnsupportedOperationException("Cannot compute size of a layout which is, or depends on a sequence layout with unspecified size");
}
String decorateLayoutString(String s) {
if (name.isPresent()) {
s = String.format("%s(%s)", s, name.get());
}
if (!hasNaturalAlignment()) {
s = alignment + "%" + s;
}
return s;
}
boolean hasNaturalAlignment() {
return size.isPresent() && size.getAsLong() == alignment;
}
@Override
public int hashCode() {
return name.hashCode() << Long.hashCode(alignment);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AbstractLayout)) {
return false;
}
return Objects.equals(name, ((AbstractLayout)other).name) &&
Objects.equals(alignment, ((AbstractLayout)other).alignment);
}
/*** Helper constants for implementing Layout::describeConstable ***/
public static final DirectMethodHandleDesc BSM_GET_STATIC_FINAL
= ConstantDescs.ofConstantBootstrap(ConstantDescs.CD_ConstantBootstraps, "getStaticFinal",
ConstantDescs.CD_Object, ConstantDescs.CD_Class);
static final ClassDesc CD_LAYOUT = MemoryLayout.class.describeConstable().get();
static final ClassDesc CD_VALUE_LAYOUT = ValueLayout.class.describeConstable().get();
static final ClassDesc CD_SEQUENCE_LAYOUT = SequenceLayout.class.describeConstable().get();
static final ClassDesc CD_GROUP_LAYOUT = GroupLayout.class.describeConstable().get();
static final ClassDesc CD_BYTEORDER = ByteOrder.class.describeConstable().get();
static final ConstantDesc BIG_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "BIG_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
static final ConstantDesc LITTLE_ENDIAN = DynamicConstantDesc.ofNamed(BSM_GET_STATIC_FINAL, "LITTLE_ENDIAN", CD_BYTEORDER, CD_BYTEORDER);
static final MethodHandleDesc MH_PADDING = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofPaddingBits",
MethodTypeDesc.of(CD_LAYOUT, ConstantDescs.CD_long));
static final MethodHandleDesc MH_VALUE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofValueBits",
MethodTypeDesc.of(CD_VALUE_LAYOUT, ConstantDescs.CD_long, CD_BYTEORDER));
static final MethodHandleDesc MH_SIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofSequence",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, ConstantDescs.CD_long, CD_LAYOUT));
static final MethodHandleDesc MH_UNSIZED_SEQUENCE = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofSequence",
MethodTypeDesc.of(CD_SEQUENCE_LAYOUT, CD_LAYOUT));
static final MethodHandleDesc MH_STRUCT = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofStruct",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
static final MethodHandleDesc MH_UNION = MethodHandleDesc.ofMethod(DirectMethodHandleDesc.Kind.INTERFACE_STATIC, CD_LAYOUT, "ofUnion",
MethodTypeDesc.of(CD_GROUP_LAYOUT, CD_LAYOUT.arrayType()));
}

View File

@ -0,0 +1,210 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.lang.constant.ConstantDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.LongBinaryOperator;
import java.util.stream.Collectors;
/**
* A group layout is used to combine together multiple <em>member layouts</em>. There are two ways in which member layouts
* can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a <em>struct</em>
* (see {@link MemoryLayout#ofStruct(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset,
* the resulting group layout is said to be a <em>union</em> (see {@link MemoryLayout#ofUnion(MemoryLayout...)}).
*
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code GroupLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
public final class GroupLayout extends AbstractLayout {
/**
* The group kind.
*/
enum Kind {
/**
* A 'struct' kind.
*/
STRUCT("", MH_STRUCT, Long::sum),
/**
* A 'union' kind.
*/
UNION("|", MH_UNION, Math::max);
final String delimTag;
final MethodHandleDesc mhDesc;
final LongBinaryOperator sizeOp;
Kind(String delimTag, MethodHandleDesc mhDesc, LongBinaryOperator sizeOp) {
this.delimTag = delimTag;
this.mhDesc = mhDesc;
this.sizeOp = sizeOp;
}
OptionalLong sizeof(List<MemoryLayout> elems) {
long size = 0;
for (MemoryLayout elem : elems) {
if (AbstractLayout.optSize(elem).isPresent()) {
size = sizeOp.applyAsLong(size, elem.bitSize());
} else {
return OptionalLong.empty();
}
}
return OptionalLong.of(size);
}
long alignof(List<MemoryLayout> elems) {
return elems.stream().mapToLong(MemoryLayout::bitAlignment).max() // max alignment in case we have member layouts
.orElse(1); // or minimal alignment if no member layout is given
}
}
private final Kind kind;
private final List<MemoryLayout> elements;
GroupLayout(Kind kind, List<MemoryLayout> elements) {
this(kind, elements, kind.alignof(elements), Optional.empty());
}
GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
super(kind.sizeof(elements), alignment, name);
this.kind = kind;
this.elements = elements;
}
/**
* Returns the member layouts associated with this group.
*
* @apiNote the order in which member layouts are returned is the same order in which member layouts have
* been passed to one of the group layout factory methods (see {@link MemoryLayout#ofStruct(MemoryLayout...)},
* {@link MemoryLayout#ofUnion(MemoryLayout...)}).
*
* @return the member layouts associated with this group.
*/
public List<MemoryLayout> memberLayouts() {
return Collections.unmodifiableList(elements);
}
@Override
public String toString() {
return decorateLayoutString(elements.stream()
.map(Object::toString)
.collect(Collectors.joining(kind.delimTag, "[", "]")));
}
/**
* Is this group layout a <em>struct</em>?
*
* @return true, if this group layout is a <em>struct</em>.
*/
public boolean isStruct() {
return kind == Kind.STRUCT;
}
/**
* Is this group layout a <em>union</em>?
*
* @return true, if this group layout is a <em>union</em>.
*/
public boolean isUnion() {
return kind == Kind.UNION;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof GroupLayout)) {
return false;
}
GroupLayout g = (GroupLayout)other;
return kind.equals(g.kind) && elements.equals(g.elements);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), kind, elements);
}
@Override
GroupLayout dup(long alignment, Optional<String> name) {
return new GroupLayout(kind, elements, alignment, name);
}
@Override
boolean hasNaturalAlignment() {
return alignment == kind.alignof(elements);
}
@Override
public Optional<DynamicConstantDesc<GroupLayout>> describeConstable() {
ConstantDesc[] constants = new ConstantDesc[1 + elements.size()];
constants[0] = kind.mhDesc;
for (int i = 0 ; i < elements.size() ; i++) {
constants[i + 1] = elements.get(i).describeConstable().get();
}
return Optional.of(DynamicConstantDesc.ofNamed(
ConstantDescs.BSM_INVOKE, kind.name().toLowerCase(),
CD_GROUP_LAYOUT, constants));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withName(String name) {
return (GroupLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public GroupLayout withBitAlignment(long alignmentBits) {
return (GroupLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import jdk.internal.foreign.MemoryAddressImpl;
/**
* A memory address encodes an offset within a given {@link MemorySegment}. Memory addresses are typically obtained
* using the {@link MemorySegment#baseAddress()} method; such addresses can then be adjusted as required,
* using {@link MemoryAddress#offset(long)}.
* <p>
* A memory address is typically used as the first argument in a memory access var handle call, to perform some operation
* on the underlying memory backing a given memory segment. Since a memory address is always associated with a memory segment,
* such access operations are always subject to spatial and temporal checks as enforced by the address' owning memory region.
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemoryAddress} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemoryAddress} directly.
*
* @apiNote In the future, if the Java language permits, {@link MemoryAddress}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
*/
public interface MemoryAddress {
/**
* Creates a new memory address with given offset (in bytes) from current one.
* @param l specified offset (in bytes), relative to this address, which should be used to create the new address.
* @return a new memory address with given offset from current one.
*/
MemoryAddress offset(long l);
/**
* The offset of this memory address into the underlying segment.
*
* @return the offset
*/
long offset();
/**
* The memory segment this address belongs to.
* @return The memory segment this address belongs to.
*/
MemorySegment segment();
/**
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
* object is also a address, and it is equal to this address.
*
* @param that the object to be compared for equality with this address.
* @return {@code true} if the specified object is equal to this address.
*/
@Override
boolean equals(Object that);
/**
* Returns the hash code value for this address.
* @return the hash code value for this address.
*/
@Override
int hashCode();
/**
* Perform bulk copy from source address to target address. More specifically, the bytes at addresses {@code src}
* through {@code src.offset(bytes - 1)} are copied into addresses {@code dst} through {@code dst.offset(bytes - 1)}.
* If the source and address ranges overlap, then the copying is performed as if the bytes at addresses {@code src}
* through {@code src.offset(bytes - 1)} were first copied into a temporary segment with size {@code bytes},
* and then the contents of the temporary segment were copied into the bytes at addresses {@code dst} through {@code dst.offset(bytes - 1)}.
* @param src the source address.
* @param dst the target address.
* @param bytes the number of bytes to be copied.
* @throws IndexOutOfBoundsException if {@code bytes < 0}, or if it is greater than the size of the segments
* associated with either {@code src} or {@code dst}.
* @throws IllegalStateException if either the source address or the target address belong to memory segments
* which have been already closed, or if access occurs from a thread other than the thread owning either segment.
* @throws UnsupportedOperationException if {@code dst} is associated with a read-only segment (see {@link MemorySegment#isReadOnly()}).
*/
static void copy(MemoryAddress src, MemoryAddress dst, long bytes) {
MemoryAddressImpl.copy((MemoryAddressImpl)src, (MemoryAddressImpl)dst, bytes);
}
}

View File

@ -0,0 +1,268 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.Utils;
import sun.invoke.util.Wrapper;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
/**
* This class defines several factory methods for constructing and combining memory access var handles.
* To obtain a memory access var handle, clients must start from one of the <em>leaf</em> methods
* (see {@link MemoryHandles#varHandle(Class, ByteOrder)},
* {@link MemoryHandles#varHandle(Class, long, ByteOrder)}). This determines the variable type
* (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the
* byte order associated to a memory access var handle. The resulting memory access var handle can then be combined in various ways
* to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type
* (of type {@link MemoryAddress}), and zero or more {@code long} coordinate types, which can be used to emulate
* multi-dimensional array indexing.
* <p>
* As an example, consider the memory layout expressed by a {@link SequenceLayout} instance constructed as follows:
* <blockquote><pre>{@code
SequenceLayout seq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
));
* }</pre></blockquote>
* To access the member layout named {@code value}, we can construct a memory access var handle as follows:
* <blockquote><pre>{@code
VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemoryAddress) -> int
handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
* }</pre></blockquote>
*
* <h2>Addressing mode</h2>
*
* The final memory location accessed by a memory access var handle can be computed as follows:
*
* <blockquote><pre>{@code
address = base + offset
* }</pre></blockquote>
*
* where {@code base} denotes the address expressed by the {@link MemoryAddress} access coordinate, and {@code offset}
* can be expressed in the following form:
*
* <blockquote><pre>{@code
offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
* }</pre></blockquote>
*
* where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as optional {@code long}
* access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
* <em>static</em> constants which are can be acquired through the {@link MemoryHandles#withOffset(VarHandle, long)}
* and the {@link MemoryHandles#withStride(VarHandle, long)} combinators, respectively.
*
* <h2><a id="memaccess-mode"></a>Alignment and access modes</h2>
*
* A memory access var handle is associated with an access size {@code S} and an alignment constraint {@code B}
* (both expressed in bytes). We say that a memory access operation is <em>fully aligned</em> if it occurs
* at a memory address {@code A} which is compatible with both alignment constraints {@code S} and {@code B}.
* If access is fully aligned then following access modes are supported and are
* guaranteed to support atomic access:
* <ul>
* <li>read write access modes for all {@code T}, with the exception of
* access modes {@code get} and {@code set} for {@code long} and
* {@code double} on 32-bit platforms.
* <li>atomic update access modes for {@code int}, {@code long},
* {@code float} or {@code double}.
* (Future major platform releases of the JDK may support additional
* types for certain currently unsupported access modes.)
* <li>numeric atomic update access modes for {@code int} and {@code long}.
* (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.)
* <li>bitwise atomic update access modes for {@code int} and {@code long}.
* (Future major platform releases of the JDK may support additional
* numeric types for certain currently unsupported access modes.)
* </ul>
*
* If {@code T} is {@code float} or {@code double} then atomic
* update access modes compare values using their bitwise representation
* (see {@link Float#floatToRawIntBits} and
* {@link Double#doubleToRawLongBits}, respectively).
* <p>
* Alternatively, a memory access operation is <em>partially aligned</em> if it occurs at a memory address {@code A}
* which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the
* {@code get} and {@code set} access modes will result in an {@code IllegalStateException}. If access is partially aligned,
* atomic access is only guaranteed with respect to the largest power of two that divides the GCD of {@code A} and {@code S}.
* <p>
* Finally, in all other cases, we say that a memory access operation is <em>misaligned</em>; in such cases an
* {@code IllegalStateException} is thrown, irrespective of the access mode being used.
*/
public final class MemoryHandles {
private final static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private MemoryHandles() {
//sorry, just the one!
}
/**
* Creates a memory access var handle with the given carrier type and byte order.
*
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
* and its variable type is set by the given carrier type.
*
* The alignment constraint for the resulting memory access var handle is the same as the in memory size of the
* carrier type, and the accessed offset is set at zero.
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
*
* @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
* {@code float}, {@code long}, and {@code double}.
* @param byteOrder the required byte order.
* @return the new memory access var handle.
* @throws IllegalArgumentException when an illegal carrier type is used
*/
public static VarHandle varHandle(Class<?> carrier, ByteOrder byteOrder) {
checkCarrier(carrier);
return varHandle(carrier,
carrierSize(carrier),
byteOrder);
}
/**
* Creates a memory access var handle with the given carrier type, alignment constraint, and byte order.
*
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
* and its variable type is set by the given carrier type.
*
* The accessed offset is zero.
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
*
* @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
* {@code float}, {@code long}, and {@code double}.
* @param alignmentBytes the alignment constraint (in bytes). Must be a power of two.
* @param byteOrder the required byte order.
* @return the new memory access var handle.
* @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two.
*/
public static VarHandle varHandle(Class<?> carrier, long alignmentBytes, ByteOrder byteOrder) {
checkCarrier(carrier);
if (alignmentBytes <= 0
|| (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2?
throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
}
return JLI.memoryAddressViewVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{});
}
/**
* Creates a memory access var handle with a fixed offset added to the accessed offset. That is,
* if the target memory access var handle accesses a memory location at offset <em>O</em>, the new memory access var
* handle will access a memory location at offset <em>O' + O</em>.
*
* The resulting memory access var handle will feature the same access coordinates as the ones in the target memory access var handle.
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
*
* @param target the target memory access handle to access after the offset adjustment.
* @param bytesOffset the offset, in bytes. Must be positive or zero.
* @return the new memory access var handle.
* @throws IllegalArgumentException when the target var handle is not a memory access var handle,
* or when {@code bytesOffset < 0}, or otherwise incompatible with the alignment constraint.
*/
public static VarHandle withOffset(VarHandle target, long bytesOffset) {
if (bytesOffset < 0) {
throw new IllegalArgumentException("Illegal offset: " + bytesOffset);
}
long alignMask = JLI.memoryAddressAlignmentMask(target);
if ((bytesOffset & alignMask) != 0) {
throw new IllegalArgumentException("Offset " + bytesOffset + " does not conform to alignment " + (alignMask + 1));
}
return JLI.memoryAddressViewVarHandle(
JLI.memoryAddressCarrier(target),
alignMask,
JLI.memoryAddressByteOrder(target),
JLI.memoryAddressOffset(target) + bytesOffset,
JLI.memoryAddressStrides(target));
}
/**
* Creates a memory access var handle with a <em>variable</em> offset added to the accessed offset.
* That is, if the target memory access var handle accesses a memory location at offset <em>O</em>,
* the new memory access var handle will access a memory location at offset <em>(S * X) + O</em>, where <em>S</em>
* is a constant <em>stride</em>, whereas <em>X</em> is a dynamic value that will be provided as an additional access
* coordinate (of type {@code long}). The new access coordinate will be <em>prepended</em> to the ones available
* in the target memory access var handles (if any).
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
*
* @param target the target memory access handle to access after the scale adjustment.
* @param bytesStride the stride, in bytes, by which to multiply the coordinate value. Must be greater than zero.
* @return the new memory access var handle.
* @throws IllegalArgumentException when the target var handle is not a memory access var handle,
* or if {@code bytesStride <= 0}, or otherwise incompatible with the alignment constraint.
*/
public static VarHandle withStride(VarHandle target, long bytesStride) {
if (bytesStride == 0) {
throw new IllegalArgumentException("Stride must be positive: " + bytesStride);
}
long alignMask = JLI.memoryAddressAlignmentMask(target);
if ((bytesStride & alignMask) != 0) {
throw new IllegalArgumentException("Stride " + bytesStride + " does not conform to alignment " + (alignMask + 1));
}
long offset = JLI.memoryAddressOffset(target);
long[] strides = JLI.memoryAddressStrides(target);
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 1, strides.length);
newStrides[0] = bytesStride;
return JLI.memoryAddressViewVarHandle(
JLI.memoryAddressCarrier(target),
alignMask,
JLI.memoryAddressByteOrder(target),
offset,
newStrides);
}
private static void checkCarrier(Class<?> carrier) {
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
throw new IllegalArgumentException("Illegal carrier: " + carrier.getSimpleName());
}
}
private static long carrierSize(Class<?> carrier) {
long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth());
return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new);
}
}

View File

@ -0,0 +1,445 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import jdk.internal.foreign.LayoutPath;
import jdk.internal.foreign.Utils;
import java.lang.constant.Constable;
import java.lang.constant.DynamicConstantDesc;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A memory layout can be used to describe the contents of a memory segment in a <em>language neutral</em> fashion.
* There are two leaves in the layout hierarchy, <em>value layouts</em>, which are used to represent values of given size and kind (see
* {@link ValueLayout}) and <em>padding layouts</em> which are used, as the name suggests, to represent a portion of a memory
* segment whose contents should be ignored, and which are primarily present for alignment reasons (see {@link MemoryLayout#ofPaddingBits(long)}).
* Some common value layout constants are defined in the {@link MemoryLayouts} class.
* <p>
* More complex layouts can be derived from simpler ones: a <em>sequence layout</em> denotes a repetition of one or more
* element layout (see {@link SequenceLayout}); a <em>group layout</em> denotes an aggregation of (typically) heterogeneous
* member layouts (see {@link GroupLayout}).
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemoryLayout} directly.
*
* <h2>Size, alignment and byte order</h2>
*
* All layouts have a size; layout size for value and padding layouts is always explicitly denoted; this means that a layout description
* always has the same size in bits, regardless of the platform in which it is used. For derived layouts, the size is computed
* as follows:
* <ul>
* <li>for a <em>finite</em> sequence layout <em>S</em> whose element layout is <em>E</em> and size is L,
* the size of <em>S</em> is that of <em>E, multiplied by L</em></li>
* <li>the size of an <em>unbounded</em> sequence layout is <em>unknown</em></li>
* <li>for a group layout <em>G</em> with member layouts <em>M1</em>, <em>M2</em>, ... <em>Mn</em> whose sizes are
* <em>S1</em>, <em>S2</em>, ... <em>Sn</em>, respectively, the size of <em>G</em> is either <em>S1 + S2 + ... + Sn</em> or
* <em>max(S1, S2, ... Sn)</em> depending on whether the group is a <em>struct</em> or an <em>union</em>, respectively</li>
* </ul>
* <p>
* Furthermore, all layouts feature a <em>natural alignment</em> which can be inferred as follows:
* <ul>
* <li>for value and padding layout <em>L</em> whose size is <em>N</em>, the natural alignment of <em>L</em> is <em>N</em></li>
* <li>for a sequence layout <em>S</em> whose element layout is <em>E</em>, the natural alignment of <em>S</em> is that of <em>E</em></li>
* <li>for a group layout <em>G</em> with member layouts <em>M1</em>, <em>M2</em>, ... <em>Mn</em> whose alignments are
* <em>A1</em>, <em>A2</em>, ... <em>An</em>, respectively, the natural alignment of <em>G</em> is <em>max(A1, A2 ... An)</em></li>
* </ul>
* A layout's natural alignment can be overridden if needed (see {@link MemoryLayout#withBitAlignment(long)}), which can be useful to describe
* hyper-aligned layouts.
* <p>
* All value layouts have an <em>explicit</em> byte order (see {@link java.nio.ByteOrder}) which is set when the layout is created.
*
* <h2><a id = "layout-paths">Layout paths</a></h2>
*
* A <em>layout path</em> originates from a <em>root</em> layout (typically a group or a sequence layout) and terminates
* at a layout nested within the root layout - this is the layout <em>selected</em> by the layout path.
* Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances.
* <p>
* Layout paths are useful in order to e.g. to obtain offset of leaf elements inside arbitrarily nested layouts
* (see {@link MemoryLayout#offset(PathElement...)}), or to quickly obtain a memory access handle corresponding to the selected
* layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}).
* <p>
* Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
* For instance, given a layout constructed as follows:
* <blockquote><pre>{@code
SequenceLayout seq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
));
* }</pre></blockquote>
*
* We can obtain the offset of the member layout named <code>value</code> from <code>seq</code>, as follows:
* <blockquote><pre>{@code
long valueOffset = seq.offset(PathElement.sequenceElement(), PathElement.groupElement("value"));
* }</pre></blockquote>
*
* Layout paths can feature one or more <em>free dimensions</em>. For instance, a layout path traversing
* an unspecified sequence element (that is, where one of the path component was obtained with the
* {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime;
* that is, the memory access var handle associated with such a layout path expression will feature an extra {@code long}
* access coordinate. The layout path constructed in the above example features exactly one free dimension.
*
* @apiNote In the future, if the Java language permits, {@link MemoryLayout}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this class are immutable and thread-safe.
*/
public interface MemoryLayout extends Constable {
/**
* Returns an {@link Optional} containing the nominal descriptor for this
* layout, if one can be constructed, or an empty {@link Optional}
* if one cannot be constructed.
*
* @return An {@link Optional} containing the resulting nominal descriptor,
* or an empty {@link Optional} if one cannot be constructed.
*/
@Override
Optional<? extends DynamicConstantDesc<? extends MemoryLayout>> describeConstable();
/**
* Computes the layout size, in bits.
*
* @return the layout size, in bits.
* @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}).
*/
long bitSize();
/**
* Computes the layout size, in bytes.
*
* @return the layout size, in bytes.
* @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}),
* or if {@code bitSize()} is not a multiple of 8.
*/
default long byteSize() {
return Utils.bitsToBytesOrThrow(bitSize(),
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
}
/**
* Return the <em>name</em> (if any) associated with this layout.
*
* @return the layout <em>name</em> (if any).
* @see MemoryLayout#withName(String)
*/
Optional<String> name();
/**
* Creates a new layout which features the desired layout <em>name</em>.
*
* @param name the layout name.
* @return a new layout which is the same as this layout, except for the <em>name</em> associated to it.
* @see MemoryLayout#name()
*/
MemoryLayout withName(String name);
/**
* Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power
* of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of
* bytes that must be aligned for any pointer that correctly points to this layout. Thus:
*
* <ul>
* <li>{@code A=8} means unaligned (in the usual sense), which is common in packets.</li>
* <li>{@code A=64} means word aligned (on LP64), {@code A=32} int aligned, {@code A=16} short aligned, etc.</li>
* <li>{@code A=512} is the most strict alignment required by the x86/SV ABI (for AVX-512 data).</li>
* </ul>
*
* @return the layout alignment constraint, in bits.
*/
long bitAlignment();
/**
* Returns the alignment constraint associated with this layout, expressed in bytes. Layout alignment defines a power
* of two {@code A} which is the byte-wise alignment of the layout, where {@code A} is the number of bytes that must be aligned
* for any pointer that correctly points to this layout. Thus:
*
* <ul>
* <li>{@code A=1} means unaligned (in the usual sense), which is common in packets.</li>
* <li>{@code A=8} means word aligned (on LP64), {@code A=4} int aligned, {@code A=2} short aligned, etc.</li>
* <li>{@code A=64} is the most strict alignment required by the x86/SV ABI (for AVX-512 data).</li>
* </ul>
*
* @return the layout alignment constraint, in bytes.
* @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8.
*/
default long byteAlignment() {
return Utils.bitsToBytesOrThrow(bitAlignment(),
() -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8"));
}
/**
* Creates a new layout which features the desired alignment constraint.
*
* @param bitAlignment the layout alignment constraint, expressed in bits.
* @return a new layout which is the same as this layout, except for the alignment constraint associated to it.
* @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than than 8.
*/
MemoryLayout withBitAlignment(long bitAlignment);
/**
* Computes the offset of the layout selected by a given layout path, where the path is considered rooted in this
* layout.
*
* @apiNote if the layout path has one (or more) free dimensions,
* the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
*
* @param elements the layout path elements.
* @return The offset of layout selected by a the layout path obtained by concatenating the path elements in {@code elements}.
* @throws IllegalArgumentException if the layout path obtained by concatenating the path elements in {@code elements}
* does not select a valid layout element.
*/
default long offset(PathElement... elements) {
LayoutPath path = LayoutPath.rootPath(this);
for (PathElement e : elements) {
path = ((LayoutPath.PathElementImpl)e).apply(path);
}
return path.offset();
}
/**
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
* where the path is considered rooted in this layout.
*
* @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
* features certain <a href="MemoryHandles.html#memaccess-mode">access mode restrictions</a>, which are common to all memory access var handles.
*
* @param carrier the var handle carrier type.
* @param elements the layout path elements.
* @return a var handle which can be used to dereference memory at the layout denoted by given layout path.
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints.
* @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void},
* {@code boolean}, or if the layout path obtained by concatenating the path elements in {@code elements}
* does not select a value layout (see {@link ValueLayout}), or if the selected value layout has a size that
* that does not match that of the specified carrier type.
*/
default VarHandle varHandle(Class<?> carrier, PathElement... elements) {
LayoutPath path = LayoutPath.rootPath(this);
for (PathElement e : elements) {
path = ((LayoutPath.PathElementImpl)e).apply(path);
}
return path.dereferenceHandle(carrier);
}
/**
* Instances of this class are used to form <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>. There
* are two kinds of path elements: <em>group path elements</em> and <em>sequence path elements</em>. Group
* path elements are used to select a given named member layout within a {@link GroupLayout}. Sequence
* path elements are used to select a sequence element layout within a {@link SequenceLayout}; selection
* of sequence element layout can be <em>explicit</em> (see {@link PathElement#sequenceElement(long)}) or
* <em>implicit</em> (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit
* sequence path elements, it acquires additional <em>free dimensions</em>.
* <p>
* Non-platform classes should not implement {@linkplain PathElement} directly.
*
* @apiNote In the future, if the Java language permits, {@link PathElement}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
*/
interface PathElement {
/**
* Returns a path element which selects a member layout with given name from a given group layout.
* The path element returned by this method does not alter the number of free dimensions of any path
* that is combined with such element.
*
* @implSpec in case multiple group elements with a matching name exist, the path element returned by this
* method will select the first one; that is, the group element with lowest offset from current path is selected.
*
* @param name the name of the group element to be selected.
* @return a path element which selects the group element with given name.
* @throws NullPointerException if the specified group element name is {@code null}.
*/
static PathElement groupElement(String name) {
Objects.requireNonNull(name);
return new LayoutPath.PathElementImpl(path -> path.groupElement(name));
}
/**
* Returns a path element which selects the element layout at the specified position in a given the sequence layout.
* The path element returned by this method does not alter the number of free dimensions of any path
* that is combined with such element.
*
* @param index the index of the sequence element to be selected.
* @return a path element which selects the sequence element layout with given index.
* @throws IllegalArgumentException if {@code index < 0}.
*/
static PathElement sequenceElement(long index) {
if (index < 0) {
throw new IllegalArgumentException("Index must be positive: " + index);
}
return new LayoutPath.PathElementImpl(path -> path.sequenceElement(index));
}
/**
* Returns a path element which selects the element layout in a <em>range</em> of positions in a given the sequence layout,
* where the range is expressed as a pair of starting index (inclusive) {@code S} and step factor (which can also be negative)
* {@code F}.
* If a path with free dimensions {@code n} is combined with the path element returned by this method,
* the number of free dimensions of the resulting path will be {@code 1 + n}. If the free dimension associated
* with this path is bound by an index {@code I}, the resulting accessed offset can be obtained with the following
* formula:
* <blockquote><pre>{@code
E * (S + I * F)
* }</pre></blockquote>
* where {@code E} is the size (in bytes) of the sequence element layout.
*
* @param start the index of the first sequence element to be selected.
* @param step the step factor at which subsequence sequence elements are to be selected.
* @return a path element which selects the sequence element layout with given index.
* @throws IllegalArgumentException if {@code start < 0}, or {@code step == 0}.
*/
static PathElement sequenceElement(long start, long step) {
if (start < 0) {
throw new IllegalArgumentException("Start index must be positive: " + start);
}
if (step == 0) {
throw new IllegalArgumentException("Step must be != 0: " + step);
}
return new LayoutPath.PathElementImpl(path -> path.sequenceElement(start, step));
}
/**
* Returns a path element which selects an unspecified element layout from a given sequence layout.
* If a path with free dimensions {@code n} is combined with the path element returned by this method,
* the number of free dimensions of the resulting path will be {@code 1 + n}.
*
* @return a path element which selects an unspecified sequence element layout.
*/
static PathElement sequenceElement() {
return new LayoutPath.PathElementImpl(LayoutPath::sequenceElement);
}
}
/**
* Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified
* object is also a layout, and it is equal to this layout.
*
* @param that the object to be compared for equality with this layout.
* @return {@code true} if the specified object is equal to this layout.
*/
boolean equals(Object that);
/**
* Returns the hash code value for this layout.
*
* @return the hash code value for this layout.
*/
int hashCode();
/**
* Returns a string representation of this layout.
*
* @return a string representation of this layout.
*/
@Override
String toString();
/**
* Create a new padding layout with given size.
*
* @param size the padding size in bits.
* @return the new selector layout.
* @throws IllegalArgumentException if {@code size <= 0}.
*/
static MemoryLayout ofPaddingBits(long size) {
AbstractLayout.checkSize(size);
return new PaddingLayout(size);
}
/**
* Create a value layout of given byte order and size.
*
* @param size the value layout size.
* @param order the value layout's byte order.
* @return a new value layout.
* @throws IllegalArgumentException if {@code size <= 0}.
*/
static ValueLayout ofValueBits(long size, ByteOrder order) {
AbstractLayout.checkSize(size);
return new ValueLayout(order, size);
}
/**
* Create a new sequence layout with given element layout and element count.
*
* @param elementCount the sequence element count.
* @param elementLayout the sequence element layout.
* @return the new sequence layout with given element layout and size.
* @throws IllegalArgumentException if {@code elementCount < 0}.
*/
static SequenceLayout ofSequence(long elementCount, MemoryLayout elementLayout) {
AbstractLayout.checkSize(elementCount, true);
OptionalLong size = OptionalLong.of(elementCount);
return new SequenceLayout(size, elementLayout);
}
/**
* Create a new sequence layout, with unbounded element count and given element layout.
*
* @param elementLayout the element layout of the sequence layout.
* @return the new sequence layout with given element layout.
*/
static SequenceLayout ofSequence(MemoryLayout elementLayout) {
return new SequenceLayout(OptionalLong.empty(), elementLayout);
}
/**
* Create a new <em>struct</em> group layout with given member layouts.
*
* @param elements The member layouts of the <em>struct</em> group layout.
* @return a new <em>struct</em> group layout with given member layouts.
*/
static GroupLayout ofStruct(MemoryLayout... elements) {
return new GroupLayout(GroupLayout.Kind.STRUCT, List.of(elements));
}
/**
* Create a new <em>union</em> group layout with given member layouts.
*
* @param elements The member layouts of the <em>union</em> layout.
* @return a new <em>union</em> group layout with given member layouts.
*/
static GroupLayout ofUnion(MemoryLayout... elements) {
return new GroupLayout(GroupLayout.Kind.UNION, List.of(elements));
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.nio.ByteOrder;
/**
* This class defines useful layout constants. Some of the constants defined in this class are explicit in both
* size and byte order (see {@link #BITS_64_BE}), and can therefore be used to explicitly and unambiguously specify the
* contents of a memory segment. Other constants make implicit byte order assumptions (see
* {@link #JAVA_INT}); as such, these constants make it easy to interoperate with other serialization-centric APIs,
* such as {@link java.nio.ByteBuffer}.
*/
public final class MemoryLayouts {
private MemoryLayouts() {
//just the one, please
}
/**
* A value layout constant with size of one byte, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_8_LE = MemoryLayout.ofValueBits(8, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_16_LE = MemoryLayout.ofValueBits(16, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_32_LE = MemoryLayout.ofValueBits(32, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#LITTLE_ENDIAN}.
*/
public static final ValueLayout BITS_64_LE = MemoryLayout.ofValueBits(64, ByteOrder.LITTLE_ENDIAN);
/**
* A value layout constant with size of one byte, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_8_BE = MemoryLayout.ofValueBits(8, ByteOrder.BIG_ENDIAN);
/**
* A value layout constant with size of two bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_16_BE = MemoryLayout.ofValueBits(16, ByteOrder.BIG_ENDIAN);
/**
* A value layout constant with size of four bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_32_BE = MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN);
/**
* A value layout constant with size of eight bytes, and byte order set to {@link ByteOrder#BIG_ENDIAN}.
*/
public static final ValueLayout BITS_64_BE = MemoryLayout.ofValueBits(64, ByteOrder.BIG_ENDIAN);
/**
* A padding layout constant with size of one byte.
*/
public static final MemoryLayout PAD_8 = MemoryLayout.ofPaddingBits(8);
/**
* A padding layout constant with size of two bytes.
*/
public static final MemoryLayout PAD_16 = MemoryLayout.ofPaddingBits(16);
/**
* A padding layout constant with size of four bytes.
*/
public static final MemoryLayout PAD_32 = MemoryLayout.ofPaddingBits(32);
/**
* A padding layout constant with size of eight bytes.
*/
public static final MemoryLayout PAD_64 = MemoryLayout.ofPaddingBits(64);
/**
* A value layout constant whose size is the same as that of a Java {@code byte}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_BYTE = MemoryLayout.ofValueBits(8, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code char}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_CHAR = MemoryLayout.ofValueBits(16, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code short}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_SHORT = MemoryLayout.ofValueBits(16, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code int}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_INT = MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code long}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code float}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_FLOAT = MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code double}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
}

View File

@ -0,0 +1,430 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.nio.ByteBuffer;
import jdk.internal.foreign.Utils;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
/**
* A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
* and temporal bounds. Spatial bounds ensure that memory access operations on a memory segment cannot affect a memory location
* which falls <em>outside</em> the boundaries of the memory segment being accessed. Temporal checks ensure that memory access
* operations on a segment cannot occur after a memory segment has been closed (see {@link MemorySegment#close()}).
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemorySegment} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MemorySegment} directly.
*
* <h2>Constructing memory segments from different sources</h2>
*
* There are multiple ways to obtain a memory segment. First, memory segments backed by off-heap memory can
* be allocated using one of the many factory methods provided (see {@link MemorySegment#allocateNative(MemoryLayout)},
* {@link MemorySegment#allocateNative(long)} and {@link MemorySegment#allocateNative(long, long)}). Memory segments obtained
* in this way are called <em>native memory segments</em>.
* <p>
* It is also possible to obtain a memory segment backed by an existing heap-allocated Java array,
* using one of the provided factory methods (e.g. {@link MemorySegment#ofArray(int[])}). Memory segments obtained
* in this way are called <em>array memory segments</em>.
* <p>
* It is possible to obtain a memory segment backed by an existing Java byte buffer (see {@link ByteBuffer}),
* using the factory method {@link MemorySegment#ofByteBuffer(ByteBuffer)}.
* Memory segments obtained in this way are called <em>buffer memory segments</em>. Note that buffer memory segments might
* be backed by native memory (as in the case of native memory segments) or heap memory (as in the case of array memory segments),
* depending on the characteristics of the byte buffer instance the segment is associated with. For instance, a buffer memory
* segment obtained from a byte buffer created with the {@link ByteBuffer#allocateDirect(int)} method will be backed
* by native memory.
* <p>
* Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method
* {@link MemorySegment#mapFromPath(Path, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>.
*
* <h2>Closing a memory segment</h2>
*
* Memory segments are closed explicitly (see {@link MemorySegment#close()}). In general when a segment is closed, all off-heap
* resources associated with it are released; this has different meanings depending on the kind of memory segment being
* considered:
* <ul>
* <li>closing a native memory segment results in <em>freeing</em> the native memory associated with it</li>
* <li>closing a mapped memory segment results in the backing memory-mapped file to be unmapped</li>
* <li>closing an acquired memory segment <b>does not</b> result in the release of resources
* (see the section on <a href="#thread-confinement">thread confinement</a> for more details)</li>
* <li>closing a buffer, or a heap segment does not have any side-effect, other than marking the segment
* as <em>not alive</em> (see {@link MemorySegment#isAlive()}). Also, since the buffer and heap segments might keep
* strong references to the original buffer or array instance, it is the responsibility of clients to ensure that
* these segments are discarded in a timely manner, so as not to prevent garbage collection to reclaim the underlying
* objects.</li>
* </ul>
*
* <h2><a id = "thread-confinement">Thread confinement</a></h2>
*
* Memory segments support strong thread-confinement guarantees. Upon creation, they are assigned an <em>owner thread</em>,
* typically the thread which initiated the creation operation. After creation, only the owner thread will be allowed
* to directly manipulate the memory segment (e.g. close the memory segment) or access the underlying memory associated with
* the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the
* owner thread will result in a runtime failure.
* <p>
* If a memory segment S owned by thread A needs to be used by thread B, B needs to explicitly <em>acquire</em> S,
* which will create a so called <em>acquired</em> memory segment owned by B (see {@link #acquire()}) backed by the same resources
* as S. A memory segment can be acquired multiple times by one or more threads; in that case, a memory segment S cannot
* be closed until all the acquired memory segments derived from S have been closed. Furthermore, closing an acquired
* memory segment does <em>not</em> trigger any deallocation action. It is therefore important that clients remember to
* explicitly close the original segment from which the acquired memory segments have been obtained in order to truly
* ensure that off-heap resources associated with the memory segment are released.
*
* <h2>Memory segment views</h2>
*
* Memory segments support <em>views</em>. It is possible to create an <em>immutable</em> view of a memory segment
* (see {@link MemorySegment#asReadOnly()}) which does not support write operations. It is also possible to create views
* whose spatial bounds are stricter than the ones of the original segment (see {@link MemorySegment#asSlice(long, long)}).
* <p>
* Temporal bounds of the original segment are inherited by the view; that is, closing a segment view, such as a sliced
* view, will cause the original segment to be closed; as such special care must be taken when sharing views
* between multiple clients. If a client want to protect itself against early closure of a segment by
* another actor, it is the responsibility of that client to take protective measures, such as calling
* {@link MemorySegment#acquire()} before sharing the view with another client.
* <p>
* To allow for interoperability with existing code, a byte buffer view can be obtained from a memory segment
* (see {@link #asByteBuffer()}). This can be useful, for instance, for those clients that want to keep using the
* {@link ByteBuffer} API, but need to operate on large memory segments. Byte buffers obtained in such a way support
* the same spatial and temporal access restrictions associated to the memory address from which they originated.
*
* @apiNote In the future, if the Java language permits, {@link MemorySegment}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types.
*
* @implSpec
* Implementations of this interface are immutable and thread-safe.
*/
public interface MemorySegment extends AutoCloseable {
/**
* The base memory address associated with this memory segment.
* @return The base memory address.
*/
MemoryAddress baseAddress();
/**
* Obtains an <a href="#thread-confinement">acquired</a> memory segment which can be used to access memory associated
* with this segment from the current thread. As a side-effect, this segment cannot be closed until the acquired
* view has been closed too (see {@link #close()}).
* @return an <a href="#thread-confinement">acquired</a> memory segment which can be used to access memory associated
* with this segment from the current thread.
* @throws IllegalStateException if this segment has been closed.
*/
MemorySegment acquire();
/**
* Is this segment accessible from the current thread?
* @return true, if this segment is accessible from the current thread.
*/
boolean isAccessible();
/**
* The size (in bytes) of this memory segment.
* @return The size (in bytes) of this memory segment.
*/
long byteSize();
/**
* Obtains a read-only view of this segment. An attempt to write memory associated with a read-only memory segment
* will fail with {@link UnsupportedOperationException}.
* @return a read-only view of this segment.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
MemorySegment asReadOnly();
/**
* Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset,
* and whose new size is specified by the given argument.
* @param offset The new segment base offset (relative to the current segment base address), specified in bytes.
* @param newSize The new segment size, specified in bytes.
* @return a new memory segment view with updated base/limit addresses.
* @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset}
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
MemorySegment asSlice(long offset, long newSize);
/**
* Is this segment alive?
* @return true, if the segment is alive.
* @see MemorySegment#close()
*/
boolean isAlive();
/**
* Is this segment read-only?
* @return true, if the segment is read-only.
* @see MemorySegment#asReadOnly()
*/
boolean isReadOnly();
/**
* Closes this memory segment. Once a memory segment has been closed, any attempt to use the memory segment,
* or to access the memory associated with the segment will fail with {@link IllegalStateException}. Depending on
* the kind of memory segment being closed, calling this method further trigger deallocation of all the resources
* associated with the memory segment.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment, or if existing acquired views of this segment are still in use (see {@link MemorySegment#acquire()}).
*/
void close();
/**
* Wraps this segment in a {@link ByteBuffer}. Some of the properties of the returned buffer are linked to
* the properties of this segment. For instance, if this segment is <em>immutable</em>
* (see {@link MemorySegment#asReadOnly()}, then the resulting buffer is <em>read-only</em>
* (see {@link ByteBuffer#isReadOnly()}. Additionally, if this is a native memory segment, the resulting buffer is
* <em>direct</em> (see {@link ByteBuffer#isDirect()}).
* <p>
* The life-cycle of the returned buffer will be tied to that of this segment. That means that if the this segment
* is closed (see {@link MemorySegment#close()}, accessing the returned
* buffer will throw an {@link IllegalStateException}.
* <p>
* The resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using
* {@link ByteBuffer#order(java.nio.ByteOrder)}.
*
* @return a {@link ByteBuffer} view of this memory segment.
* @throws UnsupportedOperationException if this segment cannot be mapped onto a {@link ByteBuffer} instance,
* e.g. because it models an heap-based segment that is not based on a {@code byte[]}), or if its size is greater
* than {@link Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
ByteBuffer asByteBuffer();
/**
* Copy the contents of this memory segment into a fresh byte array.
* @return a fresh byte array copy of this memory segment.
* @throws UnsupportedOperationException if this segment's contents cannot be copied into a {@link byte[]} instance,
* e.g. its size is greater than {@link Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
byte[] toByteArray();
/**
* Creates a new buffer memory segment that models the memory associated with the given byte
* buffer. The segment starts relative to the buffer's position (inclusive)
* and ends relative to the buffer's limit (exclusive).
* <p>
* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param bb the byte buffer backing the buffer memory segment.
* @return a new buffer memory segment.
*/
static MemorySegment ofByteBuffer(ByteBuffer bb) {
return Utils.makeBufferSegment(bb);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated byte array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(byte[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated char array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(char[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated short array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(short[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated int array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(int[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated float array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(float[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated long array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(long[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated double array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
*/
static MemorySegment ofArray(double[] arr) {
return Utils.makeArraySegment(arr);
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given layout.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
allocateNative(layout.bytesSize(), layout.bytesAlignment());
* }</pre></blockquote>
*
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
* will result in off-heap memory leaks.
*
* @param layout the layout of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if the specified layout has illegal size or alignment constraint.
*/
static MemorySegment allocateNative(MemoryLayout layout) {
return allocateNative(layout.byteSize(), layout.byteAlignment());
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
allocateNative(bytesSize, 1);
* }</pre></blockquote>
*
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
* will result in off-heap memory leaks.
*
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}.
*/
static MemorySegment allocateNative(long bytesSize) {
return allocateNative(bytesSize, 1);
}
/**
* Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
*
* @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block
* of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon.
*
* @param path the path to the file to memory map.
* @param bytesSize the size (in bytes) of the mapped memory backing the memory segment.
* @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}.
* @return a new mapped memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}.
* @throws UnsupportedOperationException if an unsupported map mode is specified.
* @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs.
*/
static MemorySegment mapFromPath(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
return Utils.makeMappedSegment(path, bytesSize, mapMode);
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and
* alignment constraint (in bytes).
*
* @implNote The initialization state of the contents of the block of off-heap memory associated with the returned native memory
* segment is unspecified and should not be relied upon. Moreover, a client is responsible to call the {@link MemorySegment#close()}
* on a native memory segment, to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so
* will result in off-heap memory leaks.
*
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes < 0}, or if {@code alignmentBytes}
* is not a power of 2.
*/
static MemorySegment allocateNative(long bytesSize, long alignmentBytes) {
if (bytesSize <= 0) {
throw new IllegalArgumentException("Invalid allocation size : " + bytesSize);
}
if (alignmentBytes < 0 ||
((alignmentBytes & (alignmentBytes - 1)) != 0L)) {
throw new IllegalArgumentException("Invalid alignment constraint : " + alignmentBytes);
}
return Utils.makeNativeSegment(bytesSize, alignmentBytes);
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A padding layout. A padding layout specifies the size of extra space which is typically not accessed by applications,
* and is typically used for aligning member layouts around word boundaries.
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code PaddingLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
/* package-private */ final class PaddingLayout extends AbstractLayout implements MemoryLayout {
PaddingLayout(long size) {
this(size, size, Optional.empty());
}
PaddingLayout(long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, name);
}
@Override
public String toString() {
return decorateLayoutString("x" + bitSize());
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof PaddingLayout)) {
return false;
}
PaddingLayout p = (PaddingLayout)other;
return bitSize() == p.bitSize();
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), bitSize());
}
@Override
PaddingLayout dup(long alignment, Optional<String> name) {
return new PaddingLayout(bitSize(), alignment, name);
}
@Override
public Optional<DynamicConstantDesc<MemoryLayout>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "padding",
CD_LAYOUT, MH_PADDING, bitSize()));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withName(String name) {
return (PaddingLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public PaddingLayout withBitAlignment(long alignmentBits) {
return (PaddingLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A sequence layout. A sequence layout is used to denote a repetition of a given layout, also called the sequence layout's <em>element layout</em>.
* The repetition count, where it exists (e.g. for <em>finite</em> sequence layouts) is said to be the the sequence layout's <em>element count</em>.
* A finite sequence layout can be thought of as a group layout where the sequence layout's element layout is repeated a number of times
* that is equal to the sequence layout's element count. In other words this layout:
*
* <pre>{@code
MemoryLayout.ofSequence(3, MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN));
* }</pre>
*
* is equivalent to the following layout:
*
* <pre>{@code
MemoryLayout.ofStruct(
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN));
* }</pre>
*
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code SequenceLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
public final class SequenceLayout extends AbstractLayout {
private final OptionalLong elemCount;
private final MemoryLayout elementLayout;
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout) {
this(elemCount, elementLayout, elementLayout.bitAlignment(), Optional.empty());
}
SequenceLayout(OptionalLong elemCount, MemoryLayout elementLayout, long alignment, Optional<String> name) {
super(elemCount.isPresent() && AbstractLayout.optSize(elementLayout).isPresent() ?
OptionalLong.of(elemCount.getAsLong() * elementLayout.bitSize()) :
OptionalLong.empty(), alignment, name);
this.elemCount = elemCount;
this.elementLayout = elementLayout;
}
/**
* Returns the element layout associated with this sequence layout.
*
* @return The element layout associated with this sequence layout.
*/
public MemoryLayout elementLayout() {
return elementLayout;
}
/**
* Returns the element count of this sequence layout (if any).
*
* @return the element count of this sequence layout (if any).
*/
public OptionalLong elementCount() {
return elemCount;
}
@Override
public String toString() {
return decorateLayoutString(String.format("[%s:%s]",
elemCount.isPresent() ? elemCount.getAsLong() : "", elementLayout));
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof SequenceLayout)) {
return false;
}
SequenceLayout s = (SequenceLayout)other;
return elemCount.equals(s.elemCount) && elementLayout.equals(s.elementLayout);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), elemCount, elementLayout);
}
@Override
SequenceLayout dup(long alignment, Optional<String> name) {
return new SequenceLayout(elementCount(), elementLayout, alignment, name);
}
@Override
boolean hasNaturalAlignment() {
return alignment == elementLayout.bitAlignment();
}
@Override
public Optional<DynamicConstantDesc<SequenceLayout>> describeConstable() {
return elemCount.isPresent() ?
Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_SEQUENCE_LAYOUT, MH_SIZED_SEQUENCE, elemCount.getAsLong(), elementLayout.describeConstable().get())) :
Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_SEQUENCE_LAYOUT, MH_UNSIZED_SEQUENCE, elementLayout.describeConstable().get()));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withName(String name) {
return (SequenceLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public SequenceLayout withBitAlignment(long alignmentBits) {
return (SequenceLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodHandleDesc;
import java.nio.ByteOrder;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A value layout. A value layout is used to model the memory layout associated with values of basic data types, such as <em>integral</em> types
* (either signed or unsigned) and <em>floating-point</em> types. Each value layout has a size and a byte order (see {@link ByteOrder}).
*
* <p>
* This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
* class; use of identity-sensitive operations (including reference equality
* ({@code ==}), identity hash code, or synchronization) on instances of
* {@code ValueLayout} may have unpredictable results and should be avoided.
* The {@code equals} method should be used for comparisons.
*
* @implSpec
* This class is immutable and thread-safe.
*/
public final class ValueLayout extends AbstractLayout implements MemoryLayout {
private final ByteOrder order;
ValueLayout(ByteOrder order, long size) {
this(order, size, size, Optional.empty());
}
ValueLayout(ByteOrder order, long size, long alignment, Optional<String> name) {
super(OptionalLong.of(size), alignment, name);
this.order = order;
}
/**
* Returns the value's byte order.
*
* @return the value's byte order.
*/
public ByteOrder order() {
return order;
}
/**
* Returns a new value layout with given byte order.
*
* @param order the desired byte order.
* @return a new value layout with given byte order.
*/
public ValueLayout withOrder(ByteOrder order) {
return new ValueLayout(order, bitSize(), alignment, optName());
}
@Override
public String toString() {
return decorateLayoutString(String.format("%s%d",
order == ByteOrder.BIG_ENDIAN ? "B" : "b",
bitSize()));
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!super.equals(other)) {
return false;
}
if (!(other instanceof ValueLayout)) {
return false;
}
ValueLayout v = (ValueLayout)other;
return order.equals(v.order) &&
bitSize() == v.bitSize() &&
alignment == v.alignment;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), order, bitSize(), alignment);
}
@Override
ValueLayout dup(long alignment, Optional<String> name) {
return new ValueLayout(order, bitSize(), alignment, name);
}
@Override
public Optional<DynamicConstantDesc<ValueLayout>> describeConstable() {
return Optional.of(DynamicConstantDesc.ofNamed(ConstantDescs.BSM_INVOKE, "value",
CD_VALUE_LAYOUT, MH_VALUE, bitSize(), order == ByteOrder.BIG_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN));
}
//hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
//but that causes issues with javadoc, see JDK-8224052
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withName(String name) {
return (ValueLayout)super.withName(name);
}
/**
* {@inheritDoc}
*/
@Override
public ValueLayout withBitAlignment(long alignmentBits) {
return (ValueLayout)super.withBitAlignment(alignmentBits);
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 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
* 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.
*
*/
/**
* <p> Classes to support low-level, safe and efficient memory access. For example:
*
* <pre>{@code
static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN);
try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
MemoryAddress base = segment.baseAddress();
for (long i = 0 ; i < 10 ; i++) {
intHandle.set(base.offset(i * 4), (int)i);
}
}
* }</pre>
*
* Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at
* a given memory location. We then create a <em>native</em> memory segment, that is, a memory segment backed by
* off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
* The segment is created inside a <em>try-with-resources</em> construct: this idiom ensures that all the memory resources
* associated with the segment will be released at the end of the block. Inside the try-with-resources block, we initialize
* the contents of the memory segment; more specifically, if we view the memory segment as a set of 10 adjacent slots,
* {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
* so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
*
* <p>
* The key abstractions introduced by this package are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}.
* The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - that is,
* an offset inside a given segment. A memory address represents the main access coordinate of a memory access var handle, which can be obtained
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
* hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given
* layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce
* memory access var handles, e.g. using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
*
* <h2><a id="deallocation"></a>Deterministic deallocation</h2>
*
* When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is
* crucial that the resources associated with a memory segment are released when the segment is no longer in use, by calling the {@link jdk.incubator.foreign.MemorySegment#close()}
* method either explicitly, or implicitly, by relying on try-with-resources construct (as demonstrated in the example above).
* Closing a given memory segment is an <em>atomic</em> operation which can either succeed - and result in the underlying
* memory associated with the segment to be released, or <em>fail</em> with an exception.
* <p>
* The deterministic deallocation model differs significantly from the implicit strategies adopted within other APIs, most
* notably the {@link java.nio.ByteBuffer} API: in that case, when a native byte buffer is created (see {@link java.nio.ByteBuffer#allocateDirect(int)}),
* the underlying memory is not released until the byte buffer reference becomes <em>unreachable</em>. While implicit deallocation
* models such as this can be very convenient - clients do not have to remember to <em>close</em> a direct buffer - such models can also make it
* hard for clients to ensure that the memory associated with a direct buffer has indeed been released.
*
* <h2><a id="safety"></a>Safety</h2>
*
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment using
* a memory address, such an address is validated (upon access), to make sure that it does not point to a memory location
* which resides <em>outside</em> the boundaries of the memory segment it refers to. We call this guarantee <em>spatial safety</em>.
* <p>
* Since memory segments can be closed (see above), a memory address is also validated (upon access) to make sure that
* the segment it belongs to has not been closed prematurely. We call this guarantee <em>temporal safety</em>. Note that,
* in the general case, guaranteeing temporal safety can be hard, as multiple threads could attempt to access and/or close
* the same memory segment concurrently. The memory access API addresses this problem by imposing strong
* <a href="MemorySegment.html#thread-confinement"><em>thread-confinement</em></a> guarantees on memory segments: each
* memory segment is associated with an owner thread, which is the only thread that can either access or close the segment.
* A thread other than the owner thread will have to explicitly <em>acquire</em> a segment in order to be able to use it.
* <p>
* Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid
* memory location - or fails.
*/
package jdk.incubator.foreign;

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 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
* 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 jdk.incubator.foreign.unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.internal.foreign.MemoryAddressImpl;
/**
* Unsafe methods to allow interop between sun.misc.unsafe and memory access API.
*/
public final class ForeignUnsafe {
private ForeignUnsafe() {
//just the one, please
}
// The following methods can be used in conjunction with the java.foreign API.
/**
* Obtain the base object (if any) associated with this address. This can be used in conjunction with
* {@link #getUnsafeOffset(MemoryAddress)} in order to obtain a base/offset addressing coordinate pair
* to be used with methods like {@link sun.misc.Unsafe#getInt(Object, long)} and the likes.
*
* @param address the address whose base object is to be obtained.
* @return the base object associated with the address, or {@code null}.
*/
public static Object getUnsafeBase(MemoryAddress address) {
return ((MemoryAddressImpl)address).unsafeGetBase();
}
/**
* Obtain the offset associated with this address. If {@link #getUnsafeBase(MemoryAddress)} returns {@code null} on the passed
* address, then the offset is to be interpreted as the (absolute) numerical value associated said address.
* Alternatively, the offset represents the displacement of a field or an array element within the containing
* base object. This can be used in conjunction with {@link #getUnsafeBase(MemoryAddress)} in order to obtain a base/offset
* addressing coordinate pair to be used with methods like {@link sun.misc.Unsafe#getInt(Object, long)} and the likes.
*
* @param address the address whose offset is to be obtained.
* @return the offset associated with the address.
*/
public static long getUnsafeOffset(MemoryAddress address) {
return ((MemoryAddressImpl)address).unsafeGetOffset();
}
}

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 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
* 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 jdk.internal.foreign;
import jdk.incubator.foreign.MemoryLayout;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import sun.invoke.util.Wrapper;
import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import java.lang.invoke.VarHandle;
import java.util.function.UnaryOperator;
import java.util.stream.LongStream;
/**
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
* a path can be constructed by selecting layout elements using the selector methods provided by this class
* (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
* Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
* by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element
* given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle(Class)}).
*/
public class LayoutPath {
private static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private final MemoryLayout layout;
private final long offset;
private final LayoutPath enclosing;
private final long[] strides;
private LayoutPath(MemoryLayout layout, long offset, long[] strides, LayoutPath enclosing) {
this.layout = layout;
this.offset = offset;
this.strides = strides;
this.enclosing = enclosing;
}
// Layout path selector methods
public LayoutPath sequenceElement() {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
MemoryLayout elem = seq.elementLayout();
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), this);
}
public LayoutPath sequenceElement(long start, long step) {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
checkSequenceBounds(seq, start);
MemoryLayout elem = seq.elementLayout();
long elemSize = elem.bitSize();
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), this);
}
public LayoutPath sequenceElement(long index) {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
checkSequenceBounds(seq, index);
long elemOffset = 0;
if (index > 0) {
//if index == 0, we do not depend on sequence element size, so skip
long elemSize = seq.elementLayout().bitSize();
elemOffset = elemSize * index;
}
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, this);
}
public LayoutPath groupElement(String name) {
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
GroupLayout g = (GroupLayout)layout;
long offset = 0;
MemoryLayout elem = null;
for (int i = 0; i < g.memberLayouts().size(); i++) {
MemoryLayout l = g.memberLayouts().get(i);
if (l.name().isPresent() &&
l.name().get().equals(name)) {
elem = l;
break;
} else {
offset += l.bitSize();
}
}
if (elem == null) {
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
}
return LayoutPath.nestedPath(elem, this.offset + offset, strides, this);
}
// Layout path projections
public long offset() {
return offset;
}
public VarHandle dereferenceHandle(Class<?> carrier) {
if (!(layout instanceof ValueLayout)) {
throw badLayoutPath("layout path does not select a value layout");
}
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class // illegal carrier?
|| Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize()) { // carrier has the right size?
throw new IllegalArgumentException("Invalid carrier: " + carrier + ", for layout " + layout);
}
checkAlignment(this);
return JLI.memoryAddressViewVarHandle(
carrier,
layout.byteAlignment() - 1, //mask
((ValueLayout) layout).order(),
Utils.bitsToBytesOrThrow(offset, IllegalStateException::new),
LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray());
}
// Layout path construction
public static LayoutPath rootPath(MemoryLayout layout) {
return new LayoutPath(layout, 0L, EMPTY_STRIDES, null);
}
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, LayoutPath encl) {
return new LayoutPath(layout, offset, strides, encl);
}
// Helper methods
private void check(Class<?> layoutClass, String msg) {
if (!layoutClass.isAssignableFrom(layout.getClass())) {
throw badLayoutPath(msg);
}
}
private void checkSequenceBounds(SequenceLayout seq, long index) {
if (seq.elementCount().isPresent() && index >= seq.elementCount().getAsLong()) {
throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount().getAsLong()));
}
}
private static IllegalArgumentException badLayoutPath(String cause) {
return new IllegalArgumentException("Bad layout path: " + cause);
}
private static void checkAlignment(LayoutPath path) {
MemoryLayout layout = path.layout;
long alignment = layout.bitAlignment();
if (path.offset % alignment != 0) {
throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout);
}
for (long stride : path.strides) {
if (stride % alignment != 0) {
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride);
}
}
LayoutPath encl = path.enclosing;
if (encl != null) {
if (encl.layout.bitAlignment() < alignment) {
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout);
}
checkAlignment(encl);
}
}
private long[] addStride(long stride) {
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 0, strides.length);
newStrides[strides.length] = stride;
return newStrides;
}
private static long[] EMPTY_STRIDES = new long[0];
/**
* This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
* is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
*/
public static class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> {
final UnaryOperator<LayoutPath> pathOp;
public PathElementImpl(UnaryOperator<LayoutPath> pathOp) {
this.pathOp = pathOp;
}
@Override
public LayoutPath apply(LayoutPath layoutPath) {
return pathOp.apply(layoutPath);
}
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 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
* 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 jdk.internal.foreign;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.misc.Unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.ref.Reference;
import java.util.Objects;
/**
* This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information
* about the segment this address is associated with, as well as an offset into such segment.
*/
public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProxy {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private final MemorySegmentImpl segment;
private final long offset;
public MemoryAddressImpl(MemorySegmentImpl segment, long offset) {
this.segment = Objects.requireNonNull(segment);
this.offset = offset;
}
public static void copy(MemoryAddressImpl src, MemoryAddressImpl dst, long size) {
src.checkAccess(0, size, true);
dst.checkAccess(0, size, false);
//check disjoint
long offsetSrc = src.unsafeGetOffset();
long offsetDst = dst.unsafeGetOffset();
Object baseSrc = src.unsafeGetBase();
Object baseDst = dst.unsafeGetBase();
UNSAFE.copyMemory(baseSrc, offsetSrc, baseDst, offsetDst, size);
}
// MemoryAddress methods
@Override
public long offset() {
return offset;
}
@Override
public MemorySegment segment() {
return segment;
}
@Override
public MemoryAddress offset(long bytes) {
return new MemoryAddressImpl(segment, offset + bytes);
}
// MemoryAddressProxy methods
public void checkAccess(long offset, long length, boolean readOnly) {
segment.checkRange(this.offset + offset, length, !readOnly);
}
public long unsafeGetOffset() {
return segment.min + offset;
}
public Object unsafeGetBase() {
return segment.base();
}
// Object methods
@Override
public int hashCode() {
return Objects.hash(unsafeGetBase(), unsafeGetOffset());
}
@Override
public boolean equals(Object that) {
if (that instanceof MemoryAddressImpl) {
MemoryAddressImpl addr = (MemoryAddressImpl)that;
return Objects.equals(unsafeGetBase(), ((MemoryAddressImpl) that).unsafeGetBase()) &&
unsafeGetOffset() == addr.unsafeGetOffset();
} else {
return false;
}
}
@Override
public String toString() {
return "MemoryAddress{ region: " + segment + " offset=0x" + Long.toHexString(offset) + " }";
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 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
* 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 jdk.internal.foreign;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
/**
* This class manages the temporal bounds associated with a memory segment. A scope has a liveness bit, which is updated
* when the scope is closed (this operation is triggered by {@link MemorySegmentImpl#close()}). Furthermore, a scope is
* associated with an <em>atomic</em> counter which can be incremented (upon calling the {@link #acquire()} method),
* and is decremented (when a previously acquired segment is later closed).
*/
public final class MemoryScope {
//reference to keep hold onto
final Object ref;
int activeCount = UNACQUIRED;
final static VarHandle COUNT_HANDLE;
static {
try {
COUNT_HANDLE = MethodHandles.lookup().findVarHandle(MemoryScope.class, "activeCount", int.class);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
final static int UNACQUIRED = 0;
final static int CLOSED = -1;
final static int MAX_ACQUIRE = Integer.MAX_VALUE;
final Runnable cleanupAction;
public MemoryScope(Object ref, Runnable cleanupAction) {
this.ref = ref;
this.cleanupAction = cleanupAction;
}
/**
* This method performs a full, thread-safe liveness check; can be used outside confinement thread.
*/
final boolean isAliveThreadSafe() {
return ((int)COUNT_HANDLE.getVolatile(this)) != CLOSED;
}
/**
* This method performs a quick liveness check; must be called from the confinement thread.
*/
final void checkAliveConfined() {
if (activeCount == CLOSED) {
throw new IllegalStateException("Segment is not alive");
}
}
MemoryScope acquire() {
int value;
do {
value = (int)COUNT_HANDLE.getVolatile(this);
if (value == CLOSED) {
//segment is not alive!
throw new IllegalStateException("Segment is not alive");
} else if (value == MAX_ACQUIRE) {
//overflow
throw new IllegalStateException("Segment acquire limit exceeded");
}
} while (!COUNT_HANDLE.compareAndSet(this, value, value + 1));
return new MemoryScope(ref, this::release);
}
private void release() {
int value;
do {
value = (int)COUNT_HANDLE.getVolatile(this);
if (value <= UNACQUIRED) {
//cannot get here - we can't close segment twice
throw new IllegalStateException();
}
} while (!COUNT_HANDLE.compareAndSet(this, value, value - 1));
}
void close() {
if (!COUNT_HANDLE.compareAndSet(this, UNACQUIRED, CLOSED)) {
//first check if already closed...
checkAliveConfined();
//...if not, then we have acquired views that are still active
throw new IllegalStateException("Cannot close a segment that has active acquired views");
}
if (cleanupAction != null) {
cleanupAction.run();
}
}
}

View File

@ -0,0 +1,209 @@
/*
* Copyright (c) 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
* 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 jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.Unsafe;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Random;
/**
* This class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
* about the segment's spatial and temporal bounds, as well as the addressing coordinates (base + offset) which allows
* unsafe access; each memory segment implementation is associated with an owner thread which is set at creation time.
* Access to certain sensitive operations on the memory segment will fail with {@code IllegalStateException} if the
* segment is either in an invalid state (e.g. it has already been closed) or if access occurs from a thread other
* than the owner thread. See {@link MemoryScope} for more details on management of temporal bounds.
*/
public final class MemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final int BYTE_ARR_BASE = UNSAFE.arrayBaseOffset(byte[].class);
final long length;
final int mask;
final long min;
final Object base;
final Thread owner;
final MemoryScope scope;
final static int READ_ONLY = 1;
final static long NONCE = new Random().nextLong();
public MemorySegmentImpl(long min, Object base, long length, int mask, Thread owner, MemoryScope scope) {
this.length = length;
this.mask = mask;
this.min = min;
this.base = base;
this.owner = owner;
this.scope = scope;
}
// MemorySegment methods
@Override
public final MemorySegmentImpl asSlice(long offset, long newSize) {
checkValidState();
checkBounds(offset, newSize);
return new MemorySegmentImpl(min + offset, base, newSize, mask, owner, scope);
}
@Override
public MemorySegment acquire() {
return new MemorySegmentImpl(min, base, length, mask, Thread.currentThread(), scope.acquire());
}
@Override
public final MemoryAddress baseAddress() {
return new MemoryAddressImpl(this, 0);
}
@Override
public final long byteSize() {
return length;
}
@Override
public final MemorySegmentImpl asReadOnly() {
checkValidState();
return new MemorySegmentImpl(min, base, length, mask | READ_ONLY, owner, scope);
}
@Override
public final boolean isAlive() {
return scope.isAliveThreadSafe();
}
@Override
public final boolean isReadOnly() {
return isSet(READ_ONLY);
}
@Override
public boolean isAccessible() {
return owner == Thread.currentThread();
}
@Override
public final void close() {
checkValidState();
scope.close();
}
@Override
public ByteBuffer asByteBuffer() {
checkValidState();
checkIntSize("ByteBuffer");
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
ByteBuffer _bb;
if (base() != null) {
if (!(base() instanceof byte[])) {
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
}
_bb = nioAccess.newHeapByteBuffer((byte[]) base(), (int)min - BYTE_ARR_BASE, (int) length, this);
} else {
_bb = nioAccess.newDirectByteBuffer(min, (int) length, null, this);
}
if (isReadOnly()) {
//scope is IMMUTABLE - obtain a RO byte buffer
_bb = _bb.asReadOnlyBuffer();
}
return _bb;
}
@Override
public byte[] toByteArray() {
checkValidState();
checkIntSize("byte[]");
byte[] arr = new byte[(int)length];
MemorySegment arrSegment = MemorySegment.ofArray(arr);
MemoryAddress.copy(this.baseAddress(), arrSegment.baseAddress(), length);
return arr;
}
// MemorySegmentProxy methods
@Override
public final void checkValidState() {
if (owner != Thread.currentThread()) {
throw new IllegalStateException("Attempt to access segment outside owning thread");
}
scope.checkAliveConfined();
}
// Object methods
@Override
public String toString() {
return "MemorySegment{ id=0x" + Long.toHexString(id()) + " limit: " + byteSize() + " }";
}
// Helper methods
void checkRange(long offset, long length, boolean writeAccess) {
checkValidState();
if (isReadOnly() && writeAccess) {
throw new UnsupportedOperationException("Cannot write to read-only memory segment");
}
checkBounds(offset, length);
}
Object base() {
return base;
}
private boolean isSet(int mask) {
return (this.mask & mask) != 0;
}
private void checkIntSize(String typeName) {
if (length > (Integer.MAX_VALUE - 8)) { //conservative check
throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
}
}
private void checkBounds(long offset, long length) {
if (length < 0 ||
offset < 0 ||
offset > this.length - length) { // careful of overflow
throw new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
this, offset, length));
}
}
private int id() {
//compute a stable and random id for this memory segment
return Math.abs(Objects.hash(base, min, NONCE));
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 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
* 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 jdk.internal.foreign;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.Unsafe;
import sun.nio.ch.FileChannelImpl;
import sun.security.action.GetBooleanAction;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.function.Supplier;
/**
* This class contains misc helper functions to support creation of memory segments.
*/
public final class Utils {
private static Unsafe unsafe = Unsafe.getUnsafe();
// The maximum alignment supported by malloc - typically 16 on 64-bit platforms.
private final static long MAX_ALIGN = 16;
private static final JavaNioAccess javaNioAccess = SharedSecrets.getJavaNioAccess();
private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory");
public static long alignUp(long n, long alignment) {
return (n + alignment - 1) & -alignment;
}
public static long bitsToBytesOrThrow(long bits, Supplier<RuntimeException> exFactory) {
if (bits % 8 == 0) {
return bits / 8;
} else {
throw exFactory.get();
}
}
// segment factories
public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes) {
long alignedSize = bytesSize;
if (alignmentBytes > MAX_ALIGN) {
alignedSize = bytesSize + (alignmentBytes - 1);
}
long buf = unsafe.allocateMemory(alignedSize);
if (!skipZeroMemory) {
unsafe.setMemory(buf, alignedSize, (byte)0);
}
long alignedBuf = Utils.alignUp(buf, alignmentBytes);
MemoryScope scope = new MemoryScope(null, () -> unsafe.freeMemory(buf));
MemorySegment segment = new MemorySegmentImpl(buf, null, alignedSize, 0, Thread.currentThread(), scope);
if (alignedBuf != buf) {
long delta = alignedBuf - buf;
segment = segment.asSlice(delta, bytesSize);
}
return segment;
}
public static MemorySegment makeArraySegment(byte[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(char[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_CHAR_BASE_OFFSET, Unsafe.ARRAY_CHAR_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(short[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_SHORT_BASE_OFFSET, Unsafe.ARRAY_SHORT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(int[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(float[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_FLOAT_BASE_OFFSET, Unsafe.ARRAY_FLOAT_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(long[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_LONG_BASE_OFFSET, Unsafe.ARRAY_LONG_INDEX_SCALE);
}
public static MemorySegment makeArraySegment(double[] arr) {
return makeArraySegment(arr, arr.length, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
}
private static MemorySegment makeArraySegment(Object arr, int size, int base, int scale) {
MemoryScope scope = new MemoryScope(null, null);
return new MemorySegmentImpl(base, arr, size * scale, 0, Thread.currentThread(), scope);
}
public static MemorySegment makeBufferSegment(ByteBuffer bb) {
long bbAddress = javaNioAccess.getBufferAddress(bb);
Object base = javaNioAccess.getBufferBase(bb);
int pos = bb.position();
int limit = bb.limit();
MemoryScope bufferScope = new MemoryScope(bb, null);
return new MemorySegmentImpl(bbAddress + pos, base, limit - pos, 0, Thread.currentThread(), bufferScope);
}
// create and map a file into a fresh segment
public static MemorySegment makeMappedSegment(Path path, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
if (bytesSize <= 0) throw new IllegalArgumentException("Requested bytes size must be > 0.");
try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) {
UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, 0L, bytesSize);
MemoryScope scope = new MemoryScope(null, unmapperProxy::unmap);
return new MemorySegmentImpl(unmapperProxy.address(), null, bytesSize, 0, Thread.currentThread(), scope);
}
}
private static OpenOption[] openOptions(FileChannel.MapMode mapMode) {
if (mapMode == FileChannel.MapMode.READ_ONLY) {
return new OpenOption[] { StandardOpenOption.READ };
} else if (mapMode == FileChannel.MapMode.READ_WRITE || mapMode == FileChannel.MapMode.PRIVATE) {
return new OpenOption[] { StandardOpenOption.READ, StandardOpenOption.WRITE };
} else {
throw new UnsupportedOperationException("Unsupported map mode: " + mapMode);
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 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
* 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.
*/
/**
* Defines the experimental foreign memory access API.
*
* {@Incubating}
*
* @moduleGraph
*/
module jdk.incubator.foreign {
exports jdk.incubator.foreign;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -71,6 +71,22 @@ public final class GraalDirectives {
public static void controlFlowAnchor() {
}
/**
* A call to this method will force the compiler to assume this instruction has a visible memory
* effect killing all memory locations.
*/
public static void sideEffect() {
}
/**
* A call to this method will force the compiler to assume this instruction has a visible memory
* effect killing all memory locations.
*/
public static int sideEffect(@SuppressWarnings("unused") int a) {
return 0;
}
/**
* Injects a probability for the given condition into the profiling information of a branch
* instruction. The probability must be a value between 0.0 and 1.0 (inclusive).

View File

@ -1298,9 +1298,9 @@ public class AMD64Assembler extends AMD64BaseAssembler {
*/
public static class VexRVMROp extends VexOp {
// @formatter:off
public static final VexRVMROp VPBLENDVB = new VexRVMROp("VPBLENDVB", P_66, M_0F3A, W0, 0x4C, VEXOpAssertion.AVX1_2);
public static final VexRVMROp VPBLENDVPS = new VexRVMROp("VPBLENDVPS", P_66, M_0F3A, W0, 0x4A, VEXOpAssertion.AVX1);
public static final VexRVMROp VPBLENDVPD = new VexRVMROp("VPBLENDVPD", P_66, M_0F3A, W0, 0x4B, VEXOpAssertion.AVX1);
public static final VexRVMROp VPBLENDVB = new VexRVMROp("VPBLENDVB", P_66, M_0F3A, W0, 0x4C, VEXOpAssertion.AVX1_2);
public static final VexRVMROp VBLENDVPS = new VexRVMROp("VBLENDVPS", P_66, M_0F3A, W0, 0x4A, VEXOpAssertion.AVX1);
public static final VexRVMROp VBLENDVPD = new VexRVMROp("VBLENDVPD", P_66, M_0F3A, W0, 0x4B, VEXOpAssertion.AVX1);
// @formatter:on
protected VexRVMROp(String opcode, int pp, int mmmmm, int w, int op, VEXOpAssertion assertion) {
@ -3139,14 +3139,14 @@ public class AMD64Assembler extends AMD64BaseAssembler {
SSEOp.XOR.emit(this, PS, dst, src);
}
protected final void decl(Register dst) {
public final void decl(Register dst) {
// Use two-byte form (one-byte form is a REX prefix in 64-bit mode)
prefix(dst);
emitByte(0xFF);
emitModRM(1, dst);
}
protected final void incl(Register dst) {
public final void incl(Register dst) {
// Use two-byte form (one-byte from is a REX prefix in 64-bit mode)
prefix(dst);
emitByte(0xFF);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
@ -111,6 +111,17 @@ public final class AVXKind {
return getAVXKind(kind.getScalar(), newSize);
}
public static AMD64Kind getMaskKind(AMD64Kind kind) {
switch (kind.getScalar()) {
case SINGLE:
return getAVXKind(AMD64Kind.DWORD, kind.getVectorLength());
case DOUBLE:
return getAVXKind(AMD64Kind.QWORD, kind.getVectorLength());
default:
return kind;
}
}
public static AMD64Kind getAVXKind(AMD64Kind base, AVXSize size) {
for (AMD64Kind ret : AMD64Kind.values()) {
if (ret.getScalar() == base && ret.getSizeInBytes() == size.getBytes()) {

View File

@ -628,6 +628,13 @@ public abstract class SPARCAssembler extends Assembler {
return value;
}
public int getOpfCCValue() {
/*
* In the opf_cc encoding for FMOVcc, the third bit is set to indicate icc/xcc.
*/
return (isFloat ? value : (value | 0x4));
}
public String getOperator() {
return operator;
}
@ -1613,7 +1620,7 @@ public abstract class SPARCAssembler extends Assembler {
inst = BitSpec.rd.setBits(inst, rd.encoding());
inst = BitSpec.op3.setBits(inst, opfLow.op3.value);
inst = BitSpec.opfCond.setBits(inst, condition.value);
inst = BitSpec.opfCC.setBits(inst, cc.value);
inst = BitSpec.opfCC.setBits(inst, cc.getOpfCCValue());
inst = BitSpec.opfLow.setBits(inst, opfLow.value);
inst = BitSpec.rs2.setBits(inst, rs2.encoding());
masm.emitInt(inst);
@ -2193,7 +2200,7 @@ public abstract class SPARCAssembler extends Assembler {
}
private void fmovcc(ConditionFlag cond, CC cc, Register rs2, Register rd, int opfLow) {
int opfCC = cc.value;
int opfCC = cc.getOpfCCValue();
int a = opfCC << 11 | opfLow << 5 | rs2.encoding;
fmt10(rd.encoding, Fpop2.value, cond.value, a);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -63,6 +63,14 @@ public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLo
private int displacement;
/*
* If this address has been improved by folding an uncompress operation into it, this is set by
* the address lowering to the uncompression scale used by the encoding strategy. It is null
* otherwise. This might be different from scale if we lowered an uncompression followed by
* further improvements that modify the scale.
*/
private Scale uncompressionScale;
public AMD64AddressNode(ValueNode base) {
this(base, null);
}
@ -72,6 +80,7 @@ public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLo
this.base = base;
this.index = index;
this.scale = Scale.Times1;
this.uncompressionScale = null;
}
public void canonicalizeIndex(SimplifierTool tool) {
@ -103,12 +112,14 @@ public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLo
AllocatableValue baseValue = base == null ? Value.ILLEGAL : tool.asAllocatable(gen.operand(base));
AllocatableValue indexValue = index == null ? Value.ILLEGAL : tool.asAllocatable(gen.operand(index));
AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue);
AllocatableValue baseReference = base == null ? null : LIRKind.derivedBaseFromValue(baseValue);
AllocatableValue indexReference;
if (index == null) {
indexReference = null;
} else if (scale.equals(Scale.Times1)) {
indexReference = LIRKind.derivedBaseFromValue(indexValue);
} else if (scale.equals(uncompressionScale) && LIRKind.isScalarCompressedReference(indexValue.getValueKind())) {
indexReference = LIRKind.derivedBaseFromValue(indexValue);
} else {
if (LIRKind.isValue(indexValue)) {
indexReference = null;
@ -163,6 +174,10 @@ public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLo
this.displacement = displacement;
}
public void setUncompressionScale(Scale scale) {
this.uncompressionScale = scale;
}
@Override
public long getMaxConstantDisplacement() {
return displacement;

View File

@ -1305,7 +1305,7 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen
return;
} else if (c instanceof VMConstant) {
VMConstant vc = (VMConstant) c;
if (size == DWORD && !GeneratePIC.getValue(getOptions())) {
if (size == DWORD && !GeneratePIC.getValue(getOptions()) && getLIRGen().target().inlineObjects) {
getLIRGen().append(new AMD64BinaryConsumer.VMConstOp(CMP.getMIOpcode(DWORD, false), left, vc));
} else {
getLIRGen().append(new AMD64BinaryConsumer.DataOp(CMP.getRMOpcode(size), size, left, vc));

View File

@ -409,7 +409,7 @@ public abstract class AMD64LIRGenerator extends LIRGenerator {
return result;
}
private static AVXSize getRegisterSize(Value a) {
protected static AVXSize getRegisterSize(Value a) {
AMD64Kind kind = (AMD64Kind) a.getPlatformKind();
if (kind.isXMM()) {
return AVXKind.getRegisterSize(kind);
@ -479,18 +479,19 @@ public abstract class AMD64LIRGenerator extends LIRGenerator {
if (JavaConstant.isNull(a.getConstant())) {
append(new AMD64BinaryConsumer.MemoryConstOp(CMP, size, b, 0, state));
return true;
} else if (a.getConstant() instanceof VMConstant && size == DWORD) {
} else if (a.getConstant() instanceof VMConstant && size == DWORD && target().inlineObjects) {
VMConstant vc = (VMConstant) a.getConstant();
append(new AMD64BinaryConsumer.MemoryVMConstOp(CMP.getMIOpcode(size, false), b, vc, state));
return true;
} else {
long value = a.getJavaConstant().asLong();
if (NumUtil.is32bit(value)) {
append(new AMD64BinaryConsumer.MemoryConstOp(CMP, size, b, (int) value, state));
return true;
} else {
return emitCompareRegMemoryOp(size, asAllocatable(a), b, state);
if (a.getConstant() instanceof JavaConstant && a.getJavaConstant().getJavaKind() != JavaKind.Object) {
long value = a.getJavaConstant().asLong();
if (NumUtil.is32bit(value)) {
append(new AMD64BinaryConsumer.MemoryConstOp(CMP, size, b, (int) value, state));
return true;
}
}
return emitCompareRegMemoryOp(size, asAllocatable(a), b, state);
}
}

View File

@ -421,6 +421,22 @@ public final class LIRKind extends ValueKind<LIRKind> {
return !isUnknownReference() && (referenceCompressionMask & (1 << idx)) != 0;
}
/**
* Check whether the given kind is a scalar (i.e., vector length 1) <b>compressed</b> reference.
*
* @param kind The kind to be checked.
* @return true if the given kind is a scalar compressed reference
*/
public static boolean isScalarCompressedReference(ValueKind<?> kind) {
if (kind instanceof LIRKind) {
LIRKind lirKind = (LIRKind) kind;
if (lirKind.getPlatformKind().getVectorLength() == 1 && lirKind.isCompressedReference(0)) {
return true;
}
}
return false;
}
/**
* Check whether this kind is a value type that doesn't need to be tracked at safepoints.
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2018, 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
@ -105,6 +105,11 @@ public class NumUtil {
return -0x80000000L <= x && x < 0x80000000L;
}
public static byte safeToByte(int v) {
assert isByte(v);
return (byte) v;
}
public static short safeToShort(int v) {
assert isShort(v);
return (short) v;

View File

@ -135,6 +135,13 @@ public abstract class Stamp implements SpeculationContextObject {
return this.equals(this.unrestricted());
}
/**
* Tests whether this stamp represents a pointer value.
*/
public boolean isPointerStamp() {
return this instanceof AbstractPointerStamp;
}
/**
* If this stamp represents a single value, the methods returns this single value. It returns
* null otherwise.

View File

@ -25,25 +25,27 @@
package org.graalvm.compiler.core.test;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import org.graalvm.compiler.nodes.FieldLocationIdentity;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.ReturnNode;
import org.graalvm.compiler.nodes.calc.ObjectEqualsNode;
import org.graalvm.compiler.nodes.memory.ReadNode;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
public class HashMapGetTest extends GraalCompilerTest {
public class HashMapGetTest extends SubprocessTest {
public static void mapGet(HashMap<Integer, Integer> map, Integer key) {
public static <K, V> void mapGet(HashMap<K, V> map, K key) {
map.get(key);
}
@Test
public void hashMapTest() {
HashMap<Integer, Integer> map = new HashMap<>();
ResolvedJavaMethod get = getResolvedJavaMethod(HashMapGetTest.class, "mapGet");
for (int i = 0; i < 5000; i++) {
for (int i = 0; i < 10000; i++) {
mapGet(map, i);
map.put(i, i);
mapGet(map, i);
@ -51,10 +53,18 @@ public class HashMapGetTest extends GraalCompilerTest {
test(get, null, map, 0);
for (IfNode ifNode : lastCompiledGraph.getNodes(IfNode.TYPE)) {
LogicNode condition = ifNode.condition();
if (ifNode.getTrueSuccessorProbability() < 0.4 && condition instanceof ObjectEqualsNode) {
assertTrue(ifNode.trueSuccessor().next() instanceof ReturnNode, "Expected return but got %s (trueSuccessor: %s)", ifNode.trueSuccessor().next(), ifNode.trueSuccessor());
if (ifNode.getTrueSuccessorProbability() < 0.4 && ifNode.predecessor() instanceof ReadNode && condition instanceof ObjectEqualsNode) {
ReadNode read = (ReadNode) ifNode.predecessor();
if (read.getLocationIdentity() instanceof FieldLocationIdentity && ((FieldLocationIdentity) read.getLocationIdentity()).getField().getName().contains("key")) {
assertTrue(ifNode.trueSuccessor().next() instanceof ReturnNode, "Expected return after %s, got %s", ifNode.trueSuccessor(), ifNode.trueSuccessor().next());
}
}
}
}
@Test
public void hashMapTestInSubprocess() throws IOException, InterruptedException {
launchSubprocess(this::hashMapTest);
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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 org.junit.Test;
public class ObjectSubstitutionsTest extends GraalCompilerTest {
public static int SideEffect;
public static final void notifySnippet() {
synchronized (ObjectSubstitutionsTest.class) {
SideEffect = System.identityHashCode(ObjectSubstitutionsTest.class);
ObjectSubstitutionsTest.class.notify();
}
}
public static final void notifyAllSnippet() {
synchronized (ObjectSubstitutionsTest.class) {
SideEffect = System.identityHashCode(ObjectSubstitutionsTest.class);
ObjectSubstitutionsTest.class.notifyAll();
}
}
@Test
public void testNotifyEmpty() {
test("notifySnippet");
}
@Test
public void testNotifyAllEmpty() {
test("notifyAllSnippet");
}
}

View File

@ -1841,6 +1841,25 @@ public final class DebugContext implements AutoCloseable {
return createTimer(format, arg1, arg2);
}
/**
* Gets the name to use for a class based on whether it appears to be an obfuscated name. The
* heuristic for an obfuscated name is that it is less than 6 characters in length and consists
* only of lower case letters.
*/
private static String getBaseName(Class<?> c) {
String simpleName = c.getSimpleName();
if (simpleName.length() < 6) {
for (int i = 0; i < simpleName.length(); i++) {
if (!Character.isLowerCase(simpleName.charAt(0))) {
return simpleName;
}
}
// Looks like an obfuscated simple class name so use qualified class name
return c.getName();
}
return simpleName;
}
/**
* There are paths where construction of formatted class names are common and the code below is
* surprisingly expensive, so compute it once and cache it.
@ -1848,17 +1867,21 @@ public final class DebugContext implements AutoCloseable {
private static final ClassValue<String> formattedClassName = new ClassValue<String>() {
@Override
protected String computeValue(Class<?> c) {
final String simpleName = c.getSimpleName();
String baseName = getBaseName(c);
if (Character.isLowerCase(baseName.charAt(0))) {
// Looks like an obfuscated simple class name so use qualified class name
baseName = c.getName();
}
Class<?> enclosingClass = c.getEnclosingClass();
if (enclosingClass != null) {
String prefix = "";
while (enclosingClass != null) {
prefix = enclosingClass.getSimpleName() + "_" + prefix;
prefix = getBaseName(enclosingClass) + "_" + prefix;
enclosingClass = enclosingClass.getEnclosingClass();
}
return prefix + simpleName;
return prefix + baseName;
} else {
return simpleName;
return baseName;
}
}
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -306,7 +306,11 @@ public final class ScopeImpl implements DebugContext.Scope {
assert owner.lastClosedScope instanceof DisabledScope : owner.lastClosedScope;
}
} catch (Throwable t) {
t.initCause(e);
if (t != e && t.getCause() == null) {
// This mitigates the chance of `e` being swallowed/lost in
// the case there's an error in the above handling of `e`.
t.initCause(e);
}
throw t;
}

View File

@ -141,7 +141,7 @@ public class AArch64HotSpotBackendFactory extends HotSpotBackendFactory {
stampProvider = createStampProvider();
}
try (InitTimer rt = timer("create GC provider")) {
gc = createGCProvider(config);
gc = createGCProvider(config, metaAccess);
}
Providers p = new Providers(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls, lowerer, null, stampProvider, gc);

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.amd64.test;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.junit.Test;
public class NumberOfTrailingZeroings003 extends GraalCompilerTest {
public static boolean numberOfTrailingZeros0() {
return Integer.numberOfTrailingZeros(0) == 32;
}
@Test
public void testNumberOfTrailingZeros0() {
test("numberOfTrailingZeros0");
}
public static boolean numberOfTrailingZeros1() {
return Integer.numberOfTrailingZeros(1) == 0;
}
@Test
public void testNumberOfTrailingZeros1() {
test("numberOfTrailingZeros1");
}
public static boolean numberOfTrailingZerosM1() {
return Integer.numberOfTrailingZeros(-1) == 0;
}
@Test
public void testNumberOfTrailingZerosM1() {
test("numberOfTrailingZerosM1");
}
public static boolean numberOfTrailingZerosMin() {
return Integer.numberOfTrailingZeros(Integer.MIN_VALUE) == 31;
}
@Test
public void testNumberOfTrailingZerosMin() {
test("numberOfTrailingZerosMin");
}
public static boolean numberOfTrailingZerosMax() {
return Integer.numberOfTrailingZeros(Integer.MAX_VALUE) == 0;
}
@Test
public void testNumberOfTrailingZerosMax() {
test("numberOfTrailingZerosMax");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -118,6 +118,7 @@ public class AMD64HotSpotAddressLowering extends AMD64CompressAddressLowering {
}
addr.setScale(scale);
addr.setUncompressionScale(scale);
addr.setIndex(compression.getValue());
return true;
}

View File

@ -133,7 +133,7 @@ public class AMD64HotSpotBackendFactory extends HotSpotBackendFactory {
stampProvider = createStampProvider();
}
try (InitTimer rt = timer("create GC provider")) {
gc = createGCProvider(config);
gc = createGCProvider(config, metaAccess);
}
Providers p = new Providers(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls, lowerer, null, stampProvider, gc);

View File

@ -101,7 +101,7 @@ public class SPARCHotSpotBackendFactory extends HotSpotBackendFactory {
HotSpotForeignCallsProvider foreignCalls = new SPARCHotSpotForeignCallsProvider(jvmciRuntime, runtime, metaAccess, codeCache, wordTypes, nativeABICallerSaveRegisters);
LoweringProvider lowerer = createLowerer(runtime, metaAccess, foreignCalls, registers, constantReflection, target);
HotSpotStampProvider stampProvider = createStampProvider();
HotSpotGCProvider gc = createGCProvider(config);
HotSpotGCProvider gc = createGCProvider(config, metaAccess);
Providers p = new Providers(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls, lowerer, null, stampProvider, gc);
HotSpotSnippetReflectionProvider snippetReflection = createSnippetReflection(runtime, constantReflection, wordTypes);
BytecodeProvider bytecodeProvider = createBytecodeProvider(metaAccess, snippetReflection);

View File

@ -249,7 +249,7 @@ public class CompilationWrapperTest extends GraalCompilerTest {
for (Probe probe : probes) {
String error = probe.test();
if (error != null) {
Assert.fail(String.format("Did not find expected occurences of '%s' in output of command: %s%n%s", probe.substring, error, proc));
Assert.fail(String.format("Did not find expected occurrences of '%s' in output of command: %s%n%s", probe.substring, error, proc));
}
}
String line = diagnosticProbe.lastMatchingLine;

View File

@ -1092,7 +1092,8 @@ public final class CompileTheWorld {
"'PartialEscapeAnalysis=false PrintCompilation=true'. " +
"Unless explicitly enabled with 'Inline=true' here, inlining is disabled.",
"MultiThreaded", "Run using multiple threads for compilation.",
"Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors().");
"Threads", "Number of threads to use for multithreaded execution. Defaults to Runtime.getRuntime().availableProcessors().",
"InvalidateInstalledCode", "Invalidate the generated code so the code cache doesn't fill up.");
// @formatter:on
}

View File

@ -0,0 +1,391 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.test;
import static org.graalvm.compiler.core.common.CompilationIdentifier.INVALID_COMPILATION_ID;
import static org.graalvm.compiler.debug.DebugOptions.DumpOnError;
import java.util.ArrayList;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.api.replacements.ClassSubstitution;
import org.graalvm.compiler.api.replacements.MethodSubstitution;
import org.graalvm.compiler.api.test.Graal;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.TTY;
import org.graalvm.compiler.java.BytecodeParserOptions;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.replacements.ReplacementsImpl;
import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
import org.graalvm.compiler.runtime.RuntimeProvider;
import org.junit.Assert;
import org.junit.Test;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class MethodSubstitutionEffectTest extends GraalCompilerTest {
public static int ValueFountain;
static class Substitutee {
public static int singleEffect(@SuppressWarnings("unused") int a) {
return 0;
}
public static int sequentialEffectInvalid(@SuppressWarnings("unused") int a) {
return 0;
}
public static void sequentialEffectInvalidVoid(@SuppressWarnings("unused") int a) {
}
public static int splitEffect(@SuppressWarnings("unused") int a) {
return 0;
}
public static void splitEffectVoid(@SuppressWarnings("unused") int a) {
}
public static int multiSplitEffectNoMerge(@SuppressWarnings("unused") int a) {
return 0;
}
public static int multiSplitEffectNoMergeInvalid(@SuppressWarnings("unused") int a) {
return 0;
}
public static int splitEffectWrong(@SuppressWarnings("unused") int a) {
return 0;
}
public static int splitParitalIntrinsicExit(@SuppressWarnings("unused") int a) {
return 0;
}
}
@ClassSubstitution(Substitutee.class)
public static class Substitutor {
@MethodSubstitution
public static int singleEffect(int a) {
return GraalDirectives.sideEffect(a);
}
@MethodSubstitution
public static int sequentialEffectInvalid(int a) {
GraalDirectives.sideEffect(a);
return GraalDirectives.sideEffect(a);
}
@MethodSubstitution
public static void sequentialEffectInvalidVoid(int a) {
GraalDirectives.sideEffect(a);
GraalDirectives.sideEffect(a);
}
@MethodSubstitution
public static int splitEffect(int a) {
int i;
if (a > 0) {
GraalDirectives.sideEffect(a);
i = a;
} else {
GraalDirectives.sideEffect(42);
i = 42;
}
return i;
}
@MethodSubstitution
public static void splitEffectVoid(int a) {
if (a > 0) {
GraalDirectives.sideEffect(a);
} else {
GraalDirectives.sideEffect(42);
}
}
@MethodSubstitution
public static int multiSplitEffectNoMerge(int a) {
switch (a) {
case 1:
GraalDirectives.sideEffect(a);
return 3;
case 2:
GraalDirectives.sideEffect(a);
return 2;
case 3:
GraalDirectives.sideEffect(a);
return 1;
default:
GraalDirectives.sideEffect(a);
return 0;
}
}
@MethodSubstitution
public static int multiSplitEffectNoMergeInvalid(int a) {
switch (a) {
case 1:
GraalDirectives.sideEffect(a);
return 3;
case 2:
GraalDirectives.sideEffect(a);
return 2;
case 3:
GraalDirectives.sideEffect(a);
return 1;
default:
GraalDirectives.sideEffect(a);
GraalDirectives.sideEffect(a);
return 0;
}
}
@MethodSubstitution
public static int splitEffectWrong(int a) {
int i;
if (a > 0) {
GraalDirectives.sideEffect(a);
GraalDirectives.sideEffect(a);
i = a;
} else {
i = 42;
GraalDirectives.controlFlowAnchor();
}
return i;
}
@MethodSubstitution
public static int splitParitalIntrinsicExit(int a) {
int i;
if (a > 0) {
i = GraalDirectives.sideEffect(a);
} else {
i = splitParitalIntrinsicExit(a);
}
return i;
}
}
@Override
protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
Registration r = new Registration(invocationPlugins, Substitutee.class, getReplacements(), bytecodeProvider);
r.registerMethodSubstitution(Substitutor.class, "singleEffect", int.class);
r.registerMethodSubstitution(Substitutor.class, "sequentialEffectInvalid", int.class);
r.registerMethodSubstitution(Substitutor.class, "sequentialEffectInvalidVoid", int.class);
r.registerMethodSubstitution(Substitutor.class, "splitEffect", int.class);
r.registerMethodSubstitution(Substitutor.class, "splitEffectVoid", int.class);
r.registerMethodSubstitution(Substitutor.class, "multiSplitEffectNoMerge", int.class);
r.registerMethodSubstitution(Substitutor.class, "multiSplitEffectNoMergeInvalid", int.class);
r.registerMethodSubstitution(Substitutor.class, "splitEffectWrong", int.class);
r.registerMethodSubstitution(Substitutor.class, "splitParitalIntrinsicExit", int.class);
super.registerInvocationPlugins(invocationPlugins);
}
private ClassfileBytecodeProvider getSystemClassLoaderBytecodeProvider() {
ReplacementsImpl d = (ReplacementsImpl) getReplacements();
MetaAccessProvider metaAccess = d.getProviders().getMetaAccess();
ClassfileBytecodeProvider bytecodeProvider = new ClassfileBytecodeProvider(metaAccess, d.snippetReflection, ClassLoader.getSystemClassLoader());
return bytecodeProvider;
}
static void snippet01() {
Substitutee.singleEffect(42);
if (ValueFountain == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet02() {
Substitutee.sequentialEffectInvalid(42);
if (ValueFountain == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet03() {
Substitutee.sequentialEffectInvalidVoid(42);
if (ValueFountain == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet04() {
Substitutee.splitEffect(ValueFountain);
if (ValueFountain == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet05() {
Substitutee.splitEffectVoid(ValueFountain);
if (ValueFountain == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet06() {
Substitutee.splitEffectWrong(ValueFountain);
if (ValueFountain == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet07() {
if (Substitutee.splitParitalIntrinsicExit(ValueFountain) == 42) {
GraalDirectives.deoptimize();
}
}
static void snippet08() {
Substitutee.multiSplitEffectNoMerge(ValueFountain);
}
StructuredGraph getGraph(String snippet) {
OptionValues options = new OptionValues(getInitialOptions(), DumpOnError, false);
/*
* We do not want to inline partial intrinsic exits in this test to test the state of the
* self recursive call.
*/
options = new OptionValues(getInitialOptions(), BytecodeParserOptions.InlinePartialIntrinsicExitDuringParsing, false);
StructuredGraph g = parseEager(getResolvedJavaMethod(snippet), AllowAssumptions.NO, options);
Suites s = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getSuites().getDefaultSuites(getInitialOptions());
s.getHighTier().apply(g, getDefaultHighTierContext());
s.getMidTier().apply(g, getDefaultMidTierContext());
return g;
}
@Test
public void test1() {
getGraph("snippet01");
}
@Test
@SuppressWarnings("try")
public void test2() {
try (AutoCloseable c = new TTY.Filter()) {
getGraph("snippet02");
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if (t.getCause() instanceof GraalError && t.getMessage().contains("unexpected node between return StateSplit and last instruction")) {
return;
}
throw new AssertionError(t);
}
}
@Test
@SuppressWarnings("try")
public void test3() {
try (AutoCloseable c = new TTY.Filter()) {
getGraph("snippet03");
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if (t.getCause() instanceof GraalError && t.getMessage().contains(" produced invalid framestate")) {
return;
}
throw new AssertionError(t);
}
}
@Test
public void test4() {
getGraph("snippet04");
}
@Test
public void test5() {
getGraph("snippet05");
}
@Test
@SuppressWarnings("try")
public void test6() {
try (AutoCloseable c = new TTY.Filter()) {
getGraph("snippet06");
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if (t.getCause() instanceof GraalError && t.getMessage().contains(" produced invalid framestate")) {
return;
}
throw new AssertionError(t);
}
}
@Test
public void test7() {
getGraph("snippet07");
}
@Test
public void test8() {
getGraph("snippet08");
}
@Test
@SuppressWarnings("try")
public void testRootCompiles() {
ArrayList<ResolvedJavaMethod> intrinisicsWithoutErrors = new ArrayList<>();
ArrayList<ResolvedJavaMethod> intrinisicsErrors = new ArrayList<>();
intrinisicsWithoutErrors.add(getResolvedJavaMethod(Substitutee.class, "singleEffect"));
intrinisicsWithoutErrors.add(getResolvedJavaMethod(Substitutee.class, "splitEffect"));
intrinisicsWithoutErrors.add(getResolvedJavaMethod(Substitutee.class, "splitEffectVoid"));
intrinisicsWithoutErrors.add(getResolvedJavaMethod(Substitutee.class, "multiSplitEffectNoMerge"));
intrinisicsWithoutErrors.add(getResolvedJavaMethod(Substitutee.class, "splitParitalIntrinsicExit"));
intrinisicsErrors.add(getResolvedJavaMethod(Substitutee.class, "sequentialEffectInvalid"));
intrinisicsErrors.add(getResolvedJavaMethod(Substitutee.class, "sequentialEffectInvalidVoid"));
intrinisicsErrors.add(getResolvedJavaMethod(Substitutee.class, "splitEffectWrong"));
intrinisicsErrors.add(getResolvedJavaMethod(Substitutee.class, "multiSplitEffectNoMergeInvalid"));
for (ResolvedJavaMethod method : intrinisicsWithoutErrors) {
StructuredGraph graph = getProviders().getReplacements().getIntrinsicGraph(method, INVALID_COMPILATION_ID, getDebugContext(), null);
getCode(method, graph);
}
for (ResolvedJavaMethod method : intrinisicsErrors) {
try (AutoCloseable c = new TTY.Filter()) {
StructuredGraph graph = getProviders().getReplacements().getIntrinsicGraph(method, INVALID_COMPILATION_ID, getDebugContext(), null);
getCode(method, graph);
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if ((t.getCause() instanceof GraalError || t instanceof GraalError) && t.getMessage().contains("invalid state")) {
continue;
}
throw new AssertionError(t);
}
}
}
}

View File

@ -0,0 +1,396 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.test;
import static org.graalvm.compiler.debug.DebugOptions.DumpOnError;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.api.replacements.ClassSubstitution;
import org.graalvm.compiler.api.replacements.MethodSubstitution;
import org.graalvm.compiler.api.test.Graal;
import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
import org.graalvm.compiler.core.common.spi.ForeignCallLinkage;
import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.TTY;
import org.graalvm.compiler.hotspot.nodes.CurrentJavaThreadNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.extended.ForeignCallNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.replacements.ReplacementsImpl;
import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
import org.graalvm.compiler.runtime.RuntimeProvider;
import jdk.internal.vm.compiler.word.LocationIdentity;
import org.junit.Assert;
import org.junit.Test;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class MethodSubstitutionForeignCallTest extends GraalCompilerTest {
public static final ForeignCallDescriptor TEST_CALL = new ForeignCallDescriptor("test", int.class, int.class);
public static class A {
static void invalidConsecutiveForeignCall1(@SuppressWarnings("unused") int phi) {
}
static void invalidConsecutiveForeignCall2(@SuppressWarnings("unused") int phi) {
}
static void validConsecutiveForeignCallReexecutable(@SuppressWarnings("unused") int phi) {
}
static void splitForeignCallInvalid(@SuppressWarnings("unused") int phi) {
}
}
@ClassSubstitution(A.class)
public static class ASubstitutions {
/*
* Invalid: two consecutive states, deopt could float in between.
*/
@MethodSubstitution
static void invalidConsecutiveForeignCall1(int phi) {
testDeopt(phi);
// invalid two consecutive calls
testDeopt(phi);
}
/*
* Invalid: two consecutive states, deopt could float in between. Same applies for
* non-deopting framestates if they are not re-executable. If they are, we are good.
*/
@MethodSubstitution
static void invalidConsecutiveForeignCall2(int phi) {
testNonDeopting(phi);
testNonDeopting(phi);
}
/*
* Valid, the foreign calls are re-executable and non-deopting (thus completely side-effect
* free), they do not need a state.
*/
@MethodSubstitution
static void validConsecutiveForeignCallReexecutable(int phi) {
testPureReexectuable(phi);
testPureReexectuable(phi);
}
/**
* Invalid: Splitting effect in a method substitution is allowed as long as it is just one
* effect per call. This is not the case here.
*/
@MethodSubstitution
static void splitForeignCallInvalid(int phi) {
if (SideEffect == 0) {
testDeopt(phi);
} else {
CurrentJavaThreadNode.get().writeByte(0, (byte) 0);
testDeopt(phi);
}
}
}
@Override
protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
invocationPlugins.register(new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
ForeignCallsProvider foreignCalls = new ForeignCallsProvider() {
@Override
public LIRKind getValueKind(JavaKind javaKind) {
return LIRKind.fromJavaKind(getTarget().arch, javaKind);
}
@Override
public ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
throw GraalError.shouldNotReachHere("Test code must not need this method");
}
@Override
public boolean isReexecutable(ForeignCallDescriptor descriptor) {
return false;
}
@Override
public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) {
return true;
}
@Override
public boolean isAvailable(ForeignCallDescriptor descriptor) {
return true;
}
@Override
public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) {
return new LocationIdentity[]{LocationIdentity.any()};
}
@Override
public boolean canDeoptimize(ForeignCallDescriptor descriptor) {
return true;
}
};
ForeignCallNode node = new ForeignCallNode(foreignCalls, TEST_CALL, arg);
node.setBci(b.bci());
b.addPush(JavaKind.Int, node);
return true;
}
}, MethodSubstitutionForeignCallTest.class, "testDeopt", int.class);
invocationPlugins.register(new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
ForeignCallsProvider foreignCalls = new ForeignCallsProvider() {
@Override
public LIRKind getValueKind(JavaKind javaKind) {
return LIRKind.fromJavaKind(getTarget().arch, javaKind);
}
@Override
public ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
throw GraalError.shouldNotReachHere("Test code must not need this method");
}
@Override
public boolean isReexecutable(ForeignCallDescriptor descriptor) {
return false;
}
@Override
public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) {
return false;
}
@Override
public boolean isAvailable(ForeignCallDescriptor descriptor) {
return true;
}
@Override
public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) {
return new LocationIdentity[]{LocationIdentity.any()};
}
@Override
public boolean canDeoptimize(ForeignCallDescriptor descriptor) {
return false;
}
};
ForeignCallNode node = new ForeignCallNode(foreignCalls, TEST_CALL, arg);
node.setBci(b.bci());
b.addPush(JavaKind.Int, node);
return true;
}
}, MethodSubstitutionForeignCallTest.class, "testNonDeopting", int.class);
invocationPlugins.register(new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
ForeignCallsProvider foreignCalls = new ForeignCallsProvider() {
@Override
public LIRKind getValueKind(JavaKind javaKind) {
return LIRKind.fromJavaKind(getTarget().arch, javaKind);
}
@Override
public ForeignCallLinkage lookupForeignCall(ForeignCallDescriptor descriptor) {
throw GraalError.shouldNotReachHere("Test code must not need this method");
}
@Override
public boolean isReexecutable(ForeignCallDescriptor descriptor) {
return true;
}
@Override
public boolean isGuaranteedSafepoint(ForeignCallDescriptor descriptor) {
return false;
}
@Override
public boolean isAvailable(ForeignCallDescriptor descriptor) {
return true;
}
@Override
public LocationIdentity[] getKilledLocations(ForeignCallDescriptor descriptor) {
return new LocationIdentity[]{LocationIdentity.any()};
}
@Override
public boolean canDeoptimize(ForeignCallDescriptor descriptor) {
return false;
}
};
ForeignCallNode node = new ForeignCallNode(foreignCalls, TEST_CALL, arg);
node.setBci(b.bci());
b.addPush(JavaKind.Int, node);
return true;
}
}, MethodSubstitutionForeignCallTest.class, "testPureReexectuable", int.class);
ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
Registration r = new Registration(invocationPlugins, A.class, getReplacements(), bytecodeProvider);
r.registerMethodSubstitution(ASubstitutions.class, "invalidConsecutiveForeignCall1", int.class);
r.registerMethodSubstitution(ASubstitutions.class, "invalidConsecutiveForeignCall2", int.class);
r.registerMethodSubstitution(ASubstitutions.class, "validConsecutiveForeignCallReexecutable", int.class);
r.registerMethodSubstitution(ASubstitutions.class, "splitForeignCallInvalid", int.class);
super.registerInvocationPlugins(invocationPlugins);
}
private ClassfileBytecodeProvider getSystemClassLoaderBytecodeProvider() {
ReplacementsImpl d = (ReplacementsImpl) getReplacements();
MetaAccessProvider metaAccess = d.getProviders().getMetaAccess();
ClassfileBytecodeProvider bytecodeProvider = new ClassfileBytecodeProvider(metaAccess, d.snippetReflection, ClassLoader.getSystemClassLoader());
return bytecodeProvider;
}
public static int SideEffect;
public static int testDeopt(int value) {
SideEffect = value;
return value;
}
public static int testNonDeopting(int value) {
return value;
}
public static int testPureReexectuable(int value) {
return value;
}
public static void testSnippetInvalidSequential() {
A.invalidConsecutiveForeignCall1(SideEffect);
if (SideEffect == 1) {
GraalDirectives.deoptimize();
}
}
public static void testNonDeoptingInvalid() {
A.invalidConsecutiveForeignCall2(SideEffect);
if (SideEffect == 1) {
GraalDirectives.deoptimize();
}
}
public static void testNonDeoptingSplit() {
A.splitForeignCallInvalid(SideEffect);
if (SideEffect == 1) {
GraalDirectives.deoptimize();
}
}
public static void testNonDeoptingReexectuable() {
A.validConsecutiveForeignCallReexecutable(SideEffect);
if (SideEffect == 1) {
GraalDirectives.deoptimize();
}
}
@Test
@SuppressWarnings("try")
public void test1() {
try (AutoCloseable c = new TTY.Filter()) {
OptionValues options = new OptionValues(getInitialOptions(), DumpOnError, false);
StructuredGraph g = parseEager(getResolvedJavaMethod("testSnippetInvalidSequential"), AllowAssumptions.NO, options);
Suites s = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getSuites().getDefaultSuites(getInitialOptions());
s.getHighTier().apply(g, getDefaultHighTierContext());
s.getMidTier().apply(g, getDefaultMidTierContext());
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if ((t.getCause() instanceof GraalError || t instanceof GraalError) && t.getMessage().contains("invalid framestate")) {
return;
}
throw new AssertionError(t);
}
}
@Test
@SuppressWarnings("try")
public void test2() {
try (AutoCloseable c = new TTY.Filter()) {
OptionValues options = new OptionValues(getInitialOptions(), DumpOnError, false);
StructuredGraph g = parseEager(getResolvedJavaMethod("testSnippetInvalidSequential"), AllowAssumptions.NO, options);
Suites s = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getSuites().getDefaultSuites(getInitialOptions());
s.getHighTier().apply(g, getDefaultHighTierContext());
s.getMidTier().apply(g, getDefaultMidTierContext());
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if ((t.getCause() instanceof GraalError || t instanceof GraalError) && t.getMessage().contains("invalid framestate")) {
return;
}
throw new AssertionError(t);
}
}
@Test
@SuppressWarnings("try")
public void test3() {
try (AutoCloseable c = new TTY.Filter()) {
OptionValues options = new OptionValues(getInitialOptions(), DumpOnError, false);
StructuredGraph g = parseEager(getResolvedJavaMethod("testNonDeoptingSplit"), AllowAssumptions.NO, options);
Suites s = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getSuites().getDefaultSuites(getInitialOptions());
s.getHighTier().apply(g, getDefaultHighTierContext());
s.getMidTier().apply(g, getDefaultMidTierContext());
Assert.fail("Compilation should not reach this point, must throw an exception before");
} catch (Throwable t) {
if ((t.getCause() instanceof GraalError || t instanceof GraalError) && t.getMessage().contains("invalid framestate")) {
return;
}
throw new AssertionError(t);
}
}
@Test
public void test4() {
StructuredGraph g = parseEager(getResolvedJavaMethod("testNonDeoptingReexectuable"), AllowAssumptions.NO);
Suites s = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getSuites().getDefaultSuites(getInitialOptions());
s.getHighTier().apply(g, getDefaultHighTierContext());
s.getMidTier().apply(g, getDefaultMidTierContext());
}
}

View File

@ -24,83 +24,117 @@
package org.graalvm.compiler.hotspot.test;
import static org.graalvm.compiler.core.common.GraalOptions.FullUnroll;
import static org.graalvm.compiler.core.common.GraalOptions.LoopPeeling;
import static org.graalvm.compiler.core.common.GraalOptions.PartialEscapeAnalysis;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.referentOffset;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.EnumSet;
import java.util.ListIterator;
import java.util.Objects;
import org.graalvm.compiler.api.test.Graal;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.HotSpotBackend;
import org.graalvm.compiler.hotspot.HotSpotGraalRuntime.HotSpotGC;
import org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil;
import org.graalvm.compiler.nodeinfo.NodeSize;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
import org.graalvm.compiler.nodes.gc.G1PostWriteBarrier;
import org.graalvm.compiler.nodes.gc.G1PreWriteBarrier;
import org.graalvm.compiler.nodes.gc.G1ReferentFieldReadBarrier;
import org.graalvm.compiler.nodes.gc.SerialWriteBarrier;
import org.graalvm.compiler.nodes.memory.HeapAccess.BarrierType;
import org.graalvm.compiler.nodes.memory.HeapAccess;
import org.graalvm.compiler.nodes.memory.ReadNode;
import org.graalvm.compiler.nodes.memory.WriteNode;
import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.phases.OptimisticOptimizations;
import org.graalvm.compiler.phases.common.GuardLoweringPhase;
import org.graalvm.compiler.phases.common.LoweringPhase;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.BasePhase;
import org.graalvm.compiler.phases.Phase;
import org.graalvm.compiler.phases.common.WriteBarrierAdditionPhase;
import org.graalvm.compiler.phases.common.inlining.InliningPhase;
import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
import org.graalvm.compiler.phases.tiers.HighTierContext;
import org.graalvm.compiler.phases.tiers.MidTierContext;
import org.graalvm.compiler.phases.tiers.Suites;
import org.graalvm.compiler.runtime.RuntimeProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import jdk.vm.ci.hotspot.HotSpotInstalledCode;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import sun.misc.Unsafe;
import jdk.vm.ci.meta.MetaAccessProvider;
/**
* The following unit tests assert the presence of write barriers for both Serial and G1 GCs.
* Normally, the tests check for compile time inserted barriers. However, there are the cases of
* unsafe loads of the java.lang.ref.Reference.referent field where runtime checks have to be
* performed also. For those cases, the unit tests check the presence of the compile-time inserted
* barriers. Concerning the runtime checks, the results of variable inputs (object types and
* offsets) passed as input parameters can be checked against printed output from the G1 write
* barrier snippets. The runtime checks have been validated offline.
* The following unit tests assert the presence of write barriers for G1 and for the other GCs that
* use a simple card mark barrier, like Serial, CMS, ParallelGC and Pthe arNew/ParOld GCs. Normally,
* the tests check for compile time inserted barriers. However, there are the cases of unsafe loads
* of the java.lang.ref.Reference.referent field where runtime checks have to be performed also. For
* those cases, the unit tests check the presence of the compile-time inserted barriers. Concerning
* the runtime checks, the results of variable inputs (object types and offsets) passed as input
* parameters can be checked against printed output from the G1 write barrier snippets. The runtime
* checks have been validated offline.
*/
public class WriteBarrierAdditionTest extends HotSpotGraalCompilerTest {
/**
* The set of GCs known at the time of writing of this test. The number of expected barrier
* might need to be adjusted for new GCs implementations.
*/
private static EnumSet<HotSpotGC> knownSupport = EnumSet.of(HotSpotGC.G1, HotSpotGC.CMS, HotSpotGC.Parallel, HotSpotGC.Serial);
private final GraalHotSpotVMConfig config = runtime().getVMConfig();
public static class Container {
public Container a;
public Container b;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Container container = (Container) o;
return Objects.equals(a, container.a) && Objects.equals(b, container.b);
}
@Override
public int hashCode() {
return Objects.hash(a, b);
}
}
private int expectedBarriers;
/**
* Expected 2 barriers for the Serial GC and 4 for G1 (2 pre + 2 post).
* Expected 2 barriers for the card mark GCs and 4 for G1 (2 pre + 2 post).
*/
@Test
public void test1() throws Exception {
testHelper("test1Snippet", (config.useG1GC) ? 4 : 2);
public void testAllocation() throws Exception {
this.expectedBarriers = (config.useG1GC) ? 4 : 2;
testWithoutPEA("testAllocationSnippet");
}
public static void test1Snippet() {
public static Container testAllocationSnippet() {
Container main = new Container();
Container temp1 = new Container();
Container temp2 = new Container();
main.a = temp1;
main.b = temp2;
return main;
}
/**
* Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post).
* Expected 4 barriers for the card mark GCs and 8 for G1 (4 pre + 4 post).
*/
@Test
public void test2() throws Exception {
testHelper("test2Snippet", config.useG1GC ? 8 : 4);
public void testLoopAllocation1() throws Exception {
this.expectedBarriers = config.useG1GC ? 8 : 4;
testWithoutPEA("test2Snippet", false);
testWithoutPEA("test2Snippet", true);
}
public static void test2Snippet(boolean test) {
@ -119,11 +153,12 @@ public class WriteBarrierAdditionTest extends HotSpotGraalCompilerTest {
}
/**
* Expected 4 barriers for the Serial GC and 8 for G1 (4 pre + 4 post).
* Expected 4 barriers for the card mark GCs and 8 for G1 (4 pre + 4 post).
*/
@Test
public void test3() throws Exception {
testHelper("test3Snippet", config.useG1GC ? 8 : 4);
public void testLoopAllocation2() throws Exception {
this.expectedBarriers = config.useG1GC ? 8 : 4;
testWithoutPEA("test3Snippet");
}
public static void test3Snippet() {
@ -140,84 +175,109 @@ public class WriteBarrierAdditionTest extends HotSpotGraalCompilerTest {
}
/**
* Expected 2 barriers for the Serial GC and 5 for G1 (3 pre + 2 post) The (2 or 4) barriers are
* emitted while initializing the fields of the WeakReference instance. The extra pre barrier of
* G1 concerns the read of the referent field.
* Expected 2 barriers for the card mark GCs and 5 for G1 (3 pre + 2 post) The (2 or 4) barriers
* are emitted while initializing the fields of the WeakReference instance. The extra pre
* barrier of G1 concerns the read of the referent field.
*/
@Test
public void test4() throws Exception {
testHelper("test4Snippet", config.useG1GC ? 5 : 2);
public void testReferenceGet() throws Exception {
this.expectedBarriers = config.useG1GC ? 1 : 0;
test("testReferenceGetSnippet");
}
public static Object test4Snippet() {
WeakReference<Object> weakRef = new WeakReference<>(new Object());
return weakRef.get();
public static Object testReferenceGetSnippet() {
return weakReference.get();
}
static WeakReference<Object> wr = new WeakReference<>(new Object());
static Container con = new Container();
static class DummyReference {
Object referent;
}
private static MetaAccessProvider getStaticMetaAccess() {
return ((HotSpotBackend) Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend()).getRuntime().getHostProviders().getMetaAccess();
}
private static final WeakReference<?> weakReference = new WeakReference<>(new Object());
private static final Object weakReferenceAsObject = new WeakReference<>(new Object());
private static final long referenceReferentFieldOffset = HotSpotReplacementsUtil.getFieldOffset(getStaticMetaAccess().lookupJavaType(Reference.class), "referent");
private static final long referenceQueueFieldOffset = HotSpotReplacementsUtil.getFieldOffset(getStaticMetaAccess().lookupJavaType(Reference.class), "queue");
private static final DummyReference dummyReference = new DummyReference();
private static final long dummyReferenceReferentFieldOffset = HotSpotReplacementsUtil.getFieldOffset(getStaticMetaAccess().lookupJavaType(DummyReference.class), "referent");
/**
* Expected 0 barrier for the Serial GC and 1 for G1. In this test, we load the correct offset
* of the WeakReference object so naturally we assert the presence of the pre barrier.
* The type is known to be WeakReference and the offset is a constant, so the
* {@link org.graalvm.compiler.nodes.extended.RawLoadNode} is converted back into a normal
* LoadFieldNode and the lowering of the field node inserts the proper barrier.
*/
@Test
public void test5() throws Exception {
testHelper("test5Snippet", config.useG1GC ? 1 : 0);
public void testReferenceReferent1() throws Exception {
this.expectedBarriers = config.useG1GC ? 1 : 0;
test("testReferenceReferentSnippet");
}
private static final boolean useCompressedOops = ((HotSpotBackend) Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend()).getRuntime().getVMConfig().useCompressedOops;
public Object test5Snippet() {
return UNSAFE.getObject(wr, useCompressedOops ? 12L : 16L);
public Object testReferenceReferentSnippet() {
return UNSAFE.getObject(weakReference, referenceReferentFieldOffset);
}
/**
* The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
* load the java.lang.ref.Reference.referent field so the pre barier has to be executed.
* The type is known to be WeakReference and the offset is non-constant, so the lowering of the
* {@link org.graalvm.compiler.nodes.extended.RawLoadNode} is guarded by a check that the offset
* is the same as {@link #referenceReferentFieldOffset} which does a barrier if requires it.
*/
@Test
public void test6() throws Exception {
test2("testUnsafeLoad", UNSAFE, wr, Long.valueOf(referentOffset(getMetaAccess())), null);
public void testReferenceReferent2() throws Exception {
this.expectedBarriers = config.useG1GC ? 1 : 0;
test("testReferenceReferent2Snippet", referenceReferentFieldOffset);
}
public Object testReferenceReferent2Snippet(long offset) {
return UNSAFE.getObject(weakReference, offset);
}
/**
* The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
* load a matching offset of a wrong object so the pre barier must not be executed.
* The type is known to be WeakReference and the offset is constant but not the referent field,
* so no barrier is required.
*/
@Test
public void test7() throws Exception {
test2("testUnsafeLoad", UNSAFE, con, Long.valueOf(referentOffset(getMetaAccess())), null);
public void testReferenceReferent3() throws Exception {
this.expectedBarriers = 0;
test("testReferenceReferent3Snippet");
}
public Object testReferenceReferent3Snippet() {
return UNSAFE.getObject(weakReference, referenceQueueFieldOffset);
}
/**
* The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
* load a non-matching offset field of the java.lang.ref.Reference object so the pre barier must
* not be executed.
* The type is a super class of WeakReference and the offset is non-constant, so the lowering of
* the {@link org.graalvm.compiler.nodes.extended.RawLoadNode} is guarded by a check that the
* offset is the same as {@link #referenceReferentFieldOffset} and the base object is a
* subclasses of {@link java.lang.ref.Reference} and does a barrier if requires it.
*/
@Test
public void test8() throws Exception {
test2("testUnsafeLoad", UNSAFE, wr, Long.valueOf(config.useCompressedOops ? 20 : 32), null);
public void testReferenceReferent4() throws Exception {
this.expectedBarriers = config.useG1GC ? 1 : 0;
test("testReferenceReferent4Snippet");
}
public Object testReferenceReferent4Snippet() {
return UNSAFE.getObject(weakReferenceAsObject, referenceReferentFieldOffset);
}
/**
* The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
* load a matching offset+disp field of the java.lang.ref.Reference object so the pre barier
* must be executed.
* The type is not related to Reference at all so no barrier check is required. This should be
* statically detectable.
*/
@Test
public void test10() throws Exception {
test2("testUnsafeLoad", UNSAFE, wr, Long.valueOf(config.useCompressedOops ? 6 : 8), Integer.valueOf(config.useCompressedOops ? 6 : 8));
public void testReferenceReferent5() throws Exception {
this.expectedBarriers = 0;
Assert.assertEquals("expected fields to have the same offset", referenceReferentFieldOffset, dummyReferenceReferentFieldOffset);
test("testReferenceReferent5Snippet");
}
/**
* The following test concerns the runtime checks of the unsafe loads. In this test, we unsafely
* load a non-matching offset+disp field of the java.lang.ref.Reference object so the pre barier
* must not be executed.
*/
@Test
public void test9() throws Exception {
test2("testUnsafeLoad", UNSAFE, wr, Long.valueOf(config.useCompressedOops ? 10 : 16), Integer.valueOf(config.useCompressedOops ? 10 : 16));
public Object testReferenceReferent5Snippet() {
return UNSAFE.getObject(dummyReference, referenceReferentFieldOffset);
}
static Object[] src = new Object[1];
@ -232,88 +292,93 @@ public class WriteBarrierAdditionTest extends HotSpotGraalCompilerTest {
}
}
public static void testArrayCopy(Object a, Object b, Object c) throws Exception {
public static void testArrayCopySnippet(Object a, Object b, Object c) throws Exception {
System.arraycopy(a, 0, b, 0, (int) c);
}
@Test
public void test11() throws Exception {
test2("testArrayCopy", src, dst, dst.length);
public void testArrayCopy() throws Exception {
this.expectedBarriers = 0;
test("testArrayCopySnippet", src, dst, dst.length);
}
public static Object testUnsafeLoad(Unsafe theUnsafe, Object a, Object b, Object c) throws Exception {
final int offset = (c == null ? 0 : ((Integer) c).intValue());
final long displacement = (b == null ? 0 : ((Long) b).longValue());
return theUnsafe.getObject(a, offset + displacement);
}
private HotSpotInstalledCode getInstalledCode(String name, boolean withUnsafePrefix) throws Exception {
final ResolvedJavaMethod javaMethod = withUnsafePrefix ? getResolvedJavaMethod(WriteBarrierAdditionTest.class, name, Unsafe.class, Object.class, Object.class, Object.class)
: getResolvedJavaMethod(WriteBarrierAdditionTest.class, name, Object.class, Object.class, Object.class);
final HotSpotInstalledCode installedCode = (HotSpotInstalledCode) getCode(javaMethod);
return installedCode;
}
@SuppressWarnings("try")
private void testHelper(final String snippetName, final int expectedBarriers) throws Exception, SecurityException {
ResolvedJavaMethod snippet = getResolvedJavaMethod(snippetName);
DebugContext debug = getDebugContext();
try (DebugContext.Scope s = debug.scope("WriteBarrierAdditionTest", snippet)) {
StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, debug);
HighTierContext highContext = getDefaultHighTierContext();
MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
new InliningPhase(new InlineEverythingPolicy(), createCanonicalizerPhase()).apply(graph, highContext);
this.createCanonicalizerPhase().apply(graph, highContext);
new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext);
new GuardLoweringPhase().apply(graph, midContext);
new LoweringPhase(this.createCanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
new WriteBarrierAdditionPhase().apply(graph, midContext);
debug.dump(DebugContext.BASIC_LEVEL, graph, "After Write Barrier Addition");
int barriers = 0;
private void verifyBarriers(StructuredGraph graph) {
Assert.assertTrue("Unknown collector selected", knownSupport.contains(runtime().getGarbageCollector()));
Assert.assertNotEquals("test must set expected barrier count", expectedBarriers, -1);
int barriers = 0;
if (config.useG1GC) {
barriers = graph.getNodes().filter(G1ReferentFieldReadBarrier.class).count() + graph.getNodes().filter(G1PreWriteBarrier.class).count() +
graph.getNodes().filter(G1PostWriteBarrier.class).count();
} else {
barriers = graph.getNodes().filter(SerialWriteBarrier.class).count();
}
if (expectedBarriers != barriers) {
Assert.assertEquals(expectedBarriers, barriers);
}
for (WriteNode write : graph.getNodes().filter(WriteNode.class)) {
if (config.useG1GC) {
barriers = graph.getNodes().filter(G1ReferentFieldReadBarrier.class).count() + graph.getNodes().filter(G1PreWriteBarrier.class).count() +
graph.getNodes().filter(G1PostWriteBarrier.class).count();
if (write.getBarrierType() != HeapAccess.BarrierType.NONE) {
Assert.assertEquals(1, write.successors().count());
Assert.assertTrue(write.next() instanceof G1PostWriteBarrier);
Assert.assertTrue(write.predecessor() instanceof G1PreWriteBarrier || write.getLocationIdentity().isImmutable());
}
} else {
barriers = graph.getNodes().filter(SerialWriteBarrier.class).count();
}
if (expectedBarriers != barriers) {
Assert.assertEquals(getScheduledGraphString(graph), expectedBarriers, barriers);
}
for (WriteNode write : graph.getNodes().filter(WriteNode.class)) {
if (config.useG1GC) {
if (write.getBarrierType() != BarrierType.NONE) {
Assert.assertEquals(1, write.successors().count());
Assert.assertTrue(write.next() instanceof G1PostWriteBarrier);
Assert.assertTrue(write.predecessor() instanceof G1PreWriteBarrier);
}
} else {
if (write.getBarrierType() != BarrierType.NONE) {
Assert.assertEquals(1, write.successors().count());
Assert.assertTrue(write.next() instanceof SerialWriteBarrier);
}
if (write.getBarrierType() != HeapAccess.BarrierType.NONE) {
Assert.assertEquals(1, write.successors().count());
Assert.assertTrue(write.next() instanceof SerialWriteBarrier);
}
}
}
for (ReadNode read : graph.getNodes().filter(ReadNode.class)) {
if (read.getBarrierType() != BarrierType.NONE) {
Assert.assertTrue(read.getAddress() instanceof OffsetAddressNode);
for (ReadNode read : graph.getNodes().filter(ReadNode.class)) {
if (read.getBarrierType() != HeapAccess.BarrierType.NONE) {
if (read.getAddress() instanceof OffsetAddressNode) {
JavaConstant constDisp = ((OffsetAddressNode) read.getAddress()).getOffset().asJavaConstant();
Assert.assertNotNull(constDisp);
Assert.assertEquals(referentOffset(getMetaAccess()), constDisp.asLong());
Assert.assertEquals(BarrierType.WEAK_FIELD, read.getBarrierType());
if (config.useG1GC) {
Assert.assertTrue(read.next() instanceof G1ReferentFieldReadBarrier);
if (constDisp != null) {
Assert.assertEquals(referentOffset(getMetaAccess()), constDisp.asLong());
}
}
Assert.assertTrue(HeapAccess.BarrierType.WEAK_FIELD == read.getBarrierType() || HeapAccess.BarrierType.MAYBE_WEAK_FIELD == read.getBarrierType());
if (config.useG1GC) {
Assert.assertTrue(read.next() instanceof G1ReferentFieldReadBarrier);
}
}
} catch (Throwable e) {
throw debug.handle(e);
}
}
private void test2(final String snippet, Object... args) throws Exception {
HotSpotInstalledCode code = getInstalledCode(snippet, args[0] instanceof Unsafe);
code.executeVarargs(args);
protected Result testWithoutPEA(String name, Object... args) {
return test(new OptionValues(getInitialOptions(), PartialEscapeAnalysis, false, FullUnroll, false, LoopPeeling, false), name, args);
}
@Before
public void before() {
expectedBarriers = -1;
}
/*
* Check the state of the barriers immediately after insertion.
*/
@Override
protected Suites createSuites(OptionValues opts) {
Suites ret = getBackend().getSuites().getDefaultSuites(opts).copy();
ListIterator<BasePhase<? super MidTierContext>> iter = ret.getMidTier().findPhase(WriteBarrierAdditionPhase.class, true);
iter.add(new Phase() {
@Override
protected void run(StructuredGraph graph) {
verifyBarriers(graph);
}
@Override
public float codeSizeIncrease() {
return NodeSize.IGNORE_SIZE_CONTRACT_FACTOR;
}
@Override
protected CharSequence getName() {
return "VerifyBarriersPhase";
}
});
return ret;
}
}

View File

@ -165,6 +165,7 @@ public class GraalHotSpotVMConfig extends GraalHotSpotVMConfigBase {
}
public final boolean useG1GC = getFlag("UseG1GC", Boolean.class);
public final boolean useCMSGC = getFlag("UseConcMarkSweepGC", Boolean.class, false);
public final int allocatePrefetchStyle = getFlag("AllocatePrefetchStyle", Integer.class);
public final int allocatePrefetchInstr = getFlag("AllocatePrefetchInstr", Integer.class);

View File

@ -39,6 +39,7 @@ import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotMetaAccessProvider;
import jdk.vm.ci.meta.MetaAccessProvider;
public abstract class HotSpotBackendFactory {
@ -54,8 +55,8 @@ public abstract class HotSpotBackendFactory {
return new HotSpotStampProvider();
}
protected HotSpotGCProvider createGCProvider(GraalHotSpotVMConfig config) {
return new HotSpotGCProvider(config);
protected HotSpotGCProvider createGCProvider(GraalHotSpotVMConfig config, MetaAccessProvider metaAccess) {
return new HotSpotGCProvider(config, metaAccess);
}
protected HotSpotReplacementsImpl createReplacements(TargetDescription target, Providers p, HotSpotSnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider) {

View File

@ -231,7 +231,7 @@ public final class HotSpotGraalRuntime implements HotSpotGraalRuntimeProvider {
public enum HotSpotGC {
// Supported GCs
Serial(true, "UseSerialGC"),
Parallel(true, "UseParallelGC", "UseParallelOldGC"),
Parallel(true, "UseParallelGC", "UseParallelOldGC", "UseParNewGC"),
CMS(true, "UseConcMarkSweepGC"),
G1(true, "UseG1GC"),
@ -355,6 +355,7 @@ public final class HotSpotGraalRuntime implements HotSpotGraalRuntimeProvider {
return null;
}
@Override
public HotSpotGC getGarbageCollector() {
return garbageCollector;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2018, 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
@ -58,6 +58,8 @@ public interface HotSpotGraalRuntimeProvider extends GraalRuntime, RuntimeProvid
return getClass().getSimpleName();
}
HotSpotGraalRuntime.HotSpotGC getGarbageCollector();
@Override
HotSpotBackend getHostBackend();

View File

@ -27,8 +27,8 @@ package org.graalvm.compiler.hotspot;
import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
import static org.graalvm.compiler.core.common.GraalOptions.UseEncodedGraphs;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
import java.util.Set;

View File

@ -30,7 +30,7 @@ import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
import static org.graalvm.compiler.core.common.GraalOptions.UseEncodedGraphs;
import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createIntrinsicInlineInfo;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION;
import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.ROOT_COMPILATION_ENCODING;
import java.util.ArrayList;
import java.util.Arrays;
@ -306,13 +306,17 @@ public class SymbolicSnippetEncoder {
StructuredGraph getMethodSubstitutionGraph(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, ReplacementsImpl replacements, IntrinsicContext.CompilationContext context,
StructuredGraph.AllowAssumptions allowAssumptions, Cancellable cancellable, OptionValues options) {
Integer startOffset = snippetStartOffsets.get(plugin.toString() + context);
IntrinsicContext.CompilationContext contextToUse = context;
if (context == IntrinsicContext.CompilationContext.ROOT_COMPILATION) {
contextToUse = IntrinsicContext.CompilationContext.ROOT_COMPILATION_ENCODING;
}
Integer startOffset = snippetStartOffsets.get(plugin.toString() + contextToUse);
if (startOffset == null) {
throw GraalError.shouldNotReachHere("plugin graph not found: " + plugin + " with " + context);
throw GraalError.shouldNotReachHere("plugin graph not found: " + plugin + " with " + contextToUse);
}
ResolvedJavaType accessingClass = replacements.getProviders().getMetaAccess().lookupJavaType(plugin.getDeclaringClass());
return decodeGraph(original, accessingClass, startOffset, replacements, context, allowAssumptions, cancellable, options);
return decodeGraph(original, accessingClass, startOffset, replacements, contextToUse, allowAssumptions, cancellable, options);
}
@SuppressWarnings("try")
@ -406,8 +410,13 @@ public class SymbolicSnippetEncoder {
//
// -J-Dgraal.Dump=SymbolicSnippetEncoder_:2 -J-Dgraal.PrintGraph=File
// -J-Dgraal.DebugStubsAndSnippets=true
IntrinsicContext.CompilationContext contextToUse = context;
if (context == IntrinsicContext.CompilationContext.ROOT_COMPILATION) {
contextToUse = IntrinsicContext.CompilationContext.ROOT_COMPILATION_ENCODING;
}
try (DebugContext debug = openDebugContext("SymbolicSnippetEncoder_", method, options)) {
StructuredGraph graph = snippetReplacements.makeGraph(debug, snippetReplacements.getDefaultReplacementBytecodeProvider(), method, args, original, trackNodeSourcePosition, null, context);
StructuredGraph graph = snippetReplacements.makeGraph(debug, snippetReplacements.getDefaultReplacementBytecodeProvider(), method, args, original, trackNodeSourcePosition, null,
contextToUse);
// Check if all methods which should be inlined are really inlined.
for (MethodCallTargetNode callTarget : graph.getNodes(MethodCallTargetNode.TYPE)) {
@ -508,7 +517,7 @@ public class SymbolicSnippetEncoder {
ResolvedJavaMethod original = plugin.getOriginalMethod(originalReplacements.getProviders().getMetaAccess());
registerMethodSubstitution(plugin, original, INLINE_AFTER_PARSING, options);
if (!original.isNative()) {
registerMethodSubstitution(plugin, original, ROOT_COMPILATION, options);
registerMethodSubstitution(plugin, original, ROOT_COMPILATION_ENCODING, options);
}
}
preparedPlugins = plugins.size();

View File

@ -74,6 +74,7 @@ import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
import org.graalvm.compiler.hotspot.nodes.type.MethodPointerStamp;
import org.graalvm.compiler.hotspot.replacements.AssertionSnippets;
import org.graalvm.compiler.hotspot.replacements.ClassGetHubNode;
import org.graalvm.compiler.hotspot.replacements.FastNotifyNode;
import org.graalvm.compiler.hotspot.replacements.HashCodeSnippets;
import org.graalvm.compiler.hotspot.replacements.HotSpotG1WriteBarrierSnippets;
import org.graalvm.compiler.hotspot.replacements.HotSpotSerialWriteBarrierSnippets;
@ -85,8 +86,11 @@ import org.graalvm.compiler.hotspot.replacements.LoadExceptionObjectSnippets;
import org.graalvm.compiler.hotspot.replacements.MonitorSnippets;
import org.graalvm.compiler.hotspot.replacements.NewObjectSnippets;
import org.graalvm.compiler.hotspot.replacements.ObjectCloneSnippets;
import org.graalvm.compiler.hotspot.replacements.ObjectSnippets;
import org.graalvm.compiler.hotspot.replacements.StringToBytesSnippets;
import org.graalvm.compiler.hotspot.replacements.UnsafeCopyMemoryNode;
import org.graalvm.compiler.hotspot.replacements.UnsafeLoadSnippets;
import org.graalvm.compiler.hotspot.replacements.UnsafeSnippets;
import org.graalvm.compiler.hotspot.replacements.aot.ResolveConstantSnippets;
import org.graalvm.compiler.hotspot.replacements.arraycopy.HotSpotArraycopySnippets;
import org.graalvm.compiler.hotspot.replacements.profiling.ProfileSnippets;
@ -107,6 +111,7 @@ import org.graalvm.compiler.nodes.ParameterNode;
import org.graalvm.compiler.nodes.SafepointNode;
import org.graalvm.compiler.nodes.StartNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
import org.graalvm.compiler.nodes.UnwindNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.AddNode;
@ -120,14 +125,12 @@ import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode;
import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode.BytecodeExceptionKind;
import org.graalvm.compiler.nodes.extended.ForeignCallNode;
import org.graalvm.compiler.nodes.extended.GetClassNode;
import org.graalvm.compiler.nodes.extended.GuardedUnsafeLoadNode;
import org.graalvm.compiler.nodes.extended.LoadHubNode;
import org.graalvm.compiler.nodes.extended.LoadMethodNode;
import org.graalvm.compiler.nodes.extended.OSRLocalNode;
import org.graalvm.compiler.nodes.extended.OSRLockNode;
import org.graalvm.compiler.nodes.extended.OSRMonitorEnterNode;
import org.graalvm.compiler.nodes.extended.OSRStartNode;
import org.graalvm.compiler.nodes.extended.RawLoadNode;
import org.graalvm.compiler.nodes.extended.StoreHubNode;
import org.graalvm.compiler.nodes.gc.G1ArrayRangePostWriteBarrier;
import org.graalvm.compiler.nodes.gc.G1ArrayRangePreWriteBarrier;
@ -202,7 +205,8 @@ public abstract class DefaultHotSpotLoweringProvider extends DefaultJavaLowering
protected HashCodeSnippets.Templates hashCodeSnippets;
protected ResolveConstantSnippets.Templates resolveConstantSnippets;
protected ProfileSnippets.Templates profileSnippets;
protected ObjectSnippets.Templates objectSnippets;
protected UnsafeSnippets.Templates unsafeSnippets;
protected ObjectCloneSnippets.Templates objectCloneSnippets;
protected ForeignCallSnippets.Templates foreignCallSnippets;
@ -212,6 +216,7 @@ public abstract class DefaultHotSpotLoweringProvider extends DefaultJavaLowering
this.runtime = runtime;
this.registers = registers;
this.constantReflection = constantReflection;
}
@Override
@ -233,6 +238,8 @@ public abstract class DefaultHotSpotLoweringProvider extends DefaultJavaLowering
resolveConstantSnippets = new ResolveConstantSnippets.Templates(options, factories, providers, target);
objectCloneSnippets = new ObjectCloneSnippets.Templates(options, factories, providers, target);
foreignCallSnippets = new ForeignCallSnippets.Templates(options, factories, providers, target);
objectSnippets = new ObjectSnippets.Templates(options, factories, providers, target);
unsafeSnippets = new UnsafeSnippets.Templates(options, factories, providers, target);
if (JavaVersionUtil.JAVA_SPEC <= 8) {
// AOT only introduced in JDK 9
profileSnippets = null;
@ -408,10 +415,19 @@ public abstract class DefaultHotSpotLoweringProvider extends DefaultJavaLowering
profileSnippets.lower((ProfileNode) n, tool);
} else if (n instanceof KlassBeingInitializedCheckNode) {
newObjectSnippets.lower((KlassBeingInitializedCheckNode) n, registers, tool);
} else if (n instanceof FastNotifyNode) {
if (graph.getGuardsStage() == GuardsStage.AFTER_FSA) {
objectSnippets.lower(n, tool);
}
} else if (n instanceof UnsafeCopyMemoryNode) {
if (graph.getGuardsStage() == GuardsStage.AFTER_FSA) {
unsafeSnippets.lower((UnsafeCopyMemoryNode) n, tool);
}
} else {
super.lower(n, tool);
}
}
}
private static void lowerComputeObjectAddressNode(ComputeObjectAddressNode n) {
@ -560,16 +576,6 @@ public abstract class DefaultHotSpotLoweringProvider extends DefaultJavaLowering
return graph.unique(new FloatingReadNode(address, OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION, null, KlassPointerStamp.klassNonNull(), AbstractBeginNode.prevBegin(anchor)));
}
@Override
protected void lowerUnsafeLoadNode(RawLoadNode load, LoweringTool tool) {
StructuredGraph graph = load.graph();
if (!(load instanceof GuardedUnsafeLoadNode) && !graph.getGuardsStage().allowsFloatingGuards() && addReadBarrier(load)) {
unsafeLoadSnippets.lower(load, tool);
} else {
super.lowerUnsafeLoadNode(load, tool);
}
}
private void lowerLoadMethodNode(LoadMethodNode loadMethodNode) {
StructuredGraph graph = loadMethodNode.graph();
HotSpotResolvedJavaMethod method = (HotSpotResolvedJavaMethod) loadMethodNode.getMethod();
@ -720,17 +726,6 @@ public abstract class DefaultHotSpotLoweringProvider extends DefaultJavaLowering
graph.replaceFixedWithFixed(node, foreignCallNode);
}
private boolean addReadBarrier(RawLoadNode load) {
if (runtime.getVMConfig().useG1GC && load.graph().getGuardsStage() == StructuredGraph.GuardsStage.FIXED_DEOPTS && load.object().getStackKind() == JavaKind.Object &&
load.accessKind() == JavaKind.Object && !StampTool.isPointerAlwaysNull(load.object())) {
ResolvedJavaType type = StampTool.typeOrNull(load.object());
if (type != null && !type.isArray()) {
return true;
}
}
return false;
}
private ReadNode createReadVirtualMethod(StructuredGraph graph, ValueNode hub, HotSpotResolvedJavaMethod method, ResolvedJavaType receiverType) {
return createReadVirtualMethod(graph, hub, method.vtableEntryOffset(receiverType));
}

View File

@ -35,11 +35,13 @@ import org.graalvm.compiler.nodes.java.AbstractNewObjectNode;
import org.graalvm.compiler.nodes.memory.FixedAccessNode;
import org.graalvm.compiler.nodes.spi.GCProvider;
import jdk.vm.ci.meta.MetaAccessProvider;
public class HotSpotGCProvider implements GCProvider {
private final BarrierSet barrierSet;
public HotSpotGCProvider(GraalHotSpotVMConfig config) {
this.barrierSet = createBarrierSet(config);
public HotSpotGCProvider(GraalHotSpotVMConfig config, MetaAccessProvider metaAccess) {
this.barrierSet = createBarrierSet(config, metaAccess);
}
@Override
@ -47,10 +49,10 @@ public class HotSpotGCProvider implements GCProvider {
return barrierSet;
}
private BarrierSet createBarrierSet(GraalHotSpotVMConfig config) {
private BarrierSet createBarrierSet(GraalHotSpotVMConfig config, MetaAccessProvider metaAccess) {
boolean useDeferredInitBarriers = config.useDeferredInitBarriers;
if (config.useG1GC) {
return new G1BarrierSet() {
return new G1BarrierSet(metaAccess) {
@Override
protected boolean writeRequiresPostBarrier(FixedAccessNode initializingWrite, ValueNode writtenValue) {
if (!super.writeRequiresPostBarrier(initializingWrite, writtenValue)) {

View File

@ -59,11 +59,11 @@ import org.graalvm.compiler.hotspot.replacements.CipherBlockChainingSubstitution
import org.graalvm.compiler.hotspot.replacements.ClassGetHubNode;
import org.graalvm.compiler.hotspot.replacements.CounterModeSubstitutions;
import org.graalvm.compiler.hotspot.replacements.DigestBaseSubstitutions;
import org.graalvm.compiler.hotspot.replacements.FastNotifyNode;
import org.graalvm.compiler.hotspot.replacements.HotSpotArraySubstitutions;
import org.graalvm.compiler.hotspot.replacements.HotSpotClassSubstitutions;
import org.graalvm.compiler.hotspot.replacements.IdentityHashCodeNode;
import org.graalvm.compiler.hotspot.replacements.ObjectCloneNode;
import org.graalvm.compiler.hotspot.replacements.ObjectSubstitutions;
import org.graalvm.compiler.hotspot.replacements.ReflectionGetCallerClassNode;
import org.graalvm.compiler.hotspot.replacements.ReflectionSubstitutions;
import org.graalvm.compiler.hotspot.replacements.SHA2Substitutions;
@ -194,7 +194,7 @@ public class HotSpotGraphBuilderPlugins {
registerCounterModePlugins(invocationPlugins, config, replacements);
registerBase64Plugins(invocationPlugins, config, metaAccess, foreignCalls);
registerUnsafePlugins(invocationPlugins, config, replacements);
StandardGraphBuilderPlugins.registerInvocationPlugins(metaAccess, snippetReflection, invocationPlugins, replacements, true, false);
StandardGraphBuilderPlugins.registerInvocationPlugins(metaAccess, snippetReflection, invocationPlugins, replacements, true, false, true);
registerArrayPlugins(invocationPlugins, replacements);
registerStringPlugins(invocationPlugins, replacements);
registerArraysSupportPlugins(invocationPlugins, config, replacements);
@ -230,12 +230,48 @@ public class HotSpotGraphBuilderPlugins {
}
});
}
r.registerMethodSubstitution(ObjectSubstitutions.class, "hashCode", Receiver.class);
r.register1("hashCode", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
ValueNode object = receiver.get();
b.addPush(JavaKind.Int, new IdentityHashCodeNode(object));
return true;
}
@Override
public boolean inlineOnly() {
return true;
}
});
if (config.inlineNotify()) {
r.registerMethodSubstitution(ObjectSubstitutions.class, "notify", Receiver.class);
r.register1("notify", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
ValueNode object = receiver.get();
b.add(new FastNotifyNode(object, false, b.bci()));
return true;
}
@Override
public boolean inlineOnly() {
return true;
}
});
}
if (config.inlineNotifyAll()) {
r.registerMethodSubstitution(ObjectSubstitutions.class, "notifyAll", Receiver.class);
r.register1("notifyAll", Receiver.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
ValueNode object = receiver.get();
b.add(new FastNotifyNode(object, true, b.bci()));
return true;
}
@Override
public boolean inlineOnly() {
return true;
}
});
}
}

View File

@ -24,18 +24,10 @@
package org.graalvm.compiler.hotspot.meta;
import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.doingUnsafeAccessOffset;
import org.graalvm.compiler.api.replacements.ClassSubstitution;
import org.graalvm.compiler.api.replacements.MethodSubstitution;
import org.graalvm.compiler.hotspot.HotSpotBackend;
import org.graalvm.compiler.hotspot.nodes.CurrentJavaThreadNode;
import org.graalvm.compiler.nodes.ComputeObjectAddressNode;
import org.graalvm.compiler.hotspot.replacements.UnsafeCopyMemoryNode;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.compiler.word.Word;
import jdk.internal.vm.compiler.word.LocationIdentity;
import jdk.internal.vm.compiler.word.WordFactory;
@ClassSubstitution(className = {"jdk.internal.misc.Unsafe", "sun.misc.Unsafe"})
public class HotSpotUnsafeSubstitutions {
@ -45,27 +37,12 @@ public class HotSpotUnsafeSubstitutions {
@SuppressWarnings("unused")
@MethodSubstitution(isStatic = false)
static void copyMemory(Object receiver, Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) {
Word srcAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(srcBase, srcOffset));
Word dstAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(destBase, destOffset));
Word size = WordFactory.signed(bytes);
HotSpotBackend.unsafeArraycopy(srcAddr, dstAddr, size);
UnsafeCopyMemoryNode.copyMemory(false, receiver, srcBase, srcOffset, destBase, destOffset, bytes);
}
@SuppressWarnings("unused")
@MethodSubstitution(value = "copyMemory", isStatic = false)
static void copyMemoryGuarded(Object receiver, Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) {
Word srcAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(srcBase, srcOffset));
Word dstAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(destBase, destOffset));
Word size = WordFactory.signed(bytes);
Word javaThread = CurrentJavaThreadNode.get();
int offset = doingUnsafeAccessOffset(INJECTED_VMCONFIG);
LocationIdentity any = LocationIdentity.any();
/* Set doingUnsafeAccess to guard and handle unsafe memory access failures */
javaThread.writeByte(offset, (byte) 1, any);
HotSpotBackend.unsafeArraycopy(srcAddr, dstAddr, size);
/* Reset doingUnsafeAccess */
javaThread.writeByte(offset, (byte) 0, any);
UnsafeCopyMemoryNode.copyMemory(true, receiver, srcBase, srcOffset, destBase, destOffset, bytes);
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.replacements;
import static org.graalvm.compiler.nodeinfo.InputType.State;
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.DeoptimizingNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
import org.graalvm.compiler.nodes.spi.Lowerable;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import jdk.internal.vm.compiler.word.LocationIdentity;
@NodeInfo(cycles = CYCLES_2, size = SIZE_0)
public class FastNotifyNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, DeoptimizingNode.DeoptDuring {
public static final NodeClass<FastNotifyNode> TYPE = NodeClass.create(FastNotifyNode.class);
private final boolean notifyAll;
private final int bci;
@Input ValueNode object;
@OptionalInput(State) FrameState stateDuring;
public FastNotifyNode(ValueNode object, boolean notifyAll, int bci) {
super(TYPE, StampFactory.forVoid());
this.object = object;
this.notifyAll = notifyAll;
this.bci = bci;
}
@Override
public void lower(LoweringTool tool) {
tool.getLowerer().lower(this, tool);
}
public boolean isNotifyAll() {
return notifyAll;
}
@Override
public LocationIdentity getKilledLocationIdentity() {
return LocationIdentity.any();
}
@Override
public FrameState stateDuring() {
return stateDuring;
}
@Override
public void setStateDuring(FrameState stateDuring) {
updateUsages(this.stateDuring, stateDuring);
this.stateDuring = stateDuring;
}
@Override
public void computeStateDuring(FrameState currentStateAfter) {
FrameState newStateDuring = currentStateAfter.duplicateModifiedDuringCall(bci, asNode().getStackKind());
setStateDuring(newStateDuring);
}
@Override
public boolean canDeoptimize() {
return true;
}
public int getBci() {
return bci;
}
}

View File

@ -56,6 +56,7 @@ import jdk.internal.vm.compiler.word.WordFactory;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class HotSpotG1WriteBarrierSnippets extends G1WriteBarrierSnippets {
public static final ForeignCallDescriptor G1WBPRECALL = new ForeignCallDescriptor("write_barrier_pre", void.class, Object.class);
@ -175,6 +176,16 @@ public final class HotSpotG1WriteBarrierSnippets extends G1WriteBarrierSnippets
return Log.LOG_PRINTF;
}
@Override
protected ResolvedJavaType referenceType() {
return HotSpotReplacementsUtil.referenceType(INJECTED_METAACCESS);
}
@Override
protected long referentOffset() {
return HotSpotReplacementsUtil.referentOffset(INJECTED_METAACCESS);
}
public static class Templates extends AbstractTemplates {
private final SnippetInfo g1PreWriteBarrier;
private final SnippetInfo g1ReferentReadBarrier;
@ -191,7 +202,9 @@ public final class HotSpotG1WriteBarrierSnippets extends G1WriteBarrierSnippets
HotSpotG1WriteBarrierSnippets receiver = new HotSpotG1WriteBarrierSnippets(config, providers.getRegisters());
g1PreWriteBarrier = snippet(G1WriteBarrierSnippets.class, "g1PreWriteBarrier", null, receiver, GC_INDEX_LOCATION, GC_LOG_LOCATION, SATB_QUEUE_MARKING_LOCATION, SATB_QUEUE_INDEX_LOCATION,
SATB_QUEUE_BUFFER_LOCATION);
g1ReferentReadBarrier = g1PreWriteBarrier;
g1ReferentReadBarrier = snippet(G1WriteBarrierSnippets.class, "g1ReferentReadBarrier", null, receiver, GC_INDEX_LOCATION, GC_LOG_LOCATION, SATB_QUEUE_MARKING_LOCATION,
SATB_QUEUE_INDEX_LOCATION,
SATB_QUEUE_BUFFER_LOCATION);
g1PostWriteBarrier = snippet(G1WriteBarrierSnippets.class, "g1PostWriteBarrier", null, receiver, GC_CARD_LOCATION, GC_INDEX_LOCATION, GC_LOG_LOCATION, CARD_QUEUE_INDEX_LOCATION,
CARD_QUEUE_BUFFER_LOCATION);
g1ArrayRangePreWriteBarrier = snippet(G1WriteBarrierSnippets.class, "g1ArrayRangePreWriteBarrier", null, receiver, GC_INDEX_LOCATION, GC_LOG_LOCATION, SATB_QUEUE_MARKING_LOCATION,

View File

@ -155,7 +155,7 @@ public class HotSpotReplacementsUtil {
}
@Fold
static int getFieldOffset(ResolvedJavaType type, String fieldName) {
public static int getFieldOffset(ResolvedJavaType type, String fieldName) {
for (ResolvedJavaField field : type.getInstanceFields(true)) {
if (field.getName().equals(fieldName)) {
return field.getOffset();
@ -889,6 +889,11 @@ public class HotSpotReplacementsUtil {
return getFieldOffset(metaAccessProvider.lookupJavaType(Reference.class), "referent");
}
@Fold
public static ResolvedJavaType referenceType(@InjectedParameter MetaAccessProvider metaAccessProvider) {
return metaAccessProvider.lookupJavaType(Reference.class);
}
public static final LocationIdentity OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION = new HotSpotOptimizingLocationIdentity("ObjArrayKlass::_element_klass") {
@Override
public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) {

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.replacements;
import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;
import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
import org.graalvm.compiler.debug.DebugHandlersFactory;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
import org.graalvm.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
import org.graalvm.compiler.nodes.PiNode;
import org.graalvm.compiler.nodes.SnippetAnchorNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.extended.ForeignCallNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.replacements.SnippetTemplate;
import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates;
import org.graalvm.compiler.replacements.SnippetTemplate.Arguments;
import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo;
import org.graalvm.compiler.replacements.Snippets;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class ObjectSnippets implements Snippets {
@NodeIntrinsic(ForeignCallNode.class)
public static native boolean fastNotifyStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object o);
@Snippet
public static void fastNotify(Object thisObj) {
if (fastNotifyStub(HotSpotHostForeignCallsProvider.NOTIFY, thisObj)) {
return;
} else {
PiNode.piCastNonNull(thisObj, SnippetAnchorNode.anchor()).notify();
}
}
@Snippet
public static void fastNotifyAll(Object thisObj) {
if (fastNotifyStub(HotSpotHostForeignCallsProvider.NOTIFY_ALL, thisObj)) {
return;
} else {
PiNode.piCastNonNull(thisObj, SnippetAnchorNode.anchor()).notifyAll();
}
}
public static class Templates extends AbstractTemplates {
private final SnippetInfo notifySnippet = snippet(ObjectSnippets.class, "fastNotify", originalNotifyCall(false), null);
private final SnippetInfo notifyAllSnippet = snippet(ObjectSnippets.class, "fastNotifyAll", originalNotifyCall(true), null);
public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
super(options, factories, providers, providers.getSnippetReflection(), target);
}
private ResolvedJavaMethod originalNotifyCall(boolean notifyAll) throws GraalError {
if (notifyAll) {
return findMethod(providers.getMetaAccess(), Object.class, "notifyAll");
} else {
return findMethod(providers.getMetaAccess(), Object.class, "notify");
}
}
public void lower(Node n, LoweringTool tool) {
if (n instanceof FastNotifyNode) {
FastNotifyNode fn = (FastNotifyNode) n;
StructuredGraph graph = (StructuredGraph) n.graph();
FrameState stateDuringCall = fn.stateDuring();
assert stateDuringCall != null : "Must have valid state for snippet recursive notify call";
Arguments args = new Arguments(fn.isNotifyAll() ? notifyAllSnippet : notifySnippet, graph.getGuardsStage(), tool.getLoweringStage());
args.add("thisObj", fn.object);
SnippetTemplate template = template(fn, args);
graph.getDebug().log("Lowering fast notify in %s: node=%s, template=%s, arguments=%s", graph, fn, template, args);
UnmodifiableEconomicMap<Node, Node> replacements = template.instantiate(providers.getMetaAccess(), fn, DEFAULT_REPLACER, args);
for (Node originalNode : replacements.getKeys()) {
if (originalNode instanceof InvokeNode) {
InvokeNode invoke = (InvokeNode) replacements.get(originalNode);
assert invoke.asNode().graph() == graph;
// Here we need to fix the bci of the invoke
invoke.replaceBci(fn.getBci());
invoke.setStateDuring(null);
invoke.setStateAfter(null);
invoke.setStateDuring(stateDuringCall);
} else if (originalNode instanceof InvokeWithExceptionNode) {
throw new GraalError("unexpected invoke with exception %s in snippet", originalNode);
}
}
} else {
GraalError.shouldNotReachHere("Unknown object snippet lowered node");
}
}
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright (c) 2012, 2018, 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.
*
* 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.hotspot.replacements;
import org.graalvm.compiler.api.replacements.ClassSubstitution;
import org.graalvm.compiler.api.replacements.MethodSubstitution;
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
import org.graalvm.compiler.hotspot.meta.HotSpotHostForeignCallsProvider;
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
import org.graalvm.compiler.nodes.extended.ForeignCallNode;
// JaCoCo Exclude
/**
* Substitutions for {@link java.lang.Object} methods.
*/
@ClassSubstitution(Object.class)
public class ObjectSubstitutions {
@MethodSubstitution(isStatic = false)
public static int hashCode(final Object thisObj) {
return IdentityHashCodeNode.identityHashCode(thisObj);
}
@MethodSubstitution(isStatic = false)
public static void notify(final Object thisObj) {
if (!fastNotifyStub(HotSpotHostForeignCallsProvider.NOTIFY, thisObj)) {
notify(thisObj);
}
}
@MethodSubstitution(isStatic = false)
public static void notifyAll(final Object thisObj) {
if (!fastNotifyStub(HotSpotHostForeignCallsProvider.NOTIFY_ALL, thisObj)) {
notifyAll(thisObj);
}
}
@NodeIntrinsic(ForeignCallNode.class)
public static native boolean fastNotifyStub(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object o);
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.replacements;
import static org.graalvm.compiler.nodeinfo.InputType.Memory;
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_64;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_16;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.AbstractStateSplit;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.ValueNodeUtil;
import org.graalvm.compiler.nodes.memory.MemoryAccess;
import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
import org.graalvm.compiler.nodes.memory.MemoryNode;
import org.graalvm.compiler.nodes.spi.Lowerable;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import jdk.internal.vm.compiler.word.LocationIdentity;
@NodeInfo(cycles = CYCLES_64, size = SIZE_16, allowedUsageTypes = {Memory})
public class UnsafeCopyMemoryNode extends AbstractStateSplit implements Lowerable, MemoryCheckpoint.Single, MemoryAccess {
public static final NodeClass<UnsafeCopyMemoryNode> TYPE = NodeClass.create(UnsafeCopyMemoryNode.class);
@Input ValueNode receiver;
@Input ValueNode srcBase;
@Input ValueNode srcOffset;
@Input ValueNode destBase;
@Input ValueNode desOffset;
@Input ValueNode bytes;
@OptionalInput(Memory) Node lastLocationAccess;
private final boolean guarded;
public UnsafeCopyMemoryNode(boolean guarded, ValueNode receiver, ValueNode srcBase, ValueNode srcOffset, ValueNode destBase, ValueNode desOffset,
ValueNode bytes) {
super(TYPE, StampFactory.forVoid());
this.guarded = guarded;
this.receiver = receiver;
this.srcBase = srcBase;
this.srcOffset = srcOffset;
this.destBase = destBase;
this.desOffset = desOffset;
this.bytes = bytes;
}
public boolean isGuarded() {
return guarded;
}
@Override
public LocationIdentity getKilledLocationIdentity() {
return LocationIdentity.any();
}
@Override
public void lower(LoweringTool tool) {
tool.getLowerer().lower(this, tool);
}
@Override
public LocationIdentity getLocationIdentity() {
return getKilledLocationIdentity();
}
@Override
public MemoryNode getLastLocationAccess() {
return (MemoryNode) lastLocationAccess;
}
@Override
public void setLastLocationAccess(MemoryNode lla) {
Node newLla = ValueNodeUtil.asNode(lla);
updateUsages(lastLocationAccess, newLla);
lastLocationAccess = newLla;
}
@NodeIntrinsic
public static native void copyMemory(@ConstantNodeParameter boolean guarded, Object receiver, Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.hotspot.replacements;
import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_VMCONFIG;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.doingUnsafeAccessOffset;
import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.debug.DebugHandlersFactory;
import org.graalvm.compiler.hotspot.HotSpotBackend;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.hotspot.nodes.CurrentJavaThreadNode;
import org.graalvm.compiler.nodes.ComputeObjectAddressNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates;
import org.graalvm.compiler.replacements.SnippetTemplate.Arguments;
import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo;
import org.graalvm.compiler.replacements.SnippetTemplate;
import org.graalvm.compiler.replacements.Snippets;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.compiler.word.Word;
import jdk.internal.vm.compiler.word.LocationIdentity;
import jdk.internal.vm.compiler.word.WordFactory;
import jdk.vm.ci.code.TargetDescription;
public class UnsafeSnippets implements Snippets {
public static final String copyMemoryName = JavaVersionUtil.JAVA_SPEC <= 8 ? "copyMemory" : "copyMemory0";
@SuppressWarnings("unused")
@Snippet
static void copyMemory(Object receiver, Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) {
Word srcAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(srcBase, srcOffset));
Word dstAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(destBase, destOffset));
Word size = WordFactory.signed(bytes);
HotSpotBackend.unsafeArraycopy(srcAddr, dstAddr, size);
}
@SuppressWarnings("unused")
@Snippet
static void copyMemoryGuarded(Object receiver, Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) {
Word srcAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(srcBase, srcOffset));
Word dstAddr = WordFactory.unsigned(ComputeObjectAddressNode.get(destBase, destOffset));
Word size = WordFactory.signed(bytes);
Word javaThread = CurrentJavaThreadNode.get();
int offset = doingUnsafeAccessOffset(INJECTED_VMCONFIG);
LocationIdentity any = LocationIdentity.any();
/* Set doingUnsafeAccess to guard and handle unsafe memory access failures */
javaThread.writeByte(offset, (byte) 1, any);
HotSpotBackend.unsafeArraycopy(srcAddr, dstAddr, size);
/* Reset doingUnsafeAccess */
javaThread.writeByte(offset, (byte) 0, any);
}
public static class Templates extends AbstractTemplates {
private final SnippetInfo copyMemory = snippet(UnsafeSnippets.class, "copyMemory");
private final SnippetInfo copyMemoryGuarded = snippet(UnsafeSnippets.class, "copyMemoryGuarded");
public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, HotSpotProviders providers, TargetDescription target) {
super(options, factories, providers, providers.getSnippetReflection(), target);
}
public void lower(UnsafeCopyMemoryNode copyMemoryNode, LoweringTool tool) {
StructuredGraph graph = copyMemoryNode.graph();
Arguments args = new Arguments(copyMemoryNode.isGuarded() ? copyMemoryGuarded : copyMemory, graph.getGuardsStage(), tool.getLoweringStage());
args.add("receiver", copyMemoryNode.receiver);
args.add("srcBase", copyMemoryNode.srcBase);
args.add("srcOffset", copyMemoryNode.srcOffset);
args.add("destBase", copyMemoryNode.destBase);
args.add("destOffset", copyMemoryNode.desOffset);
args.add("bytes", copyMemoryNode.bytes);
SnippetTemplate template = template(copyMemoryNode, args);
template.instantiate(providers.getMetaAccess(), copyMemoryNode, DEFAULT_REPLACER, args);
}
}
}

View File

@ -1079,7 +1079,7 @@ public final class BciBlockMapping {
BciBlock successor = block.getSuccessor(i);
JsrScope nextScope = scope;
if (successor == block.getJsrSuccessor()) {
nextScope = scope.push(block.getJsrReturnBci());
nextScope = scope.push(block.getJsrReturnBci(), successor);
}
if (successor == block.getRetSuccessor()) {
nextScope = scope.pop();
@ -1109,12 +1109,25 @@ public final class BciBlockMapping {
}
}
for (BciBlock successor : block.getSuccessors()) {
if (!jsrVisited.contains(successor)) {
if (!jsrVisited.contains(successor) && shouldFollowEdge(successor, scope)) {
createJsrAlternatives(blockMap, successor);
}
}
}
private static boolean shouldFollowEdge(BciBlock successor, JsrScope scope) {
if (successor instanceof ExceptionDispatchBlock && scope.getJsrEntryBlock() != null) {
ExceptionDispatchBlock exceptionDispatchBlock = (ExceptionDispatchBlock) successor;
int bci = scope.getJsrEntryBlock().startBci;
if (exceptionDispatchBlock.handler.getStartBCI() < bci && bci < exceptionDispatchBlock.handler.getEndBCI()) {
// Handler covers start of JSR block and the bci before that => don't follow edge.
return false;
}
}
return true;
}
private ExceptionDispatchBlock handleExceptions(BciBlock[] blockMap, int bci) {
ExceptionDispatchBlock lastHandler = null;
int dispatchBlocks = 0;

View File

@ -32,7 +32,6 @@ import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateRecompile;
import static jdk.vm.ci.meta.DeoptimizationAction.InvalidateReprofile;
import static jdk.vm.ci.meta.DeoptimizationAction.None;
import static jdk.vm.ci.meta.DeoptimizationReason.ClassCastException;
import static jdk.vm.ci.meta.DeoptimizationReason.JavaSubroutineMismatch;
import static jdk.vm.ci.meta.DeoptimizationReason.NullCheckException;
import static jdk.vm.ci.meta.DeoptimizationReason.RuntimeConstraint;
import static jdk.vm.ci.meta.DeoptimizationReason.UnreachedCode;
@ -684,6 +683,114 @@ public class BytecodeParser implements GraphBuilderContext {
// Restore the original return value
parser.frameState.push(returnKind, returnValue);
}
boolean inlinedIntrinsic = parser.getInvokeReturnType() != null;
if (inlinedIntrinsic) {
for (Node n : parser.graph.getNewNodes(mark)) {
if (n instanceof FrameState) {
GraalError.guarantee(((FrameState) n).bci != BytecodeFrame.INVALID_FRAMESTATE_BCI,
"Inlined call to intrinsic (callee %s) produced invalid framestate %s. " +
"Such framestates must never be used as deoptimizing targets, thus they cannot be part of a high-tier graph, " +
"and must only be used after framestate assignment. A common error is invalid usage of foreign call nodes in method " +
"substitutions, which can be avoided by ensuring such calls are either replaced with nodes that are snippet " +
"lowered after framestate assignment (see FastNotifyNode.java for example) or by ensuring all foreign use the state after of the " +
"original call instruction.",
callee, n);
}
}
} else {
/*
* Special case root compiled method substitutions
*
* Root compiled intrinsics with self recursive calls (partial intrinsic exit) must
* never produce more than one state except the start framestate since we do not
* compile calls to the original method (or inline them) but deopt
*
* See ByteCodeParser::inline and search for compilationRoot
*/
assert intrinsic == null || intrinsic.isIntrinsicEncoding() || verifyIntrinsicRootCompileEffects();
}
}
private boolean verifyIntrinsicRootCompileEffects() {
int invalidBCIsInRootCompiledIntrinsic = 0;
for (Node n : parser.graph.getNewNodes(mark)) {
if (n instanceof FrameState) {
if (((FrameState) n).bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
invalidBCIsInRootCompiledIntrinsic++;
}
}
}
if (invalidBCIsInRootCompiledIntrinsic > 1) {
int invalidBCIsToFind = invalidBCIsInRootCompiledIntrinsic;
List<ReturnNode> returns = parser.getGraph().getNodes(ReturnNode.TYPE).snapshot();
if (returns.size() > 1) {
outer: for (ReturnNode ret : returns) {
for (FixedNode f : GraphUtil.predecessorIterable(ret)) {
if (f instanceof StateSplit) {
StateSplit split = (StateSplit) f;
if (split.hasSideEffect()) {
assert ((StateSplit) f).stateAfter() != null;
if (split.stateAfter().bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
invalidBCIsToFind--;
continue outer;
}
}
}
}
}
GraalError.guarantee(invalidBCIsToFind == 0, "Root compiled intrinsic with invalid states has more than one return. " +
"This is allowed, however one path down a sink has more than one state, this is prohibited. " +
"Intrinsic %s", parser.method);
return true;
}
ReturnNode ret = returns.get(0);
MergeNode merge = null;
int mergeCount = parser.graph.getNodes(MergeNode.TYPE).count();
if (mergeCount != 1) {
throw new GraalError("Root compiled intrinsic with invalid states %s:Must have exactly one merge node. %d found", parser.method, mergeCount);
}
if (ret.predecessor() instanceof MergeNode) {
merge = (MergeNode) ret.predecessor();
}
if (merge == null) {
throw new GraalError("Root compiled intrinsic with invalid state: Unexpected node between return and merge.");
}
//@formatter:off
GraalError.guarantee(invalidBCIsInRootCompiledIntrinsic <= merge.phiPredecessorCount() + 1 /* merge itself */,
"Root compiled intrinsic with invalid states %s must at maximum produce (0,1 or if the last instruction is a merge |merge.predCount|" +
" invalid BCI state, however %d where found.",
parser.method, invalidBCIsInRootCompiledIntrinsic);
//@formatter:on
if (merge.stateAfter() != null && merge.stateAfter().bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
invalidBCIsToFind--;
}
for (EndNode pred : merge.cfgPredecessors()) {
Node lastPred = pred.predecessor();
for (FixedNode f : GraphUtil.predecessorIterable((FixedNode) lastPred)) {
if (f instanceof StateSplit) {
StateSplit split = (StateSplit) f;
if (split.hasSideEffect()) {
assert ((StateSplit) f).stateAfter() != null;
if (split.stateAfter().bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
invalidBCIsToFind--;
}
}
}
}
}
if (invalidBCIsToFind != 0) {
throw new GraalError(
"Invalid BCI state missmatch: This root compiled method substitution %s " +
"uses invalid side-effecting nodes resulting in invalid deoptimization information. " +
"Method substitutions must never have more than one state (the after state) for deoptimization." +
" Multiple states are only allowed if they are dominated by a control-flow split, there is only" +
" a single effect per branch and a post dominating merge with the same invalid_bci state " +
"(that must only be different in its return value).",
parser.method);
}
}
return true;
}
private void updateSplitFrameState(StateSplit split, JavaKind returnKind, ValueNode returnValue) {
@ -1820,7 +1927,6 @@ public class BytecodeParser implements GraphBuilderContext {
} finally {
currentInvoke = null;
}
int invokeBci = bci();
JavaTypeProfile profile = getProfileForInvoke(invokeKind);
ExceptionEdgeAction edgeAction = getActionForInvokeExceptionEdge(inlineInfo);
@ -2738,8 +2844,9 @@ public class BytecodeParser implements GraphBuilderContext {
int retAddress = scope.nextReturnAddress();
ConstantNode returnBciNode = getJsrConstant(retAddress);
LogicNode guard = IntegerEqualsNode.create(getConstantReflection(), getMetaAccess(), options, null, local, returnBciNode, NodeView.DEFAULT);
guard = graph.addOrUniqueWithInputs(guard);
append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile));
if (!guard.isTautology()) {
throw new JsrNotSupportedBailout("cannot statically decide jsr return address " + local);
}
if (!successor.getJsrScope().equals(scope.pop())) {
throw new JsrNotSupportedBailout("unstructured control flow (ret leaves more than one scope)");
}
@ -3436,7 +3543,6 @@ public class BytecodeParser implements GraphBuilderContext {
probability = getProfileProbability(canonicalizedCondition.mustNegate());
}
probability = clampProbability(probability);
genIf(condition, trueSuccessor, falseSuccessor, probability);
}
@ -3458,10 +3564,10 @@ public class BytecodeParser implements GraphBuilderContext {
// the probability coming from profile is about the original condition
probability = 1 - probability;
}
return probability;
return clampProbability(probability);
}
private static double extractInjectedProbability(IntegerEqualsNode condition) {
private double extractInjectedProbability(IntegerEqualsNode condition) {
// Propagate injected branch probability if any.
IntegerEqualsNode equalsNode = condition;
BranchProbabilityNode probabilityNode = null;
@ -3475,7 +3581,7 @@ public class BytecodeParser implements GraphBuilderContext {
}
if (probabilityNode != null && probabilityNode.getProbability().isConstant() && other != null && other.isConstant()) {
double probabilityValue = probabilityNode.getProbability().asJavaConstant().asDouble();
double probabilityValue = clampProbability(probabilityNode.getProbability().asJavaConstant().asDouble());
return other.asJavaConstant().asInt() == 0 ? 1.0 - probabilityValue : probabilityValue;
}
return -1;
@ -4166,11 +4272,15 @@ public class BytecodeParser implements GraphBuilderContext {
private JavaMethod lookupMethod(int cpi, int opcode) {
maybeEagerlyResolve(cpi, opcode);
JavaMethod result = constantPool.lookupMethod(cpi, opcode);
JavaMethod result = lookupMethodInPool(cpi, opcode);
assert !graphBuilderConfig.unresolvedIsError() || result instanceof ResolvedJavaMethod : unresolvedMethodAssertionMessage(result);
return result;
}
protected JavaMethod lookupMethodInPool(int cpi, int opcode) {
return constantPool.lookupMethod(cpi, opcode);
}
protected JavaField lookupField(int cpi, int opcode) {
maybeEagerlyResolve(cpi, opcode);
JavaField result = constantPool.lookupField(cpi, method, opcode);
@ -4319,6 +4429,7 @@ public class BytecodeParser implements GraphBuilderContext {
}
}
@SuppressWarnings("try")
protected void genInstanceOf(ResolvedJavaType resolvedType, ValueNode objectIn) {
ValueNode object = objectIn;
TypeReference checkedType = TypeReference.createTrusted(graph.getAssumptions(), resolvedType);
@ -4353,18 +4464,20 @@ public class BytecodeParser implements GraphBuilderContext {
int value = getStream().readUByte(next);
if (next <= currentBlock.endBci && (value == Bytecodes.IFEQ || value == Bytecodes.IFNE)) {
getStream().next();
BciBlock firstSucc = currentBlock.getSuccessor(0);
BciBlock secondSucc = currentBlock.getSuccessor(1);
if (firstSucc != secondSucc) {
boolean negate = value != Bytecodes.IFNE;
if (negate) {
BciBlock tmp = firstSucc;
firstSucc = secondSucc;
secondSucc = tmp;
try (DebugCloseable context = openNodeContext()) {
BciBlock firstSucc = currentBlock.getSuccessor(0);
BciBlock secondSucc = currentBlock.getSuccessor(1);
if (firstSucc != secondSucc) {
boolean negate = value != Bytecodes.IFNE;
if (negate) {
BciBlock tmp = firstSucc;
firstSucc = secondSucc;
secondSucc = tmp;
}
genIf(instanceOfNode, firstSucc, secondSucc, getProfileProbability(negate));
} else {
appendGoto(firstSucc);
}
genIf(instanceOfNode, firstSucc, secondSucc, getProfileProbability(negate));
} else {
appendGoto(firstSucc);
}
} else {
// Most frequent for value is IRETURN, followed by ISTORE.

View File

@ -25,6 +25,7 @@
package org.graalvm.compiler.java;
import org.graalvm.compiler.bytecode.Bytecodes;
import org.graalvm.compiler.java.BciBlockMapping.BciBlock;
/**
* Represents a subroutine entered via {@link Bytecodes#JSR} and exited via {@link Bytecodes#RET}.
@ -40,34 +41,46 @@ public final class JsrScope {
private final JsrScope parent;
private JsrScope(int returnBci, JsrScope parent) {
private final BciBlock jsrEntryBlock;
private JsrScope(int returnBci, BciBlock jsrEntryBlock, JsrScope parent) {
this.returnAddress = (char) returnBci;
this.parent = parent;
this.jsrEntryBlock = jsrEntryBlock;
}
private JsrScope() {
this.returnAddress = 0;
this.parent = null;
this.jsrEntryBlock = null;
}
public int nextReturnAddress() {
return returnAddress;
}
public BciBlock getJsrEntryBlock() {
return jsrEntryBlock;
}
/**
* Enters a new subroutine from the current scope represented by this object.
*
* @param returnBci the bytecode address returned to when leaving the new scope
* @return an object representing the newly entered scope
*/
public JsrScope push(int returnBci) {
public JsrScope push(int returnBci, BciBlock newJsrEntryBlock) {
if (returnBci == 0) {
throw new IllegalArgumentException("A bytecode subroutine cannot have a return address of 0");
}
if (returnBci < 1 || returnBci > 0xFFFF) {
throw new IllegalArgumentException("Bytecode subroutine return address cannot be encoded as a char: " + returnBci);
}
return new JsrScope(returnBci, this);
return new JsrScope(returnBci, newJsrEntryBlock, this);
}
public JsrScope push(int returnBci) {
return push(returnBci, null);
}
/**
@ -85,13 +98,13 @@ public final class JsrScope {
* {@code int[]} with {@code value.chars().toArray()}.
*/
public String getAncestry() {
StringBuilder sb = new StringBuilder();
String result = "";
for (JsrScope s = this; s != null; s = s.parent) {
if (!s.isEmpty()) {
sb.append(s.returnAddress);
result = s.returnAddress + result;
}
}
return sb.reverse().toString();
return result;
}
/**

View File

@ -113,7 +113,7 @@ public final class AMD64MathPowOp extends AMD64MathIntrinsicBinaryOp {
public AMD64MathPowOp() {
super(TYPE, /* GPR */ rax, rcx, rdx, r8, r9, r10, r11,
/* XMM */ xmm2, xmm3, xmm4, xmm5, xmm6, xmm7);
/* XMM */ xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7);
}
private ArrayDataPointerConstant highsigmask = pointerConstant(16, new int[]{

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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.lir.amd64.vector;
import static jdk.vm.ci.code.ValueUtil.asRegister;
import static jdk.vm.ci.code.ValueUtil.isRegister;
import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK;
import org.graalvm.compiler.asm.amd64.AMD64Address;
import org.graalvm.compiler.asm.amd64.AMD64MacroAssembler;
import org.graalvm.compiler.asm.amd64.AVXKind;
import org.graalvm.compiler.asm.amd64.AMD64Assembler.VexRVMROp;
import org.graalvm.compiler.lir.LIRInstructionClass;
import org.graalvm.compiler.lir.Opcode;
import org.graalvm.compiler.lir.amd64.AMD64LIRInstruction;
import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
import jdk.vm.ci.meta.AllocatableValue;
public class AVXBlendOp extends AMD64LIRInstruction {
public static final LIRInstructionClass<AVXBlendOp> TYPE = LIRInstructionClass.create(AVXBlendOp.class);
@Opcode private final VexRVMROp opcode;
private final AVXKind.AVXSize size;
@Def({REG}) protected AllocatableValue result;
@Use({REG}) protected AllocatableValue x;
@Use({REG, STACK}) protected AllocatableValue y;
@Use({REG}) protected AllocatableValue mask;
public AVXBlendOp(VexRVMROp opcode, AVXKind.AVXSize size, AllocatableValue result, AllocatableValue x, AllocatableValue y, AllocatableValue mask) {
super(TYPE);
this.opcode = opcode;
this.size = size;
this.result = result;
this.x = x;
this.y = y;
this.mask = mask;
}
@Override
public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
if (isRegister(y)) {
opcode.emit(masm, size, asRegister(result), asRegister(mask), asRegister(x), asRegister(y));
} else {
opcode.emit(masm, size, asRegister(result), asRegister(mask), asRegister(x), (AMD64Address) crb.asAddress(y));
}
}
}

Some files were not shown because too many files have changed in this diff Show More