Merge
This commit is contained in:
commit
83163dbfe6
1
.hgtags
1
.hgtags
@ -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
|
||||
|
@ -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), \
|
||||
))
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
}
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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()));
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) + " }";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
35
src/jdk.incubator.foreign/share/classes/module-info.java
Normal file
35
src/jdk.incubator.foreign/share/classes/module-info.java
Normal 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;
|
||||
}
|
@ -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).
|
||||
|
@ -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);
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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[]{
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user