# HG changeset patch # User Greg Lewis # Date 1495390368 25200 # Node ID d982b0f5b59a7808a57036a923f5d131ecd708d7 # Parent 368df6a4b403d5f9729835ff66438e6268a578ce# Parent 12b2ef172230eae018ad30216e7d0b241dcb82d3 Merge from main OpenJDK repository diff -r 368df6a4b403 -r d982b0f5b59a .hgtags --- a/.hgtags Thu Apr 13 17:18:04 2017 -0400 +++ b/.hgtags Sun May 21 11:12:48 2017 -0700 @@ -841,3 +841,5 @@ 6e42747134be8a4a366d337da1fc84a847ad8381 jdk7u101-b00 14d0f4da4a74897fc3274f8f549f41544bb4625a jdk7u111-b00 9efa3fae3c278a7f48badec775d7f9b2d5320b6d jdk7u111-b01 +17b40d99ea3665de2bfffe163b68f2dfcf675cba jdk7u121-b00 +95e4ea3d479ebdab9d78725776b6b11d4add6b0e jdk7u131-b00 diff -r 368df6a4b403 -r d982b0f5b59a agent/src/os/linux/libproc.h --- a/agent/src/os/linux/libproc.h Thu Apr 13 17:18:04 2017 -0400 +++ b/agent/src/os/linux/libproc.h Sun May 21 11:12:48 2017 -0700 @@ -76,7 +76,7 @@ }; #endif -#if defined(sparc) || defined(sparcv9) +#if defined(sparc) || defined(sparcv9) || defined(ppc64) #define user_regs_struct pt_regs #endif diff -r 368df6a4b403 -r d982b0f5b59a make/Makefile --- a/make/Makefile Thu Apr 13 17:18:04 2017 -0400 +++ b/make/Makefile Sun May 21 11:12:48 2017 -0700 @@ -85,6 +85,7 @@ # Typical C1/C2 targets made available with this Makefile C1_VM_TARGETS=product1 fastdebug1 optimized1 jvmg1 C2_VM_TARGETS=product fastdebug optimized jvmg +CORE_VM_TARGETS=productcore fastdebugcore optimizedcore jvmgcore ZERO_VM_TARGETS=productzero fastdebugzero optimizedzero jvmgzero SHARK_VM_TARGETS=productshark fastdebugshark optimizedshark jvmgshark @@ -127,6 +128,12 @@ all_debugshark: jvmgshark docs export_debug all_optimizedshark: optimizedshark docs export_optimized +allcore: all_productcore all_fastdebugcore +all_productcore: productcore docs export_product +all_fastdebugcore: fastdebugcore docs export_fastdebug +all_debugcore: jvmgcore docs export_debug +all_optimizedcore: optimizedcore docs export_optimized + # Do everything world: all create_jdk @@ -151,6 +158,10 @@ $(CD) $(GAMMADIR)/make; \ $(MAKE) BUILD_FLAVOR=$@ VM_TARGET=$@ generic_build2 $(ALT_OUT) +$(CORE_VM_TARGETS): + $(CD) $(GAMMADIR)/make; \ + $(MAKE) VM_TARGET=$@ generic_buildcore $(ALT_OUT) + $(ZERO_VM_TARGETS): $(CD) $(GAMMADIR)/make; \ $(MAKE) BUILD_FLAVOR=$(@:%zero=%) VM_TARGET=$@ \ @@ -203,6 +214,12 @@ $(MAKE_ARGS) $(VM_TARGET) endif +generic_buildcore: + $(MKDIR) -p $(OUTPUTDIR) + $(CD) $(OUTPUTDIR); \ + $(MAKE) -f $(ABS_OS_MAKEFILE) \ + $(MAKE_ARGS) $(VM_TARGET) + generic_buildzero: $(MKDIR) -p $(OUTPUTDIR) $(CD) $(OUTPUTDIR); \ @@ -257,10 +274,12 @@ C2_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_compiler2 ZERO_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_zero SHARK_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_shark +CORE_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_core C1_DIR=$(C1_BASE_DIR)/$(VM_SUBDIR) C2_DIR=$(C2_BASE_DIR)/$(VM_SUBDIR) ZERO_DIR=$(ZERO_BASE_DIR)/$(VM_SUBDIR) SHARK_DIR=$(SHARK_BASE_DIR)/$(VM_SUBDIR) +CORE_DIR=$(CORE_BASE_DIR)/$(VM_SUBDIR) ifeq ($(JVM_VARIANT_SERVER), true) MISC_DIR=$(C2_DIR) @@ -278,6 +297,10 @@ MISC_DIR=$(ZERO_DIR) GEN_DIR=$(ZERO_BASE_DIR)/generated endif +ifeq ($(JVM_VARIANT_CORE), true) + MISC_DIR=$(CORE_DIR) + GEN_DIR=$(CORE_BASE_DIR)/generated +endif # Bin files (windows) ifeq ($(OSNAME),windows) @@ -387,6 +410,20 @@ $(EXPORT_SERVER_DIR)/%.diz: $(ZERO_DIR)/%.diz $(install-file) endif + ifeq ($(JVM_VARIANT_CORE), true) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.$(LIBRARY_SUFFIX): $(CORE_DIR)/%.$(LIBRARY_SUFFIX) + $(install-file) + $(EXPORT_SERVER_DIR)/%.$(LIBRARY_SUFFIX): $(CORE_DIR)/%.$(LIBRARY_SUFFIX) + $(install-file) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.debuginfo: $(CORE_DIR)/%.debuginfo + $(install-file) + $(EXPORT_SERVER_DIR)/%.debuginfo: $(CORE_DIR)/%.debuginfo + $(install-file) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.diz: $(CORE_DIR)/%.diz + $(install-file) + $(EXPORT_SERVER_DIR)/%.diz: $(CORE_DIR)/%.diz + $(install-file) + endif endif # Jar file (sa-jdi.jar) diff -r 368df6a4b403 -r d982b0f5b59a make/aix/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/Makefile Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,380 @@ +# +# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# This makefile creates a build tree and lights off a build. +# You can go back into the build tree and perform rebuilds or +# incremental builds as desired. Be sure to reestablish +# environment variable settings for LD_LIBRARY_PATH and JAVA_HOME. + +# The make process now relies on java and javac. These can be +# specified either implicitly on the PATH, by setting the +# (JDK-inherited) ALT_BOOTDIR environment variable to full path to a +# JDK in which bin/java and bin/javac are present and working (e.g., +# /usr/local/java/jdk1.3/solaris), or via the (JDK-inherited) +# default BOOTDIR path value. Note that one of ALT_BOOTDIR +# or BOOTDIR has to be set. We do *not* search javac, javah, rmic etc. +# from the PATH. +# +# One can set ALT_BOOTDIR or BOOTDIR to point to a jdk that runs on +# an architecture that differs from the target architecture, as long +# as the bootstrap jdk runs under the same flavor of OS as the target +# (i.e., if the target is linux, point to a jdk that runs on a linux +# box). In order to use such a bootstrap jdk, set the make variable +# REMOTE to the desired remote command mechanism, e.g., +# +# make REMOTE="rsh -l me myotherlinuxbox" + +# Along with VM, Serviceability Agent (SA) is built for SA/JDI binding. +# JDI binding on SA produces two binaries: +# 1. sa-jdi.jar - This is build before building libjvm[_g].so +# Please refer to ./makefiles/sa.make +# 2. libsa[_g].so - Native library for SA - This is built after +# libjsig[_g].so (signal interposition library) +# Please refer to ./makefiles/vm.make +# If $(GAMMADIR)/agent dir is not present, SA components are not built. + +ifeq ($(GAMMADIR),) +include ../../make/defs.make +else +include $(GAMMADIR)/make/defs.make +endif +include $(GAMMADIR)/make/$(OSNAME)/makefiles/rules.make + +ifndef CC_INTERP + ifndef FORCE_TIERED + FORCE_TIERED=1 + endif +endif +# C1 is not ported on ppc64, so we cannot build a tiered VM: +ifeq ($(ARCH),ppc64)) + FORCE_TIERED=0 +endif + +ifdef LP64 + ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") + _JUNK_ := $(shell echo >&2 \ + $(OSNAME) $(ARCH) "*** ERROR: this platform does not support 64-bit compilers!") + @exit 1 + endif +endif + +# we need to set up LP64 correctly to satisfy sanity checks in adlc +ifneq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") + MFLAGS += " LP64=1 " +endif + +# pass USE_SUNCC further, through MFLAGS +ifdef USE_SUNCC + MFLAGS += " USE_SUNCC=1 " +endif + +# The following renders pathnames in generated Makefiles valid on +# machines other than the machine containing the build tree. +# +# For example, let's say my build tree lives on /files12 on +# exact.east.sun.com. This logic will cause GAMMADIR to begin with +# /net/exact/files12/... +# +# We only do this on SunOS variants, for a couple of reasons: +# * It is extremely rare that source trees exist on other systems +# * It has been claimed that the Linux automounter is flakey, so +# changing GAMMADIR in a way that exercises the automounter could +# prove to be a source of unreliability in the build process. +# Obviously, this Makefile is only relevant on SunOS boxes to begin +# with, but the SunOS conditionalization will make it easier to +# combine Makefiles in the future (assuming we ever do that). + +ifeq ($(OSNAME),solaris) + + # prepend current directory to relative pathnames. + NEW_GAMMADIR := \ + $(shell echo $(GAMMADIR) | \ + sed -e "s=^\([^/].*\)=$(shell pwd)/\1=" \ + ) + unexport NEW_GAMMADIR + + # If NEW_GAMMADIR doesn't already start with "/net/": + ifeq ($(strip $(filter /net/%,$(NEW_GAMMADIR))),) + # prepend /net/$(HOST) + # remove /net/$(HOST) if name already began with /home/ + # remove /net/$(HOST) if name already began with /java/ + # remove /net/$(HOST) if name already began with /lab/ + NEW_GAMMADIR := \ + $(shell echo $(NEW_GAMMADIR) | \ + sed -e "s=^\(.*\)=/net/$(HOST)\1=" \ + -e "s=^/net/$(HOST)/home/=/home/=" \ + -e "s=^/net/$(HOST)/java/=/java/=" \ + -e "s=^/net/$(HOST)/lab/=/lab/=" \ + ) + # Don't use the new value for GAMMADIR unless a file with the new + # name actually exists. + ifneq ($(wildcard $(NEW_GAMMADIR)),) + GAMMADIR := $(NEW_GAMMADIR) + endif + endif + +endif + +# BUILDARCH is set to "zero" for Zero builds. VARIANTARCH +# is used to give the build directories meaningful names. +VARIANTARCH = $(subst i386,i486,$(ZERO_LIBARCH)) + +# There is a (semi-) regular correspondence between make targets and actions: +# +# Target Tree Type Build Dir +# +# debug compiler2 __compiler2/debug +# fastdebug compiler2 __compiler2/fastdebug +# jvmg compiler2 __compiler2/jvmg +# optimized compiler2 __compiler2/optimized +# profiled compiler2 __compiler2/profiled +# product compiler2 __compiler2/product +# +# debug1 compiler1 __compiler1/debug +# fastdebug1 compiler1 __compiler1/fastdebug +# jvmg1 compiler1 __compiler1/jvmg +# optimized1 compiler1 __compiler1/optimized +# profiled1 compiler1 __compiler1/profiled +# product1 compiler1 __compiler1/product +# +# debugcore core __core/debug +# fastdebugcore core __core/fastdebug +# jvmgcore core __core/jvmg +# optimizedcore core __core/optimized +# profiledcore core __core/profiled +# productcore core __core/product +# +# debugzero zero __zero/debug +# fastdebugzero zero __zero/fastdebug +# jvmgzero zero __zero/jvmg +# optimizedzero zero __zero/optimized +# profiledzero zero __zero/profiled +# productzero zero __zero/product +# +# debugshark shark __shark/debug +# fastdebugshark shark __shark/fastdebug +# jvmgshark shark __shark/jvmg +# optimizedshark shark __shark/optimized +# profiledshark shark __shark/profiled +# productshark shark __shark/product +# +# What you get with each target: +# +# debug* - "thin" libjvm_g - debug info linked into the gamma_g launcher +# fastdebug* - optimized compile, but with asserts enabled +# jvmg* - "fat" libjvm_g - debug info linked into libjvm_g.so +# optimized* - optimized compile, no asserts +# profiled* - gprof +# product* - the shippable thing: optimized compile, no asserts, -DPRODUCT + +# This target list needs to be coordinated with the usage message +# in the build.sh script: +TARGETS = debug jvmg fastdebug optimized profiled product + +ifeq ($(findstring true, $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK)), true) + SUBDIR_DOCS = $(OSNAME)_$(VARIANTARCH)_docs +else + SUBDIR_DOCS = $(OSNAME)_$(BUILDARCH)_docs +endif +SUBDIRS_C1 = $(addprefix $(OSNAME)_$(BUILDARCH)_compiler1/,$(TARGETS)) +SUBDIRS_C2 = $(addprefix $(OSNAME)_$(BUILDARCH)_compiler2/,$(TARGETS)) +SUBDIRS_TIERED = $(addprefix $(OSNAME)_$(BUILDARCH)_tiered/,$(TARGETS)) +SUBDIRS_CORE = $(addprefix $(OSNAME)_$(BUILDARCH)_core/,$(TARGETS)) +SUBDIRS_ZERO = $(addprefix $(OSNAME)_$(VARIANTARCH)_zero/,$(TARGETS)) +SUBDIRS_SHARK = $(addprefix $(OSNAME)_$(VARIANTARCH)_shark/,$(TARGETS)) + +TARGETS_C2 = $(TARGETS) +TARGETS_C1 = $(addsuffix 1,$(TARGETS)) +TARGETS_TIERED = $(addsuffix tiered,$(TARGETS)) +TARGETS_CORE = $(addsuffix core,$(TARGETS)) +TARGETS_ZERO = $(addsuffix zero,$(TARGETS)) +TARGETS_SHARK = $(addsuffix shark,$(TARGETS)) + +BUILDTREE_MAKE = $(GAMMADIR)/make/$(OSNAME)/makefiles/buildtree.make +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) +BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HOTSPOT_RELEASE_VERSION) HOTSPOT_BUILD_VERSION=$(HOTSPOT_BUILD_VERSION) JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) +BUILDTREE_VARS += ENABLE_FULL_DEBUG_SYMBOLS=$(ENABLE_FULL_DEBUG_SYMBOLS) OBJCOPY=$(OBJCOPY) STRIP_POLICY=$(STRIP_POLICY) ZIP_DEBUGINFO_FILES=$(ZIP_DEBUGINFO_FILES) ZIPEXE=$(ZIPEXE) + +BUILDTREE = $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_VARS) + +#------------------------------------------------------------------------------- + +# Could make everything by default, but that would take a while. +all: + @echo "Try '$(MAKE) ...' where is one or more of" + @echo " $(TARGETS_C2)" + @echo " $(TARGETS_C1)" + @echo " $(TARGETS_CORE)" + @echo " $(TARGETS_ZERO)" + @echo " $(TARGETS_SHARK)" + +checks: check_os_version check_j2se_version + +# We do not want people accidentally building on old systems (e.g. Linux 2.2.x, +# Solaris 2.5.1, 2.6). +# Disable this check by setting DISABLE_HOTSPOT_OS_VERSION_CHECK=ok. + +SUPPORTED_OS_VERSION = AIX +OS_VERSION := $(shell uname -a) +EMPTY_IF_NOT_SUPPORTED = $(filter $(SUPPORTED_OS_VERSION),$(OS_VERSION)) + +check_os_version: +ifeq ($(DISABLE_HOTSPOT_OS_VERSION_CHECK)$(EMPTY_IF_NOT_SUPPORTED),) + $(QUIETLY) >&2 echo "*** This OS is not supported:" `uname -a`; exit 1; +endif + +# jvmti.make requires XSLT (J2SE 1.4.x or newer): +XSLT_CHECK = $(REMOTE) $(RUN.JAVAP) javax.xml.transform.TransformerFactory +# If not found then fail fast. +check_j2se_version: + $(QUIETLY) $(XSLT_CHECK) > /dev/null 2>&1; \ + if [ $$? -ne 0 ]; then \ + $(REMOTE) $(RUN.JAVA) -version; \ + echo "*** An XSLT processor (J2SE 1.4.x or newer) is required" \ + "to bootstrap this build" 1>&2; \ + exit 1; \ + fi + +$(SUBDIRS_TIERED): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=tiered + +$(SUBDIRS_C2): $(BUILDTREE_MAKE) +ifeq ($(FORCE_TIERED),1) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=tiered FORCE_TIERED=1 +else + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=compiler2 +endif + +$(SUBDIRS_C1): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=compiler1 + +$(SUBDIRS_CORE): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=core + +$(SUBDIRS_ZERO): $(BUILDTREE_MAKE) platform_zero + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=zero VARIANTARCH=$(VARIANTARCH) + +$(SUBDIRS_SHARK): $(BUILDTREE_MAKE) platform_zero + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=shark VARIANTARCH=$(VARIANTARCH) + +platform_zero: $(GAMMADIR)/make/$(OSNAME)/platform_zero.in + $(SED) 's/@ZERO_ARCHDEF@/$(ZERO_ARCHDEF)/g;s/@ZERO_LIBARCH@/$(ZERO_LIBARCH)/g;' < $< > $@ + +# Define INSTALL=y at command line to automatically copy JVM into JAVA_HOME + +$(TARGETS_C2): $(SUBDIRS_C2) + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) +# PPC port: skip gamma test until we get a HotSpot which has basic functionality +# cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_TIERED): $(SUBDIRS_TIERED) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_C1): $(SUBDIRS_C1) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_CORE): $(SUBDIRS_CORE) + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) +# PPC port: skip gamma test until we get a HotSpot which has basic functionality +# cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_ZERO): $(SUBDIRS_ZERO) + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_SHARK): $(SUBDIRS_SHARK) + cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) install +endif + +# Just build the tree, and nothing else: +tree: $(SUBDIRS_C2) +tree1: $(SUBDIRS_C1) +treecore: $(SUBDIRS_CORE) +treezero: $(SUBDIRS_ZERO) +treeshark: $(SUBDIRS_SHARK) + +# Doc target. This is the same for all build options. +# Hence create a docs directory beside ...$(ARCH)_[...] +# We specify 'BUILD_FLAVOR=product' so that the proper +# ENABLE_FULL_DEBUG_SYMBOLS value is used. +docs: checks + $(QUIETLY) mkdir -p $(SUBDIR_DOCS) + $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/makefiles/jvmti.make $(MFLAGS) $(BUILDTREE_VARS) JvmtiOutDir=$(SUBDIR_DOCS) BUILD_FLAVOR=product jvmtidocs + +# Synonyms for win32-like targets. +compiler2: jvmg product + +compiler1: jvmg1 product1 + +core: jvmgcore productcore + +zero: jvmgzero productzero + +shark: jvmgshark productshark + +clean_docs: + rm -rf $(SUBDIR_DOCS) + +clean_compiler1 clean_compiler2 clean_core clean_zero clean_shark: + rm -rf $(OSNAME)_$(BUILDARCH)_$(subst clean_,,$@) + +clean: clean_compiler2 clean_compiler1 clean_core clean_zero clean_shark clean_docs + +include $(GAMMADIR)/make/cscope.make + +#------------------------------------------------------------------------------- + +.PHONY: $(TARGETS_C2) $(TARGETS_C1) $(TARGETS_CORE) $(TARGETS_ZERO) $(TARGETS_SHARK) +.PHONY: tree tree1 treecore treezero treeshark +.PHONY: all compiler1 compiler2 core zero shark +.PHONY: clean clean_compiler1 clean_compiler2 clean_core clean_zero clean_shark docs clean_docs +.PHONY: checks check_os_version check_j2se_version diff -r 368df6a4b403 -r d982b0f5b59a make/aix/adlc_updater --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/adlc_updater Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,20 @@ +#! /bin/sh +# +# This file is used by adlc.make to selectively update generated +# adlc files. Because source and target diretories are relative +# paths, this file is copied to the target build directory before +# use. +# +# adlc-updater +# +fix_lines() { + # repair bare #line directives in $1 to refer to $2 + awk < $1 > $1+ ' + /^#line 999999$/ {print "#line " (NR+1) " \"" F2 "\""; next} + {print} + ' F2=$2 + mv $1+ $1 +} +fix_lines $2/$1 $3/$1 +[ -f $3/$1 ] && cmp -s $2/$1 $3/$1 || \ +( [ -f $3/$1 ] && echo Updating $3/$1 ; touch $2/made-change ; mv $2/$1 $3/$1 ) diff -r 368df6a4b403 -r d982b0f5b59a make/aix/build.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/build.sh Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,99 @@ +#! /bin/sh +# +# Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Make sure the variable JAVA_HOME is set before running this script. + +set -u + + +if [ $# != 2 ]; then + echo "Usage : $0 Build_Options Location" + echo "Build Options : debug or optimized or basicdebug or basic or clean" + echo "Location : specify any workspace which has gamma sources" + exit 1 +fi + +# Just in case: +case ${JAVA_HOME} in +/*) true;; +?*) JAVA_HOME=`( cd $JAVA_HOME; pwd )`;; +esac + +case `uname -m` in + i386|i486|i586|i686) + mach=i386 + ;; + x86_64) + mach=amd64 + ;; + *) + echo "Unsupported machine: " `uname -m` + exit 1 + ;; +esac + +if [ "${JAVA_HOME}" = "" -o ! -d "${JAVA_HOME}" -o ! -d ${JAVA_HOME}/jre/lib/${mach} ]; then + echo "JAVA_HOME needs to be set to a valid JDK path" + echo "ksh : export JAVA_HOME=/net/tetrasparc/export/gobi/JDK1.2_fcs_V/linux" + echo "csh : setenv JAVA_HOME /net/tetrasparc/export/gobi/JDK1.2_fcs_V/linux" + exit 1 +fi + + +LD_LIBRARY_PATH=${JAVA_HOME}/jre/lib/`uname -p`:\ +${JAVA_HOME}/jre/lib/`uname -p`/native_threads:${LD_LIBRARY_PATH-.} + +# This is necessary as long as we are using the old launcher +# with the new distribution format: +CLASSPATH=${JAVA_HOME}/jre/lib/rt.jar:${CLASSPATH-.} + + +for gm in gmake gnumake +do + if [ "${GNUMAKE-}" != "" ]; then break; fi + ($gm --version >/dev/null) 2>/dev/null && GNUMAKE=$gm +done +: ${GNUMAKE:?'Cannot locate the gnumake program. Stop.'} + + +echo "### ENVIRONMENT SETTINGS:" +export JAVA_HOME ; echo "JAVA_HOME=$JAVA_HOME" +export LD_LIBRARY_PATH ; echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" +export CLASSPATH ; echo "CLASSPATH=$CLASSPATH" +export GNUMAKE ; echo "GNUMAKE=$GNUMAKE" +echo "###" + +Build_Options=$1 +Location=$2 + +case ${Location} in +/*) true;; +?*) Location=`(cd ${Location}; pwd)`;; +esac + +echo \ +${GNUMAKE} -f ${Location}/make/linux/Makefile $Build_Options GAMMADIR=${Location} +${GNUMAKE} -f ${Location}/make/linux/Makefile $Build_Options GAMMADIR=${Location} diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/adjust-mflags.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/adjust-mflags.sh Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,87 @@ +#! /bin/sh +# +# Copyright (c) 1999, 2008, 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. +# +# + +# This script is used only from top.make. +# The macro $(MFLAGS-adjusted) calls this script to +# adjust the "-j" arguments to take into account +# the HOTSPOT_BUILD_JOBS variable. The default +# handling of the "-j" argument by gnumake does +# not meet our needs, so we must adjust it ourselves. + +# This argument adjustment applies to two recursive +# calls to "$(MAKE) $(MFLAGS-adjusted)" in top.make. +# One invokes adlc.make, and the other invokes vm.make. +# The adjustment propagates the desired concurrency +# level down to the sub-make (of the adlc or vm). +# The default behavior of gnumake is to run all +# sub-makes without concurrency ("-j1"). + +# Also, we use a make variable rather than an explicit +# "-j" argument to control this setting, so that +# the concurrency setting (which must be tuned separately +# for each MP system) can be set via an environment variable. +# The recommended setting is 1.5x to 2x the number of available +# CPUs on the MP system, which is large enough to keep the CPUs +# busy (even though some jobs may be I/O bound) but not too large, +# we may presume, to overflow the system's swap space. + +set -eu + +default_build_jobs=4 + +case $# in +[12]) true;; +*) >&2 echo "Usage: $0 ${MFLAGS} ${HOTSPOT_BUILD_JOBS}"; exit 2;; +esac + +MFLAGS=$1 +HOTSPOT_BUILD_JOBS=${2-} + +# Normalize any -jN argument to the form " -j${HBJ}" +MFLAGS=` + echo "$MFLAGS" \ + | sed ' + s/^-/ -/ + s/ -\([^ ][^ ]*\)j/ -\1 -j/ + s/ -j[0-9][0-9]*/ -j/ + s/ -j\([^ ]\)/ -j -\1/ + s/ -j/ -j'${HOTSPOT_BUILD_JOBS:-${default_build_jobs}}'/ + ' ` + +case ${HOTSPOT_BUILD_JOBS} in \ + +'') case ${MFLAGS} in + *\ -j*) + >&2 echo "# Note: -jN is ineffective for setting parallelism in this makefile." + >&2 echo "# please set HOTSPOT_BUILD_JOBS=${default_build_jobs} in the command line or environment." + esac;; + +?*) case ${MFLAGS} in + *\ -j*) true;; + *) MFLAGS="-j${HOTSPOT_BUILD_JOBS} ${MFLAGS}";; + esac;; +esac + +echo "${MFLAGS}" diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/adlc.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/adlc.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,234 @@ +# +# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# This makefile (adlc.make) is included from the adlc.make in the +# build directories. +# It knows how to compile, link, and run the adlc. + +include $(GAMMADIR)/make/$(Platform_os_family)/makefiles/rules.make + +# ######################################################################### + +# OUTDIR must be the same as AD_Dir = $(GENERATED)/adfiles in top.make: +GENERATED = ../generated +OUTDIR = $(GENERATED)/adfiles + +ARCH = $(Platform_arch) +OS = $(Platform_os_family) + +SOURCE.AD = $(OUTDIR)/$(OS)_$(Platform_arch_model).ad + +ifeq ("${Platform_arch_model}", "${Platform_arch}") + SOURCES.AD = \ + $(call altsrc-replace,$(HS_COMMON_SRC)/cpu/$(ARCH)/vm/$(Platform_arch_model).ad) \ + $(call altsrc-replace,$(HS_COMMON_SRC)/os_cpu/$(OS)_$(ARCH)/vm/$(OS)_$(Platform_arch_model).ad) +else + SOURCES.AD = \ + $(call altsrc-replace,$(HS_COMMON_SRC)/cpu/$(ARCH)/vm/$(Platform_arch_model).ad) \ + $(call altsrc-replace,$(HS_COMMON_SRC)/cpu/$(ARCH)/vm/$(Platform_arch).ad) \ + $(call altsrc-replace,$(HS_COMMON_SRC)/os_cpu/$(OS)_$(ARCH)/vm/$(OS)_$(Platform_arch_model).ad) +endif + +EXEC = $(OUTDIR)/adlc + +# set VPATH so make knows where to look for source files +Src_Dirs_V += $(GAMMADIR)/src/share/vm/adlc +VPATH += $(Src_Dirs_V:%=%:) + +# set INCLUDES for C preprocessor +Src_Dirs_I += $(GAMMADIR)/src/share/vm/adlc $(GENERATED) +INCLUDES += $(Src_Dirs_I:%=-I%) + +# set flags for adlc compilation +CXXFLAGS = $(SYSDEFS) $(INCLUDES) + +# Force assertions on. +CXXFLAGS += -DASSERT + +# CFLAGS_WARN holds compiler options to suppress/enable warnings. +# Suppress warnings (for now) +CFLAGS_WARN = -w +CFLAGS += $(CFLAGS_WARN) + +OBJECTNAMES = \ + adlparse.o \ + archDesc.o \ + arena.o \ + dfa.o \ + dict2.o \ + filebuff.o \ + forms.o \ + formsopt.o \ + formssel.o \ + main.o \ + adlc-opcodes.o \ + output_c.o \ + output_h.o \ + +OBJECTS = $(OBJECTNAMES:%=$(OUTDIR)/%) + +GENERATEDNAMES = \ + ad_$(Platform_arch_model).cpp \ + ad_$(Platform_arch_model).hpp \ + ad_$(Platform_arch_model)_clone.cpp \ + ad_$(Platform_arch_model)_expand.cpp \ + ad_$(Platform_arch_model)_format.cpp \ + ad_$(Platform_arch_model)_gen.cpp \ + ad_$(Platform_arch_model)_misc.cpp \ + ad_$(Platform_arch_model)_peephole.cpp \ + ad_$(Platform_arch_model)_pipeline.cpp \ + adGlobals_$(Platform_arch_model).hpp \ + dfa_$(Platform_arch_model).cpp \ + +GENERATEDFILES = $(GENERATEDNAMES:%=$(OUTDIR)/%) + +# ######################################################################### + +all: $(EXEC) + +$(EXEC) : $(OBJECTS) + @echo Making adlc + $(QUIETLY) $(HOST.LINK_NOPROF.CXX) -o $(EXEC) $(OBJECTS) + +# Random dependencies: +$(OBJECTS): opcodes.hpp classes.hpp adlc.hpp adlcVMDeps.hpp adlparse.hpp archDesc.hpp arena.hpp dict2.hpp filebuff.hpp forms.hpp formsopt.hpp formssel.hpp + +# The source files refer to ostream.h, which sparcworks calls iostream.h +$(OBJECTS): ostream.h + +ostream.h : + @echo >$@ '#include ' + +dump: + : OUTDIR=$(OUTDIR) + : OBJECTS=$(OBJECTS) + : products = $(GENERATEDFILES) + +all: $(GENERATEDFILES) + +$(GENERATEDFILES): refresh_adfiles + +# Get a unique temporary directory name, so multiple makes can run in parallel. +# Note that product files are updated via "mv", which is atomic. +TEMPDIR := $(OUTDIR)/mktmp$(shell echo $$$$) + +# Debuggable by default +CFLAGS += -g + +# Pass -D flags into ADLC. +ADLCFLAGS += $(SYSDEFS) + +# Note "+="; it is a hook so flags.make can add more flags, like -g or -DFOO. +ADLCFLAGS += -q -T + +# Normally, debugging is done directly on the ad_*.cpp files. +# But -g will put #line directives in those files pointing back to .ad. +# Some builds of gcc 3.2 have a bug that gets tickled by the extra #line directives +# so skip it for 3.2 and ealier. +ifneq "$(shell expr \( $(CC_VER_MAJOR) \> 3 \) \| \( \( $(CC_VER_MAJOR) = 3 \) \& \( $(CC_VER_MINOR) \>= 3 \) \))" "0" +ADLCFLAGS += -g +endif + +ifdef LP64 +ADLCFLAGS += -D_LP64 +else +ADLCFLAGS += -U_LP64 +endif + +# +# adlc_updater is a simple sh script, under sccs control. It is +# used to selectively update generated adlc files. This should +# provide a nice compilation speed improvement. +# +ADLC_UPDATER_DIRECTORY = $(GAMMADIR)/make/$(OS) +ADLC_UPDATER = adlc_updater +$(ADLC_UPDATER): $(ADLC_UPDATER_DIRECTORY)/$(ADLC_UPDATER) + $(QUIETLY) cp $< $@; chmod +x $@ + +# This action refreshes all generated adlc files simultaneously. +# The way it works is this: +# 1) create a scratch directory to work in. +# 2) if the current working directory does not have $(ADLC_UPDATER), copy it. +# 3) run the compiled adlc executable. This will create new adlc files in the scratch directory. +# 4) call $(ADLC_UPDATER) on each generated adlc file. It will selectively update changed or missing files. +# 5) If we actually updated any files, echo a notice. +# +refresh_adfiles: $(EXEC) $(SOURCE.AD) $(ADLC_UPDATER) + @rm -rf $(TEMPDIR); mkdir $(TEMPDIR) + $(QUIETLY) $(EXEC) $(ADLCFLAGS) $(SOURCE.AD) \ + -c$(TEMPDIR)/ad_$(Platform_arch_model).cpp -h$(TEMPDIR)/ad_$(Platform_arch_model).hpp -a$(TEMPDIR)/dfa_$(Platform_arch_model).cpp -v$(TEMPDIR)/adGlobals_$(Platform_arch_model).hpp \ + || { rm -rf $(TEMPDIR); exit 1; } + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model).cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model).hpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_clone.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_expand.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_format.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_gen.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_misc.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_peephole.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) ad_$(Platform_arch_model)_pipeline.cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) adGlobals_$(Platform_arch_model).hpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) ./$(ADLC_UPDATER) dfa_$(Platform_arch_model).cpp $(TEMPDIR) $(OUTDIR) + $(QUIETLY) [ -f $(TEMPDIR)/made-change ] \ + || echo "Rescanned $(SOURCE.AD) but encountered no changes." + $(QUIETLY) rm -rf $(TEMPDIR) + + +# ######################################################################### + +$(SOURCE.AD): $(SOURCES.AD) + $(QUIETLY) $(PROCESS_AD_FILES) $(SOURCES.AD) > $(SOURCE.AD) + +#PROCESS_AD_FILES = cat +# Pass through #line directives, in case user enables -g option above: +PROCESS_AD_FILES = awk '{ \ + if (CUR_FN != FILENAME) { CUR_FN=FILENAME; NR_BASE=NR-1; need_lineno=1 } \ + if (need_lineno && $$0 !~ /\/\//) \ + { print "\n\n\#line " (NR-NR_BASE) " \"" FILENAME "\""; need_lineno=0 }; \ + print }' + +$(OUTDIR)/%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(HOST.COMPILE.CXX) -o $@ $< $(COMPILE_DONE) + +# Some object files are given a prefix, to disambiguate +# them from objects of the same name built for the VM. +$(OUTDIR)/adlc-%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(HOST.COMPILE.CXX) -o $@ $< $(COMPILE_DONE) + +# ######################################################################### + +clean: + rm $(OBJECTS) + +cleanall: + rm $(OBJECTS) $(EXEC) + +# ######################################################################### + +.PHONY: all dump refresh_adfiles clean cleanall diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/build_vm_def.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/build_vm_def.sh Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,18 @@ +#!/bin/sh + +# If we're cross compiling use that path for nm +if [ "$CROSS_COMPILE_ARCH" != "" ]; then +NM=$ALT_COMPILER_PATH/nm +else +# On AIX we have to prevent that we pick up the 'nm' version from the GNU binutils +# which may be installed under /opt/freeware/bin. So better use an absolute path here! +NM=/usr/bin/nm +fi + +$NM -X64 -B -C $* \ + | awk '{ + if (($2="d" || $2="D") && ($3 ~ /^__vft/ || $3 ~ /^gHotSpotVM/)) print "\t" $3 ";" + if ($3 ~ /^UseSharedSpaces$/) print "\t" $3 ";" + if ($3 ~ /^SharedArchivePath__9Arguments$/) print "\t" $3 ";" + }' \ + | sort -u diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/buildtree.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/buildtree.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,510 @@ +# +# Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Usage: +# +# $(MAKE) -f buildtree.make SRCARCH=srcarch BUILDARCH=buildarch LIBARCH=libarch +# GAMMADIR=dir OS_FAMILY=os VARIANT=variant +# +# The macros ARCH, GAMMADIR, OS_FAMILY and VARIANT must be defined in the +# environment or on the command-line: +# +# ARCH - sparc, i486, ... HotSpot cpu and os_cpu source directory +# BUILDARCH - build directory +# LIBARCH - the corresponding directory in JDK/JRE +# GAMMADIR - top of workspace +# OS_FAMILY - operating system +# VARIANT - core, compiler1, compiler2, or tiered +# HOTSPOT_RELEASE_VERSION - .-b (11.0-b07) +# HOTSPOT_BUILD_VERSION - internal, internal-$(USER_RELEASE_SUFFIX) or empty +# JRE_RELEASE_VERSION - .. (1.7.0) +# +# Builds the directory trees with makefiles plus some convenience files in +# each directory: +# +# Makefile - for "make foo" +# flags.make - with macro settings +# vm.make - to support making "$(MAKE) -v vm.make" in makefiles +# adlc.make - +# trace.make - generate tracing event and type definitions +# jvmti.make - generate JVMTI bindings from the spec (JSR-163) +# sa.make - generate SA jar file and natives +# env.[ck]sh - environment settings +# test_gamma - script to run the Queens program +# +# The makefiles are split this way so that "make foo" will run faster by not +# having to read the dependency files for the vm. + +include $(GAMMADIR)/make/scm.make +include $(GAMMADIR)/make/altsrc.make + + +# 'gmake MAKE_VERBOSE=y' or 'gmake QUIETLY=' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ + +# For now, until the compiler is less wobbly: +TESTFLAGS = -Xbatch -showversion + +ifeq ($(findstring true, $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK)), true) + PLATFORM_FILE = $(shell dirname $(shell dirname $(shell pwd)))/platform_zero +else + ifdef USE_SUNCC + PLATFORM_FILE = $(GAMMADIR)/make/$(OS_FAMILY)/platform_$(BUILDARCH).suncc + else + PLATFORM_FILE = $(GAMMADIR)/make/$(OS_FAMILY)/platform_$(BUILDARCH) + endif +endif + +# Allow overriding of the arch part of the directory but default +# to BUILDARCH if nothing is specified +ifeq ($(VARIANTARCH),) + VARIANTARCH=$(BUILDARCH) +endif + +ifdef FORCE_TIERED +ifeq ($(VARIANT),tiered) +PLATFORM_DIR = $(OS_FAMILY)_$(VARIANTARCH)_compiler2 +else +PLATFORM_DIR = $(OS_FAMILY)_$(VARIANTARCH)_$(VARIANT) +endif +else +PLATFORM_DIR = $(OS_FAMILY)_$(VARIANTARCH)_$(VARIANT) +endif + +# +# We do two levels of exclusion in the shared directory. +# TOPLEVEL excludes are pruned, they are not recursively searched, +# but lower level directories can be named without fear of collision. +# ALWAYS excludes are excluded at any level in the directory tree. +# + +ALWAYS_EXCLUDE_DIRS = $(SCM_DIRS) + +ifeq ($(VARIANT),tiered) +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name agent +else +ifeq ($(VARIANT),compiler2) +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name c1 -o -name agent +else +# compiler1 and core use the same exclude list +TOPLEVEL_EXCLUDE_DIRS = $(ALWAYS_EXCLUDE_DIRS) -o -name adlc -o -name opto -o -name libadt -o -name agent +endif +endif + +# Get things from the platform file. +COMPILER = $(shell sed -n 's/^compiler[ ]*=[ ]*//p' $(PLATFORM_FILE)) + +SIMPLE_DIRS = \ + $(PLATFORM_DIR)/generated/dependencies \ + $(PLATFORM_DIR)/generated/adfiles \ + $(PLATFORM_DIR)/generated/jvmtifiles \ + $(PLATFORM_DIR)/generated/tracefiles + +TARGETS = debug fastdebug jvmg optimized product profiled +SUBMAKE_DIRS = $(addprefix $(PLATFORM_DIR)/,$(TARGETS)) + +# For dependencies and recursive makes. +BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make + +BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make trace.make jvmti.make sa.make \ + env.sh env.csh jdkpath.sh .dbxrc test_gamma + +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ + SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) + +# Define variables to be set in flags.make. +# Default values are set in make/defs.make. +ifeq ($(HOTSPOT_BUILD_VERSION),) + HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION) +else + HS_BUILD_VER=$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION) +endif +# Set BUILD_USER from system-dependent hints: $LOGNAME, $(whoami) +ifndef HOTSPOT_BUILD_USER + HOTSPOT_BUILD_USER := $(shell echo $$LOGNAME) +endif +ifndef HOTSPOT_BUILD_USER + HOTSPOT_BUILD_USER := $(shell whoami) +endif +# Define HOTSPOT_VM_DISTRO based on settings in make/openjdk_distro +# or make/hotspot_distro. +ifndef HOTSPOT_VM_DISTRO + ifeq ($(call if-has-altsrc,$(HS_COMMON_SRC)/,true,false),true) + include $(GAMMADIR)/make/hotspot_distro + else + include $(GAMMADIR)/make/openjdk_distro + endif +endif + +# if hotspot-only build and/or OPENJDK isn't passed down, need to set OPENJDK +ifndef OPENJDK + ifneq ($(call if-has-altsrc,$(HS_COMMON_SRC)/,true,false),true) + OPENJDK=true + endif +endif + +BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HS_BUILD_VER) HOTSPOT_BUILD_VERSION= JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) + +BUILDTREE = \ + $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_TARGETS) $(BUILDTREE_VARS) + +BUILDTREE_COMMENT = echo "\# Generated by $(BUILDTREE_MAKE)" + +all: $(SUBMAKE_DIRS) + +# Run make in each subdirectory recursively. +$(SUBMAKE_DIRS): $(SIMPLE_DIRS) FORCE + $(QUIETLY) [ -d $@ ] || { mkdir -p $@; } + $(QUIETLY) cd $@ && $(BUILDTREE) TARGET=$(@F) + $(QUIETLY) touch $@ + +$(SIMPLE_DIRS): + $(QUIETLY) mkdir -p $@ + +# Convenience macro which takes a source relative path, applies $(1) to the +# absolute path, and then replaces $(GAMMADIR) in the result with a +# literal "$(GAMMADIR)/" suitable for inclusion in a Makefile. +gamma-path=$(subst $(GAMMADIR),\$$(GAMMADIR),$(call $(1),$(HS_COMMON_SRC)/$(2))) + +flags.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo "Platform_file = $(PLATFORM_FILE)" | sed 's|$(GAMMADIR)|$$(GAMMADIR)|'; \ + sed -n '/=/s/^ */Platform_/p' < $(PLATFORM_FILE); \ + echo; \ + echo "GAMMADIR = $(GAMMADIR)"; \ + echo "SYSDEFS = \$$(Platform_sysdefs)"; \ + echo "SRCARCH = $(SRCARCH)"; \ + echo "BUILDARCH = $(BUILDARCH)"; \ + echo "LIBARCH = $(LIBARCH)"; \ + echo "TARGET = $(TARGET)"; \ + echo "HS_BUILD_VER = $(HS_BUILD_VER)"; \ + echo "JRE_RELEASE_VER = $(JRE_RELEASE_VERSION)"; \ + echo "SA_BUILD_VERSION = $(HS_BUILD_VER)"; \ + echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ + echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ + echo "OPENJDK = $(OPENJDK)"; \ + echo; \ + echo "# Used for platform dispatching"; \ + echo "TARGET_DEFINES = -DTARGET_OS_FAMILY_\$$(Platform_os_family)"; \ + echo "TARGET_DEFINES += -DTARGET_ARCH_\$$(Platform_arch)"; \ + echo "TARGET_DEFINES += -DTARGET_ARCH_MODEL_\$$(Platform_arch_model)"; \ + echo "TARGET_DEFINES += -DTARGET_OS_ARCH_\$$(Platform_os_arch)"; \ + echo "TARGET_DEFINES += -DTARGET_OS_ARCH_MODEL_\$$(Platform_os_arch_model)"; \ + echo "TARGET_DEFINES += -DTARGET_COMPILER_\$$(Platform_compiler)"; \ + echo "CFLAGS += \$$(TARGET_DEFINES)"; \ + echo; \ + echo "Src_Dirs_V = \\"; \ + sed 's/$$/ \\/;s|$(GAMMADIR)|$$(GAMMADIR)|' ../shared_dirs.lst; \ + echo "$(call gamma-path,altsrc,cpu/$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,commonsrc,cpu/$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,altsrc,os_cpu/$(OS_FAMILY)_$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,commonsrc,os_cpu/$(OS_FAMILY)_$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,altsrc,os/$(OS_FAMILY)/vm) \\"; \ + echo "$(call gamma-path,commonsrc,os/$(OS_FAMILY)/vm) \\"; \ + echo "$(call gamma-path,altsrc,os/posix/vm) \\"; \ + echo "$(call gamma-path,commonsrc,os/posix/vm)"; \ + echo; \ + echo "Src_Dirs_I = \\"; \ + echo "$(call gamma-path,altsrc,share/vm/prims) \\"; \ + echo "$(call gamma-path,commonsrc,share/vm/prims) \\"; \ + echo "$(call gamma-path,altsrc,share/vm) \\"; \ + echo "$(call gamma-path,commonsrc,share/vm) \\"; \ + echo "$(call gamma-path,altsrc,share/vm/precompiled) \\"; \ + echo "$(call gamma-path,commonsrc,share/vm/precompiled) \\"; \ + echo "$(call gamma-path,altsrc,cpu/$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,commonsrc,cpu/$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,altsrc,os_cpu/$(OS_FAMILY)_$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,commonsrc,os_cpu/$(OS_FAMILY)_$(SRCARCH)/vm) \\"; \ + echo "$(call gamma-path,altsrc,os/$(OS_FAMILY)/vm) \\"; \ + echo "$(call gamma-path,commonsrc,os/$(OS_FAMILY)/vm) \\"; \ + echo "$(call gamma-path,altsrc,os/posix/vm) \\"; \ + echo "$(call gamma-path,commonsrc,os/posix/vm)"; \ + [ -n "$(CFLAGS_BROWSE)" ] && \ + echo && echo "CFLAGS_BROWSE = $(CFLAGS_BROWSE)"; \ + [ -n "$(ENABLE_FULL_DEBUG_SYMBOLS)" ] && \ + echo && echo "ENABLE_FULL_DEBUG_SYMBOLS = $(ENABLE_FULL_DEBUG_SYMBOLS)"; \ + [ -n "$(OBJCOPY)" ] && \ + echo && echo "OBJCOPY = $(OBJCOPY)"; \ + [ -n "$(STRIP_POLICY)" ] && \ + echo && echo "STRIP_POLICY = $(STRIP_POLICY)"; \ + [ -n "$(ZIP_DEBUGINFO_FILES)" ] && \ + echo && echo "ZIP_DEBUGINFO_FILES = $(ZIP_DEBUGINFO_FILES)"; \ + [ -n "$(ZIPEXE)" ] && \ + echo && echo "ZIPEXE = $(ZIPEXE)"; \ + [ -n "$(HOTSPOT_EXTRA_SYSDEFS)" ] && \ + echo && \ + echo "HOTSPOT_EXTRA_SYSDEFS\$$(HOTSPOT_EXTRA_SYSDEFS) = $(HOTSPOT_EXTRA_SYSDEFS)" && \ + echo "SYSDEFS += \$$(HOTSPOT_EXTRA_SYSDEFS)"; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(VARIANT).make"; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(COMPILER).make"; \ + ) > $@ + +flags_vm.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + [ "$(TARGET)" = profiled ] && \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/optimized.make"; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(TARGET).make"; \ + ) > $@ + +../shared_dirs.lst: $(BUILDTREE_MAKE) $(GAMMADIR)/src/share/vm + @echo Creating directory list $@ + $(QUIETLY) if [ -d $(HS_ALT_SRC)/share/vm ]; then \ + find $(HS_ALT_SRC)/share/vm/* -prune \ + -type d \! \( $(TOPLEVEL_EXCLUDE_DIRS) \) -exec find {} \ + \( $(ALWAYS_EXCLUDE_DIRS) \) -prune -o -type d -print \; > $@; \ + fi; + $(QUIETLY) find $(HS_COMMON_SRC)/share/vm/* -prune \ + -type d \! \( $(TOPLEVEL_EXCLUDE_DIRS) \) -exec find {} \ + \( $(ALWAYS_EXCLUDE_DIRS) \) -prune -o -type d -print \; >> $@ + +Makefile: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/top.make"; \ + ) > $@ + +vm.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo include flags_vm.make; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +adlc.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +jvmti.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +trace.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +sa.make: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + echo include flags.make; \ + echo; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ + ) > $@ + +env.sh: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + [ -n "$$JAVA_HOME" ] && { echo ": \$${JAVA_HOME:=$${JAVA_HOME}}"; }; \ + { \ + echo "CLASSPATH=$${CLASSPATH:+$$CLASSPATH:}.:\$${JAVA_HOME}/jre/lib/rt.jar:\$${JAVA_HOME}/jre/lib/i18n.jar"; \ + } | sed s:$${JAVA_HOME:--------}:\$${JAVA_HOME}:g; \ + echo "HOTSPOT_BUILD_USER=\"$${LOGNAME:-$$USER} in `basename $(GAMMADIR)`\""; \ + echo "export JAVA_HOME CLASSPATH HOTSPOT_BUILD_USER"; \ + ) > $@ + +env.csh: env.sh + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + [ -n "$$JAVA_HOME" ] && \ + { echo "if (! \$$?JAVA_HOME) setenv JAVA_HOME \"$$JAVA_HOME\""; }; \ + sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=/setenv \1 /p' $?; \ + ) > $@ + +jdkpath.sh: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo "JDK=${JAVA_HOME}"; \ + ) > $@ + +.dbxrc: $(BUILDTREE_MAKE) + @echo Creating $@ ... + $(QUIETLY) ( \ + echo "echo '# Loading $(PLATFORM_DIR)/$(TARGET)/.dbxrc'"; \ + echo "if [ -f \"\$${HOTSPOT_DBXWARE}\" ]"; \ + echo "then"; \ + echo " source \"\$${HOTSPOT_DBXWARE}\""; \ + echo "elif [ -f \"\$$HOME/.dbxrc\" ]"; \ + echo "then"; \ + echo " source \"\$$HOME/.dbxrc\""; \ + echo "fi"; \ + ) > $@ + +# Skip the test for product builds (which only work when installed in a JDK), to +# avoid exiting with an error and causing make to halt. +NO_TEST_MSG = \ + echo "$@: skipping the test--this build must be tested in a JDK." + +NO_JAVA_HOME_MSG = \ + echo "JAVA_HOME must be set to run this test." + +DATA_MODE = $(DATA_MODE/$(BUILDARCH)) +JAVA_FLAG = $(JAVA_FLAG/$(DATA_MODE)) + +DATA_MODE/i486 = 32 +DATA_MODE/sparc = 32 +DATA_MODE/sparcv9 = 64 +DATA_MODE/amd64 = 64 +DATA_MODE/ia64 = 64 +DATA_MODE/ppc64 = 64 +DATA_MODE/zero = $(ARCH_DATA_MODEL) + +JAVA_FLAG/32 = -d32 +JAVA_FLAG/64 = -d64 + +WRONG_DATA_MODE_MSG = \ + echo "JAVA_HOME must point to a $(DATA_MODE)-bit OpenJDK." + +WRONG_JDK_MSG = \ + echo "JAVA_HOME must point to a HotSpot based JDK \\\(genuine Sun/Oracle or OpenJDK\\\)." + +CROSS_COMPILING_MSG = \ + echo "Cross compiling for ARCH $(CROSS_COMPILE_ARCH), skipping gamma run." + +test_gamma: $(BUILDTREE_MAKE) $(GAMMADIR)/make/test/Queens.java + @echo Creating $@ ... + $(QUIETLY) ( \ + echo "#!/bin/sh"; \ + echo ""; \ + $(BUILDTREE_COMMENT); \ + echo ""; \ + echo "# Include environment settings for gamma run"; \ + echo ""; \ + echo ". ./env.sh"; \ + echo ""; \ + echo "# Do not run gamma test for cross compiles"; \ + echo ""; \ + echo "if [ -n \"$(CROSS_COMPILE_ARCH)\" ]; then "; \ + echo " $(CROSS_COMPILING_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Make sure JAVA_HOME is set as it is required for gamma"; \ + echo ""; \ + echo "if [ -z \"\$${JAVA_HOME}\" ]; then "; \ + echo " $(NO_JAVA_HOME_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Check JAVA_HOME version to be used for the test"; \ + echo ""; \ + echo "\$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion > /dev/null 2>&1"; \ + echo "if [ \$$? -ne 0 ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# 'gamma' only runs with HotSpot based JDKs (genuine Sun/Oracle or OpenJDK)"; \ + echo ""; \ + echo "\$${JAVA_HOME}/bin/java $(JAVA_FLAG) -version 2>&1 | grep -E 'OpenJDK|HotSpot' > /dev/null"; \ + echo "if [ \$$? -ne 0 ]; then "; \ + echo " $(WRONG_JDK_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Use gamma_g if it exists"; \ + echo ""; \ + echo "GAMMA_PROG=gamma"; \ + echo "if [ -f gamma_g ]; then "; \ + echo " GAMMA_PROG=gamma_g"; \ + echo "fi"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " # Ensure architecture for gamma and JAVA_HOME is the same."; \ + echo " # NOTE: gamma assumes the OpenJDK directory layout."; \ + echo ""; \ + echo " GAMMA_ARCH=\"\`file \$${GAMMA_PROG} | awk '{print \$$NF}'\`\""; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " if [ ! -f \$${JVM_LIB} ]; then"; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/$${LIBARCH}/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " fi"; \ + echo " if [ ! -f \$${JVM_LIB} ] || [ -z \"\`file \$${JVM_LIB} | grep \$${GAMMA_ARCH}\`\" ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ + echo " fi"; \ + echo "fi"; \ + echo ""; \ + echo "# Compile Queens program for test"; \ + echo ""; \ + echo "rm -f Queens.class"; \ + echo "\$${JAVA_HOME}/bin/javac -d . $(GAMMADIR)/make/test/Queens.java"; \ + echo ""; \ + echo "# Set library path solely for gamma launcher test run"; \ + echo ""; \ + echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo "export LD_LIBRARY_PATH"; \ + echo "unset LD_LIBRARY_PATH_32"; \ + echo "unset LD_LIBRARY_PATH_64"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " DYLD_LIBRARY_PATH=.:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/native_threads:\$${JAVA_HOME}/jre/lib:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo " export DYLD_LIBRARY_PATH"; \ + echo "fi"; \ + echo ""; \ + echo "# Use the gamma launcher and JAVA_HOME to run the test"; \ + echo ""; \ + echo "./\$${GAMMA_PROG} $(TESTFLAGS) Queens < /dev/null"; \ + ) > $@ + $(QUIETLY) chmod +x $@ + +FORCE: + +.PHONY: all FORCE diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/compiler2.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/compiler2.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,32 @@ +# +# Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Sets make macros for making server version of VM + +TYPE=COMPILER2 + +VM_SUBDIR = server + +CFLAGS += -DCOMPILER2 diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/core.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/core.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,33 @@ +# +# Copyright (c) 1999, 2010, 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. +# +# + +# Sets make macros for making core version of VM + +# Select which files to use (in top.make) +TYPE=CORE + +# There is no "core" directory in JDK. Install core build in server directory. +VM_SUBDIR = server + +# Note: macros.hpp defines CORE diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/defs.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/defs.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,233 @@ +# +# Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# The common definitions for hotspot linux builds. +# Include the top level defs.make under make directory instead of this one. +# This file is included into make/defs.make. + +SLASH_JAVA ?= /java + +# Need PLATFORM (os-arch combo names) for jdk and hotspot, plus libarch name +#ARCH:=$(shell uname -m) +PATH_SEP = : +ifeq ($(LP64), 1) + ARCH_DATA_MODEL ?= 64 +else + ARCH_DATA_MODEL ?= 32 +endif + +ifeq ($(ARCH_DATA_MODEL), 64) + ARCH = ppc64 +else + ARCH = ppc +endif + +# PPC +ifeq ($(ARCH), ppc) + #ARCH_DATA_MODEL = 32 + PLATFORM = aix-ppc + VM_PLATFORM = aix_ppc + HS_ARCH = ppc +endif + +# On 32 bit aix we build server and client, on 64 bit just server. +ifeq ($(JVM_VARIANTS),) + ifeq ($(ARCH_DATA_MODEL), 32) + JVM_VARIANTS:=client,server + JVM_VARIANT_CLIENT:=true + JVM_VARIANT_SERVER:=true + else + JVM_VARIANTS:=server + JVM_VARIANT_SERVER:=true + endif +endif + +# PPC64 +ifeq ($(ARCH), ppc64) + #ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = aix-ppc64 + VM_PLATFORM = aix_ppc64 + HS_ARCH = ppc +endif + +# determine if HotSpot is being built in JDK6 or earlier version +JDK6_OR_EARLIER=0 +ifeq "$(shell expr \( '$(JDK_MAJOR_VERSION)' != '' \& '$(JDK_MINOR_VERSION)' != '' \& '$(JDK_MICRO_VERSION)' != '' \))" "1" + # if the longer variable names (newer build style) are set, then check those + ifeq "$(shell expr \( $(JDK_MAJOR_VERSION) = 1 \& $(JDK_MINOR_VERSION) \< 7 \))" "1" + JDK6_OR_EARLIER=1 + endif +else + # the longer variables aren't set so check the shorter variable names + ifeq "$(shell expr \( '$(JDK_MAJOR_VER)' = 1 \& '$(JDK_MINOR_VER)' \< 7 \))" "1" + JDK6_OR_EARLIER=1 + endif +endif + +ifeq ($(JDK6_OR_EARLIER),0) + # Full Debug Symbols is supported on JDK7 or newer. + # The Full Debug Symbols (FDS) default for BUILD_FLAVOR == product + # builds is enabled with debug info files ZIP'ed to save space. For + # BUILD_FLAVOR != product builds, FDS is always enabled, after all a + # debug build without debug info isn't very useful. + # The ZIP_DEBUGINFO_FILES option only has meaning when FDS is enabled. + # + # If you invoke a build with FULL_DEBUG_SYMBOLS=0, then FDS will be + # disabled for a BUILD_FLAVOR == product build. + # + # Note: Use of a different variable name for the FDS override option + # versus the FDS enabled check is intentional (FULL_DEBUG_SYMBOLS + # versus ENABLE_FULL_DEBUG_SYMBOLS). For auto build systems that pass + # in options via environment variables, use of distinct variables + # prevents strange behaviours. For example, in a BUILD_FLAVOR != + # product build, the FULL_DEBUG_SYMBOLS environment variable will be + # 0, but the ENABLE_FULL_DEBUG_SYMBOLS make variable will be 1. If + # the same variable name is used, then different values can be picked + # up by different parts of the build. Just to be clear, we only need + # two variable names because the incoming option value can be + # overridden in some situations, e.g., a BUILD_FLAVOR != product + # build. + + # Due to the multiple sub-make processes that occur this logic gets + # executed multiple times. We reduce the noise by at least checking that + # BUILD_FLAVOR has been set. + ifneq ($(BUILD_FLAVOR),) + ifeq ($(BUILD_FLAVOR), product) + FULL_DEBUG_SYMBOLS ?= 1 + ENABLE_FULL_DEBUG_SYMBOLS = $(FULL_DEBUG_SYMBOLS) + else + # debug variants always get Full Debug Symbols (if available) + ENABLE_FULL_DEBUG_SYMBOLS = 1 + endif + _JUNK_ := $(shell \ + echo >&2 "INFO: ENABLE_FULL_DEBUG_SYMBOLS=$(ENABLE_FULL_DEBUG_SYMBOLS)") + # since objcopy is optional, we set ZIP_DEBUGINFO_FILES later + + ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) + # Default OBJCOPY comes from GNU Binutils on Linux + ifeq ($(CROSS_COMPILE_ARCH),) + DEF_OBJCOPY=/usr/bin/objcopy + else + # Assume objcopy is part of the cross-compilation toolset + ifneq ($(ALT_COMPILER_PATH),) + DEF_OBJCOPY=$(ALT_COMPILER_PATH)/objcopy + endif + endif + OBJCOPY=$(shell test -x $(DEF_OBJCOPY) && echo $(DEF_OBJCOPY)) + ifneq ($(ALT_OBJCOPY),) + _JUNK_ := $(shell echo >&2 "INFO: ALT_OBJCOPY=$(ALT_OBJCOPY)") + OBJCOPY=$(shell test -x $(ALT_OBJCOPY) && echo $(ALT_OBJCOPY)) + endif + + ifeq ($(OBJCOPY),) + _JUNK_ := $(shell \ + echo >&2 "INFO: no objcopy cmd found so cannot create .debuginfo files. You may need to set ALT_OBJCOPY.") + ENABLE_FULL_DEBUG_SYMBOLS=0 + _JUNK_ := $(shell \ + echo >&2 "INFO: ENABLE_FULL_DEBUG_SYMBOLS=$(ENABLE_FULL_DEBUG_SYMBOLS)") + else + _JUNK_ := $(shell \ + echo >&2 "INFO: $(OBJCOPY) cmd found so will create .debuginfo files.") + + # Library stripping policies for .debuginfo configs: + # all_strip - strips everything from the library + # min_strip - strips most stuff from the library; leaves minimum symbols + # no_strip - does not strip the library at all + # + # Oracle security policy requires "all_strip". A waiver was granted on + # 2011.09.01 that permits using "min_strip" in the Java JDK and Java JRE. + # + # Currently, STRIP_POLICY is only used when Full Debug Symbols is enabled. + # + STRIP_POLICY ?= min_strip + + _JUNK_ := $(shell \ + echo >&2 "INFO: STRIP_POLICY=$(STRIP_POLICY)") + + ZIP_DEBUGINFO_FILES ?= 1 + + _JUNK_ := $(shell \ + echo >&2 "INFO: ZIP_DEBUGINFO_FILES=$(ZIP_DEBUGINFO_FILES)") + endif + endif # ENABLE_FULL_DEBUG_SYMBOLS=1 + endif # BUILD_FLAVOR +endif # JDK_6_OR_EARLIER + +# unused JDK_INCLUDE_SUBDIR=aix + +# Library suffix +LIBRARY_SUFFIX=so + +# FIXUP: The subdirectory for a debug build is NOT the same on all platforms +VM_DEBUG=jvmg + +EXPORT_LIST += $(EXPORT_DOCS_DIR)/platform/jvmti/jvmti.html + +# client and server subdirectories have symbolic links to ../libjsig.so +EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libjsig.$(LIBRARY_SUFFIX) +#ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libjsig.diz +# else +# EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libjsig.debuginfo +# endif +#endif +EXPORT_SERVER_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/server +EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client + +ifeq ($(findstring true, $(JVM_VARIANT_SERVER) $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK)), true) + EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.$(LIBRARY_SUFFIX) +# ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.diz +# else +# EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.debuginfo +# endif +# endif +endif + +ifeq ($(JVM_VARIANT_CLIENT),true) + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.$(LIBRARY_SUFFIX) +# ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.diz +# else +# EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.debuginfo +# endif +# endif +endif + +# Serviceability Binaries +# No SA Support for PPC or zero +ADD_SA_BINARIES/ppc = +ADD_SA_BINARIES/ppc64 = +ADD_SA_BINARIES/zero = + +EXPORT_LIST += $(ADD_SA_BINARIES/$(HS_ARCH)) + + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/dtrace.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/dtrace.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,27 @@ +# +# Copyright (c) 2005, 2008, 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. +# +# + +# Linux does not build jvm_db +LIBJVM_DB = + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/fastdebug.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/fastdebug.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,73 @@ +# +# Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +# Pare down optimization to -O2 if xlCV10.1 is in use. +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) $(QV10_OPT_CONSERVATIVE) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +ifeq ($(BUILDARCH), ia64) + # Bug in GCC, causes hang. -O1 will override the -O3 specified earlier + OPT_CFLAGS/callGenerator.o += -O1 + OPT_CFLAGS/ciTypeFlow.o += -O1 + OPT_CFLAGS/compile.o += -O1 + OPT_CFLAGS/concurrentMarkSweepGeneration.o += -O1 + OPT_CFLAGS/doCall.o += -O1 + OPT_CFLAGS/generateOopMap.o += -O1 + OPT_CFLAGS/generateOptoStub.o += -O1 + OPT_CFLAGS/graphKit.o += -O1 + OPT_CFLAGS/instanceKlass.o += -O1 + OPT_CFLAGS/interpreterRT_ia64.o += -O1 + OPT_CFLAGS/output.o += -O1 + OPT_CFLAGS/parse1.o += -O1 + OPT_CFLAGS/runtime.o += -O1 + OPT_CFLAGS/synchronizer.o += -O1 +endif + + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/make/aix/makefiles/mapfile-vers-debug + +# xlc 10.1 parameters for ipa linkage. +# - remove ipa linkage altogether. Does not seem to benefit performance, +# but increases code footprint. +# - this is a debug build in the end. Extra effort for ipa linkage is thus +# not justified. +LFLAGS_QIPA= + +G_SUFFIX = _g +VERSION = optimized +SYSDEFS += -DASSERT -DFASTDEBUG +PICFLAGS = DEFAULT diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/jsig.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/jsig.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,95 @@ +# +# Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Rules to build signal interposition library, used by vm.make + +# libjsig[_g].so: signal interposition library +JSIG = jsig +LIBJSIG = lib$(JSIG).so + +JSIG_G = $(JSIG)$(G_SUFFIX) +LIBJSIG_G = lib$(JSIG_G).so + +LIBJSIG_DEBUGINFO = lib$(JSIG).debuginfo +LIBJSIG_DIZ = lib$(JSIG).diz +LIBJSIG_G_DEBUGINFO = lib$(JSIG_G).debuginfo +LIBJSIG_G_DIZ = lib$(JSIG_G).diz + +JSIGSRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/vm + +DEST_JSIG = $(JDK_LIBDIR)/$(LIBJSIG) +DEST_JSIG_DEBUGINFO = $(JDK_LIBDIR)/$(LIBJSIG_DEBUGINFO) +DEST_JSIG_DIZ = $(JDK_LIBDIR)/$(LIBJSIG_DIZ) + +LIBJSIG_MAPFILE = $(MAKEFILES_DIR)/mapfile-vers-jsig + +# On Linux we really dont want a mapfile, as this library is small +# and preloaded using LD_PRELOAD, making functions private will +# cause problems with interposing. See CR: 6466665 +# LFLAGS_JSIG += $(MAPFLAG:FILENAME=$(LIBJSIG_MAPFILE)) + +LFLAGS_JSIG += -D_GNU_SOURCE -D_REENTRANT $(LDFLAGS_HASH_STYLE) + +LFLAGS_JSIG += $(BIN_UTILS) + +# DEBUG_BINARIES overrides everything, use full -g debug information +ifeq ($(DEBUG_BINARIES), true) + JSIG_DEBUG_CFLAGS = -g +endif + +$(LIBJSIG): $(JSIGSRCDIR)/jsig.c $(LIBJSIG_MAPFILE) + @echo Making signal interposition lib... + $(QUIETLY) $(CXX) $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ + $(LFLAGS_JSIG) $(JSIG_DEBUG_CFLAGS) -o $@ $< -ldl + $(QUIETLY) [ -f $(LIBJSIG_G) ] || { ln -s $@ $(LIBJSIG_G); } + +#ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) +# $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBJSIG_DEBUGINFO) +# $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBJSIG_DEBUGINFO) $@ +# ifeq ($(STRIP_POLICY),all_strip) +# $(QUIETLY) $(STRIP) $@ +# else +# ifeq ($(STRIP_POLICY),min_strip) +# $(QUIETLY) $(STRIP) -g $@ +# # implied else here is no stripping at all +# endif +# endif +# [ -f $(LIBJSIG_G_DEBUGINFO) ] || { ln -s $(LIBJSIG_DEBUGINFO) $(LIBJSIG_G_DEBUGINFO); } +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# $(ZIPEXE) -q -y $(LIBJSIG_DIZ) $(LIBJSIG_DEBUGINFO) $(LIBJSIG_G_DEBUGINFO) +# $(RM) $(LIBJSIG_DEBUGINFO) $(LIBJSIG_G_DEBUGINFO) +# [ -f $(LIBJSIG_G_DIZ) ] || { ln -s $(LIBJSIG_DIZ) $(LIBJSIG_G_DIZ); } +# endif +#endif + +install_jsig: $(LIBJSIG) + @echo "Copying $(LIBJSIG) to $(DEST_JSIG)" + $(QUIETLY) test -f $(LIBJSIG_DEBUGINFO) && \ + cp -f $(LIBJSIG_DEBUGINFO) $(DEST_JSIG_DEBUGINFO) + $(QUIETLY) test -f $(LIBJSIG_DIZ) && \ + cp -f $(LIBJSIG_DIZ) $(DEST_JSIG_DIZ) + $(QUIETLY) cp -f $(LIBJSIG) $(DEST_JSIG) && echo "Done" + +.PHONY: install_jsig diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/jvmg.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/jvmg.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,42 @@ +# +# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Sets make macros for making debug version of VM + +# Compiler specific DEBUG_CFLAGS are passed in from gcc.make, sparcWorks.make +DEBUG_CFLAGS/DEFAULT= $(DEBUG_CFLAGS) +DEBUG_CFLAGS/BYFILE = $(DEBUG_CFLAGS/$@)$(DEBUG_CFLAGS/DEFAULT$(DEBUG_CFLAGS/$@)) +CFLAGS += $(DEBUG_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/make/aix/makefiles/mapfile-vers-debug + +G_SUFFIX = _g +VERSION = debug +SYSDEFS += -DASSERT -DDEBUG +PICFLAGS = DEFAULT diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/jvmti.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/jvmti.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,118 @@ +# +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# This makefile (jvmti.make) is included from the jvmti.make in the +# build directories. +# +# It knows how to build and run the tools to generate jvmti. + +include $(GAMMADIR)/make/aix/makefiles/rules.make + +# ######################################################################### + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated +JvmtiOutDir = $(GENERATED)/jvmtifiles + +JvmtiSrcDir = $(GAMMADIR)/src/share/vm/prims +InterpreterSrcDir = $(GAMMADIR)/src/share/vm/interpreter + +# set VPATH so make knows where to look for source files +Src_Dirs_V += $(JvmtiSrcDir) +VPATH += $(Src_Dirs_V:%=%:) + +JvmtiGeneratedNames = \ + jvmtiEnv.hpp \ + jvmtiEnter.cpp \ + jvmtiEnterTrace.cpp \ + jvmtiEnvRecommended.cpp \ + bytecodeInterpreterWithChecks.cpp \ + jvmti.h \ + +JvmtiEnvFillSource = $(JvmtiSrcDir)/jvmtiEnvFill.java +JvmtiEnvFillClass = $(JvmtiOutDir)/jvmtiEnvFill.class + +JvmtiGenSource = $(JvmtiSrcDir)/jvmtiGen.java +JvmtiGenClass = $(JvmtiOutDir)/jvmtiGen.class + +JvmtiGeneratedFiles = $(JvmtiGeneratedNames:%=$(JvmtiOutDir)/%) + +XSLT = $(QUIETLY) $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen + +.PHONY: all jvmtidocs clean cleanall + +# ######################################################################### + +all: $(JvmtiGeneratedFiles) + +both = $(JvmtiGenClass) $(JvmtiSrcDir)/jvmti.xml $(JvmtiSrcDir)/jvmtiLib.xsl + +$(JvmtiGenClass): $(JvmtiGenSource) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JvmtiOutDir) $(JvmtiGenSource) + +$(JvmtiEnvFillClass): $(JvmtiEnvFillSource) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + +$(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnter.cpp -PARAM interface jvmti + +$(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp: $(JvmtiGenClass) $(InterpreterSrcDir)/bytecodeInterpreter.cpp $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl + @echo Generating $@ + $(XSLT) -IN $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xml -XSL $(InterpreterSrcDir)/bytecodeInterpreterWithChecks.xsl -OUT $(JvmtiOutDir)/bytecodeInterpreterWithChecks.cpp + +$(JvmtiOutDir)/jvmtiEnterTrace.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnter.xsl -OUT $(JvmtiOutDir)/jvmtiEnterTrace.cpp -PARAM interface jvmti -PARAM trace Trace + +$(JvmtiOutDir)/jvmtiEnvRecommended.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnv.xsl $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiEnvFillClass) + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiEnv.xsl -OUT $(JvmtiOutDir)/jvmtiEnvStub.cpp + $(QUIETLY) $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiEnvFill $(JvmtiSrcDir)/jvmtiEnv.cpp $(JvmtiOutDir)/jvmtiEnvStub.cpp $(JvmtiOutDir)/jvmtiEnvRecommended.cpp + +$(JvmtiOutDir)/jvmtiEnv.hpp: $(both) $(JvmtiSrcDir)/jvmtiHpp.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiHpp.xsl -OUT $(JvmtiOutDir)/jvmtiEnv.hpp + +$(JvmtiOutDir)/jvmti.h: $(both) $(JvmtiSrcDir)/jvmtiH.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmtiH.xsl -OUT $(JvmtiOutDir)/jvmti.h + +jvmtidocs: $(JvmtiOutDir)/jvmti.html + +$(JvmtiOutDir)/jvmti.html: $(both) $(JvmtiSrcDir)/jvmti.xsl + @echo Generating $@ + $(XSLT) -IN $(JvmtiSrcDir)/jvmti.xml -XSL $(JvmtiSrcDir)/jvmti.xsl -OUT $(JvmtiOutDir)/jvmti.html + +# ######################################################################### + +clean : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +cleanall : + rm $(JvmtiGenClass) $(JvmtiEnvFillClass) $(JvmtiGeneratedFiles) + +# ######################################################################### + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/launcher.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/launcher.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,97 @@ +# +# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Rules to build gamma launcher, used by vm.make + + +LAUNCHER_SCRIPT = hotspot +LAUNCHER = gamma + +LAUNCHERDIR := $(GAMMADIR)/src/os/posix/launcher +LAUNCHERDIR_SHARE := $(GAMMADIR)/src/share/tools/launcher +LAUNCHERFLAGS := $(ARCHFLAG) \ + -I$(LAUNCHERDIR) -I$(GAMMADIR)/src/share/vm/prims \ + -I$(LAUNCHERDIR_SHARE) \ + -DFULL_VERSION=\"$(HOTSPOT_RELEASE_VERSION)\" \ + -DJDK_MAJOR_VERSION=\"$(JDK_MAJOR_VERSION)\" \ + -DJDK_MINOR_VERSION=\"$(JDK_MINOR_VERSION)\" \ + -DARCH=\"$(LIBARCH)\" \ + -DGAMMA \ + -DLAUNCHER_TYPE=\"gamma\" \ + -DLINK_INTO_$(LINK_INTO) \ + $(TARGET_DEFINES) + +ifeq ($(LINK_INTO),AOUT) + LAUNCHER.o = launcher.o $(JVM_OBJ_FILES) + LAUNCHER_MAPFILE = mapfile_reorder + LFLAGS_LAUNCHER$(LDNOMAP) += $(MAPFLAG:FILENAME=$(LAUNCHER_MAPFILE)) + LFLAGS_LAUNCHER += $(SONAMEFLAG:SONAME=$(LIBJVM)) $(STATIC_LIBGCC) + LIBS_LAUNCHER += $(STATIC_STDCXX) $(LIBS) +else + LAUNCHER.o = launcher.o + LFLAGS_LAUNCHER += -L `pwd` + LIBS_LAUNCHER += -l$(JVM) $(LIBS) +endif + +# Enable runtime linking and strip link time paths from the loader section. +LFLAGS_LAUNCHER += -brtl -bnolibpath + +LINK_LAUNCHER = $(LINK.CC) + +LINK_LAUNCHER/PRE_HOOK = $(LINK_LIB.CXX/PRE_HOOK) +LINK_LAUNCHER/POST_HOOK = $(LINK_LIB.CXX/POST_HOOK) + +LAUNCHER_OUT = launcher + +SUFFIXES += .d + +SOURCES := $(shell find $(LAUNCHERDIR) -name "*.c") +SOURCES_SHARE := $(shell find $(LAUNCHERDIR_SHARE) -name "*.c") + +OBJS := $(patsubst $(LAUNCHERDIR)/%.c,$(LAUNCHER_OUT)/%.o,$(SOURCES)) $(patsubst $(LAUNCHERDIR_SHARE)/%.c,$(LAUNCHER_OUT)/%.o,$(SOURCES_SHARE)) + +DEPFILES := $(patsubst %.o,%.d,$(OBJS)) +-include $(DEPFILES) + +$(LAUNCHER_OUT)/%.o: $(LAUNCHERDIR_SHARE)/%.c + $(QUIETLY) [ -d $(LAUNCHER_OUT) ] || { mkdir -p $(LAUNCHER_OUT); } + $(QUIETLY) $(CC) -g -o $@ -c $< $(LAUNCHERFLAGS) $(CXXFLAGS) + +$(LAUNCHER_OUT)/%.o: $(LAUNCHERDIR)/%.c + $(QUIETLY) [ -d $(LAUNCHER_OUT) ] || { mkdir -p $(LAUNCHER_OUT); } + $(QUIETLY) $(CC) -g -o $@ -c $< $(LAUNCHERFLAGS) $(CXXFLAGS) + +$(LAUNCHER): $(OBJS) $(LIBJVM) $(LAUNCHER_MAPFILE) + $(QUIETLY) echo Linking launcher... + $(QUIETLY) $(LINK_LAUNCHER/PRE_HOOK) + $(QUIETLY) $(LINK_LAUNCHER) $(LFLAGS_LAUNCHER) -o $@ $(OBJS) $(LIBS_LAUNCHER) + $(QUIETLY) $(LINK_LAUNCHER/POST_HOOK) + +$(LAUNCHER): $(LAUNCHER_SCRIPT) + +$(LAUNCHER_SCRIPT): $(LAUNCHERDIR)/launcher.script + $(QUIETLY) sed -e 's/@@LIBARCH@@/$(LIBARCH)/g' $< > $@ + $(QUIETLY) chmod +x $@ + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/mapfile-vers-debug --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/mapfile-vers-debug Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,270 @@ +# +# Copyright (c) 2002, 2011, 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. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + + # JVM + JVM_Accept; + JVM_ActiveProcessorCount; + JVM_AllocateNewArray; + JVM_AllocateNewObject; + JVM_ArrayCopy; + JVM_AssertionStatusDirectives; + JVM_Available; + JVM_Bind; + JVM_ClassDepth; + JVM_ClassLoaderDepth; + JVM_Clone; + JVM_Close; + JVM_CX8Field; + JVM_CompileClass; + JVM_CompileClasses; + JVM_CompilerCommand; + JVM_Connect; + JVM_ConstantPoolGetClassAt; + JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetDoubleAt; + JVM_ConstantPoolGetFieldAt; + JVM_ConstantPoolGetFieldAtIfLoaded; + JVM_ConstantPoolGetFloatAt; + JVM_ConstantPoolGetIntAt; + JVM_ConstantPoolGetLongAt; + JVM_ConstantPoolGetMethodAt; + JVM_ConstantPoolGetMethodAtIfLoaded; + JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetSize; + JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetUTF8At; + JVM_CountStackFrames; + JVM_CurrentClassLoader; + JVM_CurrentLoadedClass; + JVM_CurrentThread; + JVM_CurrentTimeMillis; + JVM_DefineClass; + JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; + JVM_DesiredAssertionStatus; + JVM_DisableCompiler; + JVM_DoPrivileged; + JVM_DTraceGetVersion; + JVM_DTraceActivate; + JVM_DTraceIsProbeEnabled; + JVM_DTraceIsSupported; + JVM_DTraceDispose; + JVM_DumpAllStacks; + JVM_DumpThreads; + JVM_EnableCompiler; + JVM_Exit; + JVM_FillInStackTrace; + JVM_FindClassFromClass; + JVM_FindClassFromClassLoader; + JVM_FindClassFromBootLoader; + JVM_FindLibraryEntry; + JVM_FindLoadedClass; + JVM_FindPrimitiveClass; + JVM_FindSignal; + JVM_FreeMemory; + JVM_GC; + JVM_GetAllThreads; + JVM_GetArrayElement; + JVM_GetArrayLength; + JVM_GetCPClassNameUTF; + JVM_GetCPFieldClassNameUTF; + JVM_GetCPFieldModifiers; + JVM_GetCPFieldNameUTF; + JVM_GetCPFieldSignatureUTF; + JVM_GetCPMethodClassNameUTF; + JVM_GetCPMethodModifiers; + JVM_GetCPMethodNameUTF; + JVM_GetCPMethodSignatureUTF; + JVM_GetCallerClass; + JVM_GetClassAccessFlags; + JVM_GetClassAnnotations; + JVM_GetClassCPEntriesCount; + JVM_GetClassCPTypes; + JVM_GetClassConstantPool; + JVM_GetClassContext; + JVM_GetClassDeclaredConstructors; + JVM_GetClassDeclaredFields; + JVM_GetClassDeclaredMethods; + JVM_GetClassFieldsCount; + JVM_GetClassInterfaces; + JVM_GetClassLoader; + JVM_GetClassMethodsCount; + JVM_GetClassModifiers; + JVM_GetClassName; + JVM_GetClassNameUTF; + JVM_GetClassSignature; + JVM_GetClassSigners; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetHostName; + JVM_GetInheritedAccessControlContext; + JVM_GetInterfaceVersion; + JVM_GetLastErrorString; + JVM_GetManagement; + JVM_GetMethodAnnotations; + JVM_GetMethodDefaultAnnotationValue; + JVM_GetMethodIxArgsSize; + JVM_GetMethodIxByteCode; + JVM_GetMethodIxByteCodeLength; + JVM_GetMethodIxExceptionIndexes; + JVM_GetMethodIxExceptionTableEntry; + JVM_GetMethodIxExceptionTableLength; + JVM_GetMethodIxExceptionsCount; + JVM_GetMethodIxLocalsCount; + JVM_GetMethodIxMaxStack; + JVM_GetMethodIxModifiers; + JVM_GetMethodIxNameUTF; + JVM_GetMethodIxSignatureUTF; + JVM_GetMethodParameterAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetThreadStateNames; + JVM_GetThreadStateValues; + JVM_GetVersionInfo; + JVM_Halt; + JVM_HoldsLock; + JVM_IHashCode; + JVM_InitAgentProperties; + JVM_InitProperties; + JVM_InitializeCompiler; + JVM_InitializeSocketLibrary; + JVM_InternString; + JVM_Interrupt; + JVM_InvokeMethod; + JVM_IsArrayClass; + JVM_IsConstructorIx; + JVM_IsInterface; + JVM_IsInterrupted; + JVM_IsNaN; + JVM_IsPrimitiveClass; + JVM_IsSameClassPackage; + JVM_IsSilentCompiler; + JVM_IsSupportedJNIVersion; + JVM_IsThreadAlive; + JVM_LatestUserDefinedLoader; + JVM_Listen; + JVM_LoadClass0; + JVM_LoadLibrary; + JVM_Lseek; + JVM_MaxObjectInspectionAge; + JVM_MaxMemory; + JVM_MonitorNotify; + JVM_MonitorNotifyAll; + JVM_MonitorWait; + JVM_NanoTime; + JVM_NativePath; + JVM_NewArray; + JVM_NewInstanceFromConstructor; + JVM_NewMultiArray; + JVM_OnExit; + JVM_Open; + JVM_PrintStackTrace; + JVM_RaiseSignal; + JVM_RawMonitorCreate; + JVM_RawMonitorDestroy; + JVM_RawMonitorEnter; + JVM_RawMonitorExit; + JVM_Read; + JVM_Recv; + JVM_RecvFrom; + JVM_RegisterSignal; + JVM_ReleaseUTF; + JVM_ResolveClass; + JVM_ResumeThread; + JVM_Send; + JVM_SendTo; + JVM_SetArrayElement; + JVM_SetClassSigners; + JVM_SetLength; + JVM_SetNativeThreadName; + JVM_SetPrimitiveArrayElement; + JVM_SetProtectionDomain; + JVM_SetSockOpt; + JVM_SetThreadPriority; + JVM_Sleep; + JVM_Socket; + JVM_SocketAvailable; + JVM_SocketClose; + JVM_SocketShutdown; + JVM_StartThread; + JVM_StopThread; + JVM_SuspendThread; + JVM_SupportsCX8; + JVM_Sync; + JVM_Timeout; + JVM_TotalMemory; + JVM_TraceInstructions; + JVM_TraceMethodCalls; + JVM_UnloadLibrary; + JVM_Write; + JVM_Yield; + JVM_handle_linux_signal; + + # debug JVM + JVM_AccessVMBooleanFlag; + JVM_AccessVMIntFlag; + JVM_VMBreakPoint; + + # miscellaneous functions + jio_fprintf; + jio_printf; + jio_snprintf; + jio_vfprintf; + jio_vsnprintf; + fork1; + numa_warn; + numa_error; + + # Needed because there is no JVM interface for this. + sysThreadAvailableStackWithSlack; + + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + + # INSERT VTABLE SYMBOLS HERE + + local: + *; +}; + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/mapfile-vers-jsig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/mapfile-vers-jsig Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2005, 2008, 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. +# +# + +# Define library interface. + +SUNWprivate_1.1 { + global: + JVM_begin_signal_setting; + JVM_end_signal_setting; + JVM_get_libjsig_version; + JVM_get_signal_action; + sigaction; + signal; + sigset; + local: + *; +}; diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/mapfile-vers-product --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/mapfile-vers-product Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,265 @@ +# +# Copyright (c) 2002, 2011, 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. +# +# + +# Define public interface. + +SUNWprivate_1.1 { + global: + # JNI + JNI_CreateJavaVM; + JNI_GetCreatedJavaVMs; + JNI_GetDefaultJavaVMInitArgs; + + # JVM + JVM_Accept; + JVM_ActiveProcessorCount; + JVM_AllocateNewArray; + JVM_AllocateNewObject; + JVM_ArrayCopy; + JVM_AssertionStatusDirectives; + JVM_Available; + JVM_Bind; + JVM_ClassDepth; + JVM_ClassLoaderDepth; + JVM_Clone; + JVM_Close; + JVM_CX8Field; + JVM_CompileClass; + JVM_CompileClasses; + JVM_CompilerCommand; + JVM_Connect; + JVM_ConstantPoolGetClassAt; + JVM_ConstantPoolGetClassAtIfLoaded; + JVM_ConstantPoolGetDoubleAt; + JVM_ConstantPoolGetFieldAt; + JVM_ConstantPoolGetFieldAtIfLoaded; + JVM_ConstantPoolGetFloatAt; + JVM_ConstantPoolGetIntAt; + JVM_ConstantPoolGetLongAt; + JVM_ConstantPoolGetMethodAt; + JVM_ConstantPoolGetMethodAtIfLoaded; + JVM_ConstantPoolGetMemberRefInfoAt; + JVM_ConstantPoolGetSize; + JVM_ConstantPoolGetStringAt; + JVM_ConstantPoolGetUTF8At; + JVM_CountStackFrames; + JVM_CurrentClassLoader; + JVM_CurrentLoadedClass; + JVM_CurrentThread; + JVM_CurrentTimeMillis; + JVM_DefineClass; + JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; + JVM_DesiredAssertionStatus; + JVM_DisableCompiler; + JVM_DoPrivileged; + JVM_DTraceGetVersion; + JVM_DTraceActivate; + JVM_DTraceIsProbeEnabled; + JVM_DTraceIsSupported; + JVM_DTraceDispose; + JVM_DumpAllStacks; + JVM_DumpThreads; + JVM_EnableCompiler; + JVM_Exit; + JVM_FillInStackTrace; + JVM_FindClassFromClass; + JVM_FindClassFromClassLoader; + JVM_FindClassFromBootLoader; + JVM_FindLibraryEntry; + JVM_FindLoadedClass; + JVM_FindPrimitiveClass; + JVM_FindSignal; + JVM_FreeMemory; + JVM_GC; + JVM_GetAllThreads; + JVM_GetArrayElement; + JVM_GetArrayLength; + JVM_GetCPClassNameUTF; + JVM_GetCPFieldClassNameUTF; + JVM_GetCPFieldModifiers; + JVM_GetCPFieldNameUTF; + JVM_GetCPFieldSignatureUTF; + JVM_GetCPMethodClassNameUTF; + JVM_GetCPMethodModifiers; + JVM_GetCPMethodNameUTF; + JVM_GetCPMethodSignatureUTF; + JVM_GetCallerClass; + JVM_GetClassAccessFlags; + JVM_GetClassAnnotations; + JVM_GetClassCPEntriesCount; + JVM_GetClassCPTypes; + JVM_GetClassConstantPool; + JVM_GetClassContext; + JVM_GetClassDeclaredConstructors; + JVM_GetClassDeclaredFields; + JVM_GetClassDeclaredMethods; + JVM_GetClassFieldsCount; + JVM_GetClassInterfaces; + JVM_GetClassLoader; + JVM_GetClassMethodsCount; + JVM_GetClassModifiers; + JVM_GetClassName; + JVM_GetClassNameUTF; + JVM_GetClassSignature; + JVM_GetClassSigners; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetHostName; + JVM_GetInheritedAccessControlContext; + JVM_GetInterfaceVersion; + JVM_GetLastErrorString; + JVM_GetManagement; + JVM_GetMethodAnnotations; + JVM_GetMethodDefaultAnnotationValue; + JVM_GetMethodIxArgsSize; + JVM_GetMethodIxByteCode; + JVM_GetMethodIxByteCodeLength; + JVM_GetMethodIxExceptionIndexes; + JVM_GetMethodIxExceptionTableEntry; + JVM_GetMethodIxExceptionTableLength; + JVM_GetMethodIxExceptionsCount; + JVM_GetMethodIxLocalsCount; + JVM_GetMethodIxMaxStack; + JVM_GetMethodIxModifiers; + JVM_GetMethodIxNameUTF; + JVM_GetMethodIxSignatureUTF; + JVM_GetMethodParameterAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetThreadStateNames; + JVM_GetThreadStateValues; + JVM_GetVersionInfo; + JVM_Halt; + JVM_HoldsLock; + JVM_IHashCode; + JVM_InitAgentProperties; + JVM_InitProperties; + JVM_InitializeCompiler; + JVM_InitializeSocketLibrary; + JVM_InternString; + JVM_Interrupt; + JVM_InvokeMethod; + JVM_IsArrayClass; + JVM_IsConstructorIx; + JVM_IsInterface; + JVM_IsInterrupted; + JVM_IsNaN; + JVM_IsPrimitiveClass; + JVM_IsSameClassPackage; + JVM_IsSilentCompiler; + JVM_IsSupportedJNIVersion; + JVM_IsThreadAlive; + JVM_LatestUserDefinedLoader; + JVM_Listen; + JVM_LoadClass0; + JVM_LoadLibrary; + JVM_Lseek; + JVM_MaxObjectInspectionAge; + JVM_MaxMemory; + JVM_MonitorNotify; + JVM_MonitorNotifyAll; + JVM_MonitorWait; + JVM_NanoTime; + JVM_NativePath; + JVM_NewArray; + JVM_NewInstanceFromConstructor; + JVM_NewMultiArray; + JVM_OnExit; + JVM_Open; + JVM_PrintStackTrace; + JVM_RaiseSignal; + JVM_RawMonitorCreate; + JVM_RawMonitorDestroy; + JVM_RawMonitorEnter; + JVM_RawMonitorExit; + JVM_Read; + JVM_Recv; + JVM_RecvFrom; + JVM_RegisterSignal; + JVM_ReleaseUTF; + JVM_ResolveClass; + JVM_ResumeThread; + JVM_Send; + JVM_SendTo; + JVM_SetArrayElement; + JVM_SetClassSigners; + JVM_SetLength; + JVM_SetNativeThreadName; + JVM_SetPrimitiveArrayElement; + JVM_SetProtectionDomain; + JVM_SetSockOpt; + JVM_SetThreadPriority; + JVM_Sleep; + JVM_Socket; + JVM_SocketAvailable; + JVM_SocketClose; + JVM_SocketShutdown; + JVM_StartThread; + JVM_StopThread; + JVM_SuspendThread; + JVM_SupportsCX8; + JVM_Sync; + JVM_Timeout; + JVM_TotalMemory; + JVM_TraceInstructions; + JVM_TraceMethodCalls; + JVM_UnloadLibrary; + JVM_Write; + JVM_Yield; + JVM_handle_linux_signal; + + # miscellaneous functions + jio_fprintf; + jio_printf; + jio_snprintf; + jio_vfprintf; + jio_vsnprintf; + fork1; + numa_warn; + numa_error; + + # Needed because there is no JVM interface for this. + sysThreadAvailableStackWithSlack; + + # This is for Forte Analyzer profiling support. + AsyncGetCallTrace; + + # INSERT VTABLE SYMBOLS HERE + + local: + *; +}; + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/ppc64.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/ppc64.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,108 @@ +# +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Produce 64 bits object files. +CFLAGS += -q64 + +# Make c code know it is on a 64 bit platform. +# xlc: implicitly set by -q64 + +# Balanced tuning for recent versions of the POWER architecture (if supported by xlc). +QTUNE=$(if $(CPP_SUPPORTS_BALANCED_TUNING),balanced,pwr5) + +# Try to speed up the interpreter: use ppc64 instructions and inline +# glue code for external functions. +OPT_CFLAGS += -qarch=ppc64 -qtune=$(QTUNE) -qinlglue + +# We need variable length arrays +CFLAGS += -qlanglvl=c99vla +# Just to check for unwanted macro redefinitions +CFLAGS += -qlanglvl=noredefmac + +# Surpress those "implicit private" warnings xlc gives. +# - The omitted keyword "private" is assumed for base class "...". +CFLAGS += -qsuppress=1540-0198 + +# Surpress the following numerous warning: +# - 1540-1090 (I) The destructor of "..." might not be called. +# - 1500-010: (W) WARNING in ...: Infinite loop. Program may not stop. +# There are several infinite loops in the vm, suppress. +CFLAGS += -qsuppress=1540-1090 -qsuppress=1500-010 + +# Turn off floating-point optimizations that may alter program semantics +OPT_CFLAGS += -qstrict + +# Disable aggressive optimizations for functions in sharedRuntimeTrig.cpp +# and sharedRuntimeTrans.cpp on ppc64. +# -qstrict turns off the following optimizations: +# * Performing code motion and scheduling on computations such as loads +# and floating-point computations that may trigger an exception. +# * Relaxing conformance to IEEE rules. +# * Reassociating floating-point expressions. +# When using '-qstrict' there still remains one problem +# in javasoft.sqe.tests.api.java.lang.Math.sin5Tests when run in compile-all +# mode, so don't optimize sharedRuntimeTrig.cpp at all. +OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) +OPT_CFLAGS/sharedRuntimeTrans.o = $(OPT_CFLAGS/NOOPT) + +# Xlc V8 generates bad code for frame.cpp if -qipa is not present. +QIPA_COMPILE = -qipa + +# Xlc 10.1 parameters for aggressive optimization: +# - qhot=level=1: Most aggressive loop optimizations. +# - qignerrno: Assume errno is not modified by system calls. +# - qinline: Inline method calls. No suboptions for c++ compiles. +# - qxflag=ASMMIDCOALFIX: Activate fix for -O3 problem in interpreter loop. +# - qxflag=asmfastsync: Activate fix for performance problem with inline assembler with memory clobber. +QV10_OPT=$(if $(CPP_IS_V10),-qxflag=ASMMIDCOALFIX -qxflag=asmfastsync) +QV10_OPT_AGGRESSIVE=$(if $(CPP_IS_V10),-qhot=level=1 -qignerrno -qinline) +QV10_OPT_CONSERVATIVE=$(if $(CPP_IS_V10),-qhot=level=1 -qignerrno -qinline) + +# Disallow inlining for synchronizer.cpp, but perform O3 optimizations. +OPT_CFLAGS/synchronizer.o = $(OPT_CFLAGS) -qnoinline + +# Set all the xlC V10.1 options here. +OPT_CFLAGS += $(QIPA_COMPILE) $(QV10_OPT) $(QV10_OPT_AGGRESSIVE) + +export OBJECT_MODE=64 + +# PPC usees safefetch stubs. +CFLAGS += -DSAFEFETCH_STUBS + +# Let linker produce 64 bit lib. +#LFLAGS_VM += -m64 + +# Let linker find external 64 bit libs. +#LFLAGS_VM += -L/lib64 + +# Specify lib format. +#LFLAGS_VM += -Wl,-melf64ppc + +# Also build launcher as 64 bit executable. +LAUNCHERFLAGS += -q64 +#LAUNCHERFLAGS += -D_LP64=1 implicitly set by -q64 +#AOUT_FLAGS += -q64 +#AOUT_FLAGS += -L/lib64 +#AOUT_FLAGS += -Wl,-melf64ppc diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/product.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/product.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,59 @@ +# +# Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Sets make macros for making optimized version of Gamma VM +# (This is the "product", not the "release" version.) + +# Compiler specific OPT_CFLAGS are passed in from gcc.make, sparcWorks.make +OPT_CFLAGS/DEFAULT= $(OPT_CFLAGS) +OPT_CFLAGS/BYFILE = $(OPT_CFLAGS/$@)$(OPT_CFLAGS/DEFAULT$(OPT_CFLAGS/$@)) + +# (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) + +# If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings +CFLAGS$(HOTSPARC_GENERIC) += $(OPT_CFLAGS/BYFILE) + +# Set the environment variable HOTSPARC_GENERIC to "true" +# to inhibit the effect of the previous line on CFLAGS. + +# Linker mapfile +MAPFILE = $(GAMMADIR)/make/aix/makefiles/mapfile-vers-product + +# Remove ipa linkage altogether. Does not seem to benfit performance, but increases code footprint. +LFLAGS_QIPA= + +G_SUFFIX = +SYSDEFS += -DPRODUCT +VERSION = optimized + +# use -g to strip library as -x will discard its symbol table; -x is fine for +# executables. +# Note: these macros are not used in .debuginfo configs +STRIP_LIBJVM = $(STRIP) -g $@ || exit 1; +STRIP_AOUT = $(STRIP) -x $@ || exit 1; + +# If we can create .debuginfo files, then the VM is stripped in vm.make +# and this macro is not used. +# LINK_LIB.CXX/POST_HOOK += $(STRIP_$(LINK_INTO)) diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/rules.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/rules.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,203 @@ +# +# Copyright (c) 2003, 2011, 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. +# +# + +# Common rules/macros for the vm, adlc. + +# Tell make that .cpp is important +.SUFFIXES: .cpp $(SUFFIXES) + +DEMANGLER = c++filt +DEMANGLE = $(DEMANGLER) < $@ > .$@ && mv -f .$@ $@ + +# $(CC) is the c compiler (cc/gcc), $(CXX) is the c++ compiler (CC/g++). +CC_COMPILE = $(CC) $(CXXFLAGS) $(CFLAGS) +CXX_COMPILE = $(CXX) $(CXXFLAGS) $(CFLAGS) + +AS.S = $(AS) $(ASFLAGS) + +COMPILE.CC = $(CC_COMPILE) -c +GENASM.CC = $(CC_COMPILE) -S +LINK.CC = $(CC) $(LFLAGS) $(AOUT_FLAGS) $(PROF_AOUT_FLAGS) +LINK_LIB.CC = $(CC) $(LFLAGS) $(SHARED_FLAG) +PREPROCESS.CC = $(CC_COMPILE) -E + +COMPILE.CXX = $(CXX_COMPILE) -c +GENASM.CXX = $(CXX_COMPILE) -S +LINK.CXX = $(CXX) $(LFLAGS) $(AOUT_FLAGS) $(PROF_AOUT_FLAGS) +LINK_NOPROF.CXX = $(CXX) $(LFLAGS) $(AOUT_FLAGS) +LINK_LIB.CXX = $(CXX) $(LFLAGS) $(SHARED_FLAG) +PREPROCESS.CXX = $(CXX_COMPILE) -E + +# cross compiling the jvm with c2 requires host compilers to build +# adlc tool + +HOST.CXX_COMPILE = $(HOSTCXX) $(CXXFLAGS) $(CFLAGS) +HOST.COMPILE.CXX = $(HOST.CXX_COMPILE) -c +HOST.LINK_NOPROF.CXX = $(HOSTCXX) $(LFLAGS) $(AOUT_FLAGS) + + +# Effect of REMOVE_TARGET is to delete out-of-date files during "gnumake -k". +REMOVE_TARGET = rm -f $@ + +# Note use of ALT_BOOTDIR to explicitly specify location of java and +# javac; this is the same environment variable used in the J2SE build +# process for overriding the default spec, which is BOOTDIR. +# Note also that we fall back to using JAVA_HOME if neither of these is +# specified. + +ifdef ALT_BOOTDIR + +RUN.JAVA = $(ALT_BOOTDIR)/bin/java +RUN.JAVAP = $(ALT_BOOTDIR)/bin/javap +RUN.JAVAH = $(ALT_BOOTDIR)/bin/javah +RUN.JAR = $(ALT_BOOTDIR)/bin/jar +COMPILE.JAVAC = $(ALT_BOOTDIR)/bin/javac +COMPILE.RMIC = $(ALT_BOOTDIR)/bin/rmic +BOOT_JAVA_HOME = $(ALT_BOOTDIR) + +else + +ifdef BOOTDIR + +RUN.JAVA = $(BOOTDIR)/bin/java +RUN.JAVAP = $(BOOTDIR)/bin/javap +RUN.JAVAH = $(BOOTDIR)/bin/javah +RUN.JAR = $(BOOTDIR)/bin/jar +COMPILE.JAVAC = $(BOOTDIR)/bin/javac +COMPILE.RMIC = $(BOOTDIR)/bin/rmic +BOOT_JAVA_HOME = $(BOOTDIR) + +else + +ifdef JAVA_HOME + +RUN.JAVA = $(JAVA_HOME)/bin/java +RUN.JAVAP = $(JAVA_HOME)/bin/javap +RUN.JAVAH = $(JAVA_HOME)/bin/javah +RUN.JAR = $(JAVA_HOME)/bin/jar +COMPILE.JAVAC = $(JAVA_HOME)/bin/javac +COMPILE.RMIC = $(JAVA_HOME)/bin/rmic +BOOT_JAVA_HOME = $(JAVA_HOME) + +else + +# take from the PATH, if ALT_BOOTDIR, BOOTDIR and JAVA_HOME are not defined +# note that this is to support hotspot build without SA. To build +# SA along with hotspot, you need to define ALT_BOOTDIR, BOOTDIR or JAVA_HOME + +RUN.JAVA = java +RUN.JAVAP = javap +RUN.JAVAH = javah +RUN.JAR = jar +COMPILE.JAVAC = javac +COMPILE.RMIC = rmic + +endif +endif +endif + +COMPILE.JAVAC += $(BOOTSTRAP_JAVAC_FLAGS) + +SUM = /usr/bin/sum + +# 'gmake MAKE_VERBOSE=y' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ +RUN.JAR$(MAKE_VERBOSE) += >/dev/null + +# Settings for javac +BOOT_SOURCE_LANGUAGE_VERSION = 6 +BOOT_TARGET_CLASS_VERSION = 6 +JAVAC_FLAGS = -g -encoding ascii +BOOTSTRAP_JAVAC_FLAGS = $(JAVAC_FLAGS) -source $(BOOT_SOURCE_LANGUAGE_VERSION) -target $(BOOT_TARGET_CLASS_VERSION) + +# With parallel makes, print a message at the end of compilation. +ifeq ($(findstring j,$(MFLAGS)),j) +COMPILE_DONE = && { echo Done with $<; } +endif + +# Include $(NONPIC_OBJ_FILES) definition +ifndef LP64 +include $(GAMMADIR)/make/pic.make +endif + +include $(GAMMADIR)/make/altsrc.make + +# The non-PIC object files are only generated for 32 bit platforms. +ifdef LP64 +%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(COMPILE.CXX) $(DEPFLAGS) -o $@ $< $(COMPILE_DONE) +else +%.o: %.cpp + @echo Compiling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(if $(findstring $@, $(NONPIC_OBJ_FILES)), \ + $(subst $(VM_PICFLAG), ,$(COMPILE.CXX)) $(DEPFLAGS) -o $@ $< $(COMPILE_DONE), \ + $(COMPILE.CXX) $(DEPFLAGS) -o $@ $< $(COMPILE_DONE)) +endif + +%.o: %.s + @echo Assembling $< + $(QUIETLY) $(REMOVE_TARGET) + $(QUIETLY) $(AS.S) $(DEPFLAGS) -o $@ $< $(COMPILE_DONE) + +%.s: %.cpp + @echo Generating assembly for $< + $(QUIETLY) $(GENASM.CXX) -o $@ $< + $(QUIETLY) $(DEMANGLE) $(COMPILE_DONE) + +# Intermediate files (for debugging macros) +%.i: %.cpp + @echo Preprocessing $< to $@ + $(QUIETLY) $(PREPROCESS.CXX) $< > $@ $(COMPILE_DONE) + +# Override gnumake built-in rules which do sccs get operations badly. +# (They put the checked out code in the current directory, not in the +# directory of the original file.) Since this is a symptom of a teamware +# failure, and since not all problems can be detected by gnumake due +# to incomplete dependency checking... just complain and stop. +%:: s.% + @echo "=========================================================" + @echo File $@ + @echo is out of date with respect to its SCCS file. + @echo This file may be from an unresolved Teamware conflict. + @echo This is also a symptom of a Teamware bringover/putback failure + @echo in which SCCS files are updated but not checked out. + @echo Check for other out of date files in your workspace. + @echo "=========================================================" + @exit 666 + +%:: SCCS/s.% + @echo "=========================================================" + @echo File $@ + @echo is out of date with respect to its SCCS file. + @echo This file may be from an unresolved Teamware conflict. + @echo This is also a symptom of a Teamware bringover/putback failure + @echo in which SCCS files are updated but not checked out. + @echo Check for other out of date files in your workspace. + @echo "=========================================================" + @exit 666 + +.PHONY: default diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/sa.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/sa.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,116 @@ +# +# Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# This makefile (sa.make) is included from the sa.make in the +# build directories. + +# This makefile is used to build Serviceability Agent java code +# and generate JNI header file for native methods. + +include $(GAMMADIR)/make/aix/makefiles/rules.make + +include $(GAMMADIR)/make/defs.make + +AGENT_DIR = $(GAMMADIR)/agent + +include $(GAMMADIR)/make/sa.files + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated + +# tools.jar is needed by the JDI - SA binding +SA_CLASSPATH = $(BOOT_JAVA_HOME)/lib/tools.jar + +# TODO: if it's a modules image, check if SA module is installed. +MODULELIB_PATH= $(BOOT_JAVA_HOME)/lib/modules + +AGENT_FILES_LIST := $(GENERATED)/agent.classes.list + +SA_CLASSDIR = $(GENERATED)/saclasses + +SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VERSION)" + +SA_PROPERTIES = $(SA_CLASSDIR)/sa.properties + +# if $(AGENT_DIR) does not exist, we don't build SA +# also, we don't build SA on Itanium, PowerPC, ARM or zero. + +all: + if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" \ + -a "$(SRCARCH)" != "arm" \ + -a "$(SRCARCH)" != "ppc" \ + -a "$(SRCARCH)" != "zero" ] ; then \ + $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ + fi + +$(GENERATED)/sa-jdi.jar: $(AGENT_FILES) + $(QUIETLY) echo "Making $@" + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ + echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ + exit 1; \ + fi + $(QUIETLY) if [ ! -f $(SA_CLASSPATH) -a ! -d $(MODULELIB_PATH) ] ; then \ + echo "Missing $(SA_CLASSPATH) file. Use 1.6.0 or later version of JDK";\ + echo ""; \ + exit 1; \ + fi + $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ + mkdir -p $(SA_CLASSDIR); \ + fi +# Note: When indented, make tries to execute the '$(shell' comment. +# In some environments, cmd processors have limited line length. +# To prevent the javac invocation in the next block from using +# a very long cmd line, we use javac's @file-list option. We +# generate the file lists using make's built-in 'foreach' control +# flow which also avoids cmd processor line length issues. Since +# the 'foreach' is done as part of make's macro expansion phase, +# the initialization of the lists is also done in the same phase +# using '$(shell rm ...' instead of using the more traditional +# 'rm ...' rule. + $(shell rm -rf $(AGENT_FILES_LIST)) +# gnumake 3.78.1 does not accept the *'s that +# are in AGENT_FILES, so use the shell to expand them. +# Be extra carefull to not produce too long command lines in the shell! + $(foreach file,$(AGENT_FILES),$(shell ls -1 $(file) >> $(AGENT_FILES_LIST))) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) @$(AGENT_FILES_LIST) + $(QUIETLY) $(REMOTE) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer + $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) + $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql/sa.js + $(QUIETLY) cp $(AGENT_SRC_DIR)/sun/jvm/hotspot/utilities/soql/sa.js $(SA_CLASSDIR)/sun/jvm/hotspot/utilities/soql + $(QUIETLY) mkdir -p $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources + $(QUIETLY) rm -f $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/* + $(QUIETLY) cp $(AGENT_SRC_DIR)/sun/jvm/hotspot/ui/resources/*.png $(SA_CLASSDIR)/sun/jvm/hotspot/ui/resources/ + $(QUIETLY) cp -r $(AGENT_SRC_DIR)/images/* $(SA_CLASSDIR)/ + $(QUIETLY) $(REMOTE) $(RUN.JAR) cf $@ -C $(SA_CLASSDIR)/ . + $(QUIETLY) $(REMOTE) $(RUN.JAR) uf $@ -C $(AGENT_SRC_DIR) META-INF/services/com.sun.jdi.connect.Connector + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.x86.X86ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.ia64.IA64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.amd64.AMD64ThreadContext + $(QUIETLY) $(REMOTE) $(RUN.JAVAH) -classpath $(SA_CLASSDIR) -d $(GENERATED) -jni sun.jvm.hotspot.debugger.sparc.SPARCThreadContext + +clean: + rm -rf $(SA_CLASSDIR) + rm -rf $(GENERATED)/sa-jdi.jar + rm -rf $(AGENT_FILES_LIST) diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/saproc.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/saproc.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,125 @@ +# +# Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# +include $(GAMMADIR)/make/defs.make + +# Rules to build serviceability agent library, used by vm.make + +# libsaproc[_g].so: serviceability agent + +SAPROC = saproc +LIBSAPROC = lib$(SAPROC).so + +SAPROC_G = $(SAPROC)$(G_SUFFIX) +LIBSAPROC_G = lib$(SAPROC_G).so + +LIBSAPROC_DEBUGINFO = lib$(SAPROC).debuginfo +LIBSAPROC_DIZ = lib$(SAPROC).diz +LIBSAPROC_G_DEBUGINFO = lib$(SAPROC_G).debuginfo +LIBSAPROC_G_DIZ = lib$(SAPROC_G).diz + +AGENT_DIR = $(GAMMADIR)/agent + +SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family) + +SASRCFILES = $(SASRCDIR)/salibelf.c \ + $(SASRCDIR)/symtab.c \ + $(SASRCDIR)/libproc_impl.c \ + $(SASRCDIR)/ps_proc.c \ + $(SASRCDIR)/ps_core.c \ + $(SASRCDIR)/LinuxDebuggerLocal.c + +SAMAPFILE = $(SASRCDIR)/mapfile + +DEST_SAPROC = $(JDK_LIBDIR)/$(LIBSAPROC) +DEST_SAPROC_DEBUGINFO = $(JDK_LIBDIR)/$(LIBSAPROC_DEBUGINFO) +DEST_SAPROC_DIZ = $(JDK_LIBDIR)/$(LIBSAPROC_DIZ) + +# DEBUG_BINARIES overrides everything, use full -g debug information +ifeq ($(DEBUG_BINARIES), true) + SA_DEBUG_CFLAGS = -g +endif + +# if $(AGENT_DIR) does not exist, we don't build SA +# also, we don't build SA on Itanium, PPC, ARM or zero. + +ifneq ($(wildcard $(AGENT_DIR)),) +ifneq ($(filter-out ia64 arm ppc zero,$(SRCARCH)),) + BUILDLIBSAPROC = $(LIBSAPROC) +endif +endif + + +SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) $(LDFLAGS_HASH_STYLE) + +$(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) + $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ + echo "ALT_BOOTDIR, BOOTDIR or JAVA_HOME needs to be defined to build SA"; \ + exit 1; \ + fi + @echo Making SA debugger back-end... + $(QUIETLY) $(CC) -D$(BUILDARCH) -D_GNU_SOURCE \ + -D_FILE_OFFSET_BITS=64 \ + $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ + $(BIN_UTILS) \ + -I$(SASRCDIR) \ + -I$(GENERATED) \ + -I$(BOOT_JAVA_HOME)/include \ + -I$(BOOT_JAVA_HOME)/include/$(Platform_os_family) \ + $(SASRCFILES) \ + $(SA_LFLAGS) \ + $(SA_DEBUG_CFLAGS) \ + -o $@ \ + -lthread_db + $(QUIETLY) [ -f $(LIBSAPROC_G) ] || { ln -s $@ $(LIBSAPROC_G); } +ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) + $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBSAPROC_DEBUGINFO) + $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBSAPROC_DEBUGINFO) $@ + ifeq ($(STRIP_POLICY),all_strip) + $(QUIETLY) $(STRIP) $@ + else + ifeq ($(STRIP_POLICY),min_strip) + $(QUIETLY) $(STRIP) -g $@ + # implied else here is no stripping at all + endif + endif + [ -f $(LIBSAPROC_G_DEBUGINFO) ] || { ln -s $(LIBSAPROC_DEBUGINFO) $(LIBSAPROC_G_DEBUGINFO); } + ifeq ($(ZIP_DEBUGINFO_FILES),1) + $(ZIPEXE) -q -y $(LIBSAPROC_DIZ) $(LIBSAPROC_DEBUGINFO) $(LIBSAPROC_G_DEBUGINFO) + $(RM) $(LIBSAPROC_DEBUGINFO) $(LIBSAPROC_G_DEBUGINFO) + [ -f $(LIBSAPROC_G_DIZ) ] || { ln -s $(LIBSAPROC_DIZ) $(LIBSAPROC_G_DIZ); } + endif +endif + +install_saproc: $(BUILDLIBSAPROC) + $(QUIETLY) if [ -e $(LIBSAPROC) ] ; then \ + echo "Copying $(LIBSAPROC) to $(DEST_SAPROC)"; \ + test -f $(LIBSAPROC_DEBUGINFO) && \ + cp -f $(LIBSAPROC_DEBUGINFO) $(DEST_SAPROC_DEBUGINFO); \ + test -f $(LIBSAPROC_DIZ) && \ + cp -f $(LIBSAPROC_DIZ) $(DEST_SAPROC_DIZ); \ + cp -f $(LIBSAPROC) $(DEST_SAPROC) && echo "Done"; \ + fi + +.PHONY: install_saproc diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/top.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/top.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,144 @@ +# +# Copyright (c) 1999, 2011, 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. +# +# + +# top.make is included in the Makefile in the build directories. +# It DOES NOT include the vm dependency info in order to be faster. +# Its main job is to implement the incremental form of make lists. +# It also: +# -builds and runs adlc via adlc.make +# -generates JVMTI source and docs via jvmti.make (JSR-163) +# -generate sa-jdi.jar (JDI binding to core files) + +# It assumes the following flags are set: +# CFLAGS Platform_file, Src_Dirs_I, Src_Dirs_V, SYSDEFS, AOUT, Obj_Files + +# -- D. Ungar (5/97) from a file by Bill Bush + +# Don't override the built-in $(MAKE). +# Instead, use "gmake" (or "gnumake") from the command line. --Rose +#MAKE = gmake + +include $(GAMMADIR)/make/altsrc.make + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated +VM = $(GAMMADIR)/src/share/vm +Plat_File = $(Platform_file) +CDG = cd $(GENERATED); + +ifneq ($(USE_PRECOMPILED_HEADER),0) +UpdatePCH = $(MAKE) -f vm.make $(PRECOMPILED_HEADER) $(MFLAGS) +else +UpdatePCH = \# precompiled header is not used +endif + +Cached_plat = $(GENERATED)/platform.current + +AD_Dir = $(GENERATED)/adfiles +ADLC = $(AD_Dir)/adlc +AD_Spec = $(call altsrc-replace,$(HS_COMMON_SRC)/cpu/$(Platform_arch)/vm/$(Platform_arch_model).ad) +AD_Src = $(call altsrc-replace,$(HS_COMMON_SRC)/share/vm/adlc) +AD_Names = ad_$(Platform_arch_model).hpp ad_$(Platform_arch_model).cpp +AD_Files = $(AD_Names:%=$(AD_Dir)/%) + +# AD_Files_If_Required/COMPILER1 = ad_stuff +AD_Files_If_Required/COMPILER2 = ad_stuff +AD_Files_If_Required/TIERED = ad_stuff +AD_Files_If_Required = $(AD_Files_If_Required/$(TYPE)) + +# Wierd argument adjustment for "gnumake -j..." +adjust-mflags = $(GENERATED)/adjust-mflags +MFLAGS-adjusted = -r `$(adjust-mflags) "$(MFLAGS)" "$(HOTSPOT_BUILD_JOBS)"` + + +# default target: update lists, make vm +# done in stages to force sequential order with parallel make +# + +default: vm_build_preliminaries the_vm + @echo All done. + +# This is an explicit dependency for the sake of parallel makes. +vm_build_preliminaries: checks $(Cached_plat) $(AD_Files_If_Required) trace_stuff jvmti_stuff sa_stuff + @# We need a null action here, so implicit rules don't get consulted. + +$(Cached_plat): $(Plat_File) + $(CDG) cp $(Plat_File) $(Cached_plat) + +# make AD files as necessary +ad_stuff: $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f adlc.make $(MFLAGS-adjusted) + +# generate JVMTI files from the spec +jvmti_stuff: $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f jvmti.make $(MFLAGS-adjusted) + +# generate trace files +trace_stuff: jvmti_stuff $(Cached_plat) $(adjust-mflags) + @$(MAKE) -f trace.make $(MFLAGS-adjusted) + +# generate SA jar files and native header +sa_stuff: + @$(MAKE) -f sa.make $(MFLAGS-adjusted) + +# and the VM: must use other makefile with dependencies included + +# We have to go to great lengths to get control over the -jN argument +# to the recursive invocation of vm.make. The problem is that gnumake +# resets -jN to -j1 for recursive runs. (How helpful.) +# Note that the user must specify the desired parallelism level via a +# command-line or environment variable name HOTSPOT_BUILD_JOBS. +$(adjust-mflags): $(GAMMADIR)/make/$(Platform_os_family)/makefiles/adjust-mflags.sh + @+rm -f $@ $@+ + @+cat $< > $@+ + @+chmod +x $@+ + @+mv $@+ $@ + +the_vm: vm_build_preliminaries $(adjust-mflags) + @$(UpdatePCH) + @$(MAKE) -f vm.make $(MFLAGS-adjusted) + +install gamma: the_vm + @$(MAKE) -f vm.make $@ + +# next rules support "make foo.[ois]" + +%.o %.i %.s: + $(UpdatePCH) + $(MAKE) -f vm.make $(MFLAGS) $@ + #$(MAKE) -f vm.make $@ + +# this should force everything to be rebuilt +clean: + rm -f $(GENERATED)/*.class + $(MAKE) -f vm.make $(MFLAGS) clean + +# just in case it doesn't, this should do it +realclean: + $(MAKE) -f vm.make $(MFLAGS) clean + rm -fr $(GENERATED) + +.PHONY: default vm_build_preliminaries +.PHONY: lists ad_stuff jvmti_stuff sa_stuff the_vm clean realclean +.PHONY: checks check_os_version install diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/trace.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/trace.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,121 @@ +# +# Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2013 SAP AG. 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. +# +# + +# This makefile (trace.make) is included from the trace.make in the +# build directories. +# +# It knows how to build and run the tools to generate trace files. + +include $(GAMMADIR)/make/aix/makefiles/rules.make +include $(GAMMADIR)/make/altsrc.make + +# ######################################################################### + +HAS_ALT_SRC:=$(shell if [ -d $(HS_ALT_SRC)/share/vm/trace ]; then \ + echo "true"; else echo "false";\ + fi) + +TOPDIR = $(shell echo `pwd`) +GENERATED = $(TOPDIR)/../generated +JvmtiOutDir = $(GENERATED)/jvmtifiles +TraceOutDir = $(GENERATED)/tracefiles + +TraceAltSrcDir = $(HS_ALT_SRC)/share/vm/trace +TraceSrcDir = $(HS_COMMON_SRC)/share/vm/trace + +# set VPATH so make knows where to look for source files +Src_Dirs_V += $(TraceSrcDir) $(TraceAltSrcDir) +VPATH += $(Src_Dirs_V:%=%:) + +TraceGeneratedNames = \ + traceEventClasses.hpp \ + traceEventIds.hpp \ + traceTypes.hpp + +ifeq ($(HAS_ALT_SRC), true) +TraceGeneratedNames += \ + traceRequestables.hpp \ + traceEventControl.hpp + +ifeq ($(INCLUDE_TRACE), 1) +TraceGeneratedNames += traceProducer.cpp +endif + +endif + +TraceGeneratedFiles = $(TraceGeneratedNames:%=$(TraceOutDir)/%) + +XSLT = $(REMOTE) $(RUN.JAVA) -classpath $(JvmtiOutDir) jvmtiGen + +XML_DEPS = $(TraceSrcDir)/trace.xml $(TraceSrcDir)/tracetypes.xml \ + $(TraceSrcDir)/trace.dtd $(TraceSrcDir)/xinclude.mod +ifeq ($(HAS_ALT_SRC), true) + XML_DEPS += $(TraceAltSrcDir)/traceevents.xml +endif + +.PHONY: all clean cleanall + +# ######################################################################### + +all: $(TraceGeneratedFiles) + +GENERATE_CODE= \ + $(QUIETLY) echo Generating $@; \ + $(XSLT) -IN $(word 1,$^) -XSL $(word 2,$^) -OUT $@; \ + test -f $@ + +$(TraceOutDir)/traceEventIds.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventIds.xsl $(XML_DEPS) + $(GENERATE_CODE) + +$(TraceOutDir)/traceTypes.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceTypes.xsl $(XML_DEPS) + $(GENERATE_CODE) + +ifeq ($(HAS_ALT_SRC), false) + +$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceSrcDir)/traceEventClasses.xsl $(XML_DEPS) + $(GENERATE_CODE) + +else + +$(TraceOutDir)/traceEventClasses.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventClasses.xsl $(XML_DEPS) + $(GENERATE_CODE) + +$(TraceOutDir)/traceProducer.cpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceProducer.xsl $(XML_DEPS) + $(GENERATE_CODE) + +$(TraceOutDir)/traceRequestables.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceRequestables.xsl $(XML_DEPS) + $(GENERATE_CODE) + +$(TraceOutDir)/traceEventControl.hpp: $(TraceSrcDir)/trace.xml $(TraceAltSrcDir)/traceEventControl.xsl $(XML_DEPS) + $(GENERATE_CODE) + +endif + +# ######################################################################### + +clean cleanall: + rm $(TraceGeneratedFiles) + + diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/vm.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/vm.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,384 @@ +# +# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# Rules to build JVM and related libraries, included from vm.make in the build +# directory. + +# Common build rules. +MAKEFILES_DIR=$(GAMMADIR)/make/$(Platform_os_family)/makefiles +include $(MAKEFILES_DIR)/rules.make +include $(GAMMADIR)/make/altsrc.make + +default: build + +#---------------------------------------------------------------------- +# Defs + +GENERATED = ../generated +DEP_DIR = $(GENERATED)/dependencies + +# reads the generated files defining the set of .o's and the .o .h dependencies +-include $(DEP_DIR)/*.d + +# read machine-specific adjustments (%%% should do this via buildtree.make?) +ifeq ($(findstring true, $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK)), true) + include $(MAKEFILES_DIR)/zeroshark.make +else + include $(MAKEFILES_DIR)/$(BUILDARCH).make +endif + +# set VPATH so make knows where to look for source files +# Src_Dirs_V is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm +# The adfiles directory contains ad_.[ch]pp. +# The jvmtifiles directory contains jvmti*.[ch]pp +Src_Dirs_V += $(GENERATED)/adfiles $(GENERATED)/jvmtifiles $(GENERATED)/tracefiles +VPATH += $(Src_Dirs_V:%=%:) + +# set INCLUDES for C preprocessor. +Src_Dirs_I += $(GENERATED) +# The order is important for the precompiled headers to work. +INCLUDES += $(PRECOMPILED_HEADER_DIR:%=-I%) $(Src_Dirs_I:%=-I%) + +# SYMFLAG is used by {jsig,saproc}.make +ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) + # always build with debug info when we can create .debuginfo files + SYMFLAG = -g +else + ifeq (${VERSION}, debug) + SYMFLAG = -g + else + SYMFLAG = + endif +endif + +# HOTSPOT_RELEASE_VERSION and HOTSPOT_BUILD_VERSION are defined +# in $(GAMMADIR)/make/defs.make +ifeq ($(HOTSPOT_BUILD_VERSION),) + BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HOTSPOT_RELEASE_VERSION)\"" +else + BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HOTSPOT_RELEASE_VERSION)-$(HOTSPOT_BUILD_VERSION)\"" +endif + +# The following variables are defined in the generated flags.make file. +BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HS_BUILD_VER)\"" +JRE_VERSION = -DJRE_RELEASE_VERSION="\"$(JRE_RELEASE_VER)\"" +HS_LIB_ARCH = -DHOTSPOT_LIB_ARCH=\"$(LIBARCH)\" +BUILD_TARGET = -DHOTSPOT_BUILD_TARGET="\"$(TARGET)\"" +BUILD_USER = -DHOTSPOT_BUILD_USER="\"$(HOTSPOT_BUILD_USER)\"" +VM_DISTRO = -DHOTSPOT_VM_DISTRO="\"$(HOTSPOT_VM_DISTRO)\"" + +CXXFLAGS = \ + ${SYSDEFS} \ + ${INCLUDES} \ + ${BUILD_VERSION} \ + ${BUILD_TARGET} \ + ${BUILD_USER} \ + ${HS_LIB_ARCH} \ + ${VM_DISTRO} + +# This is VERY important! The version define must only be supplied to vm_version.o +# If not, ccache will not re-use the cache at all, since the version string might contain +# a time and date. +vm_version.o: CXXFLAGS += ${JRE_VERSION} + +ifeq ($(INCLUDE_TRACE), 1) +CFLAGS += -DINCLUDE_TRACE=1 +endif + +# CFLAGS_WARN holds compiler options to suppress/enable warnings. +CFLAGS += $(CFLAGS_WARN/BYFILE) + +# Do not use C++ exception handling +CFLAGS += $(CFLAGS/NOEX) + +# Extra flags from gnumake's invocation or environment +CFLAGS += $(EXTRA_CFLAGS) +LFLAGS += $(EXTRA_CFLAGS) + +# Don't set excutable bit on stack segment +# the same could be done by separate execstack command +#LFLAGS += -Xlinker -z -Xlinker noexecstack + +LIBS += -lm -ldl -lpthread + +# By default, link the *.o into the library, not the executable. +LINK_INTO$(LINK_INTO) = LIBJVM + +JDK_LIBDIR = $(JAVA_HOME)/jre/lib/$(LIBARCH) + +#---------------------------------------------------------------------- +# jvm_db & dtrace +include $(MAKEFILES_DIR)/dtrace.make + +#---------------------------------------------------------------------- +# JVM + +JVM = jvm +LIBJVM = lib$(JVM).so +LIBJVM_G = lib$(JVM)$(G_SUFFIX).so + +LIBJVM_DEBUGINFO = lib$(JVM).debuginfo +LIBJVM_DIZ = lib$(JVM).diz +LIBJVM_G_DEBUGINFO = lib$(JVM)$(G_SUFFIX).debuginfo +LIBJVM_G_DIZ = lib$(JVM)$(G_SUFFIX).diz + +SPECIAL_PATHS:=adlc c1 gc_implementation opto shark libadt + +SOURCE_PATHS=\ + $(shell find $(HS_COMMON_SRC)/share/vm/* -type d \! \ + \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) \)) +SOURCE_PATHS+=$(HS_COMMON_SRC)/os/$(Platform_os_family)/vm +SOURCE_PATHS+=$(HS_COMMON_SRC)/os/posix/vm +SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(SRCARCH)/vm +SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_family)_$(SRCARCH)/vm + +ifeq ($(INCLUDE_TRACE), 1) +SOURCE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ + find $(HS_ALT_SRC)/share/vm/jfr -type d; \ + fi) +endif + +CORE_PATHS=$(foreach path,$(SOURCE_PATHS),$(call altsrc,$(path)) $(path)) +CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/tracefiles + +COMPILER1_PATHS := $(call altsrc,$(HS_COMMON_SRC)/share/vm/c1) +COMPILER1_PATHS += $(HS_COMMON_SRC)/share/vm/c1 + +COMPILER2_PATHS := $(call altsrc,$(HS_COMMON_SRC)/share/vm/opto) +COMPILER2_PATHS += $(call altsrc,$(HS_COMMON_SRC)/share/vm/libadt) +COMPILER2_PATHS += $(HS_COMMON_SRC)/share/vm/opto +COMPILER2_PATHS += $(HS_COMMON_SRC)/share/vm/libadt +COMPILER2_PATHS += $(GENERATED)/adfiles + +SHARK_PATHS := $(GAMMADIR)/src/share/vm/shark + +# Include dirs per type. +Src_Dirs/CORE := $(CORE_PATHS) +Src_Dirs/COMPILER1 := $(CORE_PATHS) $(COMPILER1_PATHS) +Src_Dirs/COMPILER2 := $(CORE_PATHS) $(COMPILER2_PATHS) +Src_Dirs/TIERED := $(CORE_PATHS) $(COMPILER1_PATHS) $(COMPILER2_PATHS) +Src_Dirs/ZERO := $(CORE_PATHS) +Src_Dirs/SHARK := $(CORE_PATHS) $(SHARK_PATHS) +Src_Dirs := $(Src_Dirs/$(TYPE)) + +COMPILER2_SPECIFIC_FILES := opto libadt bcEscapeAnalyzer.cpp chaitin\* c2_\* runtime_\* +COMPILER1_SPECIFIC_FILES := c1_\* +SHARK_SPECIFIC_FILES := shark +ZERO_SPECIFIC_FILES := zero + +# Always exclude these. +Src_Files_EXCLUDE := jsig.c jvmtiEnvRecommended.cpp jvmtiEnvStub.cpp + +# Exclude per type. +Src_Files_EXCLUDE/CORE := $(COMPILER1_SPECIFIC_FILES) $(COMPILER2_SPECIFIC_FILES) $(ZERO_SPECIFIC_FILES) $(SHARK_SPECIFIC_FILES) ciTypeFlow.cpp +Src_Files_EXCLUDE/COMPILER1 := $(COMPILER2_SPECIFIC_FILES) $(ZERO_SPECIFIC_FILES) $(SHARK_SPECIFIC_FILES) ciTypeFlow.cpp +Src_Files_EXCLUDE/COMPILER2 := $(COMPILER1_SPECIFIC_FILES) $(ZERO_SPECIFIC_FILES) $(SHARK_SPECIFIC_FILES) +Src_Files_EXCLUDE/TIERED := $(ZERO_SPECIFIC_FILES) $(SHARK_SPECIFIC_FILES) +Src_Files_EXCLUDE/ZERO := $(COMPILER1_SPECIFIC_FILES) $(COMPILER2_SPECIFIC_FILES) $(SHARK_SPECIFIC_FILES) ciTypeFlow.cpp +Src_Files_EXCLUDE/SHARK := $(COMPILER1_SPECIFIC_FILES) $(COMPILER2_SPECIFIC_FILES) $(ZERO_SPECIFIC_FILES) + +Src_Files_EXCLUDE += $(Src_Files_EXCLUDE/$(TYPE)) + +# Disable ELF decoder on AIX (AIX uses XCOFF). +Src_Files_EXCLUDE += decoder_elf.cpp elfFile.cpp elfStringTable.cpp elfSymbolTable.cpp elfFuncDescTable.cpp + +# Special handling of arch model. +ifeq ($(Platform_arch_model), x86_32) +Src_Files_EXCLUDE += \*x86_64\* +endif +ifeq ($(Platform_arch_model), x86_64) +Src_Files_EXCLUDE += \*x86_32\* +endif + +# Locate all source files in the given directory, excluding files in Src_Files_EXCLUDE. +define findsrc + $(notdir $(shell find $(1)/. ! -name . -prune \ + -a \( -name \*.c -o -name \*.cpp -o -name \*.s \) \ + -a ! \( -name DUMMY $(addprefix -o -name ,$(Src_Files_EXCLUDE)) \))) +endef + +Src_Files := $(foreach e,$(Src_Dirs),$(call findsrc,$(e))) + +Obj_Files = $(sort $(addsuffix .o,$(basename $(Src_Files)))) + +JVM_OBJ_FILES = $(Obj_Files) + +vm_version.o: $(filter-out vm_version.o,$(JVM_OBJ_FILES)) + +mapfile : $(MAPFILE) vm.def + rm -f $@ + awk '{ if ($$0 ~ "INSERT VTABLE SYMBOLS HERE") \ + { system ("cat vm.def"); } \ + else \ + { print $$0 } \ + }' > $@ < $(MAPFILE) + +mapfile_reorder : mapfile $(REORDERFILE) + rm -f $@ + cat $^ > $@ + +vm.def: $(Res_Files) $(Obj_Files) + sh $(GAMMADIR)/make/aix/makefiles/build_vm_def.sh *.o > $@ + +ifeq ($(JVM_VARIANT_ZEROSHARK), true) + STATIC_CXX = false +else + ifeq ($(ZERO_LIBARCH), ppc64) + STATIC_CXX = false + else + STATIC_CXX = true + endif +endif + +ifeq ($(LINK_INTO),AOUT) + LIBJVM.o = + LIBJVM_MAPFILE = + LIBS_VM = $(LIBS) +else + LIBJVM.o = $(JVM_OBJ_FILES) + LIBJVM_MAPFILE$(LDNOMAP) = mapfile_reorder + LFLAGS_VM$(LDNOMAP) += $(MAPFLAG:FILENAME=$(LIBJVM_MAPFILE)) +# xlC_r ignores the -o= syntax +# LFLAGS_VM += $(SONAMEFLAG:SONAME=$(LIBJVM)) + + # JVM is statically linked with libgcc[_s] and libstdc++; this is needed to + # get around library dependency and compatibility issues. Must use gcc not + # g++ to link. + LIBS_VM += $(STATIC_STDCXX) $(LIBS) +endif + +LINK_VM = $(LINK_LIB.CXX) + +# create loadmap for libjvm.so by default. Helps in diagnosing some problems. +LFLAGS_VM += -bloadmap:libjvm.loadmap + +# rule for building precompiled header +$(PRECOMPILED_HEADER): + $(QUIETLY) echo Generating precompiled header $@ + $(QUIETLY) mkdir -p $(PRECOMPILED_HEADER_DIR) + $(QUIETLY) $(COMPILE.CXX) $(DEPFLAGS) -x c++-header $(PRECOMPILED_HEADER_SRC) -o $@ $(COMPILE_DONE) + +# making the library: + +ifneq ($(JVM_BASE_ADDR),) +# By default shared library is linked at base address == 0. Modify the +# linker script if JVM prefers a different base location. It can also be +# implemented with 'prelink -r'. But 'prelink' is not (yet) available on +# our build platform (AS-2.1). +LD_SCRIPT = libjvm.so.lds +$(LD_SCRIPT): $(LIBJVM_MAPFILE) + $(QUIETLY) { \ + rm -rf $@; \ + $(LINK_VM) -Wl,--verbose $(LFLAGS_VM) 2>&1 | \ + sed -e '/^======/,/^======/!d' \ + -e '/^======/d' \ + -e 's/0\( + SIZEOF_HEADERS\)/$(JVM_BASE_ADDR)\1/' \ + > $@; \ + } +LD_SCRIPT_FLAG = -Wl,-T,$(LD_SCRIPT) +endif + +# With more recent Redhat releases (or the cutting edge version Fedora), if +# SELinux is configured to be enabled, the runtime linker will fail to apply +# the text relocation to libjvm.so considering that it is built as a non-PIC +# DSO. To workaround that, we run chcon to libjvm.so after it is built. See +# details in bug 6538311. +$(LIBJVM): $(LIBJVM.o) $(LIBJVM_MAPFILE) $(LD_SCRIPT) + $(QUIETLY) { \ + echo Linking vm...; \ + $(LINK_LIB.CXX/PRE_HOOK) \ + $(LINK_VM) $(LD_SCRIPT_FLAG) \ + $(LFLAGS_VM) -o $@ $(LIBJVM.o) $(LIBS_VM); \ + $(LINK_LIB.CXX/POST_HOOK) \ + rm -f $@.1; ln -s $@ $@.1; \ + } +# No security contexts on AIX +# [ -f $(LIBJVM_G) ] || { ln -s $@ $(LIBJVM_G); ln -s $@.1 $(LIBJVM_G).1; }; \ +# if [ \"$(CROSS_COMPILE_ARCH)\" = \"\" ] ; then \ +# if [ -x /usr/sbin/selinuxenabled ] ; then \ +# /usr/sbin/selinuxenabled; \ +# if [ $$? = 0 ] ; then \ +# /usr/bin/chcon -t textrel_shlib_t $@; \ +# if [ $$? != 0 ]; then \ +# echo "ERROR: Cannot chcon $@"; \ +# fi \ +# fi \ +# fi \ +# fi \ +# } + +#ifeq ($(CROSS_COMPILE_ARCH),) +# ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) +# $(QUIETLY) $(OBJCOPY) --only-keep-debug $@ $(LIBJVM_DEBUGINFO) +# $(QUIETLY) $(OBJCOPY) --add-gnu-debuglink=$(LIBJVM_DEBUGINFO) $@ +# ifeq ($(STRIP_POLICY),all_strip) +# $(QUIETLY) $(STRIP) $@ +# else +# ifeq ($(STRIP_POLICY),min_strip) +# $(QUIETLY) $(STRIP) -g $@ +# # implied else here is no stripping at all +# endif +# endif +# $(QUIETLY) [ -f $(LIBJVM_G_DEBUGINFO) ] || ln -s $(LIBJVM_DEBUGINFO) $(LIBJVM_G_DEBUGINFO) +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# $(ZIPEXE) -q -y $(LIBJVM_DIZ) $(LIBJVM_DEBUGINFO) $(LIBJVM_G_DEBUGINFO) +# $(RM) $(LIBJVM_DEBUGINFO) $(LIBJVM_G_DEBUGINFO) +# [ -f $(LIBJVM_G_DIZ) ] || { ln -s $(LIBJVM_DIZ) $(LIBJVM_G_DIZ); } +# endif +# endif +#endif + +DEST_SUBDIR = $(JDK_LIBDIR)/$(VM_SUBDIR) +DEST_JVM = $(DEST_SUBDIR)/$(LIBJVM) +DEST_JVM_DEBUGINFO = $(DEST_SUBDIR)/$(LIBJVM_DEBUGINFO) +DEST_JVM_DIZ = $(DEST_SUBDIR)/$(LIBJVM_DIZ) + +install_jvm: $(LIBJVM) + @echo "Copying $(LIBJVM) to $(DEST_JVM)" + $(QUIETLY) test -f $(LIBJVM_DEBUGINFO) && \ + cp -f $(LIBJVM_DEBUGINFO) $(DEST_JVM_DEBUGINFO) + $(QUIETLY) test -f $(LIBJVM_DIZ) && \ + cp -f $(LIBJVM_DIZ) $(DEST_JVM_DIZ) + $(QUIETLY) cp -f $(LIBJVM) $(DEST_JVM) && echo "Done" + +#---------------------------------------------------------------------- +# Other files + +# Gamma launcher +include $(MAKEFILES_DIR)/launcher.make + +# Signal interposition library +include $(MAKEFILES_DIR)/jsig.make + +# Serviceability agent +include $(MAKEFILES_DIR)/saproc.make + +#---------------------------------------------------------------------- + +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(BUILDLIBSAPROC) + +install: install_jvm install_jsig install_saproc + +.PHONY: default build install install_jvm diff -r 368df6a4b403 -r d982b0f5b59a make/aix/makefiles/xlc.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/xlc.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,180 @@ +# +# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012 SAP. 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. +# +# + +#------------------------------------------------------------------------ +# CC, CXX & AS + +# Set compiler explicitly +CXX = $(COMPILER_PATH)xlC_r +CC = $(COMPILER_PATH)xlc_r +HOSTCXX = $(CXX) +HOSTCC = $(CC) + +AS = $(CC) -c + +# get xlc version +CXX_VERSION := $(shell $(CXX) -qversion 2>&1 | sed -n 's/.*Version: \([0-9.]*\)/\1/p') + +# xlc 08.00.0000.0023 and higher supports -qtune=balanced +CXX_SUPPORTS_BALANCED_TUNING=$(shell if [ $(subst .,,$(CXX_VERSION)) -ge 080000000023 ] ; then echo "true" ; fi) +# xlc 10.01 is used with aggressive optimizations to boost performance +CXX_IS_V10=$(shell if [ $(subst .,,$(CXX_VERSION)) -ge 100100000000 ] ; then echo "true" ; fi) + +# check for precompiled headers support + +# Switch off the precompiled header support. Neither xlC 8.0 nor xlC 10.0 +# support precompiled headers. Both "understand" the command line switches "-qusepcomp" and +# "-qgenpcomp" but when we specify them the following message is printed: +# "1506-755 (W) The -qusepcomp option is not supported in this release." +USE_PRECOMPILED_HEADER = 0 +ifneq ($(USE_PRECOMPILED_HEADER),0) +PRECOMPILED_HEADER_DIR=. +PRECOMPILED_HEADER_SRC=$(GAMMADIR)/src/share/vm/precompiled/precompiled.hpp +PRECOMPILED_HEADER=$(PRECOMPILED_HEADER_DIR)/precompiled.hpp.gch +endif + + +#------------------------------------------------------------------------ +# Compiler flags + +# position-independent code +PICFLAG = -qpic=large + +VM_PICFLAG/LIBJVM = $(PICFLAG) +VM_PICFLAG/AOUT = +VM_PICFLAG = $(VM_PICFLAG/$(LINK_INTO)) + +CFLAGS += $(VM_PICFLAG) +CFLAGS += -qnortti +CFLAGS += -qnoeh + +CFLAGS += -D_REENTRANT +# no xlc counterpart for -fcheck-new +# CFLAGS += -fcheck-new + +ARCHFLAG = -q64 + +CFLAGS += $(ARCHFLAG) +AOUT_FLAGS += $(ARCHFLAG) +LFLAGS += $(ARCHFLAG) +ASFLAGS += $(ARCHFLAG) + +# Use C++ Interpreter +ifdef CC_INTERP + CFLAGS += -DCC_INTERP +endif + +# Keep temporary files (.ii, .s) +# no counterpart on xlc for -save-temps, -pipe + +# Compiler warnings are treated as errors +# Do not treat warnings as errors +# WARNINGS_ARE_ERRORS = -Werror +# Except for a few acceptable ones +# ACCEPTABLE_WARNINGS = -Wpointer-arith -Wconversion -Wsign-compare +# CFLAGS_WARN/DEFAULT = $(WARNINGS_ARE_ERRORS) $(ACCEPTABLE_WARNINGS) +CFLAGS_WARN/COMMON = +CFLAGS_WARN/DEFAULT = $(CFLAGS_WARN/COMMON) $(EXTRA_WARNINGS) +# Special cases +CFLAGS_WARN/BYFILE = $(CFLAGS_WARN/$@)$(CFLAGS_WARN/DEFAULT$(CFLAGS_WARN/$@)) + +# The flags to use for an Optimized g++ build +OPT_CFLAGS += -O3 + +# Hotspot uses very unstrict aliasing turn this optimization off +OPT_CFLAGS += -qalias=noansi + +OPT_CFLAGS/NOOPT=-qnoopt + +DEPFLAGS = -qmakedep=gcc -MF $(DEP_DIR)/$(@:%=%.d) + +#------------------------------------------------------------------------ +# Linker flags + +# statically link libstdc++.so, work with gcc but ignored by g++ +STATIC_STDCXX = -Wl,-lC_r + +# statically link libgcc and/or libgcc_s, libgcc does not exist before gcc-3.x. +# Not needed on AIX. +#ifneq ("${CC_VER_MAJOR}", "2") +#STATIC_LIBGCC += -static-libgcc +#endif + +ifeq ($(BUILDARCH), ia64) +LFLAGS += -Wl,-relax +endif + +# Enable linker optimization +# no counterpart on xlc for this +# LFLAGS += -Xlinker -O1 + +# Use $(MAPFLAG:FILENAME=real_file_name) to specify a map file. +# MAPFLAG = -Xlinker --version-script=FILENAME + +# Use $(SONAMEFLAG:SONAME=soname) to specify the intrinsic name of a shared obj +# Not needed for xlC_r +#SONAMEFLAG = -Xlinker -soname=SONAME + +# Build shared library +SHARED_FLAG = -q64 -b64 -bexpall -G -bnoentry -qmkshrobj -brtl -bnolibpath + +#------------------------------------------------------------------------ +# Debug flags + +# Use the stabs format for debugging information (this is the default +# on gcc-2.91). It's good enough, has all the information about line +# numbers and local variables, and libjvm_g.so is only about 16M. +# Change this back to "-g" if you want the most expressive format. +# (warning: that could easily inflate libjvm_g.so to 150M!) +# Note: The Itanium gcc compiler crashes when using -gstabs. +DEBUG_CFLAGS += -g +ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) + FASTDEBUG_CFLAGS += -g + OPT_CFLAGS += -g +endif + +# DEBUG_BINARIES overrides everything, use full -g debug information +ifeq ($(DEBUG_BINARIES), true) + DEBUG_CFLAGS = -g + CFLAGS += $(DEBUG_CFLAGS) +endif + +# If we are building HEADLESS, pass on to VM +# so it can set the java.awt.headless property +ifdef HEADLESS +CFLAGS += -DHEADLESS +endif + +# We are building Embedded for a small device +# favor code space over speed +ifdef MINIMIZE_RAM_USAGE +CFLAGS += -DMINIMIZE_RAM_USAGE +endif + +ifdef CROSS_COMPILE_ARCH + STRIP = $(ALT_COMPILER_PATH)/strip +else + STRIP = strip +endif diff -r 368df6a4b403 -r d982b0f5b59a make/aix/platform_ppc64 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/platform_ppc64 Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,17 @@ +os_family = aix + +arch = ppc + +arch_model = ppc_64 + +os_arch = aix_ppc + +os_arch_model = aix_ppc_64 + +lib_arch = ppc64 + +compiler = xlc + +gnu_dis_arch = ppc64 + +sysdefs = -DAIX -DPPC64 diff -r 368df6a4b403 -r d982b0f5b59a make/bsd/Makefile --- a/make/bsd/Makefile Thu Apr 13 17:18:04 2017 -0400 +++ b/make/bsd/Makefile Sun May 21 11:12:48 2017 -0700 @@ -290,42 +290,54 @@ $(TARGETS_C2): $(SUBDIRS_C2) cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install endif $(TARGETS_TIERED): $(SUBDIRS_TIERED) cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_C1): $(SUBDIRS_C1) cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_CORE): $(SUBDIRS_CORE) cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_ZERO): $(SUBDIRS_ZERO) cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_SHARK): $(SUBDIRS_SHARK) cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) install endif diff -r 368df6a4b403 -r d982b0f5b59a make/defs.make --- a/make/defs.make Thu Apr 13 17:18:04 2017 -0400 +++ b/make/defs.make Sun May 21 11:12:48 2017 -0700 @@ -173,11 +173,15 @@ HOST := $(shell uname -n) endif -# If not SunOS, not Linux and not BSD, assume Windows +# If not SunOS, not Linux not BSD and not AIX, assume Windows ifneq ($(OS), Linux) ifneq ($(OS), SunOS) ifneq ($(OS), bsd) - OSNAME=windows + ifneq ($(OS), AIX) + OSNAME=windows + else + OSNAME=aix + endif else OSNAME=bsd endif @@ -266,7 +270,7 @@ # Use uname output for SRCARCH, but deal with platform differences. If ARCH # is not explicitly listed below, it is treated as x86. - SRCARCH = $(ARCH/$(filter sparc sparc64 ia64 amd64 x86_64 arm ppc zero,$(ARCH))) + SRCARCH = $(ARCH/$(filter sparc sparc64 ia64 amd64 x86_64 arm ppc ppc64 zero,$(ARCH))) ARCH/ = x86 ARCH/sparc = sparc ARCH/sparc64= sparc @@ -292,6 +296,13 @@ BUILDARCH = sparcv9 endif endif + ifeq ($(BUILDARCH), ppc) + ifdef LP64 + BUILDARCH = ppc64 + else + BUILDARCH = ppc + endif + endif # LIBARCH is 1:1 mapping from BUILDARCH LIBARCH = $(LIBARCH/$(BUILDARCH)) @@ -300,12 +311,12 @@ LIBARCH/sparc = sparc LIBARCH/sparcv9 = sparcv9 LIBARCH/ia64 = ia64 - LIBARCH/ppc64 = ppc + LIBARCH/ppc64 = ppc64 LIBARCH/ppc = ppc LIBARCH/arm = arm LIBARCH/zero = $(ZERO_LIBARCH) - LP64_ARCH = sparcv9 amd64 ia64 zero + LP64_ARCH = sparcv9 amd64 ia64 ppc64 zero endif # Required make macro settings for all platforms @@ -344,6 +355,9 @@ EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/$(JDK_INCLUDE_SUBDIR)/jni_md.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jmm.h +# By default, run Queens test after building +TEST_IN_BUILD ?= true + ifndef JAVASE_EMBEDDED EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jfr.h ifneq (${ARCH},arm) diff -r 368df6a4b403 -r d982b0f5b59a make/linux/Makefile --- a/make/linux/Makefile Thu Apr 13 17:18:04 2017 -0400 +++ b/make/linux/Makefile Sun May 21 11:12:48 2017 -0700 @@ -66,6 +66,10 @@ FORCE_TIERED=1 endif endif +# C1 is not ported on ppc64, so we cannot build a tiered VM: +ifeq ($(ARCH),ppc64) + FORCE_TIERED=0 +endif ifdef LP64 ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","") @@ -208,7 +212,7 @@ TARGETS_SHARK = $(addsuffix shark,$(TARGETS)) BUILDTREE_MAKE = $(GAMMADIR)/make/$(OSNAME)/makefiles/buildtree.make -BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) OPENJDK_TARGET_CPU_ENDIAN=$(OPENJDK_TARGET_CPU_ENDIAN) BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HOTSPOT_RELEASE_VERSION) HOTSPOT_BUILD_VERSION=$(HOTSPOT_BUILD_VERSION) JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) BUILDTREE_VARS += ENABLE_FULL_DEBUG_SYMBOLS=$(ENABLE_FULL_DEBUG_SYMBOLS) OBJCOPY=$(OBJCOPY) STRIP_POLICY=$(STRIP_POLICY) ZIP_DEBUGINFO_FILES=$(ZIP_DEBUGINFO_FILES) ZIPEXE=$(ZIPEXE) @@ -288,42 +292,54 @@ $(TARGETS_C2): $(SUBDIRS_C2) cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install endif $(TARGETS_TIERED): $(SUBDIRS_TIERED) cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_C1): $(SUBDIRS_C1) cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_CORE): $(SUBDIRS_CORE) cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_ZERO): $(SUBDIRS_ZERO) cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_SHARK): $(SUBDIRS_SHARK) cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) install endif diff -r 368df6a4b403 -r d982b0f5b59a make/linux/makefiles/buildtree.make --- a/make/linux/makefiles/buildtree.make Thu Apr 13 17:18:04 2017 -0400 +++ b/make/linux/makefiles/buildtree.make Sun May 21 11:12:48 2017 -0700 @@ -26,7 +26,7 @@ # Usage: # # $(MAKE) -f buildtree.make SRCARCH=srcarch BUILDARCH=buildarch LIBARCH=libarch -# GAMMADIR=dir OS_FAMILY=os VARIANT=variant +# GAMMADIR=dir OS_FAMILY=os VARIANT=variant OPENJDK_TARGET_CPU_ENDIAN=endianness # # The macros ARCH, GAMMADIR, OS_FAMILY and VARIANT must be defined in the # environment or on the command-line: @@ -40,7 +40,8 @@ # HOTSPOT_RELEASE_VERSION - .-b (11.0-b07) # HOTSPOT_BUILD_VERSION - internal, internal-$(USER_RELEASE_SUFFIX) or empty # JRE_RELEASE_VERSION - .. (1.7.0) -# +# OPENJDK_TARGET_CPU_ENDIAN - target endianness: 'big'/'little'. Used to differentiate +# the architecture flavor for PowerPC64 # Builds the directory trees with makefiles plus some convenience files in # each directory: # @@ -133,7 +134,8 @@ env.sh env.csh jdkpath.sh .dbxrc test_gamma BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ - SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) + SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) \ + OPENJDK_TARGET_CPU_ENDIAN=$(OPENJDK_TARGET_CPU_ENDIAN) # Define variables to be set in flags.make. # Default values are set in make/defs.make. @@ -209,6 +211,7 @@ echo "HOTSPOT_BUILD_USER = $(HOTSPOT_BUILD_USER)"; \ echo "HOTSPOT_VM_DISTRO = $(HOTSPOT_VM_DISTRO)"; \ echo "OPENJDK = $(OPENJDK)"; \ + echo "OPENJDK_TARGET_CPU_ENDIAN = $(OPENJDK_TARGET_CPU_ENDIAN)"; \ echo "ZERO_BUILD = $(ZERO_BUILD)"; \ echo; \ echo "# Used for platform dispatching"; \ @@ -408,6 +411,7 @@ DATA_MODE/sparcv9 = 64 DATA_MODE/amd64 = 64 DATA_MODE/ia64 = 64 +DATA_MODE/ppc64 = 64 DATA_MODE/zero = $(ARCH_DATA_MODEL) JAVA_FLAG/32 = -d32 @@ -416,6 +420,9 @@ WRONG_DATA_MODE_MSG = \ echo "JAVA_HOME must point to a $(DATA_MODE)-bit OpenJDK." +WRONG_JDK_MSG = \ + echo "JAVA_HOME must point to a HotSpot based JDK \\\(genuine Sun/Oracle or OpenJDK\\\)." + CROSS_COMPILING_MSG = \ echo "Cross compiling for ARCH $(CROSS_COMPILE_ARCH), skipping gamma run." @@ -452,6 +459,14 @@ echo " exit 0"; \ echo "fi"; \ echo ""; \ + echo "# 'gamma' only runs with HotSpot based JDKs (genuine Sun/Oracle or OpenJDK)"; \ + echo ""; \ + echo "\$${JAVA_HOME}/bin/java $(JAVA_FLAG) -version 2>&1 | grep -E 'OpenJDK|HotSpot' > /dev/null"; \ + echo "if [ \$$? -ne 0 ]; then "; \ + echo " $(WRONG_JDK_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ echo "# Use gamma_g if it exists"; \ echo ""; \ echo "GAMMA_PROG=gamma"; \ diff -r 368df6a4b403 -r d982b0f5b59a make/linux/makefiles/defs.make --- a/make/linux/makefiles/defs.make Thu Apr 13 17:18:04 2017 -0400 +++ b/make/linux/makefiles/defs.make Sun May 21 11:12:48 2017 -0700 @@ -126,6 +126,27 @@ endif endif +# PPC64 +ifeq ($(ARCH), ppc64) + ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = linux-ppc64 + VM_PLATFORM = linux_ppc64 + HS_ARCH = ppc + OPENJDK_TARGET_CPU_ENDIAN = big +endif + +# PPC64LE +ifeq ($(ARCH), ppc64le) + ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = linux-ppc64 + VM_PLATFORM = linux_ppc64 + HS_ARCH = ppc + OPENJDK_TARGET_CPU_ENDIAN = little + ARCH := ppc64 +endif + # determine if HotSpot is being built in JDK6 or earlier version JDK6_OR_EARLIER=0 ifeq "$(shell expr \( '$(JDK_MAJOR_VERSION)' != '' \& '$(JDK_MINOR_VERSION)' != '' \& '$(JDK_MICRO_VERSION)' != '' \))" "1" @@ -291,6 +312,7 @@ endif endif ADD_SA_BINARIES/ppc = +ADD_SA_BINARIES/ppc64 = ADD_SA_BINARIES/ia64 = ADD_SA_BINARIES/arm = ADD_SA_BINARIES/zero = diff -r 368df6a4b403 -r d982b0f5b59a make/linux/makefiles/gcc.make --- a/make/linux/makefiles/gcc.make Thu Apr 13 17:18:04 2017 -0400 +++ b/make/linux/makefiles/gcc.make Sun May 21 11:12:48 2017 -0700 @@ -109,6 +109,7 @@ ifndef E500V2 ARCHFLAG/ppc = -mcpu=powerpc endif +ARCHFLAG/ppc64 = -m64 CFLAGS += $(ARCHFLAG) AOUT_FLAGS += $(ARCHFLAG) @@ -237,6 +238,7 @@ DEBUG_CFLAGS/amd64 = -g DEBUG_CFLAGS/arm = -g DEBUG_CFLAGS/ppc = -g + DEBUG_CFLAGS/ppc64 = -g DEBUG_CFLAGS += $(DEBUG_CFLAGS/$(BUILDARCH)) ifeq ($(DEBUG_CFLAGS/$(BUILDARCH)),) DEBUG_CFLAGS += -gstabs @@ -247,6 +249,7 @@ FASTDEBUG_CFLAGS/amd64 = -g FASTDEBUG_CFLAGS/arm = -g FASTDEBUG_CFLAGS/ppc = -g + FASTDEBUG_CFLAGS/ppc64 = -g FASTDEBUG_CFLAGS += $(DEBUG_CFLAGS/$(BUILDARCH)) ifeq ($(FASTDEBUG_CFLAGS/$(BUILDARCH)),) FASTDEBUG_CFLAGS += -gstabs @@ -256,6 +259,7 @@ OPT_CFLAGS/amd64 = -g OPT_CFLAGS/arm = -g OPT_CFLAGS/ppc = -g + OPT_CFLAGS/ppc64 = -g OPT_CFLAGS += $(OPT_CFLAGS/$(BUILDARCH)) ifeq ($(OPT_CFLAGS/$(BUILDARCH)),) OPT_CFLAGS += -gstabs diff -r 368df6a4b403 -r d982b0f5b59a make/linux/makefiles/ppc64.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/makefiles/ppc64.make Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,76 @@ +# +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, 2013 SAP AG. 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. +# +# + +# make c code know it is on a 64 bit platform. +CFLAGS += -D_LP64=1 + +ifeq ($(origin OPENJDK_TARGET_CPU_ENDIAN),undefined) + # This can happen during hotspot standalone build. Set endianness from + # uname. We assume build and target machines are the same. + OPENJDK_TARGET_CPU_ENDIAN:=$(if $(filter ppc64le,$(shell uname -m)),little,big) +endif + +ifeq ($(filter $(OPENJDK_TARGET_CPU_ENDIAN),big little),) + $(error OPENJDK_TARGET_CPU_ENDIAN value should be 'big' or 'little') +endif + +ifeq ($(OPENJDK_TARGET_CPU_ENDIAN),big) + # produce 64 bits object files. + CFLAGS += -m64 + + # fixes `relocation truncated to fit' error for gcc 4.1. + CFLAGS += -mminimal-toc + + # finds use ppc64 instructions, but schedule for power5 + CFLAGS += -mcpu=powerpc64 -mtune=power5 -minsert-sched-nops=regroup_exact -mno-multiple -mno-string + + # PPC uses safefetch stubs. + CFLAGS += -DSAFEFETCH_STUBS + + # let linker produce 64 bit lib. + LFLAGS_VM += -m64 + + # let linker find external 64 bit libs. + LFLAGS_VM += -L/lib64 + + # specify lib format. + LFLAGS_VM += -Wl,-melf64ppc + + # also build launcher as 64 bit executable. + LAUNCHERFLAGS += -m64 + LAUNCHERFLAGS += -D_LP64=1 + AOUT_FLAGS += -m64 + AOUT_FLAGS += -L/lib64 + AOUT_FLAGS += -Wl,-melf64ppc +else + # Little endian machine uses ELFv2 ABI. + CFLAGS += -DVM_LITTLE_ENDIAN -DABI_ELFv2 + + # PPC uses safefetch stubs. TODO(asmundak): is this needed? + CFLAGS += -DSAFEFETCH_STUBS + + # Use Power8, this is the first CPU to support PPC64 LE with ELFv2 ABI. + CFLAGS += -mcpu=power7 -mtune=power8 -minsert-sched-nops=regroup_exact -mno-multiple -mno-string +endif diff -r 368df6a4b403 -r d982b0f5b59a make/linux/makefiles/vm.make --- a/make/linux/makefiles/vm.make Thu Apr 13 17:18:04 2017 -0400 +++ b/make/linux/makefiles/vm.make Sun May 21 11:12:48 2017 -0700 @@ -159,8 +159,8 @@ \( -name DUMMY $(foreach dir,$(SPECIAL_PATHS),-o -name $(dir)) \)) SOURCE_PATHS+=$(HS_COMMON_SRC)/os/$(Platform_os_family)/vm SOURCE_PATHS+=$(HS_COMMON_SRC)/os/posix/vm -SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(Platform_arch)/vm -SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_arch)/vm +SOURCE_PATHS+=$(HS_COMMON_SRC)/cpu/$(SRCARCH)/vm +SOURCE_PATHS+=$(HS_COMMON_SRC)/os_cpu/$(Platform_os_family)_$(SRCARCH)/vm ifeq ($(INCLUDE_TRACE), 1) SOURCE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ diff -r 368df6a4b403 -r d982b0f5b59a make/linux/platform_ppc --- a/make/linux/platform_ppc Thu Apr 13 17:18:04 2017 -0400 +++ b/make/linux/platform_ppc Sun May 21 11:12:48 2017 -0700 @@ -2,11 +2,11 @@ arch = ppc -arch_model = ppc +arch_model = ppc_32 os_arch = linux_ppc -os_arch_model = linux_ppc +os_arch_model = linux_ppc_32 lib_arch = ppc @@ -14,4 +14,4 @@ gnu_dis_arch = ppc -sysdefs = -DLINUX -D_GNU_SOURCE -DPPC +sysdefs = -DLINUX -D_GNU_SOURCE -DPPC32 diff -r 368df6a4b403 -r d982b0f5b59a make/linux/platform_ppc64 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/platform_ppc64 Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,17 @@ +os_family = linux + +arch = ppc + +arch_model = ppc_64 + +os_arch = linux_ppc + +os_arch_model = linux_ppc_64 + +lib_arch = ppc64 + +compiler = gcc + +gnu_dis_arch = ppc64 + +sysdefs = -DLINUX -D_GNU_SOURCE -DPPC64 diff -r 368df6a4b403 -r d982b0f5b59a make/solaris/Makefile --- a/make/solaris/Makefile Thu Apr 13 17:18:04 2017 -0400 +++ b/make/solaris/Makefile Sun May 21 11:12:48 2017 -0700 @@ -231,28 +231,36 @@ $(TARGETS_C2): $(SUBDIRS_C2) cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install endif $(TARGETS_TIERED): $(SUBDIRS_TIERED) cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_C1): $(SUBDIRS_C1) cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install endif $(TARGETS_CORE): $(SUBDIRS_CORE) cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) +ifeq ($(TEST_IN_BUILD),true) cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && ./test_gamma +endif ifdef INSTALL cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install endif diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/assembler_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/assembler_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,700 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "assembler_ppc.inline.hpp" +#include "gc_interface/collectedHeap.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "memory/cardTableModRefBS.hpp" +#include "memory/resourceArea.hpp" +#include "prims/methodHandles.hpp" +#include "runtime/biasedLocking.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/objectMonitor.hpp" +#include "runtime/os.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/macros.hpp" +#ifndef SERIALGC +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#endif + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) block_comment(str) +#endif + +int AbstractAssembler::code_fill_byte() { + return 0x00; // illegal instruction 0x00000000 +} + +void Assembler::print_instruction(int inst) { + Unimplemented(); +} + +// Patch instruction `inst' at offset `inst_pos' to refer to +// `dest_pos' and return the resulting instruction. We should have +// pcs, not offsets, but since all is relative, it will work out fine. +int Assembler::patched_branch(int dest_pos, int inst, int inst_pos) { + int m = 0; // mask for displacement field + int v = 0; // new value for displacement field + + switch (inv_op_ppc(inst)) { + case b_op: m = li(-1); v = li(disp(dest_pos, inst_pos)); break; + case bc_op: m = bd(-1); v = bd(disp(dest_pos, inst_pos)); break; + default: ShouldNotReachHere(); + } + return inst & ~m | v; +} + +// Return the offset, relative to _code_begin, of the destination of +// the branch inst at offset pos. +int Assembler::branch_destination(int inst, int pos) { + int r = 0; + switch (inv_op_ppc(inst)) { + case b_op: r = bxx_destination_offset(inst, pos); break; + case bc_op: r = inv_bd_field(inst, pos); break; + default: ShouldNotReachHere(); + } + return r; +} + +// Low-level andi-one-instruction-macro. +void Assembler::andi(Register a, Register s, const int ui16) { + assert(is_uimm(ui16, 16), "must be 16-bit unsigned immediate"); + if (is_power_of_2_long(((jlong) ui16)+1)) { + // pow2minus1 + clrldi(a, s, 64-log2_long((((jlong) ui16)+1))); + } else if (is_power_of_2_long((jlong) ui16)) { + // pow2 + rlwinm(a, s, 0, 31-log2_long((jlong) ui16), 31-log2_long((jlong) ui16)); + } else if (is_power_of_2_long((jlong)-ui16)) { + // negpow2 + clrrdi(a, s, log2_long((jlong)-ui16)); + } else { + andi_(a, s, ui16); + } +} + +// RegisterOrConstant version. +void Assembler::ld(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + if (s1 == noreg) { + int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true); + Assembler::ld(d, simm16_rest, d); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::ld(d, roc.as_constant(), s1); + } else { + load_const_optimized(d, roc.as_constant()); + Assembler::ldx(d, d, s1); + } + } else { + if (s1 == noreg) + Assembler::ld(d, 0, roc.as_register()); + else + Assembler::ldx(d, roc.as_register(), s1); + } +} + +void Assembler::lwa(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + if (s1 == noreg) { + int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true); + Assembler::lwa(d, simm16_rest, d); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::lwa(d, roc.as_constant(), s1); + } else { + load_const_optimized(d, roc.as_constant()); + Assembler::lwax(d, d, s1); + } + } else { + if (s1 == noreg) + Assembler::lwa(d, 0, roc.as_register()); + else + Assembler::lwax(d, roc.as_register(), s1); + } +} + +void Assembler::lwz(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + if (s1 == noreg) { + int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true); + Assembler::lwz(d, simm16_rest, d); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::lwz(d, roc.as_constant(), s1); + } else { + load_const_optimized(d, roc.as_constant()); + Assembler::lwzx(d, d, s1); + } + } else { + if (s1 == noreg) + Assembler::lwz(d, 0, roc.as_register()); + else + Assembler::lwzx(d, roc.as_register(), s1); + } +} + +void Assembler::lha(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + if (s1 == noreg) { + int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true); + Assembler::lha(d, simm16_rest, d); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::lha(d, roc.as_constant(), s1); + } else { + load_const_optimized(d, roc.as_constant()); + Assembler::lhax(d, d, s1); + } + } else { + if (s1 == noreg) + Assembler::lha(d, 0, roc.as_register()); + else + Assembler::lhax(d, roc.as_register(), s1); + } +} + +void Assembler::lhz(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + if (s1 == noreg) { + int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true); + Assembler::lhz(d, simm16_rest, d); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::lhz(d, roc.as_constant(), s1); + } else { + load_const_optimized(d, roc.as_constant()); + Assembler::lhzx(d, d, s1); + } + } else { + if (s1 == noreg) + Assembler::lhz(d, 0, roc.as_register()); + else + Assembler::lhzx(d, roc.as_register(), s1); + } +} + +void Assembler::lbz(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + if (s1 == noreg) { + int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true); + Assembler::lbz(d, simm16_rest, d); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::lbz(d, roc.as_constant(), s1); + } else { + load_const_optimized(d, roc.as_constant()); + Assembler::lbzx(d, d, s1); + } + } else { + if (s1 == noreg) + Assembler::lbz(d, 0, roc.as_register()); + else + Assembler::lbzx(d, roc.as_register(), s1); + } +} + +void Assembler::std(Register d, RegisterOrConstant roc, Register s1, Register tmp) { + if (roc.is_constant()) { + if (s1 == noreg) { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true); + Assembler::std(d, simm16_rest, tmp); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::std(d, roc.as_constant(), s1); + } else { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + load_const_optimized(tmp, roc.as_constant()); + Assembler::stdx(d, tmp, s1); + } + } else { + if (s1 == noreg) + Assembler::std(d, 0, roc.as_register()); + else + Assembler::stdx(d, roc.as_register(), s1); + } +} + +void Assembler::stw(Register d, RegisterOrConstant roc, Register s1, Register tmp) { + if (roc.is_constant()) { + if (s1 == noreg) { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true); + Assembler::stw(d, simm16_rest, tmp); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::stw(d, roc.as_constant(), s1); + } else { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + load_const_optimized(tmp, roc.as_constant()); + Assembler::stwx(d, tmp, s1); + } + } else { + if (s1 == noreg) + Assembler::stw(d, 0, roc.as_register()); + else + Assembler::stwx(d, roc.as_register(), s1); + } +} + +void Assembler::sth(Register d, RegisterOrConstant roc, Register s1, Register tmp) { + if (roc.is_constant()) { + if (s1 == noreg) { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true); + Assembler::sth(d, simm16_rest, tmp); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::sth(d, roc.as_constant(), s1); + } else { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + load_const_optimized(tmp, roc.as_constant()); + Assembler::sthx(d, tmp, s1); + } + } else { + if (s1 == noreg) + Assembler::sth(d, 0, roc.as_register()); + else + Assembler::sthx(d, roc.as_register(), s1); + } +} + +void Assembler::stb(Register d, RegisterOrConstant roc, Register s1, Register tmp) { + if (roc.is_constant()) { + if (s1 == noreg) { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true); + Assembler::stb(d, simm16_rest, tmp); + } else if (is_simm(roc.as_constant(), 16)) { + Assembler::stb(d, roc.as_constant(), s1); + } else { + guarantee(tmp != noreg, "Need tmp reg to encode large constants"); + load_const_optimized(tmp, roc.as_constant()); + Assembler::stbx(d, tmp, s1); + } + } else { + if (s1 == noreg) + Assembler::stb(d, 0, roc.as_register()); + else + Assembler::stbx(d, roc.as_register(), s1); + } +} + +void Assembler::add(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + intptr_t c = roc.as_constant(); + assert(is_simm(c, 16), "too big"); + addi(d, s1, (int)c); + } + else add(d, roc.as_register(), s1); +} + +void Assembler::subf(Register d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + intptr_t c = roc.as_constant(); + assert(is_simm(-c, 16), "too big"); + addi(d, s1, (int)-c); + } + else subf(d, roc.as_register(), s1); +} + +void Assembler::cmpd(ConditionRegister d, RegisterOrConstant roc, Register s1) { + if (roc.is_constant()) { + intptr_t c = roc.as_constant(); + assert(is_simm(c, 16), "too big"); + cmpdi(d, s1, (int)c); + } + else cmpd(d, roc.as_register(), s1); +} + +// Load a 64 bit constant. Patchable. +void Assembler::load_const(Register d, long x, Register tmp) { + // 64-bit value: x = xa xb xc xd + int xa = (x >> 48) & 0xffff; + int xb = (x >> 32) & 0xffff; + int xc = (x >> 16) & 0xffff; + int xd = (x >> 0) & 0xffff; + if (tmp == noreg) { + Assembler::lis( d, (int)(short)xa); + Assembler::ori( d, d, (unsigned int)xb); + Assembler::sldi(d, d, 32); + Assembler::oris(d, d, (unsigned int)xc); + Assembler::ori( d, d, (unsigned int)xd); + } else { + // exploit instruction level parallelism if we have a tmp register + assert_different_registers(d, tmp); + Assembler::lis(tmp, (int)(short)xa); + Assembler::lis(d, (int)(short)xc); + Assembler::ori(tmp, tmp, (unsigned int)xb); + Assembler::ori(d, d, (unsigned int)xd); + Assembler::insrdi(d, tmp, 32, 0); + } +} + +// Load a 64 bit constant, optimized, not identifyable. +// Tmp can be used to increase ILP. Set return_simm16_rest=true to get a +// 16 bit immediate offset. +int Assembler::load_const_optimized(Register d, long x, Register tmp, bool return_simm16_rest) { + // Avoid accidentally trying to use R0 for indexed addressing. + assert(d != R0, "R0 not allowed"); + assert_different_registers(d, tmp); + + short xa, xb, xc, xd; // Four 16-bit chunks of const. + long rem = x; // Remaining part of const. + + xd = rem & 0xFFFF; // Lowest 16-bit chunk. + rem = (rem >> 16) + ((unsigned short)xd >> 15); // Compensation for sign extend. + + if (rem == 0) { // opt 1: simm16 + li(d, xd); + return 0; + } + + xc = rem & 0xFFFF; // Next 16-bit chunk. + rem = (rem >> 16) + ((unsigned short)xc >> 15); // Compensation for sign extend. + + if (rem == 0) { // opt 2: simm32 + lis(d, xc); + } else { // High 32 bits needed. + + if (tmp != noreg) { // opt 3: We have a temp reg. + // No carry propagation between xc and higher chunks here (use logical instructions). + xa = (x >> 48) & 0xffff; + xb = (x >> 32) & 0xffff; // No sign compensation, we use lis+ori or li to allow usage of R0. + bool load_xa = (xa != 0) || (xb < 0); + bool return_xd = false; + + if (load_xa) { lis(tmp, xa); } + if (xc) { lis(d, xc); } + if (load_xa) { + if (xb) { ori(tmp, tmp, (unsigned short)xb); } // No addi, we support tmp == R0. + } else { + li(tmp, xb); // non-negative + } + if (xc) { + if (return_simm16_rest && xd >= 0) { return_xd = true; } // >= 0 to avoid carry propagation after insrdi/rldimi. + else if (xd) { addi(d, d, xd); } + } else { + li(d, xd); + } + insrdi(d, tmp, 32, 0); + return return_xd ? xd : 0; // non-negative + } + + xb = rem & 0xFFFF; // Next 16-bit chunk. + rem = (rem >> 16) + ((unsigned short)xb >> 15); // Compensation for sign extend. + + xa = rem & 0xFFFF; // Highest 16-bit chunk. + + // opt 4: avoid adding 0 + if (xa) { // Highest 16-bit needed? + lis(d, xa); + if (xb) { addi(d, d, xb); } + } else { + li(d, xb); + } + sldi(d, d, 32); + if (xc) { addis(d, d, xc); } + } + + // opt 5: Return offset to be inserted into following instruction. + if (return_simm16_rest) return xd; + + if (xd) { addi(d, d, xd); } + return 0; +} + +#ifndef PRODUCT +// Test of ppc assembler. +void Assembler::test_asm() { + // PPC 1, section 3.3.8, Fixed-Point Arithmetic Instructions + addi( R0, R1, 10); + addis( R5, R2, 11); + addic_( R3, R31, 42); + subfic( R21, R12, 2112); + add( R3, R2, R1); + add_( R11, R22, R30); + subf( R7, R6, R5); + subf_( R8, R9, R4); + addc( R11, R12, R13); + addc_( R14, R14, R14); + subfc( R15, R16, R17); + subfc_( R18, R20, R19); + adde( R20, R22, R24); + adde_( R29, R27, R26); + subfe( R28, R1, R0); + subfe_( R21, R11, R29); + neg( R21, R22); + neg_( R13, R23); + mulli( R0, R11, -31); + mulld( R1, R18, R21); + mulld_( R2, R17, R22); + mullw( R3, R16, R23); + mullw_( R4, R15, R24); + divd( R5, R14, R25); + divd_( R6, R13, R26); + divw( R7, R12, R27); + divw_( R8, R11, R28); + + li( R3, -4711); + + // PPC 1, section 3.3.9, Fixed-Point Compare Instructions + cmpi( CCR7, 0, R27, 4711); + cmp( CCR0, 1, R14, R11); + cmpli( CCR5, 1, R17, 45); + cmpl( CCR3, 0, R9, R10); + + cmpwi( CCR7, R27, 4711); + cmpw( CCR0, R14, R11); + cmplwi( CCR5, R17, 45); + cmplw( CCR3, R9, R10); + + cmpdi( CCR7, R27, 4711); + cmpd( CCR0, R14, R11); + cmpldi( CCR5, R17, 45); + cmpld( CCR3, R9, R10); + + // PPC 1, section 3.3.11, Fixed-Point Logical Instructions + andi_( R4, R5, 0xff); + andis_( R12, R13, 0x7b51); + ori( R1, R4, 13); + oris( R3, R5, 177); + xori( R7, R6, 51); + xoris( R29, R0, 1); + andr( R17, R21, R16); + and_( R3, R5, R15); + orr( R2, R1, R9); + or_( R17, R15, R11); + xorr( R19, R18, R10); + xor_( R31, R21, R11); + nand( R5, R7, R3); + nand_( R3, R1, R0); + nor( R2, R3, R5); + nor_( R3, R6, R8); + andc( R25, R12, R11); + andc_( R24, R22, R21); + orc( R20, R10, R12); + orc_( R22, R2, R13); + + nop(); + + // PPC 1, section 3.3.12, Fixed-Point Rotate and Shift Instructions + sld( R5, R6, R8); + sld_( R3, R5, R9); + slw( R2, R1, R10); + slw_( R6, R26, R16); + srd( R16, R24, R8); + srd_( R21, R14, R7); + srw( R22, R25, R29); + srw_( R5, R18, R17); + srad( R7, R11, R0); + srad_( R9, R13, R1); + sraw( R7, R15, R2); + sraw_( R4, R17, R3); + sldi( R3, R18, 63); + sldi_( R2, R20, 30); + slwi( R1, R21, 30); + slwi_( R7, R23, 8); + srdi( R0, R19, 2); + srdi_( R12, R24, 5); + srwi( R13, R27, 6); + srwi_( R14, R29, 7); + sradi( R15, R30, 9); + sradi_( R16, R31, 19); + srawi( R17, R31, 15); + srawi_( R18, R31, 12); + + clrrdi( R3, R30, 5); + clrldi( R9, R10, 11); + + rldicr( R19, R20, 13, 15); + rldicr_(R20, R20, 16, 14); + rldicl( R21, R21, 30, 33); + rldicl_(R22, R1, 20, 25); + rlwinm( R23, R2, 25, 10, 11); + rlwinm_(R24, R3, 12, 13, 14); + + // PPC 1, section 3.3.2 Fixed-Point Load Instructions + lwzx( R3, R5, R7); + lwz( R11, 0, R1); + lwzu( R31, -4, R11); + + lwax( R3, R5, R7); + lwa( R31, -4, R11); + lhzx( R3, R5, R7); + lhz( R31, -4, R11); + lhzu( R31, -4, R11); + + + lhax( R3, R5, R7); + lha( R31, -4, R11); + lhau( R11, 0, R1); + + lbzx( R3, R5, R7); + lbz( R31, -4, R11); + lbzu( R11, 0, R1); + + ld( R31, -4, R11); + ldx( R3, R5, R7); + ldu( R31, -4, R11); + + // PPC 1, section 3.3.3 Fixed-Point Store Instructions + stwx( R3, R5, R7); + stw( R31, -4, R11); + stwu( R11, 0, R1); + + sthx( R3, R5, R7 ); + sth( R31, -4, R11); + sthu( R31, -4, R11); + + stbx( R3, R5, R7); + stb( R31, -4, R11); + stbu( R31, -4, R11); + + std( R31, -4, R11); + stdx( R3, R5, R7); + stdu( R31, -4, R11); + + // PPC 1, section 3.3.13 Move To/From System Register Instructions + mtlr( R3); + mflr( R3); + mtctr( R3); + mfctr( R3); + mtcrf( 0xff, R15); + mtcr( R15); + mtcrf( 0x03, R15); + mtcr( R15); + mfcr( R15); + + // PPC 1, section 2.4.1 Branch Instructions + Label lbl1, lbl2, lbl3; + bind(lbl1); + + b(pc()); + b(pc() - 8); + b(lbl1); + b(lbl2); + b(lbl3); + + bl(pc() - 8); + bl(lbl1); + bl(lbl2); + + bcl(4, 10, pc() - 8); + bcl(4, 10, lbl1); + bcl(4, 10, lbl2); + + bclr( 4, 6, 0); + bclrl(4, 6, 0); + + bind(lbl2); + + bcctr( 4, 6, 0); + bcctrl(4, 6, 0); + + blt(CCR0, lbl2); + bgt(CCR1, lbl2); + beq(CCR2, lbl2); + bso(CCR3, lbl2); + bge(CCR4, lbl2); + ble(CCR5, lbl2); + bne(CCR6, lbl2); + bns(CCR7, lbl2); + + bltl(CCR0, lbl2); + bgtl(CCR1, lbl2); + beql(CCR2, lbl2); + bsol(CCR3, lbl2); + bgel(CCR4, lbl2); + blel(CCR5, lbl2); + bnel(CCR6, lbl2); + bnsl(CCR7, lbl2); + blr(); + + sync(); + icbi( R1, R2); + dcbst(R2, R3); + + // FLOATING POINT instructions ppc. + // PPC 1, section 4.6.2 Floating-Point Load Instructions + lfs( F1, -11, R3); + lfsu(F2, 123, R4); + lfsx(F3, R5, R6); + lfd( F4, 456, R7); + lfdu(F5, 789, R8); + lfdx(F6, R10, R11); + + // PPC 1, section 4.6.3 Floating-Point Store Instructions + stfs( F7, 876, R12); + stfsu( F8, 543, R13); + stfsx( F9, R14, R15); + stfd( F10, 210, R16); + stfdu( F11, 111, R17); + stfdx( F12, R18, R19); + + // PPC 1, section 4.6.4 Floating-Point Move Instructions + fmr( F13, F14); + fmr_( F14, F15); + fneg( F16, F17); + fneg_( F18, F19); + fabs( F20, F21); + fabs_( F22, F23); + fnabs( F24, F25); + fnabs_(F26, F27); + + // PPC 1, section 4.6.5.1 Floating-Point Elementary Arithmetic + // Instructions + fadd( F28, F29, F30); + fadd_( F31, F0, F1); + fadds( F2, F3, F4); + fadds_(F5, F6, F7); + fsub( F8, F9, F10); + fsub_( F11, F12, F13); + fsubs( F14, F15, F16); + fsubs_(F17, F18, F19); + fmul( F20, F21, F22); + fmul_( F23, F24, F25); + fmuls( F26, F27, F28); + fmuls_(F29, F30, F31); + fdiv( F0, F1, F2); + fdiv_( F3, F4, F5); + fdivs( F6, F7, F8); + fdivs_(F9, F10, F11); + + // PPC 1, section 4.6.6 Floating-Point Rounding and Conversion + // Instructions + frsp( F12, F13); + fctid( F14, F15); + fctidz(F16, F17); + fctiw( F18, F19); + fctiwz(F20, F21); + fcfid( F22, F23); + + // PPC 1, section 4.6.7 Floating-Point Compare Instructions + fcmpu( CCR7, F24, F25); + + tty->print_cr("\ntest_asm disassembly (0x%lx 0x%lx):", code()->insts_begin(), code()->insts_end()); + code()->decode(); +} + +#endif // !PRODUCT diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/assembler_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/assembler_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,2000 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_ASSEMBLER_PPC_HPP +#define CPU_PPC_VM_ASSEMBLER_PPC_HPP + +class ciTypeArray; + +#include "asm/assembler.hpp" +#include "utilities/sizes.hpp" + +// Address is an abstraction used to represent a memory location +// as used in assembler instructions. +// PPC instructions grok either baseReg + indexReg or baseReg + disp. +// So far we do not use this as simplification by this class is low +// on PPC with its simple addressing mode. Use RegisterOrConstant to +// represent an offset. +class Address VALUE_OBJ_CLASS_SPEC { +}; + +class AddressLiteral VALUE_OBJ_CLASS_SPEC { + private: + address _address; + RelocationHolder _rspec; + + RelocationHolder rspec_from_rtype(relocInfo::relocType rtype, address addr) { + switch (rtype) { + case relocInfo::external_word_type: + return external_word_Relocation::spec(addr); + case relocInfo::internal_word_type: + return internal_word_Relocation::spec(addr); + case relocInfo::opt_virtual_call_type: + return opt_virtual_call_Relocation::spec(); + case relocInfo::static_call_type: + return static_call_Relocation::spec(); + case relocInfo::runtime_call_type: + return runtime_call_Relocation::spec(); + case relocInfo::none: + return RelocationHolder(); + default: + ShouldNotReachHere(); + return RelocationHolder(); + } + } + + protected: + // creation + AddressLiteral() : _address(NULL), _rspec(NULL) {} + + public: + AddressLiteral(address addr, RelocationHolder const& rspec) + : _address(addr), + _rspec(rspec) {} + + AddressLiteral(address addr, relocInfo::relocType rtype = relocInfo::none) + : _address((address) addr), + _rspec(rspec_from_rtype(rtype, (address) addr)) {} + + AddressLiteral(oop* addr, relocInfo::relocType rtype = relocInfo::none) + : _address((address) addr), + _rspec(rspec_from_rtype(rtype, (address) addr)) {} + + intptr_t value() const { return (intptr_t) _address; } + + const RelocationHolder& rspec() const { return _rspec; } +}; + +// Argument is an abstraction used to represent an outgoing +// actual argument or an incoming formal parameter, whether +// it resides in memory or in a register, in a manner consistent +// with the PPC Application Binary Interface, or ABI. This is +// often referred to as the native or C calling convention. + +class Argument VALUE_OBJ_CLASS_SPEC { + private: + int _number; // The number of the argument. + public: + enum { + // Only 8 registers may contain integer parameters. + n_register_parameters = 8, + // Can have up to 8 floating registers. + n_float_register_parameters = 8, + + // PPC C calling conventions. + // The first eight arguments are passed in int regs if they are int. + n_int_register_parameters_c = 8, + // The first thirteen float arguments are passed in float regs. + n_float_register_parameters_c = 13, + // Only the first 8 parameters are not placed on the stack. Aix disassembly + // shows that xlC places all float args after argument 8 on the stack AND + // in a register. This is not documented, but we follow this convention, too. + n_regs_not_on_stack_c = 8, + }; + // creation + Argument(int number) : _number(number) {} + + int number() const { return _number; } + + // Locating register-based arguments: + bool is_register() const { return _number < n_register_parameters; } + + Register as_register() const { + assert(is_register(), "must be a register argument"); + return as_Register(number() + R3_ARG1->encoding()); + } +}; + +#if !defined(ABI_ELFv2) +// A ppc64 function descriptor. +struct FunctionDescriptor VALUE_OBJ_CLASS_SPEC { + private: + address _entry; + address _toc; + address _env; + + public: + inline address entry() const { return _entry; } + inline address toc() const { return _toc; } + inline address env() const { return _env; } + + inline void set_entry(address entry) { _entry = entry; } + inline void set_toc( address toc) { _toc = toc; } + inline void set_env( address env) { _env = env; } + + inline static ByteSize entry_offset() { return byte_offset_of(FunctionDescriptor, _entry); } + inline static ByteSize toc_offset() { return byte_offset_of(FunctionDescriptor, _toc); } + inline static ByteSize env_offset() { return byte_offset_of(FunctionDescriptor, _env); } + + // Friend functions can be called without loading toc and env. + enum { + friend_toc = 0xcafe, + friend_env = 0xc0de + }; + + inline bool is_friend_function() const { + return (toc() == (address) friend_toc) && (env() == (address) friend_env); + } + + // Constructor for stack-allocated instances. + FunctionDescriptor() { + _entry = (address) 0xbad; + _toc = (address) 0xbad; + _env = (address) 0xbad; + } +}; +#endif + +class Assembler : public AbstractAssembler { + protected: + // Displacement routines + static void print_instruction(int inst); + static int patched_branch(int dest_pos, int inst, int inst_pos); + static int branch_destination(int inst, int pos); + + friend class AbstractAssembler; + + // Code patchers need various routines like inv_wdisp() + friend class NativeInstruction; + friend class NativeGeneralJump; + friend class Relocation; + + public: + + enum shifts { + XO_21_29_SHIFT = 2, + XO_21_30_SHIFT = 1, + XO_27_29_SHIFT = 2, + XO_30_31_SHIFT = 0, + SPR_5_9_SHIFT = 11u, // SPR_5_9 field in bits 11 -- 15 + SPR_0_4_SHIFT = 16u, // SPR_0_4 field in bits 16 -- 20 + RS_SHIFT = 21u, // RS field in bits 21 -- 25 + OPCODE_SHIFT = 26u, // opcode in bits 26 -- 31 + }; + + enum opcdxos_masks { + XL_FORM_OPCODE_MASK = (63u << OPCODE_SHIFT) | (1023u << 1), + ADDI_OPCODE_MASK = (63u << OPCODE_SHIFT), + ADDIS_OPCODE_MASK = (63u << OPCODE_SHIFT), + BXX_OPCODE_MASK = (63u << OPCODE_SHIFT), + BCXX_OPCODE_MASK = (63u << OPCODE_SHIFT), + // trap instructions + TDI_OPCODE_MASK = (63u << OPCODE_SHIFT), + TWI_OPCODE_MASK = (63u << OPCODE_SHIFT), + TD_OPCODE_MASK = (63u << OPCODE_SHIFT) | (1023u << 1), + TW_OPCODE_MASK = (63u << OPCODE_SHIFT) | (1023u << 1), + LD_OPCODE_MASK = (63u << OPCODE_SHIFT) | (3u << XO_30_31_SHIFT), // DS-FORM + STD_OPCODE_MASK = LD_OPCODE_MASK, + STDU_OPCODE_MASK = STD_OPCODE_MASK, + STDX_OPCODE_MASK = (63u << OPCODE_SHIFT) | (1023u << 1), + STDUX_OPCODE_MASK = STDX_OPCODE_MASK, + STW_OPCODE_MASK = (63u << OPCODE_SHIFT), + STWU_OPCODE_MASK = STW_OPCODE_MASK, + STWX_OPCODE_MASK = (63u << OPCODE_SHIFT) | (1023u << 1), + STWUX_OPCODE_MASK = STWX_OPCODE_MASK, + MTCTR_OPCODE_MASK = ~(31u << RS_SHIFT), + ORI_OPCODE_MASK = (63u << OPCODE_SHIFT), + ORIS_OPCODE_MASK = (63u << OPCODE_SHIFT), + RLDICR_OPCODE_MASK = (63u << OPCODE_SHIFT) | (7u << XO_27_29_SHIFT) + }; + + enum opcdxos { + ADD_OPCODE = (31u << OPCODE_SHIFT | 266u << 1), + ADDC_OPCODE = (31u << OPCODE_SHIFT | 10u << 1), + ADDI_OPCODE = (14u << OPCODE_SHIFT), + ADDIS_OPCODE = (15u << OPCODE_SHIFT), + ADDIC__OPCODE = (13u << OPCODE_SHIFT), + ADDE_OPCODE = (31u << OPCODE_SHIFT | 138u << 1), + SUBF_OPCODE = (31u << OPCODE_SHIFT | 40u << 1), + SUBFC_OPCODE = (31u << OPCODE_SHIFT | 8u << 1), + SUBFE_OPCODE = (31u << OPCODE_SHIFT | 136u << 1), + SUBFIC_OPCODE = (8u << OPCODE_SHIFT), + SUBFZE_OPCODE = (31u << OPCODE_SHIFT | 200u << 1), + DIVW_OPCODE = (31u << OPCODE_SHIFT | 491u << 1), + MULLW_OPCODE = (31u << OPCODE_SHIFT | 235u << 1), + MULHW_OPCODE = (31u << OPCODE_SHIFT | 75u << 1), + MULHWU_OPCODE = (31u << OPCODE_SHIFT | 11u << 1), + MULLI_OPCODE = (7u << OPCODE_SHIFT), + AND_OPCODE = (31u << OPCODE_SHIFT | 28u << 1), + ANDI_OPCODE = (28u << OPCODE_SHIFT), + ANDIS_OPCODE = (29u << OPCODE_SHIFT), + ANDC_OPCODE = (31u << OPCODE_SHIFT | 60u << 1), + ORC_OPCODE = (31u << OPCODE_SHIFT | 412u << 1), + OR_OPCODE = (31u << OPCODE_SHIFT | 444u << 1), + ORI_OPCODE = (24u << OPCODE_SHIFT), + ORIS_OPCODE = (25u << OPCODE_SHIFT), + XOR_OPCODE = (31u << OPCODE_SHIFT | 316u << 1), + XORI_OPCODE = (26u << OPCODE_SHIFT), + XORIS_OPCODE = (27u << OPCODE_SHIFT), + + NEG_OPCODE = (31u << OPCODE_SHIFT | 104u << 1), + + RLWINM_OPCODE = (21u << OPCODE_SHIFT), + CLRRWI_OPCODE = RLWINM_OPCODE, + CLRLWI_OPCODE = RLWINM_OPCODE, + + RLWIMI_OPCODE = (20u << OPCODE_SHIFT), + + SLW_OPCODE = (31u << OPCODE_SHIFT | 24u << 1), + SLWI_OPCODE = RLWINM_OPCODE, + SRW_OPCODE = (31u << OPCODE_SHIFT | 536u << 1), + SRWI_OPCODE = RLWINM_OPCODE, + SRAW_OPCODE = (31u << OPCODE_SHIFT | 792u << 1), + SRAWI_OPCODE = (31u << OPCODE_SHIFT | 824u << 1), + + CMP_OPCODE = (31u << OPCODE_SHIFT | 0u << 1), + CMPI_OPCODE = (11u << OPCODE_SHIFT), + CMPL_OPCODE = (31u << OPCODE_SHIFT | 32u << 1), + CMPLI_OPCODE = (10u << OPCODE_SHIFT), + + ISEL_OPCODE = (31u << OPCODE_SHIFT | 15u << 1), + + MTLR_OPCODE = (31u << OPCODE_SHIFT | 467u << 1 | 8 << SPR_0_4_SHIFT), + MFLR_OPCODE = (31u << OPCODE_SHIFT | 339u << 1 | 8 << SPR_0_4_SHIFT), + + MTCRF_OPCODE = (31u << OPCODE_SHIFT | 144u << 1), + MFCR_OPCODE = (31u << OPCODE_SHIFT | 19u << 1), + MCRF_OPCODE = (19u << OPCODE_SHIFT | 0u << 1), + + // condition register logic instructions + CRAND_OPCODE = (19u << OPCODE_SHIFT | 257u << 1), + CRNAND_OPCODE = (19u << OPCODE_SHIFT | 225u << 1), + CROR_OPCODE = (19u << OPCODE_SHIFT | 449u << 1), + CRXOR_OPCODE = (19u << OPCODE_SHIFT | 193u << 1), + CRNOR_OPCODE = (19u << OPCODE_SHIFT | 33u << 1), + CREQV_OPCODE = (19u << OPCODE_SHIFT | 289u << 1), + CRANDC_OPCODE = (19u << OPCODE_SHIFT | 129u << 1), + CRORC_OPCODE = (19u << OPCODE_SHIFT | 417u << 1), + + BCLR_OPCODE = (19u << OPCODE_SHIFT | 16u << 1), + BXX_OPCODE = (18u << OPCODE_SHIFT), + BCXX_OPCODE = (16u << OPCODE_SHIFT), + + // CTR-related opcodes + BCCTR_OPCODE = (19u << OPCODE_SHIFT | 528u << 1), + MTCTR_OPCODE = (31u << OPCODE_SHIFT | 467u << 1 | 9 << SPR_0_4_SHIFT), + MFCTR_OPCODE = (31u << OPCODE_SHIFT | 339u << 1 | 9 << SPR_0_4_SHIFT), + + + LWZ_OPCODE = (32u << OPCODE_SHIFT), + LWZX_OPCODE = (31u << OPCODE_SHIFT | 23u << 1), + LWZU_OPCODE = (33u << OPCODE_SHIFT), + LWBRX_OPCODE = (31u << OPCODE_SHIFT | 534 << 1), + + LHA_OPCODE = (42u << OPCODE_SHIFT), + LHAX_OPCODE = (31u << OPCODE_SHIFT | 343u << 1), + LHAU_OPCODE = (43u << OPCODE_SHIFT), + + LHZ_OPCODE = (40u << OPCODE_SHIFT), + LHZX_OPCODE = (31u << OPCODE_SHIFT | 279u << 1), + LHZU_OPCODE = (41u << OPCODE_SHIFT), + LHBRX_OPCODE = (31u << OPCODE_SHIFT | 790 << 1), + + LBZ_OPCODE = (34u << OPCODE_SHIFT), + LBZX_OPCODE = (31u << OPCODE_SHIFT | 87u << 1), + LBZU_OPCODE = (35u << OPCODE_SHIFT), + + STW_OPCODE = (36u << OPCODE_SHIFT), + STWX_OPCODE = (31u << OPCODE_SHIFT | 151u << 1), + STWU_OPCODE = (37u << OPCODE_SHIFT), + STWUX_OPCODE = (31u << OPCODE_SHIFT | 183u << 1), + + STH_OPCODE = (44u << OPCODE_SHIFT), + STHX_OPCODE = (31u << OPCODE_SHIFT | 407u << 1), + STHU_OPCODE = (45u << OPCODE_SHIFT), + + STB_OPCODE = (38u << OPCODE_SHIFT), + STBX_OPCODE = (31u << OPCODE_SHIFT | 215u << 1), + STBU_OPCODE = (39u << OPCODE_SHIFT), + + EXTSB_OPCODE = (31u << OPCODE_SHIFT | 954u << 1), + EXTSH_OPCODE = (31u << OPCODE_SHIFT | 922u << 1), + EXTSW_OPCODE = (31u << OPCODE_SHIFT | 986u << 1), // X-FORM + + // 32 bit opcode encodings + + LWA_OPCODE = (58u << OPCODE_SHIFT | 2u << XO_30_31_SHIFT), // DS-FORM + LWAX_OPCODE = (31u << OPCODE_SHIFT | 341u << XO_21_30_SHIFT), // X-FORM + + CNTLZW_OPCODE = (31u << OPCODE_SHIFT | 26u << XO_21_30_SHIFT), // X-FORM + + // 64 bit opcode encodings + + LD_OPCODE = (58u << OPCODE_SHIFT | 0u << XO_30_31_SHIFT), // DS-FORM + LDU_OPCODE = (58u << OPCODE_SHIFT | 1u << XO_30_31_SHIFT), // DS-FORM + LDX_OPCODE = (31u << OPCODE_SHIFT | 21u << XO_21_30_SHIFT), // X-FORM + + STD_OPCODE = (62u << OPCODE_SHIFT | 0u << XO_30_31_SHIFT), // DS-FORM + STDU_OPCODE = (62u << OPCODE_SHIFT | 1u << XO_30_31_SHIFT), // DS-FORM + STDUX_OPCODE = (31u << OPCODE_SHIFT | 181u << 1), // X-FORM + STDX_OPCODE = (31u << OPCODE_SHIFT | 149u << XO_21_30_SHIFT), // X-FORM + + RLDICR_OPCODE = (30u << OPCODE_SHIFT | 1u << XO_27_29_SHIFT), // MD-FORM + RLDICL_OPCODE = (30u << OPCODE_SHIFT | 0u << XO_27_29_SHIFT), // MD-FORM + RLDIC_OPCODE = (30u << OPCODE_SHIFT | 2u << XO_27_29_SHIFT), // MD-FORM + RLDIMI_OPCODE = (30u << OPCODE_SHIFT | 3u << XO_27_29_SHIFT), // MD-FORM + + SRADI_OPCODE = (31u << OPCODE_SHIFT | 413u << XO_21_29_SHIFT), // XS-FORM + + SLD_OPCODE = (31u << OPCODE_SHIFT | 27u << 1), // X-FORM + SRD_OPCODE = (31u << OPCODE_SHIFT | 539u << 1), // X-FORM + SRAD_OPCODE = (31u << OPCODE_SHIFT | 794u << 1), // X-FORM + + MULLD_OPCODE = (31u << OPCODE_SHIFT | 233u << 1), // XO-FORM + MULHD_OPCODE = (31u << OPCODE_SHIFT | 73u << 1), // XO-FORM + MULHDU_OPCODE = (31u << OPCODE_SHIFT | 9u << 1), // XO-FORM + DIVD_OPCODE = (31u << OPCODE_SHIFT | 489u << 1), // XO-FORM + + CNTLZD_OPCODE = (31u << OPCODE_SHIFT | 58u << XO_21_30_SHIFT), // X-FORM + NAND_OPCODE = (31u << OPCODE_SHIFT | 476u << XO_21_30_SHIFT), // X-FORM + NOR_OPCODE = (31u << OPCODE_SHIFT | 124u << XO_21_30_SHIFT), // X-FORM + + + // opcodes only used for floating arithmetic + FADD_OPCODE = (63u << OPCODE_SHIFT | 21u << 1), + FADDS_OPCODE = (59u << OPCODE_SHIFT | 21u << 1), + FCMPU_OPCODE = (63u << OPCODE_SHIFT | 00u << 1), + FDIV_OPCODE = (63u << OPCODE_SHIFT | 18u << 1), + FDIVS_OPCODE = (59u << OPCODE_SHIFT | 18u << 1), + FMR_OPCODE = (63u << OPCODE_SHIFT | 72u << 1), + // These are special Power6 opcodes, reused for "lfdepx" and "stfdepx" + // on Power7. Do not use. + // MFFGPR_OPCODE = (31u << OPCODE_SHIFT | 607u << 1), + // MFTGPR_OPCODE = (31u << OPCODE_SHIFT | 735u << 1), + CMPB_OPCODE = (31u << OPCODE_SHIFT | 508 << 1), + POPCNTB_OPCODE = (31u << OPCODE_SHIFT | 122 << 1), + POPCNTW_OPCODE = (31u << OPCODE_SHIFT | 378 << 1), + POPCNTD_OPCODE = (31u << OPCODE_SHIFT | 506 << 1), + FABS_OPCODE = (63u << OPCODE_SHIFT | 264u << 1), + FNABS_OPCODE = (63u << OPCODE_SHIFT | 136u << 1), + FMUL_OPCODE = (63u << OPCODE_SHIFT | 25u << 1), + FMULS_OPCODE = (59u << OPCODE_SHIFT | 25u << 1), + FNEG_OPCODE = (63u << OPCODE_SHIFT | 40u << 1), + FSUB_OPCODE = (63u << OPCODE_SHIFT | 20u << 1), + FSUBS_OPCODE = (59u << OPCODE_SHIFT | 20u << 1), + + // PPC64-internal FPU conversion opcodes + FCFID_OPCODE = (63u << OPCODE_SHIFT | 846u << 1), + FCFIDS_OPCODE = (59u << OPCODE_SHIFT | 846u << 1), + FCTID_OPCODE = (63u << OPCODE_SHIFT | 814u << 1), + FCTIDZ_OPCODE = (63u << OPCODE_SHIFT | 815u << 1), + FCTIW_OPCODE = (63u << OPCODE_SHIFT | 14u << 1), + FCTIWZ_OPCODE = (63u << OPCODE_SHIFT | 15u << 1), + FRSP_OPCODE = (63u << OPCODE_SHIFT | 12u << 1), + + // WARNING: using fmadd results in a non-compliant vm. Some floating + // point tck tests will fail. + FMADD_OPCODE = (59u << OPCODE_SHIFT | 29u << 1), + DMADD_OPCODE = (63u << OPCODE_SHIFT | 29u << 1), + FMSUB_OPCODE = (59u << OPCODE_SHIFT | 28u << 1), + DMSUB_OPCODE = (63u << OPCODE_SHIFT | 28u << 1), + FNMADD_OPCODE = (59u << OPCODE_SHIFT | 31u << 1), + DNMADD_OPCODE = (63u << OPCODE_SHIFT | 31u << 1), + FNMSUB_OPCODE = (59u << OPCODE_SHIFT | 30u << 1), + DNMSUB_OPCODE = (63u << OPCODE_SHIFT | 30u << 1), + + LFD_OPCODE = (50u << OPCODE_SHIFT | 00u << 1), + LFDU_OPCODE = (51u << OPCODE_SHIFT | 00u << 1), + LFDX_OPCODE = (31u << OPCODE_SHIFT | 599u << 1), + LFS_OPCODE = (48u << OPCODE_SHIFT | 00u << 1), + LFSU_OPCODE = (49u << OPCODE_SHIFT | 00u << 1), + LFSX_OPCODE = (31u << OPCODE_SHIFT | 535u << 1), + + STFD_OPCODE = (54u << OPCODE_SHIFT | 00u << 1), + STFDU_OPCODE = (55u << OPCODE_SHIFT | 00u << 1), + STFDX_OPCODE = (31u << OPCODE_SHIFT | 727u << 1), + STFS_OPCODE = (52u << OPCODE_SHIFT | 00u << 1), + STFSU_OPCODE = (53u << OPCODE_SHIFT | 00u << 1), + STFSX_OPCODE = (31u << OPCODE_SHIFT | 663u << 1), + + FSQRT_OPCODE = (63u << OPCODE_SHIFT | 22u << 1), // A-FORM + FSQRTS_OPCODE = (59u << OPCODE_SHIFT | 22u << 1), // A-FORM + + // Vector instruction support for >= Power6 + // Vector Storage Access + LVEBX_OPCODE = (31u << OPCODE_SHIFT | 7u << 1), + LVEHX_OPCODE = (31u << OPCODE_SHIFT | 39u << 1), + LVEWX_OPCODE = (31u << OPCODE_SHIFT | 71u << 1), + LVX_OPCODE = (31u << OPCODE_SHIFT | 103u << 1), + LVXL_OPCODE = (31u << OPCODE_SHIFT | 359u << 1), + STVEBX_OPCODE = (31u << OPCODE_SHIFT | 135u << 1), + STVEHX_OPCODE = (31u << OPCODE_SHIFT | 167u << 1), + STVEWX_OPCODE = (31u << OPCODE_SHIFT | 199u << 1), + STVX_OPCODE = (31u << OPCODE_SHIFT | 231u << 1), + STVXL_OPCODE = (31u << OPCODE_SHIFT | 487u << 1), + LVSL_OPCODE = (31u << OPCODE_SHIFT | 6u << 1), + LVSR_OPCODE = (31u << OPCODE_SHIFT | 38u << 1), + + // Vector Permute and Formatting + VPKPX_OPCODE = (4u << OPCODE_SHIFT | 782u ), + VPKSHSS_OPCODE = (4u << OPCODE_SHIFT | 398u ), + VPKSWSS_OPCODE = (4u << OPCODE_SHIFT | 462u ), + VPKSHUS_OPCODE = (4u << OPCODE_SHIFT | 270u ), + VPKSWUS_OPCODE = (4u << OPCODE_SHIFT | 334u ), + VPKUHUM_OPCODE = (4u << OPCODE_SHIFT | 14u ), + VPKUWUM_OPCODE = (4u << OPCODE_SHIFT | 78u ), + VPKUHUS_OPCODE = (4u << OPCODE_SHIFT | 142u ), + VPKUWUS_OPCODE = (4u << OPCODE_SHIFT | 206u ), + VUPKHPX_OPCODE = (4u << OPCODE_SHIFT | 846u ), + VUPKHSB_OPCODE = (4u << OPCODE_SHIFT | 526u ), + VUPKHSH_OPCODE = (4u << OPCODE_SHIFT | 590u ), + VUPKLPX_OPCODE = (4u << OPCODE_SHIFT | 974u ), + VUPKLSB_OPCODE = (4u << OPCODE_SHIFT | 654u ), + VUPKLSH_OPCODE = (4u << OPCODE_SHIFT | 718u ), + + VMRGHB_OPCODE = (4u << OPCODE_SHIFT | 12u ), + VMRGHW_OPCODE = (4u << OPCODE_SHIFT | 140u ), + VMRGHH_OPCODE = (4u << OPCODE_SHIFT | 76u ), + VMRGLB_OPCODE = (4u << OPCODE_SHIFT | 268u ), + VMRGLW_OPCODE = (4u << OPCODE_SHIFT | 396u ), + VMRGLH_OPCODE = (4u << OPCODE_SHIFT | 332u ), + + VSPLT_OPCODE = (4u << OPCODE_SHIFT | 524u ), + VSPLTH_OPCODE = (4u << OPCODE_SHIFT | 588u ), + VSPLTW_OPCODE = (4u << OPCODE_SHIFT | 652u ), + VSPLTISB_OPCODE= (4u << OPCODE_SHIFT | 780u ), + VSPLTISH_OPCODE= (4u << OPCODE_SHIFT | 844u ), + VSPLTISW_OPCODE= (4u << OPCODE_SHIFT | 908u ), + + VPERM_OPCODE = (4u << OPCODE_SHIFT | 43u ), + VSEL_OPCODE = (4u << OPCODE_SHIFT | 42u ), + + VSL_OPCODE = (4u << OPCODE_SHIFT | 452u ), + VSLDOI_OPCODE = (4u << OPCODE_SHIFT | 44u ), + VSLO_OPCODE = (4u << OPCODE_SHIFT | 1036u ), + VSR_OPCODE = (4u << OPCODE_SHIFT | 708u ), + VSRO_OPCODE = (4u << OPCODE_SHIFT | 1100u ), + + // Vector Integer + VADDCUW_OPCODE = (4u << OPCODE_SHIFT | 384u ), + VADDSHS_OPCODE = (4u << OPCODE_SHIFT | 832u ), + VADDSBS_OPCODE = (4u << OPCODE_SHIFT | 768u ), + VADDSWS_OPCODE = (4u << OPCODE_SHIFT | 896u ), + VADDUBM_OPCODE = (4u << OPCODE_SHIFT | 0u ), + VADDUWM_OPCODE = (4u << OPCODE_SHIFT | 128u ), + VADDUHM_OPCODE = (4u << OPCODE_SHIFT | 64u ), + VADDUBS_OPCODE = (4u << OPCODE_SHIFT | 512u ), + VADDUWS_OPCODE = (4u << OPCODE_SHIFT | 640u ), + VADDUHS_OPCODE = (4u << OPCODE_SHIFT | 576u ), + VSUBCUW_OPCODE = (4u << OPCODE_SHIFT | 1408u ), + VSUBSHS_OPCODE = (4u << OPCODE_SHIFT | 1856u ), + VSUBSBS_OPCODE = (4u << OPCODE_SHIFT | 1792u ), + VSUBSWS_OPCODE = (4u << OPCODE_SHIFT | 1920u ), + VSUBUBM_OPCODE = (4u << OPCODE_SHIFT | 1024u ), + VSUBUWM_OPCODE = (4u << OPCODE_SHIFT | 1152u ), + VSUBUHM_OPCODE = (4u << OPCODE_SHIFT | 1088u ), + VSUBUBS_OPCODE = (4u << OPCODE_SHIFT | 1536u ), + VSUBUWS_OPCODE = (4u << OPCODE_SHIFT | 1664u ), + VSUBUHS_OPCODE = (4u << OPCODE_SHIFT | 1600u ), + + VMULESB_OPCODE = (4u << OPCODE_SHIFT | 776u ), + VMULEUB_OPCODE = (4u << OPCODE_SHIFT | 520u ), + VMULESH_OPCODE = (4u << OPCODE_SHIFT | 840u ), + VMULEUH_OPCODE = (4u << OPCODE_SHIFT | 584u ), + VMULOSB_OPCODE = (4u << OPCODE_SHIFT | 264u ), + VMULOUB_OPCODE = (4u << OPCODE_SHIFT | 8u ), + VMULOSH_OPCODE = (4u << OPCODE_SHIFT | 328u ), + VMULOUH_OPCODE = (4u << OPCODE_SHIFT | 72u ), + VMHADDSHS_OPCODE=(4u << OPCODE_SHIFT | 32u ), + VMHRADDSHS_OPCODE=(4u << OPCODE_SHIFT | 33u ), + VMLADDUHM_OPCODE=(4u << OPCODE_SHIFT | 34u ), + VMSUBUHM_OPCODE= (4u << OPCODE_SHIFT | 36u ), + VMSUMMBM_OPCODE= (4u << OPCODE_SHIFT | 37u ), + VMSUMSHM_OPCODE= (4u << OPCODE_SHIFT | 40u ), + VMSUMSHS_OPCODE= (4u << OPCODE_SHIFT | 41u ), + VMSUMUHM_OPCODE= (4u << OPCODE_SHIFT | 38u ), + VMSUMUHS_OPCODE= (4u << OPCODE_SHIFT | 39u ), + + VSUMSWS_OPCODE = (4u << OPCODE_SHIFT | 1928u ), + VSUM2SWS_OPCODE= (4u << OPCODE_SHIFT | 1672u ), + VSUM4SBS_OPCODE= (4u << OPCODE_SHIFT | 1800u ), + VSUM4UBS_OPCODE= (4u << OPCODE_SHIFT | 1544u ), + VSUM4SHS_OPCODE= (4u << OPCODE_SHIFT | 1608u ), + + VAVGSB_OPCODE = (4u << OPCODE_SHIFT | 1282u ), + VAVGSW_OPCODE = (4u << OPCODE_SHIFT | 1410u ), + VAVGSH_OPCODE = (4u << OPCODE_SHIFT | 1346u ), + VAVGUB_OPCODE = (4u << OPCODE_SHIFT | 1026u ), + VAVGUW_OPCODE = (4u << OPCODE_SHIFT | 1154u ), + VAVGUH_OPCODE = (4u << OPCODE_SHIFT | 1090u ), + + VMAXSB_OPCODE = (4u << OPCODE_SHIFT | 258u ), + VMAXSW_OPCODE = (4u << OPCODE_SHIFT | 386u ), + VMAXSH_OPCODE = (4u << OPCODE_SHIFT | 322u ), + VMAXUB_OPCODE = (4u << OPCODE_SHIFT | 2u ), + VMAXUW_OPCODE = (4u << OPCODE_SHIFT | 130u ), + VMAXUH_OPCODE = (4u << OPCODE_SHIFT | 66u ), + VMINSB_OPCODE = (4u << OPCODE_SHIFT | 770u ), + VMINSW_OPCODE = (4u << OPCODE_SHIFT | 898u ), + VMINSH_OPCODE = (4u << OPCODE_SHIFT | 834u ), + VMINUB_OPCODE = (4u << OPCODE_SHIFT | 514u ), + VMINUW_OPCODE = (4u << OPCODE_SHIFT | 642u ), + VMINUH_OPCODE = (4u << OPCODE_SHIFT | 578u ), + + VCMPEQUB_OPCODE= (4u << OPCODE_SHIFT | 6u ), + VCMPEQUH_OPCODE= (4u << OPCODE_SHIFT | 70u ), + VCMPEQUW_OPCODE= (4u << OPCODE_SHIFT | 134u ), + VCMPGTSH_OPCODE= (4u << OPCODE_SHIFT | 838u ), + VCMPGTSB_OPCODE= (4u << OPCODE_SHIFT | 774u ), + VCMPGTSW_OPCODE= (4u << OPCODE_SHIFT | 902u ), + VCMPGTUB_OPCODE= (4u << OPCODE_SHIFT | 518u ), + VCMPGTUH_OPCODE= (4u << OPCODE_SHIFT | 582u ), + VCMPGTUW_OPCODE= (4u << OPCODE_SHIFT | 646u ), + + VAND_OPCODE = (4u << OPCODE_SHIFT | 1028u ), + VANDC_OPCODE = (4u << OPCODE_SHIFT | 1092u ), + VNOR_OPCODE = (4u << OPCODE_SHIFT | 1284u ), + VOR_OPCODE = (4u << OPCODE_SHIFT | 1156u ), + VXOR_OPCODE = (4u << OPCODE_SHIFT | 1220u ), + VRLB_OPCODE = (4u << OPCODE_SHIFT | 4u ), + VRLW_OPCODE = (4u << OPCODE_SHIFT | 132u ), + VRLH_OPCODE = (4u << OPCODE_SHIFT | 68u ), + VSLB_OPCODE = (4u << OPCODE_SHIFT | 260u ), + VSKW_OPCODE = (4u << OPCODE_SHIFT | 388u ), + VSLH_OPCODE = (4u << OPCODE_SHIFT | 324u ), + VSRB_OPCODE = (4u << OPCODE_SHIFT | 516u ), + VSRW_OPCODE = (4u << OPCODE_SHIFT | 644u ), + VSRH_OPCODE = (4u << OPCODE_SHIFT | 580u ), + VSRAB_OPCODE = (4u << OPCODE_SHIFT | 772u ), + VSRAW_OPCODE = (4u << OPCODE_SHIFT | 900u ), + VSRAH_OPCODE = (4u << OPCODE_SHIFT | 836u ), + + // Vector Floating-Point + // not implemented yet + + // Vector Status and Control + MTVSCR_OPCODE = (4u << OPCODE_SHIFT | 1604u ), + MFVSCR_OPCODE = (4u << OPCODE_SHIFT | 1540u ), + + // Icache and dcache related instructions + DCBA_OPCODE = (31u << OPCODE_SHIFT | 758u << 1), + DCBZ_OPCODE = (31u << OPCODE_SHIFT | 1014u << 1), + DCBST_OPCODE = (31u << OPCODE_SHIFT | 54u << 1), + DCBF_OPCODE = (31u << OPCODE_SHIFT | 86u << 1), + + DCBT_OPCODE = (31u << OPCODE_SHIFT | 278u << 1), + DCBTST_OPCODE = (31u << OPCODE_SHIFT | 246u << 1), + ICBI_OPCODE = (31u << OPCODE_SHIFT | 982u << 1), + + // Instruction synchronization + ISYNC_OPCODE = (19u << OPCODE_SHIFT | 150u << 1), + // Memory barriers + SYNC_OPCODE = (31u << OPCODE_SHIFT | 598u << 1), + EIEIO_OPCODE = (31u << OPCODE_SHIFT | 854u << 1), + + // Trap instructions + TDI_OPCODE = (2u << OPCODE_SHIFT), + TWI_OPCODE = (3u << OPCODE_SHIFT), + TD_OPCODE = (31u << OPCODE_SHIFT | 68u << 1), + TW_OPCODE = (31u << OPCODE_SHIFT | 4u << 1), + + // Atomics. + LWARX_OPCODE = (31u << OPCODE_SHIFT | 20u << 1), + LDARX_OPCODE = (31u << OPCODE_SHIFT | 84u << 1), + STWCX_OPCODE = (31u << OPCODE_SHIFT | 150u << 1), + STDCX_OPCODE = (31u << OPCODE_SHIFT | 214u << 1) + + }; + + // Trap instructions TO bits + enum trap_to_bits { + // single bits + traptoLessThanSigned = 1 << 4, // 0, left end + traptoGreaterThanSigned = 1 << 3, + traptoEqual = 1 << 2, + traptoLessThanUnsigned = 1 << 1, + traptoGreaterThanUnsigned = 1 << 0, // 4, right end + + // compound ones + traptoUnconditional = (traptoLessThanSigned | + traptoGreaterThanSigned | + traptoEqual | + traptoLessThanUnsigned | + traptoGreaterThanUnsigned) + }; + + // Branch hints BH field + enum branch_hint_bh { + // bclr cases: + bhintbhBCLRisReturn = 0, + bhintbhBCLRisNotReturnButSame = 1, + bhintbhBCLRisNotPredictable = 3, + + // bcctr cases: + bhintbhBCCTRisNotReturnButSame = 0, + bhintbhBCCTRisNotPredictable = 3 + }; + + // Branch prediction hints AT field + enum branch_hint_at { + bhintatNoHint = 0, // at=00 + bhintatIsNotTaken = 2, // at=10 + bhintatIsTaken = 3 // at=11 + }; + + // Branch prediction hints + enum branch_hint_concept { + // Use the same encoding as branch_hint_at to simply code. + bhintNoHint = bhintatNoHint, + bhintIsNotTaken = bhintatIsNotTaken, + bhintIsTaken = bhintatIsTaken + }; + + // Used in BO field of branch instruction. + enum branch_condition { + bcondCRbiIs0 = 4, // bo=001at + bcondCRbiIs1 = 12, // bo=011at + bcondAlways = 20 // bo=10100 + }; + + // Branch condition with combined prediction hints. + enum branch_condition_with_hint { + bcondCRbiIs0_bhintNoHint = bcondCRbiIs0 | bhintatNoHint, + bcondCRbiIs0_bhintIsNotTaken = bcondCRbiIs0 | bhintatIsNotTaken, + bcondCRbiIs0_bhintIsTaken = bcondCRbiIs0 | bhintatIsTaken, + bcondCRbiIs1_bhintNoHint = bcondCRbiIs1 | bhintatNoHint, + bcondCRbiIs1_bhintIsNotTaken = bcondCRbiIs1 | bhintatIsNotTaken, + bcondCRbiIs1_bhintIsTaken = bcondCRbiIs1 | bhintatIsTaken, + }; + + // Branch prediction hints. + inline static int add_bhint_to_boint(const int bhint, const int boint) { + switch (boint) { + case bcondCRbiIs0: + case bcondCRbiIs1: + // branch_hint and branch_hint_at have same encodings + assert( (int)bhintNoHint == (int)bhintatNoHint + && (int)bhintIsNotTaken == (int)bhintatIsNotTaken + && (int)bhintIsTaken == (int)bhintatIsTaken, + "wrong encodings"); + assert((bhint & 0x03) == bhint, "wrong encodings"); + return (boint & ~0x03) | bhint; + case bcondAlways: + // no branch_hint + return boint; + default: + ShouldNotReachHere(); + return 0; + } + } + + // Extract bcond from boint. + inline static int inv_boint_bcond(const int boint) { + int r_bcond = boint & ~0x03; + assert(r_bcond == bcondCRbiIs0 || + r_bcond == bcondCRbiIs1 || + r_bcond == bcondAlways, + "bad branch condition"); + return r_bcond; + } + + // Extract bhint from boint. + inline static int inv_boint_bhint(const int boint) { + int r_bhint = boint & 0x03; + assert(r_bhint == bhintatNoHint || + r_bhint == bhintatIsNotTaken || + r_bhint == bhintatIsTaken, + "bad branch hint"); + return r_bhint; + } + + // Calculate opposite of given bcond. + inline static int opposite_bcond(const int bcond) { + switch (bcond) { + case bcondCRbiIs0: + return bcondCRbiIs1; + case bcondCRbiIs1: + return bcondCRbiIs0; + default: + ShouldNotReachHere(); + return 0; + } + } + + // Calculate opposite of given bhint. + inline static int opposite_bhint(const int bhint) { + switch (bhint) { + case bhintatNoHint: + return bhintatNoHint; + case bhintatIsNotTaken: + return bhintatIsTaken; + case bhintatIsTaken: + return bhintatIsNotTaken; + default: + ShouldNotReachHere(); + return 0; + } + } + + // PPC branch instructions + enum ppcops { + b_op = 18, + bc_op = 16, + bcr_op = 19 + }; + + enum Condition { + negative = 0, + less = 0, + positive = 1, + greater = 1, + zero = 2, + equal = 2, + summary_overflow = 3, + }; + + public: + // Helper functions for groups of instructions + + enum Predict { pt = 1, pn = 0 }; // pt = predict taken + + enum Membar_mask_bits { // page 184, v9 + StoreStore = 1 << 3, + LoadStore = 1 << 2, + StoreLoad = 1 << 1, + LoadLoad = 1 << 0, + + Sync = 1 << 6, + MemIssue = 1 << 5, + Lookaside = 1 << 4 + }; + + // instruction must start at passed address + static int instr_len(unsigned char *instr) { return BytesPerInstWord; } + + // instruction must be left-justified in argument + static int instr_len(unsigned long instr) { return BytesPerInstWord; } + + // longest instructions + static int instr_maxlen() { return BytesPerInstWord; } + + // Test if x is within signed immediate range for nbits. + static bool is_simm(int x, unsigned int nbits) { + assert(0 < nbits && nbits < 32, "out of bounds"); + const int min = -( ((int)1) << nbits-1 ); + const int maxplus1 = ( ((int)1) << nbits-1 ); + return min <= x && x < maxplus1; + } + + static bool is_simm(jlong x, unsigned int nbits) { + assert(0 < nbits && nbits < 64, "out of bounds"); + const jlong min = -( ((jlong)1) << nbits-1 ); + const jlong maxplus1 = ( ((jlong)1) << nbits-1 ); + return min <= x && x < maxplus1; + } + + // Test if x is within unsigned immediate range for nbits + static bool is_uimm(int x, unsigned int nbits) { + assert(0 < nbits && nbits < 32, "out of bounds"); + const int maxplus1 = ( ((int)1) << nbits ); + return 0 <= x && x < maxplus1; + } + + static bool is_uimm(jlong x, unsigned int nbits) { + assert(0 < nbits && nbits < 64, "out of bounds"); + const jlong maxplus1 = ( ((jlong)1) << nbits ); + return 0 <= x && x < maxplus1; + } + + protected: + // helpers + + // X is supposed to fit in a field "nbits" wide + // and be sign-extended. Check the range. + static void assert_signed_range(intptr_t x, int nbits) { + assert(nbits == 32 || (-(1 << nbits-1) <= x && x < (1 << nbits-1)), + "value out of range"); + } + + static void assert_signed_word_disp_range(intptr_t x, int nbits) { + assert((x & 3) == 0, "not word aligned"); + assert_signed_range(x, nbits + 2); + } + + static void assert_unsigned_const(int x, int nbits) { + assert(juint(x) < juint(1 << nbits), "unsigned constant out of range"); + } + + static int fmask(juint hi_bit, juint lo_bit) { + assert(hi_bit >= lo_bit && hi_bit < 32, "bad bits"); + return (1 << ( hi_bit-lo_bit + 1 )) - 1; + } + + // inverse of u_field + static int inv_u_field(int x, int hi_bit, int lo_bit) { + juint r = juint(x) >> lo_bit; + r &= fmask(hi_bit, lo_bit); + return int(r); + } + + // signed version: extract from field and sign-extend + static int inv_s_field_ppc(int x, int hi_bit, int lo_bit) { + x = x << (31-hi_bit); + x = x >> (31-hi_bit+lo_bit); + return x; + } + + static int u_field(int x, int hi_bit, int lo_bit) { + assert((x & ~fmask(hi_bit, lo_bit)) == 0, "value out of range"); + int r = x << lo_bit; + assert(inv_u_field(r, hi_bit, lo_bit) == x, "just checking"); + return r; + } + + // Same as u_field for signed values + static int s_field(int x, int hi_bit, int lo_bit) { + int nbits = hi_bit - lo_bit + 1; + assert(nbits == 32 || (-(1 << nbits-1) <= x && x < (1 << nbits-1)), + "value out of range"); + x &= fmask(hi_bit, lo_bit); + int r = x << lo_bit; + return r; + } + + // inv_op for ppc instructions + static int inv_op_ppc(int x) { return inv_u_field(x, 31, 26); } + + // Determine target address from li, bd field of branch instruction. + static intptr_t inv_li_field(int x) { + intptr_t r = inv_s_field_ppc(x, 25, 2); + r = (r << 2); + return r; + } + static intptr_t inv_bd_field(int x, intptr_t pos) { + intptr_t r = inv_s_field_ppc(x, 15, 2); + r = (r << 2) + pos; + return r; + } + + #define inv_opp_u_field(x, hi_bit, lo_bit) inv_u_field(x, 31-(lo_bit), 31-(hi_bit)) + #define inv_opp_s_field(x, hi_bit, lo_bit) inv_s_field_ppc(x, 31-(lo_bit), 31-(hi_bit)) + // Extract instruction fields from instruction words. + public: + static int inv_ra_field(int x) { return inv_opp_u_field(x, 15, 11); } + static int inv_rb_field(int x) { return inv_opp_u_field(x, 20, 16); } + static int inv_rt_field(int x) { return inv_opp_u_field(x, 10, 6); } + static int inv_rs_field(int x) { return inv_opp_u_field(x, 10, 6); } + // Ds uses opp_s_field(x, 31, 16), but lowest 2 bits must be 0. + // Inv_ds_field uses range (x, 29, 16) but shifts by 2 to ensure that lowest bits are 0. + static int inv_ds_field(int x) { return inv_opp_s_field(x, 29, 16) << 2; } + static int inv_d1_field(int x) { return inv_opp_s_field(x, 31, 16); } + static int inv_si_field(int x) { return inv_opp_s_field(x, 31, 16); } + static int inv_to_field(int x) { return inv_opp_u_field(x, 10, 6); } + static int inv_lk_field(int x) { return inv_opp_u_field(x, 31, 31); } + static int inv_bo_field(int x) { return inv_opp_u_field(x, 10, 6); } + static int inv_bi_field(int x) { return inv_opp_u_field(x, 15, 11); } + + #define opp_u_field(x, hi_bit, lo_bit) u_field(x, 31-(lo_bit), 31-(hi_bit)) + #define opp_s_field(x, hi_bit, lo_bit) s_field(x, 31-(lo_bit), 31-(hi_bit)) + + // instruction fields + static int aa( int x) { return opp_u_field(x, 30, 30); } + static int ba( int x) { return opp_u_field(x, 15, 11); } + static int bb( int x) { return opp_u_field(x, 20, 16); } + static int bc( int x) { return opp_u_field(x, 25, 21); } + static int bd( int x) { return opp_s_field(x, 29, 16); } + static int bf( ConditionRegister cr) { return bf(cr->encoding()); } + static int bf( int x) { return opp_u_field(x, 8, 6); } + static int bfa(ConditionRegister cr) { return bfa(cr->encoding()); } + static int bfa( int x) { return opp_u_field(x, 13, 11); } + static int bh( int x) { return opp_u_field(x, 20, 19); } + static int bi( int x) { return opp_u_field(x, 15, 11); } + static int bi0(ConditionRegister cr, Condition c) { return (cr->encoding() << 2) | c; } + static int bo( int x) { return opp_u_field(x, 10, 6); } + static int bt( int x) { return opp_u_field(x, 10, 6); } + static int d1( int x) { return opp_s_field(x, 31, 16); } + static int ds( int x) { assert((x & 0x3) == 0, "unaligned offset"); return opp_s_field(x, 31, 16); } + static int eh( int x) { return opp_u_field(x, 31, 31); } + static int flm( int x) { return opp_u_field(x, 14, 7); } + static int fra( FloatRegister r) { return fra(r->encoding());} + static int frb( FloatRegister r) { return frb(r->encoding());} + static int frc( FloatRegister r) { return frc(r->encoding());} + static int frs( FloatRegister r) { return frs(r->encoding());} + static int frt( FloatRegister r) { return frt(r->encoding());} + static int fra( int x) { return opp_u_field(x, 15, 11); } + static int frb( int x) { return opp_u_field(x, 20, 16); } + static int frc( int x) { return opp_u_field(x, 25, 21); } + static int frs( int x) { return opp_u_field(x, 10, 6); } + static int frt( int x) { return opp_u_field(x, 10, 6); } + static int fxm( int x) { return opp_u_field(x, 19, 12); } + static int l10( int x) { return opp_u_field(x, 10, 10); } + static int l15( int x) { return opp_u_field(x, 15, 15); } + static int l910( int x) { return opp_u_field(x, 10, 9); } + static int lev( int x) { return opp_u_field(x, 26, 20); } + static int li( int x) { return opp_s_field(x, 29, 6); } + static int lk( int x) { return opp_u_field(x, 31, 31); } + static int mb2125( int x) { return opp_u_field(x, 25, 21); } + static int me2630( int x) { return opp_u_field(x, 30, 26); } + static int mb2126( int x) { return opp_u_field(((x & 0x1f) << 1) | ((x & 0x20) >> 5), 26, 21); } + static int me2126( int x) { return mb2126(x); } + static int nb( int x) { return opp_u_field(x, 20, 16); } + //static int opcd( int x) { return opp_u_field(x, 5, 0); } // is contained in our opcodes + static int oe( int x) { return opp_u_field(x, 21, 21); } + static int ra( Register r) { return ra(r->encoding()); } + static int ra( int x) { return opp_u_field(x, 15, 11); } + static int rb( Register r) { return rb(r->encoding()); } + static int rb( int x) { return opp_u_field(x, 20, 16); } + static int rc( int x) { return opp_u_field(x, 31, 31); } + static int rs( Register r) { return rs(r->encoding()); } + static int rs( int x) { return opp_u_field(x, 10, 6); } + // we don't want to use R0 in memory accesses, because it has value `0' then + static int ra0mem( Register r) { assert(r != R0, "cannot use register R0 in memory access"); return ra(r); } + static int ra0mem( int x) { assert(x != 0, "cannot use register 0 in memory access"); return ra(x); } + + // register r is target + static int rt( Register r) { return rs(r); } + static int rt( int x) { return rs(x); } + static int rta( Register r) { return ra(r); } + static int rta0mem( Register r) { rta(r); return ra0mem(r); } + + static int sh1620( int x) { return opp_u_field(x, 20, 16); } + static int sh30( int x) { return opp_u_field(x, 30, 30); } + static int sh162030( int x) { return sh1620(x & 0x1f) | sh30((x & 0x20) >> 5); } + static int si( int x) { return opp_s_field(x, 31, 16); } + static int spr( int x) { return opp_u_field(x, 20, 11); } + static int sr( int x) { return opp_u_field(x, 15, 12); } + static int tbr( int x) { return opp_u_field(x, 20, 11); } + static int th( int x) { return opp_u_field(x, 10, 7); } + static int thct( int x) { assert((x&8) == 0, "must be valid cache specification"); return th(x); } + static int thds( int x) { assert((x&8) == 8, "must be valid stream specification"); return th(x); } + static int to( int x) { return opp_u_field(x, 10, 6); } + static int u( int x) { return opp_u_field(x, 19, 16); } + static int ui( int x) { return opp_u_field(x, 31, 16); } + + // Support vector instructions for >= Power6. + static int vra( int x) { return opp_u_field(x, 15, 11); } + static int vrb( int x) { return opp_u_field(x, 20, 16); } + static int vrc( int x) { return opp_u_field(x, 25, 21); } + static int vrs( int x) { return opp_u_field(x, 10, 6); } + static int vrt( int x) { return opp_u_field(x, 10, 6); } + + static int vra( VectorRegister r) { return vra(r->encoding());} + static int vrb( VectorRegister r) { return vrb(r->encoding());} + static int vrc( VectorRegister r) { return vrc(r->encoding());} + static int vrs( VectorRegister r) { return vrs(r->encoding());} + static int vrt( VectorRegister r) { return vrt(r->encoding());} + + static int vsplt_uim( int x) { return opp_u_field(x, 15, 12); } // for vsplt* instructions + static int vsplti_sim(int x) { return opp_u_field(x, 15, 11); } // for vsplti* instructions + static int vsldoi_shb(int x) { return opp_u_field(x, 25, 22); } // for vsldoi instruction + static int vcmp_rc( int x) { return opp_u_field(x, 21, 21); } // for vcmp* instructions + + //static int xo1( int x) { return opp_u_field(x, 29, 21); }// is contained in our opcodes + //static int xo2( int x) { return opp_u_field(x, 30, 21); }// is contained in our opcodes + //static int xo3( int x) { return opp_u_field(x, 30, 22); }// is contained in our opcodes + //static int xo4( int x) { return opp_u_field(x, 30, 26); }// is contained in our opcodes + //static int xo5( int x) { return opp_u_field(x, 29, 27); }// is contained in our opcodes + //static int xo6( int x) { return opp_u_field(x, 30, 27); }// is contained in our opcodes + //static int xo7( int x) { return opp_u_field(x, 31, 30); }// is contained in our opcodes + + protected: + // Compute relative address for branch. + static intptr_t disp(intptr_t x, intptr_t off) { + int xx = x - off; + xx = xx >> 2; + return xx; + } + + public: + // signed immediate, in low bits, nbits long + static int simm(int x, int nbits) { + assert_signed_range(x, nbits); + return x & ((1 << nbits) - 1); + } + + // unsigned immediate, in low bits, nbits long + static int uimm(int x, int nbits) { + assert_unsigned_const(x, nbits); + return x & ((1 << nbits) - 1); + } + + static void set_imm(int* instr, short s) { + // imm is always in the lower 16 bits of the instruction, + // so this is endian-neutral. Same for the get_imm below. + uint32_t w = *(uint32_t *)instr; + *instr = (int)((w & ~0x0000FFFF) | (s & 0x0000FFFF)); + } + + static int get_imm(address a, int instruction_number) { + return (short)((int *)a)[instruction_number]; + } + + static inline int hi16_signed( int x) { return (int)(int16_t)(x >> 16); } + static inline int lo16_unsigned(int x) { return x & 0xffff; } + + protected: + + // Extract the top 32 bits in a 64 bit word. + static int32_t hi32(int64_t x) { + int32_t r = int32_t((uint64_t)x >> 32); + return r; + } + + public: + + static inline unsigned int align_addr(unsigned int addr, unsigned int a) { + return ((addr + (a - 1)) & ~(a - 1)); + } + + static inline bool is_aligned(unsigned int addr, unsigned int a) { + return (0 == addr % a); + } + + void flush() { + AbstractAssembler::flush(); + } + + inline void emit_long(int); // shadows AbstractAssembler::emit_long + inline void emit_int32(int); // same as emit_long, will be renamed in HS25 + inline void emit_data(int); + inline void emit_data(int, RelocationHolder const&); + inline void emit_data(int, relocInfo::relocType rtype); + + // Emit an address. + inline address emit_addr(const address addr = NULL); + +#if !defined(ABI_ELFv2) + // Emit a function descriptor with the specified entry point, TOC, + // and ENV. If the entry point is NULL, the descriptor will point + // just past the descriptor. + // Use values from friend functions as defaults. + inline address emit_fd(address entry = NULL, + address toc = (address) FunctionDescriptor::friend_toc, + address env = (address) FunctionDescriptor::friend_env); +#endif + + ///////////////////////////////////////////////////////////////////////////////////// + // PPC instructions + ///////////////////////////////////////////////////////////////////////////////////// + + // Memory instructions use r0 as hard coded 0, e.g. to simulate loading + // immediates. The normal instruction encoders enforce that r0 is not + // passed to them. Use either extended mnemonics encoders or the special ra0 + // versions. + + // Issue an illegal instruction. + inline void illtrap(); + static inline bool is_illtrap(int x); + + // PPC 1, section 3.3.8, Fixed-Point Arithmetic Instructions + inline void addi( Register d, Register a, int si16); + inline void addis(Register d, Register a, int si16); + private: + inline void addi_r0ok( Register d, Register a, int si16); + inline void addis_r0ok(Register d, Register a, int si16); + public: + inline void addic_( Register d, Register a, int si16); + inline void subfic( Register d, Register a, int si16); + inline void add( Register d, Register a, Register b); + inline void add_( Register d, Register a, Register b); + inline void subf( Register d, Register a, Register b); + inline void sub( Register d, Register a, Register b); + inline void subf_( Register d, Register a, Register b); + inline void addc( Register d, Register a, Register b); + inline void addc_( Register d, Register a, Register b); + inline void subfc( Register d, Register a, Register b); + inline void subfc_( Register d, Register a, Register b); + inline void adde( Register d, Register a, Register b); + inline void adde_( Register d, Register a, Register b); + inline void subfe( Register d, Register a, Register b); + inline void subfe_( Register d, Register a, Register b); + inline void neg( Register d, Register a); + inline void neg_( Register d, Register a); + inline void mulli( Register d, Register a, int si16); + inline void mulld( Register d, Register a, Register b); + inline void mulld_( Register d, Register a, Register b); + inline void mullw( Register d, Register a, Register b); + inline void mullw_( Register d, Register a, Register b); + inline void mulhw( Register d, Register a, Register b); + inline void mulhw_( Register d, Register a, Register b); + inline void mulhd( Register d, Register a, Register b); + inline void mulhd_( Register d, Register a, Register b); + inline void mulhdu( Register d, Register a, Register b); + inline void mulhdu_(Register d, Register a, Register b); + inline void divd( Register d, Register a, Register b); + inline void divd_( Register d, Register a, Register b); + inline void divw( Register d, Register a, Register b); + inline void divw_( Register d, Register a, Register b); + + // extended mnemonics + inline void li( Register d, int si16); + inline void lis( Register d, int si16); + inline void addir(Register d, int si16, Register a); + + static bool is_addi(int x) { + return ADDI_OPCODE == (x & ADDI_OPCODE_MASK); + } + static bool is_addis(int x) { + return ADDIS_OPCODE == (x & ADDIS_OPCODE_MASK); + } + static bool is_bxx(int x) { + return BXX_OPCODE == (x & BXX_OPCODE_MASK); + } + static bool is_b(int x) { + return BXX_OPCODE == (x & BXX_OPCODE_MASK) && inv_lk_field(x) == 0; + } + static bool is_bl(int x) { + return BXX_OPCODE == (x & BXX_OPCODE_MASK) && inv_lk_field(x) == 1; + } + static bool is_bcxx(int x) { + return BCXX_OPCODE == (x & BCXX_OPCODE_MASK); + } + static bool is_bxx_or_bcxx(int x) { + return is_bxx(x) || is_bcxx(x); + } + static bool is_bctrl(int x) { + return x == 0x4e800421; + } + static bool is_bctr(int x) { + return x == 0x4e800420; + } + static bool is_bclr(int x) { + return BCLR_OPCODE == (x & XL_FORM_OPCODE_MASK); + } + static bool is_li(int x) { + return is_addi(x) && inv_ra_field(x)==0; + } + static bool is_lis(int x) { + return is_addis(x) && inv_ra_field(x)==0; + } + static bool is_mtctr(int x) { + return MTCTR_OPCODE == (x & MTCTR_OPCODE_MASK); + } + static bool is_ld(int x) { + return LD_OPCODE == (x & LD_OPCODE_MASK); + } + static bool is_std(int x) { + return STD_OPCODE == (x & STD_OPCODE_MASK); + } + static bool is_stdu(int x) { + return STDU_OPCODE == (x & STDU_OPCODE_MASK); + } + static bool is_stdx(int x) { + return STDX_OPCODE == (x & STDX_OPCODE_MASK); + } + static bool is_stdux(int x) { + return STDUX_OPCODE == (x & STDUX_OPCODE_MASK); + } + static bool is_stwx(int x) { + return STWX_OPCODE == (x & STWX_OPCODE_MASK); + } + static bool is_stwux(int x) { + return STWUX_OPCODE == (x & STWUX_OPCODE_MASK); + } + static bool is_stw(int x) { + return STW_OPCODE == (x & STW_OPCODE_MASK); + } + static bool is_stwu(int x) { + return STWU_OPCODE == (x & STWU_OPCODE_MASK); + } + static bool is_ori(int x) { + return ORI_OPCODE == (x & ORI_OPCODE_MASK); + }; + static bool is_oris(int x) { + return ORIS_OPCODE == (x & ORIS_OPCODE_MASK); + }; + static bool is_rldicr(int x) { + return (RLDICR_OPCODE == (x & RLDICR_OPCODE_MASK)); + }; + static bool is_nop(int x) { + return x == 0x60000000; + } + // endgroup opcode for Power6 + static bool is_endgroup(int x) { + return is_ori(x) && inv_ra_field(x) == 1 && inv_rs_field(x) == 1 && inv_d1_field(x) == 0; + } + + + private: + // PPC 1, section 3.3.9, Fixed-Point Compare Instructions + inline void cmpi( ConditionRegister bf, int l, Register a, int si16); + inline void cmp( ConditionRegister bf, int l, Register a, Register b); + inline void cmpli(ConditionRegister bf, int l, Register a, int ui16); + inline void cmpl( ConditionRegister bf, int l, Register a, Register b); + + public: + // extended mnemonics of Compare Instructions + inline void cmpwi( ConditionRegister crx, Register a, int si16); + inline void cmpdi( ConditionRegister crx, Register a, int si16); + inline void cmpw( ConditionRegister crx, Register a, Register b); + inline void cmpd( ConditionRegister crx, Register a, Register b); + inline void cmplwi(ConditionRegister crx, Register a, int ui16); + inline void cmpldi(ConditionRegister crx, Register a, int ui16); + inline void cmplw( ConditionRegister crx, Register a, Register b); + inline void cmpld( ConditionRegister crx, Register a, Register b); + + inline void isel( Register d, Register a, Register b, int bc); + // Convenient version which takes: Condition register, Condition code and invert flag. Omit b to keep old value. + inline void isel( Register d, ConditionRegister cr, Condition cc, bool inv, Register a, Register b = noreg); + // Set d = 0 if (cr.cc) equals 1, otherwise b. + inline void isel_0( Register d, ConditionRegister cr, Condition cc, Register b = noreg); + + // PPC 1, section 3.3.11, Fixed-Point Logical Instructions + void andi( Register a, Register s, int ui16); // optimized version + inline void andi_( Register a, Register s, int ui16); + inline void andis_( Register a, Register s, int ui16); + inline void ori( Register a, Register s, int ui16); + inline void oris( Register a, Register s, int ui16); + inline void xori( Register a, Register s, int ui16); + inline void xoris( Register a, Register s, int ui16); + inline void andr( Register a, Register s, Register b); // suffixed by 'r' as 'and' is C++ keyword + inline void and_( Register a, Register s, Register b); + // Turn or0(rx,rx,rx) into a nop and avoid that we accidently emit a + // SMT-priority change instruction (see SMT instructions below). + inline void or_unchecked(Register a, Register s, Register b); + inline void orr( Register a, Register s, Register b); // suffixed by 'r' as 'or' is C++ keyword + inline void or_( Register a, Register s, Register b); + inline void xorr( Register a, Register s, Register b); // suffixed by 'r' as 'xor' is C++ keyword + inline void xor_( Register a, Register s, Register b); + inline void nand( Register a, Register s, Register b); + inline void nand_( Register a, Register s, Register b); + inline void nor( Register a, Register s, Register b); + inline void nor_( Register a, Register s, Register b); + inline void andc( Register a, Register s, Register b); + inline void andc_( Register a, Register s, Register b); + inline void orc( Register a, Register s, Register b); + inline void orc_( Register a, Register s, Register b); + inline void extsb( Register a, Register s); + inline void extsh( Register a, Register s); + inline void extsw( Register a, Register s); + + // extended mnemonics + inline void nop(); + // NOP for FP and BR units (different versions to allow them to be in one group) + inline void fpnop0(); + inline void fpnop1(); + inline void brnop0(); + inline void brnop1(); + inline void brnop2(); + + inline void mr( Register d, Register s); + inline void ori_opt( Register d, int ui16); + inline void oris_opt(Register d, int ui16); + + // endgroup opcode for Power6 + inline void endgroup(); + + // count instructions + inline void cntlzw( Register a, Register s); + inline void cntlzw_( Register a, Register s); + inline void cntlzd( Register a, Register s); + inline void cntlzd_( Register a, Register s); + + // PPC 1, section 3.3.12, Fixed-Point Rotate and Shift Instructions + inline void sld( Register a, Register s, Register b); + inline void sld_( Register a, Register s, Register b); + inline void slw( Register a, Register s, Register b); + inline void slw_( Register a, Register s, Register b); + inline void srd( Register a, Register s, Register b); + inline void srd_( Register a, Register s, Register b); + inline void srw( Register a, Register s, Register b); + inline void srw_( Register a, Register s, Register b); + inline void srad( Register a, Register s, Register b); + inline void srad_( Register a, Register s, Register b); + inline void sraw( Register a, Register s, Register b); + inline void sraw_( Register a, Register s, Register b); + inline void sradi( Register a, Register s, int sh6); + inline void sradi_( Register a, Register s, int sh6); + inline void srawi( Register a, Register s, int sh5); + inline void srawi_( Register a, Register s, int sh5); + + // extended mnemonics for Shift Instructions + inline void sldi( Register a, Register s, int sh6); + inline void sldi_( Register a, Register s, int sh6); + inline void slwi( Register a, Register s, int sh5); + inline void slwi_( Register a, Register s, int sh5); + inline void srdi( Register a, Register s, int sh6); + inline void srdi_( Register a, Register s, int sh6); + inline void srwi( Register a, Register s, int sh5); + inline void srwi_( Register a, Register s, int sh5); + + inline void clrrdi( Register a, Register s, int ui6); + inline void clrrdi_( Register a, Register s, int ui6); + inline void clrldi( Register a, Register s, int ui6); + inline void clrldi_( Register a, Register s, int ui6); + inline void clrlsldi(Register a, Register s, int clrl6, int shl6); + inline void clrlsldi_(Register a, Register s, int clrl6, int shl6); + inline void extrdi( Register a, Register s, int n, int b); + // testbit with condition register + inline void testbitdi(ConditionRegister cr, Register a, Register s, int ui6); + + // rotate instructions + inline void rotldi( Register a, Register s, int n); + inline void rotrdi( Register a, Register s, int n); + inline void rotlwi( Register a, Register s, int n); + inline void rotrwi( Register a, Register s, int n); + + // Rotate Instructions + inline void rldic( Register a, Register s, int sh6, int mb6); + inline void rldic_( Register a, Register s, int sh6, int mb6); + inline void rldicr( Register a, Register s, int sh6, int mb6); + inline void rldicr_( Register a, Register s, int sh6, int mb6); + inline void rldicl( Register a, Register s, int sh6, int mb6); + inline void rldicl_( Register a, Register s, int sh6, int mb6); + inline void rlwinm( Register a, Register s, int sh5, int mb5, int me5); + inline void rlwinm_( Register a, Register s, int sh5, int mb5, int me5); + inline void rldimi( Register a, Register s, int sh6, int mb6); + inline void rldimi_( Register a, Register s, int sh6, int mb6); + inline void rlwimi( Register a, Register s, int sh5, int mb5, int me5); + inline void insrdi( Register a, Register s, int n, int b); + inline void insrwi( Register a, Register s, int n, int b); + + // PPC 1, section 3.3.2 Fixed-Point Load Instructions + // 4 bytes + inline void lwzx( Register d, Register s1, Register s2); + inline void lwz( Register d, int si16, Register s1); + inline void lwzu( Register d, int si16, Register s1); + + // 4 bytes + inline void lwax( Register d, Register s1, Register s2); + inline void lwa( Register d, int si16, Register s1); + + // 4 bytes reversed + inline void lwbrx( Register d, Register s1, Register s2); + + // 2 bytes + inline void lhzx( Register d, Register s1, Register s2); + inline void lhz( Register d, int si16, Register s1); + inline void lhzu( Register d, int si16, Register s1); + + // 2 bytes reversed + inline void lhbrx( Register d, Register s1, Register s2); + + // 2 bytes + inline void lhax( Register d, Register s1, Register s2); + inline void lha( Register d, int si16, Register s1); + inline void lhau( Register d, int si16, Register s1); + + // 1 byte + inline void lbzx( Register d, Register s1, Register s2); + inline void lbz( Register d, int si16, Register s1); + inline void lbzu( Register d, int si16, Register s1); + + // 8 bytes + inline void ldx( Register d, Register s1, Register s2); + inline void ld( Register d, int si16, Register s1); + inline void ldu( Register d, int si16, Register s1); + + // PPC 1, section 3.3.3 Fixed-Point Store Instructions + inline void stwx( Register d, Register s1, Register s2); + inline void stw( Register d, int si16, Register s1); + inline void stwu( Register d, int si16, Register s1); + + inline void sthx( Register d, Register s1, Register s2); + inline void sth( Register d, int si16, Register s1); + inline void sthu( Register d, int si16, Register s1); + + inline void stbx( Register d, Register s1, Register s2); + inline void stb( Register d, int si16, Register s1); + inline void stbu( Register d, int si16, Register s1); + + inline void stdx( Register d, Register s1, Register s2); + inline void std( Register d, int si16, Register s1); + inline void stdu( Register d, int si16, Register s1); + inline void stdux(Register s, Register a, Register b); + + // PPC 1, section 3.3.13 Move To/From System Register Instructions + inline void mtlr( Register s1); + inline void mflr( Register d); + inline void mtctr(Register s1); + inline void mfctr(Register d); + inline void mtcrf(int fxm, Register s); + inline void mfcr( Register d); + inline void mcrf( ConditionRegister crd, ConditionRegister cra); + inline void mtcr( Register s); + + // PPC 1, section 2.4.1 Branch Instructions + inline void b( address a, relocInfo::relocType rt = relocInfo::none); + inline void b( Label& L); + inline void bl( address a, relocInfo::relocType rt = relocInfo::none); + inline void bl( Label& L); + inline void bc( int boint, int biint, address a, relocInfo::relocType rt = relocInfo::none); + inline void bc( int boint, int biint, Label& L); + inline void bcl(int boint, int biint, address a, relocInfo::relocType rt = relocInfo::none); + inline void bcl(int boint, int biint, Label& L); + + inline void bclr( int boint, int biint, int bhint, relocInfo::relocType rt = relocInfo::none); + inline void bclrl( int boint, int biint, int bhint, relocInfo::relocType rt = relocInfo::none); + inline void bcctr( int boint, int biint, int bhint = bhintbhBCCTRisNotReturnButSame, + relocInfo::relocType rt = relocInfo::none); + inline void bcctrl(int boint, int biint, int bhint = bhintbhBCLRisReturn, + relocInfo::relocType rt = relocInfo::none); + + // helper function for b, bcxx + inline bool is_within_range_of_b(address a, address pc); + inline bool is_within_range_of_bcxx(address a, address pc); + + // get the destination of a bxx branch (b, bl, ba, bla) + static inline address bxx_destination(address baddr); + static inline address bxx_destination(int instr, address pc); + static inline intptr_t bxx_destination_offset(int instr, intptr_t bxx_pos); + + // extended mnemonics for branch instructions + inline void blt(ConditionRegister crx, Label& L); + inline void bgt(ConditionRegister crx, Label& L); + inline void beq(ConditionRegister crx, Label& L); + inline void bso(ConditionRegister crx, Label& L); + inline void bge(ConditionRegister crx, Label& L); + inline void ble(ConditionRegister crx, Label& L); + inline void bne(ConditionRegister crx, Label& L); + inline void bns(ConditionRegister crx, Label& L); + + // Branch instructions with static prediction hints. + inline void blt_predict_taken( ConditionRegister crx, Label& L); + inline void bgt_predict_taken( ConditionRegister crx, Label& L); + inline void beq_predict_taken( ConditionRegister crx, Label& L); + inline void bso_predict_taken( ConditionRegister crx, Label& L); + inline void bge_predict_taken( ConditionRegister crx, Label& L); + inline void ble_predict_taken( ConditionRegister crx, Label& L); + inline void bne_predict_taken( ConditionRegister crx, Label& L); + inline void bns_predict_taken( ConditionRegister crx, Label& L); + inline void blt_predict_not_taken(ConditionRegister crx, Label& L); + inline void bgt_predict_not_taken(ConditionRegister crx, Label& L); + inline void beq_predict_not_taken(ConditionRegister crx, Label& L); + inline void bso_predict_not_taken(ConditionRegister crx, Label& L); + inline void bge_predict_not_taken(ConditionRegister crx, Label& L); + inline void ble_predict_not_taken(ConditionRegister crx, Label& L); + inline void bne_predict_not_taken(ConditionRegister crx, Label& L); + inline void bns_predict_not_taken(ConditionRegister crx, Label& L); + + // for use in conjunction with testbitdi: + inline void btrue( ConditionRegister crx, Label& L); + inline void bfalse(ConditionRegister crx, Label& L); + + inline void bltl(ConditionRegister crx, Label& L); + inline void bgtl(ConditionRegister crx, Label& L); + inline void beql(ConditionRegister crx, Label& L); + inline void bsol(ConditionRegister crx, Label& L); + inline void bgel(ConditionRegister crx, Label& L); + inline void blel(ConditionRegister crx, Label& L); + inline void bnel(ConditionRegister crx, Label& L); + inline void bnsl(ConditionRegister crx, Label& L); + + // extended mnemonics for Branch Instructions via LR + // We use `blr' for returns. + inline void blr(relocInfo::relocType rt = relocInfo::none); + + // extended mnemonics for Branch Instructions with CTR + // bdnz means `decrement CTR and jump to L if CTR is not zero' + inline void bdnz(Label& L); + // Decrement and branch if result is zero. + inline void bdz(Label& L); + // we use `bctr[l]' for jumps/calls in function descriptor glue + // code, e.g. calls to runtime functions + inline void bctr( relocInfo::relocType rt = relocInfo::none); + inline void bctrl(relocInfo::relocType rt = relocInfo::none); + // conditional jumps/branches via CTR + inline void beqctr( ConditionRegister crx, relocInfo::relocType rt = relocInfo::none); + inline void beqctrl(ConditionRegister crx, relocInfo::relocType rt = relocInfo::none); + inline void bnectr( ConditionRegister crx, relocInfo::relocType rt = relocInfo::none); + inline void bnectrl(ConditionRegister crx, relocInfo::relocType rt = relocInfo::none); + + // condition register logic instructions + inline void crand( int d, int s1, int s2); + inline void crnand(int d, int s1, int s2); + inline void cror( int d, int s1, int s2); + inline void crxor( int d, int s1, int s2); + inline void crnor( int d, int s1, int s2); + inline void creqv( int d, int s1, int s2); + inline void crandc(int d, int s1, int s2); + inline void crorc( int d, int s1, int s2); + + // icache and dcache related instructions + inline void icbi( Register s1, Register s2); + //inline void dcba(Register s1, Register s2); // Instruction for embedded processor only. + inline void dcbz( Register s1, Register s2); + inline void dcbst( Register s1, Register s2); + inline void dcbf( Register s1, Register s2); + + enum ct_cache_specification { + ct_primary_cache = 0, + ct_secondary_cache = 2 + }; + // dcache read hint + inline void dcbt( Register s1, Register s2); + inline void dcbtct( Register s1, Register s2, int ct); + inline void dcbtds( Register s1, Register s2, int ds); + // dcache write hint + inline void dcbtst( Register s1, Register s2); + inline void dcbtstct(Register s1, Register s2, int ct); + + // machine barrier instructions: + // + // - sync two-way memory barrier, aka fence + // - lwsync orders Store|Store, + // Load|Store, + // Load|Load, + // but not Store|Load + // - eieio orders memory accesses for device memory (only) + // - isync invalidates speculatively executed instructions + // From the Power ISA 2.06 documentation: + // "[...] an isync instruction prevents the execution of + // instructions following the isync until instructions + // preceding the isync have completed, [...]" + // From IBM's AIX assembler reference: + // "The isync [...] instructions causes the processor to + // refetch any instructions that might have been fetched + // prior to the isync instruction. The instruction isync + // causes the processor to wait for all previous instructions + // to complete. Then any instructions already fetched are + // discarded and instruction processing continues in the + // environment established by the previous instructions." + // + // semantic barrier instructions: + // (as defined in orderAccess.hpp) + // + // - release orders Store|Store, (maps to lwsync) + // Load|Store + // - acquire orders Load|Store, (maps to lwsync) + // Load|Load + // - fence orders Store|Store, (maps to sync) + // Load|Store, + // Load|Load, + // Store|Load + // + private: + inline void sync(int l); + public: + inline void sync(); + inline void lwsync(); + inline void ptesync(); + inline void eieio(); + inline void isync(); + + inline void release(); + inline void acquire(); + inline void fence(); + + // atomics + inline void lwarx_unchecked(Register d, Register a, Register b, int eh1 = 0); + inline void ldarx_unchecked(Register d, Register a, Register b, int eh1 = 0); + inline bool lxarx_hint_exclusive_access(); + inline void lwarx( Register d, Register a, Register b, bool hint_exclusive_access = false); + inline void ldarx( Register d, Register a, Register b, bool hint_exclusive_access = false); + inline void stwcx_( Register s, Register a, Register b); + inline void stdcx_( Register s, Register a, Register b); + + // Instructions for adjusting thread priority for simultaneous + // multithreading (SMT) on Power5. + private: + inline void smt_prio_very_low(); + inline void smt_prio_medium_high(); + inline void smt_prio_high(); + + public: + inline void smt_prio_low(); + inline void smt_prio_medium_low(); + inline void smt_prio_medium(); + + // trap instructions + inline void twi_0(Register a); // for load with acquire semantics use load+twi_0+isync (trap can't occur) + // NOT FOR DIRECT USE!! + protected: + inline void tdi_unchecked(int tobits, Register a, int si16); + inline void twi_unchecked(int tobits, Register a, int si16); + inline void tdi( int tobits, Register a, int si16); // asserts UseSIGTRAP + inline void twi( int tobits, Register a, int si16); // asserts UseSIGTRAP + inline void td( int tobits, Register a, Register b); // asserts UseSIGTRAP + inline void tw( int tobits, Register a, Register b); // asserts UseSIGTRAP + + static bool is_tdi(int x, int tobits, int ra, int si16) { + return (TDI_OPCODE == (x & TDI_OPCODE_MASK)) + && (tobits == inv_to_field(x)) + && (ra == -1/*any reg*/ || ra == inv_ra_field(x)) + && (si16 == inv_si_field(x)); + } + + static bool is_twi(int x, int tobits, int ra, int si16) { + return (TWI_OPCODE == (x & TWI_OPCODE_MASK)) + && (tobits == inv_to_field(x)) + && (ra == -1/*any reg*/ || ra == inv_ra_field(x)) + && (si16 == inv_si_field(x)); + } + + static bool is_twi(int x, int tobits, int ra) { + return (TWI_OPCODE == (x & TWI_OPCODE_MASK)) + && (tobits == inv_to_field(x)) + && (ra == -1/*any reg*/ || ra == inv_ra_field(x)); + } + + static bool is_td(int x, int tobits, int ra, int rb) { + return (TD_OPCODE == (x & TD_OPCODE_MASK)) + && (tobits == inv_to_field(x)) + && (ra == -1/*any reg*/ || ra == inv_ra_field(x)) + && (rb == -1/*any reg*/ || rb == inv_rb_field(x)); + } + + static bool is_tw(int x, int tobits, int ra, int rb) { + return (TW_OPCODE == (x & TW_OPCODE_MASK)) + && (tobits == inv_to_field(x)) + && (ra == -1/*any reg*/ || ra == inv_ra_field(x)) + && (rb == -1/*any reg*/ || rb == inv_rb_field(x)); + } + + public: + // PPC floating point instructions + // PPC 1, section 4.6.2 Floating-Point Load Instructions + inline void lfs( FloatRegister d, int si16, Register a); + inline void lfsu( FloatRegister d, int si16, Register a); + inline void lfsx( FloatRegister d, Register a, Register b); + inline void lfd( FloatRegister d, int si16, Register a); + inline void lfdu( FloatRegister d, int si16, Register a); + inline void lfdx( FloatRegister d, Register a, Register b); + + // PPC 1, section 4.6.3 Floating-Point Store Instructions + inline void stfs( FloatRegister s, int si16, Register a); + inline void stfsu( FloatRegister s, int si16, Register a); + inline void stfsx( FloatRegister s, Register a, Register b); + inline void stfd( FloatRegister s, int si16, Register a); + inline void stfdu( FloatRegister s, int si16, Register a); + inline void stfdx( FloatRegister s, Register a, Register b); + + // PPC 1, section 4.6.4 Floating-Point Move Instructions + inline void fmr( FloatRegister d, FloatRegister b); + inline void fmr_( FloatRegister d, FloatRegister b); + + // inline void mffgpr( FloatRegister d, Register b); + // inline void mftgpr( Register d, FloatRegister b); + inline void cmpb( Register a, Register s, Register b); + inline void popcntb(Register a, Register s); + inline void popcntw(Register a, Register s); + inline void popcntd(Register a, Register s); + + inline void fneg( FloatRegister d, FloatRegister b); + inline void fneg_( FloatRegister d, FloatRegister b); + inline void fabs( FloatRegister d, FloatRegister b); + inline void fabs_( FloatRegister d, FloatRegister b); + inline void fnabs( FloatRegister d, FloatRegister b); + inline void fnabs_(FloatRegister d, FloatRegister b); + + // PPC 1, section 4.6.5.1 Floating-Point Elementary Arithmetic Instructions + inline void fadd( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fadd_( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fadds( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fadds_(FloatRegister d, FloatRegister a, FloatRegister b); + inline void fsub( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fsub_( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fsubs( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fsubs_(FloatRegister d, FloatRegister a, FloatRegister b); + inline void fmul( FloatRegister d, FloatRegister a, FloatRegister c); + inline void fmul_( FloatRegister d, FloatRegister a, FloatRegister c); + inline void fmuls( FloatRegister d, FloatRegister a, FloatRegister c); + inline void fmuls_(FloatRegister d, FloatRegister a, FloatRegister c); + inline void fdiv( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fdiv_( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fdivs( FloatRegister d, FloatRegister a, FloatRegister b); + inline void fdivs_(FloatRegister d, FloatRegister a, FloatRegister b); + + // PPC 1, section 4.6.6 Floating-Point Rounding and Conversion Instructions + inline void frsp( FloatRegister d, FloatRegister b); + inline void fctid( FloatRegister d, FloatRegister b); + inline void fctidz(FloatRegister d, FloatRegister b); + inline void fctiw( FloatRegister d, FloatRegister b); + inline void fctiwz(FloatRegister d, FloatRegister b); + inline void fcfid( FloatRegister d, FloatRegister b); + inline void fcfids(FloatRegister d, FloatRegister b); + + // PPC 1, section 4.6.7 Floating-Point Compare Instructions + inline void fcmpu( ConditionRegister crx, FloatRegister a, FloatRegister b); + + inline void fsqrt( FloatRegister d, FloatRegister b); + inline void fsqrts(FloatRegister d, FloatRegister b); + + // Vector instructions for >= Power6. + inline void lvebx( VectorRegister d, Register s1, Register s2); + inline void lvehx( VectorRegister d, Register s1, Register s2); + inline void lvewx( VectorRegister d, Register s1, Register s2); + inline void lvx( VectorRegister d, Register s1, Register s2); + inline void lvxl( VectorRegister d, Register s1, Register s2); + inline void stvebx( VectorRegister d, Register s1, Register s2); + inline void stvehx( VectorRegister d, Register s1, Register s2); + inline void stvewx( VectorRegister d, Register s1, Register s2); + inline void stvx( VectorRegister d, Register s1, Register s2); + inline void stvxl( VectorRegister d, Register s1, Register s2); + inline void lvsl( VectorRegister d, Register s1, Register s2); + inline void lvsr( VectorRegister d, Register s1, Register s2); + inline void vpkpx( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkshss( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkswss( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkshus( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkswus( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkuhum( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkuwum( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkuhus( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vpkuwus( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vupkhpx( VectorRegister d, VectorRegister b); + inline void vupkhsb( VectorRegister d, VectorRegister b); + inline void vupkhsh( VectorRegister d, VectorRegister b); + inline void vupklpx( VectorRegister d, VectorRegister b); + inline void vupklsb( VectorRegister d, VectorRegister b); + inline void vupklsh( VectorRegister d, VectorRegister b); + inline void vmrghb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmrghw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmrghh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmrglb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmrglw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmrglh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsplt( VectorRegister d, int ui4, VectorRegister b); + inline void vsplth( VectorRegister d, int ui3, VectorRegister b); + inline void vspltw( VectorRegister d, int ui2, VectorRegister b); + inline void vspltisb( VectorRegister d, int si5); + inline void vspltish( VectorRegister d, int si5); + inline void vspltisw( VectorRegister d, int si5); + inline void vperm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vsel( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vsl( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsldoi( VectorRegister d, VectorRegister a, VectorRegister b, int si4); + inline void vslo( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsr( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsro( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vaddcuw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vaddshs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vaddsbs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vaddsws( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vaddubm( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vadduwm( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vadduhm( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vaddubs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vadduws( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vadduhs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubcuw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubshs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubsbs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubsws( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsububm( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubuwm( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubuhm( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsububs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubuws( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsubuhs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmulesb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmuleub( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmulesh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmuleuh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmulosb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmuloub( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmulosh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmulouh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmhaddshs(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmhraddshs(VectorRegister d,VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmladduhm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsubuhm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsummbm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsumshm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsumshs( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsumuhm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vmsumuhs( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vsumsws( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsum2sws( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsum4sbs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsum4ubs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsum4shs( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vavgsb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vavgsw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vavgsh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vavgub( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vavguw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vavguh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmaxsb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmaxsw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmaxsh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmaxub( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmaxuw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vmaxuh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vminsb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vminsw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vminsh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vminub( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vminuw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vminuh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpequb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpequh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpequw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtsh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtsb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtsw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtub( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtuh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtuw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpequb_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpequh_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpequw_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtsh_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtsb_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtsw_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtub_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtuh_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vcmpgtuw_(VectorRegister d, VectorRegister a, VectorRegister b); + inline void vand( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vandc( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vnor( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vor( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vxor( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vrlb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vrlw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vrlh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vslb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vskw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vslh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsrb( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsrw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsrh( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsrab( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsraw( VectorRegister d, VectorRegister a, VectorRegister b); + inline void vsrah( VectorRegister d, VectorRegister a, VectorRegister b); + // Vector Floating-Point not implemented yet + inline void mtvscr( VectorRegister b); + inline void mfvscr( VectorRegister d); + + // The following encoders use r0 as second operand. These instructions + // read r0 as '0'. + inline void lwzx( Register d, Register s2); + inline void lwz( Register d, int si16); + inline void lwax( Register d, Register s2); + inline void lwa( Register d, int si16); + inline void lwbrx(Register d, Register s2); + inline void lhzx( Register d, Register s2); + inline void lhz( Register d, int si16); + inline void lhax( Register d, Register s2); + inline void lha( Register d, int si16); + inline void lhbrx(Register d, Register s2); + inline void lbzx( Register d, Register s2); + inline void lbz( Register d, int si16); + inline void ldx( Register d, Register s2); + inline void ld( Register d, int si16); + inline void stwx( Register d, Register s2); + inline void stw( Register d, int si16); + inline void sthx( Register d, Register s2); + inline void sth( Register d, int si16); + inline void stbx( Register d, Register s2); + inline void stb( Register d, int si16); + inline void stdx( Register d, Register s2); + inline void std( Register d, int si16); + + // PPC 2, section 3.2.1 Instruction Cache Instructions + inline void icbi( Register s2); + // PPC 2, section 3.2.2 Data Cache Instructions + //inlinevoid dcba( Register s2); // Instruction for embedded processor only. + inline void dcbz( Register s2); + inline void dcbst( Register s2); + inline void dcbf( Register s2); + // dcache read hint + inline void dcbt( Register s2); + inline void dcbtct( Register s2, int ct); + inline void dcbtds( Register s2, int ds); + // dcache write hint + inline void dcbtst( Register s2); + inline void dcbtstct(Register s2, int ct); + + // Atomics: use ra0mem to disallow R0 as base. + inline void lwarx_unchecked(Register d, Register b, int eh1); + inline void ldarx_unchecked(Register d, Register b, int eh1); + inline void lwarx( Register d, Register b, bool hint_exclusive_access); + inline void ldarx( Register d, Register b, bool hint_exclusive_access); + inline void stwcx_(Register s, Register b); + inline void stdcx_(Register s, Register b); + inline void lfs( FloatRegister d, int si16); + inline void lfsx( FloatRegister d, Register b); + inline void lfd( FloatRegister d, int si16); + inline void lfdx( FloatRegister d, Register b); + inline void stfs( FloatRegister s, int si16); + inline void stfsx( FloatRegister s, Register b); + inline void stfd( FloatRegister s, int si16); + inline void stfdx( FloatRegister s, Register b); + inline void lvebx( VectorRegister d, Register s2); + inline void lvehx( VectorRegister d, Register s2); + inline void lvewx( VectorRegister d, Register s2); + inline void lvx( VectorRegister d, Register s2); + inline void lvxl( VectorRegister d, Register s2); + inline void stvebx(VectorRegister d, Register s2); + inline void stvehx(VectorRegister d, Register s2); + inline void stvewx(VectorRegister d, Register s2); + inline void stvx( VectorRegister d, Register s2); + inline void stvxl( VectorRegister d, Register s2); + inline void lvsl( VectorRegister d, Register s2); + inline void lvsr( VectorRegister d, Register s2); + + // RegisterOrConstant versions. + // These emitters choose between the versions using two registers and + // those with register and immediate, depending on the content of roc. + // If the constant is not encodable as immediate, instructions to + // load the constant are emitted beforehand. Store instructions need a + // tmp reg if the constant is not encodable as immediate. + // Size unpredictable. + void ld( Register d, RegisterOrConstant roc, Register s1 = noreg); + void lwa( Register d, RegisterOrConstant roc, Register s1 = noreg); + void lwz( Register d, RegisterOrConstant roc, Register s1 = noreg); + void lha( Register d, RegisterOrConstant roc, Register s1 = noreg); + void lhz( Register d, RegisterOrConstant roc, Register s1 = noreg); + void lbz( Register d, RegisterOrConstant roc, Register s1 = noreg); + void std( Register d, RegisterOrConstant roc, Register s1 = noreg, Register tmp = noreg); + void stw( Register d, RegisterOrConstant roc, Register s1 = noreg, Register tmp = noreg); + void sth( Register d, RegisterOrConstant roc, Register s1 = noreg, Register tmp = noreg); + void stb( Register d, RegisterOrConstant roc, Register s1 = noreg, Register tmp = noreg); + void add( Register d, RegisterOrConstant roc, Register s1); + void subf(Register d, RegisterOrConstant roc, Register s1); + void cmpd(ConditionRegister d, RegisterOrConstant roc, Register s1); + + + // Emit several instructions to load a 64 bit constant. This issues a fixed + // instruction pattern so that the constant can be patched later on. + enum { + load_const_size = 5 * BytesPerInstWord + }; + void load_const(Register d, long a, Register tmp = noreg); + inline void load_const(Register d, void* a, Register tmp = noreg); + inline void load_const(Register d, Label& L, Register tmp = noreg); + inline void load_const(Register d, AddressLiteral& a, Register tmp = noreg); + + // Load a 64 bit constant, optimized, not identifyable. + // Tmp can be used to increase ILP. Set return_simm16_rest = true to get a + // 16 bit immediate offset. This is useful if the offset can be encoded in + // a succeeding instruction. + int load_const_optimized(Register d, long a, Register tmp = noreg, bool return_simm16_rest = false); + inline int load_const_optimized(Register d, void* a, Register tmp = noreg, bool return_simm16_rest = false) { + return load_const_optimized(d, (long)(unsigned long)a, tmp, return_simm16_rest); + } + + // Creation + Assembler(CodeBuffer* code) : AbstractAssembler(code) { +#ifdef CHECK_DELAY + delay_state = no_delay; +#endif + } + + // Testing +#ifndef PRODUCT + void test_asm(); +#endif +}; + +#include "macroAssembler_ppc.hpp" + +#ifdef ASSERT +// On RISC, there's no benefit to verifying instruction boundaries. +inline bool AbstractAssembler::pd_check_instruction_mark() { return false; } +#endif + +#endif // CPU_PPC_VM_ASSEMBLER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/assembler_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/assembler_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_ASSEMBLER_PPC_INLINE_HPP +#define CPU_PPC_VM_ASSEMBLER_PPC_INLINE_HPP + +#include "asm/assembler.inline.hpp" +#include "asm/codeBuffer.hpp" +#include "code/codeCache.hpp" +#include "runtime/handles.inline.hpp" + + +inline void Assembler::emit_long(int x) { + AbstractAssembler::emit_long(x); +} +inline void Assembler::emit_int32(int x) { + AbstractAssembler::emit_long(x); +} + +inline void Assembler::emit_data(int x) { + emit_int32(x); +} + +inline void Assembler::emit_data(int x, relocInfo::relocType rtype) { + relocate(rtype); + emit_int32(x); +} + +inline void Assembler::emit_data(int x, RelocationHolder const& rspec) { + relocate(rspec); + emit_int32(x); +} + +// Emit an address +inline address Assembler::emit_addr(const address addr) { + address start = pc(); + + *(address*)_code_pos = addr; + _code_pos += sizeof(address); + code()->set_insts_end(_code_pos); + + return start; +} + +#if !defined(ABI_ELFv2) +// Emit a function descriptor with the specified entry point, TOC, and +// ENV. If the entry point is NULL, the descriptor will point just +// past the descriptor. +inline address Assembler::emit_fd(address entry, address toc, address env) { + FunctionDescriptor* fd = (FunctionDescriptor*)pc(); + + assert(sizeof(FunctionDescriptor) == 3*sizeof(address), "function descriptor size"); + + (void)emit_addr(); + (void)emit_addr(); + (void)emit_addr(); + + fd->set_entry(entry == NULL ? pc() : entry); + fd->set_toc(toc); + fd->set_env(env); + + return (address)fd; +} +#endif + +// Issue an illegal instruction. 0 is guaranteed to be an illegal instruction. +inline void Assembler::illtrap() { Assembler::emit_int32(0); } +inline bool Assembler::is_illtrap(int x) { return x == 0; } + +// PPC 1, section 3.3.8, Fixed-Point Arithmetic Instructions +inline void Assembler::addi( Register d, Register a, int si16) { assert(a != R0, "r0 not allowed"); addi_r0ok( d, a, si16); } +inline void Assembler::addis( Register d, Register a, int si16) { assert(a != R0, "r0 not allowed"); addis_r0ok(d, a, si16); } +inline void Assembler::addi_r0ok(Register d,Register a,int si16) { emit_int32(ADDI_OPCODE | rt(d) | ra(a) | simm(si16, 16)); } +inline void Assembler::addis_r0ok(Register d,Register a,int si16) { emit_int32(ADDIS_OPCODE | rt(d) | ra(a) | simm(si16, 16)); } +inline void Assembler::addic_( Register d, Register a, int si16) { emit_int32(ADDIC__OPCODE | rt(d) | ra(a) | simm(si16, 16)); } +inline void Assembler::subfic( Register d, Register a, int si16) { emit_int32(SUBFIC_OPCODE | rt(d) | ra(a) | simm(si16, 16)); } +inline void Assembler::add( Register d, Register a, Register b) { emit_int32(ADD_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::add_( Register d, Register a, Register b) { emit_int32(ADD_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::subf( Register d, Register a, Register b) { emit_int32(SUBF_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::sub( Register d, Register a, Register b) { subf(d, b, a); } +inline void Assembler::subf_( Register d, Register a, Register b) { emit_int32(SUBF_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::addc( Register d, Register a, Register b) { emit_int32(ADDC_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::addc_( Register d, Register a, Register b) { emit_int32(ADDC_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::subfc( Register d, Register a, Register b) { emit_int32(SUBFC_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::subfc_( Register d, Register a, Register b) { emit_int32(SUBFC_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::adde( Register d, Register a, Register b) { emit_int32(ADDE_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::adde_( Register d, Register a, Register b) { emit_int32(ADDE_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::subfe( Register d, Register a, Register b) { emit_int32(SUBFE_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::subfe_( Register d, Register a, Register b) { emit_int32(SUBFE_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::neg( Register d, Register a) { emit_int32(NEG_OPCODE | rt(d) | ra(a) | oe(0) | rc(0)); } +inline void Assembler::neg_( Register d, Register a) { emit_int32(NEG_OPCODE | rt(d) | ra(a) | oe(0) | rc(1)); } +inline void Assembler::mulli( Register d, Register a, int si16) { emit_int32(MULLI_OPCODE | rt(d) | ra(a) | simm(si16, 16)); } +inline void Assembler::mulld( Register d, Register a, Register b) { emit_int32(MULLD_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::mulld_( Register d, Register a, Register b) { emit_int32(MULLD_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::mullw( Register d, Register a, Register b) { emit_int32(MULLW_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::mullw_( Register d, Register a, Register b) { emit_int32(MULLW_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::mulhw( Register d, Register a, Register b) { emit_int32(MULHW_OPCODE | rt(d) | ra(a) | rb(b) | rc(0)); } +inline void Assembler::mulhw_( Register d, Register a, Register b) { emit_int32(MULHW_OPCODE | rt(d) | ra(a) | rb(b) | rc(1)); } +inline void Assembler::mulhd( Register d, Register a, Register b) { emit_int32(MULHD_OPCODE | rt(d) | ra(a) | rb(b) | rc(0)); } +inline void Assembler::mulhd_( Register d, Register a, Register b) { emit_int32(MULHD_OPCODE | rt(d) | ra(a) | rb(b) | rc(1)); } +inline void Assembler::mulhdu( Register d, Register a, Register b) { emit_int32(MULHDU_OPCODE | rt(d) | ra(a) | rb(b) | rc(0)); } +inline void Assembler::mulhdu_(Register d, Register a, Register b) { emit_int32(MULHDU_OPCODE | rt(d) | ra(a) | rb(b) | rc(1)); } +inline void Assembler::divd( Register d, Register a, Register b) { emit_int32(DIVD_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::divd_( Register d, Register a, Register b) { emit_int32(DIVD_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } +inline void Assembler::divw( Register d, Register a, Register b) { emit_int32(DIVW_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(0)); } +inline void Assembler::divw_( Register d, Register a, Register b) { emit_int32(DIVW_OPCODE | rt(d) | ra(a) | rb(b) | oe(0) | rc(1)); } + +// extended mnemonics +inline void Assembler::li( Register d, int si16) { Assembler::addi_r0ok( d, R0, si16); } +inline void Assembler::lis( Register d, int si16) { Assembler::addis_r0ok(d, R0, si16); } +inline void Assembler::addir(Register d, int si16, Register a) { Assembler::addi(d, a, si16); } + +// PPC 1, section 3.3.9, Fixed-Point Compare Instructions +inline void Assembler::cmpi( ConditionRegister f, int l, Register a, int si16) { emit_int32( CMPI_OPCODE | bf(f) | l10(l) | ra(a) | simm(si16,16)); } +inline void Assembler::cmp( ConditionRegister f, int l, Register a, Register b) { emit_int32( CMP_OPCODE | bf(f) | l10(l) | ra(a) | rb(b)); } +inline void Assembler::cmpli( ConditionRegister f, int l, Register a, int ui16) { emit_int32( CMPLI_OPCODE | bf(f) | l10(l) | ra(a) | uimm(ui16,16)); } +inline void Assembler::cmpl( ConditionRegister f, int l, Register a, Register b) { emit_int32( CMPL_OPCODE | bf(f) | l10(l) | ra(a) | rb(b)); } + +// extended mnemonics of Compare Instructions +inline void Assembler::cmpwi( ConditionRegister crx, Register a, int si16) { Assembler::cmpi( crx, 0, a, si16); } +inline void Assembler::cmpdi( ConditionRegister crx, Register a, int si16) { Assembler::cmpi( crx, 1, a, si16); } +inline void Assembler::cmpw( ConditionRegister crx, Register a, Register b) { Assembler::cmp( crx, 0, a, b); } +inline void Assembler::cmpd( ConditionRegister crx, Register a, Register b) { Assembler::cmp( crx, 1, a, b); } +inline void Assembler::cmplwi(ConditionRegister crx, Register a, int ui16) { Assembler::cmpli(crx, 0, a, ui16); } +inline void Assembler::cmpldi(ConditionRegister crx, Register a, int ui16) { Assembler::cmpli(crx, 1, a, ui16); } +inline void Assembler::cmplw( ConditionRegister crx, Register a, Register b) { Assembler::cmpl( crx, 0, a, b); } +inline void Assembler::cmpld( ConditionRegister crx, Register a, Register b) { Assembler::cmpl( crx, 1, a, b); } + +inline void Assembler::isel(Register d, Register a, Register b, int c) { guarantee(VM_Version::has_isel(), "opcode not supported on this hardware"); + emit_int32(ISEL_OPCODE | rt(d) | ra(a) | rb(b) | bc(c)); } + +// PPC 1, section 3.3.11, Fixed-Point Logical Instructions +inline void Assembler::andi_( Register a, Register s, int ui16) { emit_int32(ANDI_OPCODE | rta(a) | rs(s) | uimm(ui16, 16)); } +inline void Assembler::andis_( Register a, Register s, int ui16) { emit_int32(ANDIS_OPCODE | rta(a) | rs(s) | uimm(ui16, 16)); } +inline void Assembler::ori( Register a, Register s, int ui16) { emit_int32(ORI_OPCODE | rta(a) | rs(s) | uimm(ui16, 16)); } +inline void Assembler::oris( Register a, Register s, int ui16) { emit_int32(ORIS_OPCODE | rta(a) | rs(s) | uimm(ui16, 16)); } +inline void Assembler::xori( Register a, Register s, int ui16) { emit_int32(XORI_OPCODE | rta(a) | rs(s) | uimm(ui16, 16)); } +inline void Assembler::xoris( Register a, Register s, int ui16) { emit_int32(XORIS_OPCODE | rta(a) | rs(s) | uimm(ui16, 16)); } +inline void Assembler::andr( Register a, Register s, Register b) { emit_int32(AND_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::and_( Register a, Register s, Register b) { emit_int32(AND_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } + +inline void Assembler::or_unchecked(Register a, Register s, Register b){ emit_int32(OR_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::orr( Register a, Register s, Register b) { if (a==s && s==b) { Assembler::nop(); } else { Assembler::or_unchecked(a,s,b); } } +inline void Assembler::or_( Register a, Register s, Register b) { emit_int32(OR_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::xorr( Register a, Register s, Register b) { emit_int32(XOR_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::xor_( Register a, Register s, Register b) { emit_int32(XOR_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::nand( Register a, Register s, Register b) { emit_int32(NAND_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::nand_( Register a, Register s, Register b) { emit_int32(NAND_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::nor( Register a, Register s, Register b) { emit_int32(NOR_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::nor_( Register a, Register s, Register b) { emit_int32(NOR_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::andc( Register a, Register s, Register b) { emit_int32(ANDC_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::andc_( Register a, Register s, Register b) { emit_int32(ANDC_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::orc( Register a, Register s, Register b) { emit_int32(ORC_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::orc_( Register a, Register s, Register b) { emit_int32(ORC_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::extsb( Register a, Register s) { emit_int32(EXTSB_OPCODE | rta(a) | rs(s) | rc(0)); } +inline void Assembler::extsh( Register a, Register s) { emit_int32(EXTSH_OPCODE | rta(a) | rs(s) | rc(0)); } +inline void Assembler::extsw( Register a, Register s) { emit_int32(EXTSW_OPCODE | rta(a) | rs(s) | rc(0)); } + +// extended mnemonics +inline void Assembler::nop() { Assembler::ori(R0, R0, 0); } +// NOP for FP and BR units (different versions to allow them to be in one group) +inline void Assembler::fpnop0() { Assembler::fmr(F30, F30); } +inline void Assembler::fpnop1() { Assembler::fmr(F31, F31); } +inline void Assembler::brnop0() { Assembler::mcrf(CCR2, CCR2); } +inline void Assembler::brnop1() { Assembler::mcrf(CCR3, CCR3); } +inline void Assembler::brnop2() { Assembler::mcrf(CCR4, CCR4); } + +inline void Assembler::mr( Register d, Register s) { Assembler::orr(d, s, s); } +inline void Assembler::ori_opt( Register d, int ui16) { if (ui16!=0) Assembler::ori( d, d, ui16); } +inline void Assembler::oris_opt(Register d, int ui16) { if (ui16!=0) Assembler::oris(d, d, ui16); } + +inline void Assembler::endgroup() { Assembler::ori(R1, R1, 0); } + +// count instructions +inline void Assembler::cntlzw( Register a, Register s) { emit_int32(CNTLZW_OPCODE | rta(a) | rs(s) | rc(0)); } +inline void Assembler::cntlzw_( Register a, Register s) { emit_int32(CNTLZW_OPCODE | rta(a) | rs(s) | rc(1)); } +inline void Assembler::cntlzd( Register a, Register s) { emit_int32(CNTLZD_OPCODE | rta(a) | rs(s) | rc(0)); } +inline void Assembler::cntlzd_( Register a, Register s) { emit_int32(CNTLZD_OPCODE | rta(a) | rs(s) | rc(1)); } + +// PPC 1, section 3.3.12, Fixed-Point Rotate and Shift Instructions +inline void Assembler::sld( Register a, Register s, Register b) { emit_int32(SLD_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::sld_( Register a, Register s, Register b) { emit_int32(SLD_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::slw( Register a, Register s, Register b) { emit_int32(SLW_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::slw_( Register a, Register s, Register b) { emit_int32(SLW_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::srd( Register a, Register s, Register b) { emit_int32(SRD_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::srd_( Register a, Register s, Register b) { emit_int32(SRD_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::srw( Register a, Register s, Register b) { emit_int32(SRW_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::srw_( Register a, Register s, Register b) { emit_int32(SRW_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::srad( Register a, Register s, Register b) { emit_int32(SRAD_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::srad_( Register a, Register s, Register b) { emit_int32(SRAD_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::sraw( Register a, Register s, Register b) { emit_int32(SRAW_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::sraw_( Register a, Register s, Register b) { emit_int32(SRAW_OPCODE | rta(a) | rs(s) | rb(b) | rc(1)); } +inline void Assembler::sradi( Register a, Register s, int sh6) { emit_int32(SRADI_OPCODE | rta(a) | rs(s) | sh162030(sh6) | rc(0)); } +inline void Assembler::sradi_( Register a, Register s, int sh6) { emit_int32(SRADI_OPCODE | rta(a) | rs(s) | sh162030(sh6) | rc(1)); } +inline void Assembler::srawi( Register a, Register s, int sh5) { emit_int32(SRAWI_OPCODE | rta(a) | rs(s) | sh1620(sh5) | rc(0)); } +inline void Assembler::srawi_( Register a, Register s, int sh5) { emit_int32(SRAWI_OPCODE | rta(a) | rs(s) | sh1620(sh5) | rc(1)); } + +// extended mnemonics for Shift Instructions +inline void Assembler::sldi( Register a, Register s, int sh6) { Assembler::rldicr(a, s, sh6, 63-sh6); } +inline void Assembler::sldi_( Register a, Register s, int sh6) { Assembler::rldicr_(a, s, sh6, 63-sh6); } +inline void Assembler::slwi( Register a, Register s, int sh5) { Assembler::rlwinm(a, s, sh5, 0, 31-sh5); } +inline void Assembler::slwi_( Register a, Register s, int sh5) { Assembler::rlwinm_(a, s, sh5, 0, 31-sh5); } +inline void Assembler::srdi( Register a, Register s, int sh6) { Assembler::rldicl(a, s, 64-sh6, sh6); } +inline void Assembler::srdi_( Register a, Register s, int sh6) { Assembler::rldicl_(a, s, 64-sh6, sh6); } +inline void Assembler::srwi( Register a, Register s, int sh5) { Assembler::rlwinm(a, s, 32-sh5, sh5, 31); } +inline void Assembler::srwi_( Register a, Register s, int sh5) { Assembler::rlwinm_(a, s, 32-sh5, sh5, 31); } + +inline void Assembler::clrrdi( Register a, Register s, int ui6) { Assembler::rldicr(a, s, 0, 63-ui6); } +inline void Assembler::clrrdi_( Register a, Register s, int ui6) { Assembler::rldicr_(a, s, 0, 63-ui6); } +inline void Assembler::clrldi( Register a, Register s, int ui6) { Assembler::rldicl(a, s, 0, ui6); } +inline void Assembler::clrldi_( Register a, Register s, int ui6) { Assembler::rldicl_(a, s, 0, ui6); } +inline void Assembler::clrlsldi( Register a, Register s, int clrl6, int shl6) { Assembler::rldic( a, s, shl6, clrl6-shl6); } +inline void Assembler::clrlsldi_(Register a, Register s, int clrl6, int shl6) { Assembler::rldic_(a, s, shl6, clrl6-shl6); } +inline void Assembler::extrdi( Register a, Register s, int n, int b){ Assembler::rldicl(a, s, b+n, 64-n); } +// testbit with condition register. +inline void Assembler::testbitdi(ConditionRegister cr, Register a, Register s, int ui6) { + if (cr == CCR0) { + Assembler::rldicr_(a, s, 63-ui6, 0); + } else { + Assembler::rldicr(a, s, 63-ui6, 0); + Assembler::cmpdi(cr, a, 0); + } +} + +// rotate instructions +inline void Assembler::rotldi( Register a, Register s, int n) { Assembler::rldicl(a, s, n, 0); } +inline void Assembler::rotrdi( Register a, Register s, int n) { Assembler::rldicl(a, s, 64-n, 0); } +inline void Assembler::rotlwi( Register a, Register s, int n) { Assembler::rlwinm(a, s, n, 0, 31); } +inline void Assembler::rotrwi( Register a, Register s, int n) { Assembler::rlwinm(a, s, 32-n, 0, 31); } + +inline void Assembler::rldic( Register a, Register s, int sh6, int mb6) { emit_int32(RLDIC_OPCODE | rta(a) | rs(s) | sh162030(sh6) | mb2126(mb6) | rc(0)); } +inline void Assembler::rldic_( Register a, Register s, int sh6, int mb6) { emit_int32(RLDIC_OPCODE | rta(a) | rs(s) | sh162030(sh6) | mb2126(mb6) | rc(1)); } +inline void Assembler::rldicr( Register a, Register s, int sh6, int mb6) { emit_int32(RLDICR_OPCODE | rta(a) | rs(s) | sh162030(sh6) | mb2126(mb6) | rc(0)); } +inline void Assembler::rldicr_( Register a, Register s, int sh6, int mb6) { emit_int32(RLDICR_OPCODE | rta(a) | rs(s) | sh162030(sh6) | mb2126(mb6) | rc(1)); } +inline void Assembler::rldicl( Register a, Register s, int sh6, int me6) { emit_int32(RLDICL_OPCODE | rta(a) | rs(s) | sh162030(sh6) | me2126(me6) | rc(0)); } +inline void Assembler::rldicl_( Register a, Register s, int sh6, int me6) { emit_int32(RLDICL_OPCODE | rta(a) | rs(s) | sh162030(sh6) | me2126(me6) | rc(1)); } +inline void Assembler::rlwinm( Register a, Register s, int sh5, int mb5, int me5){ emit_int32(RLWINM_OPCODE | rta(a) | rs(s) | sh1620(sh5) | mb2125(mb5) | me2630(me5) | rc(0)); } +inline void Assembler::rlwinm_( Register a, Register s, int sh5, int mb5, int me5){ emit_int32(RLWINM_OPCODE | rta(a) | rs(s) | sh1620(sh5) | mb2125(mb5) | me2630(me5) | rc(1)); } +inline void Assembler::rldimi( Register a, Register s, int sh6, int mb6) { emit_int32(RLDIMI_OPCODE | rta(a) | rs(s) | sh162030(sh6) | mb2126(mb6) | rc(0)); } +inline void Assembler::rlwimi( Register a, Register s, int sh5, int mb5, int me5){ emit_int32(RLWIMI_OPCODE | rta(a) | rs(s) | sh1620(sh5) | mb2125(mb5) | me2630(me5) | rc(0)); } +inline void Assembler::rldimi_( Register a, Register s, int sh6, int mb6) { emit_int32(RLDIMI_OPCODE | rta(a) | rs(s) | sh162030(sh6) | mb2126(mb6) | rc(1)); } +inline void Assembler::insrdi( Register a, Register s, int n, int b) { Assembler::rldimi(a, s, 64-(b+n), b); } +inline void Assembler::insrwi( Register a, Register s, int n, int b) { Assembler::rlwimi(a, s, 32-(b+n), b, b+n-1); } + +// PPC 1, section 3.3.2 Fixed-Point Load Instructions +inline void Assembler::lwzx( Register d, Register s1, Register s2) { emit_int32(LWZX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::lwz( Register d, int si16, Register s1) { emit_int32(LWZ_OPCODE | rt(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::lwzu( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LWZU_OPCODE | rt(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::lwax( Register d, Register s1, Register s2) { emit_int32(LWAX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::lwa( Register d, int si16, Register s1) { emit_int32(LWA_OPCODE | rt(d) | ds(si16) | ra0mem(s1));} + +inline void Assembler::lwbrx( Register d, Register s1, Register s2) { emit_int32(LWBRX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} + +inline void Assembler::lhzx( Register d, Register s1, Register s2) { emit_int32(LHZX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::lhz( Register d, int si16, Register s1) { emit_int32(LHZ_OPCODE | rt(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::lhzu( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LHZU_OPCODE | rt(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::lhbrx( Register d, Register s1, Register s2) { emit_int32(LHBRX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} + +inline void Assembler::lhax( Register d, Register s1, Register s2) { emit_int32(LHAX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::lha( Register d, int si16, Register s1) { emit_int32(LHA_OPCODE | rt(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::lhau( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LHAU_OPCODE | rt(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::lbzx( Register d, Register s1, Register s2) { emit_int32(LBZX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::lbz( Register d, int si16, Register s1) { emit_int32(LBZ_OPCODE | rt(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::lbzu( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LBZU_OPCODE | rt(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::ld( Register d, int si16, Register s1) { emit_int32(LD_OPCODE | rt(d) | ds(si16) | ra0mem(s1));} +inline void Assembler::ldx( Register d, Register s1, Register s2) { emit_int32(LDX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::ldu( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LDU_OPCODE | rt(d) | ds(si16) | rta0mem(s1));} + +// PPC 1, section 3.3.3 Fixed-Point Store Instructions +inline void Assembler::stwx( Register d, Register s1, Register s2) { emit_int32(STWX_OPCODE | rs(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::stw( Register d, int si16, Register s1) { emit_int32(STW_OPCODE | rs(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::stwu( Register d, int si16, Register s1) { emit_int32(STWU_OPCODE | rs(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::sthx( Register d, Register s1, Register s2) { emit_int32(STHX_OPCODE | rs(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::sth( Register d, int si16, Register s1) { emit_int32(STH_OPCODE | rs(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::sthu( Register d, int si16, Register s1) { emit_int32(STHU_OPCODE | rs(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::stbx( Register d, Register s1, Register s2) { emit_int32(STBX_OPCODE | rs(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::stb( Register d, int si16, Register s1) { emit_int32(STB_OPCODE | rs(d) | d1(si16) | ra0mem(s1));} +inline void Assembler::stbu( Register d, int si16, Register s1) { emit_int32(STBU_OPCODE | rs(d) | d1(si16) | rta0mem(s1));} + +inline void Assembler::std( Register d, int si16, Register s1) { emit_int32(STD_OPCODE | rs(d) | ds(si16) | ra0mem(s1));} +inline void Assembler::stdx( Register d, Register s1, Register s2) { emit_int32(STDX_OPCODE | rs(d) | ra0mem(s1) | rb(s2));} +inline void Assembler::stdu( Register d, int si16, Register s1) { emit_int32(STDU_OPCODE | rs(d) | ds(si16) | rta0mem(s1));} +inline void Assembler::stdux(Register s, Register a, Register b) { emit_int32(STDUX_OPCODE| rs(s) | rta0mem(a) | rb(b));} + +// PPC 1, section 3.3.13 Move To/From System Register Instructions +inline void Assembler::mtlr( Register s1) { emit_int32(MTLR_OPCODE | rs(s1)); } +inline void Assembler::mflr( Register d ) { emit_int32(MFLR_OPCODE | rt(d)); } +inline void Assembler::mtctr(Register s1) { emit_int32(MTCTR_OPCODE | rs(s1)); } +inline void Assembler::mfctr(Register d ) { emit_int32(MFCTR_OPCODE | rt(d)); } +inline void Assembler::mtcrf(int afxm, Register s){ emit_int32(MTCRF_OPCODE | fxm(afxm) | rs(s)); } +inline void Assembler::mfcr( Register d ) { emit_int32(MFCR_OPCODE | rt(d)); } +inline void Assembler::mcrf( ConditionRegister crd, ConditionRegister cra) + { emit_int32(MCRF_OPCODE | bf(crd) | bfa(cra)); } +inline void Assembler::mtcr( Register s) { Assembler::mtcrf(0xff, s); } + +// SAP JVM 2006-02-13 PPC branch instruction. +// PPC 1, section 2.4.1 Branch Instructions +inline void Assembler::b( address a, relocInfo::relocType rt) { emit_data(BXX_OPCODE| li(disp( intptr_t(a), intptr_t(pc()))) |aa(0)|lk(0), rt); } +inline void Assembler::b( Label& L) { b( target(L)); } +inline void Assembler::bl(address a, relocInfo::relocType rt) { emit_data(BXX_OPCODE| li(disp( intptr_t(a), intptr_t(pc()))) |aa(0)|lk(1), rt); } +inline void Assembler::bl(Label& L) { bl(target(L)); } +inline void Assembler::bc( int boint, int biint, address a, relocInfo::relocType rt) { emit_data(BCXX_OPCODE| bo(boint) | bi(biint) | bd(disp( intptr_t(a), intptr_t(pc()))) | aa(0) | lk(0), rt); } +inline void Assembler::bc( int boint, int biint, Label& L) { bc(boint, biint, target(L)); } +inline void Assembler::bcl(int boint, int biint, address a, relocInfo::relocType rt) { emit_data(BCXX_OPCODE| bo(boint) | bi(biint) | bd(disp( intptr_t(a), intptr_t(pc()))) | aa(0)|lk(1)); } +inline void Assembler::bcl(int boint, int biint, Label& L) { bcl(boint, biint, target(L)); } + +inline void Assembler::bclr( int boint, int biint, int bhint, relocInfo::relocType rt) { emit_data(BCLR_OPCODE | bo(boint) | bi(biint) | bh(bhint) | aa(0) | lk(0), rt); } +inline void Assembler::bclrl( int boint, int biint, int bhint, relocInfo::relocType rt) { emit_data(BCLR_OPCODE | bo(boint) | bi(biint) | bh(bhint) | aa(0) | lk(1), rt); } +inline void Assembler::bcctr( int boint, int biint, int bhint, relocInfo::relocType rt) { emit_data(BCCTR_OPCODE| bo(boint) | bi(biint) | bh(bhint) | aa(0) | lk(0), rt); } +inline void Assembler::bcctrl(int boint, int biint, int bhint, relocInfo::relocType rt) { emit_data(BCCTR_OPCODE| bo(boint) | bi(biint) | bh(bhint) | aa(0) | lk(1), rt); } + +// helper function for b +inline bool Assembler::is_within_range_of_b(address a, address pc) { + // Guard against illegal branch targets, e.g. -1 (see CompiledStaticCall and ad-file). + if ((((uint64_t)a) & 0x3) != 0) return false; + + const int range = 1 << (29-6); // li field is from bit 6 to bit 29. + int value = disp(intptr_t(a), intptr_t(pc)); + bool result = -range <= value && value < range-1; +#ifdef ASSERT + if (result) li(value); // Assert that value is in correct range. +#endif + return result; +} + +// helper functions for bcxx. +inline bool Assembler::is_within_range_of_bcxx(address a, address pc) { + // Guard against illegal branch targets, e.g. -1 (see CompiledStaticCall and ad-file). + if ((((uint64_t)a) & 0x3) != 0) return false; + + const int range = 1 << (29-16); // bd field is from bit 16 to bit 29. + int value = disp(intptr_t(a), intptr_t(pc)); + bool result = -range <= value && value < range-1; +#ifdef ASSERT + if (result) bd(value); // Assert that value is in correct range. +#endif + return result; +} + +// Get the destination of a bxx branch (b, bl, ba, bla). +address Assembler::bxx_destination(address baddr) { return bxx_destination(*(int*)baddr, baddr); } +address Assembler::bxx_destination(int instr, address pc) { return (address)bxx_destination_offset(instr, (intptr_t)pc); } +intptr_t Assembler::bxx_destination_offset(int instr, intptr_t bxx_pos) { + intptr_t displ = inv_li_field(instr); + return bxx_pos + displ; +} + +// Extended mnemonics for Branch Instructions +inline void Assembler::blt(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs1, bi0(crx, less), L); } +inline void Assembler::bgt(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs1, bi0(crx, greater), L); } +inline void Assembler::beq(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs1, bi0(crx, equal), L); } +inline void Assembler::bso(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs1, bi0(crx, summary_overflow), L); } +inline void Assembler::bge(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs0, bi0(crx, less), L); } +inline void Assembler::ble(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs0, bi0(crx, greater), L); } +inline void Assembler::bne(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs0, bi0(crx, equal), L); } +inline void Assembler::bns(ConditionRegister crx, Label& L) { Assembler::bc(bcondCRbiIs0, bi0(crx, summary_overflow), L); } + +// Branch instructions with static prediction hints. +inline void Assembler::blt_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsTaken, bi0(crx, less), L); } +inline void Assembler::bgt_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsTaken, bi0(crx, greater), L); } +inline void Assembler::beq_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsTaken, bi0(crx, equal), L); } +inline void Assembler::bso_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsTaken, bi0(crx, summary_overflow), L); } +inline void Assembler::bge_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsTaken, bi0(crx, less), L); } +inline void Assembler::ble_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsTaken, bi0(crx, greater), L); } +inline void Assembler::bne_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsTaken, bi0(crx, equal), L); } +inline void Assembler::bns_predict_taken (ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsTaken, bi0(crx, summary_overflow), L); } +inline void Assembler::blt_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsNotTaken, bi0(crx, less), L); } +inline void Assembler::bgt_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsNotTaken, bi0(crx, greater), L); } +inline void Assembler::beq_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsNotTaken, bi0(crx, equal), L); } +inline void Assembler::bso_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs1_bhintIsNotTaken, bi0(crx, summary_overflow), L); } +inline void Assembler::bge_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsNotTaken, bi0(crx, less), L); } +inline void Assembler::ble_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsNotTaken, bi0(crx, greater), L); } +inline void Assembler::bne_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsNotTaken, bi0(crx, equal), L); } +inline void Assembler::bns_predict_not_taken(ConditionRegister crx, Label& L) { bc(bcondCRbiIs0_bhintIsNotTaken, bi0(crx, summary_overflow), L); } + +// For use in conjunction with testbitdi: +inline void Assembler::btrue( ConditionRegister crx, Label& L) { Assembler::bne(crx, L); } +inline void Assembler::bfalse(ConditionRegister crx, Label& L) { Assembler::beq(crx, L); } + +inline void Assembler::bltl(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs1, bi0(crx, less), L); } +inline void Assembler::bgtl(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs1, bi0(crx, greater), L); } +inline void Assembler::beql(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs1, bi0(crx, equal), L); } +inline void Assembler::bsol(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs1, bi0(crx, summary_overflow), L); } +inline void Assembler::bgel(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs0, bi0(crx, less), L); } +inline void Assembler::blel(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs0, bi0(crx, greater), L); } +inline void Assembler::bnel(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs0, bi0(crx, equal), L); } +inline void Assembler::bnsl(ConditionRegister crx, Label& L) { Assembler::bcl(bcondCRbiIs0, bi0(crx, summary_overflow), L); } + +// Extended mnemonics for Branch Instructions via LR. +// We use `blr' for returns. +inline void Assembler::blr(relocInfo::relocType rt) { Assembler::bclr(bcondAlways, 0, bhintbhBCLRisReturn, rt); } + +// Extended mnemonics for Branch Instructions with CTR. +// Bdnz means `decrement CTR and jump to L if CTR is not zero'. +inline void Assembler::bdnz(Label& L) { Assembler::bc(16, 0, L); } +// Decrement and branch if result is zero. +inline void Assembler::bdz(Label& L) { Assembler::bc(18, 0, L); } +// We use `bctr[l]' for jumps/calls in function descriptor glue +// code, e.g. for calls to runtime functions. +inline void Assembler::bctr( relocInfo::relocType rt) { Assembler::bcctr(bcondAlways, 0, bhintbhBCCTRisNotReturnButSame, rt); } +inline void Assembler::bctrl(relocInfo::relocType rt) { Assembler::bcctrl(bcondAlways, 0, bhintbhBCCTRisNotReturnButSame, rt); } +// Conditional jumps/branches via CTR. +inline void Assembler::beqctr( ConditionRegister crx, relocInfo::relocType rt) { Assembler::bcctr( bcondCRbiIs1, bi0(crx, equal), bhintbhBCCTRisNotReturnButSame, rt); } +inline void Assembler::beqctrl(ConditionRegister crx, relocInfo::relocType rt) { Assembler::bcctrl(bcondCRbiIs1, bi0(crx, equal), bhintbhBCCTRisNotReturnButSame, rt); } +inline void Assembler::bnectr( ConditionRegister crx, relocInfo::relocType rt) { Assembler::bcctr( bcondCRbiIs0, bi0(crx, equal), bhintbhBCCTRisNotReturnButSame, rt); } +inline void Assembler::bnectrl(ConditionRegister crx, relocInfo::relocType rt) { Assembler::bcctrl(bcondCRbiIs0, bi0(crx, equal), bhintbhBCCTRisNotReturnButSame, rt); } + +// condition register logic instructions +inline void Assembler::crand( int d, int s1, int s2) { emit_int32(CRAND_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::crnand(int d, int s1, int s2) { emit_int32(CRNAND_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::cror( int d, int s1, int s2) { emit_int32(CROR_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::crxor( int d, int s1, int s2) { emit_int32(CRXOR_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::crnor( int d, int s1, int s2) { emit_int32(CRNOR_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::creqv( int d, int s1, int s2) { emit_int32(CREQV_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::crandc(int d, int s1, int s2) { emit_int32(CRANDC_OPCODE | bt(d) | ba(s1) | bb(s2)); } +inline void Assembler::crorc( int d, int s1, int s2) { emit_int32(CRORC_OPCODE | bt(d) | ba(s1) | bb(s2)); } + +// Conditional move (>= Power7) +inline void Assembler::isel( Register d, ConditionRegister cr, Condition cc, bool inv, Register a, Register b) { + if (b == noreg) b = d; // Can be omitted if old value should be kept in "else" case. + Register first = a, second = b; + if (inv) { + first = b; second = a; // exchange + } + assert(first != R0, "r0 not allowed"); + isel(d, first, second, bi0(cr, cc)); +} +inline void Assembler::isel_0( Register d, ConditionRegister cr, Condition cc, Register b) { + if (b == noreg) b = d; // Can be omitted if old value should be kept in "else" case. + isel(d, R0, b, bi0(cr, cc)); +} + +// PPC 2, section 3.2.1 Instruction Cache Instructions +inline void Assembler::icbi( Register s1, Register s2) { emit_int32( ICBI_OPCODE | ra0mem(s1) | rb(s2) ); } +// PPC 2, section 3.2.2 Data Cache Instructions +//inline void Assembler::dcba( Register s1, Register s2) { emit_int32( DCBA_OPCODE | ra0mem(s1) | rb(s2) ); } +inline void Assembler::dcbz( Register s1, Register s2) { emit_int32( DCBZ_OPCODE | ra0mem(s1) | rb(s2) ); } +inline void Assembler::dcbst( Register s1, Register s2) { emit_int32( DCBST_OPCODE | ra0mem(s1) | rb(s2) ); } +inline void Assembler::dcbf( Register s1, Register s2) { emit_int32( DCBF_OPCODE | ra0mem(s1) | rb(s2) ); } +// dcache read hint +inline void Assembler::dcbt( Register s1, Register s2) { emit_int32( DCBT_OPCODE | ra0mem(s1) | rb(s2) ); } +inline void Assembler::dcbtct( Register s1, Register s2, int ct) { emit_int32( DCBT_OPCODE | ra0mem(s1) | rb(s2) | thct(ct)); } +inline void Assembler::dcbtds( Register s1, Register s2, int ds) { emit_int32( DCBT_OPCODE | ra0mem(s1) | rb(s2) | thds(ds)); } +// dcache write hint +inline void Assembler::dcbtst( Register s1, Register s2) { emit_int32( DCBTST_OPCODE | ra0mem(s1) | rb(s2) ); } +inline void Assembler::dcbtstct(Register s1, Register s2, int ct) { emit_int32( DCBTST_OPCODE | ra0mem(s1) | rb(s2) | thct(ct)); } + +// machine barrier instructions: +inline void Assembler::sync(int a) { emit_int32( SYNC_OPCODE | l910(a)); } +inline void Assembler::sync() { Assembler::sync(0); } +inline void Assembler::lwsync() { Assembler::sync(1); } +inline void Assembler::ptesync() { Assembler::sync(2); } +inline void Assembler::eieio() { emit_int32( EIEIO_OPCODE); } +inline void Assembler::isync() { emit_int32( ISYNC_OPCODE); } + +inline void Assembler::release() { Assembler::lwsync(); } +inline void Assembler::acquire() { Assembler::lwsync(); } +inline void Assembler::fence() { Assembler::sync(); } + +// atomics +// Use ra0mem to disallow R0 as base. +inline void Assembler::lwarx_unchecked(Register d, Register a, Register b, int eh1) { emit_int32( LWARX_OPCODE | rt(d) | ra0mem(a) | rb(b) | eh(eh1)); } +inline void Assembler::ldarx_unchecked(Register d, Register a, Register b, int eh1) { emit_int32( LDARX_OPCODE | rt(d) | ra0mem(a) | rb(b) | eh(eh1)); } +inline bool Assembler::lxarx_hint_exclusive_access() { return VM_Version::has_lxarxeh(); } +inline void Assembler::lwarx( Register d, Register a, Register b, bool hint_exclusive_access) { lwarx_unchecked(d, a, b, (hint_exclusive_access && lxarx_hint_exclusive_access() && UseExtendedLoadAndReserveInstructionsPPC64) ? 1 : 0); } +inline void Assembler::ldarx( Register d, Register a, Register b, bool hint_exclusive_access) { ldarx_unchecked(d, a, b, (hint_exclusive_access && lxarx_hint_exclusive_access() && UseExtendedLoadAndReserveInstructionsPPC64) ? 1 : 0); } +inline void Assembler::stwcx_(Register s, Register a, Register b) { emit_int32( STWCX_OPCODE | rs(s) | ra0mem(a) | rb(b) | rc(1)); } +inline void Assembler::stdcx_(Register s, Register a, Register b) { emit_int32( STDCX_OPCODE | rs(s) | ra0mem(a) | rb(b) | rc(1)); } + +// Instructions for adjusting thread priority +// for simultaneous multithreading (SMT) on POWER5. +inline void Assembler::smt_prio_very_low() { Assembler::or_unchecked(R31, R31, R31); } +inline void Assembler::smt_prio_low() { Assembler::or_unchecked(R1, R1, R1); } +inline void Assembler::smt_prio_medium_low() { Assembler::or_unchecked(R6, R6, R6); } +inline void Assembler::smt_prio_medium() { Assembler::or_unchecked(R2, R2, R2); } +inline void Assembler::smt_prio_medium_high() { Assembler::or_unchecked(R5, R5, R5); } +inline void Assembler::smt_prio_high() { Assembler::or_unchecked(R3, R3, R3); } + +inline void Assembler::twi_0(Register a) { twi_unchecked(0, a, 0);} + +// trap instructions +inline void Assembler::tdi_unchecked(int tobits, Register a, int si16){ emit_int32( TDI_OPCODE | to(tobits) | ra(a) | si(si16)); } +inline void Assembler::twi_unchecked(int tobits, Register a, int si16){ emit_int32( TWI_OPCODE | to(tobits) | ra(a) | si(si16)); } +inline void Assembler::tdi(int tobits, Register a, int si16) { assert(UseSIGTRAP, "precondition"); tdi_unchecked(tobits, a, si16); } +inline void Assembler::twi(int tobits, Register a, int si16) { assert(UseSIGTRAP, "precondition"); twi_unchecked(tobits, a, si16); } +inline void Assembler::td( int tobits, Register a, Register b) { assert(UseSIGTRAP, "precondition"); emit_int32( TD_OPCODE | to(tobits) | ra(a) | rb(b)); } +inline void Assembler::tw( int tobits, Register a, Register b) { assert(UseSIGTRAP, "precondition"); emit_int32( TW_OPCODE | to(tobits) | ra(a) | rb(b)); } + +// FLOATING POINT instructions ppc. +// PPC 1, section 4.6.2 Floating-Point Load Instructions +// Use ra0mem instead of ra in some instructions below. +inline void Assembler::lfs( FloatRegister d, int si16, Register a) { emit_int32( LFS_OPCODE | frt(d) | ra0mem(a) | simm(si16,16)); } +inline void Assembler::lfsu(FloatRegister d, int si16, Register a) { emit_int32( LFSU_OPCODE | frt(d) | ra(a) | simm(si16,16)); } +inline void Assembler::lfsx(FloatRegister d, Register a, Register b) { emit_int32( LFSX_OPCODE | frt(d) | ra0mem(a) | rb(b)); } +inline void Assembler::lfd( FloatRegister d, int si16, Register a) { emit_int32( LFD_OPCODE | frt(d) | ra0mem(a) | simm(si16,16)); } +inline void Assembler::lfdu(FloatRegister d, int si16, Register a) { emit_int32( LFDU_OPCODE | frt(d) | ra(a) | simm(si16,16)); } +inline void Assembler::lfdx(FloatRegister d, Register a, Register b) { emit_int32( LFDX_OPCODE | frt(d) | ra0mem(a) | rb(b)); } + +// PPC 1, section 4.6.3 Floating-Point Store Instructions +// Use ra0mem instead of ra in some instructions below. +inline void Assembler::stfs( FloatRegister s, int si16, Register a) { emit_int32( STFS_OPCODE | frs(s) | ra0mem(a) | simm(si16,16)); } +inline void Assembler::stfsu(FloatRegister s, int si16, Register a) { emit_int32( STFSU_OPCODE | frs(s) | ra(a) | simm(si16,16)); } +inline void Assembler::stfsx(FloatRegister s, Register a, Register b){ emit_int32( STFSX_OPCODE | frs(s) | ra0mem(a) | rb(b)); } +inline void Assembler::stfd( FloatRegister s, int si16, Register a) { emit_int32( STFD_OPCODE | frs(s) | ra0mem(a) | simm(si16,16)); } +inline void Assembler::stfdu(FloatRegister s, int si16, Register a) { emit_int32( STFDU_OPCODE | frs(s) | ra(a) | simm(si16,16)); } +inline void Assembler::stfdx(FloatRegister s, Register a, Register b){ emit_int32( STFDX_OPCODE | frs(s) | ra0mem(a) | rb(b)); } + +// PPC 1, section 4.6.4 Floating-Point Move Instructions +inline void Assembler::fmr( FloatRegister d, FloatRegister b) { emit_int32( FMR_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fmr_(FloatRegister d, FloatRegister b) { emit_int32( FMR_OPCODE | frt(d) | frb(b) | rc(1)); } + +// These are special Power6 opcodes, reused for "lfdepx" and "stfdepx" +// on Power7. Do not use. +//inline void Assembler::mffgpr( FloatRegister d, Register b) { emit_int32( MFFGPR_OPCODE | frt(d) | rb(b) | rc(0)); } +//inline void Assembler::mftgpr( Register d, FloatRegister b) { emit_int32( MFTGPR_OPCODE | rt(d) | frb(b) | rc(0)); } +// add cmpb and popcntb to detect ppc power version. +inline void Assembler::cmpb( Register a, Register s, Register b) { guarantee(VM_Version::has_cmpb(), "opcode not supported on this hardware"); + emit_int32( CMPB_OPCODE | rta(a) | rs(s) | rb(b) | rc(0)); } +inline void Assembler::popcntb(Register a, Register s) { guarantee(VM_Version::has_popcntb(), "opcode not supported on this hardware"); + emit_int32( POPCNTB_OPCODE | rta(a) | rs(s)); }; +inline void Assembler::popcntw(Register a, Register s) { guarantee(VM_Version::has_popcntw(), "opcode not supported on this hardware"); + emit_int32( POPCNTW_OPCODE | rta(a) | rs(s)); }; +inline void Assembler::popcntd(Register a, Register s) { emit_int32( POPCNTD_OPCODE | rta(a) | rs(s)); }; + +inline void Assembler::fneg( FloatRegister d, FloatRegister b) { emit_int32( FNEG_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fneg_( FloatRegister d, FloatRegister b) { emit_int32( FNEG_OPCODE | frt(d) | frb(b) | rc(1)); } +inline void Assembler::fabs( FloatRegister d, FloatRegister b) { emit_int32( FABS_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fabs_( FloatRegister d, FloatRegister b) { emit_int32( FABS_OPCODE | frt(d) | frb(b) | rc(1)); } +inline void Assembler::fnabs( FloatRegister d, FloatRegister b) { emit_int32( FNABS_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fnabs_(FloatRegister d, FloatRegister b) { emit_int32( FNABS_OPCODE | frt(d) | frb(b) | rc(1)); } + +// PPC 1, section 4.6.5.1 Floating-Point Elementary Arithmetic Instructions +inline void Assembler::fadd( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FADD_OPCODE | frt(d) | fra(a) | frb(b) | rc(0)); } +inline void Assembler::fadd_( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FADD_OPCODE | frt(d) | fra(a) | frb(b) | rc(1)); } +inline void Assembler::fadds( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FADDS_OPCODE | frt(d) | fra(a) | frb(b) | rc(0)); } +inline void Assembler::fadds_(FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FADDS_OPCODE | frt(d) | fra(a) | frb(b) | rc(1)); } +inline void Assembler::fsub( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FSUB_OPCODE | frt(d) | fra(a) | frb(b) | rc(0)); } +inline void Assembler::fsub_( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FSUB_OPCODE | frt(d) | fra(a) | frb(b) | rc(1)); } +inline void Assembler::fsubs( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FSUBS_OPCODE | frt(d) | fra(a) | frb(b) | rc(0)); } +inline void Assembler::fsubs_(FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FSUBS_OPCODE | frt(d) | fra(a) | frb(b) | rc(1)); } +inline void Assembler::fmul( FloatRegister d, FloatRegister a, FloatRegister c) { emit_int32( FMUL_OPCODE | frt(d) | fra(a) | frc(c) | rc(0)); } +inline void Assembler::fmul_( FloatRegister d, FloatRegister a, FloatRegister c) { emit_int32( FMUL_OPCODE | frt(d) | fra(a) | frc(c) | rc(1)); } +inline void Assembler::fmuls( FloatRegister d, FloatRegister a, FloatRegister c) { emit_int32( FMULS_OPCODE | frt(d) | fra(a) | frc(c) | rc(0)); } +inline void Assembler::fmuls_(FloatRegister d, FloatRegister a, FloatRegister c) { emit_int32( FMULS_OPCODE | frt(d) | fra(a) | frc(c) | rc(1)); } +inline void Assembler::fdiv( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FDIV_OPCODE | frt(d) | fra(a) | frb(b) | rc(0)); } +inline void Assembler::fdiv_( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FDIV_OPCODE | frt(d) | fra(a) | frb(b) | rc(1)); } +inline void Assembler::fdivs( FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FDIVS_OPCODE | frt(d) | fra(a) | frb(b) | rc(0)); } +inline void Assembler::fdivs_(FloatRegister d, FloatRegister a, FloatRegister b) { emit_int32( FDIVS_OPCODE | frt(d) | fra(a) | frb(b) | rc(1)); } + +// PPC 1, section 4.6.6 Floating-Point Rounding and Conversion Instructions +inline void Assembler::frsp( FloatRegister d, FloatRegister b) { emit_int32( FRSP_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fctid( FloatRegister d, FloatRegister b) { emit_int32( FCTID_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fctidz(FloatRegister d, FloatRegister b) { emit_int32( FCTIDZ_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fctiw( FloatRegister d, FloatRegister b) { emit_int32( FCTIW_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fctiwz(FloatRegister d, FloatRegister b) { emit_int32( FCTIWZ_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fcfid( FloatRegister d, FloatRegister b) { emit_int32( FCFID_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fcfids(FloatRegister d, FloatRegister b) { guarantee(VM_Version::has_fcfids(), "opcode not supported on this hardware"); + emit_int32( FCFIDS_OPCODE | frt(d) | frb(b) | rc(0)); } + +// PPC 1, section 4.6.7 Floating-Point Compare Instructions +inline void Assembler::fcmpu( ConditionRegister crx, FloatRegister a, FloatRegister b) { emit_int32( FCMPU_OPCODE | bf(crx) | fra(a) | frb(b)); } + +// PPC 1, section 5.2.1 Floating-Point Arithmetic Instructions +inline void Assembler::fsqrt( FloatRegister d, FloatRegister b) { guarantee(VM_Version::has_fsqrt(), "opcode not supported on this hardware"); + emit_int32( FSQRT_OPCODE | frt(d) | frb(b) | rc(0)); } +inline void Assembler::fsqrts(FloatRegister d, FloatRegister b) { guarantee(VM_Version::has_fsqrts(), "opcode not supported on this hardware"); + emit_int32( FSQRTS_OPCODE | frt(d) | frb(b) | rc(0)); } + +// Vector instructions for >= Power6. +inline void Assembler::lvebx( VectorRegister d, Register s1, Register s2) { emit_int32( LVEBX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::lvehx( VectorRegister d, Register s1, Register s2) { emit_int32( LVEHX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::lvewx( VectorRegister d, Register s1, Register s2) { emit_int32( LVEWX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::lvx( VectorRegister d, Register s1, Register s2) { emit_int32( LVX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::lvxl( VectorRegister d, Register s1, Register s2) { emit_int32( LVXL_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::stvebx(VectorRegister d, Register s1, Register s2) { emit_int32( STVEBX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::stvehx(VectorRegister d, Register s1, Register s2) { emit_int32( STVEHX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::stvewx(VectorRegister d, Register s1, Register s2) { emit_int32( STVEWX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::stvx( VectorRegister d, Register s1, Register s2) { emit_int32( STVX_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::stvxl( VectorRegister d, Register s1, Register s2) { emit_int32( STVXL_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::lvsl( VectorRegister d, Register s1, Register s2) { emit_int32( LVSL_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } +inline void Assembler::lvsr( VectorRegister d, Register s1, Register s2) { emit_int32( LVSR_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } + +inline void Assembler::vpkpx( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKPX_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkshss( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKSHSS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkswss( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKSWSS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkshus( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKSHUS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkswus( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKSWUS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkuhum( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKUHUM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkuwum( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKUWUM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkuhus( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKUHUS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vpkuwus( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPKUWUS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vupkhpx( VectorRegister d, VectorRegister b) { emit_int32( VUPKHPX_OPCODE | vrt(d) | vrb(b)); } +inline void Assembler::vupkhsb( VectorRegister d, VectorRegister b) { emit_int32( VUPKHSB_OPCODE | vrt(d) | vrb(b)); } +inline void Assembler::vupkhsh( VectorRegister d, VectorRegister b) { emit_int32( VUPKHSH_OPCODE | vrt(d) | vrb(b)); } +inline void Assembler::vupklpx( VectorRegister d, VectorRegister b) { emit_int32( VUPKLPX_OPCODE | vrt(d) | vrb(b)); } +inline void Assembler::vupklsb( VectorRegister d, VectorRegister b) { emit_int32( VUPKLSB_OPCODE | vrt(d) | vrb(b)); } +inline void Assembler::vupklsh( VectorRegister d, VectorRegister b) { emit_int32( VUPKLSH_OPCODE | vrt(d) | vrb(b)); } +inline void Assembler::vmrghb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMRGHB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmrghw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMRGHW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmrghh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMRGHH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmrglb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMRGLB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmrglw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMRGLW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmrglh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMRGLH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsplt( VectorRegister d, int ui4, VectorRegister b) { emit_int32( VSPLT_OPCODE | vrt(d) | vsplt_uim(uimm(ui4,4)) | vrb(b)); } +inline void Assembler::vsplth( VectorRegister d, int ui3, VectorRegister b) { emit_int32( VSPLTH_OPCODE | vrt(d) | vsplt_uim(uimm(ui3,3)) | vrb(b)); } +inline void Assembler::vspltw( VectorRegister d, int ui2, VectorRegister b) { emit_int32( VSPLTW_OPCODE | vrt(d) | vsplt_uim(uimm(ui2,2)) | vrb(b)); } +inline void Assembler::vspltisb(VectorRegister d, int si5) { emit_int32( VSPLTISB_OPCODE| vrt(d) | vsplti_sim(simm(si5,5))); } +inline void Assembler::vspltish(VectorRegister d, int si5) { emit_int32( VSPLTISH_OPCODE| vrt(d) | vsplti_sim(simm(si5,5))); } +inline void Assembler::vspltisw(VectorRegister d, int si5) { emit_int32( VSPLTISW_OPCODE| vrt(d) | vsplti_sim(simm(si5,5))); } +inline void Assembler::vperm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c){ emit_int32( VPERM_OPCODE | vrt(d) | vra(a) | vrb(b) | vrc(c)); } +inline void Assembler::vsel( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c){ emit_int32( VSEL_OPCODE | vrt(d) | vra(a) | vrb(b) | vrc(c)); } +inline void Assembler::vsl( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSL_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsldoi( VectorRegister d, VectorRegister a, VectorRegister b, int si4) { emit_int32( VSLDOI_OPCODE| vrt(d) | vra(a) | vrb(b) | vsldoi_shb(simm(si4,4))); } +inline void Assembler::vslo( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSLO_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsr( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSR_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsro( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRO_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vaddcuw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDCUW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vaddshs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDSHS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vaddsbs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDSBS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vaddsws( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDSWS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vaddubm( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDUBM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vadduwm( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDUWM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vadduhm( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDUHM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vaddubs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDUBS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vadduws( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDUWS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vadduhs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VADDUHS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubcuw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBCUW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubshs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBSHS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubsbs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBSBS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubsws( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBSWS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsububm( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBUBM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubuwm( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBUWM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubuhm( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBUHM_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsububs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBUBS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubuws( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBUWS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsubuhs( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUBUHS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmulesb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULESB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmuleub( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULEUB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmulesh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULESH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmuleuh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULEUH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmulosb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULOSB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmuloub( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULOUB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmulosh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULOSH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmulouh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMULOUH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmhaddshs(VectorRegister d,VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMHADDSHS_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmhraddshs(VectorRegister d,VectorRegister a,VectorRegister b, VectorRegister c) { emit_int32( VMHRADDSHS_OPCODE| vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmladduhm(VectorRegister d,VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMLADDUHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsubuhm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUBUHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsummbm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMMBM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsumshm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMSHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsumshs(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMSHS_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsumuhm(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMUHM_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vmsumuhs(VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c) { emit_int32( VMSUMUHS_OPCODE | vrt(d) | vra(a) | vrb(b)| vrc(c)); } +inline void Assembler::vsumsws( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUMSWS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsum2sws(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUM2SWS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsum4sbs(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUM4SBS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsum4ubs(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUM4UBS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsum4shs(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSUM4SHS_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vavgsb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VAVGSB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vavgsw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VAVGSW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vavgsh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VAVGSH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vavgub( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VAVGUB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vavguw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VAVGUW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vavguh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VAVGUH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmaxsb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMAXSB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmaxsw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMAXSW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmaxsh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMAXSH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmaxub( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMAXUB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmaxuw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMAXUW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vmaxuh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMAXUH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vminsb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMINSB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vminsw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMINSW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vminsh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMINSH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vminub( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMINUB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vminuw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMINUW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vminuh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VMINUH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vcmpequb(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPEQUB_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpequh(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPEQUH_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpequw(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPEQUW_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpgtsh(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPGTSH_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpgtsb(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPGTSB_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpgtsw(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPGTSW_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpgtub(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPGTUB_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpgtuh(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPGTUH_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpgtuw(VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VCMPGTUW_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(0)); } +inline void Assembler::vcmpequb_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPEQUB_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpequh_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPEQUH_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpequw_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPEQUW_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpgtsh_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPGTSH_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpgtsb_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPGTSB_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpgtsw_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPGTSW_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpgtub_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPGTUB_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpgtuh_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPGTUH_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vcmpgtuw_(VectorRegister d,VectorRegister a, VectorRegister b) { emit_int32( VCMPGTUW_OPCODE | vrt(d) | vra(a) | vrb(b) | vcmp_rc(1)); } +inline void Assembler::vand( VectorRegister d, VectorRegister a, VectorRegister b) { guarantee(VM_Version::has_vand(), "opcode not supported on this hardware"); + emit_int32( VAND_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vandc( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VANDC_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vnor( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VNOR_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vor( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VOR_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vxor( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VXOR_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vrlb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VRLB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vrlw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VRLW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vrlh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VRLH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vslb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSLB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vskw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSKW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vslh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSLH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsrb( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsrw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsrh( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsrab( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRAB_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsraw( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRAW_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::vsrah( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSRAH_OPCODE | vrt(d) | vra(a) | vrb(b)); } +inline void Assembler::mtvscr( VectorRegister b) { emit_int32( MTVSCR_OPCODE | vrb(b)); } +inline void Assembler::mfvscr( VectorRegister d) { emit_int32( MFVSCR_OPCODE | vrt(d)); } + +// ra0 version +inline void Assembler::lwzx( Register d, Register s2) { emit_int32( LWZX_OPCODE | rt(d) | rb(s2));} +inline void Assembler::lwz( Register d, int si16 ) { emit_int32( LWZ_OPCODE | rt(d) | d1(si16));} +inline void Assembler::lwax( Register d, Register s2) { emit_int32( LWAX_OPCODE | rt(d) | rb(s2));} +inline void Assembler::lwa( Register d, int si16 ) { emit_int32( LWA_OPCODE | rt(d) | ds(si16));} +inline void Assembler::lwbrx(Register d, Register s2) { emit_int32( LWBRX_OPCODE| rt(d) | rb(s2));} +inline void Assembler::lhzx( Register d, Register s2) { emit_int32( LHZX_OPCODE | rt(d) | rb(s2));} +inline void Assembler::lhz( Register d, int si16 ) { emit_int32( LHZ_OPCODE | rt(d) | d1(si16));} +inline void Assembler::lhax( Register d, Register s2) { emit_int32( LHAX_OPCODE | rt(d) | rb(s2));} +inline void Assembler::lha( Register d, int si16 ) { emit_int32( LHA_OPCODE | rt(d) | d1(si16));} +inline void Assembler::lhbrx(Register d, Register s2) { emit_int32( LHBRX_OPCODE| rt(d) | rb(s2));} +inline void Assembler::lbzx( Register d, Register s2) { emit_int32( LBZX_OPCODE | rt(d) | rb(s2));} +inline void Assembler::lbz( Register d, int si16 ) { emit_int32( LBZ_OPCODE | rt(d) | d1(si16));} +inline void Assembler::ld( Register d, int si16 ) { emit_int32( LD_OPCODE | rt(d) | ds(si16));} +inline void Assembler::ldx( Register d, Register s2) { emit_int32( LDX_OPCODE | rt(d) | rb(s2));} +inline void Assembler::stwx( Register d, Register s2) { emit_int32( STWX_OPCODE | rs(d) | rb(s2));} +inline void Assembler::stw( Register d, int si16 ) { emit_int32( STW_OPCODE | rs(d) | d1(si16));} +inline void Assembler::sthx( Register d, Register s2) { emit_int32( STHX_OPCODE | rs(d) | rb(s2));} +inline void Assembler::sth( Register d, int si16 ) { emit_int32( STH_OPCODE | rs(d) | d1(si16));} +inline void Assembler::stbx( Register d, Register s2) { emit_int32( STBX_OPCODE | rs(d) | rb(s2));} +inline void Assembler::stb( Register d, int si16 ) { emit_int32( STB_OPCODE | rs(d) | d1(si16));} +inline void Assembler::std( Register d, int si16 ) { emit_int32( STD_OPCODE | rs(d) | ds(si16));} +inline void Assembler::stdx( Register d, Register s2) { emit_int32( STDX_OPCODE | rs(d) | rb(s2));} + +// ra0 version +inline void Assembler::icbi( Register s2) { emit_int32( ICBI_OPCODE | rb(s2) ); } +//inline void Assembler::dcba( Register s2) { emit_int32( DCBA_OPCODE | rb(s2) ); } +inline void Assembler::dcbz( Register s2) { emit_int32( DCBZ_OPCODE | rb(s2) ); } +inline void Assembler::dcbst( Register s2) { emit_int32( DCBST_OPCODE | rb(s2) ); } +inline void Assembler::dcbf( Register s2) { emit_int32( DCBF_OPCODE | rb(s2) ); } +inline void Assembler::dcbt( Register s2) { emit_int32( DCBT_OPCODE | rb(s2) ); } +inline void Assembler::dcbtct( Register s2, int ct) { emit_int32( DCBT_OPCODE | rb(s2) | thct(ct)); } +inline void Assembler::dcbtds( Register s2, int ds) { emit_int32( DCBT_OPCODE | rb(s2) | thds(ds)); } +inline void Assembler::dcbtst( Register s2) { emit_int32( DCBTST_OPCODE | rb(s2) ); } +inline void Assembler::dcbtstct(Register s2, int ct) { emit_int32( DCBTST_OPCODE | rb(s2) | thct(ct)); } + +// ra0 version +inline void Assembler::lwarx_unchecked(Register d, Register b, int eh1) { emit_int32( LWARX_OPCODE | rt(d) | rb(b) | eh(eh1)); } +inline void Assembler::ldarx_unchecked(Register d, Register b, int eh1) { emit_int32( LDARX_OPCODE | rt(d) | rb(b) | eh(eh1)); } +inline void Assembler::lwarx( Register d, Register b, bool hint_exclusive_access){ lwarx_unchecked(d, b, (hint_exclusive_access && lxarx_hint_exclusive_access() && UseExtendedLoadAndReserveInstructionsPPC64) ? 1 : 0); } +inline void Assembler::ldarx( Register d, Register b, bool hint_exclusive_access){ ldarx_unchecked(d, b, (hint_exclusive_access && lxarx_hint_exclusive_access() && UseExtendedLoadAndReserveInstructionsPPC64) ? 1 : 0); } +inline void Assembler::stwcx_(Register s, Register b) { emit_int32( STWCX_OPCODE | rs(s) | rb(b) | rc(1)); } +inline void Assembler::stdcx_(Register s, Register b) { emit_int32( STDCX_OPCODE | rs(s) | rb(b) | rc(1)); } + +// ra0 version +inline void Assembler::lfs( FloatRegister d, int si16) { emit_int32( LFS_OPCODE | frt(d) | simm(si16,16)); } +inline void Assembler::lfsx(FloatRegister d, Register b) { emit_int32( LFSX_OPCODE | frt(d) | rb(b)); } +inline void Assembler::lfd( FloatRegister d, int si16) { emit_int32( LFD_OPCODE | frt(d) | simm(si16,16)); } +inline void Assembler::lfdx(FloatRegister d, Register b) { emit_int32( LFDX_OPCODE | frt(d) | rb(b)); } + +// ra0 version +inline void Assembler::stfs( FloatRegister s, int si16) { emit_int32( STFS_OPCODE | frs(s) | simm(si16, 16)); } +inline void Assembler::stfsx(FloatRegister s, Register b) { emit_int32( STFSX_OPCODE | frs(s) | rb(b)); } +inline void Assembler::stfd( FloatRegister s, int si16) { emit_int32( STFD_OPCODE | frs(s) | simm(si16, 16)); } +inline void Assembler::stfdx(FloatRegister s, Register b) { emit_int32( STFDX_OPCODE | frs(s) | rb(b)); } + +// ra0 version +inline void Assembler::lvebx( VectorRegister d, Register s2) { emit_int32( LVEBX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::lvehx( VectorRegister d, Register s2) { emit_int32( LVEHX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::lvewx( VectorRegister d, Register s2) { emit_int32( LVEWX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::lvx( VectorRegister d, Register s2) { emit_int32( LVX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::lvxl( VectorRegister d, Register s2) { emit_int32( LVXL_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::stvebx(VectorRegister d, Register s2) { emit_int32( STVEBX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::stvehx(VectorRegister d, Register s2) { emit_int32( STVEHX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::stvewx(VectorRegister d, Register s2) { emit_int32( STVEWX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::stvx( VectorRegister d, Register s2) { emit_int32( STVX_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::stvxl( VectorRegister d, Register s2) { emit_int32( STVXL_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::lvsl( VectorRegister d, Register s2) { emit_int32( LVSL_OPCODE | vrt(d) | rb(s2)); } +inline void Assembler::lvsr( VectorRegister d, Register s2) { emit_int32( LVSR_OPCODE | vrt(d) | rb(s2)); } + +inline void Assembler::load_const(Register d, void* x, Register tmp) { + load_const(d, (long)x, tmp); +} + +// Load a 64 bit constant encoded by a `Label'. This works for bound +// labels as well as unbound ones. For unbound labels, the code will +// be patched as soon as the label gets bound. +inline void Assembler::load_const(Register d, Label& L, Register tmp) { + load_const(d, target(L), tmp); +} + +// Load a 64 bit constant encoded by an AddressLiteral. patchable. +inline void Assembler::load_const(Register d, AddressLiteral& a, Register tmp) { + assert(d != R0, "R0 not allowed"); + // First relocate (we don't change the offset in the RelocationHolder, + // just pass a.rspec()), then delegate to load_const(Register, long). + relocate(a.rspec()); + load_const(d, (long)a.value(), tmp); +} + +#include "macroAssembler_ppc.inline.hpp" + +#endif // CPU_PPC_VM_ASSEMBLER_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/bytecodeInterpreter_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/bytecodeInterpreter_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_BYTECODEINTERPRETER_PPC_HPP +#define CPU_PPC_VM_BYTECODEINTERPRETER_PPC_HPP + +// Platform specific for C++ based Interpreter +#define LOTS_OF_REGS /* Lets interpreter use plenty of registers */ + +private: + + // Save the bottom of the stack after frame manager setup. For ease of restoration after return + // from recursive interpreter call. + intptr_t* _frame_bottom; // Saved bottom of frame manager frame. + address _last_Java_pc; // Pc to return to in frame manager. + intptr_t* _last_Java_fp; // frame pointer + intptr_t* _last_Java_sp; // stack pointer + interpreterState _self_link; // Previous interpreter state // sometimes points to self??? + double _native_fresult; // Save result of native calls that might return floats. + intptr_t _native_lresult; // Save result of native calls that might return handle/longs. + +public: + address last_Java_pc(void) { return _last_Java_pc; } + intptr_t* last_Java_fp(void) { return _last_Java_fp; } + + static ByteSize native_lresult_offset() { + return byte_offset_of(BytecodeInterpreter, _native_lresult); + } + + static ByteSize native_fresult_offset() { + return byte_offset_of(BytecodeInterpreter, _native_fresult); + } + + static void pd_layout_interpreterState(interpreterState istate, address last_Java_pc, intptr_t* last_Java_fp); + +#define SET_LAST_JAVA_FRAME() THREAD->frame_anchor()->set(istate->_last_Java_sp, istate->_last_Java_pc); +#define RESET_LAST_JAVA_FRAME() THREAD->frame_anchor()->clear(); + + +// Macros for accessing the stack. +#undef STACK_INT +#undef STACK_FLOAT +#undef STACK_ADDR +#undef STACK_OBJECT +#undef STACK_DOUBLE +#undef STACK_LONG + +// JavaStack Implementation +#define STACK_SLOT(offset) ((address) &topOfStack[-(offset)]) +#define STACK_INT(offset) (*((jint*) &topOfStack[-(offset)])) +#define STACK_FLOAT(offset) (*((jfloat *) &topOfStack[-(offset)])) +#define STACK_OBJECT(offset) (*((oop *) &topOfStack [-(offset)])) +#define STACK_DOUBLE(offset) (((VMJavaVal64*) &topOfStack[-(offset)])->d) +#define STACK_LONG(offset) (((VMJavaVal64 *) &topOfStack[-(offset)])->l) + +#define SET_STACK_SLOT(value, offset) (*(intptr_t*)&topOfStack[-(offset)] = *(intptr_t*)(value)) +#define SET_STACK_ADDR(value, offset) (*((address *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_INT(value, offset) (*((jint *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_FLOAT(value, offset) (*((jfloat *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_OBJECT(value, offset) (*((oop *)&topOfStack[-(offset)]) = (value)) +#define SET_STACK_DOUBLE(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = (value)) +#define SET_STACK_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->d = \ + ((VMJavaVal64*)(addr))->d) +#define SET_STACK_LONG(value, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = (value)) +#define SET_STACK_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&topOfStack[-(offset)])->l = \ + ((VMJavaVal64*)(addr))->l) +// JavaLocals implementation + +#define LOCALS_SLOT(offset) ((intptr_t*)&locals[-(offset)]) +#define LOCALS_ADDR(offset) ((address)locals[-(offset)]) +#define LOCALS_INT(offset) (*(jint*)&(locals[-(offset)])) +#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) +#define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) +#define LOCALS_DOUBLE_AT(offset) (((address)&locals[-((offset) + 1)])) + +#define SET_LOCALS_SLOT(value, offset) (*(intptr_t*)&locals[-(offset)] = *(intptr_t *)(value)) +#define SET_LOCALS_INT(value, offset) (*((jint *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_DOUBLE(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = (value)) +#define SET_LOCALS_LONG(value, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = (value)) +#define SET_LOCALS_DOUBLE_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->d = \ + + +#endif // CPU_PPC_VM_BYTECODEINTERPRETER_PPC_PP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/bytecodeInterpreter_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/bytecodeInterpreter_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_BYTECODEINTERPRETER_PPC_INLINE_HPP +#define CPU_PPC_VM_BYTECODEINTERPRETER_PPC_INLINE_HPP + +#ifdef CC_INTERP + +// Inline interpreter functions for ppc. + +#include + +inline jfloat BytecodeInterpreter::VMfloatAdd(jfloat op1, jfloat op2) { return op1 + op2; } +inline jfloat BytecodeInterpreter::VMfloatSub(jfloat op1, jfloat op2) { return op1 - op2; } +inline jfloat BytecodeInterpreter::VMfloatMul(jfloat op1, jfloat op2) { return op1 * op2; } +inline jfloat BytecodeInterpreter::VMfloatDiv(jfloat op1, jfloat op2) { return op1 / op2; } +inline jfloat BytecodeInterpreter::VMfloatRem(jfloat op1, jfloat op2) { return (jfloat)fmod((double)op1, (double)op2); } + +inline jfloat BytecodeInterpreter::VMfloatNeg(jfloat op) { return -op; } + +inline int32_t BytecodeInterpreter::VMfloatCompare(jfloat op1, jfloat op2, int32_t direction) { + return ( op1 < op2 ? -1 : + op1 > op2 ? 1 : + op1 == op2 ? 0 : + (direction == -1 || direction == 1) ? direction : 0); + +} + +inline void BytecodeInterpreter::VMmemCopy64(uint32_t to[2], const uint32_t from[2]) { + to[0] = from[0]; to[1] = from[1]; +} + +// The long operations depend on compiler support for "long long" on ppc. + +inline jlong BytecodeInterpreter::VMlongAdd(jlong op1, jlong op2) { + return op1 + op2; +} + +inline jlong BytecodeInterpreter::VMlongAnd(jlong op1, jlong op2) { + return op1 & op2; +} + +inline jlong BytecodeInterpreter::VMlongDiv(jlong op1, jlong op2) { + if (op1 == min_jlong && op2 == -1) return op1; + return op1 / op2; +} + +inline jlong BytecodeInterpreter::VMlongMul(jlong op1, jlong op2) { + return op1 * op2; +} + +inline jlong BytecodeInterpreter::VMlongOr(jlong op1, jlong op2) { + return op1 | op2; +} + +inline jlong BytecodeInterpreter::VMlongSub(jlong op1, jlong op2) { + return op1 - op2; +} + +inline jlong BytecodeInterpreter::VMlongXor(jlong op1, jlong op2) { + return op1 ^ op2; +} + +inline jlong BytecodeInterpreter::VMlongRem(jlong op1, jlong op2) { + if (op1 == min_jlong && op2 == -1) return 0; + return op1 % op2; +} + +inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { + return ((uint64_t) op1) >> (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongShr(jlong op1, jint op2) { + return op1 >> (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongShl(jlong op1, jint op2) { + return op1 << (op2 & 0x3F); +} + +inline jlong BytecodeInterpreter::VMlongNeg(jlong op) { + return -op; +} + +inline jlong BytecodeInterpreter::VMlongNot(jlong op) { + return ~op; +} + +inline int32_t BytecodeInterpreter::VMlongLtz(jlong op) { + return (op <= 0); +} + +inline int32_t BytecodeInterpreter::VMlongGez(jlong op) { + return (op >= 0); +} + +inline int32_t BytecodeInterpreter::VMlongEqz(jlong op) { + return (op == 0); +} + +inline int32_t BytecodeInterpreter::VMlongEq(jlong op1, jlong op2) { + return (op1 == op2); +} + +inline int32_t BytecodeInterpreter::VMlongNe(jlong op1, jlong op2) { + return (op1 != op2); +} + +inline int32_t BytecodeInterpreter::VMlongGe(jlong op1, jlong op2) { + return (op1 >= op2); +} + +inline int32_t BytecodeInterpreter::VMlongLe(jlong op1, jlong op2) { + return (op1 <= op2); +} + +inline int32_t BytecodeInterpreter::VMlongLt(jlong op1, jlong op2) { + return (op1 < op2); +} + +inline int32_t BytecodeInterpreter::VMlongGt(jlong op1, jlong op2) { + return (op1 > op2); +} + +inline int32_t BytecodeInterpreter::VMlongCompare(jlong op1, jlong op2) { + return (VMlongLt(op1, op2) ? -1 : VMlongGt(op1, op2) ? 1 : 0); +} + +// Long conversions + +inline jdouble BytecodeInterpreter::VMlong2Double(jlong val) { + return (jdouble) val; +} + +inline jfloat BytecodeInterpreter::VMlong2Float(jlong val) { + return (jfloat) val; +} + +inline jint BytecodeInterpreter::VMlong2Int(jlong val) { + return (jint) val; +} + +// Double Arithmetic + +inline jdouble BytecodeInterpreter::VMdoubleAdd(jdouble op1, jdouble op2) { + return op1 + op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleDiv(jdouble op1, jdouble op2) { + return op1 / op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleMul(jdouble op1, jdouble op2) { + return op1 * op2; +} + +inline jdouble BytecodeInterpreter::VMdoubleNeg(jdouble op) { + return -op; +} + +inline jdouble BytecodeInterpreter::VMdoubleRem(jdouble op1, jdouble op2) { + return fmod(op1, op2); +} + +inline jdouble BytecodeInterpreter::VMdoubleSub(jdouble op1, jdouble op2) { + return op1 - op2; +} + +inline int32_t BytecodeInterpreter::VMdoubleCompare(jdouble op1, jdouble op2, int32_t direction) { + return ( op1 < op2 ? -1 : + op1 > op2 ? 1 : + op1 == op2 ? 0 : + (direction == -1 || direction == 1) ? direction : 0); +} + +// Double Conversions + +inline jfloat BytecodeInterpreter::VMdouble2Float(jdouble val) { + return (jfloat) val; +} + +// Float Conversions + +inline jdouble BytecodeInterpreter::VMfloat2Double(jfloat op) { + return (jdouble) op; +} + +// Integer Arithmetic + +inline jint BytecodeInterpreter::VMintAdd(jint op1, jint op2) { + return op1 + op2; +} + +inline jint BytecodeInterpreter::VMintAnd(jint op1, jint op2) { + return op1 & op2; +} + +inline jint BytecodeInterpreter::VMintDiv(jint op1, jint op2) { + /* it's possible we could catch this special case implicitly */ + if ((juint)op1 == 0x80000000 && op2 == -1) return op1; + else return op1 / op2; +} + +inline jint BytecodeInterpreter::VMintMul(jint op1, jint op2) { + return op1 * op2; +} + +inline jint BytecodeInterpreter::VMintNeg(jint op) { + return -op; +} + +inline jint BytecodeInterpreter::VMintOr(jint op1, jint op2) { + return op1 | op2; +} + +inline jint BytecodeInterpreter::VMintRem(jint op1, jint op2) { + /* it's possible we could catch this special case implicitly */ + if ((juint)op1 == 0x80000000 && op2 == -1) return 0; + else return op1 % op2; +} + +inline jint BytecodeInterpreter::VMintShl(jint op1, jint op2) { + return op1 << (op2 & 0x1f); +} + +inline jint BytecodeInterpreter::VMintShr(jint op1, jint op2) { + return op1 >> (op2 & 0x1f); +} + +inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { + return op1 - op2; +} + +inline juint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { + return ((juint) op1) >> (op2 & 0x1f); +} + +inline jint BytecodeInterpreter::VMintXor(jint op1, jint op2) { + return op1 ^ op2; +} + +inline jdouble BytecodeInterpreter::VMint2Double(jint val) { + return (jdouble) val; +} + +inline jfloat BytecodeInterpreter::VMint2Float(jint val) { + return (jfloat) val; +} + +inline jlong BytecodeInterpreter::VMint2Long(jint val) { + return (jlong) val; +} + +inline jchar BytecodeInterpreter::VMint2Char(jint val) { + return (jchar) val; +} + +inline jshort BytecodeInterpreter::VMint2Short(jint val) { + return (jshort) val; +} + +inline jbyte BytecodeInterpreter::VMint2Byte(jint val) { + return (jbyte) val; +} + +#endif // CC_INTERP + +#endif // CPU_PPC_VM_BYTECODEINTERPRETER_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/bytecodes_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/bytecodes_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "interpreter/bytecodes.hpp" + +void Bytecodes::pd_initialize() { + // No ppc specific initialization. +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/bytecodes_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/bytecodes_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_BYTECODES_PPC_HPP +#define CPU_PPC_VM_BYTECODES_PPC_HPP + +// No ppc64 specific bytecodes + +#endif // CPU_PPC_VM_BYTECODES_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/bytes_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/bytes_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_BYTES_PPC_HPP +#define CPU_PPC_VM_BYTES_PPC_HPP + +#include "memory/allocation.hpp" + +class Bytes: AllStatic { + public: + // Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering + // PowerPC needs to check for alignment. + + // Can I count on address always being a pointer to an unsigned char? Yes. + +#if defined(VM_LITTLE_ENDIAN) + + // Returns true, if the byte ordering used by Java is different from the native byte ordering + // of the underlying machine. For example, true for Intel x86, False, for Solaris on Sparc. + static inline bool is_Java_byte_ordering_different() { return true; } + + // Forward declarations of the compiler-dependent implementation + static inline u2 swap_u2(u2 x); + static inline u4 swap_u4(u4 x); + static inline u8 swap_u8(u8 x); + + static inline u2 get_native_u2(address p) { + return (intptr_t(p) & 1) == 0 + ? *(u2*)p + : ( u2(p[1]) << 8 ) + | ( u2(p[0]) ); + } + + static inline u4 get_native_u4(address p) { + switch (intptr_t(p) & 3) { + case 0: return *(u4*)p; + + case 2: return ( u4( ((u2*)p)[1] ) << 16 ) + | ( u4( ((u2*)p)[0] ) ); + + default: return ( u4(p[3]) << 24 ) + | ( u4(p[2]) << 16 ) + | ( u4(p[1]) << 8 ) + | u4(p[0]); + } + } + + static inline u8 get_native_u8(address p) { + switch (intptr_t(p) & 7) { + case 0: return *(u8*)p; + + case 4: return ( u8( ((u4*)p)[1] ) << 32 ) + | ( u8( ((u4*)p)[0] ) ); + + case 2: return ( u8( ((u2*)p)[3] ) << 48 ) + | ( u8( ((u2*)p)[2] ) << 32 ) + | ( u8( ((u2*)p)[1] ) << 16 ) + | ( u8( ((u2*)p)[0] ) ); + + default: return ( u8(p[7]) << 56 ) + | ( u8(p[6]) << 48 ) + | ( u8(p[5]) << 40 ) + | ( u8(p[4]) << 32 ) + | ( u8(p[3]) << 24 ) + | ( u8(p[2]) << 16 ) + | ( u8(p[1]) << 8 ) + | u8(p[0]); + } + } + + + + static inline void put_native_u2(address p, u2 x) { + if ( (intptr_t(p) & 1) == 0 ) *(u2*)p = x; + else { + p[1] = x >> 8; + p[0] = x; + } + } + + static inline void put_native_u4(address p, u4 x) { + switch ( intptr_t(p) & 3 ) { + case 0: *(u4*)p = x; + break; + + case 2: ((u2*)p)[1] = x >> 16; + ((u2*)p)[0] = x; + break; + + default: ((u1*)p)[3] = x >> 24; + ((u1*)p)[2] = x >> 16; + ((u1*)p)[1] = x >> 8; + ((u1*)p)[0] = x; + break; + } + } + + static inline void put_native_u8(address p, u8 x) { + switch ( intptr_t(p) & 7 ) { + case 0: *(u8*)p = x; + break; + + case 4: ((u4*)p)[1] = x >> 32; + ((u4*)p)[0] = x; + break; + + case 2: ((u2*)p)[3] = x >> 48; + ((u2*)p)[2] = x >> 32; + ((u2*)p)[1] = x >> 16; + ((u2*)p)[0] = x; + break; + + default: ((u1*)p)[7] = x >> 56; + ((u1*)p)[6] = x >> 48; + ((u1*)p)[5] = x >> 40; + ((u1*)p)[4] = x >> 32; + ((u1*)p)[3] = x >> 24; + ((u1*)p)[2] = x >> 16; + ((u1*)p)[1] = x >> 8; + ((u1*)p)[0] = x; + } + } + + // Efficient reading and writing of unaligned unsigned data in Java byte ordering (i.e. big-endian ordering) + // (no byte-order reversal is needed since Power CPUs are big-endian oriented). + static inline u2 get_Java_u2(address p) { return swap_u2(get_native_u2(p)); } + static inline u4 get_Java_u4(address p) { return swap_u4(get_native_u4(p)); } + static inline u8 get_Java_u8(address p) { return swap_u8(get_native_u8(p)); } + + static inline void put_Java_u2(address p, u2 x) { put_native_u2(p, swap_u2(x)); } + static inline void put_Java_u4(address p, u4 x) { put_native_u4(p, swap_u4(x)); } + static inline void put_Java_u8(address p, u8 x) { put_native_u8(p, swap_u8(x)); } + +#else // !defined(VM_LITTLE_ENDIAN) + + // Returns true, if the byte ordering used by Java is different from the nativ byte ordering + // of the underlying machine. For example, true for Intel x86, False, for Solaris on Sparc. + static inline bool is_Java_byte_ordering_different() { return false; } + + // Thus, a swap between native and Java ordering is always a no-op: + static inline u2 swap_u2(u2 x) { return x; } + static inline u4 swap_u4(u4 x) { return x; } + static inline u8 swap_u8(u8 x) { return x; } + + static inline u2 get_native_u2(address p) { + return (intptr_t(p) & 1) == 0 + ? *(u2*)p + : ( u2(p[0]) << 8 ) + | ( u2(p[1]) ); + } + + static inline u4 get_native_u4(address p) { + switch (intptr_t(p) & 3) { + case 0: return *(u4*)p; + + case 2: return ( u4( ((u2*)p)[0] ) << 16 ) + | ( u4( ((u2*)p)[1] ) ); + + default: return ( u4(p[0]) << 24 ) + | ( u4(p[1]) << 16 ) + | ( u4(p[2]) << 8 ) + | u4(p[3]); + } + } + + static inline u8 get_native_u8(address p) { + switch (intptr_t(p) & 7) { + case 0: return *(u8*)p; + + case 4: return ( u8( ((u4*)p)[0] ) << 32 ) + | ( u8( ((u4*)p)[1] ) ); + + case 2: return ( u8( ((u2*)p)[0] ) << 48 ) + | ( u8( ((u2*)p)[1] ) << 32 ) + | ( u8( ((u2*)p)[2] ) << 16 ) + | ( u8( ((u2*)p)[3] ) ); + + default: return ( u8(p[0]) << 56 ) + | ( u8(p[1]) << 48 ) + | ( u8(p[2]) << 40 ) + | ( u8(p[3]) << 32 ) + | ( u8(p[4]) << 24 ) + | ( u8(p[5]) << 16 ) + | ( u8(p[6]) << 8 ) + | u8(p[7]); + } + } + + + + static inline void put_native_u2(address p, u2 x) { + if ( (intptr_t(p) & 1) == 0 ) { *(u2*)p = x; } + else { + p[0] = x >> 8; + p[1] = x; + } + } + + static inline void put_native_u4(address p, u4 x) { + switch ( intptr_t(p) & 3 ) { + case 0: *(u4*)p = x; + break; + + case 2: ((u2*)p)[0] = x >> 16; + ((u2*)p)[1] = x; + break; + + default: ((u1*)p)[0] = x >> 24; + ((u1*)p)[1] = x >> 16; + ((u1*)p)[2] = x >> 8; + ((u1*)p)[3] = x; + break; + } + } + + static inline void put_native_u8(address p, u8 x) { + switch ( intptr_t(p) & 7 ) { + case 0: *(u8*)p = x; + break; + + case 4: ((u4*)p)[0] = x >> 32; + ((u4*)p)[1] = x; + break; + + case 2: ((u2*)p)[0] = x >> 48; + ((u2*)p)[1] = x >> 32; + ((u2*)p)[2] = x >> 16; + ((u2*)p)[3] = x; + break; + + default: ((u1*)p)[0] = x >> 56; + ((u1*)p)[1] = x >> 48; + ((u1*)p)[2] = x >> 40; + ((u1*)p)[3] = x >> 32; + ((u1*)p)[4] = x >> 24; + ((u1*)p)[5] = x >> 16; + ((u1*)p)[6] = x >> 8; + ((u1*)p)[7] = x; + } + } + + // Efficient reading and writing of unaligned unsigned data in Java byte ordering (i.e. big-endian ordering) + // (no byte-order reversal is needed since Power CPUs are big-endian oriented). + static inline u2 get_Java_u2(address p) { return get_native_u2(p); } + static inline u4 get_Java_u4(address p) { return get_native_u4(p); } + static inline u8 get_Java_u8(address p) { return get_native_u8(p); } + + static inline void put_Java_u2(address p, u2 x) { put_native_u2(p, x); } + static inline void put_Java_u4(address p, u4 x) { put_native_u4(p, x); } + static inline void put_Java_u8(address p, u8 x) { put_native_u8(p, x); } + +#endif // VM_LITTLE_ENDIAN +}; + +#if defined(TARGET_OS_ARCH_linux_ppc) +#include "bytes_linux_ppc.inline.hpp" +#endif + +#endif // CPU_PPC_VM_BYTES_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/c2_globals_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/c2_globals_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_C2_GLOBALS_PPC_HPP +#define CPU_PPC_VM_C2_GLOBALS_PPC_HPP + +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" + +// Sets the default values for platform dependent flags used by the server compiler. +// (see c2_globals.hpp). + +define_pd_global(bool, BackgroundCompilation, true); +define_pd_global(bool, CICompileOSR, true); +define_pd_global(bool, InlineIntrinsics, true); +define_pd_global(bool, PreferInterpreterNativeStubs, false); +define_pd_global(bool, ProfileTraps, true); +define_pd_global(bool, UseOnStackReplacement, true); +define_pd_global(bool, ProfileInterpreter, true); +define_pd_global(bool, TieredCompilation, false); +define_pd_global(intx, CompileThreshold, 10000); +define_pd_global(intx, BackEdgeThreshold, 140000); + +define_pd_global(intx, OnStackReplacePercentage, 140); +define_pd_global(intx, ConditionalMoveLimit, 3); +define_pd_global(intx, FLOATPRESSURE, 28); +define_pd_global(intx, FreqInlineSize, 175); +define_pd_global(intx, INTPRESSURE, 25); +define_pd_global(intx, InteriorEntryAlignment, 16); +define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); +define_pd_global(intx, RegisterCostAreaRatio, 16000); +define_pd_global(bool, UseTLAB, true); +define_pd_global(bool, ResizeTLAB, true); +define_pd_global(intx, LoopUnrollLimit, 60); + +// Peephole and CISC spilling both break the graph, and so make the +// scheduler sick. +define_pd_global(bool, OptoPeephole, false); +define_pd_global(bool, UseCISCSpill, false); +define_pd_global(bool, OptoBundling, false); +// GL: +// Detected a problem with unscaled compressed oops and +// narrow_oop_use_complex_address() == false. +// -Djava.io.tmpdir=./tmp -jar SPECjvm2008.jar -ikv -wt 3 -it 3 +// -bt 1 --base compiler.sunflow +// fails in Lower.visitIf->translate->tranlate->translate and +// throws an unexpected NPE. A load and a store seem to be +// reordered. Java reads about: +// loc = x.f +// x.f = 0 +// NullCheck loc +// While assembler reads: +// x.f = 0 +// loc = x.f +// NullCheck loc +define_pd_global(bool, OptoScheduling, false); + +define_pd_global(intx, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, ReservedCodeCacheSize, 256*M); +define_pd_global(intx, CodeCacheExpansionSize, 64*K); + +// Ergonomics related flags +define_pd_global(uint64_t,MaxRAM, 4ULL*G); +define_pd_global(uintx, CodeCacheMinBlockLength, 4); + +// Heap related flags +define_pd_global(uintx,PermSize, ScaleForWordSize(16*M)); +define_pd_global(uintx,MaxPermSize, ScaleForWordSize(64*M)); + +// Ergonomics related flags +define_pd_global(bool, NeverActAsServerClassMachine, false); + +#endif // CPU_PPC_VM_C2_GLOBALS_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/c2_init_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/c2_init_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "opto/compile.hpp" +#include "opto/node.hpp" +#include "runtime/globals.hpp" +#include "utilities/debug.hpp" + +// processor dependent initialization for ppc + +void Compile::pd_compiler2_init() { + + // Power7 and later + if (PowerArchitecturePPC64 > 6) { + if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { + FLAG_SET_ERGO(bool, UsePopCountInstruction, true); + } + } + + if (PowerArchitecturePPC64 == 6) { + if (FLAG_IS_DEFAULT(InsertEndGroupPPC64)) { + FLAG_SET_ERGO(bool, InsertEndGroupPPC64, true); + } + } +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/codeBuffer_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/codeBuffer_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_CODEBUFFER_PPC_HPP +#define CPU_PPC_VM_CODEBUFFER_PPC_HPP + +private: + void pd_initialize() {} + +public: + void flush_bundle(bool start_new_bundle) {} + +#endif // CPU_PPC_VM_CODEBUFFER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/compile_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/compile_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 SAP AG. 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. + * + */ + +#ifdef COMPILER2 + +#include "adfiles/ad_ppc_64.hpp" +#include "compile_ppc.hpp" +#include "opto/compile.hpp" +#include "opto/machnode.hpp" +#include "opto/node.hpp" +#include "oops/oop.inline.hpp" + +struct pdCompileEnv { + Compile *C; + MachNode *loadPoll; +}; + +void PdCompile::pd_post_matching_hook(Compile* C) { + Node *root = (Node*) C->root(); + pdCompileEnv env = { C, NULL }; + root->walk(visit_node_and_connect_toc, Node::nop, (void *)&env); +} + +// The ins before the jvms ins were changed. We added or removed 'change' +// edges. Adapt the jvms offsets. +static void fix_jvms(JVMState *jvms, int change) { + jvms->set_locoff(jvms->locoff() + change); + jvms->set_stkoff(jvms->stkoff() + change); + jvms->set_monoff(jvms->monoff() + change); + jvms->set_scloff(jvms->scloff() + change); + jvms->set_endoff(jvms->endoff() + change); + if (jvms->caller()) fix_jvms(jvms->caller(), change); +} + +// Encoding large constant as immediates requires a lot of instructions +// on PPC. Therefore we load the constants from the constant pool. +// +// To access the constant pool we must know the toc. C2 supplies a special +// mach node MachConstantBaseNode to load the toc, and adlc adds this node +// to constants if specified in the ad file. +// Unfortunately this does not work for storeCM, a store node, and call nodes. +// So we add the MachConstantBaseNode here, just after matching. +void PdCompile::visit_node_and_connect_toc(Node &node, void *env) { + pdCompileEnv *pdEnv = (pdCompileEnv *)env; + + if (node.is_Mach()) { + MachNode *m = node.as_Mach(); + if (m->ins_requires_toc() != 0) { + Node *loadToc = pdEnv->C->mach_constant_base_node(); + + // Some call nodes require the toc. We abuse the input 4 (ReturnAdr) which + // is connected to top for our purpose here. + if (m->rule() == CallLeafDirect_Ex_rule || m->rule() == CallLeafNoFPDirect_Ex_rule || + m->rule() == CallDynamicJavaDirect_rule || m->rule() == CallDynamicJavaDirectSched_Ex_rule) { + assert(m->in(TypeFunc::ReturnAdr)->is_top(), "not top?"); + MachSafePointNode *call = (MachSafePointNode *)m; + // Set register mask. + if (m->rule() == CallLeafDirect_Ex_rule || m->rule() == CallLeafNoFPDirect_Ex_rule) { + call->_in_rms[TypeFunc::ReturnAdr] = BITS64_REG_LEAF_CALL_mask(); + } else { + call->_in_rms[TypeFunc::ReturnAdr] = BITS64_REG_DYNAMIC_CALL_mask(); + } + m->set_req(TypeFunc::ReturnAdr, loadToc); + } + } + } +} + +#endif // COMPILER2 diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/compile_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/compile_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 SAP AG. 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. + * + */ + +// Name space for methods with platform dependent extensions of compile +// (this is friend of compile). + +#ifndef CPU_PPC_VM_COMPILE_PPC_HPP +#define CPU_PPC_VM_COMPILE_PPC_HPP + +class Compile; +class Node; + +class PdCompile { +public: + static void pd_post_matching_hook(Compile* C); +private: + static void visit_node_and_connect_toc(Node &node, void *env); +}; + +#endif // CPU_PPC_VM_COMPILE_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/copy_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/copy_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_COPY_PPC_HPP +#define CPU_PPC_VM_COPY_PPC_HPP + +#ifndef PPC64 +#error "copy currently only implemented for PPC64" +#endif + +// Inline functions for memory copy and fill. + +static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: (void)memcpy(to, from, count * HeapWordSize); + break; + } +} + +static void pd_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { + switch (count) { + case 8: to[7] = from[7]; + case 7: to[6] = from[6]; + case 6: to[5] = from[5]; + case 5: to[4] = from[4]; + case 4: to[3] = from[3]; + case 3: to[2] = from[2]; + case 2: to[1] = from[1]; + case 1: to[0] = from[0]; + case 0: break; + default: while (count-- > 0) { + *to++ = *from++; + } + break; + } +} + +static void pd_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + (void)memmove(to, from, count * HeapWordSize); +} + +static void pd_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + pd_disjoint_words(from, to, count); +} + +static void pd_conjoint_bytes(void* from, void* to, size_t count) { + (void)memmove(to, from, count); +} + +static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { + (void)memmove(to, from, count); +} + +// Template for atomic, element-wise copy. +template +static void copy_conjoint_atomic(T* from, T* to, size_t count) { + if (from > to) { + while (count-- > 0) { + // Copy forwards + *to++ = *from++; + } + } else { + from += count - 1; + to += count - 1; + while (count-- > 0) { + // Copy backwards + *to-- = *from--; + } + } +} + +static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + // TODO: contribute optimized version. + copy_conjoint_atomic(from, to, count); +} + +static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { + // TODO: contribute optimized version. + copy_conjoint_atomic(from, to, count); +} + +static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { + copy_conjoint_atomic(from, to, count); +} + +static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { + copy_conjoint_atomic(from, to, count); +} + +static void pd_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_bytes_atomic(from, to, count); +} + +static void pd_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + // TODO: contribute optimized version. + pd_conjoint_jshorts_atomic((jshort*)from, (jshort*)to, count); +} + +static void pd_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { + // TODO: contribute optimized version. + pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); +} + +static void pd_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +} + +static void pd_arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { + pd_conjoint_oops_atomic((oop*)from, (oop*)to, count); +} + +static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) { + julong* to = (julong*)tohw; + julong v = ((julong)value << 32) | value; + while (count-- > 0) { + *to++ = v; + } +} + +static void pd_fill_to_aligned_words(HeapWord* tohw, size_t count, juint value) { + pd_fill_to_words(tohw, count, value); +} + +static void pd_fill_to_bytes(void* to, size_t count, jubyte value) { + (void)memset(to, value, count); +} + +static void pd_zero_to_words(HeapWord* tohw, size_t count) { + pd_fill_to_words(tohw, count, 0); +} + +static void pd_zero_to_bytes(void* to, size_t count) { + (void)memset(to, 0, count); +} + +#endif // CPU_PPC_VM_COPY_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/cppInterpreterGenerator_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/cppInterpreterGenerator_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_CPPINTERPRETERGENERATOR_PPC_HPP +#define CPU_PPC_VM_CPPINTERPRETERGENERATOR_PPC_HPP + + address generate_normal_entry(void); + address generate_native_entry(void); + + void lock_method(void); + void unlock_method(void); + + void generate_counter_incr(Label& overflow); + void generate_counter_overflow(Label& do_continue); + + void generate_more_monitors(); + void generate_deopt_handling(Register result_index); + + void generate_compute_interpreter_state(Label& exception_return); + +#endif // CPU_PPC_VM_CPPINTERPRETERGENERATOR_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/cppInterpreter_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/cppInterpreter_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,3038 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "interpreter/bytecodeHistogram.hpp" +#include "interpreter/cppInterpreter.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterGenerator.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "oops/arrayOop.hpp" +#include "oops/methodDataOop.hpp" +#include "oops/methodOop.hpp" +#include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "runtime/arguments.hpp" +#include "runtime/deoptimization.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/timer.hpp" +#include "runtime/vframeArray.hpp" +#include "utilities/debug.hpp" +#ifdef SHARK +#include "shark/shark_globals.hpp" +#endif + +#ifdef CC_INTERP + +#define __ _masm-> + +// Contains is used for identifying interpreter frames during a stack-walk. +// A frame with a PC in InterpretMethod must be identified as a normal C frame. +bool CppInterpreter::contains(address pc) { + return _code->contains(pc); +} + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +static address interpreter_frame_manager = NULL; +static address frame_manager_specialized_return = NULL; +static address native_entry = NULL; + +static address interpreter_return_address = NULL; + +static address unctrap_frame_manager_entry = NULL; + +static address deopt_frame_manager_return_atos = NULL; +static address deopt_frame_manager_return_btos = NULL; +static address deopt_frame_manager_return_itos = NULL; +static address deopt_frame_manager_return_ltos = NULL; +static address deopt_frame_manager_return_ftos = NULL; +static address deopt_frame_manager_return_dtos = NULL; +static address deopt_frame_manager_return_vtos = NULL; + +// A result handler converts/unboxes a native call result into +// a java interpreter/compiler result. The current frame is an +// interpreter frame. +address CppInterpreterGenerator::generate_result_handler_for(BasicType type) { + return AbstractInterpreterGenerator::generate_result_handler_for(type); +} + +// tosca based result to c++ interpreter stack based result. +address CppInterpreterGenerator::generate_tosca_to_stack_converter(BasicType type) { + // + // A result is in the native abi result register from a native + // method call. We need to return this result to the interpreter by + // pushing the result on the interpreter's stack. + // + // Registers alive: + // R3_ARG1(R3_RET)/F1_ARG1(F1_RET) - result to move + // R4_ARG2 - address of tos + // LR + // + // Registers updated: + // R3_RET(R3_ARG1) - address of new tos (== R17_tos for T_VOID) + // + + int number_of_used_slots = 1; + + const Register tos = R4_ARG2; + Label done; + Label is_false; + + address entry = __ pc(); + + switch (type) { + case T_BOOLEAN: + __ cmpwi(CCR0, R3_RET, 0); + __ beq(CCR0, is_false); + __ li(R3_RET, 1); + __ stw(R3_RET, 0, tos); + __ b(done); + __ bind(is_false); + __ li(R3_RET, 0); + __ stw(R3_RET, 0, tos); + break; + case T_BYTE: + case T_CHAR: + case T_SHORT: + case T_INT: + __ stw(R3_RET, 0, tos); + break; + case T_LONG: + number_of_used_slots = 2; + // mark unused slot for debugging + // long goes to topmost slot + __ std(R3_RET, -BytesPerWord, tos); + __ li(R3_RET, 0); + __ std(R3_RET, 0, tos); + break; + case T_OBJECT: + __ verify_oop(R3_RET); + __ std(R3_RET, 0, tos); + break; + case T_FLOAT: + __ stfs(F1_RET, 0, tos); + break; + case T_DOUBLE: + number_of_used_slots = 2; + // mark unused slot for debugging + __ li(R3_RET, 0); + __ std(R3_RET, 0, tos); + // double goes to topmost slot + __ stfd(F1_RET, -BytesPerWord, tos); + break; + case T_VOID: + number_of_used_slots = 0; + break; + default: + ShouldNotReachHere(); + } + + __ BIND(done); + + // new expression stack top + __ addi(R3_RET, tos, -BytesPerWord * number_of_used_slots); + + __ blr(); + + return entry; +} + +address CppInterpreterGenerator::generate_stack_to_stack_converter(BasicType type) { + // + // Copy the result from the callee's stack to the caller's stack, + // caller and callee both being interpreted. + // + // Registers alive + // R3_ARG1 - address of callee's tos + BytesPerWord + // R4_ARG2 - address of caller's tos [i.e. free location] + // LR + // + // stack grows upwards, memory grows downwards. + // + // [ free ] <-- callee's tos + // [ optional result ] <-- R3_ARG1 + // [ optional dummy ] + // ... + // [ free ] <-- caller's tos, R4_ARG2 + // ... + // Registers updated + // R3_RET(R3_ARG1) - address of caller's new tos + // + // stack grows upwards, memory grows downwards. + // + // [ free ] <-- current tos, R3_RET + // [ optional result ] + // [ optional dummy ] + // ... + // + + const Register from = R3_ARG1; + const Register ret = R3_ARG1; + const Register tos = R4_ARG2; + const Register tmp1 = R21_tmp1; + const Register tmp2 = R22_tmp2; + + address entry = __ pc(); + + switch (type) { + case T_BOOLEAN: + case T_BYTE: + case T_CHAR: + case T_SHORT: + case T_INT: + case T_FLOAT: + __ lwz(tmp1, 0, from); + __ stw(tmp1, 0, tos); + // New expression stack top. + __ addi(ret, tos, - BytesPerWord); + break; + case T_LONG: + case T_DOUBLE: + // Move both entries for debug purposes even though only one is live. + __ ld(tmp1, BytesPerWord, from); + __ ld(tmp2, 0, from); + __ std(tmp1, 0, tos); + __ std(tmp2, -BytesPerWord, tos); + // New expression stack top. + __ addi(ret, tos, - 2 * BytesPerWord); // two slots + break; + case T_OBJECT: + __ ld(tmp1, 0, from); + __ verify_oop(tmp1); + __ std(tmp1, 0, tos); + // New expression stack top. + __ addi(ret, tos, - BytesPerWord); + break; + case T_VOID: + // New expression stack top. + __ mr(ret, tos); + break; + default: + ShouldNotReachHere(); + } + + __ blr(); + + return entry; +} + +address CppInterpreterGenerator::generate_stack_to_native_abi_converter(BasicType type) { + // + // Load a result from the callee's stack into the caller's expecting + // return register, callee being interpreted, caller being call stub + // or jit code. + // + // Registers alive + // R3_ARG1 - callee expression tos + BytesPerWord + // LR + // + // stack grows upwards, memory grows downwards. + // + // [ free ] <-- callee's tos + // [ optional result ] <-- R3_ARG1 + // [ optional dummy ] + // ... + // + // Registers updated + // R3_RET(R3_ARG1)/F1_RET - result + // + + const Register from = R3_ARG1; + const Register ret = R3_ARG1; + const FloatRegister fret = F1_ARG1; + + address entry = __ pc(); + + // Implemented uniformly for both kinds of endianness. The interpreter + // implements boolean, byte, char, and short as jint (4 bytes). + switch (type) { + case T_BOOLEAN: + case T_CHAR: + // zero extension + __ lwz(ret, 0, from); + break; + case T_BYTE: + case T_SHORT: + case T_INT: + // sign extension + __ lwa(ret, 0, from); + break; + case T_LONG: + __ ld(ret, 0, from); + break; + case T_OBJECT: + __ ld(ret, 0, from); + __ verify_oop(ret); + break; + case T_FLOAT: + __ lfs(fret, 0, from); + break; + case T_DOUBLE: + __ lfd(fret, 0, from); + break; + case T_VOID: + break; + default: + ShouldNotReachHere(); + } + + __ blr(); + + return entry; +} + +address CppInterpreter::return_entry(TosState state, int length) { + assert(interpreter_return_address != NULL, "Not initialized"); + return interpreter_return_address; +} + +address CppInterpreter::deopt_entry(TosState state, int length) { + address ret = NULL; + if (length != 0) { + switch (state) { + case atos: ret = deopt_frame_manager_return_atos; break; + case btos: ret = deopt_frame_manager_return_itos; break; + case ctos: + case stos: + case itos: ret = deopt_frame_manager_return_itos; break; + case ltos: ret = deopt_frame_manager_return_ltos; break; + case ftos: ret = deopt_frame_manager_return_ftos; break; + case dtos: ret = deopt_frame_manager_return_dtos; break; + case vtos: ret = deopt_frame_manager_return_vtos; break; + default: ShouldNotReachHere(); + } + } else { + ret = unctrap_frame_manager_entry; // re-execute the bytecode (e.g. uncommon trap, popframe) + } + assert(ret != NULL, "Not initialized"); + return ret; +} + +// +// Helpers for commoning out cases in the various type of method entries. +// + +// +// Registers alive +// R16_thread - JavaThread* +// R1_SP - old stack pointer +// R19_method - callee's methodOop +// R17_tos - address of caller's tos (prepushed) +// R15_prev_state - address of caller's BytecodeInterpreter or 0 +// return_pc in R21_tmp15 (only when called within generate_native_entry) +// +// Registers updated +// R14_state - address of callee's interpreter state +// R1_SP - new stack pointer +// CCR4_is_synced - current method is synchronized +// +void CppInterpreterGenerator::generate_compute_interpreter_state(Label& stack_overflow_return) { + // + // Stack layout at this point: + // + // F1 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // alignment (optional) + // [F1's outgoing Java arguments] <-- R17_tos + // ... + // F2 [PARENT_IJAVA_FRAME_ABI] + // ... + + //============================================================================= + // Allocate space for locals other than the parameters, the + // interpreter state, monitors, and the expression stack. + + const Register local_count = R21_tmp1; + const Register parameter_count = R22_tmp2; + const Register max_stack = R23_tmp3; + // Must not be overwritten within this method! + // const Register return_pc = R29_tmp9; + + const ConditionRegister is_synced = CCR4_is_synced; + const ConditionRegister is_native = CCR6; + const ConditionRegister is_static = CCR7; + + assert(is_synced != is_native, "condition code registers must be distinct"); + assert(is_synced != is_static, "condition code registers must be distinct"); + assert(is_native != is_static, "condition code registers must be distinct"); + + { + + // Local registers + const Register top_frame_size = R24_tmp4; + const Register access_flags = R25_tmp5; + const Register state_offset = R26_tmp6; + Register mem_stack_limit = R27_tmp7; + const Register page_size = R28_tmp8; + + BLOCK_COMMENT("compute_interpreter_state {"); + + // access_flags = method->access_flags(); + // TODO: PPC port: assert(4 == methodOopDesc::sz_access_flags(), "unexpected field size"); + __ lwa(access_flags, method_(access_flags)); + + // parameter_count = method->size_of_parameters(); + // TODO: PPC port: assert(2 == methodOopDesc::sz_size_of_parameters(), "unexpected field size"); + __ lhz(parameter_count, method_(size_of_parameters)); + + // local_count = method->max_locals(); + // TODO: PPC port: assert(2 == methodOopDesc::sz_max_locals(), "unexpected field size"); + __ lhz(local_count, method_(size_of_locals)); + + // max_stack = method->max_stack(); + // TODO: PPC port: assert(2 == methodOopDesc::sz_max_stack(), "unexpected field size"); + __ lhz(max_stack, method_(max_stack)); + + if (EnableInvokeDynamic) { + // Take into account 'extra_stack_entries' needed by method handles (see method.hpp). + __ addi(max_stack, max_stack, methodOopDesc::extra_stack_entries()); + } + + // mem_stack_limit = thread->stack_limit(); + __ ld(mem_stack_limit, thread_(stack_overflow_limit)); + + // Point locals at the first argument. Method's locals are the + // parameters on top of caller's expression stack. + + // tos points past last Java argument + __ sldi(R18_locals, parameter_count, Interpreter::logStackElementSize); + __ add(R18_locals, R17_tos, R18_locals); + + // R18_locals - i*BytesPerWord points to i-th Java local (i starts at 0) + + // Set is_native, is_synced, is_static - will be used later. + __ testbitdi(is_native, R0, access_flags, JVM_ACC_NATIVE_BIT); + __ testbitdi(is_synced, R0, access_flags, JVM_ACC_SYNCHRONIZED_BIT); + assert(is_synced->is_nonvolatile(), "is_synced must be non-volatile"); + __ testbitdi(is_static, R0, access_flags, JVM_ACC_STATIC_BIT); + + // PARENT_IJAVA_FRAME_ABI + // + // frame_size = + // round_to((local_count - parameter_count)*BytesPerWord + + // 2*BytesPerWord + + // alignment + + // frame::interpreter_frame_cinterpreterstate_size_in_bytes() + // sizeof(PARENT_IJAVA_FRAME_ABI) + // method->is_synchronized() ? sizeof(BasicObjectLock) : 0 + + // max_stack*BytesPerWord, + // 16) + // + // Note that this calculation is exactly mirrored by + // AbstractInterpreter::layout_activation_impl() [ and + // AbstractInterpreter::size_activation() ]. Which is used by + // deoptimization so that it can allocate the proper sized + // frame. This only happens for interpreted frames so the extra + // notes below about max_stack below are not important. The other + // thing to note is that for interpreter frames other than the + // current activation the size of the stack is the size of the live + // portion of the stack at the particular bcp and NOT the maximum + // stack that the method might use. + // + // If we're calling a native method, we replace max_stack (which is + // zero) with space for the worst-case signature handler varargs + // vector, which is: + // + // max_stack = max(Argument::n_register_parameters, parameter_count+2); + // + // We add two slots to the parameter_count, one for the jni + // environment and one for a possible native mirror. We allocate + // space for at least the number of ABI registers, even though + // InterpreterRuntime::slow_signature_handler won't write more than + // parameter_count+2 words when it creates the varargs vector at the + // top of the stack. The generated slow signature handler will just + // load trash into registers beyond the necessary number. We're + // still going to cut the stack back by the ABI register parameter + // count so as to get SP+16 pointing at the ABI outgoing parameter + // area, so we need to allocate at least that much even though we're + // going to throw it away. + // + + // Adjust max_stack for native methods: + Label skip_native_calculate_max_stack; + __ bfalse(is_native, skip_native_calculate_max_stack); + // if (is_native) { + // max_stack = max(Argument::n_register_parameters, parameter_count+2); + __ addi(max_stack, parameter_count, 2*Interpreter::stackElementWords); + __ cmpwi(CCR0, max_stack, Argument::n_register_parameters); + __ bge(CCR0, skip_native_calculate_max_stack); + __ li(max_stack, Argument::n_register_parameters); + // } + __ bind(skip_native_calculate_max_stack); + // max_stack is now in bytes + __ slwi(max_stack, max_stack, Interpreter::logStackElementSize); + + // Calculate number of non-parameter locals (in slots): + Label not_java; + __ btrue(is_native, not_java); + // if (!is_native) { + // local_count = non-parameter local count + __ sub(local_count, local_count, parameter_count); + // } else { + // // nothing to do: method->max_locals() == 0 for native methods + // } + __ bind(not_java); + + + // Calculate top_frame_size and parent_frame_resize. + { + const Register parent_frame_resize = R12_scratch2; + + BLOCK_COMMENT("Compute top_frame_size."); + // top_frame_size = TOP_IJAVA_FRAME_ABI + // + size of interpreter state + __ li(top_frame_size, frame::top_ijava_frame_abi_size + + frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + // + max_stack + __ add(top_frame_size, top_frame_size, max_stack); + // + stack slots for a BasicObjectLock for synchronized methods + { + Label not_synced; + __ bfalse(is_synced, not_synced); + __ addi(top_frame_size, top_frame_size, frame::interpreter_frame_monitor_size_in_bytes()); + __ bind(not_synced); + } + // align + __ round_to(top_frame_size, frame::alignment_in_bytes); + + + BLOCK_COMMENT("Compute parent_frame_resize."); + // parent_frame_resize = R1_SP - R17_tos + __ sub(parent_frame_resize, R1_SP, R17_tos); + //__ li(parent_frame_resize, 0); + // + PARENT_IJAVA_FRAME_ABI + // + extra two slots for the no-parameter/no-locals + // method result + __ addi(parent_frame_resize, parent_frame_resize, + frame::parent_ijava_frame_abi_size + + 2*Interpreter::stackElementSize); + // + (locals_count - params_count) + __ sldi(R0, local_count, Interpreter::logStackElementSize); + __ add(parent_frame_resize, parent_frame_resize, R0); + // align + __ round_to(parent_frame_resize, frame::alignment_in_bytes); + + // + // Stack layout at this point: + // + // The new frame F0 hasn't yet been pushed, F1 is still the top frame. + // + // F0 [TOP_IJAVA_FRAME_ABI] + // alignment (optional) + // [F0's full operand stack] + // [F0's monitors] (optional) + // [F0's BytecodeInterpreter object] + // F1 [PARENT_IJAVA_FRAME_ABI] + // alignment (optional) + // [F0's Java result] + // [F0's non-arg Java locals] + // [F1's outgoing Java arguments] <-- R17_tos + // ... + // F2 [PARENT_IJAVA_FRAME_ABI] + // ... + + + // Calculate new R14_state + // and + // test that the new memory stack pointer is above the limit, + // throw a StackOverflowError otherwise. + __ sub(R11_scratch1/*F1's SP*/, R1_SP, parent_frame_resize); + __ addi(R14_state, R11_scratch1/*F1's SP*/, + -frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + __ sub(R11_scratch1/*F0's SP*/, + R11_scratch1/*F1's SP*/, top_frame_size); + + BLOCK_COMMENT("Test for stack overflow:"); + __ cmpld(CCR0/*is_stack_overflow*/, R11_scratch1, mem_stack_limit); + __ blt(CCR0/*is_stack_overflow*/, stack_overflow_return); + + + //============================================================================= + // Frame_size doesn't overflow the stack. Allocate new frame and + // initialize interpreter state. + + // Register state + // + // R15 - local_count + // R16 - parameter_count + // R17 - max_stack + // + // R18 - frame_size + // R19 - access_flags + // CCR4_is_synced - is_synced + // + // GR_Lstate - pointer to the uninitialized new BytecodeInterpreter. + + // _last_Java_pc just needs to be close enough that we can identify + // the frame as an interpreted frame. It does not need to be the + // exact return address from either calling + // BytecodeInterpreter::InterpretMethod or the call to a jni native method. + // So we can initialize it here with a value of a bundle in this + // code fragment. We only do this initialization for java frames + // where InterpretMethod needs a a way to get a good pc value to + // store in the thread state. For interpreter frames used to call + // jni native code we just zero the value in the state and move an + // ip as needed in the native entry code. + // + // const Register last_Java_pc_addr = GR24_SCRATCH; // QQQ 27 + // const Register last_Java_pc = GR26_SCRATCH; + + // Must reference stack before setting new SP since Windows + // will not be able to deliver the exception on a bad SP. + // Windows also insists that we bang each page one at a time in order + // for the OS to map in the reserved pages. If we bang only + // the final page, Windows stops delivering exceptions to our + // VectoredExceptionHandler and terminates our program. + // Linux only requires a single bang but it's rare to have + // to bang more than 1 page so the code is enabled for both OS's. + + // BANG THE STACK + // + // Nothing to do for PPC, because updating the SP will automatically + // bang the page. + + // Up to here we have calculated the delta for the new C-frame and + // checked for a stack-overflow. Now we can savely update SP and + // resize the C-frame. + + // R14_state has already been calculated. + __ push_interpreter_frame(top_frame_size, parent_frame_resize, + R25_tmp5, R26_tmp6, R27_tmp7, R28_tmp8); + + } + + // + // Stack layout at this point: + // + // F0 has been been pushed! + // + // F0 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // alignment (optional) (now it's here, if required) + // [F0's full operand stack] + // [F0's monitors] (optional) + // [F0's BytecodeInterpreter object] + // F1 [PARENT_IJAVA_FRAME_ABI] + // alignment (optional) (now it's here, if required) + // [F0's Java result] + // [F0's non-arg Java locals] + // [F1's outgoing Java arguments] + // ... + // F2 [PARENT_IJAVA_FRAME_ABI] + // ... + // + // R14_state points to F0's BytecodeInterpreter object. + // + + } + + //============================================================================= + // new BytecodeInterpreter-object is save, let's initialize it: + BLOCK_COMMENT("New BytecodeInterpreter-object is save."); + + { + // Locals + const Register bytecode_addr = R24_tmp4; + const Register constants = R25_tmp5; + const Register tos = R26_tmp6; + const Register stack_base = R27_tmp7; + const Register local_addr = R28_tmp8; + { + Label L; + __ btrue(is_native, L); + // if (!is_native) { + // bytecode_addr = constMethod->codes(); + __ ld(bytecode_addr, method_(const)); + __ addi(bytecode_addr, bytecode_addr, in_bytes(constMethodOopDesc::codes_offset())); + // } + __ bind(L); + } + + __ ld(constants, in_bytes(methodOopDesc::const_offset()), R19_method); + __ ld(constants, in_bytes(constMethodOopDesc::constants_offset()), constants); + + // state->_prev_link = prev_state; + __ std(R15_prev_state, state_(_prev_link)); + + // For assertions only. + // TODO: not needed anyway because it coincides with `_monitor_base'. remove! + // state->_self_link = state; + DEBUG_ONLY(__ std(R14_state, state_(_self_link));) + + // state->_thread = thread; + __ std(R16_thread, state_(_thread)); + + // state->_method = method; + __ std(R19_method, state_(_method)); + + // state->_locals = locals; + __ std(R18_locals, state_(_locals)); + + // state->_oop_temp = NULL; + __ li(R0, 0); + __ std(R0, state_(_oop_temp)); + + // state->_last_Java_fp = *R1_SP // Use *R1_SP as fp + __ ld(R0, _abi(callers_sp), R1_SP); + __ std(R0, state_(_last_Java_fp)); + + BLOCK_COMMENT("load Stack base:"); + { + // Stack_base. + // if (!method->synchronized()) { + // stack_base = state; + // } else { + // stack_base = (uintptr_t)state - sizeof(BasicObjectLock); + // } + Label L; + __ mr(stack_base, R14_state); + __ bfalse(is_synced, L); + __ addi(stack_base, stack_base, -frame::interpreter_frame_monitor_size_in_bytes()); + __ bind(L); + } + + // state->_mdx = NULL; + __ li(R0, 0); + __ std(R0, state_(_mdx)); + + { + // if (method->is_native()) state->_bcp = NULL; + // else state->_bcp = bytecode_addr; + Label label1, label2; + __ bfalse(is_native, label1); + __ std(R0, state_(_bcp)); + __ b(label2); + __ bind(label1); + __ std(bytecode_addr, state_(_bcp)); + __ bind(label2); + } + + + // state->_result._to_call._callee = NULL; + __ std(R0, state_(_result._to_call._callee)); + + // state->_monitor_base = state; + __ std(R14_state, state_(_monitor_base)); + + // state->_msg = BytecodeInterpreter::method_entry; + __ li(R0, BytecodeInterpreter::method_entry); + __ stw(R0, state_(_msg)); + + // state->_last_Java_sp = R1_SP; + __ std(R1_SP, state_(_last_Java_sp)); + + // state->_stack_base = stack_base; + __ std(stack_base, state_(_stack_base)); + + // tos = stack_base - 1 slot (prepushed); + // state->_stack.Tos(tos); + __ addi(tos, stack_base, - Interpreter::stackElementSize); + __ std(tos, state_(_stack)); + + + { + BLOCK_COMMENT("get last_Java_pc:"); + // if (!is_native) state->_last_Java_pc = ; + // else state->_last_Java_pc = NULL; (just for neatness) + Label label1, label2; + __ btrue(is_native, label1); + __ get_PC_trash_LR(R0); + __ std(R0, state_(_last_Java_pc)); + __ b(label2); + __ bind(label1); + __ li(R0, 0); + __ std(R0, state_(_last_Java_pc)); + __ bind(label2); + } + + + // stack_limit = tos - max_stack; + __ sub(R0, tos, max_stack); + // state->_stack_limit = stack_limit; + __ std(R0, state_(_stack_limit)); + + + // cache = method->constants()->cache(); + __ ld(R0, constantPoolOopDesc::cache_offset_in_bytes(), constants); + // state->_constants = method->constants()->cache(); + __ std(R0, state_(_constants)); + + + + //============================================================================= + // synchronized method, allocate and initialize method object lock. + // if (!method->is_synchronized()) goto fill_locals_with_0x0s; + Label fill_locals_with_0x0s; + __ bfalse(is_synced, fill_locals_with_0x0s); + + // pool_holder = method->constants()->pool_holder(); + const int mirror_offset = in_bytes(Klass::java_mirror_offset()); + { + Label label1, label2; + // lockee = NULL; for java methods, correct value will be inserted in BytecodeInterpretMethod.hpp + __ li(R0,0); + __ bfalse(is_native, label2); + + __ bfalse(is_static, label1); + // if (method->is_static()) lockee = + // pool_holder->klass_part()->java_mirror(); + __ ld(R11_scratch1/*pool_holder*/, constantPoolOopDesc::pool_holder_offset_in_bytes(), constants); + __ ld(R0/*lockee*/, mirror_offset, R11_scratch1/*pool_holder*/); + __ b(label2); + + __ bind(label1); + // else lockee = *(oop*)locals; + __ ld(R0/*lockee*/, 0, R18_locals); + __ bind(label2); + + // monitor->set_obj(lockee); + __ std(R0/*lockee*/, BasicObjectLock::obj_offset_in_bytes(), stack_base); + } + + // See if we need to zero the locals + __ BIND(fill_locals_with_0x0s); + + + //============================================================================= + // fill locals with 0x0s + Label locals_zeroed; + __ btrue(is_native, locals_zeroed); + + if (true /* zerolocals */ || ClearInterpreterLocals) { + // local_count is already num_locals_slots - num_param_slots + __ sldi(R0, parameter_count, Interpreter::logStackElementSize); + __ sub(local_addr, R18_locals, R0); + __ cmpdi(CCR0, local_count, 0); + __ ble(CCR0, locals_zeroed); + + __ mtctr(local_count); + //__ ld_const_addr(R0, (address) 0xcafe0000babe); + __ li(R0, 0); + + Label zero_slot; + __ bind(zero_slot); + + // first local is at local_addr + __ std(R0, 0, local_addr); + __ addi(local_addr, local_addr, -BytesPerWord); + __ bdnz(zero_slot); + } + + __ BIND(locals_zeroed); + + } + BLOCK_COMMENT("} compute_interpreter_state"); +} + +// Generate code to initiate compilation on invocation counter overflow. +void CppInterpreterGenerator::generate_counter_overflow(Label& continue_entry) { + // Registers alive + // R14_state + // R16_thread + // + // Registers updated + // R14_state + // R3_ARG1 (=R3_RET) + // R4_ARG2 + + // After entering the vm we remove the activation and retry the + // entry point in case the compilation is complete. + + // InterpreterRuntime::frequency_counter_overflow takes one argument + // that indicates if the counter overflow occurs at a backwards + // branch (NULL bcp). We pass zero. The call returns the address + // of the verified entry point for the method or NULL if the + // compilation did not complete (either went background or bailed + // out). + __ li(R4_ARG2, 0); + + // Pass false to call_VM so it doesn't check for pending exceptions, + // since at this point in the method invocation the exception + // handler would try to exit the monitor of synchronized methods + // which haven't been entered yet. + // + // Returns verified_entry_point or NULL, we don't care which. + // + // Do not use the variant `frequency_counter_overflow' that returns + // a structure, because this will change the argument list by a + // hidden parameter (gcc 4.1). + + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), + R4_ARG2, + false); + // Returns verified_entry_point or NULL, we don't care which as we ignore it + // and run interpreted. + + // Reload method, it may have moved. + __ ld(R19_method, state_(_method)); + + // We jump now to the label "continue_after_compile". + __ b(continue_entry); +} + +// Increment invocation count and check for overflow. +void CppInterpreterGenerator::generate_counter_incr(Label& overflow) { + const Register invocation_counter = R11_scratch1; + const Register temp = R12_scratch2; + const Register invocation_limit = R12_scratch2; + const Register invocation_limit_addr = invocation_limit; + const Register access_flags = R11_scratch1; + + // Update standard invocation counters + __ increment_invocation_counter(invocation_counter, temp); + + // Compare against limit + assert(4 == sizeof(InvocationCounter::InterpreterInvocationLimit), + "must be 4 bytes"); + __ load_const(invocation_limit_addr, (address)&InvocationCounter::InterpreterInvocationLimit); + __ lwa(invocation_limit, 0, invocation_limit_addr); + __ cmpw(CCR0, invocation_counter, invocation_limit); + __ bge(CCR0, overflow); +} + +// +// Call a JNI method. +// +// Interpreter stub for calling a native method. (C++ interpreter) +// This sets up a somewhat different looking stack for calling the native method +// than the typical interpreter frame setup. +// +address CppInterpreterGenerator::generate_native_entry(void) { + if (native_entry != NULL) return native_entry; + address entry = __ pc(); + + // Read + // R16_thread + // R15_prev_state - address of caller's BytecodeInterpreter, if this snippet + // gets called by the frame manager. + // R19_method - callee's methodOop + // R17_tos - address of caller's tos + // R1_SP - caller's stack pointer + // R21_sender_SP - initial caller sp + // + // Update + // R14_state - address of caller's BytecodeInterpreter + // R3_RET - integer result, if any. + // F1_RET - float result, if any. + // + // + // Stack layout at this point: + // + // 0 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // alignment (optional) + // [outgoing Java arguments] <-- R17_tos + // ... + // PARENT [PARENT_IJAVA_FRAME_ABI] + // ... + // + + const bool inc_counter = UseCompiler || CountCompiledCalls; + + const Register signature_handler_fd = R21_tmp1; + const Register pending_exception = R22_tmp2; + const Register result_handler_addr = R23_tmp3; + const Register native_method_fd = R24_tmp4; + const Register access_flags = R25_tmp5; + const Register active_handles = R26_tmp6; + const Register sync_state = R27_tmp7; + const Register sync_state_addr = sync_state; // Address is dead after use. + const Register suspend_flags = R24_tmp4; + + const Register return_pc = R28_tmp8; // Register will be locked for some time. + + const ConditionRegister is_synced = CCR4_is_synced; // Live-on-exit from compute_interpreter_state. + + // R1_SP still points to caller's SP at this point. + + // Save initial_caller_sp to caller's abi. The caller frame must be + // resized before returning to get rid of the c2i arguments (if + // any). + // Override the saved SP with the senderSP so we can pop c2i + // arguments (if any) off when we return + __ std(R21_sender_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP); + + // Save LR to caller's frame. We don't use _abi(lr) here, because it is not safe. + __ mflr(return_pc); + __ std(return_pc, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + + assert(return_pc->is_nonvolatile(), "return_pc must be a non-volatile register"); + + __ verify_method_ptr(R19_method); + + //============================================================================= + + // If this snippet gets called by the frame manager (at label + // `call_special'), then R15_prev_state is valid. If this snippet + // is not called by the frame manager, but e.g. by the call stub or + // by compiled code, then R15_prev_state is invalid. + { + // Set R15_prev_state to 0 if we don't return to the frame + // manager; we will return to the call_stub or to compiled code + // instead. If R15_prev_state is 0 there will be only one + // interpreter frame (we will set this up later) in this C frame! + // So we must take care about retrieving prev_state_(_prev_link) + // and restoring R1_SP when popping that interpreter. + Label prev_state_is_valid; + + __ load_const(R11_scratch1/*frame_manager_returnpc_addr*/, (address)&frame_manager_specialized_return); + __ ld(R12_scratch2/*frame_manager_returnpc*/, 0, R11_scratch1/*frame_manager_returnpc_addr*/); + __ cmpd(CCR0, return_pc, R12_scratch2/*frame_manager_returnpc*/); + __ beq(CCR0, prev_state_is_valid); + + __ li(R15_prev_state, 0); + + __ BIND(prev_state_is_valid); + } + + //============================================================================= + // Allocate new frame and initialize interpreter state. + + Label exception_return; + Label exception_return_sync_check; + Label stack_overflow_return; + + // Generate new interpreter state and jump to stack_overflow_return in case of + // a stack overflow. + generate_compute_interpreter_state(stack_overflow_return); + + //============================================================================= + // Increment invocation counter. On overflow, entry to JNI method + // will be compiled. + Label invocation_counter_overflow; + if (inc_counter) { + generate_counter_incr(invocation_counter_overflow); + } + + Label continue_after_compile; + __ BIND(continue_after_compile); + + // access_flags = method->access_flags(); + // Load access flags. + assert(access_flags->is_nonvolatile(), + "access_flags must be in a non-volatile register"); + // Type check. + // TODO: PPC port: assert(4 == methodOopDesc::sz_access_flags(), "unexpected field size"); + __ lwz(access_flags, method_(access_flags)); + + // We don't want to reload R19_method and access_flags after calls + // to some helper functions. + assert(R19_method->is_nonvolatile(), "R19_method must be a non-volatile register"); + + // Check for synchronized methods. Must happen AFTER invocation counter + // check, so method is not locked if counter overflows. + + { + Label method_is_not_synced; + // Is_synced is still alive. + assert(is_synced->is_nonvolatile(), "is_synced must be non-volatile"); + __ bfalse(is_synced, method_is_not_synced); + + lock_method(); + // Reload method, it may have moved. + __ ld(R19_method, state_(_method)); + + __ BIND(method_is_not_synced); + } + + // jvmti/jvmpi support + __ notify_method_entry(); + + // Reload method, it may have moved. + __ ld(R19_method, state_(_method)); + + //============================================================================= + // Get and call the signature handler + + __ ld(signature_handler_fd, method_(signature_handler)); + Label call_signature_handler; + + __ cmpdi(CCR0, signature_handler_fd, 0); + __ bne(CCR0, call_signature_handler); + + // Method has never been called. Either generate a specialized + // handler or point to the slow one. + // + // Pass parameter 'false' to avoid exception check in call_VM. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), R19_method, false); + + // Check for an exception while looking up the target method. If we + // incurred one, bail. + __ ld(pending_exception, thread_(pending_exception)); + __ cmpdi(CCR0, pending_exception, 0); + __ bne(CCR0, exception_return_sync_check); // has pending exception + + // reload method + __ ld(R19_method, state_(_method)); + + // Reload signature handler, it may have been created/assigned in the meanwhile + __ ld(signature_handler_fd, method_(signature_handler)); + + __ BIND(call_signature_handler); + + // Before we call the signature handler we push a new frame to + // protect the interpreter frame volatile registers when we return + // from jni but before we can get back to Java. + + // First set the frame anchor while the SP/FP registers are + // convenient and the slow signature handler can use this same frame + // anchor. + + // We have a TOP_IJAVA_FRAME here, which belongs to us. + __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R12_scratch2/*tmp*/); + + // Now the interpreter frame (and its call chain) have been + // invalidated and flushed. We are now protected against eager + // being enabled in native code. Even if it goes eager the + // registers will be reloaded as clean and we will invalidate after + // the call so no spurious flush should be possible. + + // Call signature handler and pass locals address. + // + // Our signature handlers copy required arguments to the C stack + // (outgoing C args), R3_ARG1 to R10_ARG8, and F1_ARG1 to + // F13_ARG13. + __ mr(R3_ARG1, R18_locals); +#if !defined(ABI_ELFv2) + __ ld(signature_handler_fd, 0, signature_handler_fd); +#endif + __ call_stub(signature_handler_fd); + // reload method + __ ld(R19_method, state_(_method)); + + // Remove the register parameter varargs slots we allocated in + // compute_interpreter_state. SP+16 ends up pointing to the ABI + // outgoing argument area. + // + // Not needed on PPC64. + //__ add(SP, SP, Argument::n_register_parameters*BytesPerWord); + + assert(result_handler_addr->is_nonvolatile(), "result_handler_addr must be in a non-volatile register"); + // Save across call to native method. + __ mr(result_handler_addr, R3_RET); + + // Set up fixed parameters and call the native method. + // If the method is static, get mirror into R4_ARG2. + + { + Label method_is_not_static; + // access_flags is non-volatile and still, no need to restore it + + // restore access flags + __ testbitdi(CCR0, R0, access_flags, JVM_ACC_STATIC_BIT); + __ bfalse(CCR0, method_is_not_static); + + // constants = method->constants(); + __ ld(R11_scratch1, in_bytes(methodOopDesc::const_offset()), R19_method); + __ ld(R11_scratch1/*constants*/, in_bytes(constMethodOopDesc::constants_offset()), R11_scratch1); + // pool_holder = method->constants()->pool_holder(); + __ ld(R11_scratch1/*pool_holder*/, constantPoolOopDesc::pool_holder_offset_in_bytes(), + R11_scratch1/*constants*/); + + const int mirror_offset = in_bytes(Klass::java_mirror_offset()); + + // mirror = pool_holder->klass_part()->java_mirror(); + __ ld(R0/*mirror*/, mirror_offset, R11_scratch1/*pool_holder*/); + // state->_native_mirror = mirror; + __ std(R0/*mirror*/, state_(_oop_temp)); + // R4_ARG2 = &state->_oop_temp; + __ addir(R4_ARG2, state_(_oop_temp)); + + __ BIND(method_is_not_static); + } + + // At this point, arguments have been copied off the stack into + // their JNI positions. Oops are boxed in-place on the stack, with + // handles copied to arguments. The result handler address is in a + // register. + + // pass JNIEnv address as first parameter + __ addir(R3_ARG1, thread_(jni_environment)); + + // Load the native_method entry before we change the thread state. + __ ld(native_method_fd, method_(native_function)); + + //============================================================================= + // Transition from _thread_in_Java to _thread_in_native. As soon as + // we make this change the safepoint code needs to be certain that + // the last Java frame we established is good. The pc in that frame + // just needs to be near here not an actual return address. + + // We use release_store_fence to update values like the thread state, where + // we don't want the current thread to continue until all our prior memory + // accesses (including the new thread state) are visible to other threads. + __ li(R0, _thread_in_native); + __ release(); + + // TODO: PPC port: assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + + if (UseMembar) { + __ fence(); + } + + //============================================================================= + // Call the native method. Argument registers must not have been + // overwritten since "__ call_stub(signature_handler);" (except for + // ARG1 and ARG2 for static methods) + __ call_c(native_method_fd); + + __ std(R3_RET, state_(_native_lresult)); + __ stfd(F1_RET, state_(_native_fresult)); + + // The frame_manager_lr field, which we use for setting the last + // java frame, gets overwritten by the signature handler. Restore + // it now. + __ get_PC_trash_LR(R11_scratch1); + __ std(R11_scratch1, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + + // Because of GC R19_method may no longer be valid. + + // Block, if necessary, before resuming in _thread_in_Java state. + // In order for GC to work, don't clear the last_Java_sp until after + // blocking. + + + + //============================================================================= + // Switch thread to "native transition" state before reading the + // synchronization state. This additional state is necessary + // because reading and testing the synchronization state is not + // atomic w.r.t. GC, as this scenario demonstrates: Java thread A, + // in _thread_in_native state, loads _not_synchronized and is + // preempted. VM thread changes sync state to synchronizing and + // suspends threads for GC. Thread A is resumed to finish this + // native method, but doesn't block here since it didn't see any + // synchronization in progress, and escapes. + + // We use release_store_fence to update values like the thread state, where + // we don't want the current thread to continue until all our prior memory + // accesses (including the new thread state) are visible to other threads. + __ li(R0/*thread_state*/, _thread_in_native_trans); + __ release(); + __ stw(R0/*thread_state*/, thread_(thread_state)); + if (UseMembar) { + __ fence(); + } + // Write serialization page so that the VM thread can do a pseudo remote + // membar. We use the current thread pointer to calculate a thread + // specific offset to write to within the page. This minimizes bus + // traffic due to cache line collision. + else { + __ serialize_memory(R16_thread, R11_scratch1, R12_scratch2); + } + + // Now before we return to java we must look for a current safepoint + // (a new safepoint can not start since we entered native_trans). + // We must check here because a current safepoint could be modifying + // the callers registers right this moment. + + // Acquire isn't strictly necessary here because of the fence, but + // sync_state is declared to be volatile, so we do it anyway. + __ load_const(sync_state_addr, SafepointSynchronize::address_of_state()); + + // TODO: PPC port: assert(4 == SafepointSynchronize::sz_state(), "unexpected field size"); + __ lwz(sync_state, 0, sync_state_addr); + + // TODO: PPC port: assert(4 == Thread::sz_suspend_flags(), "unexpected field size"); + __ lwz(suspend_flags, thread_(suspend_flags)); + + __ acquire(); + + Label sync_check_done; + Label do_safepoint; + // No synchronization in progress nor yet synchronized + __ cmpwi(CCR0, sync_state, SafepointSynchronize::_not_synchronized); + // not suspended + __ cmpwi(CCR1, suspend_flags, 0); + + __ bne(CCR0, do_safepoint); + __ beq(CCR1, sync_check_done); + __ bind(do_safepoint); + // Block. We do the call directly and leave the current + // last_Java_frame setup undisturbed. We must save any possible + // native result acrosss the call. No oop is present + + __ mr(R3_ARG1, R16_thread); +#if defined(ABI_ELFv2) + __ call_c(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), + relocInfo::none); +#else + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, JavaThread::check_special_condition_for_native_trans), + relocInfo::none); +#endif + __ bind(sync_check_done); + + //============================================================================= + // <<<<<< Back in Interpreter Frame >>>>> + + // We are in thread_in_native_trans here and back in the normal + // interpreter frame. We don't have to do anything special about + // safepoints and we can switch to Java mode anytime we are ready. + + // Note: frame::interpreter_frame_result has a dependency on how the + // method result is saved across the call to post_method_exit. For + // native methods it assumes that the non-FPU/non-void result is + // saved in _native_lresult and a FPU result in _native_fresult. If + // this changes then the interpreter_frame_result implementation + // will need to be updated too. + + // On PPC64, we have stored the result directly after the native call. + + //============================================================================= + // back in Java + + // We use release_store_fence to update values like the thread state, where + // we don't want the current thread to continue until all our prior memory + // accesses (including the new thread state) are visible to other threads. + __ li(R0/*thread_state*/, _thread_in_Java); + __ release(); + __ stw(R0/*thread_state*/, thread_(thread_state)); + if (UseMembar) { + __ fence(); + } + + __ reset_last_Java_frame(); + + // Reload GR27_method, call killed it. We can't look at + // state->_method until we're back in java state because in java + // state gc can't happen until we get to a safepoint. + // + // We've set thread_state to _thread_in_Java already, so restoring + // R19_method from R14_state works; R19_method is invalid, because + // GC may have happened. + __ ld(R19_method, state_(_method)); // reload method, may have moved + + // jvmdi/jvmpi support. Whether we've got an exception pending or + // not, and whether unlocking throws an exception or not, we notify + // on native method exit. If we do have an exception, we'll end up + // in the caller's context to handle it, so if we don't do the + // notify here, we'll drop it on the floor. + + __ notify_method_exit(true/*native method*/, + ilgl /*illegal state (not used for native methods)*/, + InterpreterMacroAssembler::NotifyJVMTI, + false /*check_exceptions*/); + + //============================================================================= + // Handle exceptions + + // See if we must unlock. + // + { + Label method_is_not_synced; + // is_synced is still alive + assert(is_synced->is_nonvolatile(), "is_synced must be non-volatile"); + __ bfalse(is_synced, method_is_not_synced); + + unlock_method(); + + __ bind(method_is_not_synced); + } + + // Reset active handles after returning from native. + // thread->active_handles()->clear(); + __ ld(active_handles, thread_(active_handles)); + // JNIHandleBlock::_top is an int. + // TODO: PPC port: assert(4 == JNIHandleBlock::top_size_in_bytes(), "unexpected field size"); + __ li(R0, 0); + __ stw(R0, JNIHandleBlock::top_offset_in_bytes(), active_handles); + + Label no_pending_exception_from_native_method; + __ ld(R0/*pending_exception*/, thread_(pending_exception)); + __ cmpdi(CCR0, R0/*pending_exception*/, 0); + __ beq(CCR0, no_pending_exception_from_native_method); + + + //----------------------------------------------------------------------------- + // An exception is pending. We call into the runtime only if the + // caller was not interpreted. If it was interpreted the + // interpreter will do the correct thing. If it isn't interpreted + // (call stub/compiled code) we will change our return and continue. + __ BIND(exception_return); + + Label return_to_initial_caller_with_pending_exception; + __ cmpdi(CCR0, R15_prev_state, 0); + __ beq(CCR0, return_to_initial_caller_with_pending_exception); + + // We are returning to an interpreter activation, just pop the state, + // pop our frame, leave the exception pending, and return. + __ pop_interpreter_state(/*prev_state_may_be_0=*/false); + __ pop_interpreter_frame(R11_scratch1, R12_scratch2, R21_tmp1 /* set to return pc */, R22_tmp2); + __ mtlr(R21_tmp1); + __ blr(); + + __ BIND(exception_return_sync_check); + + assert(is_synced->is_nonvolatile(), "is_synced must be non-volatile"); + __ bfalse(is_synced, exception_return); + unlock_method(); + __ b(exception_return); + + + __ BIND(return_to_initial_caller_with_pending_exception); + // We are returning to a c2i-adapter / call-stub, get the address of the + // exception handler, pop the frame and return to the handler. + + // First, pop to caller's frame. + __ pop_interpreter_frame(R11_scratch1, R12_scratch2, R21_tmp1 /* set to return pc */, R22_tmp2); + + __ push_frame_reg_args(0, R11_scratch1); + // Get the address of the exception handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + R16_thread, + R21_tmp1 /* return pc */); + __ pop_frame(); + + // Load the PC of the the exception handler into LR. + __ mtlr(R3_RET); + + // Load exception into R3_ARG1 and clear pending exception in thread. + __ ld(R3_ARG1/*exception*/, thread_(pending_exception)); + __ li(R4_ARG2, 0); + __ std(R4_ARG2, thread_(pending_exception)); + + // Load the original return pc into R4_ARG2. + __ mr(R4_ARG2/*issuing_pc*/, R21_tmp1); + + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + // Return to exception handler. + __ blr(); + + + //----------------------------------------------------------------------------- + // No exception pending. + __ BIND(no_pending_exception_from_native_method); + + // Move native method result back into proper registers and return. + // Invoke result handler (may unbox/promote). + __ ld(R3_RET, state_(_native_lresult)); + __ lfd(F1_RET, state_(_native_fresult)); + __ call_stub(result_handler_addr); + + // We have created a new BytecodeInterpreter object, now we must destroy it. + // + // Restore previous R14_state and caller's SP. R15_prev_state may + // be 0 here, because our caller may be the call_stub or compiled + // code. + __ pop_interpreter_state(/*prev_state_may_be_0=*/true); + __ pop_interpreter_frame(R11_scratch1, R12_scratch2, R21_tmp1 /* set to return pc */, R22_tmp2); + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + // Must use the return pc which was loaded from the caller's frame + // as the VM uses return-pc-patching for deoptimization. + __ mtlr(R21_tmp1); + __ blr(); + + + + //============================================================================= + // We encountered an exception while computing the interpreter + // state, so R14_state isn't valid. Act as if we just returned from + // the callee method with a pending exception. + __ BIND(stack_overflow_return); + + // + // Register state: + // R14_state invalid; trashed by compute_interpreter_state + // R15_prev_state valid, but may be 0 + // + // R1_SP valid, points to caller's SP; wasn't yet updated by + // compute_interpreter_state + // + + // Create exception oop and make it pending. + + // Throw the exception via RuntimeStub "throw_StackOverflowError_entry". + // + // Previously, we called C-Code directly. As a consequence, a + // possible GC tried to process the argument oops of the top frame + // (see RegisterMap::clear, which sets the corresponding flag to + // true). This lead to crashes because: + // 1. The top register map did not contain locations for the argument registers + // 2. The arguments are dead anyway, could be already overwritten in the worst case + // Solution: Call via special runtime stub that pushes it's own + // frame. This runtime stub has the flag "CodeBlob::caller_must_gc_arguments()" + // set to "false", what prevents the dead arguments getting GC'd. + // + // 2 cases exist: + // 1. We were called by the c2i adapter / call stub + // 2. We were called by the frame manager + // + // Both cases are handled by this code: + // 1. - initial_caller_sp was saved in both cases on entry, so it's safe to load it back even if it was not changed. + // - control flow will be: + // throw_stackoverflow_stub->VM->throw_stackoverflow_stub->forward_excep->excp_blob of caller method + // 2. - control flow will be: + // throw_stackoverflow_stub->VM->throw_stackoverflow_stub->forward_excep->rethrow_excp_entry of frame manager->resume_method + // Since we restored the caller SP above, the rethrow_excp_entry can restore the original interpreter state + // registers using the stack and resume the calling method with a pending excp. + + // Pop any c2i extension from the stack, restore LR just to be sure + __ ld(R0, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + __ mtlr(R0); + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + // Load target address of the runtime stub. + __ load_const(R12_scratch2, (StubRoutines::throw_StackOverflowError_entry())); + __ mtctr(R12_scratch2); + __ bctr(); + + + //============================================================================= + // Counter overflow. + + if (inc_counter) { + // Handle invocation counter overflow + __ bind(invocation_counter_overflow); + + generate_counter_overflow(continue_after_compile); + } + + native_entry = entry; + return entry; +} + +bool AbstractInterpreter::can_be_compiled(methodHandle m) { + // No special entry points that preclude compilation. + return true; +} + +// Unlock the current method. +// +void CppInterpreterGenerator::unlock_method(void) { + // Find preallocated monitor and unlock method. Method monitor is + // the first one. + + // Registers alive + // R14_state + // + // Registers updated + // volatiles + // + const Register monitor = R4_ARG2; + + // Pass address of initial monitor we allocated. + // + // First monitor. + __ addi(monitor, R14_state, -frame::interpreter_frame_monitor_size_in_bytes()); + + // Unlock method + __ unlock_object(monitor); +} + +// Lock the current method. +// +void CppInterpreterGenerator::lock_method(void) { + // Find preallocated monitor and lock method. Method monitor is the + // first one. + + // + // Registers alive + // R14_state + // + // Registers updated + // volatiles + // + + const Register monitor = R4_ARG2; + const Register object = R5_ARG3; + + // Pass address of initial monitor we allocated. + __ addi(monitor, R14_state, -frame::interpreter_frame_monitor_size_in_bytes()); + + // Pass object address. + __ ld(object, BasicObjectLock::obj_offset_in_bytes(), monitor); + + // Lock method. + __ lock_object(monitor, object); +} + +// Generate code for handling resuming a deopted method. +void CppInterpreterGenerator::generate_deopt_handling(Register result_index) { + + //============================================================================= + // Returning from a compiled method into a deopted method. The + // bytecode at the bcp has completed. The result of the bytecode is + // in the native abi (the tosca for the template based + // interpreter). Any stack space that was used by the bytecode that + // has completed has been removed (e.g. parameters for an invoke) so + // all that we have to do is place any pending result on the + // expression stack and resume execution on the next bytecode. + + Label return_from_deopt_common; + + // R3_RET and F1_RET are live here! Load the array index of the + // required result stub address and continue at return_from_deopt_common. + + // Deopt needs to jump to here to enter the interpreter (return a result). + deopt_frame_manager_return_atos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_OBJECT)); + __ b(return_from_deopt_common); + + deopt_frame_manager_return_btos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_BOOLEAN)); + __ b(return_from_deopt_common); + + deopt_frame_manager_return_itos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_INT)); + __ b(return_from_deopt_common); + + deopt_frame_manager_return_ltos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_LONG)); + __ b(return_from_deopt_common); + + deopt_frame_manager_return_ftos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_FLOAT)); + __ b(return_from_deopt_common); + + deopt_frame_manager_return_dtos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_DOUBLE)); + __ b(return_from_deopt_common); + + deopt_frame_manager_return_vtos = __ pc(); + __ li(result_index, AbstractInterpreter::BasicType_as_index(T_VOID)); + // Last one, fall-through to return_from_deopt_common. + + // Deopt return common. An index is present that lets us move any + // possible result being return to the interpreter's stack. + // + __ BIND(return_from_deopt_common); + +} + +// Generate the code to handle a more_monitors message from the c++ interpreter. +void CppInterpreterGenerator::generate_more_monitors() { + + // + // Registers alive + // R16_thread - JavaThread* + // R15_prev_state - previous BytecodeInterpreter or 0 + // R14_state - BytecodeInterpreter* address of receiver's interpreter state + // R1_SP - old stack pointer + // + // Registers updated + // R1_SP - new stack pointer + // + + // Very-local scratch registers. + const Register old_tos = R21_tmp1; + const Register new_tos = R22_tmp2; + const Register stack_base = R23_tmp3; + const Register stack_limit = R24_tmp4; + const Register slot = R25_tmp5; + const Register n_slots = R25_tmp5; + + // Interpreter state fields. + const Register msg = R24_tmp4; + + // Load up relevant interpreter state. + + __ ld(stack_base, state_(_stack_base)); // Old stack_base + __ ld(old_tos, state_(_stack)); // Old tos + __ ld(stack_limit, state_(_stack_limit)); // Old stack_limit + + // extracted monitor_size + int monitor_size = frame::interpreter_frame_monitor_size_in_bytes(); + assert(Assembler::is_aligned((unsigned int)monitor_size, + (unsigned int)frame::alignment_in_bytes), + "size of a monitor must respect alignment of SP"); + + // Save and restore top LR + __ ld(R12_scratch2, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + __ resize_frame(-monitor_size, R11_scratch1);// Allocate space for new monitor + __ std(R12_scratch2, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + // Initial_caller_sp is used as unextended_sp for non initial callers. + __ std(R1_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP); + __ addi(stack_base, stack_base, -monitor_size); // New stack_base + __ addi(new_tos, old_tos, -monitor_size); // New tos + __ addi(stack_limit, stack_limit, -monitor_size); // New stack_limit + + __ std(R1_SP, state_(_last_Java_sp)); // Update frame_bottom + + __ std(stack_base, state_(_stack_base)); // Update stack_base + __ std(new_tos, state_(_stack)); // Update tos + __ std(stack_limit, state_(_stack_limit)); // Update stack_limit + + __ li(msg, BytecodeInterpreter::got_monitors); // Tell interpreter we allocated the lock + __ stw(msg, state_(_msg)); + + // Shuffle expression stack down. Recall that stack_base points + // just above the new expression stack bottom. Old_tos and new_tos + // are used to scan thru the old and new expression stacks. + + Label copy_slot, copy_slot_finished; + __ sub(n_slots, stack_base, new_tos); + __ srdi_(n_slots, n_slots, LogBytesPerWord); // compute number of slots to copy + assert(LogBytesPerWord == 3, "conflicts assembler instructions"); + __ beq(CCR0, copy_slot_finished); // nothing to copy + + __ mtctr(n_slots); + + // loop + __ bind(copy_slot); + __ ldu(slot, BytesPerWord, old_tos); // slot = *++old_tos; + __ stdu(slot, BytesPerWord, new_tos); // *++new_tos = slot; + __ bdnz(copy_slot); + + __ bind(copy_slot_finished); + + // Restart interpreter + __ li(R0, 0); + __ std(R0, BasicObjectLock::obj_offset_in_bytes(), stack_base); // Mark lock as unused +} + +address CppInterpreterGenerator::generate_normal_entry(void) { + if (interpreter_frame_manager != NULL) return interpreter_frame_manager; + + address entry = __ pc(); + + address return_from_native_pc = (address) NULL; + + // Initial entry to frame manager (from call_stub or c2i_adapter) + + // + // Registers alive + // R16_thread - JavaThread* + // R19_method - callee's methodOop (method to be invoked) + // R17_tos - address of sender tos (prepushed) + // R1_SP - SP prepared by call stub such that caller's outgoing args are near top + // LR - return address to caller (call_stub or c2i_adapter) + // R21_sender_SP - initial caller sp + // + // Registers updated + // R15_prev_state - 0 + // + // Stack layout at this point: + // + // 0 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // alignment (optional) + // [outgoing Java arguments] <-- R17_tos + // ... + // PARENT [PARENT_IJAVA_FRAME_ABI] + // ... + // + + // Save initial_caller_sp to caller's abi. + // The caller frame must be resized before returning to get rid of + // the c2i part on top of the calling compiled frame (if any). + // R21_tmp1 must match sender_sp in gen_c2i_adapter. + // Now override the saved SP with the senderSP so we can pop c2i + // arguments (if any) off when we return. + __ std(R21_sender_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP); + + // Save LR to caller's frame. We don't use _abi(lr) here, + // because it is not safe. + __ mflr(R0); + __ std(R0, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + + // If we come here, it is the first invocation of the frame manager. + // So there is no previous interpreter state. + __ li(R15_prev_state, 0); + + + // Fall through to where "recursive" invocations go. + + //============================================================================= + // Dispatch an instance of the interpreter. Recursive activations + // come here. + + Label re_dispatch; + __ BIND(re_dispatch); + + // + // Registers alive + // R16_thread - JavaThread* + // R19_method - callee's methodOop + // R17_tos - address of caller's tos (prepushed) + // R15_prev_state - address of caller's BytecodeInterpreter or 0 + // R1_SP - caller's SP trimmed such that caller's outgoing args are near top. + // + // Stack layout at this point: + // + // 0 [TOP_IJAVA_FRAME_ABI] + // alignment (optional) + // [outgoing Java arguments] + // ... + // PARENT [PARENT_IJAVA_FRAME_ABI] + // ... + + // fall through to interpreted execution + + //============================================================================= + // Allocate a new Java frame and initialize the new interpreter state. + + Label stack_overflow_return; + + // Create a suitable new Java frame plus a new BytecodeInterpreter instance + // in the current (frame manager's) C frame. + generate_compute_interpreter_state(stack_overflow_return); + + // fall through + + //============================================================================= + // Interpreter dispatch. + + Label call_interpreter; + __ BIND(call_interpreter); + + // + // Registers alive + // R16_thread - JavaThread* + // R15_prev_state - previous BytecodeInterpreter or 0 + // R14_state - address of receiver's BytecodeInterpreter + // R1_SP - receiver's stack pointer + // + + // Thread fields. + const Register pending_exception = R21_tmp1; + + // Interpreter state fields. + const Register msg = R24_tmp4; + + // MethodOop fields. + const Register parameter_count = R25_tmp5; + const Register result_index = R26_tmp6; + + const Register dummy = R28_tmp8; + + // Address of various interpreter stubs. + // R29_tmp9 is reserved. + const Register stub_addr = R27_tmp7; + + // Uncommon trap needs to jump to here to enter the interpreter + // (re-execute current bytecode). + unctrap_frame_manager_entry = __ pc(); + + // If we are profiling, store our fp (BSP) in the thread so we can + // find it during a tick. + if (Arguments::has_profile()) { + // On PPC64 we store the pointer to the current BytecodeInterpreter, + // instead of the bsp of ia64. This should suffice to be able to + // find all interesting information. + __ std(R14_state, thread_(last_interpreter_fp)); + } + + // R16_thread, R14_state and R15_prev_state are nonvolatile + // registers. There is no need to save these. If we needed to save + // some state in the current Java frame, this could be a place to do + // so. + + // Call Java bytecode dispatcher passing "BytecodeInterpreter* istate". + __ call_VM_leaf(CAST_FROM_FN_PTR(address, + JvmtiExport::can_post_interpreter_events() + ? BytecodeInterpreter::runWithChecks + : BytecodeInterpreter::run), + R14_state); + + interpreter_return_address = __ last_calls_return_pc(); + + // R16_thread, R14_state and R15_prev_state have their values preserved. + + // If we are profiling, clear the fp in the thread to tell + // the profiler that we are no longer in the interpreter. + if (Arguments::has_profile()) { + __ li(R11_scratch1, 0); + __ std(R11_scratch1, thread_(last_interpreter_fp)); + } + + // Load message from bytecode dispatcher. + // TODO: PPC port: guarantee(4 == BytecodeInterpreter::sz_msg(), "unexpected field size"); + __ lwz(msg, state_(_msg)); + + + Label more_monitors; + Label return_from_native; + Label return_from_native_common; + Label return_from_native_no_exception; + Label return_from_interpreted_method; + Label return_from_recursive_activation; + Label unwind_recursive_activation; + Label resume_interpreter; + Label return_to_initial_caller; + Label unwind_initial_activation; + Label unwind_initial_activation_pending_exception; + Label call_method; + Label call_special; + Label retry_method; + Label retry_method_osr; + Label popping_frame; + Label throwing_exception; + + // Branch according to the received message + + __ cmpwi(CCR1, msg, BytecodeInterpreter::call_method); + __ cmpwi(CCR2, msg, BytecodeInterpreter::return_from_method); + + __ beq(CCR1, call_method); + __ beq(CCR2, return_from_interpreted_method); + + __ cmpwi(CCR3, msg, BytecodeInterpreter::more_monitors); + __ cmpwi(CCR4, msg, BytecodeInterpreter::throwing_exception); + + __ beq(CCR3, more_monitors); + __ beq(CCR4, throwing_exception); + + __ cmpwi(CCR5, msg, BytecodeInterpreter::popping_frame); + __ cmpwi(CCR6, msg, BytecodeInterpreter::do_osr); + + __ beq(CCR5, popping_frame); + __ beq(CCR6, retry_method_osr); + + __ stop("bad message from interpreter"); + + + //============================================================================= + // Add a monitor just below the existing one(s). State->_stack_base + // points to the lowest existing one, so we insert the new one just + // below it and shuffle the expression stack down. Ref. the above + // stack layout picture, we must update _stack_base, _stack, _stack_limit + // and _last_Java_sp in the interpreter state. + + __ BIND(more_monitors); + + generate_more_monitors(); + __ b(call_interpreter); + + generate_deopt_handling(result_index); + + // Restoring the R14_state is already done by the deopt_blob. + + // Current tos includes no parameter slots. + __ ld(R17_tos, state_(_stack)); + __ li(msg, BytecodeInterpreter::deopt_resume); + __ b(return_from_native_common); + + // We are sent here when we are unwinding from a native method or + // adapter with an exception pending. We need to notify the interpreter + // that there is an exception to process. + // We arrive here also if the frame manager called an (interpreted) target + // which returns with a StackOverflow exception. + // The control flow is in this case is: + // frame_manager->throw_excp_stub->forward_excp->rethrow_excp_entry + + AbstractInterpreter::_rethrow_exception_entry = __ pc(); + + // Restore R14_state. + __ ld(R14_state, 0, R1_SP); + __ addi(R14_state, R14_state, + -frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + + // Store exception oop into thread object. + __ std(R3_RET, thread_(pending_exception)); + __ li(msg, BytecodeInterpreter::method_resume /*rethrow_exception*/); + // + // NOTE: the interpreter frame as setup be deopt does NOT include + // any parameter slots (good thing since we have no callee here + // and couldn't remove them) so we don't have to do any calculations + // here to figure it out. + // + __ ld(R17_tos, state_(_stack)); + __ b(return_from_native_common); + + + //============================================================================= + // Returning from a native method. Result is in the native abi + // location so we must move it to the java expression stack. + + __ BIND(return_from_native); + guarantee(return_from_native_pc == (address) NULL, "precondition"); + return_from_native_pc = __ pc(); + + // Restore R14_state. + __ ld(R14_state, 0, R1_SP); + __ addi(R14_state, R14_state, -frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + + // + // Registers alive + // R16_thread + // R14_state - address of caller's BytecodeInterpreter. + // R3_RET - integer result, if any. + // F1_RET - float result, if any. + // + // Registers updated + // R19_method - callee's methodOop + // R17_tos - caller's tos, with outgoing args popped + // result_index - index of result handler. + // msg - message for resuming interpreter. + // + + // Very-local scratch registers. + + const ConditionRegister have_pending_exception = CCR0; + + // Load callee methodOop, gc may have moved it. + __ ld(R19_method, state_(_result._to_call._callee)); + + // Load address of caller's tos. includes parameter slots. + __ ld(R17_tos, state_(_stack)); + + // Pop callee's parameters. + __ lhz(parameter_count, method_(size_of_parameters)); + __ sldi(parameter_count, parameter_count, Interpreter::logStackElementSize); + __ add(R17_tos, R17_tos, parameter_count); + + // Result stub address array index + // TODO: PPC port: assert(4 == methodOopDesc::sz_result_index(), "unexpected field size"); + __ lwa(result_index, method_(result_index)); + + __ li(msg, BytecodeInterpreter::method_resume); + + // + // Registers alive + // R16_thread + // R14_state - address of caller's BytecodeInterpreter. + // R17_tos - address of caller's tos with outgoing args already popped + // R3_RET - integer return value, if any. + // F1_RET - float return value, if any. + // result_index - index of result handler. + // msg - message for resuming interpreter. + // + // Registers updated + // R3_RET - new address of caller's tos, including result, if any + // + + __ BIND(return_from_native_common); + + // Check for pending exception + __ ld(pending_exception, thread_(pending_exception)); + __ cmpdi(CCR0, pending_exception, 0); + __ beq(CCR0, return_from_native_no_exception); + + // If there's a pending exception, we really have no result, so + // R3_RET is dead. Resume_interpreter assumes the new tos is in + // R3_RET. + __ mr(R3_RET, R17_tos); + // `resume_interpreter' expects R15_prev_state to be alive. + __ ld(R15_prev_state, state_(_prev_link)); + __ b(resume_interpreter); + + __ BIND(return_from_native_no_exception); + + // No pending exception, copy method result from native ABI register + // to tos. + + // Address of stub descriptor address array. + __ load_const(stub_addr, CppInterpreter::tosca_result_to_stack()); + + // Pass address of tos to stub. + __ mr(R4_ARG2, R17_tos); + + // Address of stub descriptor address. + __ sldi(result_index, result_index, LogBytesPerWord); + __ add(stub_addr, stub_addr, result_index); + + // Stub descriptor address. + __ ld(stub_addr, 0, stub_addr); + + // TODO: don't do this via a call, do it in place! + // + // call stub via descriptor + // in R3_ARG1/F1_ARG1: result value (R3_RET or F1_RET) + __ call_stub(stub_addr); + + // new tos = result of call in R3_RET + + // `resume_interpreter' expects R15_prev_state to be alive. + __ ld(R15_prev_state, state_(_prev_link)); + __ b(resume_interpreter); + + //============================================================================= + // We encountered an exception while computing the interpreter + // state, so R14_state isn't valid. Act as if we just returned from + // the callee method with a pending exception. + __ BIND(stack_overflow_return); + + // + // Registers alive + // R16_thread - JavaThread* + // R1_SP - old stack pointer + // R19_method - callee's methodOop + // R17_tos - address of caller's tos (prepushed) + // R15_prev_state - address of caller's BytecodeInterpreter or 0 + // R18_locals - address of callee's locals array + // + // Registers updated + // R3_RET - address of resuming tos, if recursive unwind + + Label Lskip_unextend_SP; + + { + const ConditionRegister is_initial_call = CCR0; + const Register tos_save = R21_tmp1; + const Register tmp = R22_tmp2; + + assert(tos_save->is_nonvolatile(), "need a nonvolatile"); + + // Is the exception thrown in the initial Java frame of this frame + // manager frame? + __ cmpdi(is_initial_call, R15_prev_state, 0); + __ bne(is_initial_call, Lskip_unextend_SP); + + // Pop any c2i extension from the stack. This is necessary in the + // non-recursive case (that is we were called by the c2i adapter, + // meaning we have to prev state). In this case we entered the frame + // manager through a special entry which pushes the orignal + // unextended SP to the stack. Here we load it back. + __ ld(R0, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + __ mtlr(R0); + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + // Fall through + + __ bind(Lskip_unextend_SP); + + // Throw the exception via RuntimeStub "throw_StackOverflowError_entry". + // + // Previously, we called C-Code directly. As a consequence, a + // possible GC tried to process the argument oops of the top frame + // (see RegisterMap::clear, which sets the corresponding flag to + // true). This lead to crashes because: + // 1. The top register map did not contain locations for the argument registers + // 2. The arguments are dead anyway, could be already overwritten in the worst case + // Solution: Call via special runtime stub that pushes it's own frame. This runtime stub has the flag + // "CodeBlob::caller_must_gc_arguments()" set to "false", what prevents the dead arguments getting GC'd. + // + // 2 cases exist: + // 1. We were called by the c2i adapter / call stub + // 2. We were called by the frame manager + // + // Both cases are handled by this code: + // 1. - initial_caller_sp was saved on stack => Load it back and we're ok + // - control flow will be: + // throw_stackoverflow_stub->VM->throw_stackoverflow_stub->forward_excep->excp_blob of calling method + // 2. - control flow will be: + // throw_stackoverflow_stub->VM->throw_stackoverflow_stub->forward_excep-> + // ->rethrow_excp_entry of frame manager->resume_method + // Since we restored the caller SP above, the rethrow_excp_entry can restore the original interpreter state + // registers using the stack and resume the calling method with a pending excp. + + __ load_const(R3_ARG1, (StubRoutines::throw_StackOverflowError_entry())); + __ mtctr(R3_ARG1); + __ bctr(); + } + //============================================================================= + // We have popped a frame from an interpreted call. We are assured + // of returning to an interpreted call by the popframe abi. We have + // no return value all we have to do is pop the current frame and + // then make sure that the top of stack (of the caller) gets set to + // where it was when we entered the callee (i.e. the args are still + // in place). Or we are returning to the interpreter. In the first + // case we must extract result (if any) from the java expression + // stack and store it in the location the native abi would expect + // for a call returning this type. In the second case we must simply + // do a stack to stack move as we unwind. + + __ BIND(popping_frame); + + // Registers alive + // R14_state + // R15_prev_state + // R17_tos + // + // Registers updated + // R19_method + // R3_RET + // msg + { + Label L; + + // Reload callee method, gc may have moved it. + __ ld(R19_method, state_(_method)); + + // We may be returning to a deoptimized frame in which case the + // usual assumption of a recursive return is not true. + + // not equal = is recursive call + __ cmpdi(CCR0, R15_prev_state, 0); + + __ bne(CCR0, L); + + // Pop_frame capability. + // The pop_frame api says that the underlying frame is a Java frame, in this case + // (prev_state==null) it must be a compiled frame: + // + // Stack at this point: I, C2I + C, ... + // + // The outgoing arguments of the call have just been copied (popframe_preserve_args). + // By the pop_frame api, we must end up in an interpreted frame. So the compiled frame + // will be deoptimized. Deoptimization will restore the outgoing arguments from + // popframe_preserve_args, adjust the tos such that it includes the popframe_preserve_args, + // and adjust the bci such that the call will be executed again. + // We have no results, just pop the interpreter frame, resize the compiled frame to get rid + // of the c2i extension and return to the deopt_handler. + __ b(unwind_initial_activation); + + // is recursive call + __ bind(L); + + // Resume_interpreter expects the original tos in R3_RET. + __ ld(R3_RET, prev_state_(_stack)); + + // We're done. + __ li(msg, BytecodeInterpreter::popping_frame); + + __ b(unwind_recursive_activation); + } + + + //============================================================================= + + // We have finished an interpreted call. We are either returning to + // native (call_stub/c2) or we are returning to the interpreter. + // When returning to native, we must extract the result (if any) + // from the java expression stack and store it in the location the + // native abi expects. When returning to the interpreter we must + // simply do a stack to stack move as we unwind. + + __ BIND(return_from_interpreted_method); + + // + // Registers alive + // R16_thread - JavaThread* + // R15_prev_state - address of caller's BytecodeInterpreter or 0 + // R14_state - address of callee's interpreter state + // R1_SP - callee's stack pointer + // + // Registers updated + // R19_method - callee's method + // R3_RET - address of result (new caller's tos), + // + // if returning to interpreted + // msg - message for interpreter, + // if returning to interpreted + // + + // Check if this is the initial invocation of the frame manager. + // If so, R15_prev_state will be null. + __ cmpdi(CCR0, R15_prev_state, 0); + + // Reload callee method, gc may have moved it. + __ ld(R19_method, state_(_method)); + + // Load the method's result type. + __ lwz(result_index, method_(result_index)); + + // Go to return_to_initial_caller if R15_prev_state is null. + __ beq(CCR0, return_to_initial_caller); + + // Copy callee's result to caller's expression stack via inline stack-to-stack + // converters. + { + Register new_tos = R3_RET; + Register from_temp = R4_ARG2; + Register from = R5_ARG3; + Register tos = R6_ARG4; + Register tmp1 = R7_ARG5; + Register tmp2 = R8_ARG6; + + ConditionRegister result_type_is_void = CCR1; + ConditionRegister result_type_is_long = CCR2; + ConditionRegister result_type_is_double = CCR3; + + Label stack_to_stack_void; + Label stack_to_stack_double_slot; // T_LONG, T_DOUBLE + Label stack_to_stack_single_slot; // T_BOOLEAN, T_BYTE, T_CHAR, T_SHORT, T_INT, T_FLOAT, T_OBJECT + Label stack_to_stack_done; + + // Pass callee's address of tos + BytesPerWord + __ ld(from_temp, state_(_stack)); + + // result type: void + __ cmpwi(result_type_is_void, result_index, AbstractInterpreter::BasicType_as_index(T_VOID)); + + // Pass caller's tos == callee's locals address + __ ld(tos, state_(_locals)); + + // result type: long + __ cmpwi(result_type_is_long, result_index, AbstractInterpreter::BasicType_as_index(T_LONG)); + + __ addi(from, from_temp, Interpreter::stackElementSize); + + // !! don't branch above this line !! + + // handle void + __ beq(result_type_is_void, stack_to_stack_void); + + // result type: double + __ cmpwi(result_type_is_double, result_index, AbstractInterpreter::BasicType_as_index(T_DOUBLE)); + + // handle long or double + __ beq(result_type_is_long, stack_to_stack_double_slot); + __ beq(result_type_is_double, stack_to_stack_double_slot); + + // fall through to single slot types (incl. object) + + { + __ BIND(stack_to_stack_single_slot); + // T_BOOLEAN, T_BYTE, T_CHAR, T_SHORT, T_INT, T_FLOAT, T_OBJECT + + __ ld(tmp1, 0, from); + __ std(tmp1, 0, tos); + // New expression stack top + __ addi(new_tos, tos, - BytesPerWord); + + __ b(stack_to_stack_done); + } + + { + __ BIND(stack_to_stack_double_slot); + // T_LONG, T_DOUBLE + + // Move both entries for debug purposes even though only one is live + __ ld(tmp1, BytesPerWord, from); + __ ld(tmp2, 0, from); + __ std(tmp1, 0, tos); + __ std(tmp2, -BytesPerWord, tos); + + // new expression stack top + __ addi(new_tos, tos, - 2 * BytesPerWord); // two slots + __ b(stack_to_stack_done); + } + + { + __ BIND(stack_to_stack_void); + // T_VOID + + // new expression stack top + __ mr(new_tos, tos); + // fall through to stack_to_stack_done + } + + __ BIND(stack_to_stack_done); + } + + // new tos = R3_RET + + // Get the message for the interpreter + __ li(msg, BytecodeInterpreter::method_resume); + + // And fall thru + + + //============================================================================= + // Restore caller's interpreter state and pass pointer to caller's + // new tos to caller. + + __ BIND(unwind_recursive_activation); + + // + // Registers alive + // R15_prev_state - address of caller's BytecodeInterpreter + // R3_RET - address of caller's tos + // msg - message for caller's BytecodeInterpreter + // R1_SP - callee's stack pointer + // + // Registers updated + // R14_state - address of caller's BytecodeInterpreter + // R15_prev_state - address of its parent or 0 + // + + // Pop callee's interpreter and set R14_state to caller's interpreter. + __ pop_interpreter_state(/*prev_state_may_be_0=*/false); + + // And fall thru + + + //============================================================================= + // Resume the (calling) interpreter after a call. + + __ BIND(resume_interpreter); + + // + // Registers alive + // R14_state - address of resuming BytecodeInterpreter + // R15_prev_state - address of its parent or 0 + // R3_RET - address of resuming tos + // msg - message for resuming interpreter + // R1_SP - callee's stack pointer + // + // Registers updated + // R1_SP - caller's stack pointer + // + + // Restore C stack pointer of caller (resuming interpreter), + // R14_state already points to the resuming BytecodeInterpreter. + __ pop_interpreter_frame_to_state(R14_state, R21_tmp1, R11_scratch1, R12_scratch2); + + // Store new address of tos (holding return value) in interpreter state. + __ std(R3_RET, state_(_stack)); + + // Store message for interpreter. + __ stw(msg, state_(_msg)); + + __ b(call_interpreter); + + //============================================================================= + // Interpreter returning to native code (call_stub/c1/c2) from + // initial activation. Convert stack result and unwind activation. + + __ BIND(return_to_initial_caller); + + // + // Registers alive + // R19_method - callee's methodOop + // R14_state - address of callee's interpreter state + // R16_thread - JavaThread + // R1_SP - callee's stack pointer + // + // Registers updated + // R3_RET/F1_RET - result in expected output register + // + + // If we have an exception pending we have no result and we + // must figure out where to really return to. + // + __ ld(pending_exception, thread_(pending_exception)); + __ cmpdi(CCR0, pending_exception, 0); + __ bne(CCR0, unwind_initial_activation_pending_exception); + + __ lwa(result_index, method_(result_index)); + + // Address of stub descriptor address array. + __ load_const(stub_addr, CppInterpreter::stack_result_to_native()); + + // Pass address of callee's tos + BytesPerWord. + // Will then point directly to result. + __ ld(R3_ARG1, state_(_stack)); + __ addi(R3_ARG1, R3_ARG1, Interpreter::stackElementSize); + + // Address of stub descriptor address + __ sldi(result_index, result_index, LogBytesPerWord); + __ add(stub_addr, stub_addr, result_index); + + // Stub descriptor address + __ ld(stub_addr, 0, stub_addr); + + // TODO: don't do this via a call, do it in place! + // + // call stub via descriptor + __ call_stub(stub_addr); + + __ BIND(unwind_initial_activation); + + // Unwind from initial activation. No exception is pending. + + // + // Stack layout at this point: + // + // 0 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // ... + // CALLER [PARENT_IJAVA_FRAME_ABI] + // ... + // CALLER [unextended ABI] + // ... + // + // The CALLER frame has a C2I adapter or is an entry-frame. + // + + // An interpreter frame exists, we may pop the TOP_IJAVA_FRAME and + // turn the caller's PARENT_IJAVA_FRAME back into a TOP_IJAVA_FRAME. + // But, we simply restore the return pc from the caller's frame and + // use the caller's initial_caller_sp as the new SP which pops the + // interpreter frame and "resizes" the caller's frame to its "unextended" + // size. + + // get rid of top frame + __ pop_frame(); + + // Load return PC from parent frame. + __ ld(R21_tmp1, _parent_ijava_frame_abi(lr), R1_SP); + + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + // update LR + __ mtlr(R21_tmp1); + + // return + __ blr(); + + //============================================================================= + // Unwind from initial activation. An exception is pending + + __ BIND(unwind_initial_activation_pending_exception); + + // + // Stack layout at this point: + // + // 0 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // ... + // CALLER [PARENT_IJAVA_FRAME_ABI] + // ... + // CALLER [unextended ABI] + // ... + // + // The CALLER frame has a C2I adapter or is an entry-frame. + // + + // An interpreter frame exists, we may pop the TOP_IJAVA_FRAME and + // turn the caller's PARENT_IJAVA_FRAME back into a TOP_IJAVA_FRAME. + // But, we just pop the current TOP_IJAVA_FRAME and fall through + + __ pop_frame(); + __ ld(R3_ARG1, _top_ijava_frame_abi(lr), R1_SP); + + // + // Stack layout at this point: + // + // CALLER [PARENT_IJAVA_FRAME_ABI] <-- R1_SP + // ... + // CALLER [unextended ABI] + // ... + // + // The CALLER frame has a C2I adapter or is an entry-frame. + // + // Registers alive + // R16_thread + // R3_ARG1 - return address to caller + // + // Registers updated + // R3_ARG1 - address of pending exception + // R4_ARG2 - issuing pc = return address to caller + // LR - address of exception handler stub + // + + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + __ mr(R14, R3_ARG1); // R14 := ARG1 + __ mr(R4_ARG2, R3_ARG1); // ARG2 := ARG1 + + // Find the address of the "catch_exception" stub. + __ push_frame_reg_args(0, R11_scratch1); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + R16_thread, + R4_ARG2); + __ pop_frame(); + + // Load continuation address into LR. + __ mtlr(R3_RET); + + // Load address of pending exception and clear it in thread object. + __ ld(R3_ARG1/*R3_RET*/, thread_(pending_exception)); + __ li(R4_ARG2, 0); + __ std(R4_ARG2, thread_(pending_exception)); + + // re-load issuing pc + __ mr(R4_ARG2, R14); + + // Branch to found exception handler. + __ blr(); + + //============================================================================= + // Call a new method. Compute new args and trim the expression stack + // to only what we are currently using and then recurse. + + __ BIND(call_method); + + // + // Registers alive + // R16_thread + // R14_state - address of caller's BytecodeInterpreter + // R1_SP - caller's stack pointer + // + // Registers updated + // R15_prev_state - address of caller's BytecodeInterpreter + // R17_tos - address of caller's tos + // R19_method - callee's methodOop + // R1_SP - trimmed back + // + + // Very-local scratch registers. + + const Register offset = R21_tmp1; + const Register tmp = R22_tmp2; + const Register self_entry = R23_tmp3; + const Register stub_entry = R24_tmp4; + + const ConditionRegister cr = CCR0; + + // Load the address of the frame manager. + __ load_const(self_entry, &interpreter_frame_manager); + __ ld(self_entry, 0, self_entry); + + // Load BytecodeInterpreter._result._to_call._callee (callee's methodOop). + __ ld(R19_method, state_(_result._to_call._callee)); + // Load BytecodeInterpreter._stack (outgoing tos). + __ ld(R17_tos, state_(_stack)); + + // Save address of caller's BytecodeInterpreter. + __ mr(R15_prev_state, R14_state); + + // Load the callee's entry point. + // Load BytecodeInterpreter._result._to_call._callee_entry_point. + __ ld(stub_entry, state_(_result._to_call._callee_entry_point)); + + // Check whether stub_entry is equal to self_entry. + __ cmpd(cr, self_entry, stub_entry); + // if (self_entry == stub_entry) + // do a re-dispatch + __ beq(cr, re_dispatch); + // else + // call the specialized entry (adapter for jni or compiled code) + __ BIND(call_special); + + // + // Call the entry generated by `InterpreterGenerator::generate_native_entry'. + // + // Registers alive + // R16_thread + // R15_prev_state - address of caller's BytecodeInterpreter + // R19_method - callee's methodOop + // R17_tos - address of caller's tos + // R1_SP - caller's stack pointer + // + + // Mark return from specialized entry for generate_native_entry. + guarantee(return_from_native_pc != (address) NULL, "precondition"); + frame_manager_specialized_return = return_from_native_pc; + + // Set sender_SP in case we call interpreter native wrapper which + // will expect it. Compiled code should not care. + __ mr(R21_sender_SP, R1_SP); + + // Do a tail call here, and let the link register point to + // frame_manager_specialized_return which is return_from_native_pc. + __ load_const(tmp, frame_manager_specialized_return); + __ call_stub_and_return_to(stub_entry, tmp /* return_pc=tmp */); + + + //============================================================================= + // + // InterpretMethod triggered OSR compilation of some Java method M + // and now asks to run the compiled code. We call this code the + // `callee'. + // + // This is our current idea on how OSR should look like on PPC64: + // + // While interpreting a Java method M the stack is: + // + // (InterpretMethod (M), IJAVA_FRAME (M), ANY_FRAME, ...). + // + // After having OSR compiled M, `InterpretMethod' returns to the + // frame manager, sending the message `retry_method_osr'. The stack + // is: + // + // (IJAVA_FRAME (M), ANY_FRAME, ...). + // + // The compiler will have generated an `nmethod' suitable for + // continuing execution of M at the bytecode index at which OSR took + // place. So now the frame manager calls the OSR entry. The OSR + // entry sets up a JIT_FRAME for M and continues execution of M with + // initial state determined by the IJAVA_FRAME. + // + // (JIT_FRAME (M), IJAVA_FRAME (M), ANY_FRAME, ...). + // + + __ BIND(retry_method_osr); + { + // + // Registers alive + // R16_thread + // R15_prev_state - address of caller's BytecodeInterpreter + // R14_state - address of callee's BytecodeInterpreter + // R1_SP - callee's SP before call to InterpretMethod + // + // Registers updated + // R17 - pointer to callee's locals array + // (declared via `interpreter_arg_ptr_reg' in the AD file) + // R19_method - callee's methodOop + // R1_SP - callee's SP (will become SP of OSR adapter frame) + // + + // Provide a debugger breakpoint in the frame manager if breakpoints + // in osr'd methods are requested. +#ifdef COMPILER2 + NOT_PRODUCT( if (OptoBreakpointOSR) { __ illtrap(); } ) +#endif + + // Load callee's pointer to locals array from callee's state. + // __ ld(R17, state_(_locals)); + + // Load osr entry. + __ ld(R12_scratch2, state_(_result._osr._osr_entry)); + + // Load address of temporary osr buffer to arg1. + __ ld(R3_ARG1, state_(_result._osr._osr_buf)); + __ mtctr(R12_scratch2); + + // Load method oop, gc may move it during execution of osr'd method. + __ ld(R22_tmp2, state_(_method)); + // Load message 'call_method'. + __ li(R23_tmp3, BytecodeInterpreter::call_method); + + { + // Pop the IJAVA frame of the method which we are going to call osr'd. + Label no_state, skip_no_state; + __ pop_interpreter_state(/*prev_state_may_be_0=*/true); + __ cmpdi(CCR0, R14_state,0); + __ beq(CCR0, no_state); + // return to interpreter + __ pop_interpreter_frame_to_state(R14_state, R11_scratch1, R12_scratch2, R21_tmp1); + + // Init _result._to_call._callee and tell gc that it contains a valid oop + // by setting _msg to 'call_method'. + __ std(R22_tmp2, state_(_result._to_call._callee)); + // TODO: PPC port: assert(4 == BytecodeInterpreter::sz_msg(), "unexpected field size"); + __ stw(R23_tmp3, state_(_msg)); + + __ load_const(R21_tmp1, frame_manager_specialized_return); + __ b(skip_no_state); + __ bind(no_state); + + // Return to initial caller. + + // Get rid of top frame. + __ pop_frame(); + + // Load return PC from parent frame. + __ ld(R21_tmp1, _parent_ijava_frame_abi(lr), R1_SP); + + // Resize frame to get rid of a potential extension. + __ resize_frame_to_initial_caller(R11_scratch1, R12_scratch2); + + __ bind(skip_no_state); + + // Update LR with return pc. + __ mtlr(R21_tmp1); + } + // Jump to the osr entry point. + __ bctr(); + + } + + //============================================================================= + // Interpreted method "returned" with an exception, pass it on. + // Pass no result, unwind activation and continue/return to + // interpreter/call_stub/c2. + + __ BIND(throwing_exception); + + // Check if this is the initial invocation of the frame manager. If + // so, previous interpreter state in R15_prev_state will be null. + + // New tos of caller is callee's first parameter address, that is + // callee's incoming arguments are popped. + __ ld(R3_RET, state_(_locals)); + + // Check whether this is an initial call. + __ cmpdi(CCR0, R15_prev_state, 0); + // Yes, called from the call stub or from generated code via a c2i frame. + __ beq(CCR0, unwind_initial_activation_pending_exception); + + // Send resume message, interpreter will see the exception first. + + __ li(msg, BytecodeInterpreter::method_resume); + __ b(unwind_recursive_activation); + + + //============================================================================= + // Push the last instruction out to the code buffer. + + { + __ unimplemented("end of InterpreterGenerator::generate_normal_entry", 128); + } + + interpreter_frame_manager = entry; + return interpreter_frame_manager; +} + +// Generate code for various sorts of method entries +// +address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized : break; + case Interpreter::native : // Fall thru + case Interpreter::native_synchronized : entry_point = ((CppInterpreterGenerator*)this)->generate_native_entry(); break; + case Interpreter::empty : break; + case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + // These are special interpreter intrinsics which we don't support so far. + case Interpreter::java_lang_math_sin : break; + case Interpreter::java_lang_math_cos : break; + case Interpreter::java_lang_math_tan : break; + case Interpreter::java_lang_math_abs : break; + case Interpreter::java_lang_math_log : break; + case Interpreter::java_lang_math_log10 : break; + case Interpreter::java_lang_math_sqrt : break; + case Interpreter::java_lang_math_pow : break; + case Interpreter::java_lang_math_exp : break; + case Interpreter::java_lang_ref_reference_get: entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; + default : ShouldNotReachHere(); break; + } + + if (entry_point) { + return entry_point; + } + return ((InterpreterGenerator*)this)->generate_normal_entry(); +} + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : CppInterpreterGenerator(code) { + generate_all(); // down here so it can be "virtual" +} + +// How much stack a topmost interpreter method activation needs in words. +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + // Computation is in bytes not words to match layout_activation_impl + // below, but the return is in words. + + // + // 0 [TOP_IJAVA_FRAME_ABI] \ + // alignment (optional) \ | + // [operand stack / Java parameters] > stack | | + // [monitors] (optional) > monitors | | + // [PARENT_IJAVA_FRAME_ABI] \ | | + // [BytecodeInterpreter object] > interpreter \ | | | + // alignment (optional) | round | parent | round | top + // [Java result] (2 slots) > result | | | | + // [Java non-arg locals] \ locals | | | | + // [arg locals] / / / / / + // + + int locals = method->max_locals() * BytesPerWord; + int interpreter = frame::interpreter_frame_cinterpreterstate_size_in_bytes(); + int result = 2 * BytesPerWord; + + int parent = round_to(interpreter + result + locals, 16) + frame::parent_ijava_frame_abi_size; + + int stack = method->max_stack() * BytesPerWord; + int monitors = method->is_synchronized() ? frame::interpreter_frame_monitor_size_in_bytes() : 0; + int top = round_to(parent + monitors + stack, 16) + frame::top_ijava_frame_abi_size; + + return (top / BytesPerWord); +} + +void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, + frame* caller, + frame* current, + methodOop method, + intptr_t* locals, + intptr_t* stack, + intptr_t* stack_base, + intptr_t* monitor_base, + intptr_t* frame_sp, + bool is_top_frame) { + // What about any vtable? + // + to_fill->_thread = JavaThread::current(); + // This gets filled in later but make it something recognizable for now. + to_fill->_bcp = method->code_base(); + to_fill->_locals = locals; + to_fill->_constants = method->constants()->cache(); + to_fill->_method = method; + to_fill->_mdx = NULL; + to_fill->_stack = stack; + + if (is_top_frame && JavaThread::current()->popframe_forcing_deopt_reexecution()) { + to_fill->_msg = deopt_resume2; + } else { + to_fill->_msg = method_resume; + } + to_fill->_result._to_call._bcp_advance = 0; + to_fill->_result._to_call._callee_entry_point = NULL; // doesn't matter to anyone + to_fill->_result._to_call._callee = NULL; // doesn't matter to anyone + to_fill->_prev_link = NULL; + + if (caller->is_interpreted_frame()) { + interpreterState prev = caller->get_interpreterState(); + + // Support MH calls. Make sure the interpreter will return the right address: + // 1. Caller did ordinary interpreted->compiled call call: Set a prev_state + // which makes the CPP interpreter return to frame manager "return_from_interpreted_method" + // entry after finishing execution. + // 2. Caller did a MH call: If the caller has a MethodHandleInvoke in it's + // state (invariant: must be the caller of the bottom vframe) we used the + // "call_special" entry to do the call, meaning the arguments have not been + // popped from the stack. Therefore, don't enter a prev state in this case + // in order to return to "return_from_native" frame manager entry which takes + // care of popping arguments. Also, don't overwrite the MH.invoke methodOop in + // the prev_state in order to be able to figure out the number of arguments to + // pop. + // The parameter method can represent MethodHandle.invokeExact(...). + // The MethodHandleCompiler generates these synthetic methodOops, + // including bytecodes, if an invokedynamic call gets inlined. In + // this case we want to return like from any other interpreted + // Java call, so we set _prev_link. + to_fill->_prev_link = prev; + + if (*prev->_bcp == Bytecodes::_invokeinterface || *prev->_bcp == Bytecodes::_invokedynamic) { + prev->_result._to_call._bcp_advance = 5; + } else { + prev->_result._to_call._bcp_advance = 3; + } + } + to_fill->_oop_temp = NULL; + to_fill->_stack_base = stack_base; + // Need +1 here because stack_base points to the word just above the + // first expr stack entry and stack_limit is supposed to point to + // the word just below the last expr stack entry. See + // generate_compute_interpreter_state. + to_fill->_stack_limit = stack_base - (method->max_stack() + 1); + to_fill->_monitor_base = (BasicObjectLock*) monitor_base; + + to_fill->_frame_bottom = frame_sp; + + // PPC64 specific + to_fill->_last_Java_pc = NULL; + to_fill->_last_Java_fp = NULL; + to_fill->_last_Java_sp = frame_sp; +#ifdef ASSERT + to_fill->_self_link = to_fill; + to_fill->_native_fresult = 123456.789; + to_fill->_native_lresult = CONST64(0xdeafcafedeadc0de); +#endif +} + +void BytecodeInterpreter::pd_layout_interpreterState(interpreterState istate, + address last_Java_pc, + intptr_t* last_Java_fp) { + istate->_last_Java_pc = last_Java_pc; + istate->_last_Java_fp = last_Java_fp; +} + +int AbstractInterpreter::layout_activation(methodOop method, + int temps, // Number of slots on java expression stack in use. + int popframe_args, + int monitors, // Number of active monitors. + int caller_actual_parameters, + int callee_params,// Number of slots for callee parameters. + int callee_locals,// Number of slots for locals. + frame* caller, + frame* interpreter_frame, + bool is_top_frame, + bool is_bottom_frame) { + + // NOTE this code must exactly mimic what + // InterpreterGenerator::generate_compute_interpreter_state() does + // as far as allocating an interpreter frame. However there is an + // exception. With the C++ based interpreter only the top most frame + // has a full sized expression stack. The 16 byte slop factor is + // both the abi scratch area and a place to hold a result from a + // callee on its way to the callers stack. + + int monitor_size = frame::interpreter_frame_monitor_size_in_bytes() * monitors; + int frame_size; + int top_frame_size = round_to(frame::interpreter_frame_cinterpreterstate_size_in_bytes() + + monitor_size + + (method->max_stack() *Interpreter::stackElementWords * BytesPerWord) + + 2*BytesPerWord, + frame::alignment_in_bytes) + + frame::top_ijava_frame_abi_size; + if (is_top_frame) { + frame_size = top_frame_size; + } else { + frame_size = round_to(frame::interpreter_frame_cinterpreterstate_size_in_bytes() + + monitor_size + + ((temps - callee_params + callee_locals) * + Interpreter::stackElementWords * BytesPerWord) + + 2*BytesPerWord, + frame::alignment_in_bytes) + + frame::parent_ijava_frame_abi_size; + assert(popframe_args==0, "non-zero for top_frame only"); + } + + // If we actually have a frame to layout we must now fill in all the pieces. + if (interpreter_frame != NULL) { + + intptr_t sp = (intptr_t)interpreter_frame->sp(); + intptr_t fp = *(intptr_t *)sp; + assert(fp == (intptr_t)caller->sp(), "fp must match"); + interpreterState cur_state = + (interpreterState)(fp - frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + + // Now fill in the interpreterState object. + + intptr_t* locals; + if (caller->is_interpreted_frame()) { + // Locals must agree with the caller because it will be used to set the + // caller's tos when we return. + interpreterState prev = caller->get_interpreterState(); + // Calculate start of "locals" for MH calls. For MH calls, the + // current method() (= MH target) and prev->callee() (= + // MH.invoke*()) are different and especially have different + // signatures. To pop the argumentsof the caller, we must use + // the prev->callee()->size_of_arguments() because that's what + // the caller actually pushed. Currently, for synthetic MH + // calls (deoptimized from inlined MH calls), detected by + // is_method_handle_invoke(), we use the callee's arguments + // because here, the caller's and callee's signature match. + if (true /*!caller->is_at_mh_callsite()*/) { + locals = prev->stack() + method->size_of_parameters(); + } else { + // Normal MH call. + locals = prev->stack() + prev->callee()->size_of_parameters(); + } + } else { + bool is_deopted; + locals = (intptr_t*) (fp + ((method->max_locals() - 1) * BytesPerWord) + + frame::parent_ijava_frame_abi_size); + } + + intptr_t* monitor_base = (intptr_t*) cur_state; + intptr_t* stack_base = (intptr_t*) ((intptr_t) monitor_base - monitor_size); + + // Provide pop_frame capability on PPC64, add popframe_args. + // +1 because stack is always prepushed. + intptr_t* stack = (intptr_t*) ((intptr_t) stack_base - (temps + popframe_args + 1) * BytesPerWord); + + BytecodeInterpreter::layout_interpreterState(cur_state, + caller, + interpreter_frame, + method, + locals, + stack, + stack_base, + monitor_base, + (intptr_t*)(((intptr_t)fp)-top_frame_size), + is_top_frame); + + BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, + interpreter_frame->fp()); + } + return frame_size/BytesPerWord; +} + +#endif // CC_INTERP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/cppInterpreter_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/cppInterpreter_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_CPPINTERPRETER_PPC_HPP +#define CPU_PPC_VM_CPPINTERPRETER_PPC_HPP + + protected: + + // Size of interpreter code. Increase if too small. Interpreter will + // fail with a guarantee ("not enough space for interpreter generation"); + // if too small. + // Run with +PrintInterpreter to get the VM to print out the size. + // Max size with JVMTI + + const static int InterpreterCodeSize = 12*K; + +#endif // CPU_PPC_VM_CPPINTERPRETER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/debug_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/debug_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "code/codeCache.hpp" +#include "code/nmethod.hpp" +#include "runtime/frame.hpp" +#include "runtime/init.hpp" +#include "runtime/os.hpp" +#include "utilities/debug.hpp" +#include "utilities/top.hpp" + +void pd_ps(frame f) {} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/depChecker_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/depChecker_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_DEPCHECKER_PPC_HPP +#define CPU_PPC_VM_DEPCHECKER_PPC_HPP + +// Nothing to do on ppc64 + +#endif // CPU_PPC_VM_DEPCHECKER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/disassembler_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/disassembler_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_DISASSEMBLER_PPC_HPP +#define CPU_PPC_VM_DISASSEMBLER_PPC_HPP + + static int pd_instruction_alignment() { + return sizeof(int); + } + + static const char* pd_cpu_opts() { + return "ppc64"; + } + +#endif // CPU_PPC_VM_DISASSEMBLER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/dump_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/dump_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "assembler_ppc.inline.hpp" +#include "memory/compactingPermGenGen.hpp" +#include "memory/generation.inline.hpp" +#include "memory/space.inline.hpp" + +// Generate the self-patching vtable method: +// +// This method will be called (as any other Klass virtual method) with +// the Klass itself as the first argument. Example: +// +// oop obj; +// int size = obj->klass()->klass_part()->oop_size(this); +// +// for which the virtual method call is Klass::oop_size(); +// +// The dummy method is called with the Klass object as the first +// operand, and an object as the second argument. +// + +//===================================================================== + +// All of the dummy methods in the vtable are essentially identical, +// differing only by an ordinal constant, and they bear no releationship +// to the original method which the caller intended. Also, there needs +// to be 'vtbl_list_size' instances of the vtable in order to +// differentiate between the 'vtable_list_size' original Klass objects. + +void CompactingPermGenGen::generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, + char* md_end, + char** mc_top, + char* mc_end) { + Unimplemented(); +} + diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/frame_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/frame_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "interpreter/interpreter.hpp" +#include "memory/resourceArea.hpp" +#include "oops/markOop.hpp" +#include "oops/methodOop.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/monitorChunk.hpp" +#include "runtime/signature.hpp" +#include "runtime/stubCodeGenerator.hpp" +#include "runtime/stubRoutines.hpp" +#include "vmreg_ppc.inline.hpp" +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#include "runtime/vframeArray.hpp" +#endif + +#ifdef ASSERT +void RegisterMap::check_location_valid() { +} +#endif // ASSERT + +bool frame::safe_for_sender(JavaThread *thread) { + bool safe = false; + address cursp = (address)sp(); + address curfp = (address)fp(); + if ((cursp != NULL && curfp != NULL && + (cursp <= thread->stack_base() && cursp >= thread->stack_base() - thread->stack_size())) && + (curfp <= thread->stack_base() && curfp >= thread->stack_base() - thread->stack_size())) { + safe = true; + } + return safe; +} + +bool frame::is_interpreted_frame() const { + return Interpreter::contains(pc()); +} + +frame frame::sender_for_entry_frame(RegisterMap *map) const { + assert(map != NULL, "map must be set"); + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender. + JavaFrameAnchor* jfa = entry_frame_call_wrapper()->anchor(); + assert(!entry_frame_is_first(), "next Java fp must be non zero"); + assert(jfa->last_Java_sp() > _sp, "must be above this frame on stack"); + map->clear(); + assert(map->include_argument_oops(), "should be set by clear"); + + if (jfa->last_Java_pc() != NULL) { + frame fr(jfa->last_Java_sp(), jfa->last_Java_pc()); + return fr; + } + // Last_java_pc is not set, if we come here from compiled code. The + // constructor retrieves the PC from the stack. + frame fr(jfa->last_Java_sp()); + return fr; +} + +frame frame::sender_for_interpreter_frame(RegisterMap *map) const { + // Pass callers initial_caller_sp as unextended_sp. + return frame(sender_sp(), sender_pc(), + CC_INTERP_ONLY((intptr_t*)((parent_ijava_frame_abi *)callers_abi())->initial_caller_sp) + NOT_CC_INTERP((intptr_t*)get_ijava_state()->sender_sp) + ); +} + +frame frame::sender_for_compiled_frame(RegisterMap *map) const { + assert(map != NULL, "map must be set"); + + // Frame owned by compiler. + address pc = *compiled_sender_pc_addr(_cb); + frame caller(compiled_sender_sp(_cb), pc); + + // Now adjust the map. + + // Get the rest. + if (map->update_map()) { + // Tell GC to use argument oopmaps for some runtime stubs that need it. + map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); + if (_cb->oop_maps() != NULL) { + OopMapSet::update_register_map(this, map); + } + } + + return caller; +} + +intptr_t* frame::compiled_sender_sp(CodeBlob* cb) const { + return sender_sp(); +} + +address* frame::compiled_sender_pc_addr(CodeBlob* cb) const { + return sender_pc_addr(); +} + +frame frame::sender(RegisterMap* map) const { + // Default is we do have to follow them. The sender_for_xxx will + // update it accordingly. + map->set_include_argument_oops(false); + + if (is_entry_frame()) return sender_for_entry_frame(map); + if (is_interpreted_frame()) return sender_for_interpreter_frame(map); + assert(_cb == CodeCache::find_blob(pc()),"Must be the same"); + + if (_cb != NULL) { + return sender_for_compiled_frame(map); + } + // Must be native-compiled frame, i.e. the marshaling code for native + // methods that exists in the core system. + return frame(sender_sp(), sender_pc()); +} + +void frame::patch_pc(Thread* thread, address pc) { + if (TracePcPatching) { + tty->print_cr("patch_pc at address " PTR_FORMAT " [" PTR_FORMAT " -> " PTR_FORMAT "]", + &((address*) _sp)[-1], ((address*) _sp)[-1], pc); + } + own_abi()->lr = (uint64_t)pc; + _cb = CodeCache::find_blob(pc); + if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { + address orig = (((nmethod*)_cb)->get_original_pc(this)); + assert(orig == _pc, "expected original to be stored before patching"); + _deopt_state = is_deoptimized; + // Leave _pc as is. + } else { + _deopt_state = not_deoptimized; + _pc = pc; + } +} + +void frame::pd_gc_epilog() { + if (is_interpreted_frame()) { + // Set constant pool cache entry for interpreter. + methodOop m = interpreter_frame_method(); + + *interpreter_frame_cpoolcache_addr() = m->constants()->cache(); + } +} + +bool frame::is_interpreted_frame_valid(JavaThread* thread) const { + // Is there anything to do? + assert(is_interpreted_frame(), "Not an interpreted frame"); + return true; +} + +BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { + assert(is_interpreted_frame(), "interpreted frame expected"); + methodOop method = interpreter_frame_method(); + BasicType type = method->result_type(); + + if (method->is_native()) { + // Prior to calling into the runtime to notify the method exit the possible + // result value is saved into the interpreter frame. +#ifdef CC_INTERP + interpreterState istate = get_interpreterState(); + address lresult = (address)istate + in_bytes(BytecodeInterpreter::native_lresult_offset()); + address fresult = (address)istate + in_bytes(BytecodeInterpreter::native_fresult_offset()); +#else + address lresult = (address)&(get_ijava_state()->lresult); + address fresult = (address)&(get_ijava_state()->fresult); +#endif + + switch (method->result_type()) { + case T_OBJECT: + case T_ARRAY: { + oop* obj_p = *(oop**)lresult; + oop obj = (obj_p == NULL) ? NULL : *obj_p; + assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); + *oop_result = obj; + break; + } + // We use std/stfd to store the values. + case T_BOOLEAN : value_result->z = (jboolean) *(unsigned long*)lresult; break; + case T_INT : value_result->i = (jint) *(long*)lresult; break; + case T_CHAR : value_result->c = (jchar) *(unsigned long*)lresult; break; + case T_SHORT : value_result->s = (jshort) *(long*)lresult; break; + case T_BYTE : value_result->z = (jbyte) *(long*)lresult; break; + case T_LONG : value_result->j = (jlong) *(long*)lresult; break; + case T_FLOAT : value_result->f = (jfloat) *(double*)fresult; break; + case T_DOUBLE : value_result->d = (jdouble) *(double*)fresult; break; + case T_VOID : /* Nothing to do */ break; + default : ShouldNotReachHere(); + } + } else { + intptr_t* tos_addr = interpreter_frame_tos_address(); + switch (method->result_type()) { + case T_OBJECT: + case T_ARRAY: { + oop obj = *(oop*)tos_addr; + assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); + *oop_result = obj; + } + case T_BOOLEAN : value_result->z = (jboolean) *(jint*)tos_addr; break; + case T_BYTE : value_result->b = (jbyte) *(jint*)tos_addr; break; + case T_CHAR : value_result->c = (jchar) *(jint*)tos_addr; break; + case T_SHORT : value_result->s = (jshort) *(jint*)tos_addr; break; + case T_INT : value_result->i = *(jint*)tos_addr; break; + case T_LONG : value_result->j = *(jlong*)tos_addr; break; + case T_FLOAT : value_result->f = *(jfloat*)tos_addr; break; + case T_DOUBLE : value_result->d = *(jdouble*)tos_addr; break; + case T_VOID : /* Nothing to do */ break; + default : ShouldNotReachHere(); + } + } + return type; +} + +#ifndef PRODUCT + +void frame::describe_pd(FrameValues& values, int frame_no) { + if (is_interpreted_frame()) { +#ifdef CC_INTERP + interpreterState istate = get_interpreterState(); + values.describe(frame_no, (intptr_t*)istate, "istate"); + values.describe(frame_no, (intptr_t*)&(istate->_thread), " thread"); + values.describe(frame_no, (intptr_t*)&(istate->_bcp), " bcp"); + values.describe(frame_no, (intptr_t*)&(istate->_locals), " locals"); + values.describe(frame_no, (intptr_t*)&(istate->_constants), " constants"); + values.describe(frame_no, (intptr_t*)&(istate->_method), err_msg(" method = %s", istate->_method->name_and_sig_as_C_string())); + values.describe(frame_no, (intptr_t*)&(istate->_mdx), " mdx"); + values.describe(frame_no, (intptr_t*)&(istate->_stack), " stack"); + values.describe(frame_no, (intptr_t*)&(istate->_msg), err_msg(" msg = %s", BytecodeInterpreter::C_msg(istate->_msg))); + values.describe(frame_no, (intptr_t*)&(istate->_result), " result"); + values.describe(frame_no, (intptr_t*)&(istate->_prev_link), " prev_link"); + values.describe(frame_no, (intptr_t*)&(istate->_oop_temp), " oop_temp"); + values.describe(frame_no, (intptr_t*)&(istate->_stack_base), " stack_base"); + values.describe(frame_no, (intptr_t*)&(istate->_stack_limit), " stack_limit"); + values.describe(frame_no, (intptr_t*)&(istate->_monitor_base), " monitor_base"); + values.describe(frame_no, (intptr_t*)&(istate->_frame_bottom), " frame_bottom"); + values.describe(frame_no, (intptr_t*)&(istate->_last_Java_pc), " last_Java_pc"); + values.describe(frame_no, (intptr_t*)&(istate->_last_Java_fp), " last_Java_fp"); + values.describe(frame_no, (intptr_t*)&(istate->_last_Java_sp), " last_Java_sp"); + values.describe(frame_no, (intptr_t*)&(istate->_self_link), " self_link"); + values.describe(frame_no, (intptr_t*)&(istate->_native_fresult), " native_fresult"); + values.describe(frame_no, (intptr_t*)&(istate->_native_lresult), " native_lresult"); +#else +#define DESCRIBE_ADDRESS(name) \ + values.describe(frame_no, (intptr_t*)&(get_ijava_state()->name), #name); + + DESCRIBE_ADDRESS(method); + DESCRIBE_ADDRESS(locals); + DESCRIBE_ADDRESS(monitors); + DESCRIBE_ADDRESS(cpoolCache); + DESCRIBE_ADDRESS(bcp); + DESCRIBE_ADDRESS(esp); + DESCRIBE_ADDRESS(mdx); + DESCRIBE_ADDRESS(top_frame_sp); + DESCRIBE_ADDRESS(sender_sp); + DESCRIBE_ADDRESS(oop_tmp); + DESCRIBE_ADDRESS(lresult); + DESCRIBE_ADDRESS(fresult); +#endif + } +} +#endif + +void frame::adjust_unextended_sp() { + // If we are returning to a compiled MethodHandle call site, the + // saved_fp will in fact be a saved value of the unextended SP. The + // simplest way to tell whether we are returning to such a call site + // is as follows: + + if (is_compiled_frame() && false /*is_at_mh_callsite()*/) { // TODO PPC port + // If the sender PC is a deoptimization point, get the original + // PC. For MethodHandle call site the unextended_sp is stored in + // saved_fp. + _unextended_sp = _fp - _cb->frame_size(); + +#ifdef ASSERT + nmethod *sender_nm = _cb->as_nmethod_or_null(); + assert(sender_nm && *_sp == *_unextended_sp, "backlink changed"); + + intptr_t* sp = _unextended_sp; // check if stack can be walked from here + for (int x = 0; x < 5; ++x) { // check up to a couple of backlinks + intptr_t* prev_sp = *(intptr_t**)sp; + if (prev_sp == 0) break; // end of stack + assert(prev_sp>sp, "broken stack"); + sp = prev_sp; + } + + if (sender_nm->is_deopt_mh_entry(_pc)) { // checks for deoptimization + address original_pc = sender_nm->get_original_pc(this); + assert(sender_nm->insts_contains(original_pc), "original PC must be in nmethod"); + assert(sender_nm->is_method_handle_return(original_pc), "must be"); + } +#endif + } +} + +intptr_t *frame::initial_deoptimization_info() { + // unused... but returns fp() to minimize changes introduced by 7087445 + return fp(); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/frame_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/frame_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_FRAME_PPC_HPP +#define CPU_PPC_VM_FRAME_PPC_HPP + +#include "runtime/synchronizer.hpp" +#include "utilities/top.hpp" + + // C frame layout on PPC-64. + // + // In this figure the stack grows upwards, while memory grows + // downwards. See "64-bit PowerPC ELF ABI Supplement Version 1.7", + // IBM Corp. (2003-10-29) + // (http://math-atlas.sourceforge.net/devel/assembly/PPC-elf64abi-1.7.pdf). + // + // Square brackets denote stack regions possibly larger + // than a single 64 bit slot. + // + // STACK: + // 0 [C_FRAME] <-- SP after prolog (mod 16 = 0) + // [C_FRAME] <-- SP before prolog + // ... + // [C_FRAME] + // + // C_FRAME: + // 0 [ABI_REG_ARGS] + // 112 CARG_9: outgoing arg 9 (arg_1 ... arg_8 via gpr_3 ... gpr_{10}) + // ... + // 40+M*8 CARG_M: outgoing arg M (M is the maximum of outgoing args taken over all call sites in the procedure) + // local 1 + // ... + // local N + // spill slot for vector reg (16 bytes aligned) + // ... + // spill slot for vector reg + // alignment (4 or 12 bytes) + // V SR_VRSAVE + // V+4 spill slot for GR + // ... ... + // spill slot for GR + // spill slot for FR + // ... + // spill slot for FR + // + // ABI_48: + // 0 caller's SP + // 8 space for condition register (CR) for next call + // 16 space for link register (LR) for next call + // 24 reserved + // 32 reserved + // 40 space for TOC (=R2) register for next call + // + // ABI_REG_ARGS: + // 0 [ABI_48] + // 48 CARG_1: spill slot for outgoing arg 1. used by next callee. + // ... ... + // 104 CARG_8: spill slot for outgoing arg 8. used by next callee. + // + + public: + + // C frame layout + + enum { + // stack alignment + alignment_in_bytes = 16, + // log_2(16*8 bits) = 7. + log_2_of_alignment_in_bits = 7 + }; + + // ABI_MINFRAME: + struct abi_minframe { + uint64_t callers_sp; + uint64_t cr; //_16 + uint64_t lr; +#if !defined(ABI_ELFv2) + uint64_t reserved1; //_16 + uint64_t reserved2; +#endif + uint64_t toc; //_16 + // nothing to add here! + // aligned to frame::alignment_in_bytes (16) + }; + + enum { + abi_minframe_size = sizeof(abi_minframe) + }; + + struct abi_reg_args : abi_minframe { + uint64_t carg_1; + uint64_t carg_2; //_16 + uint64_t carg_3; + uint64_t carg_4; //_16 + uint64_t carg_5; + uint64_t carg_6; //_16 + uint64_t carg_7; + uint64_t carg_8; //_16 + // aligned to frame::alignment_in_bytes (16) + }; + + enum { + abi_reg_args_size = sizeof(abi_reg_args) + }; + + #define _abi(_component) \ + (offset_of(frame::abi_reg_args, _component)) + + struct abi_reg_args_spill : abi_reg_args { + // additional spill slots + uint64_t spill_ret; + uint64_t spill_fret; //_16 + // aligned to frame::alignment_in_bytes (16) + }; + + enum { + abi_reg_args_spill_size = sizeof(abi_reg_args_spill) + }; + + #define _abi_reg_args_spill(_component) \ + (offset_of(frame::abi_reg_args_spill, _component)) + + // non-volatile GPRs: + + struct spill_nonvolatiles { + uint64_t r14; + uint64_t r15; //_16 + uint64_t r16; + uint64_t r17; //_16 + uint64_t r18; + uint64_t r19; //_16 + uint64_t r20; + uint64_t r21; //_16 + uint64_t r22; + uint64_t r23; //_16 + uint64_t r24; + uint64_t r25; //_16 + uint64_t r26; + uint64_t r27; //_16 + uint64_t r28; + uint64_t r29; //_16 + uint64_t r30; + uint64_t r31; //_16 + + double f14; + double f15; + double f16; + double f17; + double f18; + double f19; + double f20; + double f21; + double f22; + double f23; + double f24; + double f25; + double f26; + double f27; + double f28; + double f29; + double f30; + double f31; + + // aligned to frame::alignment_in_bytes (16) + }; + + enum { + spill_nonvolatiles_size = sizeof(spill_nonvolatiles) + }; + + #define _spill_nonvolatiles_neg(_component) \ + (int)(-frame::spill_nonvolatiles_size + offset_of(frame::spill_nonvolatiles, _component)) + + + +#ifndef CC_INTERP + // Frame layout for the Java template interpreter on PPC64. + // + // Diffs to the CC_INTERP are marked with 'X'. + // + // TOP_IJAVA_FRAME: + // + // 0 [TOP_IJAVA_FRAME_ABI] + // alignment (optional) + // [operand stack] + // [monitors] (optional) + // X[IJAVA_STATE] + // note: own locals are located in the caller frame. + // + // PARENT_IJAVA_FRAME: + // + // 0 [PARENT_IJAVA_FRAME_ABI] + // alignment (optional) + // [callee's Java result] + // [callee's locals w/o arguments] + // [outgoing arguments] + // [used part of operand stack w/o arguments] + // [monitors] (optional) + // X[IJAVA_STATE] + // + + struct parent_ijava_frame_abi : abi_minframe { + }; + + enum { + parent_ijava_frame_abi_size = sizeof(parent_ijava_frame_abi) + }; + +#define _parent_ijava_frame_abi(_component) \ + (offset_of(frame::parent_ijava_frame_abi, _component)) + + struct top_ijava_frame_abi : abi_reg_args { + }; + + enum { + top_ijava_frame_abi_size = sizeof(top_ijava_frame_abi) + }; + +#define _top_ijava_frame_abi(_component) \ + (offset_of(frame::top_ijava_frame_abi, _component)) + + struct ijava_state { +#ifdef ASSERT + uint64_t ijava_reserved; // Used for assertion. + uint64_t ijava_reserved2; // Inserted for alignment. +#endif + uint64_t method; + uint64_t locals; + uint64_t monitors; + uint64_t cpoolCache; + uint64_t bcp; + uint64_t esp; + uint64_t mdx; + uint64_t top_frame_sp; // Maybe define parent_frame_abi and move there. + uint64_t sender_sp; + // Slots only needed for native calls. Maybe better to move elsewhere. + uint64_t oop_tmp; + uint64_t lresult; + uint64_t fresult; + // Aligned to frame::alignment_in_bytes (16). + }; + + enum { + ijava_state_size = sizeof(ijava_state) + }; + +#define _ijava_state_neg(_component) \ + (int) (-frame::ijava_state_size + offset_of(frame::ijava_state, _component)) + +#else // CC_INTERP: + + // Frame layout for the Java C++ interpreter on PPC64. + // + // This frame layout provides a C-like frame for every Java frame. + // + // In these figures the stack grows upwards, while memory grows + // downwards. Square brackets denote regions possibly larger than + // single 64 bit slots. + // + // STACK (no JNI, no compiled code, no library calls, + // interpreter-loop is active): + // 0 [InterpretMethod] + // [TOP_IJAVA_FRAME] + // [PARENT_IJAVA_FRAME] + // ... + // [PARENT_IJAVA_FRAME] + // [ENTRY_FRAME] + // [C_FRAME] + // ... + // [C_FRAME] + // + // TOP_IJAVA_FRAME: + // 0 [TOP_IJAVA_FRAME_ABI] + // alignment (optional) + // [operand stack] + // [monitors] (optional) + // [cInterpreter object] + // result, locals, and arguments are in parent frame! + // + // PARENT_IJAVA_FRAME: + // 0 [PARENT_IJAVA_FRAME_ABI] + // alignment (optional) + // [callee's Java result] + // [callee's locals w/o arguments] + // [outgoing arguments] + // [used part of operand stack w/o arguments] + // [monitors] (optional) + // [cInterpreter object] + // + // ENTRY_FRAME: + // 0 [PARENT_IJAVA_FRAME_ABI] + // alignment (optional) + // [callee's Java result] + // [callee's locals w/o arguments] + // [outgoing arguments] + // [ENTRY_FRAME_LOCALS] + // + // PARENT_IJAVA_FRAME_ABI: + // 0 [ABI_MINFRAME] + // top_frame_sp + // initial_caller_sp + // + // TOP_IJAVA_FRAME_ABI: + // 0 [PARENT_IJAVA_FRAME_ABI] + // carg_3_unused + // carg_4_unused + // carg_5_unused + // carg_6_unused + // carg_7_unused + // frame_manager_lr + // + + // PARENT_IJAVA_FRAME_ABI + + struct parent_ijava_frame_abi : abi_minframe { + // SOE registers. + // C2i adapters spill their top-frame stack-pointer here. + uint64_t top_frame_sp; // carg_1 + // Sp of calling compiled frame before it was resized by the c2i + // adapter or sp of call stub. Does not contain a valid value for + // non-initial frames. + uint64_t initial_caller_sp; // carg_2 + // aligned to frame::alignment_in_bytes (16) + }; + + enum { + parent_ijava_frame_abi_size = sizeof(parent_ijava_frame_abi) + }; + + #define _parent_ijava_frame_abi(_component) \ + (offset_of(frame::parent_ijava_frame_abi, _component)) + + // TOP_IJAVA_FRAME_ABI + + struct top_ijava_frame_abi : parent_ijava_frame_abi { + uint64_t carg_3_unused; // carg_3 + uint64_t card_4_unused; //_16 carg_4 + uint64_t carg_5_unused; // carg_5 + uint64_t carg_6_unused; //_16 carg_6 + uint64_t carg_7_unused; // carg_7 + // Use arg8 for storing frame_manager_lr. The size of + // top_ijava_frame_abi must match abi_reg_args. + uint64_t frame_manager_lr; //_16 carg_8 + // nothing to add here! + // aligned to frame::alignment_in_bytes (16) + }; + + enum { + top_ijava_frame_abi_size = sizeof(top_ijava_frame_abi) + }; + + #define _top_ijava_frame_abi(_component) \ + (offset_of(frame::top_ijava_frame_abi, _component)) + +#endif // CC_INTERP + + // ENTRY_FRAME + + struct entry_frame_locals { + uint64_t call_wrapper_address; + uint64_t result_address; //_16 + uint64_t result_type; + uint64_t arguments_tos_address; //_16 + // aligned to frame::alignment_in_bytes (16) + uint64_t r[spill_nonvolatiles_size/sizeof(uint64_t)]; + }; + + enum { + entry_frame_locals_size = sizeof(entry_frame_locals) + }; + + #define _entry_frame_locals_neg(_component) \ + (int)(-frame::entry_frame_locals_size + offset_of(frame::entry_frame_locals, _component)) + + + // Frame layout for JIT generated methods + // + // In these figures the stack grows upwards, while memory grows + // downwards. Square brackets denote regions possibly larger than single + // 64 bit slots. + // + // STACK (interpreted Java calls JIT generated Java): + // [JIT_FRAME] <-- SP (mod 16 = 0) + // [TOP_IJAVA_FRAME] + // ... + // + // JIT_FRAME (is a C frame according to PPC-64 ABI): + // [out_preserve] + // [out_args] + // [spills] + // [pad_1] + // [monitor] (optional) + // ... + // [monitor] (optional) + // [pad_2] + // [in_preserve] added / removed by prolog / epilog + // + + // JIT_ABI (TOP and PARENT) + + struct jit_abi { + uint64_t callers_sp; + uint64_t cr; + uint64_t lr; + uint64_t toc; + // Nothing to add here! + // NOT ALIGNED to frame::alignment_in_bytes (16). + }; + + struct jit_out_preserve : jit_abi { + // Nothing to add here! + }; + + struct jit_in_preserve { + // Nothing to add here! + }; + + enum { + jit_out_preserve_size = sizeof(jit_out_preserve), + jit_in_preserve_size = sizeof(jit_in_preserve) + }; + + struct jit_monitor { + uint64_t monitor[1]; + }; + + enum { + jit_monitor_size = sizeof(jit_monitor), + }; + + private: + + // STACK: + // ... + // [THIS_FRAME] <-- this._sp (stack pointer for this frame) + // [CALLER_FRAME] <-- this.fp() (_sp of caller's frame) + // ... + // + + // frame pointer for this frame + intptr_t* _fp; + + // The frame's stack pointer before it has been extended by a c2i adapter; + // needed by deoptimization + intptr_t* _unextended_sp; + void adjust_unextended_sp(); + + public: + + // Accessors for fields + intptr_t* fp() const { return _fp; } + + // Accessors for ABIs + inline abi_minframe* own_abi() const { return (abi_minframe*) _sp; } + inline abi_minframe* callers_abi() const { return (abi_minframe*) _fp; } + + private: + + // Find codeblob and set deopt_state. + inline void find_codeblob_and_set_pc_and_deopt_state(address pc); + + public: + + // Constructors + inline frame(intptr_t* sp); + frame(intptr_t* sp, address pc); + inline frame(intptr_t* sp, address pc, intptr_t* unextended_sp); + + private: + + intptr_t* compiled_sender_sp(CodeBlob* cb) const; + address* compiled_sender_pc_addr(CodeBlob* cb) const; + address* sender_pc_addr(void) const; + + public: + +#ifdef CC_INTERP + // Additional interface for interpreter frames: + inline interpreterState get_interpreterState() const; +#else + inline ijava_state* get_ijava_state() const; + // Some convenient register frame setters/getters for deoptimization. + inline intptr_t* interpreter_frame_esp() const; + inline void interpreter_frame_set_cpcache(constantPoolCacheOop cp); + inline void interpreter_frame_set_esp(intptr_t* esp); + inline void interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp); + inline void interpreter_frame_set_sender_sp(intptr_t* sender_sp); +#endif // CC_INTERP + + // Size of a monitor in bytes. + static int interpreter_frame_monitor_size_in_bytes(); + + // The size of a cInterpreter object. + static inline int interpreter_frame_cinterpreterstate_size_in_bytes(); + + private: + + constantPoolCacheOop* interpreter_frame_cpoolcache_addr() const; + + public: + + // Additional interface for entry frames: + inline entry_frame_locals* get_entry_frame_locals() const { + return (entry_frame_locals*) (((address) fp()) - entry_frame_locals_size); + } + + enum { + // normal return address is 1 bundle past PC + pc_return_offset = 0 + }; + +#endif // CPU_PPC_VM_FRAME_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/frame_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/frame_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_FRAME_PPC_INLINE_HPP +#define CPU_PPC_VM_FRAME_PPC_INLINE_HPP + +#include "code/codeCache.hpp" + +// Inline functions for ppc64 frames: + +// Find codeblob and set deopt_state. +inline void frame::find_codeblob_and_set_pc_and_deopt_state(address pc) { + assert(pc != NULL, "precondition: must have PC"); + + _cb = CodeCache::find_blob(pc); + _pc = pc; // Must be set for get_deopt_original_pc() + + _fp = (intptr_t*)own_abi()->callers_sp; + // Use _fp - frame_size, needs to be done between _cb and _pc initialization + // and get_deopt_original_pc. + adjust_unextended_sp(); + + address original_pc = nmethod::get_deopt_original_pc(this); + if (original_pc != NULL) { + _pc = original_pc; + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } + + assert(((uint64_t)_sp & 0xf) == 0, "SP must be 16-byte aligned"); +} + +// Constructors + +// Initialize all fields, _unextended_sp will be adjusted in find_codeblob_and_set_pc_and_deopt_state. +inline frame::frame() : _sp(NULL), _unextended_sp(NULL), _fp(NULL), _cb(NULL), _pc(NULL), _deopt_state(unknown) {} + +inline frame::frame(intptr_t* sp) : _sp(sp), _unextended_sp(sp) { + find_codeblob_and_set_pc_and_deopt_state((address)own_abi()->lr); // also sets _fp and adjusts _unextended_sp +} + +inline frame::frame(intptr_t* sp, address pc) : _sp(sp), _unextended_sp(sp) { + find_codeblob_and_set_pc_and_deopt_state(pc); // also sets _fp and adjusts _unextended_sp +} + +inline frame::frame(intptr_t* sp, address pc, intptr_t* unextended_sp) : _sp(sp), _unextended_sp(unextended_sp) { + find_codeblob_and_set_pc_and_deopt_state(pc); // also sets _fp and adjusts _unextended_sp +} + +// Accessors + +// Return unique id for this frame. The id must have a value where we +// can distinguish identity and younger/older relationship. NULL +// represents an invalid (incomparable) frame. +inline intptr_t* frame::id(void) const { + // Use _fp. _sp or _unextended_sp wouldn't be correct due to resizing. + return _fp; +} + +// Return true if this frame is older (less recent activation) than +// the frame represented by id. +inline bool frame::is_older(intptr_t* id) const { + assert(this->id() != NULL && id != NULL, "NULL frame id"); + // Stack grows towards smaller addresses on ppc64. + return this->id() > id; +} + +inline int frame::frame_size(RegisterMap* map) const { + // Stack grows towards smaller addresses on PPC64: sender is at a higher address. + return sender_sp() - sp(); +} + +// Return the frame's stack pointer before it has been extended by a +// c2i adapter. This is needed by deoptimization for ignoring c2i adapter +// frames. +inline intptr_t* frame::unextended_sp() const { + return _unextended_sp; +} + +// All frames have this field. +inline address frame::sender_pc() const { + return (address)callers_abi()->lr; +} +inline address* frame::sender_pc_addr() const { + return (address*)&(callers_abi()->lr); +} + +// All frames have this field. +inline intptr_t* frame::sender_sp() const { + return (intptr_t*)callers_abi(); +} + +// All frames have this field. +inline intptr_t* frame::link() const { + return (intptr_t*)callers_abi()->callers_sp; +} + +inline intptr_t* frame::real_fp() const { + return fp(); +} + +#ifdef CC_INTERP + +inline interpreterState frame::get_interpreterState() const { + return (interpreterState)(((address)callers_abi()) + - frame::interpreter_frame_cinterpreterstate_size_in_bytes()); +} + +inline intptr_t** frame::interpreter_frame_locals_addr() const { + interpreterState istate = get_interpreterState(); + return (intptr_t**)&istate->_locals; +} + +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + interpreterState istate = get_interpreterState(); + return (intptr_t*)&istate->_bcp; +} + +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + interpreterState istate = get_interpreterState(); + return (intptr_t*)&istate->_mdx; +} + +inline intptr_t* frame::interpreter_frame_expression_stack() const { + return (intptr_t*)interpreter_frame_monitor_end() - 1; +} + +inline jint frame::interpreter_frame_expression_stack_direction() { + return -1; +} + +// top of expression stack +inline intptr_t* frame::interpreter_frame_tos_address() const { + interpreterState istate = get_interpreterState(); + return istate->_stack + 1; +} + +inline intptr_t* frame::interpreter_frame_tos_at(jint offset) const { + return &interpreter_frame_tos_address()[offset]; +} + +// monitor elements + +// in keeping with Intel side: end is lower in memory than begin; +// and beginning element is oldest element +// Also begin is one past last monitor. + +inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + return get_interpreterState()->monitor_base(); +} + +inline BasicObjectLock* frame::interpreter_frame_monitor_end() const { + return (BasicObjectLock*)get_interpreterState()->stack_base(); +} + +inline int frame::interpreter_frame_cinterpreterstate_size_in_bytes() { + // Size of an interpreter object. Not aligned with frame size. + return round_to(sizeof(BytecodeInterpreter), 8); +} + +inline methodOop* frame::interpreter_frame_method_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_method; +} + +// Constant pool cache + +inline constantPoolCacheOop* frame::interpreter_frame_cpoolcache_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_constants; // should really use accessor +} + +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_constants; +} + +#else // !CC_INTERP + +// Template Interpreter frame value accessors. + +inline frame::ijava_state* frame::get_ijava_state() const { + return (ijava_state*) ((uintptr_t)fp() - ijava_state_size); +} + +inline intptr_t** frame::interpreter_frame_locals_addr() const { + return (intptr_t**) &(get_ijava_state()->locals); +} +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + return (intptr_t*) &(get_ijava_state()->bcp); +} +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + return (intptr_t*) &(get_ijava_state()->mdx); +} +// Pointer beyond the "oldest/deepest" BasicObjectLock on stack. +inline BasicObjectLock* frame::interpreter_frame_monitor_end() const { + return (BasicObjectLock *) get_ijava_state()->monitors; +} + +inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + return (BasicObjectLock *) get_ijava_state(); +} + +// Return register stack slot addr at which currently interpreted methodOop is found. +inline methodOop* frame::interpreter_frame_method_addr() const { + return (methodOop*) &(get_ijava_state()->method); +} +inline constantPoolCacheOop* frame::interpreter_frame_cpoolcache_addr() const { + return (constantPoolCacheOop*) &(get_ijava_state()->cpoolCache); +} +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + return (constantPoolCacheOop*) &(get_ijava_state()->cpoolCache); +} + +inline oop* frame::interpreter_frame_temp_oop_addr() const { + return (oop *) &(get_ijava_state()->oop_tmp); +} +inline intptr_t* frame::interpreter_frame_esp() const { + return (intptr_t*) get_ijava_state()->esp; +} + +// Convenient setters +inline void frame::interpreter_frame_set_monitor_end(BasicObjectLock* end) { get_ijava_state()->monitors = (intptr_t) end;} +inline void frame::interpreter_frame_set_cpcache(constantPoolCacheOop cp) { *frame::interpreter_frame_cpoolcache_addr() = cp; } +inline void frame::interpreter_frame_set_esp(intptr_t* esp) { get_ijava_state()->esp = (intptr_t) esp; } +inline void frame::interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp) { get_ijava_state()->top_frame_sp = (intptr_t) top_frame_sp; } +inline void frame::interpreter_frame_set_sender_sp(intptr_t* sender_sp) { get_ijava_state()->sender_sp = (intptr_t) sender_sp; } + +inline intptr_t* frame::interpreter_frame_expression_stack() const { + return (intptr_t*)interpreter_frame_monitor_end() - 1; +} + +inline jint frame::interpreter_frame_expression_stack_direction() { + return -1; +} + +// top of expression stack +inline intptr_t* frame::interpreter_frame_tos_address() const { + return ((intptr_t*) get_ijava_state()->esp) + Interpreter::stackElementWords; +} + +inline intptr_t* frame::interpreter_frame_tos_at(jint offset) const { + return &interpreter_frame_tos_address()[offset]; +} + +#endif // CC_INTERP + +inline int frame::interpreter_frame_monitor_size() { + // Number of stack slots for a monitor. + return round_to(BasicObjectLock::size(), // number of stack slots + WordsPerLong); // number of stack slots for a Java long +} + +inline int frame::interpreter_frame_monitor_size_in_bytes() { + return frame::interpreter_frame_monitor_size() * wordSize; +} + +// entry frames + +inline intptr_t* frame::entry_frame_argument_at(int offset) const { + // Since an entry frame always calls the interpreter first, the + // parameters are on the stack and relative to known register in the + // entry frame. + intptr_t* tos = (intptr_t*)get_entry_frame_locals()->arguments_tos_address; + return &tos[offset + 1]; // prepushed tos +} + +inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { + return (JavaCallWrapper**)&get_entry_frame_locals()->call_wrapper_address; +} + +inline oop frame::saved_oop_result(RegisterMap* map) const { + return *((oop*)map->location(R3->as_VMReg())); +} + +inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { + *((oop*)map->location(R3->as_VMReg())) = obj; +} + +#endif // CPU_PPC_VM_FRAME_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/globalDefinitions_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/globalDefinitions_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_GLOBALDEFINITIONS_PPC_HPP +#define CPU_PPC_VM_GLOBALDEFINITIONS_PPC_HPP + +// Size of PPC Instructions +const int BytesPerInstWord = 4; + +const int StackAlignmentInBytes = 16; + +// Indicates whether the C calling conventions require that +// 32-bit integer argument values are properly extended to 64 bits. +// If set, SharedRuntime::c_calling_convention() must adapt +// signatures accordingly. +const bool CCallingConventionRequiresIntsAsLongs = true; + +#endif // CPU_PPC_VM_GLOBALDEFINITIONS_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/globals_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/globals_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_GLOBALS_PPC_HPP +#define CPU_PPC_VM_GLOBALS_PPC_HPP + +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" + +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) + +define_pd_global(bool, ConvertSleepToYield, true); +define_pd_global(bool, ShareVtableStubs, false); // Improves performance markedly for mtrt and compress. +define_pd_global(bool, NeedsDeoptSuspend, false); // Only register window machines need this. + + +define_pd_global(bool, ImplicitNullChecks, true); // Generate code for implicit null checks. +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast. + +// Use large code-entry alignment. +define_pd_global(intx, CodeEntryAlignment, 128); +define_pd_global(intx, OptoLoopAlignment, 16); +define_pd_global(intx, InlineFrequencyCount, 100); +define_pd_global(intx, InlineSmallCode, 1500); + +define_pd_global(intx, PreInflateSpin, 10); + +// Flags for template interpreter. +define_pd_global(bool, RewriteBytecodes, true); +define_pd_global(bool, RewriteFrequentPairs, true); + +define_pd_global(bool, UseMembar, false); + +// GC Ergo Flags +define_pd_global(intx, CMSYoungGenPerWorker, 16*M); // Default max size of CMS young gen, per GC worker thread. + + +// Platform dependent flag handling: flags only defined on this platform. +#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ + \ + /* Load poll address from thread. This is used to implement per-thread */ \ + /* safepoints on platforms != IA64. */ \ + product(bool, LoadPollAddressFromThread, false, \ + "Load polling page address from thread object (required for " \ + "per-thread safepoints on platforms != IA64)") \ + \ + product(uintx, PowerArchitecturePPC64, 0, \ + "CPU Version: x for PowerX. Currently recognizes Power5 to " \ + "Power7. Default is 0. CPUs newer than Power7 will be " \ + "recognized as Power7.") \ + \ + /* Reoptimize code-sequences of calls at runtime, e.g. replace an */ \ + /* indirect call by a direct call. */ \ + product(bool, ReoptimizeCallSequences, true, \ + "Reoptimize code-sequences of calls at runtime.") \ + \ + product(bool, UseLoadInstructionsForStackBangingPPC64, false, \ + "Use load instructions for stack banging.") \ + \ + /* special instructions */ \ + \ + product(bool, UseCountLeadingZerosInstructionsPPC64, true, \ + "Use count leading zeros instructions.") \ + \ + product(bool, UseExtendedLoadAndReserveInstructionsPPC64, false, \ + "Use extended versions of load-and-reserve instructions.") \ + \ + product(bool, UseRotateAndMaskInstructionsPPC64, true, \ + "Use rotate and mask instructions.") \ + \ + product(bool, UseStaticBranchPredictionInCompareAndSwapPPC64, true, \ + "Use static branch prediction hints in CAS operations.") \ + product(bool, UseStaticBranchPredictionForUncommonPathsPPC64, false, \ + "Use static branch prediction hints for uncommon paths.") \ + \ + product(bool, UsePower6SchedulerPPC64, false, \ + "Use Power6 Scheduler.") \ + \ + product(bool, InsertEndGroupPPC64, false, \ + "Insert EndGroup instructions to optimize for Power6.") \ + \ + /* Trap based checks. */ \ + /* Trap based checks use the ppc trap instructions to check certain */ \ + /* conditions. This instruction raises a SIGTRAP caught by the */ \ + /* exception handler of the VM. */ \ + product(bool, UseSIGTRAP, true, \ + "Allow trap instructions that make use of SIGTRAP. Use this to " \ + "switch off all optimizations requiring SIGTRAP.") \ + product(bool, TrapBasedICMissChecks, true, \ + "Raise and handle SIGTRAP if inline cache miss detected.") \ + product(bool, TrapBasedNotEntrantChecks, true, \ + "Raise and handle SIGTRAP if calling not entrant or zombie" \ + " method.") \ + product(bool, TrapBasedNullChecks, true, \ + "Generate code for null checks that uses a cmp and trap " \ + "instruction raising SIGTRAP. This is only used if an access to" \ + "null (+offset) will not raise a SIGSEGV.") \ + product(bool, TrapBasedRangeChecks, true, \ + "Raise and handle SIGTRAP if array out of bounds check fails.") \ + product(bool, TraceTraps, false, "Trace all traps the signal handler" \ + "handles.") \ + \ + product(bool, ZapMemory, false, "Write 0x0101... to empty memory." \ + " Use this to ease debugging.") \ + + +#endif // CPU_PPC_VM_GLOBALS_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/icBuffer_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/icBuffer_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "assembler_ppc.inline.hpp" +#include "code/icBuffer.hpp" +#include "gc_interface/collectedHeap.inline.hpp" +#include "interpreter/bytecodes.hpp" +#include "memory/resourceArea.hpp" +#include "nativeInst_ppc.hpp" +#include "oops/oop.inline.hpp" +#include "oops/oop.inline2.hpp" + +#define __ masm. + +int InlineCacheBuffer::ic_stub_code_size() { + return MacroAssembler::load_const_size + MacroAssembler::b64_patchable_size; +} + +void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, oop cached_oop, address entry_point) { + ResourceMark rm; + CodeBuffer code(code_begin, ic_stub_code_size()); + MacroAssembler masm(&code); + // Note: even though the code contains an embedded oop, we do not need reloc info + // because + // (1) the oop is old (i.e., doesn't matter for scavenges) + // (2) these ICStubs are removed *before* a GC happens, so the roots disappear. + + // Load the oop ... + __ load_const(R19_method, (address) cached_oop, R0); + // ... and jump to entry point. + __ b64_patchable((address) entry_point, relocInfo::none); + + __ flush(); +} + +address InlineCacheBuffer::ic_buffer_entry_point(address code_begin) { + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object + NativeJump* jump = nativeJump_at(move->next_instruction_address()); + return jump->jump_destination(); +} + +oop InlineCacheBuffer::ic_buffer_cached_oop(address code_begin) { + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object + NativeJump* jump = nativeJump_at(move->next_instruction_address()); + return (oop)move->data(); +} + diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/icache_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/icache_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "assembler_ppc.inline.hpp" +#include "runtime/icache.hpp" + +// Use inline assembler to implement icache flush. +int ICache::ppc64_flush_icache(address start, int lines, int magic) { + address end = start + (unsigned int)lines*ICache::line_size; + assert(start <= end, "flush_icache parms"); + + // store modified cache lines from data cache + for (address a = start; a < end; a += ICache::line_size) { + __asm__ __volatile__( + "dcbst 0, %0 \n" + : + : "r" (a) + : "memory"); + } + + // sync instruction + __asm__ __volatile__( + "sync \n" + : + : + : "memory"); + + // invalidate respective cache lines in instruction cache + for (address a = start; a < end; a += ICache::line_size) { + __asm__ __volatile__( + "icbi 0, %0 \n" + : + : "r" (a) + : "memory"); + } + + // discard fetched instructions + __asm__ __volatile__( + "isync \n" + : + : + : "memory"); + + return magic; +} + +void ICacheStubGenerator::generate_icache_flush(ICache::flush_icache_stub_t* flush_icache_stub) { + StubCodeMark mark(this, "ICache", "flush_icache_stub"); + + *flush_icache_stub = (ICache::flush_icache_stub_t)ICache::ppc64_flush_icache; + + // First call to flush itself + ICache::invalidate_range((address)(*flush_icache_stub), 0); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/icache_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/icache_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_ICACHE_PPC_HPP +#define CPU_PPC_VM_ICACHE_PPC_HPP + +// Interface for updating the instruction cache. Whenever the VM modifies +// code, part of the processor instruction cache potentially has to be flushed. + +class ICache : public AbstractICache { + friend class ICacheStubGenerator; + static int ppc64_flush_icache(address start, int lines, int magic); + + public: + enum { + // Actually, cache line size is 64, but keeping it as it is to be + // on the safe side on ALL PPC64 implementations. + log2_line_size = 5, + line_size = 1 << log2_line_size + }; + + static void ppc64_flush_icache_bytes(address start, int bytes) { + // Align start address to an icache line boundary and transform + // nbytes to an icache line count. + const uint line_offset = mask_address_bits(start, line_size - 1); + ppc64_flush_icache(start - line_offset, (bytes + line_offset + line_size - 1) >> log2_line_size, 0); + } +}; + +#endif // CPU_PPC_VM_ICACHE_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interp_masm_ppc_64.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interp_masm_ppc_64.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,2298 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + + +#include "precompiled.hpp" +#include "assembler_ppc.hpp" +#include "interp_masm_ppc_64.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "prims/jvmtiThreadState.hpp" + + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) block_comment(str) +#endif + +void InterpreterMacroAssembler::null_check_throw(Register a, int offset, Register temp_reg) { +#ifdef CC_INTERP + address exception_entry = StubRoutines::throw_NullPointerException_at_call_entry(); +#else + address exception_entry = Interpreter::throw_NullPointerException_entry(); +#endif + MacroAssembler::null_check_throw(a, offset, temp_reg, exception_entry); +} + +void InterpreterMacroAssembler::branch_to_entry(address entry, Register Rscratch) { + assert(entry, "Entry must have been generated by now"); + if (is_within_range_of_b(entry, pc())) { + b(entry); + } else { + load_const_optimized(Rscratch, entry, R0); + mtctr(Rscratch); + bctr(); + } +} + +#ifndef CC_INTERP + +void InterpreterMacroAssembler::dispatch_next(TosState state, int bcp_incr) { + Register bytecode = R12_scratch2; + if (bcp_incr != 0) { + lbzu(bytecode, bcp_incr, R14_bcp); + } else { + lbz(bytecode, 0, R14_bcp); + } + + dispatch_Lbyte_code(state, bytecode, Interpreter::dispatch_table(state)); +} + +void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) { + // Load current bytecode. + Register bytecode = R12_scratch2; + lbz(bytecode, 0, R14_bcp); + dispatch_Lbyte_code(state, bytecode, table); +} + +// Dispatch code executed in the prolog of a bytecode which does not do it's +// own dispatch. The dispatch address is computed and placed in R24_dispatch_addr. +void InterpreterMacroAssembler::dispatch_prolog(TosState state, int bcp_incr) { + Register bytecode = R12_scratch2; + lbz(bytecode, bcp_incr, R14_bcp); + + load_dispatch_table(R24_dispatch_addr, Interpreter::dispatch_table(state)); + + sldi(bytecode, bytecode, LogBytesPerWord); + ldx(R24_dispatch_addr, R24_dispatch_addr, bytecode); +} + +// Dispatch code executed in the epilog of a bytecode which does not do it's +// own dispatch. The dispatch address in R24_dispatch_addr is used for the +// dispatch. +void InterpreterMacroAssembler::dispatch_epilog(TosState state, int bcp_incr) { + mtctr(R24_dispatch_addr); + addi(R14_bcp, R14_bcp, bcp_incr); + bctr(); +} + +void InterpreterMacroAssembler::check_and_handle_popframe(Register scratch_reg) { + assert(scratch_reg != R0, "can't use R0 as scratch_reg here"); + if (JvmtiExport::can_pop_frame()) { + Label L; + + // Check the "pending popframe condition" flag in the current thread. + lwz(scratch_reg, in_bytes(JavaThread::popframe_condition_offset()), R16_thread); + + // Initiate popframe handling only if it is not already being + // processed. If the flag has the popframe_processing bit set, it + // means that this code is called *during* popframe handling - we + // don't want to reenter. + andi_(R0, scratch_reg, JavaThread::popframe_pending_bit); + beq(CCR0, L); + + andi_(R0, scratch_reg, JavaThread::popframe_processing_bit); + bne(CCR0, L); + + // Call the Interpreter::remove_activation_preserving_args_entry() + // func to get the address of the same-named entrypoint in the + // generated interpreter code. +#if defined(ABI_ELFv2) + call_c(CAST_FROM_FN_PTR(address, + Interpreter::remove_activation_preserving_args_entry), + relocInfo::none); +#else + call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, + Interpreter::remove_activation_preserving_args_entry), + relocInfo::none); +#endif + + // Jump to Interpreter::_remove_activation_preserving_args_entry. + mtctr(R3_RET); + bctr(); + + align(32, 12); + bind(L); + } +} + +void InterpreterMacroAssembler::check_and_handle_earlyret(Register scratch_reg) { + const Register Rthr_state_addr = scratch_reg; + if (JvmtiExport::can_force_early_return()) { + Label Lno_early_ret; + ld(Rthr_state_addr, in_bytes(JavaThread::jvmti_thread_state_offset()), R16_thread); + cmpdi(CCR0, Rthr_state_addr, 0); + beq(CCR0, Lno_early_ret); + + lwz(R0, in_bytes(JvmtiThreadState::earlyret_state_offset()), Rthr_state_addr); + cmpwi(CCR0, R0, JvmtiThreadState::earlyret_pending); + bne(CCR0, Lno_early_ret); + + // Jump to Interpreter::_earlyret_entry. + lwz(R3_ARG1, in_bytes(JvmtiThreadState::earlyret_tos_offset()), Rthr_state_addr); + call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry)); + mtlr(R3_RET); + blr(); + + align(32, 12); + bind(Lno_early_ret); + } +} + +void InterpreterMacroAssembler::load_earlyret_value(TosState state, Register Rscratch1) { + const Register RjvmtiState = Rscratch1; + const Register Rscratch2 = R0; + + ld(RjvmtiState, in_bytes(JavaThread::jvmti_thread_state_offset()), R16_thread); + li(Rscratch2, 0); + + switch (state) { + case atos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_oop_offset()), RjvmtiState); + std(Rscratch2, in_bytes(JvmtiThreadState::earlyret_oop_offset()), RjvmtiState); + break; + case ltos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); + break; + case btos: // fall through + case ztos: // fall through + case ctos: // fall through + case stos: // fall through + case itos: lwz(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); + break; + case ftos: lfs(F15_ftos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); + break; + case dtos: lfd(F15_ftos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); + break; + case vtos: break; + default : ShouldNotReachHere(); + } + + // Clean up tos value in the jvmti thread state. + std(Rscratch2, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); + // Set tos state field to illegal value. + li(Rscratch2, ilgl); + stw(Rscratch2, in_bytes(JvmtiThreadState::earlyret_tos_offset()), RjvmtiState); +} + +// Common code to dispatch and dispatch_only. +// Dispatch value in Lbyte_code and increment Lbcp. + +void InterpreterMacroAssembler::load_dispatch_table(Register dst, address* table) { + address table_base = (address)Interpreter::dispatch_table((TosState)0); + intptr_t table_offs = (intptr_t)table - (intptr_t)table_base; + if (is_simm16(table_offs)) { + addi(dst, R25_templateTableBase, (int)table_offs); + } else { + load_const_optimized(dst, table, R0); + } +} + +void InterpreterMacroAssembler::dispatch_Lbyte_code(TosState state, Register bytecode, address* table, bool verify) { + if (verify) { + unimplemented("dispatch_Lbyte_code: verify"); // See Sparc Implementation to implement this + } + +#ifdef FAST_DISPATCH + unimplemented("dispatch_Lbyte_code FAST_DISPATCH"); +#else + assert_different_registers(bytecode, R11_scratch1); + + // Calc dispatch table address. + load_dispatch_table(R11_scratch1, table); + + sldi(R12_scratch2, bytecode, LogBytesPerWord); + ldx(R11_scratch1, R11_scratch1, R12_scratch2); + + // Jump off! + mtctr(R11_scratch1); + bctr(); +#endif +} + +void InterpreterMacroAssembler::load_receiver(Register Rparam_count, Register Rrecv_dst) { + sldi(Rrecv_dst, Rparam_count, Interpreter::logStackElementSize); + ldx(Rrecv_dst, Rrecv_dst, R15_esp); +} + +// helpers for expression stack + +void InterpreterMacroAssembler::pop_i(Register r) { + lwzu(r, Interpreter::stackElementSize, R15_esp); +} + +void InterpreterMacroAssembler::pop_ptr(Register r) { + ldu(r, Interpreter::stackElementSize, R15_esp); +} + +void InterpreterMacroAssembler::pop_l(Register r) { + ld(r, Interpreter::stackElementSize, R15_esp); + addi(R15_esp, R15_esp, 2 * Interpreter::stackElementSize); +} + +void InterpreterMacroAssembler::pop_f(FloatRegister f) { + lfsu(f, Interpreter::stackElementSize, R15_esp); +} + +void InterpreterMacroAssembler::pop_d(FloatRegister f) { + lfd(f, Interpreter::stackElementSize, R15_esp); + addi(R15_esp, R15_esp, 2 * Interpreter::stackElementSize); +} + +void InterpreterMacroAssembler::push_i(Register r) { + stw(r, 0, R15_esp); + addi(R15_esp, R15_esp, - Interpreter::stackElementSize ); +} + +void InterpreterMacroAssembler::push_ptr(Register r) { + std(r, 0, R15_esp); + addi(R15_esp, R15_esp, - Interpreter::stackElementSize ); +} + +void InterpreterMacroAssembler::push_l(Register r) { + std(r, - Interpreter::stackElementSize, R15_esp); + addi(R15_esp, R15_esp, - 2 * Interpreter::stackElementSize ); +} + +void InterpreterMacroAssembler::push_f(FloatRegister f) { + stfs(f, 0, R15_esp); + addi(R15_esp, R15_esp, - Interpreter::stackElementSize ); +} + +void InterpreterMacroAssembler::push_d(FloatRegister f) { + stfd(f, - Interpreter::stackElementSize, R15_esp); + addi(R15_esp, R15_esp, - 2 * Interpreter::stackElementSize ); +} + +void InterpreterMacroAssembler::push_2ptrs(Register first, Register second) { + std(first, 0, R15_esp); + std(second, -Interpreter::stackElementSize, R15_esp); + addi(R15_esp, R15_esp, - 2 * Interpreter::stackElementSize ); +} + +void InterpreterMacroAssembler::push_l_pop_d(Register l, FloatRegister d) { + std(l, 0, R15_esp); + lfd(d, 0, R15_esp); +} + +void InterpreterMacroAssembler::push_d_pop_l(FloatRegister d, Register l) { + stfd(d, 0, R15_esp); + ld(l, 0, R15_esp); +} + +void InterpreterMacroAssembler::push(TosState state) { + switch (state) { + case atos: push_ptr(); break; + case btos: + case ztos: + case ctos: + case stos: + case itos: push_i(); break; + case ltos: push_l(); break; + case ftos: push_f(); break; + case dtos: push_d(); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } +} + +void InterpreterMacroAssembler::pop(TosState state) { + switch (state) { + case atos: pop_ptr(); break; + case btos: + case ztos: + case ctos: + case stos: + case itos: pop_i(); break; + case ltos: pop_l(); break; + case ftos: pop_f(); break; + case dtos: pop_d(); break; + case vtos: /* nothing to do */ break; + default : ShouldNotReachHere(); + } + verify_oop(R17_tos, state); +} + +void InterpreterMacroAssembler::empty_expression_stack() { + addi(R15_esp, R26_monitor, - Interpreter::stackElementSize); +} + +void InterpreterMacroAssembler::get_2_byte_integer_at_bcp(int bcp_offset, + Register Rdst, + signedOrNot is_signed) { +#if defined(VM_LITTLE_ENDIAN) + if (bcp_offset) { + load_const_optimized(Rdst, bcp_offset); + lhbrx(Rdst, R14_bcp, Rdst); + } else { + lhbrx(Rdst, R14_bcp); + } + if (is_signed == Signed) { + extsh(Rdst, Rdst); + } +#else + // Read Java big endian format. + if (is_signed == Signed) { + lha(Rdst, bcp_offset, R14_bcp); + } else { + lhz(Rdst, bcp_offset, R14_bcp); + } +#endif +} + +void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(int bcp_offset, + Register Rdst, + signedOrNot is_signed) { +#if defined(VM_LITTLE_ENDIAN) + if (bcp_offset) { + load_const_optimized(Rdst, bcp_offset); + lwbrx(Rdst, R14_bcp, Rdst); + } else { + lwbrx(Rdst, R14_bcp); + } + if (is_signed == Signed) { + extsw(Rdst, Rdst); + } +#else + // Read Java big endian format. + if (bcp_offset & 3) { // Offset unaligned? + load_const_optimized(Rdst, bcp_offset); + if (is_signed == Signed) { + lwax(Rdst, R14_bcp, Rdst); + } else { + lwzx(Rdst, R14_bcp, Rdst); + } + } else { + if (is_signed == Signed) { + lwa(Rdst, bcp_offset, R14_bcp); + } else { + lwz(Rdst, bcp_offset, R14_bcp); + } + } +#endif +} + +// Load the constant pool cache index from the bytecode stream. +// +// Kills / writes: +// - Rdst, Rscratch +void InterpreterMacroAssembler::get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + // Cache index is always in the native format, courtesy of Rewriter. + if (index_size == sizeof(u2)) { + lhz(Rdst, bcp_offset, R14_bcp); + } else if (index_size == sizeof(u4)) { + assert(EnableInvokeDynamic, "giant index used only for JSR 292"); + if (bcp_offset & 3) { + load_const_optimized(Rdst, bcp_offset); + lwax(Rdst, R14_bcp, Rdst); + } else { + lwa(Rdst, bcp_offset, R14_bcp); + } + assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line"); + nand(Rdst, Rdst, Rdst); // convert to plain index + } else if (index_size == sizeof(u1)) { + lbz(Rdst, bcp_offset, R14_bcp); + } else { + ShouldNotReachHere(); + } + // Rdst now contains cp cache index. +} + +void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size) { + get_cache_index_at_bcp(cache, bcp_offset, index_size); + sldi(cache, cache, exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord)); + add(cache, R27_constPoolCache, cache); +} + +// Load 4-byte signed or unsigned integer in Java format (that is, big-endian format) +// from (Rsrc)+offset. +void InterpreterMacroAssembler::get_u4(Register Rdst, Register Rsrc, int offset, + signedOrNot is_signed) { +#if defined(VM_LITTLE_ENDIAN) + if (offset) { + load_const_optimized(Rdst, offset); + lwbrx(Rdst, Rdst, Rsrc); + } else { + lwbrx(Rdst, Rsrc); + } + if (is_signed == Signed) { + extsw(Rdst, Rdst); + } +#else + if (is_signed == Signed) { + lwa(Rdst, offset, Rsrc); + } else { + lwz(Rdst, offset, Rsrc); + } +#endif +} + +#if 0 +// Load object from cpool->resolved_references(index). +void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index) { + assert_different_registers(result, index); + get_constant_pool(result); + + // Convert from field index to resolved_references() index and from + // word index to byte offset. Since this is a java object, it can be compressed. + Register tmp = index; // reuse + sldi(tmp, index, LogBytesPerHeapOop); + // Load pointer for resolved_references[] objArray. + ld(result, ConstantPool::resolved_references_offset_in_bytes(), result); + // JNIHandles::resolve(result) + ld(result, 0, result); +#ifdef ASSERT + Label index_ok; + lwa(R0, arrayOopDesc::length_offset_in_bytes(), result); + sldi(R0, R0, LogBytesPerHeapOop); + cmpd(CCR0, tmp, R0); + blt(CCR0, index_ok); + stop("resolved reference index out of bounds", 0x09256); + bind(index_ok); +#endif + // Add in the index. + add(result, tmp, result); + load_heap_oop(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT), result); +} +#endif + +// Generate a subtype check: branch to ok_is_subtype if sub_klass is +// a subtype of super_klass. Blows registers Rsub_klass, tmp1, tmp2. +void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Register Rsuper_klass, Register Rtmp1, + Register Rtmp2, Register Rtmp3, Label &ok_is_subtype) { + // Profile the not-null value's klass. + profile_typecheck(Rsub_klass, Rtmp1, Rtmp2); + check_klass_subtype(Rsub_klass, Rsuper_klass, Rtmp1, Rtmp2, ok_is_subtype); + profile_typecheck_failed(Rtmp1, Rtmp2); +} + +void InterpreterMacroAssembler::generate_stack_overflow_check_with_compare_and_throw(Register Rmem_frame_size, Register Rscratch1) { + Label done; + sub(Rmem_frame_size, R1_SP, Rmem_frame_size); + ld(Rscratch1, thread_(stack_overflow_limit)); + cmpld(CCR0/*is_stack_overflow*/, Rmem_frame_size, Rscratch1); + bgt(CCR0/*is_stack_overflow*/, done); + + // Load target address of the runtime stub. + assert(StubRoutines::throw_StackOverflowError_entry() != NULL, "generated in wrong order"); + load_const_optimized(Rscratch1, (StubRoutines::throw_StackOverflowError_entry()), R0); + mtctr(Rscratch1); + // Restore caller_sp. +#ifdef ASSERT + ld(Rscratch1, 0, R1_SP); + ld(R0, 0, R21_sender_SP); + cmpd(CCR0, R0, Rscratch1); + asm_assert_eq("backlink", 0x547); +#endif // ASSERT + mr(R1_SP, R21_sender_SP); + bctr(); + + align(32, 12); + bind(done); +} + +// Separate these two to allow for delay slot in middle. +// These are used to do a test and full jump to exception-throwing code. + +// Check that index is in range for array, then shift index by index_shift, +// and put arrayOop + shifted_index into res. +// Note: res is still shy of address by array offset into object. + +void InterpreterMacroAssembler::index_check_without_pop(Register Rarray, Register Rindex, int index_shift, Register Rtmp, Register Rres) { + // Check that index is in range for array, then shift index by index_shift, + // and put arrayOop + shifted_index into res. + // Note: res is still shy of address by array offset into object. + // Kills: + // - Rindex + // Writes: + // - Rres: Address that corresponds to the array index if check was successful. + verify_oop(Rarray); + const Register Rlength = R0; + const Register RsxtIndex = Rtmp; + Label LisNull, LnotOOR; + + // Array nullcheck + if (!ImplicitNullChecks) { + cmpdi(CCR0, Rarray, 0); + beq(CCR0, LisNull); + } else { + null_check_throw(Rarray, arrayOopDesc::length_offset_in_bytes(), /*temp*/RsxtIndex); + } + + // Rindex might contain garbage in upper bits (remember that we don't sign extend + // during integer arithmetic operations). So kill them and put value into same register + // where ArrayIndexOutOfBounds would expect the index in. + rldicl(RsxtIndex, Rindex, 0, 32); // zero extend 32 bit -> 64 bit + + // Index check + lwz(Rlength, arrayOopDesc::length_offset_in_bytes(), Rarray); + cmplw(CCR0, Rindex, Rlength); + sldi(RsxtIndex, RsxtIndex, index_shift); + blt(CCR0, LnotOOR); + load_dispatch_table(Rtmp, (address*)Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); + mtctr(Rtmp); + bctr(); + + if (!ImplicitNullChecks) { + bind(LisNull); + load_dispatch_table(Rtmp, (address*)Interpreter::_throw_NullPointerException_entry); + mtctr(Rtmp); + bctr(); + } + + align(32, 16); + bind(LnotOOR); + + // Calc address + add(Rres, RsxtIndex, Rarray); +} + +void InterpreterMacroAssembler::index_check(Register array, Register index, int index_shift, Register tmp, Register res) { + // pop array + pop_ptr(array); + + // check array + index_check_without_pop(array, index, index_shift, tmp, res); +} + +void InterpreterMacroAssembler::get_const(Register Rdst) { + ld(Rdst, in_bytes(methodOopDesc::const_offset()), R19_method); +} + +void InterpreterMacroAssembler::get_constant_pool(Register Rdst) { + get_const(Rdst); + ld(Rdst, in_bytes(constMethodOopDesc::constants_offset()), Rdst); +} + +void InterpreterMacroAssembler::get_constant_pool_cache(Register Rdst) { + get_constant_pool(Rdst); + ld(Rdst, constantPoolOopDesc::cache_offset_in_bytes(), Rdst); +} + +void InterpreterMacroAssembler::get_cpool_and_tags(Register Rcpool, Register Rtags) { + get_constant_pool(Rcpool); + ld(Rtags, constantPoolOopDesc::tags_offset_in_bytes(), Rcpool); +} + +// Unlock if synchronized method. +// +// Unlock the receiver if this is a synchronized method. +// Unlock any Java monitors from synchronized blocks. +// +// If there are locked Java monitors +// If throw_monitor_exception +// throws IllegalMonitorStateException +// Else if install_monitor_exception +// installs IllegalMonitorStateException +// Else +// no error processing +void InterpreterMacroAssembler::unlock_if_synchronized_method(TosState state, + bool throw_monitor_exception, + bool install_monitor_exception) { + Label Lunlocked, Lno_unlock; + { + Register Rdo_not_unlock_flag = R11_scratch1; + Register Raccess_flags = R12_scratch2; + + // Check if synchronized method or unlocking prevented by + // JavaThread::do_not_unlock_if_synchronized flag. + lbz(Rdo_not_unlock_flag, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); + lwz(Raccess_flags, in_bytes(methodOopDesc::access_flags_offset()), R19_method); + li(R0, 0); + stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); // reset flag + + push(state); + + // Skip if we don't have to unlock. + rldicl_(R0, Raccess_flags, 64-JVM_ACC_SYNCHRONIZED_BIT, 63); // Extract bit and compare to 0. + beq(CCR0, Lunlocked); + + cmpwi(CCR0, Rdo_not_unlock_flag, 0); + bne(CCR0, Lno_unlock); + } + + // Unlock + { + Register Rmonitor_base = R11_scratch1; + + Label Lunlock; + // If it's still locked, everything is ok, unlock it. + ld(Rmonitor_base, 0, R1_SP); + addi(Rmonitor_base, Rmonitor_base, - (frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes())); // Monitor base + + ld(R0, BasicObjectLock::obj_offset_in_bytes(), Rmonitor_base); + cmpdi(CCR0, R0, 0); + bne(CCR0, Lunlock); + + // If it's already unlocked, throw exception. + if (throw_monitor_exception) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + if (install_monitor_exception) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception)); + b(Lunlocked); + } + } + + bind(Lunlock); + unlock_object(Rmonitor_base); + } + + // Check that all other monitors are unlocked. Throw IllegelMonitorState exception if not. + bind(Lunlocked); + { + Label Lexception, Lrestart; + Register Rcurrent_obj_addr = R11_scratch1; + const int delta = frame::interpreter_frame_monitor_size_in_bytes(); + assert((delta & LongAlignmentMask) == 0, "sizeof BasicObjectLock must be even number of doublewords"); + + bind(Lrestart); + // Set up search loop: Calc num of iterations. + { + Register Riterations = R12_scratch2; + Register Rmonitor_base = Rcurrent_obj_addr; + ld(Rmonitor_base, 0, R1_SP); + addi(Rmonitor_base, Rmonitor_base, - frame::ijava_state_size); // Monitor base + + subf_(Riterations, R26_monitor, Rmonitor_base); + ble(CCR0, Lno_unlock); + + addi(Rcurrent_obj_addr, Rmonitor_base, BasicObjectLock::obj_offset_in_bytes() - frame::interpreter_frame_monitor_size_in_bytes()); + // Check if any monitor is on stack, bail out if not + srdi(Riterations, Riterations, exact_log2(delta)); + mtctr(Riterations); + } + + // The search loop: Look for locked monitors. + { + const Register Rcurrent_obj = R0; + Label Lloop; + + ld(Rcurrent_obj, 0, Rcurrent_obj_addr); + addi(Rcurrent_obj_addr, Rcurrent_obj_addr, -delta); + bind(Lloop); + + // Check if current entry is used. + cmpdi(CCR0, Rcurrent_obj, 0); + bne(CCR0, Lexception); + // Preload next iteration's compare value. + ld(Rcurrent_obj, 0, Rcurrent_obj_addr); + addi(Rcurrent_obj_addr, Rcurrent_obj_addr, -delta); + bdnz(Lloop); + } + // Fell through: Everything's unlocked => finish. + b(Lno_unlock); + + // An object is still locked => need to throw exception. + bind(Lexception); + if (throw_monitor_exception) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + should_not_reach_here(); + } else { + // Stack unrolling. Unlock object and if requested, install illegal_monitor_exception. + // Unlock does not block, so don't have to worry about the frame. + Register Rmonitor_addr = R11_scratch1; + addi(Rmonitor_addr, Rcurrent_obj_addr, -BasicObjectLock::obj_offset_in_bytes() + delta); + unlock_object(Rmonitor_addr); + if (install_monitor_exception) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception)); + } + b(Lrestart); + } + } + + align(32, 12); + bind(Lno_unlock); + pop(state); +} + +// Support function for remove_activation & Co. +void InterpreterMacroAssembler::merge_frames(Register Rsender_sp, Register return_pc, Register Rscratch1, Register Rscratch2) { + // Pop interpreter frame. + ld(Rscratch1, 0, R1_SP); // *SP + ld(Rsender_sp, _ijava_state_neg(sender_sp), Rscratch1); // top_frame_sp + ld(Rscratch2, 0, Rscratch1); // **SP +#ifdef ASSERT + { + Label Lok; + ld(R0, _ijava_state_neg(ijava_reserved), Rscratch1); + cmpdi(CCR0, R0, 0x5afe); + beq(CCR0, Lok); + stop("frame corrupted (remove activation)", 0x5afe); + bind(Lok); + } +#endif + if (return_pc!=noreg) { + ld(return_pc, _abi(lr), Rscratch1); // LR + } + + // Merge top frames. + subf(Rscratch1, R1_SP, Rsender_sp); // top_frame_sp - SP + stdux(Rscratch2, R1_SP, Rscratch1); // atomically set *(SP = top_frame_sp) = **SP +} + +void InterpreterMacroAssembler::narrow(Register result) { + Register ret_type = R11_scratch1; + ld(R11_scratch1, in_bytes(methodOopDesc::const_offset()), R19_method); + lbz(ret_type, in_bytes(constMethodOopDesc::result_type_offset()), R11_scratch1); + + Label notBool, notByte, notChar, done; + + // common case first + cmpwi(CCR0, ret_type, T_INT); + beq(CCR0, done); + + cmpwi(CCR0, ret_type, T_BOOLEAN); + bne(CCR0, notBool); + andi(result, result, 0x1); + b(done); + + bind(notBool); + cmpwi(CCR0, ret_type, T_BYTE); + bne(CCR0, notByte); + extsb(result, result); + b(done); + + bind(notByte); + cmpwi(CCR0, ret_type, T_CHAR); + bne(CCR0, notChar); + andi(result, result, 0xffff); + b(done); + + bind(notChar); + // cmpwi(CCR0, ret_type, T_SHORT); // all that's left + // bne(CCR0, done); + extsh(result, result); + + // Nothing to do for T_INT + bind(done); +} + +// Remove activation. +// +// Unlock the receiver if this is a synchronized method. +// Unlock any Java monitors from synchronized blocks. +// Remove the activation from the stack. +// +// If there are locked Java monitors +// If throw_monitor_exception +// throws IllegalMonitorStateException +// Else if install_monitor_exception +// installs IllegalMonitorStateException +// Else +// no error processing +void InterpreterMacroAssembler::remove_activation(TosState state, + bool throw_monitor_exception, + bool install_monitor_exception) { + unlock_if_synchronized_method(state, throw_monitor_exception, install_monitor_exception); + + // Save result (push state before jvmti call and pop it afterwards) and notify jvmti. + notify_method_exit(false, state, NotifyJVMTI, true); + + verify_oop(R17_tos, state); + verify_oop(R19_method); + verify_thread(); + + merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2); + mtlr(R0); +} + +#endif // !CC_INTERP + +// Lock object +// +// Registers alive +// monitor - Address of the BasicObjectLock to be used for locking, +// which must be initialized with the object to lock. +// object - Address of the object to be locked. +// +void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { + if (UseHeavyMonitors) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + monitor, /*check_for_exceptions=*/true CC_INTERP_ONLY(&& false)); + } else { + // template code: + // + // markOop displaced_header = obj->mark().set_unlocked(); + // monitor->lock()->set_displaced_header(displaced_header); + // if (Atomic::cmpxchg_ptr(/*ex=*/monitor, /*addr*/obj->mark_addr(), /*cmp*/displaced_header) == displaced_header) { + // // We stored the monitor address into the object's mark word. + // } else if (THREAD->is_lock_owned((address)displaced_header)) + // // Simple recursive case. + // monitor->lock()->set_displaced_header(NULL); + // } else { + // // Slow path. + // InterpreterRuntime::monitorenter(THREAD, monitor); + // } + + const Register displaced_header = R7_ARG5; + const Register object_mark_addr = R8_ARG6; + const Register current_header = R9_ARG7; + const Register tmp = R10_ARG8; + + Label done; + Label cas_failed, slow_case; + + assert_different_registers(displaced_header, object_mark_addr, current_header, tmp); + + // markOop displaced_header = obj->mark().set_unlocked(); + + // Load markOop from object into displaced_header. + ld(displaced_header, oopDesc::mark_offset_in_bytes(), object); + + if (UseBiasedLocking) { + biased_locking_enter(CCR0, object, displaced_header, tmp, current_header, done, &slow_case); + } + + // Set displaced_header to be (markOop of object | UNLOCK_VALUE). + ori(displaced_header, displaced_header, markOopDesc::unlocked_value); + + // monitor->lock()->set_displaced_header(displaced_header); + + // Initialize the box (Must happen before we update the object mark!). + std(displaced_header, BasicObjectLock::lock_offset_in_bytes() + + BasicLock::displaced_header_offset_in_bytes(), monitor); + + // if (Atomic::cmpxchg_ptr(/*ex=*/monitor, /*addr*/obj->mark_addr(), /*cmp*/displaced_header) == displaced_header) { + + // Store stack address of the BasicObjectLock (this is monitor) into object. + addi(object_mark_addr, object, oopDesc::mark_offset_in_bytes()); + + // Must fence, otherwise, preceding store(s) may float below cmpxchg. + // CmpxchgX sets CCR0 to cmpX(current, displaced). + membar(Assembler::StoreStore); + cmpxchgd(/*flag=*/CCR0, + /*current_value=*/current_header, + /*compare_value=*/displaced_header, /*exchange_value=*/monitor, + /*where=*/object_mark_addr, + MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock(), + noreg, + &cas_failed); + + // If the compare-and-exchange succeeded, then we found an unlocked + // object and we have now locked it. + b(done); + bind(cas_failed); + + // } else if (THREAD->is_lock_owned((address)displaced_header)) + // // Simple recursive case. + // monitor->lock()->set_displaced_header(NULL); + + // We did not see an unlocked object so try the fast recursive case. + + // Check if owner is self by comparing the value in the markOop of object + // (current_header) with the stack pointer. + sub(current_header, current_header, R1_SP); + + assert(os::vm_page_size() > 0xfff, "page size too small - change the constant"); + load_const_optimized(tmp, + (address) (~(os::vm_page_size()-1) | + markOopDesc::lock_mask_in_place)); + + and_(R0/*==0?*/, current_header, tmp); + // If condition is true we are done and hence we can store 0 in the displaced + // header indicating it is a recursive lock. + bne(CCR0, slow_case); + release(); + std(R0/*==0!*/, BasicObjectLock::lock_offset_in_bytes() + + BasicLock::displaced_header_offset_in_bytes(), monitor); + b(done); + + // } else { + // // Slow path. + // InterpreterRuntime::monitorenter(THREAD, monitor); + + // None of the above fast optimizations worked so we have to get into the + // slow case of monitor enter. + bind(slow_case); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), + monitor, /*check_for_exceptions=*/true CC_INTERP_ONLY(&& false)); + // } + align(32, 12); + bind(done); + } +} + +// Unlocks an object. Used in monitorexit bytecode and remove_activation. +// +// Registers alive +// monitor - Address of the BasicObjectLock to be used for locking, +// which must be initialized with the object to lock. +// +// Throw IllegalMonitorException if object is not locked by current thread. +void InterpreterMacroAssembler::unlock_object(Register monitor, bool check_for_exceptions) { + if (UseHeavyMonitors) { + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), + monitor, check_for_exceptions CC_INTERP_ONLY(&& false)); + } else { + + // template code: + // + // if ((displaced_header = monitor->displaced_header()) == NULL) { + // // Recursive unlock. Mark the monitor unlocked by setting the object field to NULL. + // monitor->set_obj(NULL); + // } else if (Atomic::cmpxchg_ptr(displaced_header, obj->mark_addr(), monitor) == monitor) { + // // We swapped the unlocked mark in displaced_header into the object's mark word. + // monitor->set_obj(NULL); + // } else { + // // Slow path. + // InterpreterRuntime::monitorexit(THREAD, monitor); + // } + + const Register object = R7_ARG5; + const Register displaced_header = R8_ARG6; + const Register object_mark_addr = R9_ARG7; + const Register current_header = R10_ARG8; + + Label free_slot; + Label slow_case; + + assert_different_registers(object, displaced_header, object_mark_addr, current_header); + + if (UseBiasedLocking) { + // The object address from the monitor is in object. + ld(object, BasicObjectLock::obj_offset_in_bytes(), monitor); + assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + biased_locking_exit(CCR0, object, displaced_header, free_slot); + } + + // Test first if we are in the fast recursive case. + ld(displaced_header, BasicObjectLock::lock_offset_in_bytes() + + BasicLock::displaced_header_offset_in_bytes(), monitor); + + // If the displaced header is zero, we have a recursive unlock. + cmpdi(CCR0, displaced_header, 0); + beq(CCR0, free_slot); // recursive unlock + + // } else if (Atomic::cmpxchg_ptr(displaced_header, obj->mark_addr(), monitor) == monitor) { + // // We swapped the unlocked mark in displaced_header into the object's mark word. + // monitor->set_obj(NULL); + + // If we still have a lightweight lock, unlock the object and be done. + + // The object address from the monitor is in object. + if (!UseBiasedLocking) { ld(object, BasicObjectLock::obj_offset_in_bytes(), monitor); } + addi(object_mark_addr, object, oopDesc::mark_offset_in_bytes()); + + // We have the displaced header in displaced_header. If the lock is still + // lightweight, it will contain the monitor address and we'll store the + // displaced header back into the object's mark word. + // CmpxchgX sets CCR0 to cmpX(current, monitor). + cmpxchgd(/*flag=*/CCR0, + /*current_value=*/current_header, + /*compare_value=*/monitor, /*exchange_value=*/displaced_header, + /*where=*/object_mark_addr, + MacroAssembler::MemBarRel, + MacroAssembler::cmpxchgx_hint_release_lock(), + noreg, + &slow_case); + b(free_slot); + + // } else { + // // Slow path. + // InterpreterRuntime::monitorexit(THREAD, monitor); + + // The lock has been converted into a heavy lock and hence + // we need to get into the slow case. + bind(slow_case); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), + monitor, check_for_exceptions CC_INTERP_ONLY(&& false)); + // } + + Label done; + b(done); // Monitor register may be overwritten! Runtime has already freed the slot. + + // Exchange worked, do monitor->set_obj(NULL); + align(32, 12); + bind(free_slot); + li(R0, 0); + std(R0, BasicObjectLock::obj_offset_in_bytes(), monitor); + bind(done); + } +} + +#ifndef CC_INTERP + +// Load compiled (i2c) or interpreter entry when calling from interpreted and +// do the call. Centralized so that all interpreter calls will do the same actions. +// If jvmti single stepping is on for a thread we must not call compiled code. +// +// Input: +// - Rtarget_method: method to call +// - Rret_addr: return address +// - 2 scratch regs +// +void InterpreterMacroAssembler::call_from_interpreter(Register Rtarget_method, Register Rret_addr, Register Rscratch1, Register Rscratch2) { + assert_different_registers(Rscratch1, Rscratch2, Rtarget_method, Rret_addr); + // Assume we want to go compiled if available. + const Register Rtarget_addr = Rscratch1; + const Register Rinterp_only = Rscratch2; + + ld(Rtarget_addr, in_bytes(methodOopDesc::from_interpreted_offset()), Rtarget_method); + + if (JvmtiExport::can_post_interpreter_events()) { + lwz(Rinterp_only, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread); + + // JVMTI events, such as single-stepping, are implemented partly by avoiding running + // compiled code in threads for which the event is enabled. Check here for + // interp_only_mode if these events CAN be enabled. + Label done; + verify_thread(); + cmpwi(CCR0, Rinterp_only, 0); + beq(CCR0, done); + ld(Rtarget_addr, in_bytes(methodOopDesc::interpreter_entry_offset()), Rtarget_method); + align(32, 12); + bind(done); + } + +#ifdef ASSERT + { + Label Lok; + cmpdi(CCR0, Rtarget_addr, 0); + bne(CCR0, Lok); + stop("null entry point"); + bind(Lok); + } +#endif // ASSERT + + mr(R21_sender_SP, R1_SP); + + // Calc a precise SP for the call. The SP value we calculated in + // generate_fixed_frame() is based on the max_stack() value, so we would waste stack space + // if esp is not max. Also, the i2c adapter extends the stack space without restoring + // our pre-calced value, so repeating calls via i2c would result in stack overflow. + // Since esp already points to an empty slot, we just have to sub 1 additional slot + // to meet the abi scratch requirements. + // The max_stack pointer will get restored by means of the GR_Lmax_stack local in + // the return entry of the interpreter. + addi(Rscratch2, R15_esp, Interpreter::stackElementSize - frame::abi_reg_args_size); + clrrdi(Rscratch2, Rscratch2, exact_log2(frame::alignment_in_bytes)); // round towards smaller address + resize_frame_absolute(Rscratch2, Rscratch2, R0); + + mr_if_needed(R19_method, Rtarget_method); + mtctr(Rtarget_addr); + mtlr(Rret_addr); + + save_interpreter_state(Rscratch2); +#ifdef ASSERT + ld(Rscratch1, _ijava_state_neg(top_frame_sp), Rscratch2); // Rscratch2 contains fp + cmpd(CCR0, R21_sender_SP, Rscratch1); + asm_assert_eq("top_frame_sp incorrect", 0x951); +#endif + + bctr(); +} + +// Set the method data pointer for the current bcp. +void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() { + assert(ProfileInterpreter, "must be profiling interpreter"); + Label get_continue; + ld(R28_mdx, in_bytes(methodOopDesc::method_data_offset()), R19_method); + test_method_data_pointer(get_continue); + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), R19_method, R14_bcp); + + addi(R28_mdx, R28_mdx, in_bytes(methodDataOopDesc::data_offset())); + add(R28_mdx, R28_mdx, R3_RET); + bind(get_continue); +} + +// Test ImethodDataPtr. If it is null, continue at the specified label. +void InterpreterMacroAssembler::test_method_data_pointer(Label& zero_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + cmpdi(CCR0, R28_mdx, 0); + beq(CCR0, zero_continue); +} + +void InterpreterMacroAssembler::verify_method_data_pointer() { + assert(ProfileInterpreter, "must be profiling interpreter"); +#ifdef ASSERT + Label verify_continue; + test_method_data_pointer(verify_continue); + + // If the mdp is valid, it will point to a DataLayout header which is + // consistent with the bcp. The converse is highly probable also. + lhz(R11_scratch1, in_bytes(DataLayout::bci_offset()), R28_mdx); + ld(R12_scratch2, in_bytes(methodOopDesc::const_offset()), R19_method); + addi(R11_scratch1, R11_scratch1, in_bytes(constMethodOopDesc::codes_offset())); + add(R11_scratch1, R12_scratch2, R12_scratch2); + cmpd(CCR0, R11_scratch1, R14_bcp); + beq(CCR0, verify_continue); + + call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), R19_method, R14_bcp, R28_mdx); + + bind(verify_continue); +#endif +} + +void InterpreterMacroAssembler::test_invocation_counter_for_mdp(Register invocation_count, + Register Rscratch, + Label &profile_continue) { + assert(ProfileInterpreter, "must be profiling interpreter"); + // Control will flow to "profile_continue" if the counter is less than the + // limit or if we call profile_method(). + Label done; + + // If no method data exists, and the counter is high enough, make one. + int ipl_offs = load_const_optimized(Rscratch, &InvocationCounter::InterpreterProfileLimit, R0, true); + lwz(Rscratch, ipl_offs, Rscratch); + + cmpdi(CCR0, R28_mdx, 0); + // Test to see if we should create a method data oop. + cmpd(CCR1, Rscratch /* InterpreterProfileLimit */, invocation_count); + bne(CCR0, done); + bge(CCR1, profile_continue); + + // Build it now. + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); + set_method_data_pointer_for_bcp(); + b(profile_continue); + + align(32, 12); + bind(done); +} + +void InterpreterMacroAssembler::test_backedge_count_for_osr(Register backedge_count, Register branch_bcp, Register Rtmp) { + assert_different_registers(backedge_count, Rtmp, branch_bcp); + assert(UseOnStackReplacement,"Must UseOnStackReplacement to test_backedge_count_for_osr"); + + Label did_not_overflow; + Label overflow_with_error; + + int ibbl_offs = load_const_optimized(Rtmp, &InvocationCounter::InterpreterBackwardBranchLimit, R0, true); + lwz(Rtmp, ibbl_offs, Rtmp); + cmpw(CCR0, backedge_count, Rtmp); + + blt(CCR0, did_not_overflow); + + // When ProfileInterpreter is on, the backedge_count comes from the + // methodDataOop, which value does not get reset on the call to + // frequency_counter_overflow(). To avoid excessive calls to the overflow + // routine while the method is being compiled, add a second test to make sure + // the overflow function is called only once every overflow_frequency. + if (ProfileInterpreter) { + const int overflow_frequency = 1024; + li(Rtmp, overflow_frequency-1); + andr(Rtmp, Rtmp, backedge_count); + cmpwi(CCR0, Rtmp, 0); + bne(CCR0, did_not_overflow); + } + + // Overflow in loop, pass branch bytecode. + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), branch_bcp, true); + + // Was an OSR adapter generated? + // O0 = osr nmethod + cmpdi(CCR0, R3_RET, 0); + beq(CCR0, overflow_with_error); + + // Has the nmethod been invalidated already? + lwz(Rtmp, nmethod::entry_bci_offset(), R3_RET); + cmpwi(CCR0, Rtmp, InvalidOSREntryBci); + beq(CCR0, overflow_with_error); + + // Migrate the interpreter frame off of the stack. + // We can use all registers because we will not return to interpreter from this point. + + // Save nmethod. + const Register osr_nmethod = R31; + mr(osr_nmethod, R3_RET); + set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R11_scratch1); + call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin), R16_thread); + reset_last_Java_frame(); + // OSR buffer is in ARG1 + + // Remove the interpreter frame. + merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2); + + // Jump to the osr code. + ld(R11_scratch1, nmethod::osr_entry_point_offset(), osr_nmethod); + mtlr(R0); + mtctr(R11_scratch1); + bctr(); + + align(32, 12); + bind(overflow_with_error); + bind(did_not_overflow); +} + +// Store a value at some constant offset from the method data pointer. +void InterpreterMacroAssembler::set_mdp_data_at(int constant, Register value) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + std(value, constant, R28_mdx); +} + +// Increment the value at some constant offset from the method data pointer. +void InterpreterMacroAssembler::increment_mdp_data_at(int constant, + Register counter_addr, + Register Rbumped_count, + bool decrement) { + // Locate the counter at a fixed offset from the mdp: + addi(counter_addr, R28_mdx, constant); + increment_mdp_data_at(counter_addr, Rbumped_count, decrement); +} + +// Increment the value at some non-fixed (reg + constant) offset from +// the method data pointer. +void InterpreterMacroAssembler::increment_mdp_data_at(Register reg, + int constant, + Register scratch, + Register Rbumped_count, + bool decrement) { + // Add the constant to reg to get the offset. + add(scratch, R28_mdx, reg); + // Then calculate the counter address. + addi(scratch, scratch, constant); + increment_mdp_data_at(scratch, Rbumped_count, decrement); +} + +void InterpreterMacroAssembler::increment_mdp_data_at(Register counter_addr, + Register Rbumped_count, + bool decrement) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + // Load the counter. + ld(Rbumped_count, 0, counter_addr); + + if (decrement) { + // Decrement the register. Set condition codes. + addi(Rbumped_count, Rbumped_count, - DataLayout::counter_increment); + // Store the decremented counter, if it is still negative. + std(Rbumped_count, 0, counter_addr); + // Note: add/sub overflow check are not ported, since 64 bit + // calculation should never overflow. + } else { + // Increment the register. Set carry flag. + addi(Rbumped_count, Rbumped_count, DataLayout::counter_increment); + // Store the incremented counter. + std(Rbumped_count, 0, counter_addr); + } +} + +// Set a flag value at the current method data pointer position. +void InterpreterMacroAssembler::set_mdp_flag_at(int flag_constant, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + // Load the data header. + lbz(scratch, in_bytes(DataLayout::flags_offset()), R28_mdx); + // Set the flag. + ori(scratch, scratch, flag_constant); + // Store the modified header. + stb(scratch, in_bytes(DataLayout::flags_offset()), R28_mdx); +} + +// Test the location at some offset from the method data pointer. +// If it is not equal to value, branch to the not_equal_continue Label. +void InterpreterMacroAssembler::test_mdp_data_at(int offset, + Register value, + Label& not_equal_continue, + Register test_out) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + ld(test_out, offset, R28_mdx); + cmpd(CCR0, value, test_out); + bne(CCR0, not_equal_continue); +} + +// Update the method data pointer by the displacement located at some fixed +// offset from the method data pointer. +void InterpreterMacroAssembler::update_mdp_by_offset(int offset_of_disp, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + ld(scratch, offset_of_disp, R28_mdx); + add(R28_mdx, scratch, R28_mdx); +} + +// Update the method data pointer by the displacement located at the +// offset (reg + offset_of_disp). +void InterpreterMacroAssembler::update_mdp_by_offset(Register reg, + int offset_of_disp, + Register scratch) { + assert(ProfileInterpreter, "must be profiling interpreter"); + + add(scratch, reg, R28_mdx); + ld(scratch, offset_of_disp, scratch); + add(R28_mdx, scratch, R28_mdx); +} + +// Update the method data pointer by a simple constant displacement. +void InterpreterMacroAssembler::update_mdp_by_constant(int constant) { + assert(ProfileInterpreter, "must be profiling interpreter"); + addi(R28_mdx, R28_mdx, constant); +} + +// Update the method data pointer for a _ret bytecode whose target +// was not among our cached targets. +void InterpreterMacroAssembler::update_mdp_for_ret(TosState state, + Register return_bci) { + assert(ProfileInterpreter, "must be profiling interpreter"); + push(state); + mr(R21_tmp1, return_bci); // Protect return_bci, in case it is volatile. + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), return_bci); + mr(return_bci, R21_tmp1); + pop(state); +} + +// Increments the backedge counter. +// Returns backedge counter + invocation counter in Rdst. +void InterpreterMacroAssembler::increment_backedge_counter(const Register Rdst, + const Register Rtmp1, Register Rscratch) { + assert(UseCompiler, "incrementing must be useful"); + assert_different_registers(Rdst, Rtmp1); + const Register invocation_counter = Rtmp1; + const Register counter = Rdst; + // TODO ppc port assert(4 == InvocationCounter::sz_counter(), "unexpected field size."); + + // Load backedge counter + lwz(counter, in_bytes(methodOopDesc::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()), R19_method); + // Load invocation counter + lwz(invocation_counter, in_bytes(methodOopDesc::invocation_counter_offset()) + in_bytes(InvocationCounter::counter_offset()), R19_method); + + // Add the delta to the backedge counter. + addi(counter, counter, InvocationCounter::count_increment); + + // Mask the invocation counter. + li(Rscratch, InvocationCounter::count_mask_value); + andr(invocation_counter, invocation_counter, Rscratch); + + // Store new counter value. + stw(counter, in_bytes(methodOopDesc::backedge_counter_offset()) + + in_bytes(InvocationCounter::counter_offset()), R19_method); + // Return invocation counter + backedge counter. + add(counter, counter, invocation_counter); +} + +// Count a taken branch in the bytecodes. +void InterpreterMacroAssembler::profile_taken_branch(Register scratch, Register bumped_count) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are taking a branch. Increment the taken count. + increment_mdp_data_at(in_bytes(JumpData::taken_offset()), scratch, bumped_count); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(in_bytes(JumpData::displacement_offset()), scratch); + bind (profile_continue); + } +} + +// Count a not-taken branch in the bytecodes. +void InterpreterMacroAssembler::profile_not_taken_branch(Register scratch1, Register scratch2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are taking a branch. Increment the not taken count. + increment_mdp_data_at(in_bytes(BranchData::not_taken_offset()), scratch1, scratch2); + + // The method data pointer needs to be updated to correspond to the + // next bytecode. + update_mdp_by_constant(in_bytes(BranchData::branch_data_size())); + bind (profile_continue); + } +} + +// Count a non-virtual call in the bytecodes. +void InterpreterMacroAssembler::profile_call(Register scratch1, Register scratch2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(in_bytes(CounterData::counter_data_size())); + bind (profile_continue); + } +} + +// Count a final call in the bytecodes. +void InterpreterMacroAssembler::profile_final_call(Register scratch1, Register scratch2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // We are making a call. Increment the count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); + bind (profile_continue); + } +} + +// Count a virtual call in the bytecodes. +void InterpreterMacroAssembler::profile_virtual_call(Register Rreceiver, + Register Rscratch1, + Register Rscratch2, + bool receiver_can_be_null) { + if (!ProfileInterpreter) { return; } + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + Label skip_receiver_profile; + if (receiver_can_be_null) { + Label not_null; + cmpdi(CCR0, Rreceiver, 0); + bne(CCR0, not_null); + // We are making a call. Increment the count for null receiver. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), Rscratch1, Rscratch2); + b(skip_receiver_profile); + bind(not_null); + } + + // Record the receiver type. + record_klass_in_profile(Rreceiver, Rscratch1, Rscratch2, true); + bind(skip_receiver_profile); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); + bind (profile_continue); +} + +void InterpreterMacroAssembler::profile_typecheck(Register Rklass, Register Rscratch1, Register Rscratch2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + + // Record the object type. + record_klass_in_profile(Rklass, Rscratch1, Rscratch2, false); + } + + // The method data pointer needs to be updated. + update_mdp_by_constant(mdp_delta); + + bind (profile_continue); + } +} + +void InterpreterMacroAssembler::profile_typecheck_failed(Register Rscratch1, Register Rscratch2) { + if (ProfileInterpreter && TypeProfileCasts) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + int count_offset = in_bytes(CounterData::count_offset()); + // Back up the address, since we have already bumped the mdp. + count_offset -= in_bytes(VirtualCallData::virtual_call_data_size()); + + // *Decrement* the counter. We expect to see zero or small negatives. + increment_mdp_data_at(count_offset, Rscratch1, Rscratch2, true); + + bind (profile_continue); + } +} + +// Count a ret in the bytecodes. +void InterpreterMacroAssembler::profile_ret(TosState state, Register return_bci, Register scratch1, Register scratch2) { + if (ProfileInterpreter) { + Label profile_continue; + uint row; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // Update the total ret count. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2 ); + + for (row = 0; row < RetData::row_limit(); row++) { + Label next_test; + + // See if return_bci is equal to bci[n]: + test_mdp_data_at(in_bytes(RetData::bci_offset(row)), return_bci, next_test, scratch1); + + // return_bci is equal to bci[n]. Increment the count. + increment_mdp_data_at(in_bytes(RetData::bci_count_offset(row)), scratch1, scratch2); + + // The method data pointer needs to be updated to reflect the new target. + update_mdp_by_offset(in_bytes(RetData::bci_displacement_offset(row)), scratch1); + b(profile_continue); + bind(next_test); + } + + update_mdp_for_ret(state, return_bci); + + bind (profile_continue); + } +} + +// Count the default case of a switch construct. +void InterpreterMacroAssembler::profile_switch_default(Register scratch1, Register scratch2) { + if (ProfileInterpreter) { + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // Update the default case count + increment_mdp_data_at(in_bytes(MultiBranchData::default_count_offset()), + scratch1, scratch2); + + // The method data pointer needs to be updated. + update_mdp_by_offset(in_bytes(MultiBranchData::default_displacement_offset()), + scratch1); + + bind (profile_continue); + } +} + +// Count the index'th case of a switch construct. +void InterpreterMacroAssembler::profile_switch_case(Register index, + Register scratch1, + Register scratch2, + Register scratch3) { + if (ProfileInterpreter) { + assert_different_registers(index, scratch1, scratch2, scratch3); + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + // Build the base (index * per_case_size_in_bytes()) + case_array_offset_in_bytes(). + li(scratch3, in_bytes(MultiBranchData::case_array_offset())); + + assert (in_bytes(MultiBranchData::per_case_size()) == 16, "so that shladd works"); + sldi(scratch1, index, exact_log2(in_bytes(MultiBranchData::per_case_size()))); + add(scratch1, scratch1, scratch3); + + // Update the case count. + increment_mdp_data_at(scratch1, in_bytes(MultiBranchData::relative_count_offset()), scratch2, scratch3); + + // The method data pointer needs to be updated. + update_mdp_by_offset(scratch1, in_bytes(MultiBranchData::relative_displacement_offset()), scratch2); + + bind (profile_continue); + } +} + +void InterpreterMacroAssembler::profile_null_seen(Register Rscratch1, Register Rscratch2) { + if (ProfileInterpreter) { + assert_different_registers(Rscratch1, Rscratch2); + Label profile_continue; + + // If no method data exists, go to profile_continue. + test_method_data_pointer(profile_continue); + + set_mdp_flag_at(BitData::null_seen_byte_constant(), Rscratch1); + + // The method data pointer needs to be updated. + int mdp_delta = in_bytes(BitData::bit_data_size()); + if (TypeProfileCasts) { + mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); + } + update_mdp_by_constant(mdp_delta); + + bind (profile_continue); + } +} + +void InterpreterMacroAssembler::record_klass_in_profile(Register Rreceiver, + Register Rscratch1, Register Rscratch2, + bool is_virtual_call) { + assert(ProfileInterpreter, "must be profiling"); + assert_different_registers(Rreceiver, Rscratch1, Rscratch2); + + Label done; + record_klass_in_profile_helper(Rreceiver, Rscratch1, Rscratch2, 0, done, is_virtual_call); + bind (done); +} + +void InterpreterMacroAssembler::record_klass_in_profile_helper( + Register receiver, Register scratch1, Register scratch2, + int start_row, Label& done, bool is_virtual_call) { + if (TypeProfileWidth == 0) { + if (is_virtual_call) { + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2); + } + return; + } + + int last_row = VirtualCallData::row_limit() - 1; + assert(start_row <= last_row, "must be work left to do"); + // Test this row for both the receiver and for null. + // Take any of three different outcomes: + // 1. found receiver => increment count and goto done + // 2. found null => keep looking for case 1, maybe allocate this cell + // 3. found something else => keep looking for cases 1 and 2 + // Case 3 is handled by a recursive call. + for (int row = start_row; row <= last_row; row++) { + Label next_test; + bool test_for_null_also = (row == start_row); + + // See if the receiver is receiver[n]. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row)); + test_mdp_data_at(recvr_offset, receiver, next_test, scratch1); + // delayed()->tst(scratch); + + // The receiver is receiver[n]. Increment count[n]. + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); + increment_mdp_data_at(count_offset, scratch1, scratch2); + b(done); + bind(next_test); + + if (test_for_null_also) { + Label found_null; + // Failed the equality check on receiver[n]... Test for null. + if (start_row == last_row) { + // The only thing left to do is handle the null case. + if (is_virtual_call) { + // Scratch1 contains test_out from test_mdp_data_at. + cmpdi(CCR0, scratch1, 0); + beq(CCR0, found_null); + // Receiver did not match any saved receiver and there is no empty row for it. + // Increment total counter to indicate polymorphic case. + increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2); + b(done); + bind(found_null); + } else { + cmpdi(CCR0, scratch1, 0); + bne(CCR0, done); + } + break; + } + // Since null is rare, make it be the branch-taken case. + cmpdi(CCR0, scratch1, 0); + beq(CCR0, found_null); + + // Put all the "Case 3" tests here. + record_klass_in_profile_helper(receiver, scratch1, scratch2, start_row + 1, done, is_virtual_call); + + // Found a null. Keep searching for a matching receiver, + // but remember that this is an empty (unused) slot. + bind(found_null); + } + } + + // In the fall-through case, we found no matching receiver, but we + // observed the receiver[start_row] is NULL. + + // Fill in the receiver field and increment the count. + int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row)); + set_mdp_data_at(recvr_offset, receiver); + int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); + li(scratch1, DataLayout::counter_increment); + set_mdp_data_at(count_offset, scratch1); + if (start_row > 0) { + b(done); + } +} + +// Add a InterpMonitorElem to stack (see frame_sparc.hpp). +void InterpreterMacroAssembler::add_monitor_to_stack(bool stack_is_empty, Register Rtemp1, Register Rtemp2) { + + // Very-local scratch registers. + const Register esp = Rtemp1; + const Register slot = Rtemp2; + + // Extracted monitor_size. + int monitor_size = frame::interpreter_frame_monitor_size_in_bytes(); + assert(Assembler::is_aligned((unsigned int)monitor_size, + (unsigned int)frame::alignment_in_bytes), + "size of a monitor must respect alignment of SP"); + + resize_frame(-monitor_size, /*temp*/esp); // Allocate space for new monitor + std(R1_SP, _ijava_state_neg(top_frame_sp), esp); // esp contains fp + + // Shuffle expression stack down. Recall that stack_base points + // just above the new expression stack bottom. Old_tos and new_tos + // are used to scan thru the old and new expression stacks. + if (!stack_is_empty) { + Label copy_slot, copy_slot_finished; + const Register n_slots = slot; + + addi(esp, R15_esp, Interpreter::stackElementSize); // Point to first element (pre-pushed stack). + subf(n_slots, esp, R26_monitor); + srdi_(n_slots, n_slots, LogBytesPerWord); // Compute number of slots to copy. + assert(LogBytesPerWord == 3, "conflicts assembler instructions"); + beq(CCR0, copy_slot_finished); // Nothing to copy. + + mtctr(n_slots); + + // loop + bind(copy_slot); + ld(slot, 0, esp); // Move expression stack down. + std(slot, -monitor_size, esp); // distance = monitor_size + addi(esp, esp, BytesPerWord); + bdnz(copy_slot); + + bind(copy_slot_finished); + } + + addi(R15_esp, R15_esp, -monitor_size); + addi(R26_monitor, R26_monitor, -monitor_size); + + // Restart interpreter +} + +// ============================================================================ +// Java locals access + +// Load a local variable at index in Rindex into register Rdst_value. +// Also puts address of local into Rdst_address as a service. +// Kills: +// - Rdst_value +// - Rdst_address +void InterpreterMacroAssembler::load_local_int(Register Rdst_value, Register Rdst_address, Register Rindex) { + sldi(Rdst_address, Rindex, Interpreter::logStackElementSize); + subf(Rdst_address, Rdst_address, R18_locals); + lwz(Rdst_value, 0, Rdst_address); +} + +// Load a local variable at index in Rindex into register Rdst_value. +// Also puts address of local into Rdst_address as a service. +// Kills: +// - Rdst_value +// - Rdst_address +void InterpreterMacroAssembler::load_local_long(Register Rdst_value, Register Rdst_address, Register Rindex) { + sldi(Rdst_address, Rindex, Interpreter::logStackElementSize); + subf(Rdst_address, Rdst_address, R18_locals); + ld(Rdst_value, -8, Rdst_address); +} + +// Load a local variable at index in Rindex into register Rdst_value. +// Also puts address of local into Rdst_address as a service. +// Input: +// - Rindex: slot nr of local variable +// Kills: +// - Rdst_value +// - Rdst_address +void InterpreterMacroAssembler::load_local_ptr(Register Rdst_value, Register Rdst_address, Register Rindex) { + sldi(Rdst_address, Rindex, Interpreter::logStackElementSize); + subf(Rdst_address, Rdst_address, R18_locals); + ld(Rdst_value, 0, Rdst_address); +} + +// Load a local variable at index in Rindex into register Rdst_value. +// Also puts address of local into Rdst_address as a service. +// Kills: +// - Rdst_value +// - Rdst_address +void InterpreterMacroAssembler::load_local_float(FloatRegister Rdst_value, Register Rdst_address, Register Rindex) { + sldi(Rdst_address, Rindex, Interpreter::logStackElementSize); + subf(Rdst_address, Rdst_address, R18_locals); + lfs(Rdst_value, 0, Rdst_address); +} + +// Load a local variable at index in Rindex into register Rdst_value. +// Also puts address of local into Rdst_address as a service. +// Kills: +// - Rdst_value +// - Rdst_address +void InterpreterMacroAssembler::load_local_double(FloatRegister Rdst_value, Register Rdst_address, Register Rindex) { + sldi(Rdst_address, Rindex, Interpreter::logStackElementSize); + subf(Rdst_address, Rdst_address, R18_locals); + lfd(Rdst_value, -8, Rdst_address); +} + +// Store an int value at local variable slot Rindex. +// Kills: +// - Rindex +void InterpreterMacroAssembler::store_local_int(Register Rvalue, Register Rindex) { + sldi(Rindex, Rindex, Interpreter::logStackElementSize); + subf(Rindex, Rindex, R18_locals); + stw(Rvalue, 0, Rindex); +} + +// Store a long value at local variable slot Rindex. +// Kills: +// - Rindex +void InterpreterMacroAssembler::store_local_long(Register Rvalue, Register Rindex) { + sldi(Rindex, Rindex, Interpreter::logStackElementSize); + subf(Rindex, Rindex, R18_locals); + std(Rvalue, -8, Rindex); +} + +// Store an oop value at local variable slot Rindex. +// Kills: +// - Rindex +void InterpreterMacroAssembler::store_local_ptr(Register Rvalue, Register Rindex) { + sldi(Rindex, Rindex, Interpreter::logStackElementSize); + subf(Rindex, Rindex, R18_locals); + std(Rvalue, 0, Rindex); +} + +// Store an int value at local variable slot Rindex. +// Kills: +// - Rindex +void InterpreterMacroAssembler::store_local_float(FloatRegister Rvalue, Register Rindex) { + sldi(Rindex, Rindex, Interpreter::logStackElementSize); + subf(Rindex, Rindex, R18_locals); + stfs(Rvalue, 0, Rindex); +} + +// Store an int value at local variable slot Rindex. +// Kills: +// - Rindex +void InterpreterMacroAssembler::store_local_double(FloatRegister Rvalue, Register Rindex) { + sldi(Rindex, Rindex, Interpreter::logStackElementSize); + subf(Rindex, Rindex, R18_locals); + stfd(Rvalue, -8, Rindex); +} + +// Read pending exception from thread and jump to interpreter. +// Throw exception entry if one if pending. Fall through otherwise. +void InterpreterMacroAssembler::check_and_forward_exception(Register Rscratch1, Register Rscratch2) { + assert_different_registers(Rscratch1, Rscratch2, R3); + Register Rexception = Rscratch1; + Register Rtmp = Rscratch2; + Label Ldone; + // Get pending exception oop. + ld(Rexception, thread_(pending_exception)); + cmpdi(CCR0, Rexception, 0); + beq(CCR0, Ldone); + li(Rtmp, 0); + mr_if_needed(R3, Rexception); + std(Rtmp, thread_(pending_exception)); // Clear exception in thread + if (Interpreter::rethrow_exception_entry() != NULL) { + // Already got entry address. + load_dispatch_table(Rtmp, (address*)Interpreter::rethrow_exception_entry()); + } else { + // Dynamically load entry address. + int simm16_rest = load_const_optimized(Rtmp, &Interpreter::_rethrow_exception_entry, R0, true); + ld(Rtmp, simm16_rest, Rtmp); + } + mtctr(Rtmp); + save_interpreter_state(Rtmp); + bctr(); + + align(32, 12); + bind(Ldone); +} + +void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { + save_interpreter_state(R11_scratch1); + + MacroAssembler::call_VM(oop_result, entry_point, false); + + restore_interpreter_state(R11_scratch1, /*bcp_and_mdx_only*/ true); + + check_and_handle_popframe(R11_scratch1); + check_and_handle_earlyret(R11_scratch1); + // Now check exceptions manually. + if (check_exceptions) { + check_and_forward_exception(R11_scratch1, R12_scratch2); + } +} + +void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions) { + // ARG1 is reserved for the thread. + mr_if_needed(R4_ARG2, arg_1); + call_VM(oop_result, entry_point, check_exceptions); +} + +void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { + // ARG1 is reserved for the thread. + mr_if_needed(R4_ARG2, arg_1); + assert(arg_2 != R4_ARG2, "smashed argument"); + mr_if_needed(R5_ARG3, arg_2); + call_VM(oop_result, entry_point, check_exceptions); +} + +void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) { + // ARG1 is reserved for the thread. + mr_if_needed(R4_ARG2, arg_1); + assert(arg_2 != R4_ARG2, "smashed argument"); + mr_if_needed(R5_ARG3, arg_2); + assert(arg_3 != R4_ARG2 && arg_3 != R5_ARG3, "smashed argument"); + mr_if_needed(R6_ARG4, arg_3); + call_VM(oop_result, entry_point, check_exceptions); +} + +void InterpreterMacroAssembler::save_interpreter_state(Register scratch) { + ld(scratch, 0, R1_SP); + std(R15_esp, _ijava_state_neg(esp), scratch); + std(R14_bcp, _ijava_state_neg(bcp), scratch); + std(R26_monitor, _ijava_state_neg(monitors), scratch); + if (ProfileInterpreter) { std(R28_mdx, _ijava_state_neg(mdx), scratch); } + // Other entries should be unchanged. +} + + +void InterpreterMacroAssembler::restore_interpreter_state(Register scratch, bool oops_only) { + ld(scratch, 0, R1_SP); + ld(R19_method, _ijava_state_neg(method), scratch); + ld(R27_constPoolCache, _ijava_state_neg(cpoolCache), scratch); + ld(R14_bcp, _ijava_state_neg(bcp), scratch); // related to method + ld(R28_mdx, _ijava_state_neg(mdx), scratch); // related to method + if (!oops_only) { // Following ones are stack addresses and don't require reload after GC. + ld(R18_locals, _ijava_state_neg(locals), scratch); + ld(R26_monitor, _ijava_state_neg(monitors), scratch); + ld(R15_esp, _ijava_state_neg(esp), scratch); + } +#ifdef ASSERT + { + Label Lok; + subf(R0, R1_SP, scratch); + cmpdi(CCR0, R0, frame::abi_reg_args_size + frame::ijava_state_size); + bge(CCR0, Lok); + stop("frame too small (restore istate)", 0x5432); + bind(Lok); + } + { + Label Lok; + ld(R0, _ijava_state_neg(ijava_reserved), scratch); + cmpdi(CCR0, R0, 0x5afe); + beq(CCR0, Lok); + stop("frame corrupted (restore istate)", 0x5afe); + bind(Lok); + } +#endif +} + +#endif // !CC_INTERP + +void InterpreterMacroAssembler::get_method_counters(Register method, + Register Rcounters, + Label& skip) { +#if 0 + BLOCK_COMMENT("Load and ev. allocate counter object {"); + Label has_counters; + ld(Rcounters, in_bytes(Method::method_counters_offset()), method); + cmpdi(CCR0, Rcounters, 0); + bne(CCR0, has_counters); + call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::build_method_counters), method, false); + ld(Rcounters, in_bytes(Method::method_counters_offset()), method); + cmpdi(CCR0, Rcounters, 0); + beq(CCR0, skip); // No MethodCounters, OutOfMemory. + BLOCK_COMMENT("} Load and ev. allocate counter object"); + + bind(has_counters); +#endif +} + +void InterpreterMacroAssembler::increment_invocation_counter(Register Rtmp, Register Rtmp2) { + assert(UseCompiler, "incrementing must be useful"); + // Address inv_counter(Lmethod, 0, in_bytes(methodOopDesc::invocation_counter_offset() + // + InvocationCounter::counter_offset())); + // Address be_counter(Lmethod, 0, in_bytes(methodOopDesc::backedge_counter_offset() + // + InvocationCounter::counter_offset())); + int delta = InvocationCounter::count_increment; + + // Load each counter in a register. + // ld(inv_counter, Rtmp); + // ld(be_counter, Rtmp2); + int inv_counter_offset = in_bytes(methodOopDesc::invocation_counter_offset() + + InvocationCounter::counter_offset()); + int be_counter_offset = in_bytes(methodOopDesc::backedge_counter_offset() + + InvocationCounter::counter_offset()); + + BLOCK_COMMENT("Increment profiling counters {"); + + // Load the backedge counter. + lwz(Rtmp2, be_counter_offset, R19_method); // is unsigned int + // Mask the backedge counter. + li(Rtmp, InvocationCounter::count_mask_value); + andr(Rtmp2, Rtmp, Rtmp2); // Cannot use andi, need sign extension of count_mask_value. + + // Load the invocation counter. + lwz(Rtmp, inv_counter_offset, R19_method); // is unsigned int + // Add the delta to the invocation counter and store the result. + addi(Rtmp, Rtmp, delta); + // Store value. + stw(Rtmp, inv_counter_offset, R19_method); + + // Add invocation counter + backedge counter. + add(Rtmp, Rtmp2, Rtmp); + + // Note that this macro must leave the backedge_count + invocation_count in Rtmp! + BLOCK_COMMENT("} Increment profiling counters"); +} + +void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) { + if (state == atos) { MacroAssembler::verify_oop(reg); } +} + +#ifndef CC_INTERP +// Local helper function for the verify_oop_or_return_address macro. +static bool verify_return_address(methodOop m, int bci) { +#ifndef PRODUCT + address pc = (address)(m->constMethod()) + in_bytes(constMethodOopDesc::codes_offset()) + bci; + // Assume it is a valid return address if it is inside m and is preceded by a jsr. + if (!m->contains(pc)) return false; + address jsr_pc; + jsr_pc = pc - Bytecodes::length_for(Bytecodes::_jsr); + if (*jsr_pc == Bytecodes::_jsr && jsr_pc >= m->code_base()) return true; + jsr_pc = pc - Bytecodes::length_for(Bytecodes::_jsr_w); + if (*jsr_pc == Bytecodes::_jsr_w && jsr_pc >= m->code_base()) return true; +#endif // PRODUCT + return false; +} + +void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) { + if (VerifyFPU) { + unimplemented("verfiyFPU"); + } +} + +void InterpreterMacroAssembler::verify_oop_or_return_address(Register reg, Register Rtmp) { + if (!VerifyOops) return; + + // The VM documentation for the astore[_wide] bytecode allows + // the TOS to be not only an oop but also a return address. + Label test; + Label skip; + // See if it is an address (in the current method): + + const int log2_bytecode_size_limit = 16; + srdi_(Rtmp, reg, log2_bytecode_size_limit); + bne(CCR0, test); + + address fd = CAST_FROM_FN_PTR(address, verify_return_address); + unsigned int nbytes_save = 10*8; // 10 volatile gprs + + save_LR_CR(Rtmp); + push_frame_reg_args(nbytes_save, Rtmp); + save_volatile_gprs(R1_SP, 112); // except R0 + + load_const_optimized(Rtmp, fd, R0); + mr_if_needed(R4_ARG2, reg); + mr(R3_ARG1, R19_method); + call_c(Rtmp); // call C + + restore_volatile_gprs(R1_SP, 112); // except R0 + pop_frame(); + restore_LR_CR(Rtmp); + b(skip); + + // Perform a more elaborate out-of-line call. + // Not an address; verify it: + bind(test); + verify_oop(reg); + bind(skip); +} +#endif // !CC_INTERP + +// Inline assembly for: +// +// if (thread is in interp_only_mode) { +// InterpreterRuntime::post_method_entry(); +// } +// if (*jvmpi::event_flags_array_at_addr(JVMPI_EVENT_METHOD_ENTRY ) || +// *jvmpi::event_flags_array_at_addr(JVMPI_EVENT_METHOD_ENTRY2) ) { +// SharedRuntime::jvmpi_method_entry(method, receiver); +// } +void InterpreterMacroAssembler::notify_method_entry() { + // JVMTI + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack + // depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (JvmtiExport::can_post_interpreter_events()) { + Label jvmti_post_done; + + lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread); + cmpwi(CCR0, R0, 0); + beq(CCR0, jvmti_post_done); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry), + /*check_exceptions=*/true CC_INTERP_ONLY(&& false)); + + bind(jvmti_post_done); + } +} + +// Inline assembly for: +// +// if (thread is in interp_only_mode) { +// // save result +// InterpreterRuntime::post_method_exit(); +// // restore result +// } +// if (*jvmpi::event_flags_array_at_addr(JVMPI_EVENT_METHOD_EXIT)) { +// // save result +// SharedRuntime::jvmpi_method_exit(); +// // restore result +// } +// +// Native methods have their result stored in d_tmp and l_tmp. +// Java methods have their result stored in the expression stack. +void InterpreterMacroAssembler::notify_method_exit(bool is_native_method, TosState state, + NotifyMethodExitMode mode, bool check_exceptions) { + // JVMTI + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack + // depth. If it is possible to enter interp_only_mode we add + // the code to check if the event should be sent. + if (mode == NotifyJVMTI && JvmtiExport::can_post_interpreter_events()) { + Label jvmti_post_done; + + lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread); + cmpwi(CCR0, R0, 0); + beq(CCR0, jvmti_post_done); + CC_INTERP_ONLY(assert(is_native_method && !check_exceptions, "must not push state")); + if (!is_native_method) push(state); // Expose tos to GC. + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit), + /*check_exceptions=*/check_exceptions); + if (!is_native_method) pop(state); + + align(32, 12); + bind(jvmti_post_done); + } + + // Dtrace support not implemented. +} + +#ifdef CC_INTERP +// Convert the current TOP_IJAVA_FRAME into a PARENT_IJAVA_FRAME +// (using parent_frame_resize) and push a new interpreter +// TOP_IJAVA_FRAME (using frame_size). +void InterpreterMacroAssembler::push_interpreter_frame(Register top_frame_size, Register parent_frame_resize, + Register tmp1, Register tmp2, Register tmp3, + Register tmp4, Register pc) { + assert_different_registers(top_frame_size, parent_frame_resize, tmp1, tmp2, tmp3, tmp4); + ld(tmp1, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + mr(tmp2/*top_frame_sp*/, R1_SP); + // Move initial_caller_sp. + ld(tmp4, _top_ijava_frame_abi(initial_caller_sp), R1_SP); + neg(parent_frame_resize, parent_frame_resize); + resize_frame(parent_frame_resize/*-parent_frame_resize*/, tmp3); + + // Set LR in new parent frame. + std(tmp1, _abi(lr), R1_SP); + // Set top_frame_sp info for new parent frame. + std(tmp2, _parent_ijava_frame_abi(top_frame_sp), R1_SP); + std(tmp4, _parent_ijava_frame_abi(initial_caller_sp), R1_SP); + + // Push new TOP_IJAVA_FRAME. + push_frame(top_frame_size, tmp2); + + get_PC_trash_LR(tmp3); + std(tmp3, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + // Used for non-initial callers by unextended_sp(). + std(R1_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP); +} + +// Pop the topmost TOP_IJAVA_FRAME and convert the previous +// PARENT_IJAVA_FRAME back into a TOP_IJAVA_FRAME. +void InterpreterMacroAssembler::pop_interpreter_frame(Register tmp1, Register tmp2, Register tmp3, Register tmp4) { + assert_different_registers(tmp1, tmp2, tmp3, tmp4); + + ld(tmp1/*caller's sp*/, _abi(callers_sp), R1_SP); + ld(tmp3, _abi(lr), tmp1); + + ld(tmp4, _parent_ijava_frame_abi(initial_caller_sp), tmp1); + + ld(tmp2/*caller's caller's sp*/, _abi(callers_sp), tmp1); + // Merge top frame. + std(tmp2, _abi(callers_sp), R1_SP); + + ld(tmp2, _parent_ijava_frame_abi(top_frame_sp), tmp1); + + // Update C stack pointer to caller's top_abi. + resize_frame_absolute(tmp2/*addr*/, tmp1/*tmp*/, tmp2/*tmp*/); + + // Update LR in top_frame. + std(tmp3, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + + std(tmp4, _top_ijava_frame_abi(initial_caller_sp), R1_SP); + + // Store the top-frame stack-pointer for c2i adapters. + std(R1_SP, _top_ijava_frame_abi(top_frame_sp), R1_SP); +} + +// Turn state's interpreter frame into the current TOP_IJAVA_FRAME. +void InterpreterMacroAssembler::pop_interpreter_frame_to_state(Register state, Register tmp1, Register tmp2, Register tmp3) { + assert_different_registers(R14_state, R15_prev_state, tmp1, tmp2, tmp3); + + if (state == R14_state) { + ld(tmp1/*state's fp*/, state_(_last_Java_fp)); + ld(tmp2/*state's sp*/, state_(_last_Java_sp)); + } else if (state == R15_prev_state) { + ld(tmp1/*state's fp*/, prev_state_(_last_Java_fp)); + ld(tmp2/*state's sp*/, prev_state_(_last_Java_sp)); + } else { + ShouldNotReachHere(); + } + + // Merge top frames. + std(tmp1, _abi(callers_sp), R1_SP); + + // Tmp2 is new SP. + // Tmp1 is parent's SP. + resize_frame_absolute(tmp2/*addr*/, tmp1/*tmp*/, tmp2/*tmp*/); + + // Update LR in top_frame. + // Must be interpreter frame. + get_PC_trash_LR(tmp3); + std(tmp3, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + // Used for non-initial callers by unextended_sp(). + std(R1_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP); +} + +// Set SP to initial caller's sp, but before fix the back chain. +void InterpreterMacroAssembler::resize_frame_to_initial_caller(Register tmp1, Register tmp2) { + ld(tmp1, _parent_ijava_frame_abi(initial_caller_sp), R1_SP); + ld(tmp2, _parent_ijava_frame_abi(callers_sp), R1_SP); + std(tmp2, _parent_ijava_frame_abi(callers_sp), tmp1); // Fix back chain ... + mr(R1_SP, tmp1); // ... and resize to initial caller. +} + +// Pop the current interpreter state (without popping the correspoding +// frame) and restore R14_state and R15_prev_state accordingly. +// Use prev_state_may_be_0 to indicate whether prev_state may be 0 +// in order to generate an extra check before retrieving prev_state_(_prev_link). +void InterpreterMacroAssembler::pop_interpreter_state(bool prev_state_may_be_0) +{ + // Move prev_state to state and restore prev_state from state_(_prev_link). + Label prev_state_is_0; + mr(R14_state, R15_prev_state); + + // Don't retrieve /*state==*/prev_state_(_prev_link) + // if /*state==*/prev_state is 0. + if (prev_state_may_be_0) { + cmpdi(CCR0, R15_prev_state, 0); + beq(CCR0, prev_state_is_0); + } + + ld(R15_prev_state, /*state==*/prev_state_(_prev_link)); + bind(prev_state_is_0); +} + +void InterpreterMacroAssembler::restore_prev_state() { + // _prev_link is private, but cInterpreter is a friend. + ld(R15_prev_state, state_(_prev_link)); +} +#endif // CC_INTERP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interp_masm_ppc_64.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interp_masm_ppc_64.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_INTERP_MASM_PPC_64_HPP +#define CPU_PPC_VM_INTERP_MASM_PPC_64_HPP + +#include "assembler_ppc.inline.hpp" +#include "interpreter/invocationCounter.hpp" + +// This file specializes the assembler with interpreter-specific macros. + + +class InterpreterMacroAssembler: public MacroAssembler { + + public: + InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} + + void null_check_throw(Register a, int offset, Register temp_reg); + + void branch_to_entry(address entry, Register Rscratch); + + // Handy address generation macros. +#define thread_(field_name) in_bytes(JavaThread::field_name ## _offset()), R16_thread +#define method_(field_name) in_bytes(methodOopDesc::field_name ## _offset()), R19_method + +#ifdef CC_INTERP +#define state_(field_name) in_bytes(byte_offset_of(BytecodeInterpreter, field_name)), R14_state +#define prev_state_(field_name) in_bytes(byte_offset_of(BytecodeInterpreter, field_name)), R15_prev_state + void pop (TosState state) {}; // Not needed. + void push(TosState state) {}; // Not needed. +#endif + +#ifndef CC_INTERP + virtual void check_and_handle_popframe(Register java_thread); + virtual void check_and_handle_earlyret(Register java_thread); + + // Base routine for all dispatches. + void dispatch_base(TosState state, address* table); + + void load_earlyret_value(TosState state, Register Rscratch1); + + static const Address l_tmp; + static const Address d_tmp; + + // dispatch routines + void dispatch_next(TosState state, int step = 0); + void dispatch_via (TosState state, address* table); + void load_dispatch_table(Register dst, address* table); + void dispatch_Lbyte_code(TosState state, Register bytecode, address* table, bool verify = false); + + // Called by shared interpreter generator. + void dispatch_prolog(TosState state, int step = 0); + void dispatch_epilog(TosState state, int step = 0); + + // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls. + void super_call_VM_leaf(Register thread_cache, address entry_point, Register arg_1); + void super_call_VM(Register thread_cache, Register oop_result, Register last_java_sp, + address entry_point, Register arg_1, Register arg_2, bool check_exception = true); + + // Generate a subtype check: branch to ok_is_subtype if sub_klass is + // a subtype of super_klass. Blows registers tmp1, tmp2 and tmp3. + void gen_subtype_check(Register sub_klass, Register super_klass, + Register tmp1, Register tmp2, Register tmp3, Label &ok_is_subtype); + + // Load object from cpool->resolved_references(index). + void load_resolved_reference_at_index(Register result, Register index); + + void generate_stack_overflow_check_with_compare_and_throw(Register Rmem_frame_size, Register Rscratch1); + void load_receiver(Register Rparam_count, Register Rrecv_dst); + + // helpers for expression stack + void pop_i( Register r = R17_tos); + void pop_ptr( Register r = R17_tos); + void pop_l( Register r = R17_tos); + void pop_f(FloatRegister f = F15_ftos); + void pop_d(FloatRegister f = F15_ftos ); + + void push_i( Register r = R17_tos); + void push_ptr( Register r = R17_tos); + void push_l( Register r = R17_tos); + void push_f(FloatRegister f = F15_ftos ); + void push_d(FloatRegister f = F15_ftos); + + void push_2ptrs(Register first, Register second); + + void push_l_pop_d(Register l = R17_tos, FloatRegister d = F15_ftos); + void push_d_pop_l(FloatRegister d = F15_ftos, Register l = R17_tos); + + void pop (TosState state); // transition vtos -> state + void push(TosState state); // transition state -> vtos + void empty_expression_stack(); // Resets both Lesp and SP. + + public: + // Load values from bytecode stream: + + enum signedOrNot { Signed, Unsigned }; + enum setCCOrNot { set_CC, dont_set_CC }; + + void get_2_byte_integer_at_bcp(int bcp_offset, + Register Rdst, + signedOrNot is_signed); + + void get_4_byte_integer_at_bcp(int bcp_offset, + Register Rdst, + signedOrNot is_signed = Unsigned); + + void get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size); + + void get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size = sizeof(u2)); + + void get_u4(Register Rdst, Register Rsrc, int offset, signedOrNot is_signed); + + // common code + + void field_offset_at(int n, Register tmp, Register dest, Register base); + int field_offset_at(Register object, address bcp, int offset); + void fast_iaaccess(int n, address bcp); + void fast_iagetfield(address bcp); + void fast_iaputfield(address bcp, bool do_store_check); + + void index_check(Register array, Register index, int index_shift, Register tmp, Register res); + void index_check_without_pop(Register array, Register index, int index_shift, Register tmp, Register res); + + void get_const(Register Rdst); + void get_constant_pool(Register Rdst); + void get_constant_pool_cache(Register Rdst); + void get_cpool_and_tags(Register Rcpool, Register Rtags); + void is_a(Label& L); + + void narrow(Register result); + + // Java Call Helpers + void call_from_interpreter(Register Rtarget_method, Register Rret_addr, Register Rscratch1, Register Rscratch2); + + // -------------------------------------------------- + + void unlock_if_synchronized_method(TosState state, bool throw_monitor_exception = true, + bool install_monitor_exception = true); + + // Removes the current activation (incl. unlocking of monitors). + // Additionally this code is used for earlyReturn in which case we + // want to skip throwing an exception and installing an exception. + void remove_activation(TosState state, + bool throw_monitor_exception = true, + bool install_monitor_exception = true); + void merge_frames(Register Rtop_frame_sp, Register return_pc, Register Rscratch1, Register Rscratch2); // merge top frames + + void add_monitor_to_stack(bool stack_is_empty, Register Rtemp1, Register Rtemp2); + + // Local variable access helpers + void load_local_int(Register Rdst_value, Register Rdst_address, Register Rindex); + void load_local_long(Register Rdst_value, Register Rdst_address, Register Rindex); + void load_local_ptr(Register Rdst_value, Register Rdst_address, Register Rindex); + void load_local_float(FloatRegister Rdst_value, Register Rdst_address, Register Rindex); + void load_local_double(FloatRegister Rdst_value, Register Rdst_address, Register Rindex); + void store_local_int(Register Rvalue, Register Rindex); + void store_local_long(Register Rvalue, Register Rindex); + void store_local_ptr(Register Rvalue, Register Rindex); + void store_local_float(FloatRegister Rvalue, Register Rindex); + void store_local_double(FloatRegister Rvalue, Register Rindex); + + // Call VM for std frames + // Special call VM versions that check for exceptions and forward exception + // via short cut (not via expensive forward exception stub). + void check_and_forward_exception(Register Rscratch1, Register Rscratch2); + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true); + // Should not be used: + void call_VM(Register oop_result, Register last_java_sp, address entry_point, bool check_exceptions = true) {ShouldNotReachHere();} + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions = true) {ShouldNotReachHere();} + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true) {ShouldNotReachHere();} + void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true) {ShouldNotReachHere();} + + Address first_local_in_stack(); + + enum LoadOrStore { load, store }; + void static_iload_or_store(int which_local, LoadOrStore direction, Register Rtmp); + void static_aload_or_store(int which_local, LoadOrStore direction, Register Rtmp); + void static_dload_or_store(int which_local, LoadOrStore direction); + + void save_interpreter_state(Register scratch); + void restore_interpreter_state(Register scratch, bool bcp_and_mdx_only = false); + + void increment_backedge_counter(Register Rtmp, Register Rtmp2, Register Rscratch); + void test_backedge_count_for_osr(Register backedge_count, Register branch_bcp, Register Rtmp); + + void record_static_call_in_profile(Register Rentry, Register Rtmp); + void record_receiver_call_in_profile(Register Rklass, Register Rentry, Register Rtmp); +#endif // !CC_INTERP + + void get_method_counters(Register method, Register Rcounters, Label& skip); + void increment_invocation_counter(Register Rtmp, Register Rtmp2); + + // Object locking + void lock_object (Register lock_reg, Register obj_reg); + void unlock_object(Register lock_reg, bool check_for_exceptions = true); + +#ifndef CC_INTERP + + // Interpreter profiling operations + void set_method_data_pointer_for_bcp(); + void test_method_data_pointer(Label& zero_continue); + void verify_method_data_pointer(); + void test_invocation_counter_for_mdp(Register invocation_count, Register Rscratch, Label &profile_continue); + + void set_mdp_data_at(int constant, Register value); + + void increment_mdp_data_at(int constant, Register counter_addr, Register Rbumped_count, bool decrement = false); + + void increment_mdp_data_at(Register counter_addr, Register Rbumped_count, bool decrement = false); + void increment_mdp_data_at(Register reg, int constant, Register scratch, Register Rbumped_count, bool decrement = false); + + void set_mdp_flag_at(int flag_constant, Register scratch); + void test_mdp_data_at(int offset, Register value, Label& not_equal_continue, Register test_out); + + void update_mdp_by_offset(int offset_of_disp, Register scratch); + void update_mdp_by_offset(Register reg, int offset_of_disp, + Register scratch); + void update_mdp_by_constant(int constant); + void update_mdp_for_ret(TosState state, Register return_bci); + + void profile_taken_branch(Register scratch, Register bumped_count); + void profile_not_taken_branch(Register scratch1, Register scratch2); + void profile_call(Register scratch1, Register scratch2); + void profile_final_call(Register scratch1, Register scratch2); + void profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2, bool receiver_can_be_null); + void profile_typecheck(Register Rklass, Register Rscratch1, Register Rscratch2); + void profile_typecheck_failed(Register Rscratch1, Register Rscratch2); + void profile_ret(TosState state, Register return_bci, Register scratch1, Register scratch2); + void profile_switch_default(Register scratch1, Register scratch2); + void profile_switch_case(Register index, Register scratch1,Register scratch2, Register scratch3); + void profile_null_seen(Register Rscratch1, Register Rscratch2); + void record_klass_in_profile(Register receiver, Register scratch1, Register scratch2, bool is_virtual_call); + void record_klass_in_profile_helper(Register receiver, Register scratch1, Register scratch2, int start_row, Label& done, bool is_virtual_call); + +#endif // !CC_INTERP + + // Debugging + void verify_oop(Register reg, TosState state = atos); // only if +VerifyOops && state == atos +#ifndef CC_INTERP + void verify_oop_or_return_address(Register reg, Register rtmp); // for astore + void verify_FPU(int stack_depth, TosState state = ftos); +#endif // !CC_INTERP + + typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode; + + // Support for jvmdi/jvmpi. + void notify_method_entry(); + void notify_method_exit(bool is_native_method, TosState state, + NotifyMethodExitMode mode, bool check_exceptions); + +#ifdef CC_INTERP + // Convert the current TOP_IJAVA_FRAME into a PARENT_IJAVA_FRAME + // (using parent_frame_resize) and push a new interpreter + // TOP_IJAVA_FRAME (using frame_size). + void push_interpreter_frame(Register top_frame_size, Register parent_frame_resize, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register pc=noreg); + + // Pop the topmost TOP_IJAVA_FRAME and convert the previous + // PARENT_IJAVA_FRAME back into a TOP_IJAVA_FRAME. + void pop_interpreter_frame(Register tmp1, Register tmp2, Register tmp3, Register tmp4); + + // Turn state's interpreter frame into the current TOP_IJAVA_FRAME. + void pop_interpreter_frame_to_state(Register state, Register tmp1, Register tmp2, Register tmp3); + + // Set SP to initial caller's sp, but before fix the back chain. + void resize_frame_to_initial_caller(Register tmp1, Register tmp2); + + // Pop the current interpreter state (without popping the + // correspoding frame) and restore R14_state and R15_prev_state + // accordingly. Use prev_state_may_be_0 to indicate whether + // prev_state may be 0 in order to generate an extra check before + // retrieving prev_state_(_prev_link). + void pop_interpreter_state(bool prev_state_may_be_0); + + void restore_prev_state(); +#endif +}; + +#endif // CPU_PPC_VM_INTERP_MASM_PPC_64_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interpreterGenerator_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interpreterGenerator_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_INTERPRETERGENERATOR_PPC_HPP +#define CPU_PPC_VM_INTERPRETERGENERATOR_PPC_HPP + + friend class AbstractInterpreterGenerator; + + private: + + address generate_abstract_entry(void); + address generate_accessor_entry(void); + address generate_Reference_get_entry(void); + +#endif // CPU_PPC_VM_INTERPRETERGENERATOR_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interpreterRT_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interpreterRT_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/universe.inline.hpp" +#include "oops/methodOop.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/icache.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/signature.hpp" + +#define __ _masm-> + +// Access macros for Java and C arguments. +// The first Java argument is at index -1. +#define locals_j_arg_at(index) (Interpreter::local_offset_in_bytes(index)), R18_locals +// The first C argument is at index 0. +#define sp_c_arg_at(index) ((index)*wordSize + _abi(carg_1)), R1_SP + +// Implementation of SignatureHandlerGenerator + +void InterpreterRuntime::SignatureHandlerGenerator::pass_int() { + Argument jni_arg(jni_offset()); + Register r = jni_arg.is_register() ? jni_arg.as_register() : R0; + + __ lwa(r, locals_j_arg_at(offset())); // sign extension of integer + if (DEBUG_ONLY(true ||) !jni_arg.is_register()) { + __ std(r, sp_c_arg_at(jni_arg.number())); + } +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_long() { + Argument jni_arg(jni_offset()); + Register r = jni_arg.is_register() ? jni_arg.as_register() : R0; + + __ ld(r, locals_j_arg_at(offset()+1)); // long resides in upper slot + if (DEBUG_ONLY(true ||) !jni_arg.is_register()) { + __ std(r, sp_c_arg_at(jni_arg.number())); + } +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_float() { + FloatRegister fp_reg = (_num_used_fp_arg_regs < 13/*max_fp_register_arguments*/) + ? as_FloatRegister((_num_used_fp_arg_regs++) + F1_ARG1->encoding()) + : F0; + + __ lfs(fp_reg, locals_j_arg_at(offset())); + if (DEBUG_ONLY(true ||) jni_offset() > 8) { + __ stfs(fp_reg, sp_c_arg_at(jni_offset())); + } +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_double() { + FloatRegister fp_reg = (_num_used_fp_arg_regs < 13/*max_fp_register_arguments*/) + ? as_FloatRegister((_num_used_fp_arg_regs++) + F1_ARG1->encoding()) + : F0; + + __ lfd(fp_reg, locals_j_arg_at(offset()+1)); + if (DEBUG_ONLY(true ||) jni_offset() > 8) { + __ stfd(fp_reg, sp_c_arg_at(jni_offset())); + } +} + +void InterpreterRuntime::SignatureHandlerGenerator::pass_object() { + Argument jni_arg(jni_offset()); + Register r = jni_arg.is_register() ? jni_arg.as_register() : R11_scratch1; + + // The handle for a receiver will never be null. + bool do_NULL_check = offset() != 0 || is_static(); + + Label do_null; + if (do_NULL_check) { + __ ld(R0, locals_j_arg_at(offset())); + __ cmpdi(CCR0, R0, 0); + __ li(r, 0); + __ beq(CCR0, do_null); + } + __ addir(r, locals_j_arg_at(offset())); + __ bind(do_null); + if (DEBUG_ONLY(true ||) !jni_arg.is_register()) { + __ std(r, sp_c_arg_at(jni_arg.number())); + } +} + +void InterpreterRuntime::SignatureHandlerGenerator::generate(uint64_t fingerprint) { +#if !defined(ABI_ELFv2) + // Emit fd for current codebuffer. Needs patching! + __ emit_fd(); +#endif + + // Generate code to handle arguments. + iterate(fingerprint); + + // Return the result handler. + __ load_const(R3_RET, AbstractInterpreter::result_handler(method()->result_type())); + __ blr(); + + __ flush(); +} + +#undef __ + +// Implementation of SignatureHandlerLibrary + +void SignatureHandlerLibrary::pd_set_handler(address handler) { +#if !defined(ABI_ELFv2) + // patch fd here. + FunctionDescriptor* fd = (FunctionDescriptor*) handler; + + fd->set_entry(handler + (int)sizeof(FunctionDescriptor)); + assert(fd->toc() == (address)0xcafe, "need to adjust TOC here"); +#endif +} + + +// Access function to get the signature. +IRT_ENTRY(address, InterpreterRuntime::get_signature(JavaThread* thread, methodOop method)) + methodHandle m(thread, method); + assert(m->is_native(), "sanity check"); + Symbol *s = m->signature(); + return (address) s->base(); +IRT_END + +IRT_ENTRY(address, InterpreterRuntime::get_result_handler(JavaThread* thread, methodOop method)) + methodHandle m(thread, method); + assert(m->is_native(), "sanity check"); + return AbstractInterpreter::result_handler(m->result_type()); +IRT_END diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interpreterRT_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interpreterRT_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_INTERPRETERRT_PPC_HPP +#define CPU_PPC_VM_INTERPRETERRT_PPC_HPP + +#include "memory/allocation.hpp" + +// native method calls + +class SignatureHandlerGenerator: public NativeSignatureIterator { + private: + MacroAssembler* _masm; + // number of already used floating-point argument registers + int _num_used_fp_arg_regs; + + void pass_int(); + void pass_long(); + void pass_double(); + void pass_float(); + void pass_object(); + + public: + // Creation + SignatureHandlerGenerator(methodHandle method, CodeBuffer* buffer) : NativeSignatureIterator(method) { + _masm = new MacroAssembler(buffer); + _num_used_fp_arg_regs = 0; + } + + // Code generation + void generate(uint64_t fingerprint); +}; + +// Support for generate_slow_signature_handler. +static address get_result_handler(JavaThread* thread, methodOop method); + +// A function to get the signature. +static address get_signature(JavaThread* thread, methodOop method); + +#endif // CPU_PPC_VM_INTERPRETERRT_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interpreter_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interpreter_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,821 @@ +/* + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2015 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "interpreter/bytecodeHistogram.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterGenerator.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "interpreter/templateTable.hpp" +#include "oops/arrayOop.hpp" +#include "oops/methodDataOop.hpp" +#include "oops/methodOop.hpp" +#include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "prims/methodHandles.hpp" +#include "runtime/arguments.hpp" +#include "runtime/deoptimization.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/timer.hpp" +#include "runtime/vframeArray.hpp" +#include "utilities/debug.hpp" +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#endif + +#define __ _masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +int AbstractInterpreter::BasicType_as_index(BasicType type) { + int i = 0; + switch (type) { + case T_BOOLEAN: i = 0; break; + case T_CHAR : i = 1; break; + case T_BYTE : i = 2; break; + case T_SHORT : i = 3; break; + case T_INT : i = 4; break; + case T_LONG : i = 5; break; + case T_VOID : i = 6; break; + case T_FLOAT : i = 7; break; + case T_DOUBLE : i = 8; break; + case T_OBJECT : i = 9; break; + case T_ARRAY : i = 9; break; + default : ShouldNotReachHere(); + } + assert(0 <= i && i < AbstractInterpreter::number_of_result_handlers, "index out of bounds"); + return i; +} + +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + // Slow_signature handler that respects the PPC C calling conventions. + // + // We get called by the native entry code with our output register + // area == 8. First we call InterpreterRuntime::get_result_handler + // to copy the pointer to the signature string temporarily to the + // first C-argument and to return the result_handler in + // R3_RET. Since native_entry will copy the jni-pointer to the + // first C-argument slot later on, it is OK to occupy this slot + // temporarilly. Then we copy the argument list on the java + // expression stack into native varargs format on the native stack + // and load arguments into argument registers. Integer arguments in + // the varargs vector will be sign-extended to 8 bytes. + // + // On entry: + // R3_ARG1 - intptr_t* Address of java argument list in memory. + // R15_prev_state - BytecodeInterpreter* Address of interpreter state for + // this method + // R19_method + // + // On exit (just before return instruction): + // R3_RET - contains the address of the result_handler. + // R4_ARG2 - is not updated for static methods and contains "this" otherwise. + // R5_ARG3-R10_ARG8: - When the (i-2)th Java argument is not of type float or double, + // ARGi contains this argument. Otherwise, ARGi is not updated. + // F1_ARG1-F13_ARG13 - contain the first 13 arguments of type float or double. + + const int LogSizeOfTwoInstructions = 3; + + // FIXME: use Argument:: GL: Argument names different numbers! + const int max_fp_register_arguments = 13; + const int max_int_register_arguments = 6; // first 2 are reserved + + const Register arg_java = R21_tmp1; + const Register arg_c = R22_tmp2; + const Register signature = R23_tmp3; // is string + const Register sig_byte = R24_tmp4; + const Register fpcnt = R25_tmp5; + const Register argcnt = R26_tmp6; + const Register intSlot = R27_tmp7; + const Register target_sp = R28_tmp8; + const FloatRegister floatSlot = F0; + + address entry = __ function_entry(); + + __ save_LR_CR(R0); + __ save_nonvolatile_gprs(R1_SP, _spill_nonvolatiles_neg(r14)); + // We use target_sp for storing arguments in the C frame. + __ mr(target_sp, R1_SP); + __ push_frame_reg_args_nonvolatiles(0, R11_scratch1); + + __ mr(arg_java, R3_ARG1); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::get_signature), R16_thread, R19_method); + + // Signature is in R3_RET. Signature is callee saved. + __ mr(signature, R3_RET); + + // Reload method, it may have moved. +#ifdef CC_INTERP + __ ld(R19_method, state_(_method)); +#else + __ ld(R19_method, 0, target_sp); + __ ld(R19_method, _ijava_state_neg(method), R19_method); +#endif + + // Get the result handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::get_result_handler), R16_thread, R19_method); + + // Reload method, it may have moved. +#ifdef CC_INTERP + __ ld(R19_method, state_(_method)); +#else + __ ld(R19_method, 0, target_sp); + __ ld(R19_method, _ijava_state_neg(method), R19_method); +#endif + + { + Label L; + // test if static + // _access_flags._flags must be at offset 0. + // TODO PPC port: requires change in shared code. + //assert(in_bytes(AccessFlags::flags_offset()) == 0, + // "MethodOopDesc._access_flags == MethodOopDesc._access_flags._flags"); + // _access_flags must be a 32 bit value. + assert(sizeof(AccessFlags) == 4, "wrong size"); + __ lwa(R11_scratch1/*access_flags*/, method_(access_flags)); + // testbit with condition register. + __ testbitdi(CCR0, R0, R11_scratch1/*access_flags*/, JVM_ACC_STATIC_BIT); + __ btrue(CCR0, L); + // For non-static functions, pass "this" in R4_ARG2 and copy it + // to 2nd C-arg slot. + // We need to box the Java object here, so we use arg_java + // (address of current Java stack slot) as argument and don't + // dereference it as in case of ints, floats, etc. + __ mr(R4_ARG2, arg_java); + __ addi(arg_java, arg_java, -BytesPerWord); + __ std(R4_ARG2, _abi(carg_2), target_sp); + __ bind(L); + } + + // Will be incremented directly after loop_start. argcnt=0 + // corresponds to 3rd C argument. + __ li(argcnt, -1); + // arg_c points to 3rd C argument + __ addi(arg_c, target_sp, _abi(carg_3)); + // no floating-point args parsed so far + __ li(fpcnt, 0); + + Label move_intSlot_to_ARG, move_floatSlot_to_FARG; + Label loop_start, loop_end; + Label do_int, do_long, do_float, do_double, do_dontreachhere, do_object, do_array, do_boxed; + + // signature points to '(' at entry +#ifdef ASSERT + __ lbz(sig_byte, 0, signature); + __ cmplwi(CCR0, sig_byte, '('); + __ bne(CCR0, do_dontreachhere); +#endif + + __ bind(loop_start); + + __ addi(argcnt, argcnt, 1); + __ lbzu(sig_byte, 1, signature); + + __ cmplwi(CCR0, sig_byte, ')'); // end of signature + __ beq(CCR0, loop_end); + + __ cmplwi(CCR0, sig_byte, 'B'); // byte + __ beq(CCR0, do_int); + + __ cmplwi(CCR0, sig_byte, 'C'); // char + __ beq(CCR0, do_int); + + __ cmplwi(CCR0, sig_byte, 'D'); // double + __ beq(CCR0, do_double); + + __ cmplwi(CCR0, sig_byte, 'F'); // float + __ beq(CCR0, do_float); + + __ cmplwi(CCR0, sig_byte, 'I'); // int + __ beq(CCR0, do_int); + + __ cmplwi(CCR0, sig_byte, 'J'); // long + __ beq(CCR0, do_long); + + __ cmplwi(CCR0, sig_byte, 'S'); // short + __ beq(CCR0, do_int); + + __ cmplwi(CCR0, sig_byte, 'Z'); // boolean + __ beq(CCR0, do_int); + + __ cmplwi(CCR0, sig_byte, 'L'); // object + __ beq(CCR0, do_object); + + __ cmplwi(CCR0, sig_byte, '['); // array + __ beq(CCR0, do_array); + + // __ cmplwi(CCR0, sig_byte, 'V'); // void cannot appear since we do not parse the return type + // __ beq(CCR0, do_void); + + __ bind(do_dontreachhere); + + __ unimplemented("ShouldNotReachHere in slow_signature_handler", 120); + + __ bind(do_array); + + { + Label start_skip, end_skip; + + __ bind(start_skip); + __ lbzu(sig_byte, 1, signature); + __ cmplwi(CCR0, sig_byte, '['); + __ beq(CCR0, start_skip); // skip further brackets + __ cmplwi(CCR0, sig_byte, '9'); + __ bgt(CCR0, end_skip); // no optional size + __ cmplwi(CCR0, sig_byte, '0'); + __ bge(CCR0, start_skip); // skip optional size + __ bind(end_skip); + + __ cmplwi(CCR0, sig_byte, 'L'); + __ beq(CCR0, do_object); // for arrays of objects, the name of the object must be skipped + __ b(do_boxed); // otherwise, go directly to do_boxed + } + + __ bind(do_object); + { + Label L; + __ bind(L); + __ lbzu(sig_byte, 1, signature); + __ cmplwi(CCR0, sig_byte, ';'); + __ bne(CCR0, L); + } + // Need to box the Java object here, so we use arg_java (address of + // current Java stack slot) as argument and don't dereference it as + // in case of ints, floats, etc. + Label do_null; + __ bind(do_boxed); + __ ld(R0,0, arg_java); + __ cmpdi(CCR0, R0, 0); + __ li(intSlot,0); + __ beq(CCR0, do_null); + __ mr(intSlot, arg_java); + __ bind(do_null); + __ std(intSlot, 0, arg_c); + __ addi(arg_java, arg_java, -BytesPerWord); + __ addi(arg_c, arg_c, BytesPerWord); + __ cmplwi(CCR0, argcnt, max_int_register_arguments); + __ blt(CCR0, move_intSlot_to_ARG); + __ b(loop_start); + + __ bind(do_int); + __ lwa(intSlot, 0, arg_java); + __ std(intSlot, 0, arg_c); + __ addi(arg_java, arg_java, -BytesPerWord); + __ addi(arg_c, arg_c, BytesPerWord); + __ cmplwi(CCR0, argcnt, max_int_register_arguments); + __ blt(CCR0, move_intSlot_to_ARG); + __ b(loop_start); + + __ bind(do_long); + __ ld(intSlot, -BytesPerWord, arg_java); + __ std(intSlot, 0, arg_c); + __ addi(arg_java, arg_java, - 2 * BytesPerWord); + __ addi(arg_c, arg_c, BytesPerWord); + __ cmplwi(CCR0, argcnt, max_int_register_arguments); + __ blt(CCR0, move_intSlot_to_ARG); + __ b(loop_start); + + __ bind(do_float); + __ lfs(floatSlot, 0, arg_java); +#if defined(LINUX) + // Linux uses ELF ABI. Both original ELF and ELFv2 ABIs have float + // in the least significant word of an argument slot. +#if defined(VM_LITTLE_ENDIAN) + __ stfs(floatSlot, 0, arg_c); +#else + __ stfs(floatSlot, 4, arg_c); +#endif +#elif defined(AIX) + // Although AIX runs on big endian CPU, float is in most significant + // word of an argument slot. + __ stfs(floatSlot, 0, arg_c); +#else +#error "unknown OS" +#endif + __ addi(arg_java, arg_java, -BytesPerWord); + __ addi(arg_c, arg_c, BytesPerWord); + __ cmplwi(CCR0, fpcnt, max_fp_register_arguments); + __ blt(CCR0, move_floatSlot_to_FARG); + __ b(loop_start); + + __ bind(do_double); + __ lfd(floatSlot, - BytesPerWord, arg_java); + __ stfd(floatSlot, 0, arg_c); + __ addi(arg_java, arg_java, - 2 * BytesPerWord); + __ addi(arg_c, arg_c, BytesPerWord); + __ cmplwi(CCR0, fpcnt, max_fp_register_arguments); + __ blt(CCR0, move_floatSlot_to_FARG); + __ b(loop_start); + + __ bind(loop_end); + + __ pop_frame(); + __ restore_nonvolatile_gprs(R1_SP, _spill_nonvolatiles_neg(r14)); + __ restore_LR_CR(R0); + + __ blr(); + + Label move_int_arg, move_float_arg; + __ bind(move_int_arg); // each case must consist of 2 instructions (otherwise adapt LogSizeOfTwoInstructions) + __ mr(R5_ARG3, intSlot); __ b(loop_start); + __ mr(R6_ARG4, intSlot); __ b(loop_start); + __ mr(R7_ARG5, intSlot); __ b(loop_start); + __ mr(R8_ARG6, intSlot); __ b(loop_start); + __ mr(R9_ARG7, intSlot); __ b(loop_start); + __ mr(R10_ARG8, intSlot); __ b(loop_start); + + __ bind(move_float_arg); // each case must consist of 2 instructions (otherwise adapt LogSizeOfTwoInstructions) + __ fmr(F1_ARG1, floatSlot); __ b(loop_start); + __ fmr(F2_ARG2, floatSlot); __ b(loop_start); + __ fmr(F3_ARG3, floatSlot); __ b(loop_start); + __ fmr(F4_ARG4, floatSlot); __ b(loop_start); + __ fmr(F5_ARG5, floatSlot); __ b(loop_start); + __ fmr(F6_ARG6, floatSlot); __ b(loop_start); + __ fmr(F7_ARG7, floatSlot); __ b(loop_start); + __ fmr(F8_ARG8, floatSlot); __ b(loop_start); + __ fmr(F9_ARG9, floatSlot); __ b(loop_start); + __ fmr(F10_ARG10, floatSlot); __ b(loop_start); + __ fmr(F11_ARG11, floatSlot); __ b(loop_start); + __ fmr(F12_ARG12, floatSlot); __ b(loop_start); + __ fmr(F13_ARG13, floatSlot); __ b(loop_start); + + __ bind(move_intSlot_to_ARG); + __ sldi(R0, argcnt, LogSizeOfTwoInstructions); + __ load_const(R11_scratch1, move_int_arg); // Label must be bound here. + __ add(R11_scratch1, R0, R11_scratch1); + __ mtctr(R11_scratch1/*branch_target*/); + __ bctr(); + __ bind(move_floatSlot_to_FARG); + __ sldi(R0, fpcnt, LogSizeOfTwoInstructions); + __ addi(fpcnt, fpcnt, 1); + __ load_const(R11_scratch1, move_float_arg); // Label must be bound here. + __ add(R11_scratch1, R0, R11_scratch1); + __ mtctr(R11_scratch1/*branch_target*/); + __ bctr(); + + return entry; +} + +address AbstractInterpreterGenerator::generate_result_handler_for(BasicType type) { + // + // Registers alive + // R3_RET + // LR + // + // Registers updated + // R3_RET + // + + Label done; + address entry = __ pc(); + + switch (type) { + case T_BOOLEAN: + // convert !=0 to 1 + __ neg(R0, R3_RET); + __ orr(R0, R3_RET, R0); + __ srwi(R3_RET, R0, 31); + break; + case T_BYTE: + // sign extend 8 bits + __ extsb(R3_RET, R3_RET); + break; + case T_CHAR: + // zero extend 16 bits + __ clrldi(R3_RET, R3_RET, 48); + break; + case T_SHORT: + // sign extend 16 bits + __ extsh(R3_RET, R3_RET); + break; + case T_INT: + // sign extend 32 bits + __ extsw(R3_RET, R3_RET); + break; + case T_LONG: + break; + case T_OBJECT: + // unbox result if not null + __ cmpdi(CCR0, R3_RET, 0); + __ beq(CCR0, done); + __ ld(R3_RET, 0, R3_RET); + __ verify_oop(R3_RET); + break; + case T_FLOAT: + break; + case T_DOUBLE: + break; + case T_VOID: + break; + default: ShouldNotReachHere(); + } + + __ BIND(done); + __ blr(); + + return entry; +} + +// Abstract method entry. +// +address InterpreterGenerator::generate_abstract_entry(void) { + address entry = __ pc(); + + // + // Registers alive + // R16_thread - JavaThread* + // R19_method - callee's method (method to be invoked) + // R1_SP - SP prepared such that caller's outgoing args are near top + // LR - return address to caller + // + // Stack layout at this point: + // + // 0 [TOP_IJAVA_FRAME_ABI] <-- R1_SP + // alignment (optional) + // [outgoing Java arguments] + // ... + // PARENT [PARENT_IJAVA_FRAME_ABI] + // ... + // + + // Can't use call_VM here because we have not set up a new + // interpreter state. Make the call to the vm and make it look like + // our caller set up the JavaFrameAnchor. + __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R12_scratch2/*tmp*/); + + // Push a new C frame and save LR. + __ save_LR_CR(R0); + __ push_frame_reg_args(0, R11_scratch1); + + // This is not a leaf but we have a JavaFrameAnchor now and we will + // check (create) exceptions afterward so this is ok. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError), + R16_thread); + + // Pop the C frame and restore LR. + __ pop_frame(); + __ restore_LR_CR(R0); + + // Reset JavaFrameAnchor from call_VM_leaf above. + __ reset_last_Java_frame(); + +#ifdef CC_INTERP + // Return to frame manager, it will handle the pending exception. + __ blr(); +#else + // We don't know our caller, so jump to the general forward exception stub, + // which will also pop our full frame off. Satisfy the interface of + // SharedRuntime::generate_forward_exception() + __ load_const_optimized(R11_scratch1, StubRoutines::forward_exception_entry(), R0); + __ mtctr(R11_scratch1); + __ bctr(); +#endif + + return entry; +} + +// Call an accessor method (assuming it is resolved, otherwise drop into +// vanilla (slow path) entry. +address InterpreterGenerator::generate_accessor_entry(void) { + if (!UseFastAccessorMethods && (!FLAG_IS_ERGO(UseFastAccessorMethods))) { + return NULL; + } + + Label Lslow_path, Lacquire; + + const Register + Rclass_or_obj = R3_ARG1, + Rconst_method = R4_ARG2, + Rcodes = Rconst_method, + Rcpool_cache = R5_ARG3, + Rscratch = R11_scratch1, + Rjvmti_mode = Rscratch, + Roffset = R12_scratch2, + Rflags = R6_ARG4, + Rbtable = R7_ARG5; + + static address branch_table[number_of_states]; + + address entry = __ pc(); + + // Check for safepoint: + // Ditch this, real man don't need safepoint checks. + + // Also check for JVMTI mode + // Check for null obj, take slow path if so. + __ ld(Rclass_or_obj, Interpreter::stackElementSize, CC_INTERP_ONLY(R17_tos) NOT_CC_INTERP(R15_esp)); + __ lwz(Rjvmti_mode, thread_(interp_only_mode)); + __ cmpdi(CCR1, Rclass_or_obj, 0); + __ cmpwi(CCR0, Rjvmti_mode, 0); + __ crorc(/*CCR0 eq*/2, /*CCR1 eq*/4+2, /*CCR0 eq*/2); + __ beq(CCR0, Lslow_path); // this==null or jvmti_mode!=0 + + // Do 2 things in parallel: + // 1. Load the index out of the first instruction word, which looks like this: + // <0x2a><0xb4>. + // 2. Load constant pool cache base. + __ ld(Rconst_method, in_bytes(methodOopDesc::const_offset()), R19_method); + __ ld(Rcpool_cache, in_bytes(constMethodOopDesc::constants_offset()), Rconst_method); + + __ lhz(Rcodes, in_bytes(constMethodOopDesc::codes_offset()) + 2, Rconst_method); // Lower half of 32 bit field. + __ ld(Rcpool_cache, constantPoolOopDesc::cache_offset_in_bytes(), Rcpool_cache); + + // Get the const pool entry by means of . + const int codes_shift = exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord); + __ slwi(Rscratch, Rcodes, codes_shift); // (codes&0xFFFF)<print_cr("accessor_entry: branch_table[%d] = 0x%llx (opcode 0x%llx)", i, branch_table[i], *((unsigned int*)branch_table[i])); + } +#endif + + __ bind(Lslow_path); + __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), Rscratch); + __ flush(); + + return entry; +} + +// Interpreter intrinsic for WeakReference.get(). +// 1. Don't push a full blown frame and go on dispatching, but fetch the value +// into R8 and return quickly +// 2. If G1 is active we *must* execute this intrinsic for corrrectness: +// It contains a GC barrier which puts the reference into the satb buffer +// to indicate that someone holds a strong reference to the object the +// weak ref points to! +address InterpreterGenerator::generate_Reference_get_entry(void) { + // Code: _aload_0, _getfield, _areturn + // parameter size = 1 + // + // The code that gets generated by this routine is split into 2 parts: + // 1. the "intrinsified" code for G1 (or any SATB based GC), + // 2. the slow path - which is an expansion of the regular method entry. + // + // Notes: + // * In the G1 code we do not check whether we need to block for + // a safepoint. If G1 is enabled then we must execute the specialized + // code for Reference.get (except when the Reference object is null) + // so that we can log the value in the referent field with an SATB + // update buffer. + // If the code for the getfield template is modified so that the + // G1 pre-barrier code is executed when the current method is + // Reference.get() then going through the normal method entry + // will be fine. + // * The G1 code can, however, check the receiver object (the instance + // of java.lang.Reference) and jump to the slow path if null. If the + // Reference object is null then we obviously cannot fetch the referent + // and so we don't need to call the G1 pre-barrier. Thus we can use the + // regular method entry code to generate the NPE. + // + // This code is based on generate_accessor_enty. + + address entry = __ pc(); + + const int referent_offset = java_lang_ref_Reference::referent_offset; + guarantee(referent_offset > 0, "referent offset not initialized"); + + if (UseG1GC) { + Label slow_path; + + // Debugging not possible, so can't use __ skip_if_jvmti_mode(slow_path, GR31_SCRATCH); + + // In the G1 code we don't check if we need to reach a safepoint. We + // continue and the thread will safepoint at the next bytecode dispatch. + + // If the receiver is null then it is OK to jump to the slow path. + __ ld(R3_RET, Interpreter::stackElementSize, CC_INTERP_ONLY(R17_tos) NOT_CC_INTERP(R15_esp)); // get receiver + + // Check if receiver == NULL and go the slow path. + __ cmpdi(CCR0, R3_RET, 0); + __ beq(CCR0, slow_path); + + // Load the value of the referent field. + __ load_heap_oop(R3_RET, referent_offset, R3_RET); + + // Generate the G1 pre-barrier code to log the value of + // the referent field in an SATB buffer. Note with + // these parameters the pre-barrier does not generate + // the load of the previous value. + + // Restore caller sp for c2i case. +#ifdef ASSERT + __ ld(R9_ARG7, 0, R1_SP); + __ ld(R10_ARG8, 0, R21_sender_SP); + __ cmpd(CCR0, R9_ARG7, R10_ARG8); + __ asm_assert_eq("backlink", 0x544); +#endif // ASSERT + __ mr(R1_SP, R21_sender_SP); // Cut the stack back to where the caller started. + + __ g1_write_barrier_pre(noreg, // obj + noreg, // offset + R3_RET, // pre_val + R11_scratch1, // tmp + R12_scratch2, // tmp + true); // needs_frame + + __ blr(); + + // Generate regular method entry. + __ bind(slow_path); + __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R11_scratch1); + __ flush(); + + return entry; + } else { + return generate_accessor_entry(); + } +} + +void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { + // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in + // the days we had adapter frames. When we deoptimize a situation where a + // compiled caller calls a compiled caller will have registers it expects + // to survive the call to the callee. If we deoptimize the callee the only + // way we can restore these registers is to have the oldest interpreter + // frame that we create restore these values. That is what this routine + // will accomplish. + + // At the moment we have modified c2 to not have any callee save registers + // so this problem does not exist and this routine is just a place holder. + + assert(f->is_interpreted_frame(), "must be interpreted"); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/interpreter_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/interpreter_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_INTERPRETER_PPC_HPP +#define CPU_PPC_VM_INTERPRETER_PPC_HPP + + public: + + // Stack index relative to tos (which points at value). + static int expr_index_at(int i) { + return stackElementWords * i; + } + + // Already negated by c++ interpreter. + static int local_index_at(int i) { + assert(i <= 0, "local direction already negated"); + return stackElementWords * i; + } + +#ifndef CC_INTERP + // The offset in bytes to access a expression stack slot + // relative to the esp pointer. + static int expr_offset_in_bytes(int slot) { + return stackElementSize * slot + wordSize; + } +#endif + +#endif // CPU_PPC_VM_INTERPRETER_PPC_PP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_JAVAFRAMEANCHOR_PPC_HPP +#define CPU_PPC_VM_JAVAFRAMEANCHOR_PPC_HPP + +public: + // Each arch must define reset, save, restore + // These are used by objects that only care about: + // 1 - initializing a new state (thread creation, javaCalls) + // 2 - saving a current state (javaCalls) + // 3 - restoring an old state (javaCalls) + + inline void clear(void) { + // clearing _last_Java_sp must be first + _last_Java_sp = NULL; + // fence? + OrderAccess::release(); + _last_Java_pc = NULL; + } + + inline void set(intptr_t* sp, address pc) { + _last_Java_pc = pc; + OrderAccess::release(); + _last_Java_sp = sp; + } + + void copy(JavaFrameAnchor* src) { + // In order to make sure the transition state is valid for "this". + // We must clear _last_Java_sp before copying the rest of the new data. + // + // Hack Alert: Temporary bugfix for 4717480/4721647 + // To act like previous version (pd_cache_state) don't NULL _last_Java_sp + // unless the value is changing. + if (_last_Java_sp != src->_last_Java_sp) { + _last_Java_sp = NULL; + OrderAccess::release(); + } + _last_Java_pc = src->_last_Java_pc; + // Must be last so profiler will always see valid frame if has_last_frame() is true. + OrderAccess::release(); + _last_Java_sp = src->_last_Java_sp; + } + + // Always walkable. + bool walkable(void) { return true; } + // Never any thing to do since we are always walkable and can find address of return addresses. + void make_walkable(JavaThread* thread) { } + + intptr_t* last_Java_sp(void) const { return _last_Java_sp; } + + address last_Java_pc(void) { return _last_Java_pc; } + + void set_last_Java_sp(intptr_t* sp) { OrderAccess::release(); _last_Java_sp = sp; } + +#endif // CPU_PPC_VM_JAVAFRAMEANCHOR_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/jniFastGetField_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/jniFastGetField_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "assembler_ppc.inline.hpp" +#include "memory/resourceArea.hpp" +#include "prims/jniFastGetField.hpp" +#include "prims/jvm_misc.hpp" +#include "runtime/safepoint.hpp" + + +address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { + // We don't have fast jni accessors. + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_boolean_field() { + return generate_fast_get_int_field0(T_BOOLEAN); +} + +address JNI_FastGetField::generate_fast_get_byte_field() { + return generate_fast_get_int_field0(T_BYTE); +} + +address JNI_FastGetField::generate_fast_get_char_field() { + return generate_fast_get_int_field0(T_CHAR); +} + +address JNI_FastGetField::generate_fast_get_short_field() { + return generate_fast_get_int_field0(T_SHORT); +} + +address JNI_FastGetField::generate_fast_get_int_field() { + return generate_fast_get_int_field0(T_INT); +} + +address JNI_FastGetField::generate_fast_get_long_field() { + // We don't have fast jni accessors. + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_float_field0(BasicType type) { + // We don't have fast jni accessors. + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_float_field() { + return generate_fast_get_float_field0(T_FLOAT); +} + +address JNI_FastGetField::generate_fast_get_double_field() { + return generate_fast_get_float_field0(T_DOUBLE); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/jniTypes_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/jniTypes_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_JNITYPES_PPC_HPP +#define CPU_PPC_VM_JNITYPES_PPC_HPP + +#include "memory/allocation.hpp" +#include "oops/oop.hpp" +#include "prims/jni.h" + +// This file holds platform-dependent routines used to write primitive +// jni types to the array of arguments passed into JavaCalls::call. + +class JNITypes : AllStatic { + // These functions write a java primitive type (in native format) to + // a java stack slot array to be passed as an argument to + // JavaCalls:calls. I.e., they are functionally 'push' operations + // if they have a 'pos' formal parameter. Note that jlong's and + // jdouble's are written _in reverse_ of the order in which they + // appear in the interpreter stack. This is because call stubs (see + // stubGenerator_sparc.cpp) reverse the argument list constructed by + // JavaCallArguments (see javaCalls.hpp). + + private: + +#ifndef PPC64 +#error "ppc32 support currently not implemented!!!" +#endif // PPC64 + + public: + // Ints are stored in native format in one JavaCallArgument slot at *to. + static inline void put_int(jint from, intptr_t *to) { *(jint *)(to + 0 ) = from; } + static inline void put_int(jint from, intptr_t *to, int& pos) { *(jint *)(to + pos++) = from; } + static inline void put_int(jint *from, intptr_t *to, int& pos) { *(jint *)(to + pos++) = *from; } + + // Longs are stored in native format in one JavaCallArgument slot at + // *(to+1). + static inline void put_long(jlong from, intptr_t *to) { + *(jlong*) (to + 1) = from; + } + + static inline void put_long(jlong from, intptr_t *to, int& pos) { + *(jlong*) (to + 1 + pos) = from; + pos += 2; + } + + static inline void put_long(jlong *from, intptr_t *to, int& pos) { + *(jlong*) (to + 1 + pos) = *from; + pos += 2; + } + + // Oops are stored in native format in one JavaCallArgument slot at *to. + static inline void put_obj(oop from, intptr_t *to) { *(oop *)(to + 0 ) = from; } + static inline void put_obj(oop from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = from; } + static inline void put_obj(oop *from, intptr_t *to, int& pos) { *(oop *)(to + pos++) = *from; } + + // Floats are stored in native format in one JavaCallArgument slot at *to. + static inline void put_float(jfloat from, intptr_t *to) { *(jfloat *)(to + 0 ) = from; } + static inline void put_float(jfloat from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = from; } + static inline void put_float(jfloat *from, intptr_t *to, int& pos) { *(jfloat *)(to + pos++) = *from; } + + // Doubles are stored in native word format in one JavaCallArgument + // slot at *(to+1). + static inline void put_double(jdouble from, intptr_t *to) { + *(jdouble*) (to + 1) = from; + } + + static inline void put_double(jdouble from, intptr_t *to, int& pos) { + *(jdouble*) (to + 1 + pos) = from; + pos += 2; + } + + static inline void put_double(jdouble *from, intptr_t *to, int& pos) { + *(jdouble*) (to + 1 + pos) = *from; + pos += 2; + } + + // The get_xxx routines, on the other hand, actually _do_ fetch + // java primitive types from the interpreter stack. + // No need to worry about alignment on Intel. + static inline jint get_int (intptr_t *from) { return *(jint *) from; } + static inline jlong get_long (intptr_t *from) { return *(jlong *) (from + 1); } + static inline oop get_obj (intptr_t *from) { return *(oop *) from; } + static inline jfloat get_float (intptr_t *from) { return *(jfloat *) from; } + static inline jdouble get_double(intptr_t *from) { return *(jdouble *)(from + 1); } +}; + +#endif // CPU_PPC_VM_JNITYPES_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/jni_ppc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/jni_ppc.h Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + */ + +#ifndef CPU_PPC_VM_JNI_PPC_H +#define CPU_PPC_VM_JNI_PPC_H + +// Note: please do not change these without also changing jni_md.h in the JDK +// repository +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif +#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #define JNIEXPORT __attribute__((visibility("default"))) + #define JNIIMPORT __attribute__((visibility("default"))) +#else + #define JNIEXPORT + #define JNIIMPORT +#endif + +#define JNICALL + +typedef int jint; + +#if defined(_LP64) + typedef long jlong; +#else + typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif // CPU_PPC_VM_JNI_PPC_H diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/macroAssembler_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/macroAssembler_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,3053 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "asm/assembler.inline.hpp" +#include "macroAssembler_ppc.inline.hpp" +#include "compiler/disassembler.hpp" +#include "gc_interface/collectedHeap.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "memory/cardTableModRefBS.hpp" +#include "memory/resourceArea.hpp" +#include "prims/methodHandles.hpp" +#include "runtime/biasedLocking.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/objectMonitor.hpp" +#include "runtime/os.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/macros.hpp" +#ifndef SERIALGC +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#endif + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) block_comment(str) +#endif + +#ifdef ASSERT +// On RISC, there's no benefit to verifying instruction boundaries. +//bool AbstractAssembler::pd_check_instruction_mark() { return false; } +#endif + +void MacroAssembler::ld_largeoffset_unchecked(Register d, int si31, Register a, int emit_filler_nop) { + assert(Assembler::is_simm(si31, 31) && si31 >= 0, "si31 out of range"); + if (Assembler::is_simm(si31, 16)) { + ld(d, si31, a); + if (emit_filler_nop) nop(); + } else { + const int hi = MacroAssembler::largeoffset_si16_si16_hi(si31); + const int lo = MacroAssembler::largeoffset_si16_si16_lo(si31); + addis(d, a, hi); + ld(d, lo, d); + } +} + +void MacroAssembler::ld_largeoffset(Register d, int si31, Register a, int emit_filler_nop) { + assert_different_registers(d, a); + ld_largeoffset_unchecked(d, si31, a, emit_filler_nop); +} + +void MacroAssembler::load_sized_value(Register dst, RegisterOrConstant offs, Register base, + size_t size_in_bytes, bool is_signed) { + switch (size_in_bytes) { + case 8: ld(dst, offs, base); break; + case 4: is_signed ? lwa(dst, offs, base) : lwz(dst, offs, base); break; + case 2: is_signed ? lha(dst, offs, base) : lhz(dst, offs, base); break; + case 1: lbz(dst, offs, base); if (is_signed) extsb(dst, dst); break; // lba doesn't exist :( + default: ShouldNotReachHere(); + } +} + +void MacroAssembler::store_sized_value(Register dst, RegisterOrConstant offs, Register base, + size_t size_in_bytes) { + switch (size_in_bytes) { + case 8: std(dst, offs, base); break; + case 4: stw(dst, offs, base); break; + case 2: sth(dst, offs, base); break; + case 1: stb(dst, offs, base); break; + default: ShouldNotReachHere(); + } +} + +void MacroAssembler::align(int modulus, int max, int rem) { + int padding = (rem + modulus - (offset() % modulus)) % modulus; + if (padding > max) return; + for (int c = (padding >> 2); c > 0; --c) { nop(); } +} + +// Issue instructions that calculate given TOC from global TOC. +void MacroAssembler::calculate_address_from_global_toc(Register dst, address addr, bool hi16, bool lo16, + bool add_relocation, bool emit_dummy_addr) { + int offset = -1; + if (emit_dummy_addr) { + offset = -128; // dummy address + } else if (addr != (address)(intptr_t)-1) { + offset = MacroAssembler::offset_to_global_toc(addr); + } + + if (hi16) { + addis(dst, R29, MacroAssembler::largeoffset_si16_si16_hi(offset)); + } + if (lo16) { + if (add_relocation) { + // Relocate at the addi to avoid confusion with a load from the method's TOC. + relocate(internal_word_Relocation::spec(addr)); + } + addi(dst, dst, MacroAssembler::largeoffset_si16_si16_lo(offset)); + } +} + +int MacroAssembler::patch_calculate_address_from_global_toc_at(address a, address bound, address addr) { + const int offset = MacroAssembler::offset_to_global_toc(addr); + + const address inst2_addr = a; + const int inst2 = *(int *)inst2_addr; + + // The relocation points to the second instruction, the addi, + // and the addi reads and writes the same register dst. + const int dst = inv_rt_field(inst2); + assert(is_addi(inst2) && inv_ra_field(inst2) == dst, "must be addi reading and writing dst"); + + // Now, find the preceding addis which writes to dst. + int inst1 = 0; + address inst1_addr = inst2_addr - BytesPerInstWord; + while (inst1_addr >= bound) { + inst1 = *(int *) inst1_addr; + if (is_addis(inst1) && inv_rt_field(inst1) == dst) { + // Stop, found the addis which writes dst. + break; + } + inst1_addr -= BytesPerInstWord; + } + + assert(is_addis(inst1) && inv_ra_field(inst1) == 29 /* R29 */, "source must be global TOC"); + set_imm((int *)inst1_addr, MacroAssembler::largeoffset_si16_si16_hi(offset)); + set_imm((int *)inst2_addr, MacroAssembler::largeoffset_si16_si16_lo(offset)); + return (int)((intptr_t)addr - (intptr_t)inst1_addr); +} + +address MacroAssembler::get_address_of_calculate_address_from_global_toc_at(address a, address bound) { + const address inst2_addr = a; + const int inst2 = *(int *)inst2_addr; + + // The relocation points to the second instruction, the addi, + // and the addi reads and writes the same register dst. + const int dst = inv_rt_field(inst2); + assert(is_addi(inst2) && inv_ra_field(inst2) == dst, "must be addi reading and writing dst"); + + // Now, find the preceding addis which writes to dst. + int inst1 = 0; + address inst1_addr = inst2_addr - BytesPerInstWord; + while (inst1_addr >= bound) { + inst1 = *(int *) inst1_addr; + if (is_addis(inst1) && inv_rt_field(inst1) == dst) { + // stop, found the addis which writes dst + break; + } + inst1_addr -= BytesPerInstWord; + } + + assert(is_addis(inst1) && inv_ra_field(inst1) == 29 /* R29 */, "source must be global TOC"); + + int offset = (get_imm(inst1_addr, 0) << 16) + get_imm(inst2_addr, 0); + // -1 is a special case + if (offset == -1) { + return (address)(intptr_t)-1; + } else { + return global_toc() + offset; + } +} + +#ifdef _LP64 +// Patch compressed oops constants. +int MacroAssembler::patch_set_narrow_oop(address a, address bound, long data) { + assert(UseCompressedOops, "Should only patch compressed oops"); + + const address inst2_addr = a; + const int inst2 = *(int *)inst2_addr; + + // The relocation points to the second instruction, the addi, + // and the addi reads and writes the same register dst. + const int dst = inv_rt_field(inst2); + assert(is_addi(inst2) && inv_ra_field(inst2) == dst, "must be addi reading and writing dst"); + // Now, find the preceding addis which writes to dst. + int inst1 = 0; + address inst1_addr = inst2_addr - BytesPerInstWord; + bool inst1_found = false; + while (inst1_addr >= bound) { + inst1 = *(int *)inst1_addr; + if (is_lis(inst1) && inv_rs_field(inst1) == dst) { inst1_found = true; break; } + inst1_addr -= BytesPerInstWord; + } + assert(inst1_found, "inst is not lis"); + narrowOop no = oopDesc::encode_heap_oop((oop)data); + + int xc = (no >> 16) & 0xffff; + int xd = (no >> 0) & 0xffff; + + set_imm((int *)inst1_addr,((short)(xc + ((xd & 0x8000) != 0 ? 1 : 0)))); // see enc_load_con_narrow1/2 + set_imm((int *)inst2_addr, (short)(xd)); + return (int)((intptr_t)inst2_addr - (intptr_t)inst1_addr); +} + +// Get compressed constant. +int MacroAssembler::get_narrow_oop(address a, address bound) { + assert(UseCompressedOops, "Should only patch compressed oops"); + + const address inst2_addr = a; + const int inst2 = *(int *)inst2_addr; + + // The relocation points to the second instruction, the addi, + // and the addi reads and writes the same register dst. + const int dst = inv_rt_field(inst2); + assert(is_addi(inst2) && inv_ra_field(inst2) == dst, "must be addi reading and writing dst"); + // Now, find the preceding lis which writes to dst. + int inst1 = 0; + address inst1_addr = inst2_addr - BytesPerInstWord; + bool inst1_found = false; + + while (inst1_addr >= bound) { + inst1 = *(int *) inst1_addr; + if (is_lis(inst1) && inv_rs_field(inst1) == dst) { inst1_found = true; break;} + inst1_addr -= BytesPerInstWord; + } + assert(inst1_found, "inst is not lis"); + + uint xl = ((unsigned int) (get_imm(inst2_addr,0) & 0xffff)); + uint xh = (((((xl & 0x8000) != 0 ? -1 : 0) + get_imm(inst1_addr,0)) & 0xffff) << 16); + return (int) (xl | xh); +} +#endif // _LP64 + +void MacroAssembler::load_const_from_method_toc(Register dst, AddressLiteral& a, Register toc) { + int toc_offset = 0; + // Use RelocationHolder::none for the constant pool entry, otherwise + // we will end up with a failing NativeCall::verify(x) where x is + // the address of the constant pool entry. + // FIXME: We should insert relocation information for oops at the constant + // pool entries instead of inserting it at the loads; patching of a constant + // pool entry should be less expensive. + if (!code_section()->scratch_emit()) { + address oop_address = address_constant((address)a.value(), RelocationHolder::none); + // Relocate at the pc of the load. + relocate(a.rspec()); + toc_offset = (int)(oop_address - code()->consts()->start()); + } + ld_largeoffset_unchecked(dst, toc_offset, toc, true); +} + +bool MacroAssembler::is_load_const_from_method_toc_at(address a) { + const address inst1_addr = a; + const int inst1 = *(int *)inst1_addr; + + // The relocation points to the ld or the addis. + return (is_ld(inst1)) || + (is_addis(inst1) && inv_ra_field(inst1) != 0); +} + +int MacroAssembler::get_offset_of_load_const_from_method_toc_at(address a) { + assert(is_load_const_from_method_toc_at(a), "must be load_const_from_method_toc"); + + const address inst1_addr = a; + const int inst1 = *(int *)inst1_addr; + + if (is_ld(inst1)) { + return inv_d1_field(inst1); + } else if (is_addis(inst1)) { + const int dst = inv_rt_field(inst1); + + // Now, find the succeeding ld which reads and writes to dst. + address inst2_addr = inst1_addr + BytesPerInstWord; + int inst2 = 0; + while (true) { + inst2 = *(int *) inst2_addr; + if (is_ld(inst2) && inv_ra_field(inst2) == dst && inv_rt_field(inst2) == dst) { + // Stop, found the ld which reads and writes dst. + break; + } + inst2_addr += BytesPerInstWord; + } + return (inv_d1_field(inst1) << 16) + inv_d1_field(inst2); + } + ShouldNotReachHere(); + return 0; +} + +// Get the constant from a `load_const' sequence. +long MacroAssembler::get_const(address a) { + assert(is_load_const_at(a), "not a load of a constant"); + const int *p = (const int*) a; + unsigned long x = (((unsigned long) (get_imm(a,0) & 0xffff)) << 48); + if (is_ori(*(p+1))) { + x |= (((unsigned long) (get_imm(a,1) & 0xffff)) << 32); + x |= (((unsigned long) (get_imm(a,3) & 0xffff)) << 16); + x |= (((unsigned long) (get_imm(a,4) & 0xffff))); + } else if (is_lis(*(p+1))) { + x |= (((unsigned long) (get_imm(a,2) & 0xffff)) << 32); + x |= (((unsigned long) (get_imm(a,1) & 0xffff)) << 16); + x |= (((unsigned long) (get_imm(a,3) & 0xffff))); + } else { + ShouldNotReachHere(); + return (long) 0; + } + return (long) x; +} + +// Patch the 64 bit constant of a `load_const' sequence. This is a low +// level procedure. It neither flushes the instruction cache nor is it +// mt safe. +void MacroAssembler::patch_const(address a, long x) { + assert(is_load_const_at(a), "not a load of a constant"); + int *p = (int*) a; + if (is_ori(*(p+1))) { + set_imm(0 + p, (x >> 48) & 0xffff); + set_imm(1 + p, (x >> 32) & 0xffff); + set_imm(3 + p, (x >> 16) & 0xffff); + set_imm(4 + p, x & 0xffff); + } else if (is_lis(*(p+1))) { + set_imm(0 + p, (x >> 48) & 0xffff); + set_imm(2 + p, (x >> 32) & 0xffff); + set_imm(1 + p, (x >> 16) & 0xffff); + set_imm(3 + p, x & 0xffff); + } else { + ShouldNotReachHere(); + } +} + +AddressLiteral MacroAssembler::allocate_oop_address(jobject obj) { + assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int oop_index = oop_recorder()->allocate_index(obj); + return AddressLiteral(address(obj), oop_Relocation::spec(oop_index)); +} + +AddressLiteral MacroAssembler::constant_oop_address(jobject obj) { + assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int oop_index = oop_recorder()->find_index(obj); + return AddressLiteral(address(obj), oop_Relocation::spec(oop_index)); +} + +RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_addr, + Register tmp, int offset) { + intptr_t value = *delayed_value_addr; + if (value != 0) { + return RegisterOrConstant(value + offset); + } + + // Load indirectly to solve generation ordering problem. + // static address, no relocation + int simm16_offset = load_const_optimized(tmp, delayed_value_addr, noreg, true); + ld(tmp, simm16_offset, tmp); // must be aligned ((xa & 3) == 0) + + if (offset != 0) { + addi(tmp, tmp, offset); + } + + return RegisterOrConstant(tmp); +} + +#ifndef PRODUCT +void MacroAssembler::pd_print_patched_instruction(address branch) { + Unimplemented(); // TODO: PPC port +} +#endif // ndef PRODUCT + +// Conditional far branch for destinations encodable in 24+2 bits. +void MacroAssembler::bc_far(int boint, int biint, Label& dest, int optimize) { + + // If requested by flag optimize, relocate the bc_far as a + // runtime_call and prepare for optimizing it when the code gets + // relocated. + if (optimize == bc_far_optimize_on_relocate) { + relocate(relocInfo::runtime_call_type); + } + + // variant 2: + // + // b!cxx SKIP + // bxx DEST + // SKIP: + // + + const int opposite_boint = add_bhint_to_boint(opposite_bhint(inv_boint_bhint(boint)), + opposite_bcond(inv_boint_bcond(boint))); + + // We emit two branches. + // First, a conditional branch which jumps around the far branch. + const address not_taken_pc = pc() + 2 * BytesPerInstWord; + const address bc_pc = pc(); + bc(opposite_boint, biint, not_taken_pc); + + const int bc_instr = *(int*)bc_pc; + assert(not_taken_pc == (address)inv_bd_field(bc_instr, (intptr_t)bc_pc), "postcondition"); + assert(opposite_boint == inv_bo_field(bc_instr), "postcondition"); + assert(boint == add_bhint_to_boint(opposite_bhint(inv_boint_bhint(inv_bo_field(bc_instr))), + opposite_bcond(inv_boint_bcond(inv_bo_field(bc_instr)))), + "postcondition"); + assert(biint == inv_bi_field(bc_instr), "postcondition"); + + // Second, an unconditional far branch which jumps to dest. + // Note: target(dest) remembers the current pc (see CodeSection::target) + // and returns the current pc if the label is not bound yet; when + // the label gets bound, the unconditional far branch will be patched. + const address target_pc = target(dest); + const address b_pc = pc(); + b(target_pc); + + assert(not_taken_pc == pc(), "postcondition"); + assert(dest.is_bound() || target_pc == b_pc, "postcondition"); +} + +bool MacroAssembler::is_bc_far_at(address instruction_addr) { + return is_bc_far_variant1_at(instruction_addr) || + is_bc_far_variant2_at(instruction_addr) || + is_bc_far_variant3_at(instruction_addr); +} + +address MacroAssembler::get_dest_of_bc_far_at(address instruction_addr) { + if (is_bc_far_variant1_at(instruction_addr)) { + const address instruction_1_addr = instruction_addr; + const int instruction_1 = *(int*)instruction_1_addr; + return (address)inv_bd_field(instruction_1, (intptr_t)instruction_1_addr); + } else if (is_bc_far_variant2_at(instruction_addr)) { + const address instruction_2_addr = instruction_addr + 4; + return bxx_destination(instruction_2_addr); + } else if (is_bc_far_variant3_at(instruction_addr)) { + return instruction_addr + 8; + } + // variant 4 ??? + ShouldNotReachHere(); + return NULL; +} +void MacroAssembler::set_dest_of_bc_far_at(address instruction_addr, address dest) { + + if (is_bc_far_variant3_at(instruction_addr)) { + // variant 3, far cond branch to the next instruction, already patched to nops: + // + // nop + // endgroup + // SKIP/DEST: + // + return; + } + + // first, extract boint and biint from the current branch + int boint = 0; + int biint = 0; + + ResourceMark rm; + const int code_size = 2 * BytesPerInstWord; + CodeBuffer buf(instruction_addr, code_size); + MacroAssembler masm(&buf); + if (is_bc_far_variant2_at(instruction_addr) && dest == instruction_addr + 8) { + // Far branch to next instruction: Optimize it by patching nops (produce variant 3). + masm.nop(); + masm.endgroup(); + } else { + if (is_bc_far_variant1_at(instruction_addr)) { + // variant 1, the 1st instruction contains the destination address: + // + // bcxx DEST + // endgroup + // + const int instruction_1 = *(int*)(instruction_addr); + boint = inv_bo_field(instruction_1); + biint = inv_bi_field(instruction_1); + } else if (is_bc_far_variant2_at(instruction_addr)) { + // variant 2, the 2nd instruction contains the destination address: + // + // b!cxx SKIP + // bxx DEST + // SKIP: + // + const int instruction_1 = *(int*)(instruction_addr); + boint = add_bhint_to_boint(opposite_bhint(inv_boint_bhint(inv_bo_field(instruction_1))), + opposite_bcond(inv_boint_bcond(inv_bo_field(instruction_1)))); + biint = inv_bi_field(instruction_1); + } else { + // variant 4??? + ShouldNotReachHere(); + } + + // second, set the new branch destination and optimize the code + if (dest != instruction_addr + 4 && // the bc_far is still unbound! + masm.is_within_range_of_bcxx(dest, instruction_addr)) { + // variant 1: + // + // bcxx DEST + // endgroup + // + masm.bc(boint, biint, dest); + masm.endgroup(); + } else { + // variant 2: + // + // b!cxx SKIP + // bxx DEST + // SKIP: + // + const int opposite_boint = add_bhint_to_boint(opposite_bhint(inv_boint_bhint(boint)), + opposite_bcond(inv_boint_bcond(boint))); + const address not_taken_pc = masm.pc() + 2 * BytesPerInstWord; + masm.bc(opposite_boint, biint, not_taken_pc); + masm.b(dest); + } + } + ICache::ppc64_flush_icache_bytes(instruction_addr, code_size); +} + +// Emit a NOT mt-safe patchable 64 bit absolute call/jump. +void MacroAssembler::bxx64_patchable(address dest, relocInfo::relocType rt, bool link) { + // get current pc + uint64_t start_pc = (uint64_t) pc(); + + const address pc_of_bl = (address) (start_pc + (6*BytesPerInstWord)); // bl is last + const address pc_of_b = (address) (start_pc + (0*BytesPerInstWord)); // b is first + + // relocate here + if (rt != relocInfo::none) { + relocate(rt); + } + + if ( ReoptimizeCallSequences && + (( link && is_within_range_of_b(dest, pc_of_bl)) || + (!link && is_within_range_of_b(dest, pc_of_b)))) { + // variant 2: + // Emit an optimized, pc-relative call/jump. + + if (link) { + // some padding + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + + // do the call + assert(pc() == pc_of_bl, "just checking"); + bl(dest, relocInfo::none); + } else { + // do the jump + assert(pc() == pc_of_b, "just checking"); + b(dest, relocInfo::none); + + // some padding + nop(); + nop(); + nop(); + nop(); + nop(); + nop(); + } + + // Assert that we can identify the emitted call/jump. + assert(is_bxx64_patchable_variant2_at((address)start_pc, link), + "can't identify emitted call"); + } else { + // variant 1: + mr(R0, R11); // spill R11 -> R0. + + // Load the destination address into CTR, + // calculate destination relative to global toc. + calculate_address_from_global_toc(R11, dest, true, true, false); + + mtctr(R11); + mr(R11, R0); // spill R11 <- R0. + nop(); + + // do the call/jump + if (link) { + bctrl(); + } else{ + bctr(); + } + // Assert that we can identify the emitted call/jump. + assert(is_bxx64_patchable_variant1b_at((address)start_pc, link), + "can't identify emitted call"); + } + + // Assert that we can identify the emitted call/jump. + assert(is_bxx64_patchable_at((address)start_pc, link), + "can't identify emitted call"); + assert(get_dest_of_bxx64_patchable_at((address)start_pc, link) == dest, + "wrong encoding of dest address"); +} + +// Identify a bxx64_patchable instruction. +bool MacroAssembler::is_bxx64_patchable_at(address instruction_addr, bool link) { + return is_bxx64_patchable_variant1b_at(instruction_addr, link) + //|| is_bxx64_patchable_variant1_at(instruction_addr, link) + || is_bxx64_patchable_variant2_at(instruction_addr, link); +} + +// Does the call64_patchable instruction use a pc-relative encoding of +// the call destination? +bool MacroAssembler::is_bxx64_patchable_pcrelative_at(address instruction_addr, bool link) { + // variant 2 is pc-relative + return is_bxx64_patchable_variant2_at(instruction_addr, link); +} + +// Identify variant 1. +bool MacroAssembler::is_bxx64_patchable_variant1_at(address instruction_addr, bool link) { + unsigned int* instr = (unsigned int*) instruction_addr; + return (link ? is_bctrl(instr[6]) : is_bctr(instr[6])) // bctr[l] + && is_mtctr(instr[5]) // mtctr + && is_load_const_at(instruction_addr); +} + +// Identify variant 1b: load destination relative to global toc. +bool MacroAssembler::is_bxx64_patchable_variant1b_at(address instruction_addr, bool link) { + unsigned int* instr = (unsigned int*) instruction_addr; + return (link ? is_bctrl(instr[6]) : is_bctr(instr[6])) // bctr[l] + && is_mtctr(instr[3]) // mtctr + && is_calculate_address_from_global_toc_at(instruction_addr + 2*BytesPerInstWord, instruction_addr); +} + +// Identify variant 2. +bool MacroAssembler::is_bxx64_patchable_variant2_at(address instruction_addr, bool link) { + unsigned int* instr = (unsigned int*) instruction_addr; + if (link) { + return is_bl (instr[6]) // bl dest is last + && is_nop(instr[0]) // nop + && is_nop(instr[1]) // nop + && is_nop(instr[2]) // nop + && is_nop(instr[3]) // nop + && is_nop(instr[4]) // nop + && is_nop(instr[5]); // nop + } else { + return is_b (instr[0]) // b dest is first + && is_nop(instr[1]) // nop + && is_nop(instr[2]) // nop + && is_nop(instr[3]) // nop + && is_nop(instr[4]) // nop + && is_nop(instr[5]) // nop + && is_nop(instr[6]); // nop + } +} + +// Set dest address of a bxx64_patchable instruction. +void MacroAssembler::set_dest_of_bxx64_patchable_at(address instruction_addr, address dest, bool link) { + ResourceMark rm; + int code_size = MacroAssembler::bxx64_patchable_size; + CodeBuffer buf(instruction_addr, code_size); + MacroAssembler masm(&buf); + masm.bxx64_patchable(dest, relocInfo::none, link); + ICache::ppc64_flush_icache_bytes(instruction_addr, code_size); +} + +// Get dest address of a bxx64_patchable instruction. +address MacroAssembler::get_dest_of_bxx64_patchable_at(address instruction_addr, bool link) { + if (is_bxx64_patchable_variant1_at(instruction_addr, link)) { + return (address) (unsigned long) get_const(instruction_addr); + } else if (is_bxx64_patchable_variant2_at(instruction_addr, link)) { + unsigned int* instr = (unsigned int*) instruction_addr; + if (link) { + const int instr_idx = 6; // bl is last + int branchoffset = branch_destination(instr[instr_idx], 0); + return instruction_addr + branchoffset + instr_idx*BytesPerInstWord; + } else { + const int instr_idx = 0; // b is first + int branchoffset = branch_destination(instr[instr_idx], 0); + return instruction_addr + branchoffset + instr_idx*BytesPerInstWord; + } + // Load dest relative to global toc. + } else if (is_bxx64_patchable_variant1b_at(instruction_addr, link)) { + return get_address_of_calculate_address_from_global_toc_at(instruction_addr + 2*BytesPerInstWord, + instruction_addr); + } else { + ShouldNotReachHere(); + return NULL; + } +} + +// Uses ordering which corresponds to ABI: +// _savegpr0_14: std r14,-144(r1) +// _savegpr0_15: std r15,-136(r1) +// _savegpr0_16: std r16,-128(r1) +void MacroAssembler::save_nonvolatile_gprs(Register dst, int offset) { + std(R14, offset, dst); offset += 8; + std(R15, offset, dst); offset += 8; + std(R16, offset, dst); offset += 8; + std(R17, offset, dst); offset += 8; + std(R18, offset, dst); offset += 8; + std(R19, offset, dst); offset += 8; + std(R20, offset, dst); offset += 8; + std(R21, offset, dst); offset += 8; + std(R22, offset, dst); offset += 8; + std(R23, offset, dst); offset += 8; + std(R24, offset, dst); offset += 8; + std(R25, offset, dst); offset += 8; + std(R26, offset, dst); offset += 8; + std(R27, offset, dst); offset += 8; + std(R28, offset, dst); offset += 8; + std(R29, offset, dst); offset += 8; + std(R30, offset, dst); offset += 8; + std(R31, offset, dst); offset += 8; + + stfd(F14, offset, dst); offset += 8; + stfd(F15, offset, dst); offset += 8; + stfd(F16, offset, dst); offset += 8; + stfd(F17, offset, dst); offset += 8; + stfd(F18, offset, dst); offset += 8; + stfd(F19, offset, dst); offset += 8; + stfd(F20, offset, dst); offset += 8; + stfd(F21, offset, dst); offset += 8; + stfd(F22, offset, dst); offset += 8; + stfd(F23, offset, dst); offset += 8; + stfd(F24, offset, dst); offset += 8; + stfd(F25, offset, dst); offset += 8; + stfd(F26, offset, dst); offset += 8; + stfd(F27, offset, dst); offset += 8; + stfd(F28, offset, dst); offset += 8; + stfd(F29, offset, dst); offset += 8; + stfd(F30, offset, dst); offset += 8; + stfd(F31, offset, dst); +} + +// Uses ordering which corresponds to ABI: +// _restgpr0_14: ld r14,-144(r1) +// _restgpr0_15: ld r15,-136(r1) +// _restgpr0_16: ld r16,-128(r1) +void MacroAssembler::restore_nonvolatile_gprs(Register src, int offset) { + ld(R14, offset, src); offset += 8; + ld(R15, offset, src); offset += 8; + ld(R16, offset, src); offset += 8; + ld(R17, offset, src); offset += 8; + ld(R18, offset, src); offset += 8; + ld(R19, offset, src); offset += 8; + ld(R20, offset, src); offset += 8; + ld(R21, offset, src); offset += 8; + ld(R22, offset, src); offset += 8; + ld(R23, offset, src); offset += 8; + ld(R24, offset, src); offset += 8; + ld(R25, offset, src); offset += 8; + ld(R26, offset, src); offset += 8; + ld(R27, offset, src); offset += 8; + ld(R28, offset, src); offset += 8; + ld(R29, offset, src); offset += 8; + ld(R30, offset, src); offset += 8; + ld(R31, offset, src); offset += 8; + + // FP registers + lfd(F14, offset, src); offset += 8; + lfd(F15, offset, src); offset += 8; + lfd(F16, offset, src); offset += 8; + lfd(F17, offset, src); offset += 8; + lfd(F18, offset, src); offset += 8; + lfd(F19, offset, src); offset += 8; + lfd(F20, offset, src); offset += 8; + lfd(F21, offset, src); offset += 8; + lfd(F22, offset, src); offset += 8; + lfd(F23, offset, src); offset += 8; + lfd(F24, offset, src); offset += 8; + lfd(F25, offset, src); offset += 8; + lfd(F26, offset, src); offset += 8; + lfd(F27, offset, src); offset += 8; + lfd(F28, offset, src); offset += 8; + lfd(F29, offset, src); offset += 8; + lfd(F30, offset, src); offset += 8; + lfd(F31, offset, src); +} + +// For verify_oops. +void MacroAssembler::save_volatile_gprs(Register dst, int offset) { + std(R3, offset, dst); offset += 8; + std(R4, offset, dst); offset += 8; + std(R5, offset, dst); offset += 8; + std(R6, offset, dst); offset += 8; + std(R7, offset, dst); offset += 8; + std(R8, offset, dst); offset += 8; + std(R9, offset, dst); offset += 8; + std(R10, offset, dst); offset += 8; + std(R11, offset, dst); offset += 8; + std(R12, offset, dst); +} + +// For verify_oops. +void MacroAssembler::restore_volatile_gprs(Register src, int offset) { + ld(R3, offset, src); offset += 8; + ld(R4, offset, src); offset += 8; + ld(R5, offset, src); offset += 8; + ld(R6, offset, src); offset += 8; + ld(R7, offset, src); offset += 8; + ld(R8, offset, src); offset += 8; + ld(R9, offset, src); offset += 8; + ld(R10, offset, src); offset += 8; + ld(R11, offset, src); offset += 8; + ld(R12, offset, src); +} + +void MacroAssembler::save_LR_CR(Register tmp) { + mfcr(tmp); + std(tmp, _abi(cr), R1_SP); + mflr(tmp); + std(tmp, _abi(lr), R1_SP); + // Tmp must contain lr on exit! (see return_addr and prolog in ppc64.ad) +} + +void MacroAssembler::restore_LR_CR(Register tmp) { + assert(tmp != R1_SP, "must be distinct"); + ld(tmp, _abi(lr), R1_SP); + mtlr(tmp); + ld(tmp, _abi(cr), R1_SP); + mtcr(tmp); +} + +address MacroAssembler::get_PC_trash_LR(Register result) { + Label L; + bl(L); + bind(L); + address lr_pc = pc(); + mflr(result); + return lr_pc; +} + +void MacroAssembler::resize_frame(Register offset, Register tmp) { +#ifdef ASSERT + assert_different_registers(offset, tmp, R1_SP); + andi_(tmp, offset, frame::alignment_in_bytes-1); + asm_assert_eq("resize_frame: unaligned", 0x204); +#endif + + // tmp <- *(SP) + ld(tmp, _abi(callers_sp), R1_SP); + // addr <- SP + offset; + // *(addr) <- tmp; + // SP <- addr + stdux(tmp, R1_SP, offset); +} + +void MacroAssembler::resize_frame(int offset, Register tmp) { + assert(is_simm(offset, 16), "too big an offset"); + assert_different_registers(tmp, R1_SP); + assert((offset & (frame::alignment_in_bytes-1))==0, "resize_frame: unaligned"); + // tmp <- *(SP) + ld(tmp, _abi(callers_sp), R1_SP); + // addr <- SP + offset; + // *(addr) <- tmp; + // SP <- addr + stdu(tmp, offset, R1_SP); +} + +void MacroAssembler::resize_frame_absolute(Register addr, Register tmp1, Register tmp2) { + // (addr == tmp1) || (addr == tmp2) is allowed here! + assert(tmp1 != tmp2, "must be distinct"); + + // compute offset w.r.t. current stack pointer + // tmp_1 <- addr - SP (!) + subf(tmp1, R1_SP, addr); + + // atomically update SP keeping back link. + resize_frame(tmp1/* offset */, tmp2/* tmp */); +} + +void MacroAssembler::push_frame(Register bytes, Register tmp) { +#ifdef ASSERT + assert(bytes != R0, "r0 not allowed here"); + andi_(R0, bytes, frame::alignment_in_bytes-1); + asm_assert_eq("push_frame(Reg, Reg): unaligned", 0x203); +#endif + neg(tmp, bytes); + stdux(R1_SP, R1_SP, tmp); +} + +// Push a frame of size `bytes'. +void MacroAssembler::push_frame(unsigned int bytes, Register tmp) { + long offset = align_addr(bytes, frame::alignment_in_bytes); + if (is_simm(-offset, 16)) { + stdu(R1_SP, -offset, R1_SP); + } else { + load_const(tmp, -offset); + stdux(R1_SP, R1_SP, tmp); + } +} + +// Push a frame of size `bytes' plus abi_reg_args on top. +void MacroAssembler::push_frame_reg_args(unsigned int bytes, Register tmp) { + push_frame(bytes + frame::abi_reg_args_size, tmp); +} + +// Setup up a new C frame with a spill area for non-volatile GPRs and +// additional space for local variables. +void MacroAssembler::push_frame_reg_args_nonvolatiles(unsigned int bytes, + Register tmp) { + push_frame(bytes + frame::abi_reg_args_size + frame::spill_nonvolatiles_size, tmp); +} + +// Pop current C frame. +void MacroAssembler::pop_frame() { + ld(R1_SP, _abi(callers_sp), R1_SP); +} + +#if defined(ABI_ELFv2) +address MacroAssembler::branch_to(Register r_function_entry, bool and_link) { + // TODO(asmundak): make sure the caller uses R12 as function descriptor + // most of the times. + if (R12 != r_function_entry) { + mr(R12, r_function_entry); + } + mtctr(R12); + // Do a call or a branch. + if (and_link) { + bctrl(); + } else { + bctr(); + } + _last_calls_return_pc = pc(); + + return _last_calls_return_pc; +} + +// Call a C function via a function descriptor and use full C +// calling conventions. Updates and returns _last_calls_return_pc. +address MacroAssembler::call_c(Register r_function_entry) { + return branch_to(r_function_entry, /*and_link=*/true); +} + +// For tail calls: only branch, don't link, so callee returns to caller of this function. +address MacroAssembler::call_c_and_return_to_caller(Register r_function_entry) { + return branch_to(r_function_entry, /*and_link=*/false); +} + +address MacroAssembler::call_c(address function_entry, relocInfo::relocType rt) { + load_const(R12, function_entry, R0); + return branch_to(R12, /*and_link=*/true); +} + +#else +// Generic version of a call to C function via a function descriptor +// with variable support for C calling conventions (TOC, ENV, etc.). +// Updates and returns _last_calls_return_pc. +address MacroAssembler::branch_to(Register function_descriptor, bool and_link, bool save_toc_before_call, + bool restore_toc_after_call, bool load_toc_of_callee, bool load_env_of_callee) { + // we emit standard ptrgl glue code here + assert((function_descriptor != R0), "function_descriptor cannot be R0"); + + // retrieve necessary entries from the function descriptor + ld(R0, in_bytes(FunctionDescriptor::entry_offset()), function_descriptor); + mtctr(R0); + + if (load_toc_of_callee) { + ld(R2_TOC, in_bytes(FunctionDescriptor::toc_offset()), function_descriptor); + } + if (load_env_of_callee) { + ld(R11, in_bytes(FunctionDescriptor::env_offset()), function_descriptor); + } else if (load_toc_of_callee) { + li(R11, 0); + } + + // do a call or a branch + if (and_link) { + bctrl(); + } else { + bctr(); + } + _last_calls_return_pc = pc(); + + return _last_calls_return_pc; +} + +// Call a C function via a function descriptor and use full C calling +// conventions. +// We don't use the TOC in generated code, so there is no need to save +// and restore its value. +address MacroAssembler::call_c(Register fd) { + return branch_to(fd, /*and_link=*/true, + /*save toc=*/false, + /*restore toc=*/false, + /*load toc=*/true, + /*load env=*/true); +} + +address MacroAssembler::call_c_and_return_to_caller(Register fd) { + return branch_to(fd, /*and_link=*/false, + /*save toc=*/false, + /*restore toc=*/false, + /*load toc=*/true, + /*load env=*/true); +} + +address MacroAssembler::call_c(const FunctionDescriptor* fd, relocInfo::relocType rt) { + if (rt != relocInfo::none) { + // this call needs to be relocatable + if (!ReoptimizeCallSequences + || (rt != relocInfo::runtime_call_type && rt != relocInfo::none) + || fd == NULL // support code-size estimation + || !fd->is_friend_function() + || fd->entry() == NULL) { + // it's not a friend function as defined by class FunctionDescriptor, + // so do a full call-c here. + load_const(R11, (address)fd, R0); + + bool has_env = (fd != NULL && fd->env() != NULL); + return branch_to(R11, /*and_link=*/true, + /*save toc=*/false, + /*restore toc=*/false, + /*load toc=*/true, + /*load env=*/has_env); + } else { + // It's a friend function. Load the entry point and don't care about + // toc and env. Use an optimizable call instruction, but ensure the + // same code-size as in the case of a non-friend function. + nop(); + nop(); + nop(); + bl64_patchable(fd->entry(), rt); + _last_calls_return_pc = pc(); + return _last_calls_return_pc; + } + } else { + // This call does not need to be relocatable, do more aggressive + // optimizations. + if (!ReoptimizeCallSequences + || !fd->is_friend_function()) { + // It's not a friend function as defined by class FunctionDescriptor, + // so do a full call-c here. + load_const(R11, (address)fd, R0); + return branch_to(R11, /*and_link=*/true, + /*save toc=*/false, + /*restore toc=*/false, + /*load toc=*/true, + /*load env=*/true); + } else { + // it's a friend function, load the entry point and don't care about + // toc and env. + address dest = fd->entry(); + if (is_within_range_of_b(dest, pc())) { + bl(dest); + } else { + bl64_patchable(dest, rt); + } + _last_calls_return_pc = pc(); + return _last_calls_return_pc; + } + } +} + +// Call a C function. All constants needed reside in TOC. +// +// Read the address to call from the TOC. +// Read env from TOC, if fd specifies an env. +// Read new TOC from TOC. +address MacroAssembler::call_c_using_toc(const FunctionDescriptor* fd, + relocInfo::relocType rt, Register toc) { + if (!ReoptimizeCallSequences + || (rt != relocInfo::runtime_call_type && rt != relocInfo::none) + || !fd->is_friend_function()) { + // It's not a friend function as defined by class FunctionDescriptor, + // so do a full call-c here. + assert(fd->entry() != NULL, "function must be linked"); + + AddressLiteral fd_entry(fd->entry()); + load_const_from_method_toc(R11, fd_entry, toc); + mtctr(R11); + if (fd->env() == NULL) { + li(R11, 0); + nop(); + } else { + AddressLiteral fd_env(fd->env()); + load_const_from_method_toc(R11, fd_env, toc); + } + AddressLiteral fd_toc(fd->toc()); + load_toc_from_toc(R2_TOC, fd_toc, toc); + // R2_TOC is killed. + bctrl(); + _last_calls_return_pc = pc(); + } else { + // It's a friend function, load the entry point and don't care about + // toc and env. Use an optimizable call instruction, but ensure the + // same code-size as in the case of a non-friend function. + nop(); + bl64_patchable(fd->entry(), rt); + _last_calls_return_pc = pc(); + } + return _last_calls_return_pc; +} +#endif // ABI_ELFv2 + +void MacroAssembler::call_VM_base(Register oop_result, + Register last_java_sp, + address entry_point, + bool check_exceptions) { + BLOCK_COMMENT("call_VM {"); + // Determine last_java_sp register. + if (!last_java_sp->is_valid()) { + last_java_sp = R1_SP; + } + set_top_ijava_frame_at_SP_as_last_Java_frame(last_java_sp, R11_scratch1); + + // ARG1 must hold thread address. + mr(R3_ARG1, R16_thread); +#if defined(ABI_ELFv2) + address return_pc = call_c(entry_point, relocInfo::none); +#else + address return_pc = call_c((FunctionDescriptor*)entry_point, relocInfo::none); +#endif + + reset_last_Java_frame(); + + // Check for pending exceptions. + if (check_exceptions) { + // We don't check for exceptions here. + ShouldNotReachHere(); + } + + // Get oop result if there is one and reset the value in the thread. + if (oop_result->is_valid()) { + get_vm_result(oop_result); + } + + _last_calls_return_pc = return_pc; + BLOCK_COMMENT("} call_VM"); +} + +void MacroAssembler::call_VM_leaf_base(address entry_point) { + BLOCK_COMMENT("call_VM_leaf {"); +#if defined(ABI_ELFv2) + call_c(entry_point, relocInfo::none); +#else + call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, entry_point), relocInfo::none); +#endif + BLOCK_COMMENT("} call_VM_leaf"); +} + +void MacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) { + call_VM_base(oop_result, noreg, entry_point, check_exceptions); +} + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, + bool check_exceptions) { + // R3_ARG1 is reserved for the thread. + mr_if_needed(R4_ARG2, arg_1); + call_VM(oop_result, entry_point, check_exceptions); +} + +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, + bool check_exceptions) { + // R3_ARG1 is reserved for the thread + mr_if_needed(R4_ARG2, arg_1); + assert(arg_2 != R4_ARG2, "smashed argument"); + mr_if_needed(R5_ARG3, arg_2); + call_VM(oop_result, entry_point, check_exceptions); +} + +void MacroAssembler::call_VM_leaf(address entry_point) { + call_VM_leaf_base(entry_point); +} + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1) { + mr_if_needed(R3_ARG1, arg_1); + call_VM_leaf(entry_point); +} + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1, Register arg_2) { + mr_if_needed(R3_ARG1, arg_1); + assert(arg_2 != R3_ARG1, "smashed argument"); + mr_if_needed(R4_ARG2, arg_2); + call_VM_leaf(entry_point); +} + +void MacroAssembler::call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3) { + mr_if_needed(R3_ARG1, arg_1); + assert(arg_2 != R3_ARG1, "smashed argument"); + mr_if_needed(R4_ARG2, arg_2); + assert(arg_3 != R3_ARG1 && arg_3 != R4_ARG2, "smashed argument"); + mr_if_needed(R5_ARG3, arg_3); + call_VM_leaf(entry_point); +} + +// Check whether instruction is a read access to the polling page +// which was emitted by load_from_polling_page(..). +bool MacroAssembler::is_load_from_polling_page(int instruction, void* ucontext, + address* polling_address_ptr) { + if (!is_ld(instruction)) + return false; // It's not a ld. Fail. + + int rt = inv_rt_field(instruction); + int ra = inv_ra_field(instruction); + int ds = inv_ds_field(instruction); + if (!(ds == 0 && ra != 0 && rt == 0)) { + return false; // It's not a ld(r0, X, ra). Fail. + } + + if (!ucontext) { + // Set polling address. + if (polling_address_ptr != NULL) { + *polling_address_ptr = NULL; + } + return true; // No ucontext given. Can't check value of ra. Assume true. + } + +#ifdef LINUX + // Ucontext given. Check that register ra contains the address of + // the safepoing polling page. + ucontext_t* uc = (ucontext_t*) ucontext; + // Set polling address. + address addr = (address)uc->uc_mcontext.regs->gpr[ra] + (ssize_t)ds; + if (polling_address_ptr != NULL) { + *polling_address_ptr = addr; + } + return os::is_poll_address(addr); +#else + // Not on Linux, ucontext must be NULL. + ShouldNotReachHere(); + return false; +#endif +} + +bool MacroAssembler::is_memory_serialization(int instruction, JavaThread* thread, void* ucontext) { +#ifdef LINUX + ucontext_t* uc = (ucontext_t*) ucontext; + + if (is_stwx(instruction) || is_stwux(instruction)) { + int ra = inv_ra_field(instruction); + int rb = inv_rb_field(instruction); + + // look up content of ra and rb in ucontext + address ra_val=(address)uc->uc_mcontext.regs->gpr[ra]; + long rb_val=(long)uc->uc_mcontext.regs->gpr[rb]; + return os::is_memory_serialize_page(thread, ra_val+rb_val); + } else if (is_stw(instruction) || is_stwu(instruction)) { + int ra = inv_ra_field(instruction); + int d1 = inv_d1_field(instruction); + + // look up content of ra in ucontext + address ra_val=(address)uc->uc_mcontext.regs->gpr[ra]; + return os::is_memory_serialize_page(thread, ra_val+d1); + } else { + return false; + } +#else + // workaround not needed on !LINUX :-) + ShouldNotCallThis(); + return false; +#endif +} + +void MacroAssembler::bang_stack_with_offset(int offset) { + // When increasing the stack, the old stack pointer will be written + // to the new top of stack according to the PPC64 abi. + // Therefore, stack banging is not necessary when increasing + // the stack by <= os::vm_page_size() bytes. + // When increasing the stack by a larger amount, this method is + // called repeatedly to bang the intermediate pages. + + // Stack grows down, caller passes positive offset. + assert(offset > 0, "must bang with positive offset"); + + long stdoffset = -offset; + + if (is_simm(stdoffset, 16)) { + // Signed 16 bit offset, a simple std is ok. + if (UseLoadInstructionsForStackBangingPPC64) { + ld(R0, (int)(signed short)stdoffset, R1_SP); + } else { + std(R0,(int)(signed short)stdoffset, R1_SP); + } + } else if (is_simm(stdoffset, 31)) { + const int hi = MacroAssembler::largeoffset_si16_si16_hi(stdoffset); + const int lo = MacroAssembler::largeoffset_si16_si16_lo(stdoffset); + + Register tmp = R11; + addis(tmp, R1_SP, hi); + if (UseLoadInstructionsForStackBangingPPC64) { + ld(R0, lo, tmp); + } else { + std(R0, lo, tmp); + } + } else { + ShouldNotReachHere(); + } +} + +// If instruction is a stack bang of the form +// std R0, x(Ry), (see bang_stack_with_offset()) +// stdu R1_SP, x(R1_SP), (see push_frame(), resize_frame()) +// or stdux R1_SP, Rx, R1_SP (see push_frame(), resize_frame()) +// return the banged address. Otherwise, return 0. +address MacroAssembler::get_stack_bang_address(int instruction, void *ucontext) { +#ifdef LINUX + ucontext_t* uc = (ucontext_t*) ucontext; + int rs = inv_rs_field(instruction); + int ra = inv_ra_field(instruction); + if ( (is_ld(instruction) && rs == 0 && UseLoadInstructionsForStackBangingPPC64) + || (is_std(instruction) && rs == 0 && !UseLoadInstructionsForStackBangingPPC64) + || (is_stdu(instruction) && rs == 1)) { + int ds = inv_ds_field(instruction); + // return banged address + return ds+(address)uc->uc_mcontext.regs->gpr[ra]; + } else if (is_stdux(instruction) && rs == 1) { + int rb = inv_rb_field(instruction); + address sp = (address)uc->uc_mcontext.regs->gpr[1]; + long rb_val = (long)uc->uc_mcontext.regs->gpr[rb]; + return ra != 1 || rb_val >= 0 ? NULL // not a stack bang + : sp + rb_val; // banged address + } + return NULL; // not a stack bang +#else + // workaround not needed on !LINUX :-) + ShouldNotCallThis(); + return NULL; +#endif +} + +// CmpxchgX sets condition register to cmpX(current, compare). +void MacroAssembler::cmpxchgw(ConditionRegister flag, Register dest_current_value, + Register compare_value, Register exchange_value, + Register addr_base, int semantics, bool cmpxchgx_hint, + Register int_flag_success, bool contention_hint) { + Label retry; + Label failed; + Label done; + + // Save one branch if result is returned via register and + // result register is different from the other ones. + bool use_result_reg = (int_flag_success != noreg); + bool preset_result_reg = (int_flag_success != dest_current_value && int_flag_success != compare_value && + int_flag_success != exchange_value && int_flag_success != addr_base); + + // release/fence semantics + if (semantics & MemBarRel) { + release(); + } + + if (use_result_reg && preset_result_reg) { + li(int_flag_success, 0); // preset (assume cas failed) + } + + // Add simple guard in order to reduce risk of starving under high contention (recommended by IBM). + if (contention_hint) { // Don't try to reserve if cmp fails. + lwz(dest_current_value, 0, addr_base); + cmpw(flag, dest_current_value, compare_value); + bne(flag, failed); + } + + // atomic emulation loop + bind(retry); + + lwarx(dest_current_value, addr_base, cmpxchgx_hint); + cmpw(flag, dest_current_value, compare_value); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + bne_predict_not_taken(flag, failed); + } else { + bne( flag, failed); + } + // branch to done => (flag == ne), (dest_current_value != compare_value) + // fall through => (flag == eq), (dest_current_value == compare_value) + + stwcx_(exchange_value, addr_base); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + bne_predict_not_taken(CCR0, retry); // StXcx_ sets CCR0. + } else { + bne( CCR0, retry); // StXcx_ sets CCR0. + } + // fall through => (flag == eq), (dest_current_value == compare_value), (swapped) + + // Result in register (must do this at the end because int_flag_success can be the + // same register as one above). + if (use_result_reg) { + li(int_flag_success, 1); + } + + if (semantics & MemBarFenceAfter) { + fence(); + } else if (semantics & MemBarAcq) { + isync(); + } + + if (use_result_reg && !preset_result_reg) { + b(done); + } + + bind(failed); + if (use_result_reg && !preset_result_reg) { + li(int_flag_success, 0); + } + + bind(done); + // (flag == ne) => (dest_current_value != compare_value), (!swapped) + // (flag == eq) => (dest_current_value == compare_value), ( swapped) +} + +// Preforms atomic compare exchange: +// if (compare_value == *addr_base) +// *addr_base = exchange_value +// int_flag_success = 1; +// else +// int_flag_success = 0; +// +// ConditionRegister flag = cmp(compare_value, *addr_base) +// Register dest_current_value = *addr_base +// Register compare_value Used to compare with value in memory +// Register exchange_value Written to memory if compare_value == *addr_base +// Register addr_base The memory location to compareXChange +// Register int_flag_success Set to 1 if exchange_value was written to *addr_base +// +// To avoid the costly compare exchange the value is tested beforehand. +// Several special cases exist to avoid that unnecessary information is generated. +// +void MacroAssembler::cmpxchgd(ConditionRegister flag, + Register dest_current_value, Register compare_value, Register exchange_value, + Register addr_base, int semantics, bool cmpxchgx_hint, + Register int_flag_success, Label* failed_ext, bool contention_hint) { + Label retry; + Label failed_int; + Label& failed = (failed_ext != NULL) ? *failed_ext : failed_int; + Label done; + + // Save one branch if result is returned via register and result register is different from the other ones. + bool use_result_reg = (int_flag_success!=noreg); + bool preset_result_reg = (int_flag_success!=dest_current_value && int_flag_success!=compare_value && + int_flag_success!=exchange_value && int_flag_success!=addr_base); + assert(int_flag_success == noreg || failed_ext == NULL, "cannot have both"); + + // release/fence semantics + if (semantics & MemBarRel) { + release(); + } + + if (use_result_reg && preset_result_reg) { + li(int_flag_success, 0); // preset (assume cas failed) + } + + // Add simple guard in order to reduce risk of starving under high contention (recommended by IBM). + if (contention_hint) { // Don't try to reserve if cmp fails. + ld(dest_current_value, 0, addr_base); + cmpd(flag, dest_current_value, compare_value); + bne(flag, failed); + } + + // atomic emulation loop + bind(retry); + + ldarx(dest_current_value, addr_base, cmpxchgx_hint); + cmpd(flag, dest_current_value, compare_value); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + bne_predict_not_taken(flag, failed); + } else { + bne( flag, failed); + } + + stdcx_(exchange_value, addr_base); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + bne_predict_not_taken(CCR0, retry); // stXcx_ sets CCR0 + } else { + bne( CCR0, retry); // stXcx_ sets CCR0 + } + + // result in register (must do this at the end because int_flag_success can be the same register as one above) + if (use_result_reg) { + li(int_flag_success, 1); + } + + // POWER6 doesn't need isync in CAS. + // Always emit isync to be on the safe side. + if (semantics & MemBarFenceAfter) { + fence(); + } else if (semantics & MemBarAcq) { + isync(); + } + + if (use_result_reg && !preset_result_reg) { + b(done); + } + + bind(failed_int); + if (use_result_reg && !preset_result_reg) { + li(int_flag_success, 0); + } + + bind(done); + // (flag == ne) => (dest_current_value != compare_value), (!swapped) + // (flag == eq) => (dest_current_value == compare_value), ( swapped) +} + +// Look up the method for a megamorphic invokeinterface call. +// The target method is determined by . +// The receiver klass is in recv_klass. +// On success, the result will be in method_result, and execution falls through. +// On failure, execution transfers to the given label. +void MacroAssembler::lookup_interface_method(Register recv_klass, + Register intf_klass, + RegisterOrConstant itable_index, + Register method_result, + Register scan_temp, + Register sethi_temp, + Label& L_no_such_interface) { + assert_different_registers(recv_klass, intf_klass, method_result, scan_temp); + assert(itable_index.is_constant() || itable_index.as_register() == method_result, + "caller must use same register for non-constant itable index as for method"); + + // Compute start of first itableOffsetEntry (which is at the end of the vtable). + int vtable_base = instanceKlass::vtable_start_offset() * wordSize; + int itentry_off = itableMethodEntry::method_offset_in_bytes(); + int logMEsize = exact_log2(itableMethodEntry::size() * wordSize); + int scan_step = itableOffsetEntry::size() * wordSize; + int log_vte_size= exact_log2(vtableEntry::size() * wordSize); + + lwz(scan_temp, instanceKlass::vtable_length_offset() * wordSize, recv_klass); + // %%% We should store the aligned, prescaled offset in the klassoop. + // Then the next several instructions would fold away. + + sldi(scan_temp, scan_temp, log_vte_size); + addi(scan_temp, scan_temp, vtable_base); + add(scan_temp, recv_klass, scan_temp); + + // Adjust recv_klass by scaled itable_index, so we can free itable_index. + if (itable_index.is_register()) { + Register itable_offset = itable_index.as_register(); + sldi(itable_offset, itable_offset, logMEsize); + if (itentry_off) addi(itable_offset, itable_offset, itentry_off); + add(recv_klass, itable_offset, recv_klass); + } else { + long itable_offset = (long)itable_index.as_constant(); + load_const_optimized(sethi_temp, (itable_offset<itable(); scan->interface() != NULL; scan += scan_step) { + // if (scan->interface() == intf) { + // result = (klass + scan->offset() + itable_index); + // } + // } + Label search, found_method; + + for (int peel = 1; peel >= 0; peel--) { + // %%%% Could load both offset and interface in one ldx, if they were + // in the opposite order. This would save a load. + ld(method_result, itableOffsetEntry::interface_offset_in_bytes(), scan_temp); + + // Check that this entry is non-null. A null entry means that + // the receiver class doesn't implement the interface, and wasn't the + // same as when the caller was compiled. + cmpd(CCR0, method_result, intf_klass); + + if (peel) { + beq(CCR0, found_method); + } else { + bne(CCR0, search); + // (invert the test to fall through to found_method...) + } + + if (!peel) break; + + bind(search); + + cmpdi(CCR0, method_result, 0); + beq(CCR0, L_no_such_interface); + addi(scan_temp, scan_temp, scan_step); + } + + bind(found_method); + + // Got a hit. + int ito_offset = itableOffsetEntry::offset_offset_in_bytes(); + lwz(scan_temp, ito_offset, scan_temp); + ldx(method_result, scan_temp, recv_klass); +} + +// virtual method calling +void MacroAssembler::lookup_virtual_method(Register recv_klass, + RegisterOrConstant vtable_index, + Register method_result) { + + assert_different_registers(recv_klass, method_result, vtable_index.register_or_noreg()); + + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); + + if (vtable_index.is_register()) { + sldi(vtable_index.as_register(), vtable_index.as_register(), LogBytesPerWord); + add(recv_klass, vtable_index.as_register(), recv_klass); + } else { + addi(recv_klass, recv_klass, vtable_index.as_constant() << LogBytesPerWord); + } + ld(R19_method, base + vtableEntry::method_offset_in_bytes(), recv_klass); +} + +/////////////////////////////////////////// subtype checking //////////////////////////////////////////// + +void MacroAssembler::check_klass_subtype_fast_path(Register sub_klass, + Register super_klass, + Register temp1_reg, + Register temp2_reg, + Label& L_success, + Label& L_failure) { + + const Register check_cache_offset = temp1_reg; + const Register cached_super = temp2_reg; + + assert_different_registers(sub_klass, super_klass, check_cache_offset, cached_super); + + int sco_offset = in_bytes(Klass::super_check_offset_offset()); + int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); + + // If the pointers are equal, we are done (e.g., String[] elements). + // This self-check enables sharing of secondary supertype arrays among + // non-primary types such as array-of-interface. Otherwise, each such + // type would need its own customized SSA. + // We move this check to the front of the fast path because many + // type checks are in fact trivially successful in this manner, + // so we get a nicely predicted branch right at the start of the check. + cmpd(CCR0, sub_klass, super_klass); + beq(CCR0, L_success); + + // Check the supertype display: + lwz(check_cache_offset, sco_offset, super_klass); + // The loaded value is the offset from KlassOopDesc. + + ldx(cached_super, check_cache_offset, sub_klass); + cmpd(CCR0, cached_super, super_klass); + beq(CCR0, L_success); + + // This check has worked decisively for primary supers. + // Secondary supers are sought in the super_cache ('super_cache_addr'). + // (Secondary supers are interfaces and very deeply nested subtypes.) + // This works in the same check above because of a tricky aliasing + // between the super_cache and the primary super display elements. + // (The 'super_check_addr' can address either, as the case requires.) + // Note that the cache is updated below if it does not help us find + // what we need immediately. + // So if it was a primary super, we can just fail immediately. + // Otherwise, it's the slow path for us (no success at this point). + + cmpwi(CCR0, check_cache_offset, sc_offset); + bne(CCR0, L_failure); + // bind(slow_path); // fallthru +} + +void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass, + Register super_klass, + Register temp1_reg, + Register temp2_reg, + Label* L_success, + Register result_reg) { + const Register array_ptr = temp1_reg; // current value from cache array + const Register temp = temp2_reg; + + assert_different_registers(sub_klass, super_klass, array_ptr, temp); + + int source_offset = in_bytes(Klass::secondary_supers_offset()); + int target_offset = in_bytes(Klass::secondary_super_cache_offset()); + + int length_offset = arrayOopDesc::length_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(T_OBJECT); + + Label hit, loop, failure, fallthru; + + ld(array_ptr, source_offset, sub_klass); + + //assert(4 == arrayOopDesc::length_length_in_bytes(), "precondition violated."); + lwz(temp, length_offset, array_ptr); + cmpwi(CCR0, temp, 0); + beq(CCR0, result_reg!=noreg ? failure : fallthru); // length 0 + + if (UseCompressedOops) { + encode_heap_oop_not_null(super_klass); + } + + mtctr(temp); // load ctr + + bind(loop); + if (UseCompressedOops) { + // Oops in table are compressed. + lwz(temp, base_offset, array_ptr); + cmpw(CCR0, temp, super_klass); + } else { + ld(temp, base_offset, array_ptr); + cmpd(CCR0, temp, super_klass); + } + beq(CCR0, hit); + addi(array_ptr, array_ptr, BytesPerHeapOop); + bdnz(loop); + + if (UseCompressedOops) { + decode_heap_oop_not_null(super_klass); // restore super_klass + } + + bind(failure); + if (result_reg!=noreg) li(result_reg, 1); // load non-zero result (indicates a miss) + b(fallthru); + + bind(hit); + if (UseCompressedOops) { + decode_heap_oop_not_null(super_klass); // restore super_klass + } + std(super_klass, target_offset, sub_klass); // save result to cache + if (result_reg != noreg) li(result_reg, 0); // load zero result (indicates a hit) + if (L_success != NULL) b(*L_success); + + bind(fallthru); +} + +// Try fast path, then go to slow one if not successful +void MacroAssembler::check_klass_subtype(Register sub_klass, + Register super_klass, + Register temp1_reg, + Register temp2_reg, + Label& L_success) { + Label L_failure; + check_klass_subtype_fast_path(sub_klass, super_klass, temp1_reg, temp2_reg, L_success, L_failure); + check_klass_subtype_slow_path(sub_klass, super_klass, temp1_reg, temp2_reg, &L_success); + bind(L_failure); // Fallthru if not successful. +} + +void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg, + Register temp_reg, + Label& wrong_method_type) { + assert_different_registers(mtype_reg, mh_reg, temp_reg); + // Compare method type against that of the receiver. + load_heap_oop_not_null(temp_reg, delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg), mh_reg); + cmpd(CCR0, temp_reg, mtype_reg); + bne(CCR0, wrong_method_type); +} + +RegisterOrConstant MacroAssembler::argument_offset(RegisterOrConstant arg_slot, + Register temp_reg, + int extra_slot_offset) { + // cf. TemplateTable::prepare_invoke(), if (load_receiver). + int stackElementSize = Interpreter::stackElementSize; + int offset = extra_slot_offset * stackElementSize; + if (arg_slot.is_constant()) { + offset += arg_slot.as_constant() * stackElementSize; + return offset; + } else { + assert(temp_reg != noreg, "must specify"); + sldi(temp_reg, arg_slot.as_register(), exact_log2(stackElementSize)); + if (offset != 0) + addi(temp_reg, temp_reg, offset); + return temp_reg; + } +} + +void MacroAssembler::biased_locking_enter(ConditionRegister cr_reg, Register obj_reg, + Register mark_reg, Register temp_reg, + Register temp2_reg, Label& done, Label* slow_case) { + assert(UseBiasedLocking, "why call this otherwise?"); + +#ifdef ASSERT + assert_different_registers(obj_reg, mark_reg, temp_reg, temp2_reg); +#endif + + Label cas_label; + + // Branch to done if fast path fails and no slow_case provided. + Label *slow_case_int = (slow_case != NULL) ? slow_case : &done; + + // Biased locking + // See whether the lock is currently biased toward our thread and + // whether the epoch is still valid + // Note that the runtime guarantees sufficient alignment of JavaThread + // pointers to allow age to be placed into low bits + assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, + "biased locking makes assumptions about bit layout"); + + if (PrintBiasedLockingStatistics) { + load_const(temp_reg, (address) BiasedLocking::total_entry_count_addr(), temp2_reg); + lwz(temp2_reg, 0, temp_reg); + addi(temp2_reg, temp2_reg, 1); + stw(temp2_reg, 0, temp_reg); + } + + andi(temp_reg, mark_reg, markOopDesc::biased_lock_mask_in_place); + cmpwi(cr_reg, temp_reg, markOopDesc::biased_lock_pattern); + bne(cr_reg, cas_label); + + load_heap_oop_with_trap_null_check(temp_reg, oopDesc::klass_offset_in_bytes(), obj_reg); + load_const_optimized(temp2_reg, ~((int) markOopDesc::age_mask_in_place)); + ld(temp_reg, in_bytes(Klass::prototype_header_offset()), temp_reg); + orr(temp_reg, R16_thread, temp_reg); + xorr(temp_reg, mark_reg, temp_reg); + andr(temp_reg, temp_reg, temp2_reg); + cmpdi(cr_reg, temp_reg, 0); + if (PrintBiasedLockingStatistics) { + Label l; + bne(cr_reg, l); + load_const(mark_reg, (address) BiasedLocking::biased_lock_entry_count_addr()); + lwz(temp2_reg, 0, mark_reg); + addi(temp2_reg, temp2_reg, 1); + stw(temp2_reg, 0, mark_reg); + // restore mark_reg + ld(mark_reg, oopDesc::mark_offset_in_bytes(), obj_reg); + bind(l); + } + beq(cr_reg, done); + + Label try_revoke_bias; + Label try_rebias; + + // At this point we know that the header has the bias pattern and + // that we are not the bias owner in the current epoch. We need to + // figure out more details about the state of the header in order to + // know what operations can be legally performed on the object's + // header. + + // If the low three bits in the xor result aren't clear, that means + // the prototype header is no longer biased and we have to revoke + // the bias on this object. + andi(temp2_reg, temp_reg, markOopDesc::biased_lock_mask_in_place); + cmpwi(cr_reg, temp2_reg, 0); + bne(cr_reg, try_revoke_bias); + + // Biasing is still enabled for this data type. See whether the + // epoch of the current bias is still valid, meaning that the epoch + // bits of the mark word are equal to the epoch bits of the + // prototype header. (Note that the prototype header's epoch bits + // only change at a safepoint.) If not, attempt to rebias the object + // toward the current thread. Note that we must be absolutely sure + // that the current epoch is invalid in order to do this because + // otherwise the manipulations it performs on the mark word are + // illegal. + + int shift_amount = 64 - markOopDesc::epoch_shift; + // rotate epoch bits to right (little) end and set other bits to 0 + // [ big part | epoch | little part ] -> [ 0..0 | epoch ] + rldicl_(temp2_reg, temp_reg, shift_amount, 64 - markOopDesc::epoch_bits); + // branch if epoch bits are != 0, i.e. they differ, because the epoch has been incremented + bne(CCR0, try_rebias); + + // The epoch of the current bias is still valid but we know nothing + // about the owner; it might be set or it might be clear. Try to + // acquire the bias of the object using an atomic operation. If this + // fails we will go in to the runtime to revoke the object's bias. + // Note that we first construct the presumed unbiased header so we + // don't accidentally blow away another thread's valid bias. + andi(mark_reg, mark_reg, (markOopDesc::biased_lock_mask_in_place | + markOopDesc::age_mask_in_place | + markOopDesc::epoch_mask_in_place)); + orr(temp_reg, R16_thread, mark_reg); + + assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + + // CmpxchgX sets cr_reg to cmpX(temp2_reg, mark_reg). + fence(); // TODO: replace by MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq ? + cmpxchgd(/*flag=*/cr_reg, /*current_value=*/temp2_reg, + /*compare_value=*/mark_reg, /*exchange_value=*/temp_reg, + /*where=*/obj_reg, + MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock(), + noreg, slow_case_int); // bail out if failed + + // If the biasing toward our thread failed, this means that + // another thread succeeded in biasing it toward itself and we + // need to revoke that bias. The revocation will occur in the + // interpreter runtime in the slow case. + if (PrintBiasedLockingStatistics) { + load_const(temp_reg, (address) BiasedLocking::anonymously_biased_lock_entry_count_addr(), temp2_reg); + lwz(temp2_reg, 0, temp_reg); + addi(temp2_reg, temp2_reg, 1); + stw(temp2_reg, 0, temp_reg); + } + b(done); + + bind(try_rebias); + // At this point we know the epoch has expired, meaning that the + // current "bias owner", if any, is actually invalid. Under these + // circumstances _only_, we are allowed to use the current header's + // value as the comparison value when doing the cas to acquire the + // bias in the current epoch. In other words, we allow transfer of + // the bias from one thread to another directly in this situation. + andi(temp_reg, mark_reg, markOopDesc::age_mask_in_place); + orr(temp_reg, R16_thread, temp_reg); + load_heap_oop_with_trap_null_check(temp2_reg, oopDesc::klass_offset_in_bytes(), obj_reg); + ld(temp2_reg, in_bytes(Klass::prototype_header_offset()), temp2_reg); + orr(temp_reg, temp_reg, temp2_reg); + + assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + + // CmpxchgX sets cr_reg to cmpX(temp2_reg, mark_reg). + fence(); // TODO: replace by MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq ? + cmpxchgd(/*flag=*/cr_reg, /*current_value=*/temp2_reg, + /*compare_value=*/mark_reg, /*exchange_value=*/temp_reg, + /*where=*/obj_reg, + MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock(), + noreg, slow_case_int); // bail out if failed + + // If the biasing toward our thread failed, this means that + // another thread succeeded in biasing it toward itself and we + // need to revoke that bias. The revocation will occur in the + // interpreter runtime in the slow case. + if (PrintBiasedLockingStatistics) { + load_const(temp_reg, (address) BiasedLocking::rebiased_lock_entry_count_addr(), temp2_reg); + lwz(temp2_reg, 0, temp_reg); + addi(temp2_reg, temp2_reg, 1); + stw(temp2_reg, 0, temp_reg); + } + b(done); + + bind(try_revoke_bias); + // The prototype mark in the klass doesn't have the bias bit set any + // more, indicating that objects of this data type are not supposed + // to be biased any more. We are going to try to reset the mark of + // this object to the prototype value and fall through to the + // CAS-based locking scheme. Note that if our CAS fails, it means + // that another thread raced us for the privilege of revoking the + // bias of this particular object, so it's okay to continue in the + // normal locking code. + load_heap_oop_with_trap_null_check(temp_reg, oopDesc::klass_offset_in_bytes(), obj_reg); + ld(temp_reg, in_bytes(Klass::prototype_header_offset()), temp_reg); + andi(temp2_reg, mark_reg, markOopDesc::age_mask_in_place); + orr(temp_reg, temp_reg, temp2_reg); + + assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + + // CmpxchgX sets cr_reg to cmpX(temp2_reg, mark_reg). + fence(); // TODO: replace by MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq ? + cmpxchgd(/*flag=*/cr_reg, /*current_value=*/temp2_reg, + /*compare_value=*/mark_reg, /*exchange_value=*/temp_reg, + /*where=*/obj_reg, + MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock()); + + // reload markOop in mark_reg before continuing with lightweight locking + ld(mark_reg, oopDesc::mark_offset_in_bytes(), obj_reg); + + // Fall through to the normal CAS-based lock, because no matter what + // the result of the above CAS, some thread must have succeeded in + // removing the bias bit from the object's header. + if (PrintBiasedLockingStatistics) { + Label l; + bne(cr_reg, l); + load_const(temp_reg, (address) BiasedLocking::revoked_lock_entry_count_addr(), temp2_reg); + lwz(temp2_reg, 0, temp_reg); + addi(temp2_reg, temp2_reg, 1); + stw(temp2_reg, 0, temp_reg); + bind(l); + } + + bind(cas_label); +} + +void MacroAssembler::biased_locking_exit (ConditionRegister cr_reg, Register mark_addr, Register temp_reg, Label& done) { + // Check for biased locking unlock case, which is a no-op + // Note: we do not have to check the thread ID for two reasons. + // First, the interpreter checks for IllegalMonitorStateException at + // a higher level. Second, if the bias was revoked while we held the + // lock, the object could not be rebiased toward another thread, so + // the bias bit would be clear. + + ld(temp_reg, 0, mark_addr); + andi(temp_reg, temp_reg, markOopDesc::biased_lock_mask_in_place); + + cmpwi(cr_reg, temp_reg, markOopDesc::biased_lock_pattern); + beq(cr_reg, done); +} + +// "The box" is the space on the stack where we copy the object mark. +void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register oop, Register box, + Register temp, Register displaced_header, Register current_header) { + assert_different_registers(oop, box, temp, displaced_header, current_header); + assert(flag != CCR0, "bad condition register"); + Label cont; + Label object_has_monitor; + Label cas_failed; + + // Load markOop from object into displaced_header. + ld(displaced_header, oopDesc::mark_offset_in_bytes(), oop); + + + // Always do locking in runtime. + if (EmitSync & 0x01) { + cmpdi(flag, oop, 0); // Oop can't be 0 here => always false. + return; + } + + if (UseBiasedLocking) { + biased_locking_enter(flag, oop, displaced_header, temp, current_header, cont); + } + + // Handle existing monitor. + if ((EmitSync & 0x02) == 0) { + // The object has an existing monitor iff (mark & monitor_value) != 0. + andi_(temp, displaced_header, markOopDesc::monitor_value); + bne(CCR0, object_has_monitor); + } + + // Set displaced_header to be (markOop of object | UNLOCK_VALUE). + ori(displaced_header, displaced_header, markOopDesc::unlocked_value); + + // Load Compare Value application register. + + // Initialize the box. (Must happen before we update the object mark!) + std(displaced_header, BasicLock::displaced_header_offset_in_bytes(), box); + + // Must fence, otherwise, preceding store(s) may float below cmpxchg. + // Compare object markOop with mark and if equal exchange scratch1 with object markOop. + // CmpxchgX sets cr_reg to cmpX(current, displaced). + membar(Assembler::StoreStore); + cmpxchgd(/*flag=*/flag, + /*current_value=*/current_header, + /*compare_value=*/displaced_header, + /*exchange_value=*/box, + /*where=*/oop, + MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock(), + noreg, + &cas_failed); + assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + + // If the compare-and-exchange succeeded, then we found an unlocked + // object and we have now locked it. + b(cont); + + bind(cas_failed); + // We did not see an unlocked object so try the fast recursive case. + + // Check if the owner is self by comparing the value in the markOop of object + // (current_header) with the stack pointer. + sub(current_header, current_header, R1_SP); + load_const_optimized(temp, (address) (~(os::vm_page_size()-1) | + markOopDesc::lock_mask_in_place)); + + and_(R0/*==0?*/, current_header, temp); + // If condition is true we are cont and hence we can store 0 as the + // displaced header in the box, which indicates that it is a recursive lock. + mcrf(flag,CCR0); + std(R0/*==0, perhaps*/, BasicLock::displaced_header_offset_in_bytes(), box); + + // Handle existing monitor. + if ((EmitSync & 0x02) == 0) { + b(cont); + + bind(object_has_monitor); + // The object's monitor m is unlocked iff m->owner == NULL, + // otherwise m->owner may contain a thread or a stack address. + // + // Try to CAS m->owner from NULL to current thread. + addi(temp, displaced_header, ObjectMonitor::owner_offset_in_bytes()-markOopDesc::monitor_value); + li(displaced_header, 0); + // CmpxchgX sets flag to cmpX(current, displaced). + cmpxchgd(/*flag=*/flag, + /*current_value=*/current_header, + /*compare_value=*/displaced_header, + /*exchange_value=*/R16_thread, + /*where=*/temp, + MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq, + MacroAssembler::cmpxchgx_hint_acquire_lock()); + + // Store a non-null value into the box. + std(box, BasicLock::displaced_header_offset_in_bytes(), box); + +# ifdef ASSERT + bne(flag, cont); + // We have acquired the monitor, check some invariants. + addi(/*monitor=*/temp, temp, -ObjectMonitor::owner_offset_in_bytes()); + // Invariant 1: _recursions should be 0. + //assert(ObjectMonitor::recursions_size_in_bytes() == 8, "unexpected size"); + asm_assert_mem8_is_zero(ObjectMonitor::recursions_offset_in_bytes(), temp, + "monitor->_recursions should be 0", -1); + // Invariant 2: OwnerIsThread shouldn't be 0. + //assert(ObjectMonitor::OwnerIsThread_size_in_bytes() == 4, "unexpected size"); + //asm_assert_mem4_isnot_zero(ObjectMonitor::OwnerIsThread_offset_in_bytes(), temp, + // "monitor->OwnerIsThread shouldn't be 0", -1); +# endif + } + + bind(cont); + // flag == EQ indicates success + // flag == NE indicates failure +} + +void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Register oop, Register box, + Register temp, Register displaced_header, Register current_header) { + assert_different_registers(oop, box, temp, displaced_header, current_header); + assert(flag != CCR0, "bad condition register"); + Label cont; + Label object_has_monitor; + + // Always do locking in runtime. + if (EmitSync & 0x01) { + cmpdi(flag, oop, 0); // Oop can't be 0 here => always false. + return; + } + + if (UseBiasedLocking) { + biased_locking_exit(flag, oop, current_header, cont); + } + + // Find the lock address and load the displaced header from the stack. + ld(displaced_header, BasicLock::displaced_header_offset_in_bytes(), box); + + // If the displaced header is 0, we have a recursive unlock. + cmpdi(flag, displaced_header, 0); + beq(flag, cont); + + // Handle existing monitor. + if ((EmitSync & 0x02) == 0) { + // The object has an existing monitor iff (mark & monitor_value) != 0. + ld(current_header, oopDesc::mark_offset_in_bytes(), oop); + andi(temp, current_header, markOopDesc::monitor_value); + cmpdi(flag, temp, 0); + bne(flag, object_has_monitor); + } + + + // Check if it is still a light weight lock, this is is true if we see + // the stack address of the basicLock in the markOop of the object. + // Cmpxchg sets flag to cmpd(current_header, box). + cmpxchgd(/*flag=*/flag, + /*current_value=*/current_header, + /*compare_value=*/box, + /*exchange_value=*/displaced_header, + /*where=*/oop, + MacroAssembler::MemBarRel, + MacroAssembler::cmpxchgx_hint_release_lock(), + noreg, + &cont); + + assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + + // Handle existing monitor. + if ((EmitSync & 0x02) == 0) { + b(cont); + + bind(object_has_monitor); + addi(current_header, current_header, -markOopDesc::monitor_value); // monitor + ld(temp, ObjectMonitor::owner_offset_in_bytes(), current_header); + ld(displaced_header, ObjectMonitor::recursions_offset_in_bytes(), current_header); + xorr(temp, R16_thread, temp); // Will be 0 if we are the owner. + orr(temp, temp, displaced_header); // Will be 0 if there are 0 recursions. + cmpdi(flag, temp, 0); + bne(flag, cont); + + ld(temp, ObjectMonitor::EntryList_offset_in_bytes(), current_header); + ld(displaced_header, ObjectMonitor::cxq_offset_in_bytes(), current_header); + orr(temp, temp, displaced_header); // Will be 0 if both are 0. + cmpdi(flag, temp, 0); + bne(flag, cont); + release(); + std(temp, ObjectMonitor::owner_offset_in_bytes(), current_header); + } + + bind(cont); + // flag == EQ indicates success + // flag == NE indicates failure +} + +// Write serialization page so VM thread can do a pseudo remote membar. +// We use the current thread pointer to calculate a thread specific +// offset to write to within the page. This minimizes bus traffic +// due to cache line collision. +void MacroAssembler::serialize_memory(Register thread, Register tmp1, Register tmp2) { + srdi(tmp2, thread, os::get_serialize_page_shift_count()); + + int mask = os::vm_page_size() - sizeof(int); + if (Assembler::is_simm(mask, 16)) { + andi(tmp2, tmp2, mask); + } else { + lis(tmp1, (int)((signed short) (mask >> 16))); + ori(tmp1, tmp1, mask & 0x0000ffff); + andr(tmp2, tmp2, tmp1); + } + + load_const(tmp1, (long) os::get_memory_serialize_page()); + release(); + stwx(R0, tmp1, tmp2); +} + + +// GC barrier helper macros + +// Write the card table byte if needed. +void MacroAssembler::card_write_barrier_post(Register Rstore_addr, Register Rnew_val, Register Rtmp) { + CardTableModRefBS* bs = (CardTableModRefBS*) Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::CardTableModRef || + bs->kind() == BarrierSet::CardTableExtension, "wrong barrier"); +#ifdef ASSERT + cmpdi(CCR0, Rnew_val, 0); + asm_assert_ne("null oop not allowed", 0x321); +#endif + card_table_write(bs->byte_map_base, Rtmp, Rstore_addr); +} + +// Write the card table byte. +void MacroAssembler::card_table_write(jbyte* byte_map_base, Register Rtmp, Register Robj) { + assert_different_registers(Robj, Rtmp, R0); + load_const_optimized(Rtmp, (address)byte_map_base, R0); + srdi(Robj, Robj, CardTableModRefBS::card_shift); + li(R0, 0); // dirty + if (UseConcMarkSweepGC) membar(Assembler::StoreStore); + stbx(R0, Rtmp, Robj); +} + +#ifndef SERIALGC + +// General G1 pre-barrier generator. +// Goal: record the previous value if it is not null. +void MacroAssembler::g1_write_barrier_pre(Register Robj, RegisterOrConstant offset, Register Rpre_val, + Register Rtmp1, Register Rtmp2, bool needs_frame) { + Label runtime, filtered; + + // Is marking active? + if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { + lwz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + } else { + guarantee(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); + lbz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + } + cmpdi(CCR0, Rtmp1, 0); + beq(CCR0, filtered); + + // Do we need to load the previous value? + if (Robj != noreg) { + // Load the previous value... + if (UseCompressedOops) { + lwz(Rpre_val, offset, Robj); + } else { + ld(Rpre_val, offset, Robj); + } + // Previous value has been loaded into Rpre_val. + } + assert(Rpre_val != noreg, "must have a real register"); + + // Is the previous value null? + cmpdi(CCR0, Rpre_val, 0); + beq(CCR0, filtered); + + if (Robj != noreg && UseCompressedOops) { + decode_heap_oop_not_null(Rpre_val); + } + + // OK, it's not filtered, so we'll need to call enqueue. In the normal + // case, pre_val will be a scratch G-reg, but there are some cases in + // which it's an O-reg. In the first case, do a normal call. In the + // latter, do a save here and call the frameless version. + + // Can we store original value in the thread's buffer? + // Is index == 0? + // (The index field is typed as size_t.) + const Register Rbuffer = Rtmp1, Rindex = Rtmp2; + + ld(Rindex, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + cmpdi(CCR0, Rindex, 0); + beq(CCR0, runtime); // If index == 0, goto runtime. + ld(Rbuffer, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_buf()), R16_thread); + + addi(Rindex, Rindex, -wordSize); // Decrement index. + std(Rindex, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + + // Record the previous value. + stdx(Rpre_val, Rbuffer, Rindex); + b(filtered); + + bind(runtime); + + // VM call need frame to access(write) O register. + if (needs_frame) { + save_LR_CR(Rtmp1); + push_frame_reg_args(0, Rtmp2); + } + + if (Rpre_val->is_volatile() && Robj == noreg) mr(R31, Rpre_val); // Save pre_val across C call if it was preloaded. + call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_pre), Rpre_val, R16_thread); + if (Rpre_val->is_volatile() && Robj == noreg) mr(Rpre_val, R31); // restore + + if (needs_frame) { + pop_frame(); + restore_LR_CR(Rtmp1); + } + + bind(filtered); +} + +// General G1 post-barrier generator +// Store cross-region card. +void MacroAssembler::g1_write_barrier_post(Register Rstore_addr, Register Rnew_val, Register Rtmp1, Register Rtmp2, Register Rtmp3, Label *filtered_ext) { + Label runtime, filtered_int; + Label& filtered = (filtered_ext != NULL) ? *filtered_ext : filtered_int; + assert_different_registers(Rstore_addr, Rnew_val, Rtmp1, Rtmp2); + + G1SATBCardTableModRefBS* bs = (G1SATBCardTableModRefBS*) Universe::heap()->barrier_set(); + assert(bs->kind() == BarrierSet::G1SATBCT || + bs->kind() == BarrierSet::G1SATBCTLogging, "wrong barrier"); + + // Does store cross heap regions? + if (G1RSBarrierRegionFilter) { + xorr(Rtmp1, Rstore_addr, Rnew_val); + srdi_(Rtmp1, Rtmp1, HeapRegion::LogOfHRGrainBytes); + beq(CCR0, filtered); + } + + // Crosses regions, storing NULL? +#ifdef ASSERT + cmpdi(CCR0, Rnew_val, 0); + asm_assert_ne("null oop not allowed (G1)", 0x322); // Checked by caller on PPC64, so following branch is obsolete: + //beq(CCR0, filtered); +#endif + + // Storing region crossing non-NULL, is card already dirty? + assert(sizeof(*bs->byte_map_base) == sizeof(jbyte), "adjust this code"); + const Register Rcard_addr = Rtmp1; + Register Rbase = Rtmp2; + load_const_optimized(Rbase, (address)bs->byte_map_base, /*temp*/ Rtmp3); + + srdi(Rcard_addr, Rstore_addr, CardTableModRefBS::card_shift); + + // Get the address of the card. + lbzx(/*card value*/ Rtmp3, Rbase, Rcard_addr); + + assert(CardTableModRefBS::dirty_card_val() == 0, "otherwise check this code"); + cmpwi(CCR0, Rtmp3 /* card value */, 0); + beq(CCR0, filtered); + + // Storing a region crossing, non-NULL oop, card is clean. + // Dirty card and log. + li(Rtmp3, 0); // dirty + //release(); // G1: oops are allowed to get visible after dirty marking. + stbx(Rtmp3, Rbase, Rcard_addr); + + add(Rcard_addr, Rbase, Rcard_addr); // This is the address which needs to get enqueued. + Rbase = noreg; // end of lifetime + + const Register Rqueue_index = Rtmp2, + Rqueue_buf = Rtmp3; + ld(Rqueue_index, in_bytes(JavaThread::dirty_card_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + cmpdi(CCR0, Rqueue_index, 0); + beq(CCR0, runtime); // index == 0 then jump to runtime + ld(Rqueue_buf, in_bytes(JavaThread::dirty_card_queue_offset() + PtrQueue::byte_offset_of_buf()), R16_thread); + + addi(Rqueue_index, Rqueue_index, -wordSize); // decrement index + std(Rqueue_index, in_bytes(JavaThread::dirty_card_queue_offset() + PtrQueue::byte_offset_of_index()), R16_thread); + + stdx(Rcard_addr, Rqueue_buf, Rqueue_index); // store card + b(filtered); + + bind(runtime); + + // Save the live input values. + call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), Rcard_addr, R16_thread); + + bind(filtered_int); +} +#endif // SERIALGC + +// Values for last_Java_pc, and last_Java_sp must comply to the rules +// in frame_ppc64.hpp. +void MacroAssembler::set_last_Java_frame(Register last_Java_sp, Register last_Java_pc) { + // Always set last_Java_pc and flags first because once last_Java_sp + // is visible has_last_Java_frame is true and users will look at the + // rest of the fields. (Note: flags should always be zero before we + // get here so doesn't need to be set.) + + // Verify that last_Java_pc was zeroed on return to Java + asm_assert_mem8_is_zero(in_bytes(JavaThread::last_Java_pc_offset()), R16_thread, + "last_Java_pc not zeroed before leaving Java", 0x200); + + // When returning from calling out from Java mode the frame anchor's + // last_Java_pc will always be set to NULL. It is set here so that + // if we are doing a call to native (not VM) that we capture the + // known pc and don't have to rely on the native call having a + // standard frame linkage where we can find the pc. + if (last_Java_pc != noreg) + std(last_Java_pc, in_bytes(JavaThread::last_Java_pc_offset()), R16_thread); + + // Set last_Java_sp last. + std(last_Java_sp, in_bytes(JavaThread::last_Java_sp_offset()), R16_thread); +} + +void MacroAssembler::reset_last_Java_frame(void) { + asm_assert_mem8_isnot_zero(in_bytes(JavaThread::last_Java_sp_offset()), + R16_thread, "SP was not set, still zero", 0x202); + + BLOCK_COMMENT("reset_last_Java_frame {"); + li(R0, 0); + + // _last_Java_sp = 0 + std(R0, in_bytes(JavaThread::last_Java_sp_offset()), R16_thread); + + // _last_Java_pc = 0 + std(R0, in_bytes(JavaThread::last_Java_pc_offset()), R16_thread); + BLOCK_COMMENT("} reset_last_Java_frame"); +} + +void MacroAssembler::set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1) { + assert_different_registers(sp, tmp1); + + // sp points to a TOP_IJAVA_FRAME, retrieve frame's PC via + // TOP_IJAVA_FRAME_ABI. + // FIXME: assert that we really have a TOP_IJAVA_FRAME here! +#ifdef CC_INTERP + ld(tmp1/*pc*/, _top_ijava_frame_abi(frame_manager_lr), sp); +#else + address entry = pc(); + load_const_optimized(tmp1, entry); +#endif + + set_last_Java_frame(/*sp=*/sp, /*pc=*/tmp1); +} + +void MacroAssembler::get_vm_result(Register oop_result) { + // Read: + // R16_thread + // R16_thread->in_bytes(JavaThread::vm_result_offset()) + // + // Updated: + // oop_result + // R16_thread->in_bytes(JavaThread::vm_result_offset()) + + ld(oop_result, in_bytes(JavaThread::vm_result_offset()), R16_thread); + li(R0, 0); + std(R0, in_bytes(JavaThread::vm_result_offset()), R16_thread); + + verify_oop(oop_result); +} + +// Clear Array +// Kills both input registers. tmp == R0 is allowed. +void MacroAssembler::clear_memory_doubleword(Register base_ptr, Register cnt_dwords, Register tmp) { + // Procedure for large arrays (uses data cache block zero instruction). + Label startloop, fast, fastloop, small_rest, restloop, done; + const int cl_size = VM_Version::get_cache_line_size(), + cl_dwords = cl_size>>3, + cl_dw_addr_bits = exact_log2(cl_dwords), + dcbz_min = 1; // Min count of dcbz executions, needs to be >0. + +//2: + cmpdi(CCR1, cnt_dwords, ((dcbz_min+1)<=dcbz_min lines included). + blt(CCR1, small_rest); // Too small. + rldicl_(tmp, base_ptr, 64-3, 64-cl_dw_addr_bits); // Extract dword offset within first cache line. + beq(CCR0, fast); // Already 128byte aligned. + + subfic(tmp, tmp, cl_dwords); + mtctr(tmp); // Set ctr to hit 128byte boundary (00). + andi(cnt_dwords, cnt_dwords, cl_dwords-1); // Rest in dwords. + mtctr(tmp); // Load counter. +//16: + bind(fastloop); + dcbz(base_ptr); // Clear 128byte aligned block. + addi(base_ptr, base_ptr, cl_size); + bdnz(fastloop); + if (InsertEndGroupPPC64) { endgroup(); } else { nop(); } +//20: + bind(small_rest); + cmpdi(CCR0, cnt_dwords, 0); // size 0? + beq(CCR0, done); // rest == 0 + li(tmp, 0); + mtctr(cnt_dwords); // Load counter. +//24: + bind(restloop); // Clear rest. + std(tmp, 0, base_ptr); // Clear 8byte aligned block. + addi(base_ptr, base_ptr, 8); + bdnz(restloop); +//27: + bind(done); +} + +/////////////////////////////////////////// String intrinsics //////////////////////////////////////////// + +// Search for a single jchar in an jchar[]. +// +// Assumes that result differs from all other registers. +// +// Haystack, needle are the addresses of jchar-arrays. +// NeedleChar is needle[0] if it is known at compile time. +// Haycnt is the length of the haystack. We assume haycnt >=1. +// +// Preserves haystack, haycnt, kills all other registers. +// +// If needle == R0, we search for the constant needleChar. +void MacroAssembler::string_indexof_1(Register result, Register haystack, Register haycnt, + Register needle, jchar needleChar, + Register tmp1, Register tmp2) { + + assert_different_registers(result, haystack, haycnt, needle, tmp1, tmp2); + + Label L_InnerLoop, L_FinalCheck, L_Found1, L_Found2, L_Found3, L_NotFound, L_End; + Register needle0 = needle, // Contains needle[0]. + addr = tmp1, + ch1 = tmp2, + ch2 = R0; + +//2 (variable) or 3 (const): + if (needle != R0) lhz(needle0, 0, needle); // Preload needle character, needle has len==1. + dcbtct(haystack, 0x00); // Indicate R/O access to haystack. + + srwi_(tmp2, haycnt, 1); // Shift right by exact_log2(UNROLL_FACTOR). + mr(addr, haystack); + beq(CCR0, L_FinalCheck); + mtctr(tmp2); // Move to count register. +//8: + bind(L_InnerLoop); // Main work horse (2x unrolled search loop). + lhz(ch1, 0, addr); // Load characters from haystack. + lhz(ch2, 2, addr); + (needle != R0) ? cmpw(CCR0, ch1, needle0) : cmplwi(CCR0, ch1, needleChar); + (needle != R0) ? cmpw(CCR1, ch2, needle0) : cmplwi(CCR1, ch2, needleChar); + beq(CCR0, L_Found1); // Did we find the needle? + beq(CCR1, L_Found2); + addi(addr, addr, 4); + bdnz(L_InnerLoop); +//16: + bind(L_FinalCheck); + andi_(R0, haycnt, 1); + beq(CCR0, L_NotFound); + lhz(ch1, 0, addr); // One position left at which we have to compare. + (needle != R0) ? cmpw(CCR1, ch1, needle0) : cmplwi(CCR1, ch1, needleChar); + beq(CCR1, L_Found3); +//21: + bind(L_NotFound); + li(result, -1); // Not found. + b(L_End); + + bind(L_Found2); + addi(addr, addr, 2); +//24: + bind(L_Found1); + bind(L_Found3); // Return index ... + subf(addr, haystack, addr); // relative to haystack, + srdi(result, addr, 1); // in characters. + bind(L_End); +} + + +// Implementation of IndexOf for jchar arrays. +// +// The length of haystack and needle are not constant, i.e. passed in a register. +// +// Preserves registers haystack, needle. +// Kills registers haycnt, needlecnt. +// Assumes that result differs from all other registers. +// Haystack, needle are the addresses of jchar-arrays. +// Haycnt, needlecnt are the lengths of them, respectively. +// +// Needlecntval must be zero or 15-bit unsigned immediate and > 1. +void MacroAssembler::string_indexof(Register result, Register haystack, Register haycnt, + Register needle, ciTypeArray* needle_values, Register needlecnt, int needlecntval, + Register tmp1, Register tmp2, Register tmp3, Register tmp4) { + + // Ensure 0=2, bail out otherwise. + // ************************************************************************************************** + +//1 (variable) or 3 (const): + dcbtct(needle, 0x00); // Indicate R/O access to str1. + dcbtct(haystack, 0x00); // Indicate R/O access to str2. + + // Compute last haystack addr to use if no match gets found. + if (needlecntval == 0) { // variable needlecnt +//3: + subf(ch1, needlecnt, haycnt); // Last character index to compare is haycnt-needlecnt. + addi(addr, haystack, -2); // Accesses use pre-increment. + cmpwi(CCR6, needlecnt, 2); + blt(CCR6, L_TooShort); // Variable needlecnt: handle short needle separately. + slwi(ch1, ch1, 1); // Scale to number of bytes. + lwz(n_start, 0, needle); // Load first 2 characters of needle. + add(last_addr, haystack, ch1); // Point to last address to compare (haystack+2*(haycnt-needlecnt)). + addi(needlecnt, needlecnt, -2); // Rest of needle. + } else { // constant needlecnt + guarantee(needlecntval != 1, "IndexOf with single-character needle must be handled separately"); + assert((needlecntval & 0x7fff) == needlecntval, "wrong immediate"); +//5: + addi(ch1, haycnt, -needlecntval); // Last character index to compare is haycnt-needlecnt. + lwz(n_start, 0, needle); // Load first 2 characters of needle. + addi(addr, haystack, -2); // Accesses use pre-increment. + slwi(ch1, ch1, 1); // Scale to number of bytes. + add(last_addr, haystack, ch1); // Point to last address to compare (haystack+2*(haycnt-needlecnt)). + li(needlecnt, needlecntval-2); // Rest of needle. + } + + // Main Loop (now we have at least 3 characters). +//11: + Label L_OuterLoop, L_InnerLoop, L_FinalCheck, L_Comp1, L_Comp2, L_Comp3; + bind(L_OuterLoop); // Search for 1st 2 characters. + Register addr_diff = tmp4; + subf(addr_diff, addr, last_addr); // Difference between already checked address and last address to check. + addi(addr, addr, 2); // This is the new address we want to use for comparing. + srdi_(ch2, addr_diff, 2); + beq(CCR0, L_FinalCheck); // 2 characters left? + mtctr(ch2); // addr_diff/4 +//16: + bind(L_InnerLoop); // Main work horse (2x unrolled search loop) + lwz(ch1, 0, addr); // Load 2 characters of haystack (ignore alignment). + lwz(ch2, 2, addr); + cmpw(CCR0, ch1, n_start); // Compare 2 characters (1 would be sufficient but try to reduce branches to CompLoop). + cmpw(CCR1, ch2, n_start); + beq(CCR0, L_Comp1); // Did we find the needle start? + beq(CCR1, L_Comp2); + addi(addr, addr, 4); + bdnz(L_InnerLoop); +//24: + bind(L_FinalCheck); + rldicl_(addr_diff, addr_diff, 64-1, 63); // Remaining characters not covered by InnerLoop: (addr_diff>>1)&1. + beq(CCR0, L_NotFound); + lwz(ch1, 0, addr); // One position left at which we have to compare. + cmpw(CCR1, ch1, n_start); + beq(CCR1, L_Comp3); +//29: + bind(L_NotFound); + li(result, -1); // not found + b(L_End); + + + // ************************************************************************************************** + // Special Case: unfortunately, the variable needle case can be called with needlecnt<2 + // ************************************************************************************************** +//31: + if ((needlecntval>>1) !=1 ) { // Const needlecnt is 2 or 3? Reduce code size. + int nopcnt = 5; + if (needlecntval !=0 ) ++nopcnt; // Balance alignment (other case: see below). + if (needlecntval == 0) { // We have to handle these cases separately. + Label L_OneCharLoop; + bind(L_TooShort); + mtctr(haycnt); + lhz(n_start, 0, needle); // First character of needle + bind(L_OneCharLoop); + lhzu(ch1, 2, addr); + cmpw(CCR1, ch1, n_start); + beq(CCR1, L_Found); // Did we find the one character needle? + bdnz(L_OneCharLoop); + li(result, -1); // Not found. + b(L_End); + } // 8 instructions, so no impact on alignment. + for (int x = 0; x < nopcnt; ++x) nop(); + } + + // ************************************************************************************************** + // Regular Case Part II: compare rest of needle (first 2 characters have been compared already) + // ************************************************************************************************** + + // Compare the rest +//36 if needlecntval==0, else 37: + bind(L_Comp2); + addi(addr, addr, 2); // First comparison has failed, 2nd one hit. + bind(L_Comp1); // Addr points to possible needle start. + bind(L_Comp3); // Could have created a copy and use a different return address but saving code size here. + if (needlecntval != 2) { // Const needlecnt==2? + if (needlecntval != 3) { + if (needlecntval == 0) beq(CCR6, L_Found); // Variable needlecnt==2? + Register ind_reg = tmp4; + li(ind_reg, 2*2); // First 2 characters are already compared, use index 2. + mtctr(needlecnt); // Decremented by 2, still > 0. +//40: + Label L_CompLoop; + bind(L_CompLoop); + lhzx(ch2, needle, ind_reg); + lhzx(ch1, addr, ind_reg); + cmpw(CCR1, ch1, ch2); + bne(CCR1, L_OuterLoop); + addi(ind_reg, ind_reg, 2); + bdnz(L_CompLoop); + } else { // No loop required if there's only one needle character left. + lhz(ch2, 2*2, needle); + lhz(ch1, 2*2, addr); + cmpw(CCR1, ch1, ch2); + bne(CCR1, L_OuterLoop); + } + } + // Return index ... +//46: + bind(L_Found); + subf(addr, haystack, addr); // relative to haystack, ... + srdi(result, addr, 1); // in characters. +//48: + bind(L_End); +} + +// Implementation of Compare for jchar arrays. +// +// Kills the registers str1, str2, cnt1, cnt2. +// Kills cr0, ctr. +// Assumes that result differes from the input registers. +void MacroAssembler::string_compare(Register str1_reg, Register str2_reg, Register cnt1_reg, Register cnt2_reg, + Register result_reg, Register tmp_reg) { + assert_different_registers(result_reg, str1_reg, str2_reg, cnt1_reg, cnt2_reg, tmp_reg); + + Label Ldone, Lslow_case, Lslow_loop, Lfast_loop; + Register cnt_diff = R0, + limit_reg = cnt1_reg, + chr1_reg = result_reg, + chr2_reg = cnt2_reg, + addr_diff = str2_reg; + + // Offset 0 should be 32 byte aligned. +//-4: + dcbtct(str1_reg, 0x00); // Indicate R/O access to str1. + dcbtct(str2_reg, 0x00); // Indicate R/O access to str2. +//-2: + // Compute min(cnt1, cnt2) and check if 0 (bail out if we don't need to compare characters). + subf(result_reg, cnt2_reg, cnt1_reg); // difference between cnt1/2 + subf_(addr_diff, str1_reg, str2_reg); // alias? + beq(CCR0, Ldone); // return cnt difference if both ones are identical + srawi(limit_reg, result_reg, 31); // generate signmask (cnt1/2 must be non-negative so cnt_diff can't overflow) + mr(cnt_diff, result_reg); + andr(limit_reg, result_reg, limit_reg); // difference or zero (negative): cnt14 characters for fast loop + andi(limit_reg, tmp_reg, 4-1); // remaining characters + + // Adapt str1_reg str2_reg for the first loop iteration + mtctr(chr2_reg); // (min(cnt1, cnt2)-1)/4 + addi(limit_reg, limit_reg, 4+1); // compare last 5-8 characters in slow_case if mismatch found in fast_loop +//16: + // Compare the rest of the characters + bind(Lfast_loop); + ld(chr1_reg, 0, str1_reg); + ldx(chr2_reg, str1_reg, addr_diff); + cmpd(CCR0, chr2_reg, chr1_reg); + bne(CCR0, Lslow_case); // return chr1_reg + addi(str1_reg, str1_reg, 4*2); + bdnz(Lfast_loop); + addi(limit_reg, limit_reg, -4); // no mismatch found in fast_loop, only 1-4 characters missing +//23: + bind(Lslow_case); + mtctr(limit_reg); +//24: + bind(Lslow_loop); + lhz(chr1_reg, 0, str1_reg); + lhzx(chr2_reg, str1_reg, addr_diff); + subf_(result_reg, chr2_reg, chr1_reg); + bne(CCR0, Ldone); // return chr1_reg + addi(str1_reg, str1_reg, 1*2); + bdnz(Lslow_loop); +//30: + // If strings are equal up to min length, return the length difference. + mr(result_reg, cnt_diff); + nop(); // alignment +//32: + // Otherwise, return the difference between the first mismatched chars. + bind(Ldone); +} + + +// Compare char[] arrays. +// +// str1_reg USE only +// str2_reg USE only +// cnt_reg USE_DEF, due to tmp reg shortage +// result_reg DEF only, might compromise USE only registers +void MacroAssembler::char_arrays_equals(Register str1_reg, Register str2_reg, Register cnt_reg, Register result_reg, + Register tmp1_reg, Register tmp2_reg, Register tmp3_reg, Register tmp4_reg, + Register tmp5_reg) { + + // Str1 may be the same register as str2 which can occur e.g. after scalar replacement. + assert_different_registers(result_reg, str1_reg, cnt_reg, tmp1_reg, tmp2_reg, tmp3_reg, tmp4_reg, tmp5_reg); + assert_different_registers(result_reg, str2_reg, cnt_reg, tmp1_reg, tmp2_reg, tmp3_reg, tmp4_reg, tmp5_reg); + + // Offset 0 should be 32 byte aligned. + Label Linit_cbc, Lcbc, Lloop, Ldone_true, Ldone_false; + Register index_reg = tmp5_reg; + Register cbc_iter = tmp4_reg; + +//-1: + dcbtct(str1_reg, 0x00); // Indicate R/O access to str1. + dcbtct(str2_reg, 0x00); // Indicate R/O access to str2. +//1: + andi(cbc_iter, cnt_reg, 4-1); // Remaining iterations after 4 java characters per iteration loop. + li(index_reg, 0); // init + li(result_reg, 0); // assume false + srwi_(tmp2_reg, cnt_reg, exact_log2(4)); // Div: 4 java characters per iteration (main loop). + + cmpwi(CCR1, cbc_iter, 0); // CCR1 = (cbc_iter==0) + beq(CCR0, Linit_cbc); // too short + mtctr(tmp2_reg); +//8: + bind(Lloop); + ldx(tmp1_reg, str1_reg, index_reg); + ldx(tmp2_reg, str2_reg, index_reg); + cmpd(CCR0, tmp1_reg, tmp2_reg); + bne(CCR0, Ldone_false); // Unequal char pair found -> done. + addi(index_reg, index_reg, 4*sizeof(jchar)); + bdnz(Lloop); +//14: + bind(Linit_cbc); + beq(CCR1, Ldone_true); + mtctr(cbc_iter); +//16: + bind(Lcbc); + lhzx(tmp1_reg, str1_reg, index_reg); + lhzx(tmp2_reg, str2_reg, index_reg); + cmpw(CCR0, tmp1_reg, tmp2_reg); + bne(CCR0, Ldone_false); // Unequal char pair found -> done. + addi(index_reg, index_reg, 1*sizeof(jchar)); + bdnz(Lcbc); + nop(); + bind(Ldone_true); + li(result_reg, 1); +//24: + bind(Ldone_false); +} + + +void MacroAssembler::char_arrays_equalsImm(Register str1_reg, Register str2_reg, int cntval, Register result_reg, + Register tmp1_reg, Register tmp2_reg) { + // Str1 may be the same register as str2 which can occur e.g. after scalar replacement. + assert_different_registers(result_reg, str1_reg, tmp1_reg, tmp2_reg); + assert_different_registers(result_reg, str2_reg, tmp1_reg, tmp2_reg); + assert(sizeof(jchar) == 2, "must be"); + assert(cntval >= 0 && ((cntval & 0x7fff) == cntval), "wrong immediate"); + + Label Ldone_false; + + if (cntval < 16) { // short case + if (cntval != 0) li(result_reg, 0); // assume false + + const int num_bytes = cntval*sizeof(jchar); + int index = 0; + for (int next_index; (next_index = index + 8) <= num_bytes; index = next_index) { + ld(tmp1_reg, index, str1_reg); + ld(tmp2_reg, index, str2_reg); + cmpd(CCR0, tmp1_reg, tmp2_reg); + bne(CCR0, Ldone_false); + } + if (cntval & 2) { + lwz(tmp1_reg, index, str1_reg); + lwz(tmp2_reg, index, str2_reg); + cmpw(CCR0, tmp1_reg, tmp2_reg); + bne(CCR0, Ldone_false); + index += 4; + } + if (cntval & 1) { + lhz(tmp1_reg, index, str1_reg); + lhz(tmp2_reg, index, str2_reg); + cmpw(CCR0, tmp1_reg, tmp2_reg); + bne(CCR0, Ldone_false); + } + // fallthrough: true + } else { + Label Lloop; + Register index_reg = tmp1_reg; + const int loopcnt = cntval/4; + assert(loopcnt > 0, "must be"); + // Offset 0 should be 32 byte aligned. + //2: + dcbtct(str1_reg, 0x00); // Indicate R/O access to str1. + dcbtct(str2_reg, 0x00); // Indicate R/O access to str2. + li(tmp2_reg, loopcnt); + li(index_reg, 0); // init + li(result_reg, 0); // assume false + mtctr(tmp2_reg); + //8: + bind(Lloop); + ldx(R0, str1_reg, index_reg); + ldx(tmp2_reg, str2_reg, index_reg); + cmpd(CCR0, R0, tmp2_reg); + bne(CCR0, Ldone_false); // Unequal char pair found -> done. + addi(index_reg, index_reg, 4*sizeof(jchar)); + bdnz(Lloop); + //14: + if (cntval & 2) { + lwzx(R0, str1_reg, index_reg); + lwzx(tmp2_reg, str2_reg, index_reg); + cmpw(CCR0, R0, tmp2_reg); + bne(CCR0, Ldone_false); + if (cntval & 1) addi(index_reg, index_reg, 2*sizeof(jchar)); + } + if (cntval & 1) { + lhzx(R0, str1_reg, index_reg); + lhzx(tmp2_reg, str2_reg, index_reg); + cmpw(CCR0, R0, tmp2_reg); + bne(CCR0, Ldone_false); + } + // fallthru: true + } + li(result_reg, 1); + bind(Ldone_false); +} + + +void MacroAssembler::asm_assert(bool check_equal, const char *msg, int id) { +#ifdef ASSERT + Label ok; + if (check_equal) { + beq(CCR0, ok); + } else { + bne(CCR0, ok); + } + stop(msg, id); + bind(ok); +#endif +} + +void MacroAssembler::asm_assert_mems_zero(bool check_equal, int size, int mem_offset, + Register mem_base, const char* msg, int id) { +#ifdef ASSERT + switch (size) { + case 4: + lwz(R0, mem_offset, mem_base); + cmpwi(CCR0, R0, 0); + break; + case 8: + ld(R0, mem_offset, mem_base); + cmpdi(CCR0, R0, 0); + break; + default: + ShouldNotReachHere(); + } + asm_assert(check_equal, msg, id); +#endif // ASSERT +} + +void MacroAssembler::verify_thread() { + if (VerifyThread) { + unimplemented("'VerifyThread' currently not implemented on PPC"); + } +} + +// READ: oop. KILL: R0. Volatile floats perhaps. +void MacroAssembler::verify_oop(Register oop, const char* msg) { + if (!VerifyOops) { + return; + } + // Will be preserved. + Register tmp = R11; + assert(oop != tmp, "precondition"); + unsigned int nbytes_save = 10*8; // 10 volatile gprs + address/* FunctionDescriptor** */fd = StubRoutines::verify_oop_subroutine_entry_address(); + // save tmp + mr(R0, tmp); + // kill tmp + save_LR_CR(tmp); + push_frame_reg_args(nbytes_save, tmp); + // restore tmp + mr(tmp, R0); + save_volatile_gprs(R1_SP, 112); // except R0 + // load FunctionDescriptor** / entry_address * + load_const(tmp, fd); + // load FunctionDescriptor* / entry_address + ld(tmp, 0, tmp); + mr(R4_ARG2, oop); + load_const(R3_ARG1, (address)msg); + // call destination for its side effect + call_c(tmp); + restore_volatile_gprs(R1_SP, 112); // except R0 + pop_frame(); + // save tmp + mr(R0, tmp); + // kill tmp + restore_LR_CR(tmp); + // restore tmp + mr(tmp, R0); +} + +const char* stop_types[] = { + "stop", + "untested", + "unimplemented", + "shouldnotreachhere" +}; + +static void stop_on_request(int tp, const char* msg) { + tty->print("PPC assembly code requires stop: (%s) %s\n", (void *)stop_types[tp%/*stop_end*/4], msg); + guarantee(false, err_msg("PPC assembly code requires stop: %s", msg)); +} + +// Call a C-function that prints output. +void MacroAssembler::stop(int type, const char* msg, int id) { +#ifndef PRODUCT + block_comment(err_msg("stop: %s %s {", stop_types[type%stop_end], msg)); +#else + block_comment("stop {"); +#endif + + // setup arguments + load_const_optimized(R3_ARG1, type); + load_const_optimized(R4_ARG2, (void *)msg, /*tmp=*/R0); + call_VM_leaf(CAST_FROM_FN_PTR(address, stop_on_request), R3_ARG1, R4_ARG2); + illtrap(); + emit_int32(id); + block_comment("} stop;"); +} + +#ifndef PRODUCT +// Write pattern 0x0101010101010101 in memory region [low-before, high+after]. +// Val, addr are temp registers. +// If low == addr, addr is killed. +// High is preserved. +void MacroAssembler::zap_from_to(Register low, int before, Register high, int after, Register val, Register addr) { + if (!ZapMemory) return; + + assert_different_registers(low, val); + + BLOCK_COMMENT("zap memory region {"); + load_const_optimized(val, 0x0101010101010101); + int size = before + after; + if (low == high && size < 5 && size > 0) { + int offset = -before*BytesPerWord; + for (int i = 0; i < size; ++i) { + std(val, offset, low); + offset += (1*BytesPerWord); + } + } else { + addi(addr, low, -before*BytesPerWord); + assert_different_registers(high, val); + if (after) addi(high, high, after * BytesPerWord); + Label loop; + bind(loop); + std(val, 0, addr); + addi(addr, addr, 8); + cmpd(CCR6, addr, high); + ble(CCR6, loop); + if (after) addi(high, high, -after * BytesPerWord); // Correct back to old value. + } + BLOCK_COMMENT("} zap memory region"); +} + +#endif // !PRODUCT + +SkipIfEqualZero::SkipIfEqualZero(MacroAssembler* masm, Register temp, const bool* flag_addr) : _masm(masm), _label() { + int simm16_offset = masm->load_const_optimized(temp, (address)flag_addr, R0, true); + assert(sizeof(bool) == 1, "PowerPC ABI"); + masm->lbz(temp, simm16_offset, temp); + masm->cmpwi(CCR0, temp, 0); + masm->beq(CCR0, _label); +} + +SkipIfEqualZero::~SkipIfEqualZero() { + _masm->bind(_label); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/macroAssembler_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/macroAssembler_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_MACROASSEMBLER_PPC_HPP +#define CPU_PPC_VM_MACROASSEMBLER_PPC_HPP + +#include "asm/assembler.hpp" + +// MacroAssembler extends Assembler by a few frequently used macros. + +class ciTypeArray; + +class MacroAssembler: public Assembler { + public: + MacroAssembler(CodeBuffer* code) : Assembler(code) {} + + // + // Optimized instruction emitters + // + + inline static int largeoffset_si16_si16_hi(int si31) { return (si31 + (1<<15)) >> 16; } + inline static int largeoffset_si16_si16_lo(int si31) { return si31 - (((si31 + (1<<15)) >> 16) << 16); } + + // load d = *[a+si31] + // Emits several instructions if the offset is not encodable in one instruction. + void ld_largeoffset_unchecked(Register d, int si31, Register a, int emit_filler_nop); + void ld_largeoffset (Register d, int si31, Register a, int emit_filler_nop); + inline static bool is_ld_largeoffset(address a); + inline static int get_ld_largeoffset_offset(address a); + + inline void round_to(Register r, int modulus); + + // Load/store with type given by parameter. + void load_sized_value( Register dst, RegisterOrConstant offs, Register base, size_t size_in_bytes, bool is_signed); + void store_sized_value(Register dst, RegisterOrConstant offs, Register base, size_t size_in_bytes); + + // Move register if destination register and target register are different + inline void mr_if_needed(Register rd, Register rs); + inline void fmr_if_needed(FloatRegister rd, FloatRegister rs); + // This is dedicated for emitting scheduled mach nodes. For better + // readability of the ad file I put it here. + // Endgroups are not needed if + // - the scheduler is off + // - the scheduler found that there is a natural group end, in that + // case it reduced the size of the instruction used in the test + // yielding 'needed'. + inline void endgroup_if_needed(bool needed); + + // Memory barriers. + inline void membar(int bits); + inline void release(); + inline void acquire(); + inline void fence(); + + // nop padding + void align(int modulus, int max = 252, int rem = 0); + + // + // Constants, loading constants, TOC support + // + + // Address of the global TOC. + inline static address global_toc(); + // Offset of given address to the global TOC. + inline static int offset_to_global_toc(const address addr); + + // Address of TOC of the current method. + inline address method_toc(); + // Offset of given address to TOC of the current method. + inline int offset_to_method_toc(const address addr); + + // Global TOC. + void calculate_address_from_global_toc(Register dst, address addr, + bool hi16 = true, bool lo16 = true, + bool add_relocation = true, bool emit_dummy_addr = false); + inline void calculate_address_from_global_toc_hi16only(Register dst, address addr) { + calculate_address_from_global_toc(dst, addr, true, false); + }; + inline void calculate_address_from_global_toc_lo16only(Register dst, address addr) { + calculate_address_from_global_toc(dst, addr, false, true); + }; + + inline static bool is_calculate_address_from_global_toc_at(address a, address bound); + static int patch_calculate_address_from_global_toc_at(address a, address addr, address bound); + static address get_address_of_calculate_address_from_global_toc_at(address a, address addr); + +#ifdef _LP64 + // Patch narrow oop constant. + inline static bool is_set_narrow_oop(address a, address bound); + static int patch_set_narrow_oop(address a, address bound, long data); + static int get_narrow_oop(address a, address bound); +#endif + + inline static bool is_load_const_at(address a); + + // Emits an oop const to the constant pool, loads the constant, and + // sets a relocation info with address current_pc. + void load_const_from_method_toc(Register dst, AddressLiteral& a, Register toc); + void load_toc_from_toc(Register dst, AddressLiteral& a, Register toc) { + assert(dst == R2_TOC, "base register must be TOC"); + load_const_from_method_toc(dst, a, toc); + } + + static bool is_load_const_from_method_toc_at(address a); + static int get_offset_of_load_const_from_method_toc_at(address a); + + // Get the 64 bit constant from a `load_const' sequence. + static long get_const(address load_const); + + // Patch the 64 bit constant of a `load_const' sequence. This is a + // low level procedure. It neither flushes the instruction cache nor + // is it atomic. + static void patch_const(address load_const, long x); + + // Oops used directly in compiled code are stored in the constant pool, + // and loaded from there. + // Allocate new entry for oop in constant pool. Generate relocation. + AddressLiteral allocate_oop_address(jobject obj); + // Find oop obj in constant pool. Return relocation with it's index. + AddressLiteral constant_oop_address(jobject obj); + + // Find oop in constant pool and emit instructions to load it. + // Uses constant_oop_address. + inline void set_oop_constant(jobject obj, Register d); + // Same as load_address. + inline void set_oop (AddressLiteral obj_addr, Register d); + + // Read runtime constant: Issue load if constant not yet established, + // else use real constant. + virtual RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, + Register tmp, + int offset); + + // + // branch, jump + // + + inline void pd_patch_instruction(address branch, address target); + NOT_PRODUCT(static void pd_print_patched_instruction(address branch);) + + // Conditional far branch for destinations encodable in 24+2 bits. + // Same interface as bc, e.g. no inverse boint-field. + enum { + bc_far_optimize_not = 0, + bc_far_optimize_on_relocate = 1 + }; + // optimize: flag for telling the conditional far branch to optimize + // itself when relocated. + void bc_far(int boint, int biint, Label& dest, int optimize); + // Relocation of conditional far branches. + static bool is_bc_far_at(address instruction_addr); + static address get_dest_of_bc_far_at(address instruction_addr); + static void set_dest_of_bc_far_at(address instruction_addr, address dest); + private: + static bool inline is_bc_far_variant1_at(address instruction_addr); + static bool inline is_bc_far_variant2_at(address instruction_addr); + static bool inline is_bc_far_variant3_at(address instruction_addr); + public: + + // Convenience bc_far versions. + inline void blt_far(ConditionRegister crx, Label& L, int optimize); + inline void bgt_far(ConditionRegister crx, Label& L, int optimize); + inline void beq_far(ConditionRegister crx, Label& L, int optimize); + inline void bso_far(ConditionRegister crx, Label& L, int optimize); + inline void bge_far(ConditionRegister crx, Label& L, int optimize); + inline void ble_far(ConditionRegister crx, Label& L, int optimize); + inline void bne_far(ConditionRegister crx, Label& L, int optimize); + inline void bns_far(ConditionRegister crx, Label& L, int optimize); + + // Emit, identify and patch a NOT mt-safe patchable 64 bit absolute call/jump. + private: + enum { + bxx64_patchable_instruction_count = (2/*load_codecache_const*/ + 3/*5load_const*/ + 1/*mtctr*/ + 1/*bctrl*/), + bxx64_patchable_size = bxx64_patchable_instruction_count * BytesPerInstWord, + bxx64_patchable_ret_addr_offset = bxx64_patchable_size + }; + void bxx64_patchable(address target, relocInfo::relocType rt, bool link); + static bool is_bxx64_patchable_at( address instruction_addr, bool link); + // Does the instruction use a pc-relative encoding of the destination? + static bool is_bxx64_patchable_pcrelative_at( address instruction_addr, bool link); + static bool is_bxx64_patchable_variant1_at( address instruction_addr, bool link); + // Load destination relative to global toc. + static bool is_bxx64_patchable_variant1b_at( address instruction_addr, bool link); + static bool is_bxx64_patchable_variant2_at( address instruction_addr, bool link); + static void set_dest_of_bxx64_patchable_at( address instruction_addr, address target, bool link); + static address get_dest_of_bxx64_patchable_at(address instruction_addr, bool link); + + public: + // call + enum { + bl64_patchable_instruction_count = bxx64_patchable_instruction_count, + bl64_patchable_size = bxx64_patchable_size, + bl64_patchable_ret_addr_offset = bxx64_patchable_ret_addr_offset + }; + inline void bl64_patchable(address target, relocInfo::relocType rt) { + bxx64_patchable(target, rt, /*link=*/true); + } + inline static bool is_bl64_patchable_at(address instruction_addr) { + return is_bxx64_patchable_at(instruction_addr, /*link=*/true); + } + inline static bool is_bl64_patchable_pcrelative_at(address instruction_addr) { + return is_bxx64_patchable_pcrelative_at(instruction_addr, /*link=*/true); + } + inline static void set_dest_of_bl64_patchable_at(address instruction_addr, address target) { + set_dest_of_bxx64_patchable_at(instruction_addr, target, /*link=*/true); + } + inline static address get_dest_of_bl64_patchable_at(address instruction_addr) { + return get_dest_of_bxx64_patchable_at(instruction_addr, /*link=*/true); + } + // jump + enum { + b64_patchable_instruction_count = bxx64_patchable_instruction_count, + b64_patchable_size = bxx64_patchable_size, + }; + inline void b64_patchable(address target, relocInfo::relocType rt) { + bxx64_patchable(target, rt, /*link=*/false); + } + inline static bool is_b64_patchable_at(address instruction_addr) { + return is_bxx64_patchable_at(instruction_addr, /*link=*/false); + } + inline static bool is_b64_patchable_pcrelative_at(address instruction_addr) { + return is_bxx64_patchable_pcrelative_at(instruction_addr, /*link=*/false); + } + inline static void set_dest_of_b64_patchable_at(address instruction_addr, address target) { + set_dest_of_bxx64_patchable_at(instruction_addr, target, /*link=*/false); + } + inline static address get_dest_of_b64_patchable_at(address instruction_addr) { + return get_dest_of_bxx64_patchable_at(instruction_addr, /*link=*/false); + } + + // + // Support for frame handling + // + + // some ABI-related functions + void save_nonvolatile_gprs( Register dst_base, int offset); + void restore_nonvolatile_gprs(Register src_base, int offset); + void save_volatile_gprs( Register dst_base, int offset); + void restore_volatile_gprs(Register src_base, int offset); + void save_LR_CR( Register tmp); // tmp contains LR on return. + void restore_LR_CR(Register tmp); + + // Get current PC using bl-next-instruction trick. + address get_PC_trash_LR(Register result); + + // Resize current frame either relatively wrt to current SP or absolute. + void resize_frame(Register offset, Register tmp); + void resize_frame(int offset, Register tmp); + void resize_frame_absolute(Register addr, Register tmp1, Register tmp2); + + // Push a frame of size bytes. + void push_frame(Register bytes, Register tmp); + + // Push a frame of size `bytes'. No abi space provided. + void push_frame(unsigned int bytes, Register tmp); + + // Push a frame of size `bytes' plus abi_reg_args on top. + void push_frame_reg_args(unsigned int bytes, Register tmp); + + // Setup up a new C frame with a spill area for non-volatile GPRs and additional + // space for local variables + void push_frame_reg_args_nonvolatiles(unsigned int bytes, Register tmp); + + // pop current C frame + void pop_frame(); + + // + // Calls + // + + private: + address _last_calls_return_pc; + +#if defined(ABI_ELFv2) + // Generic version of a call to C function. + // Updates and returns _last_calls_return_pc. + address branch_to(Register function_entry, bool and_link); +#else + // Generic version of a call to C function via a function descriptor + // with variable support for C calling conventions (TOC, ENV, etc.). + // updates and returns _last_calls_return_pc. + address branch_to(Register function_descriptor, bool and_link, bool save_toc_before_call, + bool restore_toc_after_call, bool load_toc_of_callee, bool load_env_of_callee); +#endif + + public: + + // Get the pc where the last call will return to. returns _last_calls_return_pc. + inline address last_calls_return_pc(); + +#if defined(ABI_ELFv2) + // Call a C function via a function descriptor and use full C + // calling conventions. Updates and returns _last_calls_return_pc. + address call_c(Register function_entry); + // For tail calls: only branch, don't link, so callee returns to caller of this function. + address call_c_and_return_to_caller(Register function_entry); + address call_c(address function_entry, relocInfo::relocType rt); +#else + // Call a C function via a function descriptor and use full C + // calling conventions. Updates and returns _last_calls_return_pc. + address call_c(Register function_descriptor); + // For tail calls: only branch, don't link, so callee returns to caller of this function. + address call_c_and_return_to_caller(Register function_descriptor); + address call_c(const FunctionDescriptor* function_descriptor, relocInfo::relocType rt); + address call_c_using_toc(const FunctionDescriptor* function_descriptor, relocInfo::relocType rt, + Register toc); +#endif + + protected: + + // It is imperative that all calls into the VM are handled via the + // call_VM macros. They make sure that the stack linkage is setup + // correctly. call_VM's correspond to ENTRY/ENTRY_X entry points + // while call_VM_leaf's correspond to LEAF entry points. + // + // This is the base routine called by the different versions of + // call_VM. The interpreter may customize this version by overriding + // it for its purposes (e.g., to save/restore additional registers + // when doing a VM call). + // + // If no last_java_sp is specified (noreg) then SP will be used instead. + virtual void call_VM_base( + // where an oop-result ends up if any; use noreg otherwise + Register oop_result, + // to set up last_Java_frame in stubs; use noreg otherwise + Register last_java_sp, + // the entry point + address entry_point, + // flag which indicates if exception should be checked + bool check_exception = true + ); + + // Support for VM calls. This is the base routine called by the + // different versions of call_VM_leaf. The interpreter may customize + // this version by overriding it for its purposes (e.g., to + // save/restore additional registers when doing a VM call). + void call_VM_leaf_base(address entry_point); + + public: + // Call into the VM. + // Passes the thread pointer (in R3_ARG1) as a prepended argument. + // Makes sure oop return values are visible to the GC. + void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM_leaf(address entry_point); + void call_VM_leaf(address entry_point, Register arg_1); + void call_VM_leaf(address entry_point, Register arg_1, Register arg_2); + void call_VM_leaf(address entry_point, Register arg_1, Register arg_2, Register arg_3); + + // Call a stub function via a function descriptor, but don't save + // TOC before call, don't setup TOC and ENV for call, and don't + // restore TOC after call. Updates and returns _last_calls_return_pc. + inline address call_stub(Register function_entry); + inline void call_stub_and_return_to(Register function_entry, Register return_pc); + + // + // Java utilities + // + + // Read from the polling page, its address is already in a register. + inline void load_from_polling_page(Register polling_page_address, int offset = 0); + // Check whether instruction is a read access to the polling page + // which was emitted by load_from_polling_page(..). + static bool is_load_from_polling_page(int instruction, void* ucontext/*may be NULL*/, + address* polling_address_ptr = NULL); + + // Check whether instruction is a write access to the memory + // serialization page realized by one of the instructions stw, stwu, + // stwx, or stwux. + static bool is_memory_serialization(int instruction, JavaThread* thread, void* ucontext); + + // Support for NULL-checks + // + // Generates code that causes a NULL OS exception if the content of reg is NULL. + // If the accessed location is M[reg + offset] and the offset is known, provide the + // offset. No explicit code generation is needed if the offset is within a certain + // range (0 <= offset <= page_size). + + // Stack overflow checking + void bang_stack_with_offset(int offset); + + // If instruction is a stack bang of the form ld, stdu, or + // stdux, return the banged address. Otherwise, return 0. + static address get_stack_bang_address(int instruction, void* ucontext); + + // Atomics + // CmpxchgX sets condition register to cmpX(current, compare). + // (flag == ne) => (dest_current_value != compare_value), (!swapped) + // (flag == eq) => (dest_current_value == compare_value), ( swapped) + static inline bool cmpxchgx_hint_acquire_lock() { return true; } + // The stxcx will probably not be succeeded by a releasing store. + static inline bool cmpxchgx_hint_release_lock() { return false; } + static inline bool cmpxchgx_hint_atomic_update() { return false; } + + // Cmpxchg semantics + enum { + MemBarNone = 0, + MemBarRel = 1, + MemBarAcq = 2, + MemBarFenceAfter = 4 // use powers of 2 + }; + void cmpxchgw(ConditionRegister flag, + Register dest_current_value, Register compare_value, Register exchange_value, Register addr_base, + int semantics, bool cmpxchgx_hint = false, + Register int_flag_success = noreg, bool contention_hint = false); + void cmpxchgd(ConditionRegister flag, + Register dest_current_value, Register compare_value, Register exchange_value, Register addr_base, + int semantics, bool cmpxchgx_hint = false, + Register int_flag_success = noreg, Label* failed = NULL, bool contention_hint = false); + + // interface method calling + void lookup_interface_method(Register recv_klass, + Register intf_klass, + RegisterOrConstant itable_index, + Register method_result, + Register temp_reg, Register temp2_reg, + Label& no_such_interface); + + // virtual method calling + void lookup_virtual_method(Register recv_klass, + RegisterOrConstant vtable_index, + Register method_result); + + // Test sub_klass against super_klass, with fast and slow paths. + + // The fast path produces a tri-state answer: yes / no / maybe-slow. + // One of the three labels can be NULL, meaning take the fall-through. + // If super_check_offset is -1, the value is loaded up from super_klass. + // No registers are killed, except temp_reg and temp2_reg. + // If super_check_offset is not -1, temp2_reg is not used and can be noreg. + void check_klass_subtype_fast_path(Register sub_klass, + Register super_klass, + Register temp1_reg, + Register temp2_reg, + Label& L_success, + Label& L_failure); + + // The rest of the type check; must be wired to a corresponding fast path. + // It does not repeat the fast path logic, so don't use it standalone. + // The temp_reg can be noreg, if no temps are available. + // It can also be sub_klass or super_klass, meaning it's OK to kill that one. + // Updates the sub's secondary super cache as necessary. + void check_klass_subtype_slow_path(Register sub_klass, + Register super_klass, + Register temp1_reg, + Register temp2_reg, + Label* L_success = NULL, + Register result_reg = noreg); + + // Simplified, combined version, good for typical uses. + // Falls through on failure. + void check_klass_subtype(Register sub_klass, + Register super_klass, + Register temp1_reg, + Register temp2_reg, + Label& L_success); + + // Method handle support (JSR 292). + void check_method_handle_type(Register mtype_reg, Register mh_reg, Register temp_reg, Label& wrong_method_type); + + RegisterOrConstant argument_offset(RegisterOrConstant arg_slot, Register temp_reg, int extra_slot_offset = 0); + + // Biased locking support + // Upon entry,obj_reg must contain the target object, and mark_reg + // must contain the target object's header. + // Destroys mark_reg if an attempt is made to bias an anonymously + // biased lock. In this case a failure will go either to the slow + // case or fall through with the notEqual condition code set with + // the expectation that the slow case in the runtime will be called. + // In the fall-through case where the CAS-based lock is done, + // mark_reg is not destroyed. + void biased_locking_enter(ConditionRegister cr_reg, Register obj_reg, Register mark_reg, Register temp_reg, + Register temp2_reg, Label& done, Label* slow_case = NULL); + // Upon entry, the base register of mark_addr must contain the oop. + // Destroys temp_reg. + // If allow_delay_slot_filling is set to true, the next instruction + // emitted after this one will go in an annulled delay slot if the + // biased locking exit case failed. + void biased_locking_exit(ConditionRegister cr_reg, Register mark_addr, Register temp_reg, Label& done); + + void compiler_fast_lock_object( ConditionRegister flag, Register oop, Register box, Register tmp1, Register tmp2, Register tmp3); + void compiler_fast_unlock_object(ConditionRegister flag, Register oop, Register box, Register tmp1, Register tmp2, Register tmp3); + + // Support for serializing memory accesses between threads + void serialize_memory(Register thread, Register tmp1, Register tmp2); + + // GC barrier support. + void card_write_barrier_post(Register Rstore_addr, Register Rnew_val, Register Rtmp); + void card_table_write(jbyte* byte_map_base, Register Rtmp, Register Robj); + +#ifndef SERIALGC + // General G1 pre-barrier generator. + void g1_write_barrier_pre(Register Robj, RegisterOrConstant offset, Register Rpre_val, + Register Rtmp1, Register Rtmp2, bool needs_frame = false); + // General G1 post-barrier generator + void g1_write_barrier_post(Register Rstore_addr, Register Rnew_val, Register Rtmp1, + Register Rtmp2, Register Rtmp3, Label *filtered_ext = NULL); +#endif // SERIALGC + + // Support for managing the JavaThread pointer (i.e.; the reference to + // thread-local information). + + // Support for last Java frame (but use call_VM instead where possible): + // access R16_thread->last_Java_sp. + void set_last_Java_frame(Register last_java_sp, Register last_Java_pc); + void reset_last_Java_frame(void); + void set_top_ijava_frame_at_SP_as_last_Java_frame(Register sp, Register tmp1); + + // Read vm result from thread: oop_result = R16_thread->result; + void get_vm_result (Register oop_result); + + static bool needs_explicit_null_check(intptr_t offset); + + // Trap-instruction-based checks. + // Range checks can be distinguished from zero checks as they check 32 bit, + // zero checks all 64 bits (tw, td). + inline void trap_null_check(Register a, trap_to_bits cmp = traptoEqual); + static bool is_trap_null_check(int x) { + return is_tdi(x, traptoEqual, -1/*any reg*/, 0) || + is_tdi(x, traptoGreaterThanUnsigned, -1/*any reg*/, 0); + } + + inline void trap_zombie_not_entrant(); + static bool is_trap_zombie_not_entrant(int x) { return is_tdi(x, traptoUnconditional, 0/*reg 0*/, 1); } + + inline void trap_should_not_reach_here(); + static bool is_trap_should_not_reach_here(int x) { return is_tdi(x, traptoUnconditional, 0/*reg 0*/, 2); } + + inline void trap_ic_miss_check(Register a, Register b); + static bool is_trap_ic_miss_check(int x) { + return is_td(x, traptoGreaterThanUnsigned | traptoLessThanUnsigned, -1/*any reg*/, -1/*any reg*/); + } + + // Implicit or explicit null check, jumps to static address exception_entry. + inline void null_check_throw(Register a, int offset, Register temp_reg, address exception_entry); + + // Check accessed object for null. Use SIGTRAP-based null checks on AIX. + inline void load_with_trap_null_check(Register d, int si16, Register s1); + // Variant for heap OOPs including decompression of compressed OOPs. + inline void load_heap_oop_with_trap_null_check(Register d, RegisterOrConstant offs, Register s1); + + // Load heap oop and decompress. Loaded oop may not be null. + inline void load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1 = noreg); + inline void store_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1, + /*specify if d must stay uncompressed*/ Register tmp = noreg); + + // Null allowed. + inline void load_heap_oop(Register d, RegisterOrConstant offs, Register s1 = noreg); + + // Load klass oop from klass field. + inline void load_klass(Register dst, Register src); + + // Encode/decode heap oop. Oop may not be null, else en/decoding goes wrong. + inline Register encode_heap_oop_not_null(Register d, Register src = noreg); + inline void decode_heap_oop_not_null(Register d); + + // Null allowed. + inline void decode_heap_oop(Register d); + + // SIGTRAP-based range checks for arrays. + inline void trap_range_check_l(Register a, Register b); + inline void trap_range_check_l(Register a, int si16); + static bool is_trap_range_check_l(int x) { + return (is_tw (x, traptoLessThanUnsigned, -1/*any reg*/, -1/*any reg*/) || + is_twi(x, traptoLessThanUnsigned, -1/*any reg*/) ); + } + inline void trap_range_check_le(Register a, int si16); + static bool is_trap_range_check_le(int x) { + return is_twi(x, traptoEqual | traptoLessThanUnsigned, -1/*any reg*/); + } + inline void trap_range_check_g(Register a, int si16); + static bool is_trap_range_check_g(int x) { + return is_twi(x, traptoGreaterThanUnsigned, -1/*any reg*/); + } + inline void trap_range_check_ge(Register a, Register b); + inline void trap_range_check_ge(Register a, int si16); + static bool is_trap_range_check_ge(int x) { + return (is_tw (x, traptoEqual | traptoGreaterThanUnsigned, -1/*any reg*/, -1/*any reg*/) || + is_twi(x, traptoEqual | traptoGreaterThanUnsigned, -1/*any reg*/) ); + } + static bool is_trap_range_check(int x) { + return is_trap_range_check_l(x) || is_trap_range_check_le(x) || + is_trap_range_check_g(x) || is_trap_range_check_ge(x); + } + + void clear_memory_doubleword(Register base_ptr, Register cnt_dwords, Register tmp = R0); + + // Needle of length 1. + void string_indexof_1(Register result, Register haystack, Register haycnt, + Register needle, jchar needleChar, + Register tmp1, Register tmp2); + // General indexof, eventually with constant needle length. + void string_indexof(Register result, Register haystack, Register haycnt, + Register needle, ciTypeArray* needle_values, Register needlecnt, int needlecntval, + Register tmp1, Register tmp2, Register tmp3, Register tmp4); + void string_compare(Register str1_reg, Register str2_reg, Register cnt1_reg, Register cnt2_reg, + Register result_reg, Register tmp_reg); + void char_arrays_equals(Register str1_reg, Register str2_reg, Register cnt_reg, Register result_reg, + Register tmp1_reg, Register tmp2_reg, Register tmp3_reg, Register tmp4_reg, + Register tmp5_reg); + void char_arrays_equalsImm(Register str1_reg, Register str2_reg, int cntval, Register result_reg, + Register tmp1_reg, Register tmp2_reg); + + // + // Debugging + // + + // assert on cr0 + void asm_assert(bool check_equal, const char* msg, int id); + void asm_assert_eq(const char* msg, int id) { asm_assert(true, msg, id); } + void asm_assert_ne(const char* msg, int id) { asm_assert(false, msg, id); } + + private: + void asm_assert_mems_zero(bool check_equal, int size, int mem_offset, Register mem_base, + const char* msg, int id); + + public: + + void asm_assert_mem8_is_zero(int mem_offset, Register mem_base, const char* msg, int id) { + asm_assert_mems_zero(true, 8, mem_offset, mem_base, msg, id); + } + void asm_assert_mem8_isnot_zero(int mem_offset, Register mem_base, const char* msg, int id) { + asm_assert_mems_zero(false, 8, mem_offset, mem_base, msg, id); + } + + // Verify R16_thread contents. + void verify_thread(); + + // Emit code to verify that reg contains a valid oop if +VerifyOops is set. + void verify_oop(Register reg, const char* s = "broken oop"); + + // TODO: verify method and klass metadata (compare against vptr?) + void _verify_method_ptr(Register reg, const char * msg, const char * file, int line) {} + void _verify_klass_ptr(Register reg, const char * msg, const char * file, int line) {} + + // Convenience method returning function entry. For the ELFv1 case + // creates function descriptor at the current address and returs + // the pointer to it. For the ELFv2 case returns the current address. + inline address function_entry(); + +#define verify_method_ptr(reg) _verify_method_ptr(reg, "broken method " #reg, __FILE__, __LINE__) +#define verify_klass_ptr(reg) _verify_klass_ptr(reg, "broken klass " #reg, __FILE__, __LINE__) + + private: + + enum { + stop_stop = 0, + stop_untested = 1, + stop_unimplemented = 2, + stop_shouldnotreachhere = 3, + stop_end = 4 + }; + void stop(int type, const char* msg, int id); + + public: + // Prints msg, dumps registers and stops execution. + void stop (const char* msg = "", int id = 0) { stop(stop_stop, msg, id); } + void untested (const char* msg = "", int id = 0) { stop(stop_untested, msg, id); } + void unimplemented(const char* msg = "", int id = 0) { stop(stop_unimplemented, msg, id); } + void should_not_reach_here() { stop(stop_shouldnotreachhere, "", -1); } + + void zap_from_to(Register low, int before, Register high, int after, Register val, Register addr) PRODUCT_RETURN; +}; + +// class SkipIfEqualZero: +// +// Instantiating this class will result in assembly code being output that will +// jump around any code emitted between the creation of the instance and it's +// automatic destruction at the end of a scope block, depending on the value of +// the flag passed to the constructor, which will be checked at run-time. +class SkipIfEqualZero : public StackObj { + private: + MacroAssembler* _masm; + Label _label; + + public: + // 'Temp' is a temp register that this object can use (and trash). + explicit SkipIfEqualZero(MacroAssembler*, Register temp, const bool* flag_addr); + ~SkipIfEqualZero(); +}; + +#endif // CPU_PPC_VM_MACROASSEMBLER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_MACROASSEMBLER_PPC_INLINE_HPP +#define CPU_PPC_VM_MACROASSEMBLER_PPC_INLINE_HPP + +#include "asm/assembler.inline.hpp" +#include "macroAssembler_ppc.hpp" +#include "asm/codeBuffer.hpp" +#include "code/codeCache.hpp" + +inline bool MacroAssembler::is_ld_largeoffset(address a) { + const int inst1 = *(int *)a; + const int inst2 = *(int *)(a+4); + return (is_ld(inst1)) || + (is_addis(inst1) && is_ld(inst2) && inv_ra_field(inst2) == inv_rt_field(inst1)); +} + +inline int MacroAssembler::get_ld_largeoffset_offset(address a) { + assert(MacroAssembler::is_ld_largeoffset(a), "must be ld with large offset"); + + const int inst1 = *(int *)a; + if (is_ld(inst1)) { + return inv_d1_field(inst1); + } else { + const int inst2 = *(int *)(a+4); + return (inv_d1_field(inst1) << 16) + inv_d1_field(inst2); + } +} + +inline void MacroAssembler::round_to(Register r, int modulus) { + assert(is_power_of_2_long((jlong)modulus), "must be power of 2"); + addi(r, r, modulus-1); + clrrdi(r, r, log2_long((jlong)modulus)); +} + +// Move register if destination register and target register are different. +inline void MacroAssembler::mr_if_needed(Register rd, Register rs) { + if (rs != rd) mr(rd, rs); +} +inline void MacroAssembler::fmr_if_needed(FloatRegister rd, FloatRegister rs) { + if (rs != rd) fmr(rd, rs); +} +inline void MacroAssembler::endgroup_if_needed(bool needed) { + if (needed) { + endgroup(); + } +} + +inline void MacroAssembler::membar(int bits) { + // TODO: use elemental_membar(bits) for Power 8 and disable optimization of acquire-release + // (Matcher::post_membar_release where we use PPC64_ONLY(xop == Op_MemBarRelease ||)) + if (bits & StoreLoad) sync(); else lwsync(); +} +inline void MacroAssembler::release() { membar(LoadStore | StoreStore); } +inline void MacroAssembler::acquire() { membar(LoadLoad | LoadStore); } +inline void MacroAssembler::fence() { membar(LoadLoad | LoadStore | StoreLoad | StoreStore); } + +// Address of the global TOC. +inline address MacroAssembler::global_toc() { + return CodeCache::low_bound(); +} + +// Offset of given address to the global TOC. +inline int MacroAssembler::offset_to_global_toc(const address addr) { + intptr_t offset = (intptr_t)addr - (intptr_t)MacroAssembler::global_toc(); + assert(Assembler::is_simm((long)offset, 31) && offset >= 0, "must be in range"); + return (int)offset; +} + +// Address of current method's TOC. +inline address MacroAssembler::method_toc() { + return code()->consts()->start(); +} + +// Offset of given address to current method's TOC. +inline int MacroAssembler::offset_to_method_toc(address addr) { + intptr_t offset = (intptr_t)addr - (intptr_t)method_toc(); + assert(is_simm((long)offset, 31) && offset >= 0, "must be in range"); + return (int)offset; +} + +inline bool MacroAssembler::is_calculate_address_from_global_toc_at(address a, address bound) { + const address inst2_addr = a; + const int inst2 = *(int *) a; + + // The relocation points to the second instruction, the addi. + if (!is_addi(inst2)) return false; + + // The addi reads and writes the same register dst. + const int dst = inv_rt_field(inst2); + if (inv_ra_field(inst2) != dst) return false; + + // Now, find the preceding addis which writes to dst. + int inst1 = 0; + address inst1_addr = inst2_addr - BytesPerInstWord; + while (inst1_addr >= bound) { + inst1 = *(int *) inst1_addr; + if (is_addis(inst1) && inv_rt_field(inst1) == dst) { + // stop, found the addis which writes dst + break; + } + inst1_addr -= BytesPerInstWord; + } + + if (!(inst1 == 0 || inv_ra_field(inst1) == 29 /* R29 */)) return false; + return is_addis(inst1); +} + +#ifdef _LP64 +// Detect narrow oop constants. +inline bool MacroAssembler::is_set_narrow_oop(address a, address bound) { + const address inst2_addr = a; + const int inst2 = *(int *)a; + + // The relocation points to the second instruction, the addi. + if (!is_addi(inst2)) return false; + + // The addi reads and writes the same register dst. + const int dst = inv_rt_field(inst2); + if (inv_ra_field(inst2) != dst) return false; + + // Now, find the preceding addis which writes to dst. + int inst1 = 0; + address inst1_addr = inst2_addr - BytesPerInstWord; + while (inst1_addr >= bound) { + inst1 = *(int *) inst1_addr; + if (is_lis(inst1) && inv_rs_field(inst1) == dst) return true; + inst1_addr -= BytesPerInstWord; + } + return false; +} +#endif + + +inline bool MacroAssembler::is_load_const_at(address a) { + const int* p_inst = (int *) a; + bool b = is_lis(*p_inst++); + if (is_ori(*p_inst)) { + p_inst++; + b = b && is_rldicr(*p_inst++); // TODO: could be made more precise: `sldi'! + b = b && is_oris(*p_inst++); + b = b && is_ori(*p_inst); + } else if (is_lis(*p_inst)) { + p_inst++; + b = b && is_ori(*p_inst++); + b = b && is_ori(*p_inst); + // TODO: could enhance reliability by adding is_insrdi + } else return false; + return b; +} + +inline void MacroAssembler::set_oop_constant(jobject obj, Register d) { + set_oop(constant_oop_address(obj), d); +} + +inline void MacroAssembler::set_oop(AddressLiteral obj_addr, Register d) { + assert(obj_addr.rspec().type() == relocInfo::oop_type, "must be an oop reloc"); + load_const(d, obj_addr); +} + +inline void MacroAssembler::pd_patch_instruction(address branch, address target) { + jint& stub_inst = *(jint*) branch; + stub_inst = patched_branch(target - branch, stub_inst, 0); +} + +// Relocation of conditional far branches. +inline bool MacroAssembler::is_bc_far_variant1_at(address instruction_addr) { + // Variant 1, the 1st instruction contains the destination address: + // + // bcxx DEST + // endgroup + // + const int instruction_1 = *(int*)(instruction_addr); + const int instruction_2 = *(int*)(instruction_addr + 4); + return is_bcxx(instruction_1) && + (inv_bd_field(instruction_1, (intptr_t)instruction_addr) != (intptr_t)(instruction_addr + 2*4)) && + is_endgroup(instruction_2); +} + +// Relocation of conditional far branches. +inline bool MacroAssembler::is_bc_far_variant2_at(address instruction_addr) { + // Variant 2, the 2nd instruction contains the destination address: + // + // b!cxx SKIP + // bxx DEST + // SKIP: + // + const int instruction_1 = *(int*)(instruction_addr); + const int instruction_2 = *(int*)(instruction_addr + 4); + return is_bcxx(instruction_1) && + (inv_bd_field(instruction_1, (intptr_t)instruction_addr) == (intptr_t)(instruction_addr + 2*4)) && + is_bxx(instruction_2); +} + +// Relocation for conditional branches +inline bool MacroAssembler::is_bc_far_variant3_at(address instruction_addr) { + // Variant 3, far cond branch to the next instruction, already patched to nops: + // + // nop + // endgroup + // SKIP/DEST: + // + const int instruction_1 = *(int*)(instruction_addr); + const int instruction_2 = *(int*)(instruction_addr + 4); + return is_nop(instruction_1) && + is_endgroup(instruction_2); +} + + +// Convenience bc_far versions +inline void MacroAssembler::blt_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs1, bi0(crx, less), L, optimize); } +inline void MacroAssembler::bgt_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs1, bi0(crx, greater), L, optimize); } +inline void MacroAssembler::beq_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs1, bi0(crx, equal), L, optimize); } +inline void MacroAssembler::bso_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs1, bi0(crx, summary_overflow), L, optimize); } +inline void MacroAssembler::bge_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs0, bi0(crx, less), L, optimize); } +inline void MacroAssembler::ble_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs0, bi0(crx, greater), L, optimize); } +inline void MacroAssembler::bne_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs0, bi0(crx, equal), L, optimize); } +inline void MacroAssembler::bns_far(ConditionRegister crx, Label& L, int optimize) { MacroAssembler::bc_far(bcondCRbiIs0, bi0(crx, summary_overflow), L, optimize); } + +inline address MacroAssembler::call_stub(Register function_entry) { + mtctr(function_entry); + bctrl(); + return pc(); +} + +inline void MacroAssembler::call_stub_and_return_to(Register function_entry, Register return_pc) { + assert_different_registers(function_entry, return_pc); + mtlr(return_pc); + mtctr(function_entry); + bctr(); +} + +// Get the pc where the last emitted call will return to. +inline address MacroAssembler::last_calls_return_pc() { + return _last_calls_return_pc; +} + +// Read from the polling page, its address is already in a register. +inline void MacroAssembler::load_from_polling_page(Register polling_page_address, int offset) { + ld(R0, offset, polling_page_address); +} + +// Trap-instruction-based checks. + +inline void MacroAssembler::trap_null_check(Register a, trap_to_bits cmp) { + assert(TrapBasedNullChecks, "sanity"); + tdi(cmp, a/*reg a*/, 0); +} +inline void MacroAssembler::trap_zombie_not_entrant() { + tdi(traptoUnconditional, 0/*reg 0*/, 1); +} +inline void MacroAssembler::trap_should_not_reach_here() { + tdi_unchecked(traptoUnconditional, 0/*reg 0*/, 2); +} + +inline void MacroAssembler::trap_ic_miss_check(Register a, Register b) { + td(traptoGreaterThanUnsigned | traptoLessThanUnsigned, a, b); +} + +// Do an explicit null check if access to a+offset will not raise a SIGSEGV. +// Either issue a trap instruction that raises SIGTRAP, or do a compare that +// branches to exception_entry. +// No support for compressed oops (base page of heap). Does not distinguish +// loads and stores. +inline void MacroAssembler::null_check_throw(Register a, int offset, Register temp_reg, + address exception_entry) { + if (!ImplicitNullChecks || needs_explicit_null_check(offset) || !os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + assert(UseSIGTRAP, "sanity"); + trap_null_check(a); + } else { + Label ok; + cmpdi(CCR0, a, 0); + bne(CCR0, ok); + load_const_optimized(temp_reg, exception_entry); + mtctr(temp_reg); + bctr(); + bind(ok); + } + } +} + +inline void MacroAssembler::load_with_trap_null_check(Register d, int si16, Register s1) { + if (!os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + trap_null_check(s1); + } + } + ld(d, si16, s1); +} + +// Attention: No null check for loaded uncompressed OOP. Can be used for loading klass field. +inline void MacroAssembler::load_heap_oop_with_trap_null_check(Register d, RegisterOrConstant si16, + Register s1) { + if (!os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + trap_null_check(s1); + } + } + load_heap_oop_not_null(d, si16, s1); +} + +inline void MacroAssembler::load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1) { + if (UseCompressedOops) { + lwz(d, offs, s1); + // Attention: no null check here! + decode_heap_oop_not_null(d); + } else { + ld(d, offs, s1); + } +} + +inline void MacroAssembler::store_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1, Register tmp) { + if (UseCompressedOops) { + Register compressedOop = encode_heap_oop_not_null((tmp != noreg) ? tmp : d, d); + stw(compressedOop, offs, s1); + } else { + std(d, offs, s1); + } +} + +inline void MacroAssembler::load_heap_oop(Register d, RegisterOrConstant offs, Register s1) { + if (UseCompressedOops) { + lwz(d, offs, s1); + decode_heap_oop(d); + } else { + ld(d, offs, s1); + } +} + +inline void MacroAssembler::load_klass(Register dst, Register src) { + load_heap_oop_not_null(dst, oopDesc::klass_offset_in_bytes(), src); +} + +inline Register MacroAssembler::encode_heap_oop_not_null(Register d, Register src) { + Register current = (src!=noreg) ? src : d; // Compressed oop is in d if no src provided. + if (Universe::narrow_oop_base() != NULL) { + sub(d, current, R30); + current = d; + } + if (Universe::narrow_oop_shift() != 0) { + srdi(d, current, LogMinObjAlignmentInBytes); + current = d; + } + return current; // Encoded oop is in this register. +} + +inline void MacroAssembler::decode_heap_oop_not_null(Register d) { + if (Universe::narrow_oop_shift() != 0) { + assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); + sldi(d, d, LogMinObjAlignmentInBytes); + } + if (Universe::narrow_oop_base() != NULL) { + add(d, d, R30); + } +} + +inline void MacroAssembler::decode_heap_oop(Register d) { + Label isNull; + if (Universe::narrow_oop_base() != NULL) { + cmpwi(CCR0, d, 0); + beq(CCR0, isNull); + } + if (Universe::narrow_oop_shift() != 0) { + assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); + sldi(d, d, LogMinObjAlignmentInBytes); + } + if (Universe::narrow_oop_base() != NULL) { + add(d, d, R30); + } + bind(isNull); +} + +// SIGTRAP-based range checks for arrays. +inline void MacroAssembler::trap_range_check_l(Register a, Register b) { + tw (traptoLessThanUnsigned, a/*reg a*/, b/*reg b*/); +} +inline void MacroAssembler::trap_range_check_l(Register a, int si16) { + twi(traptoLessThanUnsigned, a/*reg a*/, si16); +} +inline void MacroAssembler::trap_range_check_le(Register a, int si16) { + twi(traptoEqual | traptoLessThanUnsigned, a/*reg a*/, si16); +} +inline void MacroAssembler::trap_range_check_g(Register a, int si16) { + twi(traptoGreaterThanUnsigned, a/*reg a*/, si16); +} +inline void MacroAssembler::trap_range_check_ge(Register a, Register b) { + tw (traptoEqual | traptoGreaterThanUnsigned, a/*reg a*/, b/*reg b*/); +} +inline void MacroAssembler::trap_range_check_ge(Register a, int si16) { + twi(traptoEqual | traptoGreaterThanUnsigned, a/*reg a*/, si16); +} + +#if defined(ABI_ELFv2) +inline address MacroAssembler::function_entry() { return pc(); } +#else +inline address MacroAssembler::function_entry() { return emit_fd(); } +#endif +#endif // CPU_PPC_VM_MACROASSEMBLER_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/methodHandles_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/methodHandles_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,558 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "interpreter/interpreter.hpp" +#include "memory/allocation.inline.hpp" +#include "prims/methodHandles.hpp" + +#define __ _masm-> + +#ifdef CC_INTERP +#define EXCEPTION_ENTRY StubRoutines::throw_NullPointerException_at_call_entry() +#else +#define EXCEPTION_ENTRY Interpreter::throw_NullPointerException_entry() +#endif + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant. +inline static RegisterOrConstant constant(int value) { + return RegisterOrConstant(value); +} + +void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg, Register temp_reg, Register temp2_reg) { + if (VerifyMethodHandles) + verify_klass(_masm, klass_reg, SystemDictionaryHandles::Class_klass(), temp_reg, temp2_reg, + "MH argument is a Class"); + __ load_heap_oop_not_null(klass_reg, java_lang_Class::klass_offset_in_bytes(), klass_reg); +} + +#ifdef ASSERT +static int check_nonzero(const char* xname, int x) { + assert(x != 0, err_msg("%s should be nonzero", xname)); + return x; +} +#define NONZERO(x) check_nonzero(#x, x) +#else //ASSERT +#define NONZERO(x) (x) +#endif //ASSERT + +#ifdef ASSERT +void MethodHandles::verify_klass(MacroAssembler* _masm, + Register obj_reg, KlassHandle klass, + Register temp_reg, Register temp2_reg, + const char* error_message) { + oop* klass_addr = klass.raw_value(); + assert(klass_addr >= SystemDictionaryHandles::Object_klass().raw_value() && + klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(), + "must be one of the SystemDictionaryHandles"); + Label L_ok, L_bad; + BLOCK_COMMENT("verify_klass {"); + __ verify_oop(obj_reg); + __ cmpdi(CCR0, obj_reg, 0); + __ beq(CCR0, L_bad); + __ load_klass(temp_reg, obj_reg); + // klass_addr is a klass in allstatic SystemDictionaryHandles. Can't get GCed. + __ load_const_optimized(temp2_reg, (address) klass_addr); // No relocation needed. Load optimized. + __ ld(temp2_reg, 0, temp2_reg); + __ cmpd(CCR0, temp_reg, temp2_reg); + __ beq(CCR0, L_ok); + __ ld(temp_reg, klass->super_check_offset(), temp_reg); + __ cmpd(CCR0, temp_reg, temp2_reg); + __ beq(CCR0, L_ok); + __ BIND(L_bad); + __ stop(error_message); + __ BIND(L_ok); + BLOCK_COMMENT("} verify_klass"); +} + +void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) { + Label L; + BLOCK_COMMENT("verify_ref_kind {"); + __ load_sized_value(temp, NONZERO(java_lang_invoke_MemberName::flags_offset_in_bytes()), member_reg, + sizeof(u4), /*is_signed*/ false); + // assert(sizeof(u4) == sizeof(java.lang.invoke.MemberName.flags), ""); + __ srwi( temp, temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_SHIFT); + __ andi(temp, temp, java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK); + __ cmpwi(CCR1, temp, ref_kind); + __ beq(CCR1, L); + { char* buf = NEW_C_HEAP_ARRAY(char, 100, mtInternal); + jio_snprintf(buf, 100, "verify_ref_kind expected %x", ref_kind); + if (ref_kind == JVM_REF_invokeVirtual || + ref_kind == JVM_REF_invokeSpecial) + // could do this for all ref_kinds, but would explode assembly code size + trace_method_handle(_masm, buf); + __ stop(buf); + } + BLOCK_COMMENT("} verify_ref_kind"); + __ BIND(L); +} + +#endif // ASSERT + +void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register target, Register temp, + bool for_compiler_entry) { + Label L_no_such_method; + assert(method == R19_method, "interpreter calling convention"); + assert_different_registers(method, target, temp); + __ verify_oop(method); + + if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) { + Label run_compiled_code; + // JVMTI events, such as single-stepping, are implemented partly by avoiding running + // compiled code in threads for which the event is enabled. Check here for + // interp_only_mode if these events CAN be enabled. + __ verify_thread(); + __ lwz(temp, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread); + __ cmplwi(CCR0, temp, 0); + __ beq(CCR0, run_compiled_code); + // Null method test is replicated below in compiled case, + // it might be able to address across the verify_thread() + __ cmplwi(CCR0, R19_method, 0); + __ beq(CCR0, L_no_such_method); + __ ld(target, in_bytes(methodOopDesc::interpreter_entry_offset()), R19_method); + __ mtctr(target); + __ bctr(); + __ BIND(run_compiled_code); + } + + // Compiled case, either static or fall-through from runtime conditional + __ cmplwi(CCR0, R19_method, 0); + __ beq(CCR0, L_no_such_method); + + const ByteSize entry_offset = for_compiler_entry ? methodOopDesc::from_compiled_offset() : + methodOopDesc::from_interpreted_offset(); + __ ld(target, in_bytes(entry_offset), R19_method); + __ mtctr(target); + __ bctr(); + + __ bind(L_no_such_method); + assert(StubRoutines::throw_AbstractMethodError_entry() != NULL, "not yet generated!"); + __ load_const_optimized(target, StubRoutines::throw_AbstractMethodError_entry()); + __ mtctr(target); + __ bctr(); +} + + +void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm, + Register recv, Register method_temp, + Register temp2, Register temp3, + bool for_compiler_entry) { + BLOCK_COMMENT("jump_to_lambda_form {"); + // This is the initial entry point of a lazy method handle. + // After type checking, it picks up the invoker from the LambdaForm. + assert_different_registers(recv, method_temp, temp2); // temp3 is only passed on + assert(method_temp == R19_method, "required register for loading method"); + + // Load the invoker, as MH -> MH.form -> LF.vmentry + __ verify_oop(recv); + __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_MethodHandle::form_offset_in_bytes()), recv); + __ verify_oop(method_temp); + __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_LambdaForm::vmentry_offset_in_bytes()), method_temp); + __ verify_oop(method_temp); + // the following assumes that a methodOop is normally compressed in the vmtarget field: + __ load_heap_oop_not_null(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()), method_temp); + __ verify_oop(method_temp); + + if (VerifyMethodHandles && !for_compiler_entry) { + // make sure recv is already on stack + __ load_sized_value(temp2, in_bytes(methodOopDesc::size_of_parameters_offset()), method_temp, + sizeof(u2), /*is_signed*/ false); + // assert(sizeof(u2) == sizeof(methodOopDesc::_size_of_parameters), ""); + Label L; + __ ld(temp2, __ argument_offset(temp2, temp2, 0), CC_INTERP_ONLY(R17_tos) NOT_CC_INTERP(R15_esp)); + __ cmpd(CCR1, temp2, recv); + __ beq(CCR1, L); + __ stop("receiver not on stack"); + __ BIND(L); + } + + jump_from_method_handle(_masm, method_temp, temp2, temp3, for_compiler_entry); + BLOCK_COMMENT("} jump_to_lambda_form"); +} + + + +// Code generation +address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm, + vmIntrinsics::ID iid) { + const bool not_for_compiler_entry = false; // this is the interpreter entry + assert(is_signature_polymorphic(iid), "expected invoke iid"); + if (iid == vmIntrinsics::_invokeGeneric || + iid == vmIntrinsics::_compiledLambdaForm) { + // Perhaps surprisingly, the symbolic references visible to Java are not directly used. + // They are linked to Java-generated adapters via MethodHandleNatives.linkMethod. + // They all allow an appendix argument. + __ stop("Should not reach here"); // empty stubs make SG sick + return NULL; + } + + Register argbase = CC_INTERP_ONLY(R17_tos) NOT_CC_INTERP(R15_esp); // parameter (preserved) + Register argslot = R3; + Register temp1 = R6; + Register param_size = R7; + + // here's where control starts out: + __ align(CodeEntryAlignment); + address entry_point = __ pc(); + + if (VerifyMethodHandles) { + Label L; + BLOCK_COMMENT("verify_intrinsic_id {"); + __ load_sized_value(temp1, methodOopDesc::intrinsic_id_offset_in_bytes(), R19_method, + sizeof(u1), /*is_signed*/ false); + // assert(sizeof(u1) == sizeof(methodOopDesc::_intrinsic_id), ""); + __ cmpwi(CCR1, temp1, (int) iid); + __ beq(CCR1, L); + if (iid == vmIntrinsics::_linkToVirtual || + iid == vmIntrinsics::_linkToSpecial) { + // could do this for all kinds, but would explode assembly code size + trace_method_handle(_masm, "bad methodOop::intrinsic_id"); + } + __ stop("bad methodOop::intrinsic_id"); + __ BIND(L); + BLOCK_COMMENT("} verify_intrinsic_id"); + } + + // First task: Find out how big the argument list is. + int ref_kind = signature_polymorphic_intrinsic_ref_kind(iid); + assert(ref_kind != 0 || iid == vmIntrinsics::_invokeBasic, "must be _invokeBasic or a linkTo intrinsic"); + if (ref_kind == 0 || MethodHandles::ref_kind_has_receiver(ref_kind)) { + __ load_sized_value(param_size, in_bytes(methodOopDesc::size_of_parameters_offset()), R19_method, + sizeof(u2), /*is_signed*/ false); + // assert(sizeof(u2) == sizeof(methodOopDesc::_size_of_parameters), ""); + } else { + DEBUG_ONLY(param_size = noreg); + } + + Register tmp_mh = noreg; + if (!is_signature_polymorphic_static(iid)) { + __ ld(tmp_mh = temp1, __ argument_offset(param_size, param_size, 0), argbase); + DEBUG_ONLY(param_size = noreg); + } + + if (TraceMethodHandles) { + trace_method_handle_interpreter_entry(_masm, iid); + } + + if (iid == vmIntrinsics::_invokeBasic) { + generate_method_handle_dispatch(_masm, iid, tmp_mh, noreg, not_for_compiler_entry); + + } else { + // Adjust argument list by popping the trailing MemberName argument. + Register tmp_recv = noreg; + if (MethodHandles::ref_kind_has_receiver(ref_kind)) { + // Load the receiver (not the MH; the actual MemberName's receiver) up from the interpreter stack. + __ ld(tmp_recv = temp1, __ argument_offset(param_size, param_size, 0), argbase); + DEBUG_ONLY(param_size = noreg); + } + Register R19_member = R19_method; // MemberName ptr; incoming method ptr is dead now + __ ld(R19_member, RegisterOrConstant((intptr_t)8), argbase); + __ add(argbase, Interpreter::stackElementSize, argbase); + generate_method_handle_dispatch(_masm, iid, tmp_recv, R19_member, not_for_compiler_entry); + } + + return entry_point; +} + +void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, + vmIntrinsics::ID iid, + Register receiver_reg, + Register member_reg, + bool for_compiler_entry) { + assert(is_signature_polymorphic(iid), "expected invoke iid"); + Register temp1 = (for_compiler_entry ? R25_tmp5 : R7); + Register temp2 = (for_compiler_entry ? R22_tmp2 : R8); + Register temp3 = (for_compiler_entry ? R23_tmp3 : R9); + Register temp4 = (for_compiler_entry ? R24_tmp4 : R10); + if (receiver_reg != noreg) assert_different_registers(temp1, temp2, temp3, temp4, receiver_reg); + if (member_reg != noreg) assert_different_registers(temp1, temp2, temp3, temp4, member_reg); + + if (iid == vmIntrinsics::_invokeBasic) { + // indirect through MH.form.vmentry.vmtarget + jump_to_lambda_form(_masm, receiver_reg, R19_method, temp1, temp2, for_compiler_entry); + } else { + // The method is a member invoker used by direct method handles. + if (VerifyMethodHandles) { + // make sure the trailing argument really is a MemberName (caller responsibility) + verify_klass(_masm, member_reg, SystemDictionaryHandles::MemberName_klass(), + temp1, temp2, + "MemberName required for invokeVirtual etc."); + } + + Register temp1_recv_klass = temp1; + if (iid != vmIntrinsics::_linkToStatic) { + __ verify_oop(receiver_reg); + if (iid == vmIntrinsics::_linkToSpecial) { + // Don't actually load the klass; just null-check the receiver. + __ null_check_throw(receiver_reg, -1, temp1, EXCEPTION_ENTRY); + } else { + // load receiver klass itself + __ null_check_throw(receiver_reg, oopDesc::klass_offset_in_bytes(), temp1, EXCEPTION_ENTRY); + __ load_klass(temp1_recv_klass, receiver_reg); + __ verify_oop(temp1_recv_klass); + } + BLOCK_COMMENT("check_receiver {"); + // The receiver for the MemberName must be in receiver_reg. + // Check the receiver against the MemberName.clazz + if (VerifyMethodHandles && iid == vmIntrinsics::_linkToSpecial) { + // Did not load it above... + __ load_klass(temp1_recv_klass, receiver_reg); + __ verify_oop(temp1_recv_klass); + } + if (VerifyMethodHandles && iid != vmIntrinsics::_linkToInterface) { + Label L_ok; + Register temp2_defc = temp2; + __ load_heap_oop_not_null(temp2_defc, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()), member_reg); + load_klass_from_Class(_masm, temp2_defc, temp3, temp4); + __ verify_oop(temp2_defc); + __ check_klass_subtype(temp1_recv_klass, temp2_defc, temp3, temp4, L_ok); + // If we get here, the type check failed! + __ stop("receiver class disagrees with MemberName.clazz"); + __ BIND(L_ok); + } + BLOCK_COMMENT("} check_receiver"); + } + if (iid == vmIntrinsics::_linkToSpecial || + iid == vmIntrinsics::_linkToStatic) { + DEBUG_ONLY(temp1_recv_klass = noreg); // these guys didn't load the recv_klass + } + + // Live registers at this point: + // member_reg - MemberName that was the trailing argument + // temp1_recv_klass - klass of stacked receiver, if needed + // O5_savedSP - interpreter linkage (if interpreted) + // O0..O5 - compiler arguments (if compiled) + + Label L_incompatible_class_change_error; + switch (iid) { + case vmIntrinsics::_linkToSpecial: + if (VerifyMethodHandles) { + verify_ref_kind(_masm, JVM_REF_invokeSpecial, member_reg, temp2); + } + __ load_heap_oop_not_null(R19_method, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()), member_reg); + break; + + case vmIntrinsics::_linkToStatic: + if (VerifyMethodHandles) { + verify_ref_kind(_masm, JVM_REF_invokeStatic, member_reg, temp2); + } + __ load_heap_oop_not_null(R19_method, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()), member_reg); + break; + + case vmIntrinsics::_linkToVirtual: + { + // same as TemplateTable::invokevirtual, + // minus the CP setup and profiling: + + if (VerifyMethodHandles) { + verify_ref_kind(_masm, JVM_REF_invokeVirtual, member_reg, temp2); + } + + // pick out the vtable index from the MemberName, and then we can discard it: + Register temp2_index = temp2; + __ ld(temp2_index, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes()), member_reg); + + if (VerifyMethodHandles) { + Label L_index_ok; + __ cmpdi(CCR1, temp2_index, 0); + __ bge(CCR1, L_index_ok); + __ stop("no virtual index"); + __ BIND(L_index_ok); + } + + // Note: The verifier invariants allow us to ignore MemberName.clazz and vmtarget + // at this point. And VerifyMethodHandles has already checked clazz, if needed. + + // get target methodOop & entry point + __ lookup_virtual_method(temp1_recv_klass, temp2_index, R19_method); + break; + } + + case vmIntrinsics::_linkToInterface: + { + // same as TemplateTable::invokeinterface + // (minus the CP setup and profiling, with different argument motion) + if (VerifyMethodHandles) { + verify_ref_kind(_masm, JVM_REF_invokeInterface, member_reg, temp2); + } + + Register temp2_intf = temp2; + __ load_heap_oop_not_null(temp2_intf, NONZERO(java_lang_invoke_MemberName::clazz_offset_in_bytes()), member_reg); + load_klass_from_Class(_masm, temp2_intf, temp3, temp4); + __ verify_oop(temp2_intf); + + Register vtable_index = R19_method; + __ ld(vtable_index, NONZERO(java_lang_invoke_MemberName::vmindex_offset_in_bytes()), member_reg); + if (VerifyMethodHandles) { + Label L_index_ok; + __ cmpdi(CCR1, vtable_index, 0); + __ bge(CCR1, L_index_ok); + __ stop("invalid vtable index for MH.invokeInterface"); + __ BIND(L_index_ok); + } + + // given intf, index, and recv klass, dispatch to the implementation method + __ lookup_interface_method(temp1_recv_klass, temp2_intf, + // note: next two args must be the same: + vtable_index, R19_method, + temp3, temp4, + L_incompatible_class_change_error); + break; + } + + default: + fatal(err_msg_res("unexpected intrinsic %d: %s", iid, vmIntrinsics::name_at(iid))); + break; + } + + // Live at this point: + // R19_method + // O5_savedSP (if interpreted) + + // After figuring out which concrete method to call, jump into it. + // Note that this works in the interpreter with no data motion. + // But the compiled version will require that rcx_recv be shifted out. + __ verify_oop(R19_method); + jump_from_method_handle(_masm, R19_method, temp1, temp2, for_compiler_entry); + + if (iid == vmIntrinsics::_linkToInterface) { + __ BIND(L_incompatible_class_change_error); + __ load_const_optimized(temp1, StubRoutines::throw_IncompatibleClassChangeError_entry()); + __ mtctr(temp1); + __ bctr(); + } + } +} + +#ifndef PRODUCT +void trace_method_handle_stub(const char* adaptername, + oopDesc* mh, + intptr_t* entry_sp, + intptr_t* saved_regs) { + + bool has_mh = (strstr(adaptername, "/static") == NULL && + strstr(adaptername, "linkTo") == NULL); // static linkers don't have MH + const char* mh_reg_name = has_mh ? "method_handle" : "R6"; + tty->print_cr("MH %s %s="INTPTR_FORMAT " sp=" INTPTR_FORMAT, + adaptername, mh_reg_name, (intptr_t) mh, entry_sp); + + if (Verbose) { + tty->print_cr("Registers:"); + const int abi_offset = frame::abi_reg_args_size / 8; + for (int i = R3->encoding(); i <= R12->encoding(); i++) { + Register r = as_Register(i); + int count = i - R3->encoding(); + // The registers are stored in reverse order on the stack (by save_volatile_gprs(R1_SP, abi_reg_args_size)). + tty->print("%3s=" PTR_FORMAT, r->name(), saved_regs[abi_offset + count]); + if ((count + 1) % 4 == 0) { + tty->cr(); + } else { + tty->print(", "); + } + } + tty->cr(); + + { + // dumping last frame with frame::describe + + JavaThread* p = JavaThread::active(); + + ResourceMark rm; + PRESERVE_EXCEPTION_MARK; // may not be needed by safer and unexpensive here + FrameValues values; + + // Note: We want to allow trace_method_handle from any call site. + // While trace_method_handle creates a frame, it may be entered + // without a PC on the stack top (e.g. not just after a call). + // Walking that frame could lead to failures due to that invalid PC. + // => carefully detect that frame when doing the stack walking + + // Current C frame + frame cur_frame = os::current_frame(); + + // Robust search of trace_calling_frame (independant of inlining). + // Assumes saved_regs comes from a pusha in the trace_calling_frame. + assert(cur_frame.sp() < saved_regs, "registers not saved on stack ?"); + frame trace_calling_frame = os::get_sender_for_C_frame(&cur_frame); + while (trace_calling_frame.fp() < saved_regs) { + trace_calling_frame = os::get_sender_for_C_frame(&trace_calling_frame); + } + + // Safely create a frame and call frame::describe. + intptr_t *dump_sp = trace_calling_frame.sender_sp(); + + frame dump_frame = frame(dump_sp); + dump_frame.describe(values, 1); + + values.describe(-1, saved_regs, "raw top of stack"); + + tty->print_cr("Stack layout:"); + values.print(p); + } + + if (has_mh && mh->is_oop()) { + mh->print(); + if (java_lang_invoke_MethodHandle::is_instance(mh)) { + if (java_lang_invoke_MethodHandle::form_offset_in_bytes() != 0) + java_lang_invoke_MethodHandle::form(mh)->print(); + } + } + } +} + +void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { + if (!TraceMethodHandles) return; + + BLOCK_COMMENT("trace_method_handle {"); + + int nbytes_save = 10 * 8; // 10 volatile gprs + __ save_LR_CR(R0); + __ mr(R0, R1_SP); // saved_sp + assert(Assembler::is_simm(-nbytes_save, 16), "Overwriting R0"); + // Push_frame_reg_args only uses R0 if nbytes_save is wider than 16 bit. + __ push_frame_reg_args(nbytes_save, R0); + __ save_volatile_gprs(R1_SP, frame::abi_reg_args_size); // Except R0. + + __ load_const(R3_ARG1, (address)adaptername); + __ mr(R4_ARG2, R6); // method handle (See generate_method_handle_interpreter_entry above.) + __ mr(R5_ARG3, R0); // saved_sp + __ mr(R6_ARG4, R1_SP); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub)); + + __ restore_volatile_gprs(R1_SP, 112); // Except R0. + __ pop_frame(); + __ restore_LR_CR(R0); + + BLOCK_COMMENT("} trace_method_handle"); +} +#endif // PRODUCT diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/methodHandles_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/methodHandles_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// Platform-specific definitions for method handles. +// These definitions are inlined into class MethodHandles. + +// Adapters +static unsigned int adapter_code_size() { + return 32*K DEBUG_ONLY(+ 16*K) + + (TraceMethodHandles ? 16*K : 0) + + (VerifyMethodHandles ? 32*K : 0) + + (VerifyOops ? 32*K : 0); +} + +// Additional helper methods for MethodHandles code generation: +public: + static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg, Register temp_reg, Register temp2_reg); + + static void verify_klass(MacroAssembler* _masm, + Register obj_reg, KlassHandle klass, + Register temp_reg, Register temp2_reg, + const char* error_message = "wrong klass") NOT_DEBUG_RETURN; + + static void verify_method_handle(MacroAssembler* _masm, Register mh_reg, + Register temp_reg, Register temp2_reg) { + verify_klass(_masm, mh_reg, SystemDictionaryHandles::MethodHandle_klass(), + temp_reg, temp2_reg, + "reference is a MH"); + } + + static void verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) NOT_DEBUG_RETURN; + + // Similar to InterpreterMacroAssembler::jump_from_interpreted. + // Takes care of special dispatch from single stepping too. + static void jump_from_method_handle(MacroAssembler* _masm, Register method, + Register temp, Register temp2, + bool for_compiler_entry); + + static void jump_to_lambda_form(MacroAssembler* _masm, + Register recv, Register method_temp, + Register temp2, Register temp3, + bool for_compiler_entry); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/nativeInst_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/nativeInst_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,378 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "assembler_ppc.inline.hpp" +#include "memory/resourceArea.hpp" +#include "nativeInst_ppc.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/handles.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/ostream.hpp" +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#endif + +// We use an illtrap for marking a method as not_entrant or zombie iff !UseSIGTRAP +// Work around a C++ compiler bug which changes 'this' +bool NativeInstruction::is_sigill_zombie_not_entrant_at(address addr) { + assert(!UseSIGTRAP, "precondition"); + if (*(int*)addr != 0 /*illtrap*/) return false; + CodeBlob* cb = CodeCache::find_blob_unsafe(addr); + if (cb == NULL || !cb->is_nmethod()) return false; + nmethod *nm = (nmethod *)cb; + // This method is not_entrant or zombie iff the illtrap instruction is + // located at the verified entry point. + return nm->verified_entry_point() == addr; +} + +#ifdef ASSERT +void NativeInstruction::verify() { + // Make sure code pattern is actually an instruction address. + address addr = addr_at(0); + if (addr == 0 || ((intptr_t)addr & 3) != 0) { + fatal("not an instruction address"); + } +} +#endif // ASSERT + +// Extract call destination from a NativeCall. The call might use a trampoline stub. +address NativeCall::destination() const { + address addr = (address)this; + address destination = Assembler::bxx_destination(addr); + + // Do we use a trampoline stub for this call? + CodeBlob* cb = CodeCache::find_blob_unsafe(addr); // Else we get assertion if nmethod is zombie. + assert(cb && cb->is_nmethod(), "sanity"); + nmethod *nm = (nmethod *)cb; + if (nm->stub_contains(destination) && is_NativeCallTrampolineStub_at(destination)) { + // Yes we do, so get the destination from the trampoline stub. + const address trampoline_stub_addr = destination; + destination = NativeCallTrampolineStub_at(trampoline_stub_addr)->destination(); + } + + return destination; +} + +// Similar to replace_mt_safe, but just changes the destination. The +// important thing is that free-running threads are able to execute this +// call instruction at all times. Thus, the displacement field must be +// instruction-word-aligned. +// +// Used in the runtime linkage of calls; see class CompiledIC. +// +// Add parameter assert_lock to switch off assertion +// during code generation, where no patching lock is needed. +void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { + assert(!assert_lock || + (Patching_lock->is_locked() || SafepointSynchronize::is_at_safepoint()), + "concurrent code patching"); + + ResourceMark rm; + int code_size = 1 * BytesPerInstWord; + address addr_call = addr_at(0); + assert(MacroAssembler::is_bl(*(int*)addr_call), "unexpected code at call-site"); + + CodeBuffer cb(addr_call, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); + + // Patch the call. + if (ReoptimizeCallSequences && + a->is_within_range_of_b(dest, addr_call)) { + a->bl(dest); + } else { + address trampoline_stub_addr = get_trampoline(); + + // We did not find a trampoline stub because the current codeblob + // does not provide this information. The branch will be patched + // later during a final fixup, when all necessary information is + // available. + if (trampoline_stub_addr == 0) + return; + + // Patch the constant in the call's trampoline stub. + NativeCallTrampolineStub_at(trampoline_stub_addr)->set_destination(dest); + + a->bl(trampoline_stub_addr); + } + ICache::ppc64_flush_icache_bytes(addr_call, code_size); +} + +address NativeCall::get_trampoline() { + address call_addr = addr_at(0); + + CodeBlob *code = CodeCache::find_blob(call_addr); + assert(code != NULL, "Could not find the containing code blob"); + + // There are no relocations available when the code gets relocated + // because of CodeBuffer expansion. + if (code->relocation_size() == 0) + return NULL; + + address bl_destination = Assembler::bxx_destination(call_addr); + if (code->content_contains(bl_destination) && + is_NativeCallTrampolineStub_at(bl_destination)) + return bl_destination; + + // If the codeBlob is not a nmethod, this is because we get here from the + // CodeBlob constructor, which is called within the nmethod constructor. + return trampoline_stub_Relocation::get_trampoline_for(call_addr, (nmethod*)code); +} + +#ifdef ASSERT +void NativeCall::verify() { + address addr = addr_at(0); + + if (!NativeCall::is_call_at(addr)) { + tty->print_cr("not a NativeCall at " PTR_FORMAT, addr); + // TODO: PPC port: Disassembler::decode(addr - 20, addr + 20, tty); + fatal(err_msg("not a NativeCall at " PTR_FORMAT, addr)); + } +} +#endif // ASSERT + +#ifdef ASSERT +void NativeFarCall::verify() { + address addr = addr_at(0); + + NativeInstruction::verify(); + if (!NativeFarCall::is_far_call_at(addr)) { + tty->print_cr("not a NativeFarCall at " PTR_FORMAT, addr); + // TODO: PPC port: Disassembler::decode(addr, 20, 20, tty); + fatal(err_msg("not a NativeFarCall at " PTR_FORMAT, addr)); + } +} +#endif // ASSERT + +address NativeMovConstReg::next_instruction_address() const { +#ifdef ASSERT + CodeBlob* nm = CodeCache::find_blob(instruction_address()); + assert(!MacroAssembler::is_set_narrow_oop(addr_at(0), nm->content_begin()), "Should not patch narrow oop here"); +#endif + + if (MacroAssembler::is_load_const_from_method_toc_at(addr_at(0))) { + return addr_at(load_const_from_method_toc_instruction_size); + } else { + return addr_at(load_const_instruction_size); + } +} + +intptr_t NativeMovConstReg::data() const { + address addr = addr_at(0); + CodeBlob* cb = CodeCache::find_blob_unsafe(addr); + + if (MacroAssembler::is_load_const_at(addr)) { + return MacroAssembler::get_const(addr); + } else if (MacroAssembler::is_set_narrow_oop(addr, cb->content_begin())) { + narrowOop no = (narrowOop)MacroAssembler::get_narrow_oop(addr, cb->content_begin()); + return (intptr_t)oopDesc::decode_heap_oop(no); + } else { + assert(MacroAssembler::is_load_const_from_method_toc_at(addr), "must be load_const_from_pool"); + + address ctable = cb->content_begin(); + int offset = MacroAssembler::get_offset_of_load_const_from_method_toc_at(addr); + return *(intptr_t *)(ctable + offset); + } +} + +address NativeMovConstReg::set_data_plain(intptr_t data, CodeBlob *cb) { + address addr = instruction_address(); + address next_address = NULL; + if (!cb) cb = CodeCache::find_blob(addr); + + if (cb != NULL && MacroAssembler::is_load_const_from_method_toc_at(addr)) { + // A load from the method's TOC (ctable). + assert(cb->is_nmethod(), "must be nmethod"); + const address ctable = cb->content_begin(); + const int toc_offset = MacroAssembler::get_offset_of_load_const_from_method_toc_at(addr); + *(intptr_t *)(ctable + toc_offset) = data; + next_address = addr + BytesPerInstWord; + } else if (cb != NULL && + MacroAssembler::is_calculate_address_from_global_toc_at(addr, cb->content_begin())) { + // A calculation relative to the global TOC. + if (MacroAssembler::get_address_of_calculate_address_from_global_toc_at(addr, cb->content_begin()) != + (address)data) { + const int invalidated_range = + MacroAssembler::patch_calculate_address_from_global_toc_at(addr, cb->content_begin(), + (address)data); + const address start = invalidated_range < 0 ? addr + invalidated_range : addr; + // FIXME: + const int range = invalidated_range < 0 ? 4 - invalidated_range : 8; + ICache::ppc64_flush_icache_bytes(start, range); + } + next_address = addr + 1 * BytesPerInstWord; + } else if (MacroAssembler::is_load_const_at(addr)) { + // A normal 5 instruction load_const code sequence. + if (MacroAssembler::get_const(addr) != (long)data) { + // This is not mt safe, ok in methods like CodeBuffer::copy_code(). + MacroAssembler::patch_const(addr, (long)data); + ICache::ppc64_flush_icache_bytes(addr, load_const_instruction_size); + } + next_address = addr + 5 * BytesPerInstWord; + } else if (MacroAssembler::is_bl(* (int*) addr)) { + // A single branch-and-link instruction. + ResourceMark rm; + const int code_size = 1 * BytesPerInstWord; + CodeBuffer cb(addr, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); + a->bl((address) data); + ICache::ppc64_flush_icache_bytes(addr, code_size); + next_address = addr + code_size; + } else { + ShouldNotReachHere(); + } + + return next_address; +} + +void NativeMovConstReg::set_data(intptr_t data) { + // Store the value into the instruction stream. + CodeBlob *cb = CodeCache::find_blob(instruction_address()); + address next_address = set_data_plain(data, cb); + + // Also store the value into an oop_Relocation cell, if any. + if (cb && cb->is_nmethod()) { + RelocIterator iter((nmethod *) cb, instruction_address(), next_address); + oop* oop_addr = NULL; + while (iter.next()) { + if (iter.type() == relocInfo::oop_type) { + oop_Relocation *r = iter.oop_reloc(); + if (oop_addr == NULL) { + oop_addr = r->oop_addr(); + *oop_addr = (oop)data; + } else { + assert(oop_addr == r->oop_addr(), "must be only one set-oop here") ; + } + } + } + } +} + +void NativeMovConstReg::set_narrow_oop(intptr_t data, CodeBlob *code /* = NULL */) { + address addr = addr_at(0); + CodeBlob* cb = (code) ? code : CodeCache::find_blob(instruction_address()); + if (MacroAssembler::get_narrow_oop(addr, cb->content_begin()) == (long)data) return; + const int invalidated_range = + MacroAssembler::patch_set_narrow_oop(addr, cb->content_begin(), (long)data); + const address start = invalidated_range < 0 ? addr + invalidated_range : addr; + // FIXME: + const int range = invalidated_range < 0 ? 4 - invalidated_range : 8; + ICache::ppc64_flush_icache_bytes(start, range); +} + +// Do not use an assertion here. Let clients decide whether they only +// want this when assertions are enabled. +#ifdef ASSERT +void NativeMovConstReg::verify() { + address addr = addr_at(0); + CodeBlob* cb = CodeCache::find_blob_unsafe(addr); // find_nmethod() asserts if nmethod is zombie. + if (! MacroAssembler::is_load_const_at(addr) && + ! MacroAssembler::is_load_const_from_method_toc_at(addr) && + ! (cb != NULL && MacroAssembler::is_calculate_address_from_global_toc_at(addr, cb->content_begin())) && + ! (cb != NULL && MacroAssembler::is_set_narrow_oop(addr, cb->content_begin())) && + ! MacroAssembler::is_bl(*((int*) addr))) { + tty->print_cr("not a NativeMovConstReg at " PTR_FORMAT, addr); + // TODO: PPC port Disassembler::decode(addr, 20, 20, tty); + fatal(err_msg("not a NativeMovConstReg at " PTR_FORMAT, addr)); + } +} +#endif // ASSERT + +void NativeJump::patch_verified_entry(address entry, address verified_entry, address dest) { + ResourceMark rm; + int code_size = 1 * BytesPerInstWord; + CodeBuffer cb(verified_entry, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); +#ifdef COMPILER2 + assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "expected fixed destination of patch"); +#endif + // Patch this nmethod atomically. Always use illtrap/trap in debug build. + if (DEBUG_ONLY(false &&) a->is_within_range_of_b(dest, a->pc())) { + a->b(dest); + } else { + // The signal handler will continue at dest=OptoRuntime::handle_wrong_method_stub(). + if (TrapBasedNotEntrantChecks) { + // We use a special trap for marking a method as not_entrant or zombie. + a->trap_zombie_not_entrant(); + } else { + // We use an illtrap for marking a method as not_entrant or zombie. + a->illtrap(); + } + } + ICache::ppc64_flush_icache_bytes(verified_entry, code_size); +} + +#ifdef ASSERT +void NativeJump::verify() { + address addr = addr_at(0); + + NativeInstruction::verify(); + if (!NativeJump::is_jump_at(addr)) { + tty->print_cr("not a NativeJump at " PTR_FORMAT, addr); + // TODO: PPC port: Disassembler::decode(addr, 20, 20, tty); + fatal(err_msg("not a NativeJump at " PTR_FORMAT, addr)); + } +} +#endif // ASSERT + +//------------------------------------------------------------------- + +// Call trampoline stubs. +// +// Layout and instructions of a call trampoline stub: +// 0: load the TOC (part 1) +// 4: load the TOC (part 2) +// 8: load the call target from the constant pool (part 1) +// [12: load the call target from the constant pool (part 2, optional)] +// ..: branch via CTR +// + +address NativeCallTrampolineStub::encoded_destination_addr() const { + address instruction_addr = addr_at(2 * BytesPerInstWord); + assert(MacroAssembler::is_ld_largeoffset(instruction_addr), + "must be a ld with large offset (from the constant pool)"); + + return instruction_addr; +} + +address NativeCallTrampolineStub::destination() const { + CodeBlob* cb = CodeCache::find_blob(addr_at(0)); + address ctable = cb->content_begin(); + + return *(address*)(ctable + destination_toc_offset()); +} + +int NativeCallTrampolineStub::destination_toc_offset() const { + return MacroAssembler::get_ld_largeoffset_offset(encoded_destination_addr()); +} + +void NativeCallTrampolineStub::set_destination(address new_destination) { + CodeBlob* cb = CodeCache::find_blob(addr_at(0)); + address ctable = cb->content_begin(); + + *(address*)(ctable + destination_toc_offset()) = new_destination; +} + diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/nativeInst_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/nativeInst_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_NATIVEINST_PPC_HPP +#define CPU_PPC_VM_NATIVEINST_PPC_HPP + +#include "asm/assembler.hpp" +#include "memory/allocation.hpp" +#include "runtime/icache.hpp" +#include "runtime/os.hpp" +#include "utilities/top.hpp" + +// We have interfaces for the following instructions: +// +// - NativeInstruction +// - NativeCall +// - NativeFarCall +// - NativeMovConstReg +// - NativeJump +// - NativeIllegalInstruction +// - NativeConditionalFarBranch +// - NativeCallTrampolineStub + +// The base class for different kinds of native instruction abstractions. +// It provides the primitive operations to manipulate code relative to this. +class NativeInstruction VALUE_OBJ_CLASS_SPEC { + friend class Relocation; + + public: + bool is_sigtrap_ic_miss_check() { + assert(UseSIGTRAP, "precondition"); + return MacroAssembler::is_trap_ic_miss_check(long_at(0)); + } + + bool is_sigtrap_null_check() { + assert(UseSIGTRAP && TrapBasedNullChecks, "precondition"); + return MacroAssembler::is_trap_null_check(long_at(0)); + } + + // We use a special trap for marking a method as not_entrant or zombie + // iff UseSIGTRAP. + bool is_sigtrap_zombie_not_entrant() { + assert(UseSIGTRAP, "precondition"); + return MacroAssembler::is_trap_zombie_not_entrant(long_at(0)); + } + + // We use an illtrap for marking a method as not_entrant or zombie + // iff !UseSIGTRAP. + bool is_sigill_zombie_not_entrant() { + assert(!UseSIGTRAP, "precondition"); + // Work around a C++ compiler bug which changes 'this'. + return NativeInstruction::is_sigill_zombie_not_entrant_at(addr_at(0)); + } + static bool is_sigill_zombie_not_entrant_at(address addr); + + // SIGTRAP-based implicit range checks + bool is_sigtrap_range_check() { + assert(UseSIGTRAP && TrapBasedRangeChecks, "precondition"); + return MacroAssembler::is_trap_range_check(long_at(0)); + } + + // 'should not reach here'. + bool is_sigtrap_should_not_reach_here() { + return MacroAssembler::is_trap_should_not_reach_here(long_at(0)); + } + + bool is_safepoint_poll() { + // Is the current instruction a POTENTIAL read access to the polling page? + // The current arguments of the instruction are not checked! + return MacroAssembler::is_load_from_polling_page(long_at(0), NULL); + } + + bool is_memory_serialization(JavaThread *thread, void *ucontext) { + // Is the current instruction a write access of thread to the + // memory serialization page? + return MacroAssembler::is_memory_serialization(long_at(0), thread, ucontext); + } + + address get_stack_bang_address(void *ucontext) { + // If long_at(0) is not a stack bang, return 0. Otherwise, return + // banged address. + return MacroAssembler::get_stack_bang_address(long_at(0), ucontext); + } + + protected: + address addr_at(int offset) const { return address(this) + offset; } + int long_at(int offset) const { return *(int*)addr_at(offset); } + + public: + void verify() NOT_DEBUG_RETURN; +}; + +inline NativeInstruction* nativeInstruction_at(address address) { + NativeInstruction* inst = (NativeInstruction*)address; + inst->verify(); + return inst; +} + +// The NativeCall is an abstraction for accessing/manipulating call +// instructions. It is used to manipulate inline caches, primitive & +// dll calls, etc. +// +// Sparc distinguishes `NativeCall' and `NativeFarCall'. On PPC64, +// at present, we provide a single class `NativeCall' representing the +// sequence `load_const, mtctr, bctrl' or the sequence 'ld_from_toc, +// mtctr, bctrl'. +class NativeCall: public NativeInstruction { + public: + + enum ppc_specific_constants { + load_const_instruction_size = 28, + load_const_from_method_toc_instruction_size = 16, + instruction_size = 16 // Used in shared code for calls with reloc_info. + }; + + static bool is_call_at(address a) { + return Assembler::is_bl(*(int*)(a)); + } + + static bool is_call_before(address return_address) { + return NativeCall::is_call_at(return_address - 4); + } + + address instruction_address() const { + return addr_at(0); + } + + address next_instruction_address() const { + // We have only bl. + assert(MacroAssembler::is_bl(*(int*)instruction_address()), "Should be bl instruction!"); + return addr_at(4); + } + + address return_address() const { + return next_instruction_address(); + } + + address destination() const; + + // The parameter assert_lock disables the assertion during code generation. + void set_destination_mt_safe(address dest, bool assert_lock = true); + + address get_trampoline(); + + void verify_alignment() {} // do nothing on ppc + void verify() NOT_DEBUG_RETURN; +}; + +inline NativeCall* nativeCall_at(address instr) { + NativeCall* call = (NativeCall*)instr; + call->verify(); + return call; +} + +inline NativeCall* nativeCall_before(address return_address) { + NativeCall* call = NULL; + if (MacroAssembler::is_bl(*(int*)(return_address - 4))) + call = (NativeCall*)(return_address - 4); + call->verify(); + return call; +} + +// The NativeFarCall is an abstraction for accessing/manipulating native +// call-anywhere instructions. +// Used to call native methods which may be loaded anywhere in the address +// space, possibly out of reach of a call instruction. +class NativeFarCall: public NativeInstruction { + public: + // We use MacroAssembler::bl64_patchable() for implementing a + // call-anywhere instruction. + + // Checks whether instr points at a NativeFarCall instruction. + static bool is_far_call_at(address instr) { + return MacroAssembler::is_bl64_patchable_at(instr); + } + + // Does the NativeFarCall implementation use a pc-relative encoding + // of the call destination? + // Used when relocating code. + bool is_pcrelative() { + assert(MacroAssembler::is_bl64_patchable_at((address)this), + "unexpected call type"); + return MacroAssembler::is_bl64_patchable_pcrelative_at((address)this); + } + + // Returns the NativeFarCall's destination. + address destination() const { + assert(MacroAssembler::is_bl64_patchable_at((address)this), + "unexpected call type"); + return MacroAssembler::get_dest_of_bl64_patchable_at((address)this); + } + + // Sets the NativeCall's destination, not necessarily mt-safe. + // Used when relocating code. + void set_destination(address dest) { + // Set new destination (implementation of call may change here). + assert(MacroAssembler::is_bl64_patchable_at((address)this), + "unexpected call type"); + MacroAssembler::set_dest_of_bl64_patchable_at((address)this, dest); + } + + void verify() NOT_DEBUG_RETURN; +}; + +// Instantiates a NativeFarCall object starting at the given instruction +// address and returns the NativeFarCall object. +inline NativeFarCall* nativeFarCall_at(address instr) { + NativeFarCall* call = (NativeFarCall*)instr; + call->verify(); + return call; +} + +// An interface for accessing/manipulating native set_oop imm, reg instructions. +// (used to manipulate inlined data references, etc.) +class NativeMovConstReg: public NativeInstruction { + public: + + enum ppc_specific_constants { + load_const_instruction_size = 20, + load_const_from_method_toc_instruction_size = 8, + instruction_size = 8 // Used in shared code for calls with reloc_info. + }; + + address instruction_address() const { + return addr_at(0); + } + + address next_instruction_address() const; + + // (The [set_]data accessor respects oop_type relocs also.) + intptr_t data() const; + + // Patch the code stream. + address set_data_plain(intptr_t x, CodeBlob *code); + // Patch the code stream and oop pool. + void set_data(intptr_t x); + + // Patch narrow oop constants. + void set_narrow_oop(intptr_t data, CodeBlob *code = NULL); + + void verify() NOT_DEBUG_RETURN; +}; + +inline NativeMovConstReg* nativeMovConstReg_at(address address) { + NativeMovConstReg* test = (NativeMovConstReg*)address; + test->verify(); + return test; +} + +// The NativeJump is an abstraction for accessing/manipulating native +// jump-anywhere instructions. +class NativeJump: public NativeInstruction { + public: + // We use MacroAssembler::b64_patchable() for implementing a + // jump-anywhere instruction. + + enum ppc_specific_constants { + instruction_size = MacroAssembler::b64_patchable_size + }; + + // Checks whether instr points at a NativeJump instruction. + static bool is_jump_at(address instr) { + return MacroAssembler::is_b64_patchable_at(instr) + || ( MacroAssembler::is_load_const_from_method_toc_at(instr) + && Assembler::is_mtctr(*(int*)(instr + 2 * 4)) + && Assembler::is_bctr(*(int*)(instr + 3 * 4))); + } + + // Does the NativeJump implementation use a pc-relative encoding + // of the call destination? + // Used when relocating code or patching jumps. + bool is_pcrelative() { + return MacroAssembler::is_b64_patchable_pcrelative_at((address)this); + } + + // Returns the NativeJump's destination. + address jump_destination() const { + if (MacroAssembler::is_b64_patchable_at((address)this)) { + return MacroAssembler::get_dest_of_b64_patchable_at((address)this); + } else if (MacroAssembler::is_load_const_from_method_toc_at((address)this) + && Assembler::is_mtctr(*(int*)((address)this + 2 * 4)) + && Assembler::is_bctr(*(int*)((address)this + 3 * 4))) { + return (address)((NativeMovConstReg *)this)->data(); + } else { + ShouldNotReachHere(); + return NULL; + } + } + + // Sets the NativeJump's destination, not necessarily mt-safe. + // Used when relocating code or patching jumps. + void set_jump_destination(address dest) { + // Set new destination (implementation of call may change here). + if (MacroAssembler::is_b64_patchable_at((address)this)) { + MacroAssembler::set_dest_of_b64_patchable_at((address)this, dest); + } else if (MacroAssembler::is_load_const_from_method_toc_at((address)this) + && Assembler::is_mtctr(*(int*)((address)this + 2 * 4)) + && Assembler::is_bctr(*(int*)((address)this + 3 * 4))) { + ((NativeMovConstReg *)this)->set_data((intptr_t)dest); + } else { + ShouldNotReachHere(); + } + } + + // MT-safe insertion of native jump at verified method entry + static void patch_verified_entry(address entry, address verified_entry, address dest); + + void verify() NOT_DEBUG_RETURN; + + static void check_verified_entry_alignment(address entry, address verified_entry) { + // We just patch one instruction on ppc64, so the jump doesn't have to + // be aligned. Nothing to do here. + } +}; + +// Instantiates a NativeJump object starting at the given instruction +// address and returns the NativeJump object. +inline NativeJump* nativeJump_at(address instr) { + NativeJump* call = (NativeJump*)instr; + call->verify(); + return call; +} + +// NativeConditionalFarBranch is abstraction for accessing/manipulating +// conditional far branches. +class NativeConditionalFarBranch : public NativeInstruction { + public: + + static bool is_conditional_far_branch_at(address instr) { + return MacroAssembler::is_bc_far_at(instr); + } + + address branch_destination() const { + return MacroAssembler::get_dest_of_bc_far_at((address)this); + } + + void set_branch_destination(address dest) { + MacroAssembler::set_dest_of_bc_far_at((address)this, dest); + } +}; + +inline NativeConditionalFarBranch* NativeConditionalFarBranch_at(address address) { + assert(NativeConditionalFarBranch::is_conditional_far_branch_at(address), + "must be a conditional far branch"); + return (NativeConditionalFarBranch*)address; +} + +// Call trampoline stubs. +class NativeCallTrampolineStub : public NativeInstruction { + private: + + address encoded_destination_addr() const; + + public: + + address destination() const; + int destination_toc_offset() const; + + void set_destination(address new_destination); +}; + +inline bool is_NativeCallTrampolineStub_at(address address) { + int first_instr = *(int*)address; + return Assembler::is_addis(first_instr) && + (Register)(intptr_t)Assembler::inv_rt_field(first_instr) == R12_scratch2; +} + +inline NativeCallTrampolineStub* NativeCallTrampolineStub_at(address address) { + assert(is_NativeCallTrampolineStub_at(address), "no call trampoline found"); + return (NativeCallTrampolineStub*)address; +} + +#endif // CPU_PPC_VM_NATIVEINST_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/ppc.ad --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/ppc.ad Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,12869 @@ +// +// Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. +// Copyright 2012, 2014 SAP AG. 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. +// +// + +// +// PPC64 Architecture Description File +// + +//----------REGISTER DEFINITION BLOCK------------------------------------------ +// This information is used by the matcher and the register allocator to +// describe individual registers and classes of registers within the target +// architecture. +register %{ +//----------Architecture Description Register Definitions---------------------- +// General Registers +// "reg_def" name (register save type, C convention save type, +// ideal register type, encoding); +// +// Register Save Types: +// +// NS = No-Save: The register allocator assumes that these registers +// can be used without saving upon entry to the method, & +// that they do not need to be saved at call sites. +// +// SOC = Save-On-Call: The register allocator assumes that these registers +// can be used without saving upon entry to the method, +// but that they must be saved at call sites. +// These are called "volatiles" on ppc. +// +// SOE = Save-On-Entry: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, but they do not need to be saved at call +// sites. +// These are called "nonvolatiles" on ppc. +// +// AS = Always-Save: The register allocator assumes that these registers +// must be saved before using them upon entry to the +// method, & that they must be saved at call sites. +// +// Ideal Register Type is used to determine how to save & restore a +// register. Op_RegI will get spilled with LoadI/StoreI, Op_RegP will get +// spilled with LoadP/StoreP. If the register supports both, use Op_RegI. +// +// The encoding number is the actual bit-pattern placed into the opcodes. +// +// PPC64 register definitions, based on the 64-bit PowerPC ELF ABI +// Supplement Version 1.7 as of 2003-10-29. +// +// For each 64-bit register we must define two registers: the register +// itself, e.g. R3, and a corresponding virtual other (32-bit-)'half', +// e.g. R3_H, which is needed by the allocator, but is not used +// for stores, loads, etc. + +// ---------------------------- +// Integer/Long Registers +// ---------------------------- + + // PPC64 has 32 64-bit integer registers. + + // types: v = volatile, nv = non-volatile, s = system + reg_def R0 ( SOC, SOC, Op_RegI, 0, R0->as_VMReg() ); // v used in prologs + reg_def R0_H ( SOC, SOC, Op_RegI, 99, R0->as_VMReg()->next() ); + reg_def R1 ( NS, NS, Op_RegI, 1, R1->as_VMReg() ); // s SP + reg_def R1_H ( NS, NS, Op_RegI, 99, R1->as_VMReg()->next() ); + reg_def R2 ( SOC, SOC, Op_RegI, 2, R2->as_VMReg() ); // v TOC + reg_def R2_H ( SOC, SOC, Op_RegI, 99, R2->as_VMReg()->next() ); + reg_def R3 ( SOC, SOC, Op_RegI, 3, R3->as_VMReg() ); // v iarg1 & iret + reg_def R3_H ( SOC, SOC, Op_RegI, 99, R3->as_VMReg()->next() ); + reg_def R4 ( SOC, SOC, Op_RegI, 4, R4->as_VMReg() ); // iarg2 + reg_def R4_H ( SOC, SOC, Op_RegI, 99, R4->as_VMReg()->next() ); + reg_def R5 ( SOC, SOC, Op_RegI, 5, R5->as_VMReg() ); // v iarg3 + reg_def R5_H ( SOC, SOC, Op_RegI, 99, R5->as_VMReg()->next() ); + reg_def R6 ( SOC, SOC, Op_RegI, 6, R6->as_VMReg() ); // v iarg4 + reg_def R6_H ( SOC, SOC, Op_RegI, 99, R6->as_VMReg()->next() ); + reg_def R7 ( SOC, SOC, Op_RegI, 7, R7->as_VMReg() ); // v iarg5 + reg_def R7_H ( SOC, SOC, Op_RegI, 99, R7->as_VMReg()->next() ); + reg_def R8 ( SOC, SOC, Op_RegI, 8, R8->as_VMReg() ); // v iarg6 + reg_def R8_H ( SOC, SOC, Op_RegI, 99, R8->as_VMReg()->next() ); + reg_def R9 ( SOC, SOC, Op_RegI, 9, R9->as_VMReg() ); // v iarg7 + reg_def R9_H ( SOC, SOC, Op_RegI, 99, R9->as_VMReg()->next() ); + reg_def R10 ( SOC, SOC, Op_RegI, 10, R10->as_VMReg() ); // v iarg8 + reg_def R10_H( SOC, SOC, Op_RegI, 99, R10->as_VMReg()->next()); + reg_def R11 ( SOC, SOC, Op_RegI, 11, R11->as_VMReg() ); // v ENV / scratch + reg_def R11_H( SOC, SOC, Op_RegI, 99, R11->as_VMReg()->next()); + reg_def R12 ( SOC, SOC, Op_RegI, 12, R12->as_VMReg() ); // v scratch + reg_def R12_H( SOC, SOC, Op_RegI, 99, R12->as_VMReg()->next()); + reg_def R13 ( NS, NS, Op_RegI, 13, R13->as_VMReg() ); // s system thread id + reg_def R13_H( NS, NS, Op_RegI, 99, R13->as_VMReg()->next()); + reg_def R14 ( SOC, SOE, Op_RegI, 14, R14->as_VMReg() ); // nv + reg_def R14_H( SOC, SOE, Op_RegI, 99, R14->as_VMReg()->next()); + reg_def R15 ( SOC, SOE, Op_RegI, 15, R15->as_VMReg() ); // nv + reg_def R15_H( SOC, SOE, Op_RegI, 99, R15->as_VMReg()->next()); + reg_def R16 ( SOC, SOE, Op_RegI, 16, R16->as_VMReg() ); // nv + reg_def R16_H( SOC, SOE, Op_RegI, 99, R16->as_VMReg()->next()); + reg_def R17 ( SOC, SOE, Op_RegI, 17, R17->as_VMReg() ); // nv + reg_def R17_H( SOC, SOE, Op_RegI, 99, R17->as_VMReg()->next()); + reg_def R18 ( SOC, SOE, Op_RegI, 18, R18->as_VMReg() ); // nv + reg_def R18_H( SOC, SOE, Op_RegI, 99, R18->as_VMReg()->next()); + reg_def R19 ( SOC, SOE, Op_RegI, 19, R19->as_VMReg() ); // nv + reg_def R19_H( SOC, SOE, Op_RegI, 99, R19->as_VMReg()->next()); + reg_def R20 ( SOC, SOE, Op_RegI, 20, R20->as_VMReg() ); // nv + reg_def R20_H( SOC, SOE, Op_RegI, 99, R20->as_VMReg()->next()); + reg_def R21 ( SOC, SOE, Op_RegI, 21, R21->as_VMReg() ); // nv + reg_def R21_H( SOC, SOE, Op_RegI, 99, R21->as_VMReg()->next()); + reg_def R22 ( SOC, SOE, Op_RegI, 22, R22->as_VMReg() ); // nv + reg_def R22_H( SOC, SOE, Op_RegI, 99, R22->as_VMReg()->next()); + reg_def R23 ( SOC, SOE, Op_RegI, 23, R23->as_VMReg() ); // nv + reg_def R23_H( SOC, SOE, Op_RegI, 99, R23->as_VMReg()->next()); + reg_def R24 ( SOC, SOE, Op_RegI, 24, R24->as_VMReg() ); // nv + reg_def R24_H( SOC, SOE, Op_RegI, 99, R24->as_VMReg()->next()); + reg_def R25 ( SOC, SOE, Op_RegI, 25, R25->as_VMReg() ); // nv + reg_def R25_H( SOC, SOE, Op_RegI, 99, R25->as_VMReg()->next()); + reg_def R26 ( SOC, SOE, Op_RegI, 26, R26->as_VMReg() ); // nv + reg_def R26_H( SOC, SOE, Op_RegI, 99, R26->as_VMReg()->next()); + reg_def R27 ( SOC, SOE, Op_RegI, 27, R27->as_VMReg() ); // nv + reg_def R27_H( SOC, SOE, Op_RegI, 99, R27->as_VMReg()->next()); + reg_def R28 ( SOC, SOE, Op_RegI, 28, R28->as_VMReg() ); // nv + reg_def R28_H( SOC, SOE, Op_RegI, 99, R28->as_VMReg()->next()); + reg_def R29 ( SOC, SOE, Op_RegI, 29, R29->as_VMReg() ); // nv + reg_def R29_H( SOC, SOE, Op_RegI, 99, R29->as_VMReg()->next()); + reg_def R30 ( SOC, SOE, Op_RegI, 30, R30->as_VMReg() ); // nv + reg_def R30_H( SOC, SOE, Op_RegI, 99, R30->as_VMReg()->next()); + reg_def R31 ( SOC, SOE, Op_RegI, 31, R31->as_VMReg() ); // nv + reg_def R31_H( SOC, SOE, Op_RegI, 99, R31->as_VMReg()->next()); + + +// ---------------------------- +// Float/Double Registers +// ---------------------------- + + // Double Registers + // The rules of ADL require that double registers be defined in pairs. + // Each pair must be two 32-bit values, but not necessarily a pair of + // single float registers. In each pair, ADLC-assigned register numbers + // must be adjacent, with the lower number even. Finally, when the + // CPU stores such a register pair to memory, the word associated with + // the lower ADLC-assigned number must be stored to the lower address. + + // PPC64 has 32 64-bit floating-point registers. Each can store a single + // or double precision floating-point value. + + // types: v = volatile, nv = non-volatile, s = system + reg_def F0 ( SOC, SOC, Op_RegF, 0, F0->as_VMReg() ); // v scratch + reg_def F0_H ( SOC, SOC, Op_RegF, 99, F0->as_VMReg()->next() ); + reg_def F1 ( SOC, SOC, Op_RegF, 1, F1->as_VMReg() ); // v farg1 & fret + reg_def F1_H ( SOC, SOC, Op_RegF, 99, F1->as_VMReg()->next() ); + reg_def F2 ( SOC, SOC, Op_RegF, 2, F2->as_VMReg() ); // v farg2 + reg_def F2_H ( SOC, SOC, Op_RegF, 99, F2->as_VMReg()->next() ); + reg_def F3 ( SOC, SOC, Op_RegF, 3, F3->as_VMReg() ); // v farg3 + reg_def F3_H ( SOC, SOC, Op_RegF, 99, F3->as_VMReg()->next() ); + reg_def F4 ( SOC, SOC, Op_RegF, 4, F4->as_VMReg() ); // v farg4 + reg_def F4_H ( SOC, SOC, Op_RegF, 99, F4->as_VMReg()->next() ); + reg_def F5 ( SOC, SOC, Op_RegF, 5, F5->as_VMReg() ); // v farg5 + reg_def F5_H ( SOC, SOC, Op_RegF, 99, F5->as_VMReg()->next() ); + reg_def F6 ( SOC, SOC, Op_RegF, 6, F6->as_VMReg() ); // v farg6 + reg_def F6_H ( SOC, SOC, Op_RegF, 99, F6->as_VMReg()->next() ); + reg_def F7 ( SOC, SOC, Op_RegF, 7, F7->as_VMReg() ); // v farg7 + reg_def F7_H ( SOC, SOC, Op_RegF, 99, F7->as_VMReg()->next() ); + reg_def F8 ( SOC, SOC, Op_RegF, 8, F8->as_VMReg() ); // v farg8 + reg_def F8_H ( SOC, SOC, Op_RegF, 99, F8->as_VMReg()->next() ); + reg_def F9 ( SOC, SOC, Op_RegF, 9, F9->as_VMReg() ); // v farg9 + reg_def F9_H ( SOC, SOC, Op_RegF, 99, F9->as_VMReg()->next() ); + reg_def F10 ( SOC, SOC, Op_RegF, 10, F10->as_VMReg() ); // v farg10 + reg_def F10_H( SOC, SOC, Op_RegF, 99, F10->as_VMReg()->next()); + reg_def F11 ( SOC, SOC, Op_RegF, 11, F11->as_VMReg() ); // v farg11 + reg_def F11_H( SOC, SOC, Op_RegF, 99, F11->as_VMReg()->next()); + reg_def F12 ( SOC, SOC, Op_RegF, 12, F12->as_VMReg() ); // v farg12 + reg_def F12_H( SOC, SOC, Op_RegF, 99, F12->as_VMReg()->next()); + reg_def F13 ( SOC, SOC, Op_RegF, 13, F13->as_VMReg() ); // v farg13 + reg_def F13_H( SOC, SOC, Op_RegF, 99, F13->as_VMReg()->next()); + reg_def F14 ( SOC, SOE, Op_RegF, 14, F14->as_VMReg() ); // nv + reg_def F14_H( SOC, SOE, Op_RegF, 99, F14->as_VMReg()->next()); + reg_def F15 ( SOC, SOE, Op_RegF, 15, F15->as_VMReg() ); // nv + reg_def F15_H( SOC, SOE, Op_RegF, 99, F15->as_VMReg()->next()); + reg_def F16 ( SOC, SOE, Op_RegF, 16, F16->as_VMReg() ); // nv + reg_def F16_H( SOC, SOE, Op_RegF, 99, F16->as_VMReg()->next()); + reg_def F17 ( SOC, SOE, Op_RegF, 17, F17->as_VMReg() ); // nv + reg_def F17_H( SOC, SOE, Op_RegF, 99, F17->as_VMReg()->next()); + reg_def F18 ( SOC, SOE, Op_RegF, 18, F18->as_VMReg() ); // nv + reg_def F18_H( SOC, SOE, Op_RegF, 99, F18->as_VMReg()->next()); + reg_def F19 ( SOC, SOE, Op_RegF, 19, F19->as_VMReg() ); // nv + reg_def F19_H( SOC, SOE, Op_RegF, 99, F19->as_VMReg()->next()); + reg_def F20 ( SOC, SOE, Op_RegF, 20, F20->as_VMReg() ); // nv + reg_def F20_H( SOC, SOE, Op_RegF, 99, F20->as_VMReg()->next()); + reg_def F21 ( SOC, SOE, Op_RegF, 21, F21->as_VMReg() ); // nv + reg_def F21_H( SOC, SOE, Op_RegF, 99, F21->as_VMReg()->next()); + reg_def F22 ( SOC, SOE, Op_RegF, 22, F22->as_VMReg() ); // nv + reg_def F22_H( SOC, SOE, Op_RegF, 99, F22->as_VMReg()->next()); + reg_def F23 ( SOC, SOE, Op_RegF, 23, F23->as_VMReg() ); // nv + reg_def F23_H( SOC, SOE, Op_RegF, 99, F23->as_VMReg()->next()); + reg_def F24 ( SOC, SOE, Op_RegF, 24, F24->as_VMReg() ); // nv + reg_def F24_H( SOC, SOE, Op_RegF, 99, F24->as_VMReg()->next()); + reg_def F25 ( SOC, SOE, Op_RegF, 25, F25->as_VMReg() ); // nv + reg_def F25_H( SOC, SOE, Op_RegF, 99, F25->as_VMReg()->next()); + reg_def F26 ( SOC, SOE, Op_RegF, 26, F26->as_VMReg() ); // nv + reg_def F26_H( SOC, SOE, Op_RegF, 99, F26->as_VMReg()->next()); + reg_def F27 ( SOC, SOE, Op_RegF, 27, F27->as_VMReg() ); // nv + reg_def F27_H( SOC, SOE, Op_RegF, 99, F27->as_VMReg()->next()); + reg_def F28 ( SOC, SOE, Op_RegF, 28, F28->as_VMReg() ); // nv + reg_def F28_H( SOC, SOE, Op_RegF, 99, F28->as_VMReg()->next()); + reg_def F29 ( SOC, SOE, Op_RegF, 29, F29->as_VMReg() ); // nv + reg_def F29_H( SOC, SOE, Op_RegF, 99, F29->as_VMReg()->next()); + reg_def F30 ( SOC, SOE, Op_RegF, 30, F30->as_VMReg() ); // nv + reg_def F30_H( SOC, SOE, Op_RegF, 99, F30->as_VMReg()->next()); + reg_def F31 ( SOC, SOE, Op_RegF, 31, F31->as_VMReg() ); // nv + reg_def F31_H( SOC, SOE, Op_RegF, 99, F31->as_VMReg()->next()); + +// ---------------------------- +// Special Registers +// ---------------------------- + +// Condition Codes Flag Registers + + // PPC64 has 8 condition code "registers" which are all contained + // in the CR register. + + // types: v = volatile, nv = non-volatile, s = system + reg_def CCR0(SOC, SOC, Op_RegFlags, 0, CCR0->as_VMReg()); // v + reg_def CCR1(SOC, SOC, Op_RegFlags, 1, CCR1->as_VMReg()); // v + reg_def CCR2(SOC, SOC, Op_RegFlags, 2, CCR2->as_VMReg()); // nv + reg_def CCR3(SOC, SOC, Op_RegFlags, 3, CCR3->as_VMReg()); // nv + reg_def CCR4(SOC, SOC, Op_RegFlags, 4, CCR4->as_VMReg()); // nv + reg_def CCR5(SOC, SOC, Op_RegFlags, 5, CCR5->as_VMReg()); // v + reg_def CCR6(SOC, SOC, Op_RegFlags, 6, CCR6->as_VMReg()); // v + reg_def CCR7(SOC, SOC, Op_RegFlags, 7, CCR7->as_VMReg()); // v + + // Special registers of PPC64 + + reg_def SR_XER( SOC, SOC, Op_RegP, 0, SR_XER->as_VMReg()); // v + reg_def SR_LR( SOC, SOC, Op_RegP, 1, SR_LR->as_VMReg()); // v + reg_def SR_CTR( SOC, SOC, Op_RegP, 2, SR_CTR->as_VMReg()); // v + reg_def SR_VRSAVE( SOC, SOC, Op_RegP, 3, SR_VRSAVE->as_VMReg()); // v + reg_def SR_SPEFSCR(SOC, SOC, Op_RegP, 4, SR_SPEFSCR->as_VMReg()); // v + reg_def SR_PPR( SOC, SOC, Op_RegP, 5, SR_PPR->as_VMReg()); // v + + +// ---------------------------- +// Specify priority of register selection within phases of register +// allocation. Highest priority is first. A useful heuristic is to +// give registers a low priority when they are required by machine +// instructions, like EAX and EDX on I486, and choose no-save registers +// before save-on-call, & save-on-call before save-on-entry. Registers +// which participate in fixed calling sequences should come last. +// Registers which are used as pairs must fall on an even boundary. + +// It's worth about 1% on SPEC geomean to get this right. + +// Chunk0, chunk1, and chunk2 form the MachRegisterNumbers enumeration +// in adGlobals_ppc64.hpp which defines the _num values, e.g. +// R3_num. Therefore, R3_num may not be (and in reality is not) +// the same as R3->encoding()! Furthermore, we cannot make any +// assumptions on ordering, e.g. R3_num may be less than R2_num. +// Additionally, the function +// static enum RC rc_class(OptoReg::Name reg ) +// maps a given _num value to its chunk type (except for flags) +// and its current implementation relies on chunk0 and chunk1 having a +// size of 64 each. + +// If you change this allocation class, please have a look at the +// default values for the parameters RoundRobinIntegerRegIntervalStart +// and RoundRobinFloatRegIntervalStart + +alloc_class chunk0 ( + // Chunk0 contains *all* 64 integer registers halves. + + // "non-volatile" registers + R14, R14_H, + R15, R15_H, + R17, R17_H, + R18, R18_H, + R19, R19_H, + R20, R20_H, + R21, R21_H, + R22, R22_H, + R23, R23_H, + R24, R24_H, + R25, R25_H, + R26, R26_H, + R27, R27_H, + R28, R28_H, + R29, R29_H, + R30, R30_H, + R31, R31_H, + + // scratch/special registers + R11, R11_H, + R12, R12_H, + + // argument registers + R10, R10_H, + R9, R9_H, + R8, R8_H, + R7, R7_H, + R6, R6_H, + R5, R5_H, + R4, R4_H, + R3, R3_H, + + // special registers, not available for allocation + R16, R16_H, // R16_thread + R13, R13_H, // system thread id + R2, R2_H, // may be used for TOC + R1, R1_H, // SP + R0, R0_H // R0 (scratch) +); + +// If you change this allocation class, please have a look at the +// default values for the parameters RoundRobinIntegerRegIntervalStart +// and RoundRobinFloatRegIntervalStart + +alloc_class chunk1 ( + // Chunk1 contains *all* 64 floating-point registers halves. + + // scratch register + F0, F0_H, + + // argument registers + F13, F13_H, + F12, F12_H, + F11, F11_H, + F10, F10_H, + F9, F9_H, + F8, F8_H, + F7, F7_H, + F6, F6_H, + F5, F5_H, + F4, F4_H, + F3, F3_H, + F2, F2_H, + F1, F1_H, + + // non-volatile registers + F14, F14_H, + F15, F15_H, + F16, F16_H, + F17, F17_H, + F18, F18_H, + F19, F19_H, + F20, F20_H, + F21, F21_H, + F22, F22_H, + F23, F23_H, + F24, F24_H, + F25, F25_H, + F26, F26_H, + F27, F27_H, + F28, F28_H, + F29, F29_H, + F30, F30_H, + F31, F31_H +); + +alloc_class chunk2 ( + // Chunk2 contains *all* 8 condition code registers. + + CCR0, + CCR1, + CCR2, + CCR3, + CCR4, + CCR5, + CCR6, + CCR7 +); + +alloc_class chunk3 ( + // special registers + // These registers are not allocated, but used for nodes generated by postalloc expand. + SR_XER, + SR_LR, + SR_CTR, + SR_VRSAVE, + SR_SPEFSCR, + SR_PPR +); + +//-------Architecture Description Register Classes----------------------- + +// Several register classes are automatically defined based upon +// information in this architecture description. + +// 1) reg_class inline_cache_reg ( as defined in frame section ) +// 2) reg_class compiler_method_oop_reg ( as defined in frame section ) +// 2) reg_class interpreter_method_oop_reg ( as defined in frame section ) +// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) +// + +// ---------------------------- +// 32 Bit Register Classes +// ---------------------------- + +// We specify registers twice, once as read/write, and once read-only. +// We use the read-only registers for source operands. With this, we +// can include preset read only registers in this class, as a hard-coded +// '0'-register. (We used to simulate this on ppc.) + +// 32 bit registers that can be read and written i.e. these registers +// can be dest (or src) of normal instructions. +reg_class bits32_reg_rw( +/*R0*/ // R0 +/*R1*/ // SP + R2, // TOC + R3, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, +/*R13*/ // system thread id + R14, + R15, +/*R16*/ // R16_thread + R17, + R18, + R19, + R20, + R21, + R22, + R23, + R24, + R25, + R26, + R27, + R28, +/*R29*/ // global TOC +/*R30*/ // Narrow Oop Base + R31 +); + +// 32 bit registers that can only be read i.e. these registers can +// only be src of all instructions. +reg_class bits32_reg_ro( +/*R0*/ // R0 +/*R1*/ // SP + R2 // TOC + R3, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, +/*R13*/ // system thread id + R14, + R15, +/*R16*/ // R16_thread + R17, + R18, + R19, + R20, + R21, + R22, + R23, + R24, + R25, + R26, + R27, + R28, +/*R29*/ +/*R30*/ // Narrow Oop Base + R31 +); + +// Complement-required-in-pipeline operands for narrow oops. +reg_class bits32_reg_ro_not_complement ( +/*R0*/ // R0 + R1, // SP + R2, // TOC + R3, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, +/*R13,*/ // system thread id + R14, + R15, + R16, // R16_thread + R17, + R18, + R19, + R20, + R21, + R22, +/*R23, + R24, + R25, + R26, + R27, + R28,*/ +/*R29,*/ // TODO: let allocator handle TOC!! +/*R30,*/ + R31 +); + +// Complement-required-in-pipeline operands for narrow oops. +// See 64-bit declaration. +reg_class bits32_reg_ro_complement ( + R23, + R24, + R25, + R26, + R27, + R28 +); + +reg_class rscratch1_bits32_reg(R11); +reg_class rscratch2_bits32_reg(R12); +reg_class rarg1_bits32_reg(R3); +reg_class rarg2_bits32_reg(R4); +reg_class rarg3_bits32_reg(R5); +reg_class rarg4_bits32_reg(R6); + +// ---------------------------- +// 64 Bit Register Classes +// ---------------------------- +// 64-bit build means 64-bit pointers means hi/lo pairs + +reg_class rscratch1_bits64_reg(R11_H, R11); +reg_class rscratch2_bits64_reg(R12_H, R12); +reg_class rarg1_bits64_reg(R3_H, R3); +reg_class rarg2_bits64_reg(R4_H, R4); +reg_class rarg3_bits64_reg(R5_H, R5); +reg_class rarg4_bits64_reg(R6_H, R6); +// Thread register, 'written' by tlsLoadP, see there. +reg_class thread_bits64_reg(R16_H, R16); + +reg_class r19_bits64_reg(R19_H, R19); + +// 64 bit registers that can be read and written i.e. these registers +// can be dest (or src) of normal instructions. +reg_class bits64_reg_rw( +/*R0_H, R0*/ // R0 +/*R1_H, R1*/ // SP + R2_H, R2, // TOC + R3_H, R3, + R4_H, R4, + R5_H, R5, + R6_H, R6, + R7_H, R7, + R8_H, R8, + R9_H, R9, + R10_H, R10, + R11_H, R11, + R12_H, R12, +/*R13_H, R13*/ // system thread id + R14_H, R14, + R15_H, R15, +/*R16_H, R16*/ // R16_thread + R17_H, R17, + R18_H, R18, + R19_H, R19, + R20_H, R20, + R21_H, R21, + R22_H, R22, + R23_H, R23, + R24_H, R24, + R25_H, R25, + R26_H, R26, + R27_H, R27, + R28_H, R28, +/*R29_H, R29*/ +/*R30_H, R30*/ + R31_H, R31 +); + +// 64 bit registers used excluding r2, r11 and r12 +// Used to hold the TOC to avoid collisions with expanded LeafCall which uses +// r2, r11 and r12 internally. +reg_class bits64_reg_leaf_call( +/*R0_H, R0*/ // R0 +/*R1_H, R1*/ // SP +/*R2_H, R2*/ // TOC + R3_H, R3, + R4_H, R4, + R5_H, R5, + R6_H, R6, + R7_H, R7, + R8_H, R8, + R9_H, R9, + R10_H, R10, +/*R11_H, R11*/ +/*R12_H, R12*/ +/*R13_H, R13*/ // system thread id + R14_H, R14, + R15_H, R15, +/*R16_H, R16*/ // R16_thread + R17_H, R17, + R18_H, R18, + R19_H, R19, + R20_H, R20, + R21_H, R21, + R22_H, R22, + R23_H, R23, + R24_H, R24, + R25_H, R25, + R26_H, R26, + R27_H, R27, + R28_H, R28, +/*R29_H, R29*/ +/*R30_H, R30*/ + R31_H, R31 +); + +// 64 bit registers used excluding r19. +// Used to hold the TOC to avoid collisions with expanded DynamicCall +// which uses r19 as inline cache internally. +reg_class bits64_reg_dynamic_call( +/*R0_H, R0*/ // R0 +/*R1_H, R1*/ // SP + R2_H, R2, // TOC + R3_H, R3, + R4_H, R4, + R5_H, R5, + R6_H, R6, + R7_H, R7, + R8_H, R8, + R9_H, R9, + R10_H, R10, + R11_H, R11, + R12_H, R12, +/*R13_H, R13*/ // system thread id + R14_H, R14, + R15_H, R15, +/*R16_H, R16*/ // R16_thread + R17_H, R17, + R18_H, R18, +/*R19_H, R19*/ + R20_H, R20, + R21_H, R21, + R22_H, R22, + R23_H, R23, + R24_H, R24, + R25_H, R25, + R26_H, R26, + R27_H, R27, + R28_H, R28, +/*R29_H, R29*/ +/*R30_H, R30*/ + R31_H, R31 +); + +// 64 bit registers that can only be read i.e. these registers can +// only be src of all instructions. +reg_class bits64_reg_ro( +/*R0_H, R0*/ // R0 + R1_H, R1, + R2_H, R2, // TOC + R3_H, R3, + R4_H, R4, + R5_H, R5, + R6_H, R6, + R7_H, R7, + R8_H, R8, + R9_H, R9, + R10_H, R10, + R11_H, R11, + R12_H, R12, +/*R13_H, R13*/ // system thread id + R14_H, R14, + R15_H, R15, + R16_H, R16, // R16_thread + R17_H, R17, + R18_H, R18, + R19_H, R19, + R20_H, R20, + R21_H, R21, + R22_H, R22, + R23_H, R23, + R24_H, R24, + R25_H, R25, + R26_H, R26, + R27_H, R27, + R28_H, R28, +/*R29_H, R29*/ // TODO: let allocator handle TOC!! +/*R30_H, R30,*/ + R31_H, R31 +); + +// Complement-required-in-pipeline operands. +reg_class bits64_reg_ro_not_complement ( +/*R0_H, R0*/ // R0 + R1_H, R1, // SP + R2_H, R2, // TOC + R3_H, R3, + R4_H, R4, + R5_H, R5, + R6_H, R6, + R7_H, R7, + R8_H, R8, + R9_H, R9, + R10_H, R10, + R11_H, R11, + R12_H, R12, +/*R13_H, R13*/ // system thread id + R14_H, R14, + R15_H, R15, + R16_H, R16, // R16_thread + R17_H, R17, + R18_H, R18, + R19_H, R19, + R20_H, R20, + R21_H, R21, + R22_H, R22, +/*R23_H, R23, + R24_H, R24, + R25_H, R25, + R26_H, R26, + R27_H, R27, + R28_H, R28,*/ +/*R29_H, R29*/ // TODO: let allocator handle TOC!! +/*R30_H, R30,*/ + R31_H, R31 +); + +// Complement-required-in-pipeline operands. +// This register mask is used for the trap instructions that implement +// the null checks on AIX. The trap instruction first computes the +// complement of the value it shall trap on. Because of this, the +// instruction can not be scheduled in the same cycle as an other +// instruction reading the normal value of the same register. So we +// force the value to check into 'bits64_reg_ro_not_complement' +// and then copy it to 'bits64_reg_ro_complement' for the trap. +reg_class bits64_reg_ro_complement ( + R23_H, R23, + R24_H, R24, + R25_H, R25, + R26_H, R26, + R27_H, R27, + R28_H, R28 +); + + +// ---------------------------- +// Special Class for Condition Code Flags Register + +reg_class int_flags( +/*CCR0*/ // scratch +/*CCR1*/ // scratch +/*CCR2*/ // nv! +/*CCR3*/ // nv! +/*CCR4*/ // nv! + CCR5, + CCR6, + CCR7 +); + +reg_class int_flags_CR0(CCR0); +reg_class int_flags_CR1(CCR1); +reg_class int_flags_CR6(CCR6); +reg_class ctr_reg(SR_CTR); + +// ---------------------------- +// Float Register Classes +// ---------------------------- + +reg_class flt_reg( +/*F0*/ // scratch + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, // nv! + F15, // nv! + F16, // nv! + F17, // nv! + F18, // nv! + F19, // nv! + F20, // nv! + F21, // nv! + F22, // nv! + F23, // nv! + F24, // nv! + F25, // nv! + F26, // nv! + F27, // nv! + F28, // nv! + F29, // nv! + F30, // nv! + F31 // nv! +); + +// Double precision float registers have virtual `high halves' that +// are needed by the allocator. +reg_class dbl_reg( +/*F0, F0_H*/ // scratch + F1, F1_H, + F2, F2_H, + F3, F3_H, + F4, F4_H, + F5, F5_H, + F6, F6_H, + F7, F7_H, + F8, F8_H, + F9, F9_H, + F10, F10_H, + F11, F11_H, + F12, F12_H, + F13, F13_H, + F14, F14_H, // nv! + F15, F15_H, // nv! + F16, F16_H, // nv! + F17, F17_H, // nv! + F18, F18_H, // nv! + F19, F19_H, // nv! + F20, F20_H, // nv! + F21, F21_H, // nv! + F22, F22_H, // nv! + F23, F23_H, // nv! + F24, F24_H, // nv! + F25, F25_H, // nv! + F26, F26_H, // nv! + F27, F27_H, // nv! + F28, F28_H, // nv! + F29, F29_H, // nv! + F30, F30_H, // nv! + F31, F31_H // nv! +); + + %} + +//----------DEFINITION BLOCK--------------------------------------------------- +// Define name --> value mappings to inform the ADLC of an integer valued name +// Current support includes integer values in the range [0, 0x7FFFFFFF] +// Format: +// int_def ( , ); +// Generated Code in ad_.hpp +// #define () +// // value == +// Generated code in ad_.cpp adlc_verification() +// assert( == , "Expect () to equal "); +// +definitions %{ + // The default cost (of an ALU instruction). + int_def DEFAULT_COST_LOW ( 30, 30); + int_def DEFAULT_COST ( 100, 100); + int_def HUGE_COST (1000000, 1000000); + + // Memory refs + int_def MEMORY_REF_COST_LOW ( 200, DEFAULT_COST * 2); + int_def MEMORY_REF_COST ( 300, DEFAULT_COST * 3); + + // Branches are even more expensive. + int_def BRANCH_COST ( 900, DEFAULT_COST * 9); + int_def CALL_COST ( 1300, DEFAULT_COST * 13); +%} + + +//----------SOURCE BLOCK------------------------------------------------------- +// This is a block of C++ code which provides values, functions, and +// definitions necessary in the rest of the architecture description. +source_hpp %{ + // Header information of the source block. + // Method declarations/definitions which are used outside + // the ad-scope can conveniently be defined here. + // + // To keep related declarations/definitions/uses close together, + // we switch between source %{ }% and source_hpp %{ }% freely as needed. + + // Returns true if Node n is followed by a MemBar node that + // will do an acquire. If so, this node must not do the acquire + // operation. + bool followed_by_acquire(const Node *n); +%} + +source %{ + +// Optimize load-acquire. +// +// Check if acquire is unnecessary due to following operation that does +// acquire anyways. +// Walk the pattern: +// +// n: Load.acq +// | +// MemBarAcquire +// | | +// Proj(ctrl) Proj(mem) +// | | +// MemBarRelease/Volatile +// +bool followed_by_acquire(const Node *load) { + assert(load->is_Load(), "So far implemented only for loads."); + + // Find MemBarAcquire. + const Node *mba = NULL; + for (DUIterator_Fast imax, i = load->fast_outs(imax); i < imax; i++) { + const Node *out = load->fast_out(i); + if (out->Opcode() == Op_MemBarAcquire) { + if (out->in(0) == load) continue; // Skip control edge, membar should be found via precedence edge. + mba = out; + break; + } + } + if (!mba) return false; + + // Find following MemBar node. + // + // The following node must be reachable by control AND memory + // edge to assure no other operations are in between the two nodes. + // + // So first get the Proj node, mem_proj, to use it to iterate forward. + Node *mem_proj = NULL; + for (DUIterator_Fast imax, i = mba->fast_outs(imax); i < imax; i++) { + mem_proj = mba->fast_out(i); // Throw out-of-bounds if proj not found + assert(mem_proj->is_Proj(), "only projections here"); + ProjNode *proj = mem_proj->as_Proj(); + if (proj->_con == TypeFunc::Memory && + !Compile::current()->node_arena()->contains(mem_proj)) // Unmatched old-space only + break; + } + assert(mem_proj->as_Proj()->_con == TypeFunc::Memory, "Graph broken"); + + // Search MemBar behind Proj. If there are other memory operations + // behind the Proj we lost. + for (DUIterator_Fast jmax, j = mem_proj->fast_outs(jmax); j < jmax; j++) { + Node *x = mem_proj->fast_out(j); + // Proj might have an edge to a store or load node which precedes the membar. + if (x->is_Mem()) return false; + + // On PPC64 release and volatile are implemented by an instruction + // that also has acquire semantics. I.e. there is no need for an + // acquire before these. + int xop = x->Opcode(); + if (xop == Op_MemBarRelease || xop == Op_MemBarVolatile) { + // Make sure we're not missing Call/Phi/MergeMem by checking + // control edges. The control edge must directly lead back + // to the MemBarAcquire + Node *ctrl_proj = x->in(0); + if (ctrl_proj->is_Proj() && ctrl_proj->in(0) == mba) { + return true; + } + } + } + + return false; +} + +#define __ _masm. + +// Tertiary op of a LoadP or StoreP encoding. +#define REGP_OP true + +static ConditionRegister reg_to_ConditionRegister_object(int register_encoding); +static FloatRegister reg_to_FloatRegister_object(int register_encoding); +static Register reg_to_register_object(int register_encoding); + +// **************************************************************************** + +// REQUIRED FUNCTIONALITY + +// !!!!! Special hack to get all type of calls to specify the byte offset +// from the start of the call to the point where the return address +// will point. + +// PPC port: Removed use of lazy constant construct. + +int MachCallStaticJavaNode::ret_addr_offset() { + // It's only a single branch-and-link instruction. + return 4; +} + +int MachCallDynamicJavaNode::ret_addr_offset() { + // Offset is 4 with postalloc expanded calls (bl is one instruction). We use + // postalloc expanded calls if we use inline caches and do not update method data. + if (UseInlineCaches) + return 4; + + int vtable_index = this->_vtable_index; + if (vtable_index < 0) { + // Must be invalid_vtable_index, not nonvirtual_vtable_index. + assert(vtable_index == methodOopDesc::invalid_vtable_index, "correct sentinel value"); + return 12; + } else { + assert(!UseInlineCaches, "expect vtable calls only if not using ICs"); + return 24; + } +} + +int MachCallRuntimeNode::ret_addr_offset() { +#if defined(ABI_ELFv2) + return 28; +#else + return 40; +#endif +} + +//============================================================================= + +// condition code conversions + +static int cc_to_boint(int cc) { + return Assembler::bcondCRbiIs0 | (cc & 8); +} + +static int cc_to_inverse_boint(int cc) { + return Assembler::bcondCRbiIs0 | (8-(cc & 8)); +} + +static int cc_to_biint(int cc, int flags_reg) { + return (flags_reg << 2) | (cc & 3); +} + +//============================================================================= + +// Compute padding required for nodes which need alignment. The padding +// is the number of bytes (not instructions) which will be inserted before +// the instruction. The padding must match the size of a NOP instruction. + +int string_indexOf_imm1_charNode::compute_padding(int current_offset) const { + return (3*4-current_offset)&31; +} + +int string_indexOf_imm1Node::compute_padding(int current_offset) const { + return (2*4-current_offset)&31; +} + +int string_indexOf_immNode::compute_padding(int current_offset) const { + return (3*4-current_offset)&31; +} + +int string_indexOfNode::compute_padding(int current_offset) const { + return (1*4-current_offset)&31; +} + +int string_compareNode::compute_padding(int current_offset) const { + return (4*4-current_offset)&31; +} + +int string_equals_immNode::compute_padding(int current_offset) const { + if (opnd_array(3)->constant() < 16) return 0; // Don't insert nops for short version (loop completely unrolled). + return (2*4-current_offset)&31; +} + +int string_equalsNode::compute_padding(int current_offset) const { + return (7*4-current_offset)&31; +} + +int inlineCallClearArrayNode::compute_padding(int current_offset) const { + return (2*4-current_offset)&31; +} + +//============================================================================= + +// Indicate if the safepoint node needs the polling page as an input. +bool SafePointNode::needs_polling_address_input() { + // The address is loaded from thread by a seperate node. + return true; +} + +//============================================================================= + +// Emit an interrupt that is caught by the debugger (for debugging compiler). +void emit_break(CodeBuffer &cbuf) { + MacroAssembler _masm(&cbuf); + __ illtrap(); +} + +#ifndef PRODUCT +void MachBreakpointNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + st->print("BREAKPOINT"); +} +#endif + +void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + emit_break(cbuf); +} + +uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const { + return MachNode::size(ra_); +} + +//============================================================================= + +void emit_nop(CodeBuffer &cbuf) { + MacroAssembler _masm(&cbuf); + __ nop(); +} + +static inline void emit_long(CodeBuffer &cbuf, int value) { + *((int*)(cbuf.insts_end())) = value; + cbuf.set_insts_end(cbuf.insts_end() + BytesPerInstWord); +} + +//============================================================================= + +// Emit a java_to_interp stub. +// +// The stub is initially created with a -1 target and a NULL oop, the stub +// is fixed up when the corresponding call is converted from calling compiled +// code to calling interpreted code. + +// Offset from start of compiled java to interpreter stub to the load +// constant that loads the inline cache (IC) (8 if we have to load +// toc). +const int CompiledStaticCall::comp_to_int_load_offset = 8; + +const uint java_to_interp_stub_size = 12 * BytesPerInstWord; + +void emit_java_to_interp_stub(MacroAssembler &_masm, const int insts_relocation_offset) { + // Start the stub. + address stub = __ start_a_stub(java_to_interp_stub_size); + if (stub == NULL) { + Compile::current()->env()->record_out_of_memory_failure(); + return; + } + + // For java_to_interp stubs we use R11_scratch1 as scratch register + // and in call trampoline stubs we use R12_scratch2. This way we + // can distinguish them (see is_NativeCallTrampolineStub_at()). + Register reg_scratch = R11_scratch1; + + // Create a static stub relocation which relates this stub + // with the call instruction at insts_call_instruction_offset in the + // instructions code-section. + __ relocate(static_stub_Relocation::spec(__ code()->insts()->start() + insts_relocation_offset)); + const int stub_start_offset = __ offset(); + + // Now, create the stub's code: + // - load the TOC + // - load the inline cache oop from the constant pool + // - load the call target from the constant pool + // - call + __ calculate_address_from_global_toc(reg_scratch, __ method_toc()); + AddressLiteral oop = __ allocate_oop_address(NULL); + __ load_const_from_method_toc(reg_to_register_object(Matcher::inline_cache_reg_encode()), oop, reg_scratch); + + if (ReoptimizeCallSequences) { + __ b64_patchable((address)-1, relocInfo::none); + } else { + AddressLiteral a((address)-1); + __ load_const_from_method_toc(reg_scratch, a, reg_scratch); + __ mtctr(reg_scratch); + __ bctr(); + } + + // FIXME: Assert that the stub can be identified and patched. + + // Java_to_interp_stub_size should be good. + assert((uint)(__ offset() - stub_start_offset) <= java_to_interp_stub_size, "should be good size"); + assert(!is_NativeCallTrampolineStub_at(__ addr_at(stub_start_offset)), + "must not confuse java_to_interp with trampoline stubs"); + + // End the stub. + __ end_a_stub(); +} + +// Size of java_to_interp stub, this doesn't need to be accurate but it must +// be larger or equal to the real size of the stub. +// Used for optimization in Compile::Shorten_branches. +uint size_java_to_interp() { + return java_to_interp_stub_size; +} + +// Number of relocation entries needed by compiled java to interpreter +// call stub. +// Used for optimization in Compile::Shorten_branches. +uint reloc_java_to_interp() { + return 5; +} + +//============================================================================= + +// Emit a trampoline stub for a call to a target which is too far away. +// +// code sequences: +// +// call-site: +// branch-and-link to or +// +// Related trampoline stub for this call-site in the stub section: +// load the call target from the constant pool +// branch via CTR (LR/link still points to the call-site above) + +const uint trampoline_stub_size = 6 * BytesPerInstWord; + +void emit_trampoline_stub(MacroAssembler &_masm, int destination_toc_offset, int insts_call_instruction_offset) { + // Start the stub. + address stub = __ start_a_stub(Compile::MAX_stubs_size/2); + if (stub == NULL) { + Compile::current()->env()->record_out_of_memory_failure(); + return; + } + + // For java_to_interp stubs we use R11_scratch1 as scratch register + // and in call trampoline stubs we use R12_scratch2. This way we + // can distinguish them (see is_NativeCallTrampolineStub_at()). + Register reg_scratch = R12_scratch2; + + // Create a trampoline stub relocation which relates this trampoline stub + // with the call instruction at insts_call_instruction_offset in the + // instructions code-section. + __ relocate(trampoline_stub_Relocation::spec(__ code()->insts()->start() + insts_call_instruction_offset)); + const int stub_start_offset = __ offset(); + + // Now, create the trampoline stub's code: + // - load the TOC + // - load the call target from the constant pool + // - call + __ calculate_address_from_global_toc(reg_scratch, __ method_toc()); + __ ld_largeoffset_unchecked(reg_scratch, destination_toc_offset, reg_scratch, false); + __ mtctr(reg_scratch); + __ bctr(); + + const address stub_start_addr = __ addr_at(stub_start_offset); + + // FIXME: Assert that the trampoline stub can be identified and patched. + + // Assert that the encoded destination_toc_offset can be identified and that it is correct. + assert(destination_toc_offset == NativeCallTrampolineStub_at(stub_start_addr)->destination_toc_offset(), + "encoded offset into the constant pool must match"); + // Trampoline_stub_size should be good. + assert((uint)(__ offset() - stub_start_offset) <= trampoline_stub_size, "should be good size"); + assert(is_NativeCallTrampolineStub_at(stub_start_addr), "doesn't look like a trampoline"); + + // End the stub. + __ end_a_stub(); +} + +// Size of trampoline stub, this doesn't need to be accurate but it must +// be larger or equal to the real size of the stub. +// Used for optimization in Compile::Shorten_branches. +uint size_call_trampoline() { + return trampoline_stub_size; +} + +// Number of relocation entries needed by trampoline stub. +// Used for optimization in Compile::Shorten_branches. +uint reloc_call_trampoline() { + return 5; +} + +//============================================================================= + +// Emit an inline branch-and-link call and a related trampoline stub. +// +// code sequences: +// +// call-site: +// branch-and-link to or +// +// Related trampoline stub for this call-site in the stub section: +// load the call target from the constant pool +// branch via CTR (LR/link still points to the call-site above) +// + +typedef struct { + int insts_call_instruction_offset; + int ret_addr_offset; +} EmitCallOffsets; + +// Emit a branch-and-link instruction that branches to a trampoline. +// - Remember the offset of the branch-and-link instruction. +// - Add a relocation at the branch-and-link instruction. +// - Emit a branch-and-link. +// - Remember the return pc offset. +EmitCallOffsets emit_call_with_trampoline_stub(MacroAssembler &_masm, address entry_point, relocInfo::relocType rtype) { + EmitCallOffsets offsets = { -1, -1 }; + const int start_offset = __ offset(); + offsets.insts_call_instruction_offset = __ offset(); + + // No entry point given, use the current pc. + if (entry_point == NULL) entry_point = __ pc(); + + if (!Compile::current()->in_scratch_emit_size()) { + // Put the entry point as a constant into the constant pool. + const address entry_point_toc_addr = __ address_constant(entry_point, RelocationHolder::none); + const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr); + + // Emit the trampoline stub which will be related to the branch-and-link below. + emit_trampoline_stub(_masm, entry_point_toc_offset, offsets.insts_call_instruction_offset); + __ relocate(rtype); + } + + // Note: At this point we do not have the address of the trampoline + // stub, and the entry point might be too far away for bl, so __ pc() + // serves as dummy and the bl will be patched later. + __ bl((address) __ pc()); + + offsets.ret_addr_offset = __ offset() - start_offset; + + return offsets; +} + +//============================================================================= + +// Factory for creating loadConL* nodes for large/small constant pool. + +static inline jlong replicate_immF(float con) { + // Replicate float con 2 times and pack into vector. + int val = *((int*)&con); + jlong lval = val; + lval = (lval << 32) | (lval & 0xFFFFFFFFl); + return lval; +} + +typedef struct { + loadConL_hiNode *_large_hi; + loadConL_loNode *_large_lo; + loadConLNode *_small; + MachNode *_last; +} loadConLNodesTuple; + +loadConLNodesTuple loadConLNodesTuple_create(Compile *C, PhaseRegAlloc *ra_, Node *toc, immLOper *immSrc, + bool isoop, OptoReg::Name reg_second, OptoReg::Name reg_first) { + loadConLNodesTuple nodes; + MachOper *immIsOop = new (C) immIOper(isoop ? 1 : 0); + + const bool large_constant_pool = true; // TODO: PPC port C->cfg()->_consts_size > 4000; + if (large_constant_pool) { + // Create new nodes. + loadConL_hiNode *m1 = new (C) loadConL_hiNode(); + loadConL_loNode *m2 = new (C) loadConL_loNode(); + + // inputs for new nodes + m1->add_req(NULL, toc); + m2->add_req(NULL, m1); + + // operands for new nodes + m1->_opnds[0] = new (C) iRegLdstOper(); // dst + m1->_opnds[1] = immSrc; // src + m1->_opnds[2] = new (C) iRegPdstOper(); // toc + m1->_opnds[3] = immIsOop; // isoop + m2->_opnds[0] = new (C) iRegLdstOper(); // dst + m2->_opnds[1] = immSrc; // src + m2->_opnds[2] = new (C) iRegLdstOper(); // base + m2->_opnds[3] = immIsOop; // isoop + + // Initialize ins_attrib TOC fields. + m1->_const_toc_offset = -1; + m2->_const_toc_offset_hi_node = m1; + + // Initialize ins_attrib instruction offset. + m1->_cbuf_insts_offset = -1; + + // register allocation for new nodes + ra_->set_pair(m1->_idx, reg_second, reg_first); + ra_->set_pair(m2->_idx, reg_second, reg_first); + + // Create result. + nodes._large_hi = m1; + nodes._large_lo = m2; + nodes._small = NULL; + nodes._last = nodes._large_lo; + assert(m2->bottom_type()->isa_long(), "must be long"); + } else { + loadConLNode *m2 = new (C) loadConLNode(); + + // inputs for new nodes + m2->add_req(NULL, toc); + + // operands for new nodes + m2->_opnds[0] = new (C) iRegLdstOper(); // dst + m2->_opnds[1] = immSrc; // src + m2->_opnds[2] = new (C) iRegPdstOper(); // toc + m2->_opnds[3] = immIsOop; // isoop + + // Initialize ins_attrib instruction offset. + m2->_cbuf_insts_offset = -1; + + // register allocation for new nodes + ra_->set_pair(m2->_idx, reg_second, reg_first); + + // Create result. + nodes._large_hi = NULL; + nodes._large_lo = NULL; + nodes._small = m2; + nodes._last = nodes._small; + assert(m2->bottom_type()->isa_long(), "must be long"); + } + + return nodes; +} + +//============================================================================= + +// Factory for creating loadConP* nodes for large/small constant pool. + +typedef struct { + loadConP_hiNode *_large_hi; + loadConP_loNode *_large_lo; + loadConPNode *_small; + MachNode *_last; +} loadConPNodesTuple; + +loadConPNodesTuple loadConPNodesTuple_create(Compile *C, PhaseRegAlloc *ra_, Node *toc, immPOper *immSrc, + OptoReg::Name reg_second, OptoReg::Name reg_first) { + loadConPNodesTuple nodes; + const bool isoop = immSrc->constant_is_oop(); + MachOper *immIsOop = new (C) immIOper(isoop ? 1 : 0); + + const bool large_constant_pool = true; // TODO: PPC port C->cfg()->_consts_size > 4000; + if (large_constant_pool) { + // Create new nodes. + loadConP_hiNode *m1 = new (C) loadConP_hiNode(); + loadConP_loNode *m2 = new (C) loadConP_loNode(); + + // inputs for new nodes + m1->add_req(NULL, toc); + m2->add_req(NULL, m1); + + // operands for new nodes + m1->_opnds[0] = new (C) iRegPdstOper(); // dst + m1->_opnds[1] = immSrc; // src + m1->_opnds[2] = new (C) iRegPdstOper(); // toc + m1->_opnds[3] = immIsOop; // isoop + m2->_opnds[0] = new (C) iRegPdstOper(); // dst + m2->_opnds[1] = immSrc; // src + m2->_opnds[2] = new (C) iRegLdstOper(); // base + m2->_opnds[3] = immIsOop; // isoop + + // Initialize ins_attrib TOC fields. + m1->_const_toc_offset = -1; + m2->_const_toc_offset_hi_node = m1; + + // register allocation for new nodes + ra_->set_pair(m1->_idx, reg_second, reg_first); + ra_->set_pair(m2->_idx, reg_second, reg_first); + + // Create result. + nodes._large_hi = m1; + nodes._large_lo = m2; + nodes._small = NULL; + nodes._last = nodes._large_lo; + assert(m2->bottom_type()->isa_ptr(), "must be ptr"); + } else { + loadConPNode *m2 = new (C) loadConPNode(); + + // inputs for new nodes + m2->add_req(NULL, toc); + + // operands for new nodes + m2->_opnds[0] = new (C) iRegPdstOper(); // dst + m2->_opnds[1] = immSrc; // src + m2->_opnds[2] = new (C) iRegPdstOper(); // toc + m2->_opnds[3] = immIsOop; // isoop + + // register allocation for new nodes + ra_->set_pair(m2->_idx, reg_second, reg_first); + + nodes._large_hi = NULL; + nodes._large_lo = NULL; + nodes._small = m2; + nodes._last = nodes._small; + assert(m2->bottom_type()->isa_ptr(), "must be ptr"); + } + + return nodes; +} + +//============================================================================= + +const RegMask& MachConstantBaseNode::_out_RegMask = BITS64_REG_RW_mask(); +int Compile::ConstantTable::calculate_table_base_offset() const { + return 0; // absolute addressing, no offset +} + +bool MachConstantBaseNode::requires_late_expand() const { return true; } +void MachConstantBaseNode::lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + Compile *C = ra_->C; + + iRegPdstOper *op_dst = new (C) iRegPdstOper(); + MachNode *m1 = new (C) loadToc_hiNode(); + MachNode *m2 = new (C) loadToc_loNode(); + + m1->add_req(NULL); + m2->add_req(NULL, m1); + m1->_opnds[0] = op_dst; + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_dst; + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m1); + nodes->push(m2); +} + +void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { + // Is late expanded. + ShouldNotReachHere(); +} + +uint MachConstantBaseNode::size(PhaseRegAlloc* ra_) const { + return 0; +} + +#ifndef PRODUCT +void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const { + st->print("-- \t// MachConstantBaseNode (empty encoding)"); +} +#endif + +//============================================================================= + +#ifndef PRODUCT +void MachPrologNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + Compile* C = ra_->C; + const long framesize = C->frame_slots() << LogBytesPerInt; + + st->print("PROLOG\n\t"); + if (C->need_stack_bang(framesize)) { + st->print("stack_overflow_check\n\t"); + } + + if (!false /* TODO: PPC port C->is_frameless_method()*/) { + st->print("save return pc\n\t"); + st->print("push frame %d\n\t", -framesize); + } +} +#endif + +// Macro used instead of the common __ to emulate the pipes of PPC. +// Instead of e.g. __ ld(...) one hase to write ___(ld) ld(...) This enables the +// micro scheduler to cope with "hand written" assembler like in the prolog. Though +// still no scheduling of this code is possible, the micro scheduler is aware of the +// code and can update its internal data. The following mechanism is used to achieve this: +// The micro scheduler calls size() of each compound node during scheduling. size() does a +// dummy emit and only during this dummy emit C->hb_scheduling() is not NULL. +#if 0 // TODO: PPC port +#define ___(op) if (UsePower6SchedulerPPC64 && C->hb_scheduling()) \ + C->hb_scheduling()->_pdScheduling->PdEmulatePipe(ppc64Opcode_##op); \ + _masm. +#define ___stop if (UsePower6SchedulerPPC64 && C->hb_scheduling()) \ + C->hb_scheduling()->_pdScheduling->PdEmulatePipe(archOpcode_none) +#define ___advance if (UsePower6SchedulerPPC64 && C->hb_scheduling()) \ + C->hb_scheduling()->_pdScheduling->advance_offset +#else +#define ___(op) if (UsePower6SchedulerPPC64) \ + Unimplemented(); \ + _masm. +#define ___stop if (UsePower6SchedulerPPC64) \ + Unimplemented() +#define ___advance if (UsePower6SchedulerPPC64) \ + Unimplemented() +#endif + +void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + Compile* C = ra_->C; + MacroAssembler _masm(&cbuf); + + const long framesize = ((long)C->frame_slots()) << LogBytesPerInt; + assert(framesize%(2*wordSize) == 0, "must preserve 2*wordSize alignment"); + + const bool method_is_frameless = false /* TODO: PPC port C->is_frameless_method()*/; + + const Register return_pc = R20; // Must match return_addr() in frame section. + const Register callers_sp = R21; + const Register push_frame_temp = R22; + const Register toc_temp = R23; + assert_different_registers(R11, return_pc, callers_sp, push_frame_temp, toc_temp); + + if (method_is_frameless) { + // Add nop at beginning of all frameless methods to prevent any + // oop instructions from getting overwritten by make_not_entrant + // (patching attempt would fail). + ___(nop) nop(); + } else { + // Get return pc. + ___(mflr) mflr(return_pc); + } + + // Calls to C2R adapters often do not accept exceptional returns. + // We require that their callers must bang for them. But be + // careful, because some VM calls (such as call site linkage) can + // use several kilobytes of stack. But the stack safety zone should + // account for that. See bugs 4446381, 4468289, 4497237. + if (C->need_stack_bang(framesize) && UseStackBanging) { + // Unfortunately we cannot use the function provided in + // assembler.cpp as we have to emulate the pipes. So I had to + // insert the code of generate_stack_overflow_check(), see + // assembler.cpp for some illuminative comments. + const int page_size = os::vm_page_size(); + int bang_end = StackShadowPages*page_size; + + // This is how far the previous frame's stack banging extended. + const int bang_end_safe = bang_end; + + if (framesize > page_size) { + bang_end += framesize; + } + + int bang_offset = bang_end_safe; + + while (bang_offset <= bang_end) { + // Need at least one stack bang at end of shadow zone. + + // Again I had to copy code, this time from assembler_ppc64.cpp, + // bang_stack_with_offset - see there for comments. + + // Stack grows down, caller passes positive offset. + assert(bang_offset > 0, "must bang with positive offset"); + + long stdoffset = -bang_offset; + + if (Assembler::is_simm(stdoffset, 16)) { + // Signed 16 bit offset, a simple std is ok. + if (UseLoadInstructionsForStackBangingPPC64) { + ___(ld) ld(R0, (int)(signed short)stdoffset, R1_SP); + } else { + ___(std) std(R0, (int)(signed short)stdoffset, R1_SP); + } + } else if (Assembler::is_simm(stdoffset, 31)) { + // Use largeoffset calculations for addis & ld/std. + const int hi = MacroAssembler::largeoffset_si16_si16_hi(stdoffset); + const int lo = MacroAssembler::largeoffset_si16_si16_lo(stdoffset); + + Register tmp = R11; + ___(addis) addis(tmp, R1_SP, hi); + if (UseLoadInstructionsForStackBangingPPC64) { + ___(ld) ld(R0, lo, tmp); + } else { + ___(std) std(R0, lo, tmp); + } + } else { + ShouldNotReachHere(); + } + + bang_offset += page_size; + } + // R11 trashed + } // C->need_stack_bang(framesize) && UseStackBanging + + unsigned int bytes = (unsigned int)framesize; + long offset = Assembler::align_addr(bytes, frame::alignment_in_bytes); + ciMethod *currMethod = C -> method(); + + // Optimized version for most common case. + if (UsePower6SchedulerPPC64 && + !method_is_frameless && Assembler::is_simm((int)(-(_abi(lr) + offset)), 16) && + !(false /* ConstantsALot TODO: PPC port*/)) { + ___(or) mr(callers_sp, R1_SP); + ___(addi) addi(R1_SP, R1_SP, -offset); + ___stop; // Emulator won't recognize dependency. + ___(std) std(return_pc, _abi(lr) + offset, R1_SP); + ___(std) std(callers_sp, 0, R1_SP); + return; + } + + if (!method_is_frameless) { + // Get callers sp. + ___(or) mr(callers_sp, R1_SP); + + // Push method's frame, modifies SP. + assert(Assembler::is_uimm(framesize, 32U), "wrong type"); + // The ABI is already accounted for in 'framesize' via the + // 'out_preserve' area. + Register tmp = push_frame_temp; + // Had to insert code of push_frame((unsigned int)framesize, push_frame_temp). + if (Assembler::is_simm(-offset, 16)) { + ___(stdu) stdu(R1_SP, -offset, R1_SP); + } else { + long x = -offset; + // Had to insert load_const(tmp, -offset). + ___(addis) lis( tmp, (int)((signed short)(((x >> 32) & 0xffff0000) >> 16))); + ___(ori) ori( tmp, tmp, ((x >> 32) & 0x0000ffff)); + ___(rldicr) sldi(tmp, tmp, 32); + ___(oris) oris(tmp, tmp, (x & 0xffff0000) >> 16); + ___(ori) ori( tmp, tmp, (x & 0x0000ffff)); + + ___(stdux) stdux(R1_SP, R1_SP, tmp); + } + } +#if 0 // TODO: PPC port + // For testing large constant pools, emit a lot of constants to constant pool. + // "Randomize" const_size. + if (ConstantsALot) { + const int num_consts = const_size(); + for (int i = 0; i < num_consts; i++) { + __ long_constant(0xB0B5B00BBABE); + } + } +#endif + if (!method_is_frameless) { + // Save return pc. + ___(std) std(return_pc, _abi(lr), callers_sp); + } +} +#undef ___ +#undef ___stop + +uint MachPrologNode::size(PhaseRegAlloc *ra_) const { + // Variable size. determine dynamically. + return MachNode::size(ra_); +} + +int MachPrologNode::reloc() const { + // Return number of relocatable values contained in this instruction. + return 1; // 1 reloc entry for load_const(toc). +} + +//============================================================================= + +#ifndef PRODUCT +void MachEpilogNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + Compile* C = ra_->C; + + st->print("EPILOG\n\t"); + st->print("restore return pc\n\t"); + st->print("pop frame\n\t"); + + if (do_polling() && C->is_method_compilation()) { + st->print("touch polling page\n\t"); + } +} +#endif + +void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + Compile* C = ra_->C; + MacroAssembler _masm(&cbuf); + + const long framesize = ((long)C->frame_slots()) << LogBytesPerInt; + assert(framesize >= 0, "negative frame-size?"); + + const bool method_needs_polling = do_polling() && C->is_method_compilation(); + const bool method_is_frameless = false /* TODO: PPC port C->is_frameless_method()*/; + const Register return_pc = R11; + const Register polling_page = R12; + + if (!method_is_frameless) { + // Restore return pc relative to callers' sp. + __ ld(return_pc, ((int)framesize) + _abi(lr), R1_SP); + } + + if (method_needs_polling) { + if (LoadPollAddressFromThread) { + // TODO: PPC port __ ld(polling_page, in_bytes(JavaThread::poll_address_offset()), R16_thread); + Unimplemented(); + } else { + __ load_const_optimized(polling_page, (long)(address) os::get_polling_page()); // TODO: PPC port: get_standard_polling_page() + } + } + + if (!method_is_frameless) { + // Move return pc to LR. + __ mtlr(return_pc); + // Pop frame (fixed frame-size). + __ addi(R1_SP, R1_SP, (int)framesize); + } + + if (method_needs_polling) { + // We need to mark the code position where the load from the safepoint + // polling page was emitted as relocInfo::poll_return_type here. + __ relocate(relocInfo::poll_return_type); + __ load_from_polling_page(polling_page); + } +} + +uint MachEpilogNode::size(PhaseRegAlloc *ra_) const { + // Variable size. Determine dynamically. + return MachNode::size(ra_); +} + +int MachEpilogNode::reloc() const { + // Return number of relocatable values contained in this instruction. + return 1; // 1 for load_from_polling_page. +} + +const Pipeline * MachEpilogNode::pipeline() const { + return MachNode::pipeline_class(); +} + +// This method seems to be obsolete. It is declared in machnode.hpp +// and defined in all *.ad files, but it is never called. Should we +// get rid of it? +int MachEpilogNode::safepoint_offset() const { + assert(do_polling(), "no return for this epilog node"); + return 0; +} + +//============================================================================= + +#if 0 // TODO: PPC port +// SIGTRAP-based null checks (on AIX). +#ifdef AIX +void MachNullCheckNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + // Emits entries in the null-pointer exception handler table ... + assert(ImplicitNullChecks, "precondition"); + assert(MachNullCheckNode::uses_checked_address_as_required_input(), "precondition"); + + if (!memory_op_does_trap_hint()) { + // ... and a null-check trap if the memory op is not guaranteed to be a store. + MacroAssembler _masm(&cbuf); + // Trap-instruction-based null checks. + int reg = ra_->get_reg_first(in(MachNullCheckNode::checked_address_in_index())); + _masm.trap_null_check(as_Register(Matcher::_regEncode[reg])); + } +} + +uint MachNullCheckNode::size(PhaseRegAlloc *ra_) const { + if (!memory_op_does_trap_hint()) { + // We emit a trap instruction, 4 bytes. + return 4; + } else { + // The store will handle the implicit null check, nothing here. + return 0; + } +} +#endif + +void MachLoadPollAddrLateNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { + MacroAssembler _masm(&cbuf); + if (LoadPollAddressFromThread) { + _masm.ld(R11, in_bytes(JavaThread::poll_address_offset()), R16_thread); + } else { + _masm.nop(); + } +} + +uint MachLoadPollAddrLateNode::size(PhaseRegAlloc* ra_) const { + if (LoadPollAddressFromThread) { + return 4; + } else { + return 4; + } +} + +#ifndef PRODUCT +void MachLoadPollAddrLateNode::format(PhaseRegAlloc* ra_, outputStream* st) const { + st->print_cr(" LD R11, PollAddressOffset, R16_thread \t// LoadPollAddressFromThread"); +} +#endif + +const RegMask &MachLoadPollAddrLateNode::out_RegMask() const { + return RSCRATCH1_BITS64_REG_mask(); +} +#endif // PPC port + +// ============================================================================= + +// Figure out which register class each belongs in: rc_int, rc_float or +// rc_stack. +enum RC { rc_bad, rc_int, rc_float, rc_stack }; + +static enum RC rc_class(OptoReg::Name reg) { + // Return the register class for the given register. The given register + // reg is a _num value, which is an index into the MachRegisterNumbers + // enumeration in adGlobals_ppc64.hpp. + + if (reg == OptoReg::Bad) return rc_bad; + + // We have 64 integer register halves, starting at index 0. + if (reg < 64) return rc_int; + + // We have 64 floating-point register halves, starting at index 64. + if (reg < 64+64) return rc_float; + + // Between float regs & stack are the flags regs. + assert(OptoReg::is_stack(reg), "blow up if spilling flags"); + + return rc_stack; +} + +static int ld_st_helper(CodeBuffer *cbuf, const char *op_str, uint opcode, int reg, int offset, + bool do_print, Compile* C, outputStream *st) { + + assert(opcode == Assembler::LD_OPCODE || + opcode == Assembler::STD_OPCODE || + opcode == Assembler::LWZ_OPCODE || + opcode == Assembler::STW_OPCODE || + opcode == Assembler::LFD_OPCODE || + opcode == Assembler::STFD_OPCODE || + opcode == Assembler::LFS_OPCODE || + opcode == Assembler::STFS_OPCODE, + "opcode not supported"); + + if (cbuf) { + int d = + (Assembler::LD_OPCODE == opcode || Assembler::STD_OPCODE == opcode) ? + Assembler::ds(offset+0 /* TODO: PPC port C->frame_slots_sp_bias_in_bytes()*/) + : Assembler::d1(offset+0 /* TODO: PPC port C->frame_slots_sp_bias_in_bytes()*/); // Makes no difference in opt build. + emit_long(*cbuf, opcode | Assembler::rt(Matcher::_regEncode[reg]) | d | Assembler::ra(R1_SP)); + } +#ifndef PRODUCT + else if (do_print) { + st->print("%-7s %s, [R1_SP + #%d+%d] \t// spill copy", + op_str, + Matcher::regName[reg], + offset, 0 /* TODO: PPC port C->frame_slots_sp_bias_in_bytes()*/); + } +#endif + return 4; // size +} + +uint MachSpillCopyNode::implementation(CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream *st) const { + Compile* C = ra_->C; + + // Get registers to move. + OptoReg::Name src_hi = ra_->get_reg_second(in(1)); + OptoReg::Name src_lo = ra_->get_reg_first(in(1)); + OptoReg::Name dst_hi = ra_->get_reg_second(this); + OptoReg::Name dst_lo = ra_->get_reg_first(this); + + enum RC src_hi_rc = rc_class(src_hi); + enum RC src_lo_rc = rc_class(src_lo); + enum RC dst_hi_rc = rc_class(dst_hi); + enum RC dst_lo_rc = rc_class(dst_lo); + + assert(src_lo != OptoReg::Bad && dst_lo != OptoReg::Bad, "must move at least 1 register"); + if (src_hi != OptoReg::Bad) + assert((src_lo&1)==0 && src_lo+1==src_hi && + (dst_lo&1)==0 && dst_lo+1==dst_hi, + "expected aligned-adjacent pairs"); + // Generate spill code! + int size = 0; + + if (src_lo == dst_lo && src_hi == dst_hi) + return size; // Self copy, no move. + + // -------------------------------------- + // Memory->Memory Spill. Use R0 to hold the value. + if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { + int src_offset = ra_->reg2offset(src_lo); + int dst_offset = ra_->reg2offset(dst_lo); + if (src_hi != OptoReg::Bad) { + assert(src_hi_rc==rc_stack && dst_hi_rc==rc_stack, + "expected same type of move for high parts"); + size += ld_st_helper(cbuf, "LD ", Assembler::LD_OPCODE, R0_num, src_offset, !do_size, C, st); + if (!cbuf && !do_size) st->print("\n\t"); + size += ld_st_helper(cbuf, "STD ", Assembler::STD_OPCODE, R0_num, dst_offset, !do_size, C, st); + } else { + size += ld_st_helper(cbuf, "LWZ ", Assembler::LWZ_OPCODE, R0_num, src_offset, !do_size, C, st); + if (!cbuf && !do_size) st->print("\n\t"); + size += ld_st_helper(cbuf, "STW ", Assembler::STW_OPCODE, R0_num, dst_offset, !do_size, C, st); + } + return size; + } + + // -------------------------------------- + // Check for float->int copy; requires a trip through memory. + if (src_lo_rc == rc_float && dst_lo_rc == rc_int) { + Unimplemented(); + } + + // -------------------------------------- + // Check for integer reg-reg copy. + if (src_lo_rc == rc_int && dst_lo_rc == rc_int) { + Register Rsrc = as_Register(Matcher::_regEncode[src_lo]); + Register Rdst = as_Register(Matcher::_regEncode[dst_lo]); + size = (Rsrc != Rdst) ? 4 : 0; + + if (cbuf) { + MacroAssembler _masm(cbuf); + if (size) { + __ mr(Rdst, Rsrc); + } + } +#ifndef PRODUCT + else if (!do_size) { + if (size) { + st->print("%-7s %s, %s \t// spill copy", "MR", Matcher::regName[dst_lo], Matcher::regName[src_lo]); + } else { + st->print("%-7s %s, %s \t// spill copy", "MR-NOP", Matcher::regName[dst_lo], Matcher::regName[src_lo]); + } + } +#endif + return size; + } + + // Check for integer store. + if (src_lo_rc == rc_int && dst_lo_rc == rc_stack) { + int dst_offset = ra_->reg2offset(dst_lo); + if (src_hi != OptoReg::Bad) { + assert(src_hi_rc==rc_int && dst_hi_rc==rc_stack, + "expected same type of move for high parts"); + size += ld_st_helper(cbuf, "STD ", Assembler::STD_OPCODE, src_lo, dst_offset, !do_size, C, st); + } else { + size += ld_st_helper(cbuf, "STW ", Assembler::STW_OPCODE, src_lo, dst_offset, !do_size, C, st); + } + return size; + } + + // Check for integer load. + if (dst_lo_rc == rc_int && src_lo_rc == rc_stack) { + int src_offset = ra_->reg2offset(src_lo); + if (src_hi != OptoReg::Bad) { + assert(dst_hi_rc==rc_int && src_hi_rc==rc_stack, + "expected same type of move for high parts"); + size += ld_st_helper(cbuf, "LD ", Assembler::LD_OPCODE, dst_lo, src_offset, !do_size, C, st); + } else { + size += ld_st_helper(cbuf, "LWZ ", Assembler::LWZ_OPCODE, dst_lo, src_offset, !do_size, C, st); + } + return size; + } + + // Check for float reg-reg copy. + if (src_lo_rc == rc_float && dst_lo_rc == rc_float) { + if (cbuf) { + MacroAssembler _masm(cbuf); + FloatRegister Rsrc = as_FloatRegister(Matcher::_regEncode[src_lo]); + FloatRegister Rdst = as_FloatRegister(Matcher::_regEncode[dst_lo]); + __ fmr(Rdst, Rsrc); + } +#ifndef PRODUCT + else if (!do_size) { + st->print("%-7s %s, %s \t// spill copy", "FMR", Matcher::regName[dst_lo], Matcher::regName[src_lo]); + } +#endif + return 4; + } + + // Check for float store. + if (src_lo_rc == rc_float && dst_lo_rc == rc_stack) { + int dst_offset = ra_->reg2offset(dst_lo); + if (src_hi != OptoReg::Bad) { + assert(src_hi_rc==rc_float && dst_hi_rc==rc_stack, + "expected same type of move for high parts"); + size += ld_st_helper(cbuf, "STFD", Assembler::STFD_OPCODE, src_lo, dst_offset, !do_size, C, st); + } else { + size += ld_st_helper(cbuf, "STFS", Assembler::STFS_OPCODE, src_lo, dst_offset, !do_size, C, st); + } + return size; + } + + // Check for float load. + if (dst_lo_rc == rc_float && src_lo_rc == rc_stack) { + int src_offset = ra_->reg2offset(src_lo); + if (src_hi != OptoReg::Bad) { + assert(dst_hi_rc==rc_float && src_hi_rc==rc_stack, + "expected same type of move for high parts"); + size += ld_st_helper(cbuf, "LFD ", Assembler::LFD_OPCODE, dst_lo, src_offset, !do_size, C, st); + } else { + size += ld_st_helper(cbuf, "LFS ", Assembler::LFS_OPCODE, dst_lo, src_offset, !do_size, C, st); + } + return size; + } + + // -------------------------------------------------------------------- + // Check for hi bits still needing moving. Only happens for misaligned + // arguments to native calls. + if (src_hi == dst_hi) + return size; // Self copy; no move. + + assert(src_hi_rc != rc_bad && dst_hi_rc != rc_bad, "src_hi & dst_hi cannot be Bad"); + ShouldNotReachHere(); // Unimplemented + return 0; +} + +#ifndef PRODUCT +void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + if (!ra_) + st->print("N%d = SpillCopy(N%d)", _idx, in(1)->_idx); + else + implementation(NULL, ra_, false, st); +} +#endif + +void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + implementation(&cbuf, ra_, false, NULL); +} + +uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const { + return implementation(NULL, ra_, true, NULL); +} + +#if 0 // TODO: PPC port +ArchOpcode MachSpillCopyNode_archOpcode(MachSpillCopyNode *n, PhaseRegAlloc *ra_) { +#ifndef PRODUCT + if (ra_->node_regs_max_index() == 0) return archOpcode_undefined; +#endif + assert(ra_->node_regs_max_index() != 0, ""); + + // Get registers to move. + OptoReg::Name src_hi = ra_->get_reg_second(n->in(1)); + OptoReg::Name src_lo = ra_->get_reg_first(n->in(1)); + OptoReg::Name dst_hi = ra_->get_reg_second(n); + OptoReg::Name dst_lo = ra_->get_reg_first(n); + + enum RC src_lo_rc = rc_class(src_lo); + enum RC dst_lo_rc = rc_class(dst_lo); + + if (src_lo == dst_lo && src_hi == dst_hi) + return ppc64Opcode_none; // Self copy, no move. + + // -------------------------------------- + // Memory->Memory Spill. Use R0 to hold the value. + if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { + return ppc64Opcode_compound; + } + + // -------------------------------------- + // Check for float->int copy; requires a trip through memory. + if (src_lo_rc == rc_float && dst_lo_rc == rc_int) { + Unimplemented(); + } + + // -------------------------------------- + // Check for integer reg-reg copy. + if (src_lo_rc == rc_int && dst_lo_rc == rc_int) { + Register Rsrc = as_Register(Matcher::_regEncode[src_lo]); + Register Rdst = as_Register(Matcher::_regEncode[dst_lo]); + if (Rsrc == Rdst) { + return ppc64Opcode_none; + } else { + return ppc64Opcode_or; + } + } + + // Check for integer store. + if (src_lo_rc == rc_int && dst_lo_rc == rc_stack) { + if (src_hi != OptoReg::Bad) { + return ppc64Opcode_std; + } else { + return ppc64Opcode_stw; + } + } + + // Check for integer load. + if (dst_lo_rc == rc_int && src_lo_rc == rc_stack) { + if (src_hi != OptoReg::Bad) { + return ppc64Opcode_ld; + } else { + return ppc64Opcode_lwz; + } + } + + // Check for float reg-reg copy. + if (src_lo_rc == rc_float && dst_lo_rc == rc_float) { + return ppc64Opcode_fmr; + } + + // Check for float store. + if (src_lo_rc == rc_float && dst_lo_rc == rc_stack) { + if (src_hi != OptoReg::Bad) { + return ppc64Opcode_stfd; + } else { + return ppc64Opcode_stfs; + } + } + + // Check for float load. + if (dst_lo_rc == rc_float && src_lo_rc == rc_stack) { + if (src_hi != OptoReg::Bad) { + return ppc64Opcode_lfd; + } else { + return ppc64Opcode_lfs; + } + } + + // -------------------------------------------------------------------- + // Check for hi bits still needing moving. Only happens for misaligned + // arguments to native calls. + if (src_hi == dst_hi) + return ppc64Opcode_none; // Self copy; no move. + + ShouldNotReachHere(); + return ppc64Opcode_undefined; +} +#endif // PPC port + +#ifndef PRODUCT +void MachNopNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + st->print("NOP \t// %d nops to pad for loops.", _count); +} +#endif + +void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *) const { + MacroAssembler _masm(&cbuf); + // _count contains the number of nops needed for padding. + for (int i = 0; i < _count; i++) { + __ nop(); + } +} + +uint MachNopNode::size(PhaseRegAlloc *ra_) const { + return _count * 4; +} + +#ifndef PRODUCT +void BoxLockNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_reg_first(this); + st->print("ADDI %s, SP, %d \t// box node", Matcher::regName[reg], offset); +} +#endif + +void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + MacroAssembler _masm(&cbuf); + + int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); + int reg = ra_->get_encode(this); + + if (Assembler::is_simm(offset, 16)) { + __ addi(as_Register(reg), R1, offset); + } else { + ShouldNotReachHere(); + } +} + +uint BoxLockNode::size(PhaseRegAlloc *ra_) const { + // BoxLockNode is not a MachNode, so we can't just call MachNode::size(ra_). + return 4; +} + +#ifndef PRODUCT +void MachUEPNode::format(PhaseRegAlloc *ra_, outputStream *st) const { + st->print_cr("---- MachUEPNode ----"); + st->print_cr("..."); +} +#endif + +void MachUEPNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { + // This is the unverified entry point. + MacroAssembler _masm(&cbuf); + + // Inline_cache contains a klass. + Register ic_klass = as_Register(Matcher::inline_cache_reg_encode()); + Register receiver_klass = R0; // tmp + + assert_different_registers(ic_klass, receiver_klass, R11_scratch1, R3_ARG1); + assert(R11_scratch1 == R11, "need prologue scratch register"); + + // Check for NULL argument if we don't have implicit null checks. + if (!ImplicitNullChecks || !os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + __ trap_null_check(R3_ARG1); + } else { + Label valid; + __ cmpdi(CCR0, R3_ARG1, 0); + __ bne_predict_taken(CCR0, valid); + // We have a null argument, branch to ic_miss_stub. + __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(), + relocInfo::runtime_call_type); + __ bind(valid); + } + } + // Assume argument is not NULL, load klass from receiver. + __ load_klass(receiver_klass, R3_ARG1); + + if (TrapBasedICMissChecks) { + __ trap_ic_miss_check(receiver_klass, ic_klass); + } else { + Label valid; + __ cmpd(CCR0, receiver_klass, ic_klass); + __ beq_predict_taken(CCR0, valid); + // We have an unexpected klass, branch to ic_miss_stub. + __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(), + relocInfo::runtime_call_type); + __ bind(valid); + } + + // Argument is valid and klass is as expected, continue. +} + +#if 0 // TODO: PPC port +// Optimize UEP code on z (save a load_const() call in main path). +int MachUEPNode::ep_offset() { + return 0; +} +#endif + +uint MachUEPNode::size(PhaseRegAlloc *ra_) const { + // Variable size. Determine dynamically. + return MachNode::size(ra_); +} + +//============================================================================= + +uint size_exception_handler() { + // The exception_handler is a b64_patchable. + return MacroAssembler::b64_patchable_size; +} + +uint size_deopt_handler() { + // The deopt_handler is a bl64_patchable. + return MacroAssembler::bl64_patchable_size; +} + +int emit_exception_handler(CodeBuffer &cbuf) { + MacroAssembler _masm(&cbuf); + + address base = __ start_a_stub(size_exception_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + + int offset = __ offset(); + __ b64_patchable((address)OptoRuntime::exception_blob()->content_begin(), + relocInfo::runtime_call_type); + assert(__ offset() - offset == (int)size_exception_handler(), "must be fixed size"); + __ end_a_stub(); + + return offset; +} + +// The deopt_handler is like the exception handler, but it calls to +// the deoptimization blob instead of jumping to the exception blob. +int emit_deopt_handler(CodeBuffer& cbuf) { + MacroAssembler _masm(&cbuf); + + address base = __ start_a_stub(size_deopt_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + + int offset = __ offset(); + __ bl64_patchable((address)SharedRuntime::deopt_blob()->unpack(), + relocInfo::runtime_call_type); + assert(__ offset() - offset == (int) size_deopt_handler(), "must be fixed size"); + __ end_a_stub(); + + return offset; +} + +//============================================================================= + +// Given a register encoding, produce an Integer Register object. +static Register reg_to_register_object(int register_encoding) { + // Verify ppc encoding by example. + assert(R12->encoding() == R12_enc, "wrong coding"); + return as_Register(register_encoding); +} + +// Given a register encoding, produce a Float Register object. +static FloatRegister reg_to_FloatRegister_object(int register_encoding) { + // Verify ppc encoding by example. + assert(F5->encoding() == F5_enc, "wrong coding"); + return as_FloatRegister(register_encoding); +} +// Given a register encoding, produce a ConditionRegister object. +static ConditionRegister reg_to_ConditionRegister_object(int register_encoding) { + // Verify ppc encoding by example. + assert(CCR5->encoding() == CCR5_enc, "wrong coding"); + return as_ConditionRegister(register_encoding); +} + +const bool Matcher::match_rule_supported(int opcode) { + if (!has_match_rule(opcode)) + return false; + + switch (opcode) { + case Op_SqrtD: + return VM_Version::has_fsqrt(); + case Op_CountLeadingZerosI: + case Op_CountLeadingZerosL: + case Op_CountTrailingZerosI: + case Op_CountTrailingZerosL: + if (!UseCountLeadingZerosInstructionsPPC64) + return false; + break; + + case Op_PopCountI: + case Op_PopCountL: + return (UsePopCountInstruction && VM_Version::has_popcntw()); + + case Op_StrComp: + return SpecialStringCompareTo; + case Op_StrEquals: + return SpecialStringEquals; + case Op_StrIndexOf: + return SpecialStringIndexOf; + } + + return true; // Per default match rules are supported. +} + +int Matcher::regnum_to_fpu_offset(int regnum) { + // No user for this method? + Unimplemented(); + return 999; +} + +const bool Matcher::convL2FSupported(void) { + // fcfids can do the conversion (>= Power7). + // fcfid + frsp showed rounding problem when result should be 0x3f800001. + return VM_Version::has_fcfids(); // False means that conversion is done by runtime call. +} + +// Map Types to machine register types +const int Matcher::base2reg[Type::lastype] = { + Node::NotAMachineReg,0,0, Op_RegI, Op_RegL, 0, Op_RegN, + Node::NotAMachineReg, Node::NotAMachineReg, /* tuple, array */ + 0, Op_RegL, 0, 0, /* Vectors */ + Op_RegP, Op_RegP, Op_RegP, Op_RegP, Op_RegP, Op_RegP, /* the pointers */ + 0, 0/*abio*/, + Op_RegP /* Return address */, 0, /* the memories */ + Op_RegF, Op_RegF, Op_RegF, Op_RegD, Op_RegD, Op_RegD, + 0 /*bottom*/ +}; + +// Vector width in bytes. +const int Matcher::vector_width_in_bytes(BasicType bt) { + assert(MaxVectorSize == 8, ""); + return 8; +} + +// Vector ideal reg. +const int Matcher::vector_ideal_reg(int size) { + assert(MaxVectorSize == 8 && size == 8, ""); + return Op_RegL; +} + +const int Matcher::vector_shift_count_ideal_reg(int size) { + fatal("vector shift is not supported"); + return Node::NotAMachineReg; +} + +// Limits on vector size (number of elements) loaded into vector. +const int Matcher::max_vector_size(const BasicType bt) { + assert(is_java_primitive(bt), "only primitive type vectors"); + return vector_width_in_bytes(bt)/type2aelembytes(bt); +} + +const int Matcher::min_vector_size(const BasicType bt) { + return max_vector_size(bt); // Same as max. +} + +// PPC doesn't support misaligned vectors store/load. +const bool Matcher::misaligned_vectors_ok() { + return false; +} + +// RETURNS: whether this branch offset is short enough that a short +// branch can be used. +// +// If the platform does not provide any short branch variants, then +// this method should return `false' for offset 0. +// +// `Compile::Fill_buffer' will decide on basis of this information +// whether to do the pass `Compile::Shorten_branches' at all. +// +// And `Compile::Shorten_branches' will decide on basis of this +// information whether to replace particular branch sites by short +// ones. +bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) { + // Is the offset within the range of a ppc64 pc relative branch? + bool b; + + const int safety_zone = 3 * BytesPerInstWord; + b = Assembler::is_simm((offset<0 ? offset-safety_zone : offset+safety_zone), + 29 - 16 + 1 + 2); + return b; +} + +const bool Matcher::isSimpleConstant64(jlong value) { + // Probably always true, even if a temp register is required. + return true; +} +/* TODO: PPC port +// Make a new machine dependent decode node (with its operands). +MachTypeNode *Matcher::make_decode_node(Compile *C) { + assert(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0, + "This method is only implemented for unscaled cOops mode so far"); + MachTypeNode *decode = new (C) decodeN_unscaledNode(); + decode->set_opnd_array(0, new (C) iRegPdstOper()); + decode->set_opnd_array(1, new (C) iRegNsrcOper()); + return decode; +} +*/ +// Threshold size for cleararray. +const int Matcher::init_array_short_size = 8 * BytesPerLong; + +// false => size gets scaled to BytesPerLong, ok. +const bool Matcher::init_array_count_is_in_bytes = false; + +// Use conditional move (CMOVL) on Power7. +const int Matcher::long_cmove_cost() { return 0; } // this only makes long cmoves more expensive than int cmoves + +// Suppress CMOVF. Conditional move available (sort of) on PPC64 only from P7 onwards. Not exploited yet. +// fsel doesn't accept a condition register as input, so this would be slightly different. +const int Matcher::float_cmove_cost() { return ConditionalMoveLimit; } + +// Power6 requires late expand (see block.cpp for description of late expand). +const bool Matcher::require_late_expand = true; + +// Should the Matcher clone shifts on addressing modes, expecting them to +// be subsumed into complex addressing expressions or compute them into +// registers? True for Intel but false for most RISCs. +const bool Matcher::clone_shift_expressions = false; + +// Do we need to mask the count passed to shift instructions or does +// the cpu only look at the lower 5/6 bits anyway? +// PowerPC requires masked shift counts. +const bool Matcher::need_masked_shift_count = true; + +// This affects two different things: +// - how Decode nodes are matched +// - how ImplicitNullCheck opportunities are recognized +// If true, the matcher will try to remove all Decodes and match them +// (as operands) into nodes. NullChecks are not prepared to deal with +// Decodes by final_graph_reshaping(). +// If false, final_graph_reshaping() forces the decode behind the Cmp +// for a NullCheck. The matcher matches the Decode node into a register. +// Implicit_null_check optimization moves the Decode along with the +// memory operation back up before the NullCheck. +bool Matcher::narrow_oop_use_complex_address() { + // TODO: PPC port if (MatchDecodeNodes) return true; + return false; +} + +// Is it better to copy float constants, or load them directly from memory? +// Intel can load a float constant from a direct address, requiring no +// extra registers. Most RISCs will have to materialize an address into a +// register first, so they would do better to copy the constant from stack. +const bool Matcher::rematerialize_float_constants = false; + +// If CPU can load and store mis-aligned doubles directly then no fixup is +// needed. Else we split the double into 2 integer pieces and move it +// piece-by-piece. Only happens when passing doubles into C code as the +// Java calling convention forces doubles to be aligned. +const bool Matcher::misaligned_doubles_ok = true; + +void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) { + Unimplemented(); +} + +// Advertise here if the CPU requires explicit rounding operations +// to implement the UseStrictFP mode. +const bool Matcher::strict_fp_requires_explicit_rounding = false; + +// Do floats take an entire double register or just half? +// +// A float occupies a ppc64 double register. For the allocator, a +// ppc64 double register appears as a pair of float registers. +bool Matcher::float_in_double() { return true; } + +// Do ints take an entire long register or just half? +// The relevant question is how the int is callee-saved: +// the whole long is written but de-opt'ing will have to extract +// the relevant 32 bits. +const bool Matcher::int_in_long = true; + +// Constants for c2c and c calling conventions. + +const MachRegisterNumbers iarg_reg[8] = { + R3_num, R4_num, R5_num, R6_num, + R7_num, R8_num, R9_num, R10_num +}; + +const MachRegisterNumbers farg_reg[13] = { + F1_num, F2_num, F3_num, F4_num, + F5_num, F6_num, F7_num, F8_num, + F9_num, F10_num, F11_num, F12_num, + F13_num +}; + +const int num_iarg_registers = sizeof(iarg_reg) / sizeof(iarg_reg[0]); + +const int num_farg_registers = sizeof(farg_reg) / sizeof(farg_reg[0]); + +// Return whether or not this register is ever used as an argument. This +// function is used on startup to build the trampoline stubs in generateOptoStub. +// Registers not mentioned will be killed by the VM call in the trampoline, and +// arguments in those registers not be available to the callee. +bool Matcher::can_be_java_arg(int reg) { + // We return true for all registers contained in iarg_reg[] and + // farg_reg[] and their virtual halves. + // We must include the virtual halves in order to get STDs and LDs + // instead of STWs and LWs in the trampoline stubs. + + if ( reg == R3_num || reg == R3_H_num + || reg == R4_num || reg == R4_H_num + || reg == R5_num || reg == R5_H_num + || reg == R6_num || reg == R6_H_num + || reg == R7_num || reg == R7_H_num + || reg == R8_num || reg == R8_H_num + || reg == R9_num || reg == R9_H_num + || reg == R10_num || reg == R10_H_num) + return true; + + if ( reg == F1_num || reg == F1_H_num + || reg == F2_num || reg == F2_H_num + || reg == F3_num || reg == F3_H_num + || reg == F4_num || reg == F4_H_num + || reg == F5_num || reg == F5_H_num + || reg == F6_num || reg == F6_H_num + || reg == F7_num || reg == F7_H_num + || reg == F8_num || reg == F8_H_num + || reg == F9_num || reg == F9_H_num + || reg == F10_num || reg == F10_H_num + || reg == F11_num || reg == F11_H_num + || reg == F12_num || reg == F12_H_num + || reg == F13_num || reg == F13_H_num) + return true; + + return false; +} + +bool Matcher::is_spillable_arg(int reg) { + return can_be_java_arg(reg); +} + +bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { + return false; +} + +// Register for DIVI projection of divmodI. +RegMask Matcher::divI_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for MODI projection of divmodI. +RegMask Matcher::modI_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for DIVL projection of divmodL. +RegMask Matcher::divL_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +// Register for MODL projection of divmodL. +RegMask Matcher::modL_proj_mask() { + ShouldNotReachHere(); + return RegMask(); +} + +const RegMask Matcher::method_handle_invoke_SP_save_mask() { + return RegMask(); +} + +%} + +//----------ENCODING BLOCK----------------------------------------------------- +// This block specifies the encoding classes used by the compiler to output +// byte streams. Encoding classes are parameterized macros used by +// Machine Instruction Nodes in order to generate the bit encoding of the +// instruction. Operands specify their base encoding interface with the +// interface keyword. There are currently supported four interfaces, +// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an +// operand to generate a function which returns its register number when +// queried. CONST_INTER causes an operand to generate a function which +// returns the value of the constant when queried. MEMORY_INTER causes an +// operand to generate four functions which return the Base Register, the +// Index Register, the Scale Value, and the Offset Value of the operand when +// queried. COND_INTER causes an operand to generate six functions which +// return the encoding code (ie - encoding bits for the instruction) +// associated with each basic boolean condition for a conditional instruction. +// +// Instructions specify two basic values for encoding. Again, a function +// is available to check if the constant displacement is an oop. They use the +// ins_encode keyword to specify their encoding classes (which must be +// a sequence of enc_class names, and their parameters, specified in +// the encoding block), and they use the +// opcode keyword to specify, in order, their primary, secondary, and +// tertiary opcode. Only the opcode sections which a particular instruction +// needs for encoding need to be specified. +encode %{ + enc_class enc_unimplemented %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + __ unimplemented("Unimplemented mach node encoding in AD file.", 13); + %} + + enc_class enc_untested %{ +#ifdef ASSERT + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + __ untested("Untested mach node encoding in AD file."); +#else + // TODO: PPC port $archOpcode(ppc64Opcode_none); +#endif + %} + + // Use release_store for card-marking to ensure that previous + // oop-stores are visible before the card-mark change. + enc_class enc_cms_card_mark(memory mem, iRegLdst releaseFieldAddr) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + // FIXME: Implement this as a cmove and use a fixed condition code + // register which is written on every transition to compiled code, + // e.g. in call-stub and when returning from runtime stubs. + // + // Proposed code sequence for the cmove implementation: + // + // Label skip_release; + // __ beq(CCRfixed, skip_release); + // __ release(); + // __ bind(skip_release); + // __ stb(card mark); + + MacroAssembler _masm(&cbuf); + Label skip_release; + +#if 0 // TODO: PPC port + // Check CMSCollectorCardTableModRefBSExt::_requires_release and do the + // release conditionally. + __ lwz(R0, 0, reg_to_register_object($releaseFieldAddr$$reg)); + __ cmpwi(CCR0, R0, 0); + __ beq_predict_taken(CCR0, skip_release); +#endif + __ li(R0, 0); + __ release(); +#if 0 // TODO: PPC port + __ bind(skip_release); +#endif + + // Do the store. + if ($mem$$index == 0) { + __ stb(R0, $mem$$disp, reg_to_register_object($mem$$base)); + } else { + assert(0 == $mem$$disp, "no displacement possible with indexed load/stores on ppc"); + __ stbx(R0, reg_to_register_object($mem$$base), reg_to_register_object($mem$$index)); + } + %} + + enc_class enc_andi(iRegIdst dst, iRegIsrc src1, uimmI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andi_); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + // FIXME: avoid andi_ ? + __ andi_(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_xori(iRegIdst dst, iRegIsrc src1, uimmI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xori); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); int Isrc2 = $src2$$constant; + __ xori(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_ori(iRegIdst dst, iRegIsrc src1, uimmI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = ($src2$$constant) & 0xFFFF; + __ ori(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_addi(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + __ addi(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_addis(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = ($src2$$constant)>>16; + __ addis(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_addi_neg(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = ($src2$$constant) * (-1) ; + __ addi(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_subfic(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subfic); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + __ subfic(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_mulli(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulli); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + __ mulli(Rdst, Rsrc1, Isrc2); + %} + + enc_class enc_neg(iRegIdst dst, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_neg); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ neg(Rdst, Rsrc1); + %} + + enc_class enc_fcfid(RegD dst, RegD tmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcfid); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rtmp = reg_to_FloatRegister_object($tmp$$reg); + __ fcfid(Rdst, Rtmp); + %} + + enc_class enc_fcfids(RegF dst, RegD tmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcfid); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rtmp = reg_to_FloatRegister_object($tmp$$reg); + __ fcfids(Rdst, Rtmp); + %} + + enc_class enc_extsw(iRegLdst dst, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ extsw(Rdst, Rsrc1); + %} + + enc_class enc_extswII(iRegIdst dst, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ extsw(Rdst, Rsrc1); + %} + + enc_class enc_extswLL(iRegLdst dst, iRegLsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ extsw(Rdst, Rsrc1); + %} + + enc_class enc_extsh(iRegLdst dst, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsh); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ extsh(Rdst, Rsrc1); + %} + + enc_class enc_extsb(iRegIdst dst, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsb); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ extsb(Rdst, Rsrc1); + %} + + enc_class enc_not(iRegIdst dst, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_nor); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + __ nor(Rdst, Rsrc1, Rsrc1); + %} + + enc_class enc_andc(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andc); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ andc(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_and(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_and); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ andr(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_or(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ or_unchecked(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_xor(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xor); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ xorr(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fadds(RegF dst, RegF src1, RegF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fadds); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fadds(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fadd(RegF dst, RegF src1, RegF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fadd); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fadd(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fsubs(RegF dst, RegF src1, RegF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsubs); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fsubs(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fsub(RegF dst, RegF src1, RegF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsub); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fsub(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fdivs(RegF dst, RegF src1, RegR src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fdivs); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fdivs(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_add(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ add(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_subf(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ subf(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_mullw(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mullw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ mullw(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_mulld(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulld); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ mulld(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_mulhd(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulhd); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ mulhd(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fdiv(RegF dst, RegF src1, RegR src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fdiv); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fdiv(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fmuls(regF dst, regF src1, regF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmuls); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fmuls(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fmul(regF dst, regF src1, regF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmul); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fmul(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_fabs(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fabs); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fabs(Rdst, Rsrc1); + %} + + enc_class enc_fnabs(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fnabs); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fnabs(Rdst, Rsrc1); + %} + + enc_class enc_fsqrt(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsqrt); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fsqrt(Rdst, Rsrc1); + %} + + enc_class enc_frsp(regF dst, regD src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_frsp); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ frsp(Rdst, Rsrc1); + %} + + enc_class enc_fsqrts(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsqrts); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fsqrts(Rdst, Rsrc1); + %} + + enc_class enc_fneg(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fneg); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fneg(Rdst, Rsrc1); + %} + + enc_class enc_lwz(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwz); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lwz(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_lwz_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lwz(Rdst, Idisp, Rbase); + __ twi_0(Rdst); // SAPJVM MD 2011-10-06 replaced cmp-br-isync by twi-isync + __ isync(); + %} + + // SAPJVM GL 2013-10-29 Match ConvI2L(LoadI) + enc_class enc_lwa(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwa); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes() */; + } + __ lwa(Rdst, Idisp, Rbase); + %} + + enc_class enc_lwa_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwa); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lwa(Rdst, Idisp, Rbase); + __ twi_0(Rdst); + __ isync(); + %} + + enc_class enc_lhz(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lhz); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lhz(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_lhz_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lhz(Rdst, Idisp, Rbase); + __ twi_0(Rdst); + __ isync(); + %} + + enc_class enc_lha(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lha); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lha(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_lha_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lha(Rdst, Idisp, Rbase); + __ twi_0(Rdst); + __ isync(); + %} + + enc_class enc_lbz(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lbz); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lbz(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_lbz_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lbz(Rdst, Idisp, Rbase); + __ twi_0(Rdst); + __ isync(); + %} + + enc_class enc_ld(iRegLdst dst, memoryAlg4 mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + // Operand 'ds' requires 4-alignment. + assert((Idisp & 0x3) == 0, "unaligned offset"); + __ ld(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_ld_ac(iRegLdst dst, memoryAlg4 mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + // Operand 'ds' requires 4-alignment. + assert((Idisp & 0x3) == 0, "unaligned offset"); + __ ld(Rdst, Idisp, Rbase); + __ twi_0(Rdst); + __ isync(); + %} + + enc_class enc_lfs(RegF dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfs); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lfs(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_lfs_ac(RegF dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + Label next; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lfs(Rdst, Idisp, Rbase); + __ fcmpu(CCR0, Rdst, Rdst); + __ bne(CCR0, next); + __ bind(next); + __ isync(); + %} + + enc_class enc_lfd(RegF dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfd); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lfd(Rdst, Idisp, Rbase); + %} + + // Load acquire. + enc_class enc_lfd_ac(RegF dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + Label next; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ lfd(Rdst, Idisp, Rbase); + __ fcmpu(CCR0, Rdst, Rdst); + __ bne(CCR0, next); + __ bind(next); + __ isync(); + %} + + enc_class enc_stfs(RegF src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stfs); + MacroAssembler _masm(&cbuf); + FloatRegister Rsrc = reg_to_FloatRegister_object($src$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ stfs(Rsrc, Idisp, Rbase); + %} + + enc_class enc_stfd(RegF src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stfd); + MacroAssembler _masm(&cbuf); + FloatRegister Rsrc = reg_to_FloatRegister_object($src$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ stfd(Rsrc, Idisp, Rbase); + %} + + enc_class enc_stw(iRegIsrc src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stw); + MacroAssembler _masm(&cbuf); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ stw(Rsrc, Idisp, Rbase); + %} + + enc_class enc_stb(iRegIsrc src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stb); + MacroAssembler _masm(&cbuf); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ stb(Rsrc, Idisp, Rbase); + %} + + // Store for G1 card marking. + enc_class enc_stb0(memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ li(R0, 0); + __ stb(R0, Idisp, Rbase); + %} + + enc_class enc_sth(iRegIsrc src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sth); + MacroAssembler _masm(&cbuf); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + __ sth(Rsrc, Idisp, Rbase); + %} + + enc_class enc_std(iRegIsrc src, memoryAlg4 mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_std); + MacroAssembler _masm(&cbuf); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rbase = reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + // Operand 'ds' requires 4-alignment. + assert((Idisp & 0x3) == 0, "unaligned offset"); + __ std(Rsrc, Idisp, Rbase); + %} + + enc_class enc_cmpw(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmp); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmpw(Rcrx, Rsrc1, Rsrc2); + %} + + enc_class enc_cmpld(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmpld(Rcrx, Rsrc1, Rsrc2); + %} + + enc_class enc_cmplw(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmplw(Rcrx, Rsrc1, Rsrc2); + %} + + enc_class enc_cmpd(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmp); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmpd(Rcrx, Rsrc1, Rsrc2); + %} + + enc_class enc_cmpdi(flagsReg crx, iRegIsrc src1, immL16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpi); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmpdi(Rcrx, Rsrc1, Isrc2); + %} + + enc_class enc_cmpwi(flagsReg crx, iRegIsrc src1, immL16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpi); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmpwi(Rcrx, Rsrc1, Isrc2); + %} + + enc_class enc_cmplwi(flagsReg crx, iRegIsrc src1, immL16 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpli); + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + __ cmplwi(Rcrx, Rsrc1, Isrc2); + %} + + enc_class enc_btst_reg(iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_and_); + + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ and_(R0, Rsrc1, Rsrc2); + %} + + enc_class enc_slwi(iRegIdst dst, iRegIsrc src1, immI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int isrc = ($src2$$constant) & 0x1f; + __ slwi(Rdst, Rsrc1, isrc); + %} + + enc_class enc_slw(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_slw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ slw(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_sldi(iRegIdst dst, iRegIsrc src1, immI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int isrc = ($src2$$constant) & 0x3f; + __ sldi(Rdst, Rsrc1, isrc); + %} + + enc_class enc_sld(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sld); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ sld(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_clrlsldi(iRegLdst dst, iRegIsrc src, uimmI6 clearleftbits, uimmI6 shiftleftbits) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldic); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + int iclearleftbits = $clearleftbits$$constant; + int ishiftleftbits = $shiftleftbits$$constant; + __ clrlsldi(Rdst, Rsrc, iclearleftbits, ishiftleftbits); + %} + + // 32G aligned narrow oop base. + // Extract bits 32+3 .. 3 and place them right-justified in dst. + enc_class enc_extrdi_encode(iRegLdst dst, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + __ rldicl(Rdst, Rsrc, 64-Universe::narrow_oop_shift(), 32); + %} + + enc_class enc_clrldi(iRegLdst dst, iRegIsrc src, uimmI6 clearleftbits) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + int iclearleftbits = $clearleftbits$$constant; + __ clrldi(Rdst, Rsrc, iclearleftbits); + %} + + enc_class enc_srwi(iRegIdst dst, iRegIsrc src1, immI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int isrc = ($src2$$constant) & 0x1f; + __ srwi(Rdst, Rsrc1, isrc); + %} + + enc_class enc_srw(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ srw(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_srawi(iRegIdst dst, iRegIsrc src1, immI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srawi); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int isrc = ($src2$$constant) & 0x1f; + __ srawi(Rdst, Rsrc1, isrc); + %} + + enc_class enc_sraw(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sraw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ sraw(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_sradi(iRegIdst dst, iRegIsrc src1, immI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sradi); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int isrc = ($src2$$constant) & 0x3f; + __ sradi(Rdst, Rsrc1, isrc); + %} + + enc_class enc_srad(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srad); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ srad(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_srdi(iRegLdst dst, iRegLsrc src1, immI src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int isrc = ($src2$$constant) & 0x3f; + __ srdi(Rdst, Rsrc1, isrc); + %} + + enc_class enc_srd(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srd); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ srd(Rdst, Rsrc1, Rsrc2); + %} + + // Rotate word left. + enc_class enc_rotlwi(iRegIdst dst, iRegIsrc src, immI8 shift) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + int ishift = $shift$$constant; + __ rotlwi(Rdst, Rsrc, ishift); + %} + + // Rotate word right. + enc_class enc_rotrwi(iRegIdst dst, iRegIsrc src, immI8 shift) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + int ishift = $shift$$constant; + __ rotrwi(Rdst, Rsrc, ishift); + %} + + enc_class enc_andI_reg_immInegpow2(iRegIdst dst, iRegIsrc src1, immInegpow2 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int src2 = $src2$$constant; + __ clrrdi(Rdst, Rsrc1, log2_long((jlong) (julong) (juint) -src2)); + %} + + enc_class enc_andL_reg_immLnegpow2(iRegLdst dst, iRegLsrc src1, immLnegpow2 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + long src2 = $src2$$constant; + __ clrrdi(Rdst, Rsrc1, log2_long((jlong)-src2)); + %} + + enc_class enc_and_reg_immpow2minus1(iRegLdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + long src2 = $src2$$constant; + __ clrldi(Rdst, Rsrc1, 64-log2_long((((jlong) src2)+1))); + %} + + enc_class enc_andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + long src2 = $src2$$constant; + __ rlwinm(Rdst, Rsrc1, 0, (31-log2_long((jlong) src2)) & 0x1f, (31-log2_long((jlong) src2)) & 0x1f); + %} + + enc_class enc_convI2B_andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + long src2 = $src2$$constant; + __ rlwinm(Rdst, Rsrc1, (32-log2_long((jlong) src2)) & 0x1f, 31, 31); + %} + + enc_class enc_load_toc1(iRegLdst dst) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + + __ calculate_address_from_global_toc_hi16only(Rdst, __ method_toc()); + %} + + enc_class enc_load_toc2(iRegLdst dst, iRegLdst src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + + __ calculate_address_from_global_toc_lo16only(Rdst, __ method_toc()); + %} + + // Load compressed oop constant. + enc_class enc_load_con_narrow1(iRegNdst dst, immN src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + int Csrc = $src$$constant; + int xc = (Csrc >> 16) & 0xffff; + int xd = (Csrc >> 0) & 0xffff; + int value = (int)(short)(xc + ((xd & 0x8000) != 0 ? 1 : 0)); // Compensate sign extend like in patch_set_narrow_oop. + __ lis(Rdst, value); + %} + + // SLoad compressed oop constant. + enc_class enc_load_con_narrow2(iRegNdst dst, iRegNsrc src1, immN src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Csrc = $src2$$constant; + assert(__ oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int oop_index = __ oop_recorder()->find_index((jobject)$src2$$constant); + RelocationHolder rspec = oop_Relocation::spec(oop_index); + + __ relocate(rspec, 1); + __ addi(Rdst, Rsrc1, (int)(short)(Csrc & 0xffff)); + %} + + enc_class enc_convI2B_regI__cmove(iRegIdst dst, iRegIsrc src, flagsReg flags, immI16 zero, immI16 notzero) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($flags$$reg); + + Label done; + __ cmpwi(Rcrx, Rsrc, 0); + __ li(Rdst, $zero$$constant); + __ beq(Rcrx, done); + __ li(Rdst, $notzero$$constant); + __ bind(done); + %} + + enc_class enc_convP2B_regP__cmove(iRegIdst dst, iRegPsrc src, flagsReg flags, immI16 zero, immI16 notzero) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($flags$$reg); + + Label done; + __ cmpdi(Rcrx, Rsrc, 0); + __ li(Rdst, $zero$$constant); + __ beq(Rcrx, done); + __ li(Rdst, $notzero$$constant); + __ bind(done); + %} + + enc_class enc_lShiftI_andI_immInegpow2_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 src2, immU5 src3) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); // FIXME: assert that rlwinm is equal to addi + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + long src2 = $src2$$constant; + long src3 = $src3$$constant; + long maskbits = src3 + log2_long((jlong) (julong) (juint) -src2); + if (maskbits >= 32) { + __ li(Rdst, 0); // addi + } else { + __ rlwinm(Rdst, Rsrc1, src3 & 0x1f, 0, (31-maskbits) & 0x1f); + } + %} + + enc_class enc_lShiftI_andI_immInegpow2_rShiftI_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 src2, immU5 src3) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); // FIXME: assert that rlwinm is equal to addi + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + long src2 = $src2$$constant; + long src3 = $src3$$constant; + long maskbits = src3 + log2_long((jlong) (julong) (juint) -src2); + if (maskbits >= 32) { + __ li(Rdst, 0); // addi + } else { + __ rlwinm(Rdst, Rsrc1, 0, 0, (31-maskbits) & 0x1f); + } + %} + + enc_class enc_zeroextendw(iRegLdst dst, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + __ clrldi(Rdst, Rsrc, 32); + %} + + enc_class enc_fcmpu(flagsReg crx, regF src1, regF src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcmpu); + MacroAssembler _masm(&cbuf); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + FloatRegister Rsrc2 = reg_to_FloatRegister_object($src2$$reg); + __ fcmpu(Rcrx, Rsrc1, Rsrc2); + %} + + enc_class enc_fctiwz(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fctiwz); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fctiwz(Rdst, Rsrc1); + %} + + enc_class enc_fctidz(regF dst, regF src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fctiwz); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc1 = reg_to_FloatRegister_object($src1$$reg); + __ fctidz(Rdst, Rsrc1); + %} + + enc_class enc_mr_if_needed(iRegIdst dst, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + if (Rsrc != Rdst) { + __ mr(Rdst, Rsrc); + } + %} + + enc_class enc_fmr_if_needed(regF dst, regF src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc = reg_to_FloatRegister_object($src$$reg); + if (Rsrc != Rdst) { + __ fmr(Rdst, Rsrc); + } + %} + + // Unconditional branch. + enc_class enc_b(Label lbl) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_b); + + MacroAssembler _masm(&cbuf); + Label d; // dummy + __ bind(d); + Label* p = ($lbl$$label); + // `p' is `NULL' when this encoding class is used only to + // determine the size of the encoded instruction. + Label& l = (NULL == p)? d : *(p); + __ b(l); + %} + + enc_class enc_bc(flagsReg crx, cmpOp cmp, Label lbl) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_bc); + + MacroAssembler _masm(&cbuf); + Label d; // dummy + __ bind(d); + Label* p = ($lbl$$label); + // `p' is `NULL' when this encoding class is used only to + // determine the size of the encoded instruction. + Label& l = (NULL == p)? d : *(p); + int cc = $cmp$$cmpcode; + int flags_reg = $crx$$reg; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + int bhint = Assembler::bhintNoHint; + + if (UseStaticBranchPredictionForUncommonPathsPPC64) { + if (_prob <= PROB_NEVER) { + bhint = Assembler::bhintIsNotTaken; + } else if (_prob >= PROB_ALWAYS) { + bhint = Assembler::bhintIsTaken; + } + } + + __ bc(Assembler::add_bhint_to_boint(bhint, cc_to_boint(cc)), + cc_to_biint(cc, flags_reg), + l); + %} + + enc_class enc_bc_far(flagsReg crx, cmpOp cmp, Label lbl) %{ + // The scheduler doesn't know about branch shortening, so we set the opcode + // to ppc64Opcode_bc in order to hide this detail from the scheduler. + // TODO: PPC port $archOpcode(ppc64Opcode_bc); + + MacroAssembler _masm(&cbuf); + Label d; // dummy + __ bind(d); + Label* p = ($lbl$$label); + // `p' is `NULL' when this encoding class is used only to + // determine the size of the encoded instruction. + Label& l = (NULL == p)? d : *(p); + int cc = $cmp$$cmpcode; + int flags_reg = $crx$$reg; + int bhint = Assembler::bhintNoHint; + + if (UseStaticBranchPredictionForUncommonPathsPPC64) { + if (_prob <= PROB_NEVER) { + bhint = Assembler::bhintIsNotTaken; + } else if (_prob >= PROB_ALWAYS) { + bhint = Assembler::bhintIsTaken; + } + } + + // Tell the conditional far branch to optimize itself when being relocated. + __ bc_far(Assembler::add_bhint_to_boint(bhint, cc_to_boint(cc)), + cc_to_biint(cc, flags_reg), + l, + MacroAssembler::bc_far_optimize_on_relocate); + %} + + // Branch used with Power6 scheduling (can be shortened without changing the node). + enc_class enc_bc_short_far(flagsReg crx, cmpOp cmp, Label lbl) %{ + // The scheduler doesn't know about branch shortening, so we set the opcode + // to ppc64Opcode_bc in order to hide this detail from the scheduler. + // TODO: PPC port $archOpcode(ppc64Opcode_bc); + + MacroAssembler _masm(&cbuf); + Label d; // dummy + __ bind(d); + Label* p = ($lbl$$label); + // `p' is `NULL' when this encoding class is used only to + // determine the size of the encoded instruction. + Label& l = (NULL == p)? d : *(p); + int cc = $cmp$$cmpcode; + int flags_reg = $crx$$reg; + int bhint = Assembler::bhintNoHint; + + if (UseStaticBranchPredictionForUncommonPathsPPC64) { + if (_prob <= PROB_NEVER) { + bhint = Assembler::bhintIsNotTaken; + } else if (_prob >= PROB_ALWAYS) { + bhint = Assembler::bhintIsTaken; + } + } + +#if 0 // TODO: PPC port + if (_size == 8) { + // Tell the conditional far branch to optimize itself when being relocated. + __ bc_far(Assembler::add_bhint_to_boint(bhint, cc_to_boint(cc)), + cc_to_biint(cc, flags_reg), + l, + MacroAssembler::bc_far_optimize_on_relocate); + } else { + __ bc (Assembler::add_bhint_to_boint(bhint, cc_to_boint(cc)), + cc_to_biint(cc, flags_reg), + l); + } +#endif + Unimplemented(); + %} + + enc_class enc_cntlzw(iRegIdst dst, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cntlzw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + __ cntlzw(Rdst, Rsrc); + %} + + enc_class enc_cntlzd(iRegIdst dst, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cntlzd); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + __ cntlzd(Rdst, Rsrc); + %} + + enc_class enc_li(iRegIdst dst, immI src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + int v = $src$$constant; + __ li(Rdst, (int)((short) (v & 0xFFFF))); + %} + + enc_class enc_lis_32(iRegIdst dst, immI src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + int v = $src$$constant; + // Lis sign extends 16-bit src then shifts it 16 bit to the left. + __ lis(Rdst, (int)((short) ((v & 0xFFFF0000) >> 16))); + %} + + // Late expand emitter for loading a long constant from the method's TOC. + enc_class lateExpand_load_long_constant(iRegLdst dst, immL src, iRegPdst toc) %{ + // Create new nodes. + loadConLNodesTuple loadConLNodes = + loadConLNodesTuple_create(C, ra_, n_toc, op_src, false, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + // Push new nodes. + if (loadConLNodes._large_hi) nodes->push(loadConLNodes._large_hi); + if (loadConLNodes._last) nodes->push(loadConLNodes._last); + + // some asserts + assert(nodes->length() >= 1, "must have created at least 1 node"); + assert(loadConLNodes._last->bottom_type()->isa_long(), "must be long"); + %} + + // Late expand emitter for loading a replicatef float constant from the method's TOC. + enc_class lateExpand_load_replF_constant(iRegLdst dst, immF src, iRegPdst toc) %{ + // Create new nodes. + + // Make an operand with the bit pattern to load as float. + immLOper *op_repl = new (C) immLOper((jlong)replicate_immF(op_src->constantF())); + + loadConLNodesTuple loadConLNodes = + loadConLNodesTuple_create(C, ra_, n_toc, op_repl, false, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + // Push new nodes. + if (loadConLNodes._large_hi) nodes->push(loadConLNodes._large_hi); + if (loadConLNodes._last) nodes->push(loadConLNodes._last); + + assert(nodes->length() >= 1, "must have created at least 1 node"); + assert(loadConLNodes._last->bottom_type()->isa_long(), "must be long"); + %} + + // Load compressed oop constant. + enc_class lateExpand_load_conN(iRegNdst dst, immN src) %{ + MachNode *m1 = new (C) loadConN1Node(); + MachNode *m2 = new (C) loadConN2Node(); + MachNode *m3 = new (C) clearMs32bNode(); + m1->add_req(NULL); + m2->add_req(NULL, m1); + m3->add_req(NULL, m2); + m1->_opnds[0] = op_dst; + m1->_opnds[1] = op_src; + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_dst; + m2->_opnds[2] = op_src; + m3->_opnds[0] = op_dst; + m3->_opnds[1] = op_dst; + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(m3->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m1); + nodes->push(m2); + nodes->push(m3); + %} + + // Late expand emitter for loading a ptr constant from the method's TOC. + enc_class lateExpand_load_ptr_constant(iRegPdst dst, immP src, iRegPdst toc) %{ + // Create new nodes. + loadConPNodesTuple loadConPNodes = + loadConPNodesTuple_create(C, ra_, n_toc, op_src, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + // Push new nodes. + if (loadConPNodes._large_hi) nodes->push(loadConPNodes._large_hi); + if (loadConPNodes._last) nodes->push(loadConPNodes._last); + + // some asserts + assert(nodes->length() >= 1, "must have created at least 1 node"); + assert(loadConPNodes._last->bottom_type()->isa_ptr(), "must be ptr"); + %} + + enc_class lateExpand_load_float_constant(regF dst, immF src, iRegPdst toc) %{ + bool large_constant_pool = true; // TODO: PPC port C->cfg()->_consts_size > 4000; + + MachNode *m2; + if (large_constant_pool) { + m2 = new (C) loadConFCompNode(); + } else { + m2 = new (C) loadConFNode(); + } + // inputs for new nodes + m2->add_req(NULL, n_toc); + + // operands for new nodes + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_src; + m2->_opnds[2] = new (C) iRegPdstOper(); // constanttablebase + + // register allocation for new nodes + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m2); + %} + + enc_class lateExpand_load_double_constant(regD dst, immD src, iRegPdst toc) %{ + bool large_constant_pool = true; // TODO: PPC port C->cfg()->_consts_size > 4000; + + MachNode *m2; + if (large_constant_pool) { + m2 = new (C) loadConDCompNode(); + } else { + m2 = new (C) loadConDNode(); + } + // inputs for new nodes + m2->add_req(NULL, n_toc); + + // operands for new nodes + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_src; + m2->_opnds[2] = new (C) iRegPdstOper(); // constanttablebase + + // register allocation for new nodes + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m2); + %} + + enc_class enc_load_long_constL_hi(iRegLdst dst, iRegLdst toc, immL src, immI isoop) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rtoc = reg_to_register_object($toc$$reg); + assert($isoop$$constant == 1 || $isoop$$constant == 0, "must be 1 or 0"); + + if (!ra_->C->in_scratch_emit_size()) { + address const_toc_addr; + if ($isoop$$constant == 1) { + // Create an oop constant and a corresponding relocation. + AddressLiteral a = __ allocate_oop_address((jobject)$src$$constant); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); + } else { + // Create a non-oop constant, no relocation needed. + const_toc_addr = __ long_constant((jlong)$src$$constant); + } + + // Get the constant's TOC offset. + const int toc_offset = __ offset_to_method_toc(const_toc_addr); + // Store the toc offset of the constant. + ((loadConL_hiNode*)this)->_const_toc_offset = toc_offset; + + // Also keep the current instruction offset in mind. + ((loadConL_hiNode*)this)->_cbuf_insts_offset = __ offset(); + } + + __ addis(Rdst, Rtoc, MacroAssembler::largeoffset_si16_si16_hi(_const_toc_offset)); + %} + + enc_class enc_load_long_constP_hi(iRegLdst dst, iRegLdst toc, immP src, immI isoop) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rtoc = reg_to_register_object($toc$$reg); + assert($isoop$$constant == 1 || $isoop$$constant == 0, "must be 1 or 0"); + + if (!ra_->C->in_scratch_emit_size()) { + address const_toc_addr; + if ($isoop$$constant == 1) { + // Create an oop constant and a corresponding relocation. + AddressLiteral a = __ allocate_oop_address((jobject)$src$$constant); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); + } else { + // Create a non-oop constant, no relocation needed. + const_toc_addr = __ long_constant((jlong)$src$$constant); + } + + // Get the constant's TOC offset. + const int toc_offset = __ offset_to_method_toc(const_toc_addr); + + // Store the toc offset of the constant. + ((loadConP_hiNode*)this)->_const_toc_offset = toc_offset; + } + + __ addis(Rdst, Rtoc, MacroAssembler::largeoffset_si16_si16_hi(_const_toc_offset)); + %} + + enc_class enc_load_long_const_lo(iRegLdst dst, immL src, iRegLsrc base, immI isoop) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rbase = reg_to_register_object($base$$reg); + + int offset = ra_->C->in_scratch_emit_size() ? 0 : this->_const_toc_offset_hi_node->_const_toc_offset; + __ ld(Rdst, MacroAssembler::largeoffset_si16_si16_lo(offset), Rbase); + %} + + enc_class enc_load_long_constL(iRegLdst dst, immL src, iRegLdst toc, immI isoop) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rtoc = reg_to_register_object($toc$$reg); + int toc_offset = 0; + assert($isoop$$constant == 1 || $isoop$$constant == 0, "must be 1 or 0"); + + if (!ra_->C->in_scratch_emit_size()) { + address const_toc_addr; + if ($isoop$$constant == 1) { +#if 0 // TODO: PPC port + // Create an oop constant and a corresponding relocation. + AddressLiteral a = __ allocate_oop_address((jobject)$src$$constant); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); +#endif + Unimplemented(); + } else { + // Create a non-oop constant, no relocation needed. + const_toc_addr = __ long_constant((jlong)$src$$constant); + } + + // Get the constant's TOC offset. + toc_offset = __ offset_to_method_toc(const_toc_addr); + + // Keep the current instruction offset in mind. + ((loadConLNode*)this)->_cbuf_insts_offset = __ offset(); + } + + __ ld(Rdst, toc_offset, Rtoc); + %} + + enc_class enc_load_long_constP(iRegLdst dst, immP src, iRegLdst toc, immI isoop) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rtoc = reg_to_register_object($toc$$reg); + int toc_offset = 0; + assert($isoop$$constant == 1 || $isoop$$constant == 0, "must be 1 or 0"); + + if (!Compile::current()->in_scratch_emit_size()) { + address const_toc_addr; + if ($isoop$$constant == 1) { +#if 0 // TODO: PPC port + // Create an oop constant and a corresponding relocation. + AddressLiteral a = __ allocate_oop_address((jobject)$src$$constant); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); +#endif + Unimplemented(); + } else { + // Create a non-oop constant, no relocation needed. + const_toc_addr = __ long_constant((jlong)$src$$constant); + } + + // Get the constant's TOC offset. + toc_offset = __ offset_to_method_toc(const_toc_addr); + } + + __ ld(Rdst, toc_offset, Rtoc); + %} + + enc_class enc_load_float_const(regF dst, immF src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfs); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + address float_address = __ float_constant($src$$constant); + Register Rtoc; + Rtoc = reg_to_register_object($toc$$reg); + __ lfs(Rdst, __ offset_to_method_toc(float_address), Rtoc); + %} + + // As enc_load_float_const, but for large constant pool. + enc_class enc_load_float_const_comp(regF dst, immF src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + address float_address = __ float_constant($src$$constant); + int offset = __ offset_to_method_toc(float_address); + Register Rtoc; + Rtoc = reg_to_register_object($toc$$reg); + + int hi = (offset + (1<<15))>>16; + int lo = offset - hi * (1<<16); + __ addis(Rtoc, Rtoc, hi); + __ lfs(Rdst, lo, Rtoc); + __ addis(Rtoc, Rtoc, -hi); + %} + + enc_class enc_load_double_const(regD dst, immD src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfd); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + address float_address = __ double_constant($src$$constant); + Register Rtoc; + Rtoc = reg_to_register_object($toc$$reg); + __ lfd(Rdst, __ offset_to_method_toc(float_address), Rtoc); + %} + + // As enc_load_double_const, but for large constant pool. + enc_class enc_load_double_const_comp(regD dst, immD src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + address float_address = __ double_constant($src$$constant); + int offset = __ offset_to_method_toc(float_address); + Register Rtoc; + Rtoc = reg_to_register_object($toc$$reg); + + int hi = (offset + (1<<15))>>16; + int lo = offset - hi * (1<<16); + __ addis(Rtoc, Rtoc, hi); + __ lfd(Rdst, lo, Rtoc); + __ addis(Rtoc, Rtoc, -hi); + %} + + // Write-prefetch instruction; index register only. + // This instruction is safe to execute with an invalid address. + enc_class enc_mem_store_prefetch(indirect mem, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + MacroAssembler _masm(&cbuf); + __ dcbtst(reg_to_register_object($src$$reg), reg_to_register_object($mem$$base)); + %} + + enc_class enc_mem_store_prefetch_no_offset(indirect mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + MacroAssembler _masm(&cbuf); + __ dcbtst(reg_to_register_object($mem$$base)); + %} + + // Version which sets the content to zero. + enc_class enc_mem_store_prefetch_zero(indirect mem, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + MacroAssembler _masm(&cbuf); + __ dcbz(reg_to_register_object($src$$reg), reg_to_register_object($mem$$base)); + %} + + enc_class enc_mem_store_prefetch_zero_no_offset(indirect mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + MacroAssembler _masm(&cbuf); + __ dcbz(reg_to_register_object($mem$$base)); + %} + + // Read-prefetch instruction; index register only. + // This instruction is safe to execute with an invalid address. + enc_class enc_mem_load_prefetch(indirect mem, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbt); + MacroAssembler _masm(&cbuf); + __ dcbt(reg_to_register_object($src$$reg), reg_to_register_object($mem$$base)); + %} + + enc_class enc_mem_load_prefetch_no_offset(indirect mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbt); + MacroAssembler _masm(&cbuf); + __ dcbt(reg_to_register_object($mem$$base)); + %} + + // Implicit range checks. + enc_class enc_rangeCheck_le_iReg_uimm15(cmpOp cmp, iRegIsrc src1, uimmI15 src2, Label lbl) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_twi); + + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + if ($cmp$$cmpcode == 0x1 /* less_equal */) { + __ trap_range_check_le(Rsrc1, Isrc2); + } else { + // Both successors are uncommon traps, probability is 0. + // Node got flipped during fixup flow. + assert($cmp$$cmpcode == 0x9, "must be greater"); + __ trap_range_check_g(Rsrc1, Isrc2); + } + %} + + // Implicit range checks. + enc_class enc_rangeCheck_ge_iReg_iReg(cmpOp cmp, iRegIsrc src1, iRegIsrc src2, Label lbl) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tw); + + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + if ($cmp$$cmpcode == 0x0 /* greater_equal */) { + __ trap_range_check_ge(Rsrc1, Rsrc2); + } else { + // Both successors are uncommon traps, probability is 0. + // Node got flipped during fixup flow. + assert($cmp$$cmpcode == 0x8, "must be less"); + __ trap_range_check_l(Rsrc1, Rsrc2); + } + %} + + // Implicit range checks. + enc_class enc_rangeCheck_ge_iReg_uimm15(cmpOp cmp, iRegIsrc src1, uimmI15 src2, Label lbl) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_twi); + + MacroAssembler _masm(&cbuf); + Register Rsrc1 = reg_to_register_object($src1$$reg); + int Isrc2 = $src2$$constant; + if ($cmp$$cmpcode == 0x0 /* greater_equal */) { + __ trap_range_check_ge(Rsrc1, Isrc2); + } else { + // Both successors are uncommon traps, probability is 0. + // Node got flipped during fixup flow. + assert($cmp$$cmpcode == 0x8, "must be less"); + __ trap_range_check_l(Rsrc1, Isrc2); + } + %} + + // Implicit zero checks. + enc_class enc_zeroCheckP_eq_iReg(cmpOp cmp, iRegPdst value, immP_0 zero, Label lbl) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tdi); + MacroAssembler _masm(&cbuf); + Register Rvalue = reg_to_register_object($value$$reg); + if ($cmp$$cmpcode == 0xA) { + __ trap_null_check(Rvalue); + } else { + // Both successors are uncommon traps, probability is 0. + // Node got flipped during fixup flow. + assert($cmp$$cmpcode == 0x2 , "must be equal(0xA) or notEqual(0x2)"); + __ trap_null_check(Rvalue, Assembler::traptoGreaterThanUnsigned); + } + %} + + enc_class enc_Ret() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_blr); + MacroAssembler _masm(&cbuf); + // LR is restored in MachEpilogNode. Just do the RET here. + __ blr(); + %} + + // Encoding class for traceable jumps (TODO: trace). + enc_class enc_form_jmpl(iRegPdstNoScratch dest) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + __ mtctr(as_Register($dest$$reg)); + __ bctr(); + %} + + enc_class enc_jump_set_exception_pc(iRegPdstNoScratch dest) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdest = reg_to_register_object($dest$$reg); + __ ld(R4_ARG2/* issuing pc */, _abi(lr), R1_SP); + __ mtctr(Rdest); + __ bctr(); + %} + + enc_class enc_shouldnotreachhere() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tdi); + + MacroAssembler _masm(&cbuf); + __ trap_should_not_reach_here(); + %} + + // Inlined partial subtype check. + enc_class enc_PartialSubtypeCheck(iRegPdst result, iRegPdst subklass, iRegPdst superklass, + iRegPdst tmp_klass, iRegPdst tmp_arrayptr) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + + const Register reg_result = reg_to_register_object($result$$reg); + const Register reg_subklass = reg_to_register_object($subklass$$reg); + const Register reg_superklass = reg_to_register_object($superklass$$reg); + const Register reg_klass = reg_to_register_object($tmp_klass$$reg); // index into cache array + const Register reg_arrayptr = reg_to_register_object($tmp_arrayptr$$reg); // current value from cache array + + __ check_klass_subtype_slow_path(reg_subklass, reg_superklass, reg_arrayptr, reg_klass, NULL, reg_result); + %} + + enc_class lateExpand_decode_oop_not_null(iRegPdst dst, iRegNsrc src) %{ + decodeN_shiftNode *n1 = new (C) decodeN_shiftNode(); + n1->add_req(n_region, n_src); + n1->_opnds[0] = op_dst; + n1->_opnds[1] = op_src; + n1->_bottom_type = _bottom_type; + + decodeN_addNode *n2 = new (C) decodeN_addNode(); + n2->add_req(n_region, n1); + n2->_opnds[0] = op_dst; + n2->_opnds[1] = op_dst; + n2->_bottom_type = _bottom_type; + ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); + ra_->set_oop(n2, true); + + nodes->push(n1); + nodes->push(n2); + %} + + enc_class lateExpand_decode_oop(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ + decodeN_shiftNode *n_shift = new (C) decodeN_shiftNode(); + cmpN_reg_imm0Node *n_compare = new (C) cmpN_reg_imm0Node(); + + n_compare->add_req(n_region, n_src); + n_compare->_opnds[0] = op_crx; + n_compare->_opnds[1] = op_src; + n_compare->_opnds[2] = new (C) immN_0Oper(TypeNarrowOop::NULL_PTR); + + n_shift->add_req(n_region, n_src); + n_shift->_opnds[0] = op_dst; + n_shift->_opnds[1] = op_src; + n_shift->_bottom_type = _bottom_type; + + if (VM_Version::has_isel()) { + // use isel instruction with Power 7 + + decodeN_addNode *n_add_base = new (C) decodeN_addNode(); + n_add_base->add_req(n_region, n_shift); + n_add_base->_opnds[0] = op_dst; + n_add_base->_opnds[1] = op_dst; + n_add_base->_bottom_type = _bottom_type; + + cond_set_0_ptrNode *n_cond_set = new (C) cond_set_0_ptrNode(); + n_cond_set->add_req(n_region, n_compare, n_add_base); + n_cond_set->_opnds[0] = op_dst; + n_cond_set->_opnds[1] = op_crx; + n_cond_set->_opnds[2] = op_dst; + n_cond_set->_bottom_type = _bottom_type; + + assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); + ra_->set_oop(n_cond_set, true); + + ra_->set_pair(n_shift->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); + ra_->set_pair(n_add_base->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_cond_set->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n_compare); + nodes->push(n_shift); + nodes->push(n_add_base); + nodes->push(n_cond_set); + + } else { + // before Power 7 + cond_add_baseNode *n_add_base = new (C) cond_add_baseNode(); + + n_add_base->add_req(n_region, n_compare, n_shift); + n_add_base->_opnds[0] = op_dst; + n_add_base->_opnds[1] = op_crx; + n_add_base->_opnds[2] = op_dst; + n_add_base->_bottom_type = _bottom_type; + + assert(ra_->is_oop(this) == true, "A decodeN node must produce an oop!"); + ra_->set_oop(n_add_base, true); + + ra_->set_pair(n_shift->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); + ra_->set_pair(n_add_base->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n_compare); + nodes->push(n_shift); + nodes->push(n_add_base); + } + %} + + enc_class lateExpand_encode_oop_not_null(iRegNdst dst, iRegPdst src) %{ + + encodeP_subNode *n1 = new (C) encodeP_subNode(); + n1->add_req(n_region, n_src); + n1->_opnds[0] = op_dst; + n1->_opnds[1] = op_src; + n1->_bottom_type = _bottom_type; + + encodeP_shiftNode *n2 = new (C) encodeP_shiftNode(); + n2->add_req(n_region, n1); + n2->_opnds[0] = op_dst; + n2->_opnds[1] = op_dst; + n2->_bottom_type = _bottom_type; + ra_->set_pair(n1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n1); + nodes->push(n2); + assert(!(ra_->is_oop(this)), "sanity"); // This is not supposed to be GC'ed. + %} + + enc_class lateExpand_encode_oop(iRegNdst dst, iRegPdst src, flagsReg crx) %{ + + if (VM_Version::has_isel()) { + // use isel instruction with Power 7 + cmpP_reg_imm16Node *n_compare = new (C) cmpP_reg_imm16Node(); + encodeP_subNode *n_sub_base = new (C) encodeP_subNode(); + encodeP_shiftNode *n_shift = new (C) encodeP_shiftNode(); + cond_set_0_oopNode *n_cond_set = new (C) cond_set_0_oopNode(); + + n_compare->add_req(n_region, n_src); + n_compare->_opnds[0] = op_crx; + n_compare->_opnds[1] = op_src; + n_compare->_opnds[2] = new (C) immL16Oper(0); + + n_sub_base->add_req(n_region, n_src); + n_sub_base->_opnds[0] = op_dst; + n_sub_base->_opnds[1] = op_src; + n_sub_base->_bottom_type = _bottom_type; + + n_shift->add_req(n_region, n_sub_base); + n_shift->_opnds[0] = op_dst; + n_shift->_opnds[1] = op_dst; + n_shift->_bottom_type = _bottom_type; + + n_cond_set->add_req(n_region, n_compare, n_shift); + n_cond_set->_opnds[0] = op_dst; + n_cond_set->_opnds[1] = op_crx; + n_cond_set->_opnds[2] = op_dst; + n_cond_set->_bottom_type = _bottom_type; + + ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); + ra_->set_pair(n_sub_base->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_shift->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_cond_set->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n_compare); + nodes->push(n_sub_base); + nodes->push(n_shift); + nodes->push(n_cond_set); + + } else { + // before Power 7 + moveRegNode *n_move = new (C) moveRegNode(); + cmpP_reg_imm16Node *n_compare = new (C) cmpP_reg_imm16Node(); + encodeP_shiftNode *n_shift = new (C) encodeP_shiftNode(); + cond_sub_baseNode *n_sub_base = new (C) cond_sub_baseNode(); + + n_move->add_req(n_region, n_src); + n_move->_opnds[0] = op_dst; + n_move->_opnds[1] = op_src; + ra_->set_oop(n_move, true); // Until here, 'n_move' still produces an oop. + + n_compare->add_req(n_region, n_src); + n_compare->add_prec(n_move); + + n_compare->_opnds[0] = op_crx; + n_compare->_opnds[1] = op_src; + n_compare->_opnds[2] = new (C) immL16Oper(0); + + n_sub_base->add_req(n_region, n_compare, n_src); + n_sub_base->_opnds[0] = op_dst; + n_sub_base->_opnds[1] = op_crx; + n_sub_base->_opnds[2] = op_src; + n_sub_base->_bottom_type = _bottom_type; + + n_shift->add_req(n_region, n_sub_base); + n_shift->_opnds[0] = op_dst; + n_shift->_opnds[1] = op_dst; + n_shift->_bottom_type = _bottom_type; + + ra_->set_pair(n_shift->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_compare->_idx, ra_->get_reg_second(n_crx), ra_->get_reg_first(n_crx)); + ra_->set_pair(n_sub_base->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + ra_->set_pair(n_move->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(n_move); + nodes->push(n_compare); + nodes->push(n_sub_base); + nodes->push(n_shift); + } + + assert(!(ra_->is_oop(this)), "sanity"); // This is not supposed to be GC'ed. + %} + + enc_class emit_decode_oop(iRegPdst dst, iRegNsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + assert (LogMinObjAlignmentInBytes == Universe::narrow_oop_shift(), "decode alg wrong"); + const Register reg_dst = reg_to_register_object($dst$$reg); + const Register reg_src = reg_to_register_object($src$$reg); + __ sldi(reg_dst, reg_src, LogMinObjAlignmentInBytes); + if (Universe::narrow_oop_base() != NULL) { + Label done; + __ cmpdi(CCR0, reg_src, 0); + __ beq(CCR0, done); + __ add(reg_dst, reg_dst, R30); + __ bind(done); + } + %} + + // Late expand emitter for runtime leaf calls. + enc_class lateExpand_java_to_runtime_call(method meth) %{ + Node *toc = in(TypeFunc::ReturnAdr); + loadConLNodesTuple loadConLNodes_Entry; +#if defined(ABI_ELFv2) + jlong entry_address = (jlong) this->entry_point(); + assert(entry_address, "need address here"); + loadConLNodes_Entry = loadConLNodesTuple_create(C, ra_, toc, new (C) immLOper(entry_address), + false, OptoReg::Name(R12_H_num), OptoReg::Name(R12_num)); +#else + // Get the struct that describes the function we are about to call. + FunctionDescriptor* fd = (FunctionDescriptor*) this->entry_point(); + assert(fd, "need fd here"); + jlong entry_address = (jlong) fd->entry(); + + // new nodes + loadConLNodesTuple loadConLNodes_Env; + loadConLNodesTuple loadConLNodes_Toc; + + // Create nodes and operands for loading the entry point. + loadConLNodes_Entry = loadConLNodesTuple_create(C, ra_, toc, new (C) immLOper(entry_address), + false, OptoReg::Name(R12_H_num), OptoReg::Name(R12_num)); + + // Create nodes and operands for loading the env pointer. + if (fd->env() != NULL) { + loadConLNodes_Env = loadConLNodesTuple_create(C, ra_, toc, new (C) immLOper((jlong) fd->env()), + false, OptoReg::Name(R11_H_num), OptoReg::Name(R11_num)); + } else { + loadConLNodes_Env._large_hi = NULL; + loadConLNodes_Env._large_lo = NULL; + loadConLNodes_Env._small = NULL; + loadConLNodes_Env._last = new (C) loadConL16Node(); + loadConLNodes_Env._last->_opnds[0] = new (C) iRegLdstOper(); + loadConLNodes_Env._last->_opnds[1] = new (C) immL16Oper(0); + ra_->set_pair(loadConLNodes_Env._last->_idx, OptoReg::Name(R11_H_num), OptoReg::Name(R11_num)); + } + + // Create nodes and operands for loading the Toc point. + loadConLNodes_Toc = loadConLNodesTuple_create(C, ra_, toc, new (C) immLOper((jlong) fd->toc()), + false, OptoReg::Name(R2_H_num), OptoReg::Name(R2_num)); +#endif // ABI_ELFv2 + // mtctr node + MachNode *mtctr = new (C) CallLeafDirect_mtctrNode(); + assert(loadConLNodes_Entry._last != NULL, "entry must exist"); + mtctr->add_req(0, loadConLNodes_Entry._last); + + mtctr->_opnds[0] = new (C) iRegLdstOper(); + mtctr->_opnds[1] = new (C) iRegLdstOper(); + + // call node + MachCallLeafNode *call = new (C) CallLeafDirectNode(); + + call->_opnds[0] = _opnds[0]; + call->_opnds[1] = new (C) methodOper((intptr_t) entry_address); // May get set later. + + // Make the new call node look like the old one. + call->_name = _name; + call->_tf = _tf; + call->_entry_point = _entry_point; + call->_cnt = _cnt; + call->_argsize = _argsize; + call->_oop_map = _oop_map; + call->_jvms = _jvms; + call->_jvmadj = _jvmadj; + call->_in_rms = _in_rms; + call->_nesting = _nesting; + + // New call needs all inputs of old call. + // Req... + for (uint i = 0; i < req(); ++i) { + if (i != TypeFunc::ReturnAdr) { + call->add_req(in(i)); + } else { + // put the mtctr where ReturnAdr would be + call->add_req(mtctr); + } + } + + // ...as well as prec + for (uint i = req(); i < len(); ++i) { + call->add_prec(in(i)); + } + +#if !defined(ABI_ELFv2) + // ... and more prec. + if (loadConLNodes_Env._last) call->add_prec(loadConLNodes_Env._last); + if (loadConLNodes_Toc._last) call->add_prec(loadConLNodes_Toc._last); +#endif + + // registers + ra_->set1(mtctr->_idx, OptoReg::Name(SR_CTR_num)); + + // Insert the new nodes. + if (loadConLNodes_Entry._large_hi) nodes->push(loadConLNodes_Entry._large_hi); + if (loadConLNodes_Entry._last) nodes->push(loadConLNodes_Entry._last); +#if !defined(ABI_ELFv2) + if (loadConLNodes_Env._large_hi) nodes->push(loadConLNodes_Env._large_hi); + if (loadConLNodes_Env._last) nodes->push(loadConLNodes_Env._last); + if (loadConLNodes_Toc._large_hi) nodes->push(loadConLNodes_Toc._large_hi); + if (loadConLNodes_Toc._last) nodes->push(loadConLNodes_Toc._last); +#endif + nodes->push(mtctr); + nodes->push(call); + %} + + // Move to ctr for leaf call. + enc_class enc_leaf_call_mtctr(iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mtctr); + MacroAssembler _masm(&cbuf); + Register Rsrc = reg_to_register_object($src$$reg); + __ mtctr(Rsrc); + %} + + // Branch to ctr and link for leaf call. + enc_class enc_leaf_call(method meth) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_bctrl); + MacroAssembler _masm(&cbuf); + __ bctrl(); + %} + + // a runtime call + enc_class enc_java_to_runtime_call (method meth) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + const address start_pc = __ pc(); + +#if defined(ABI_ELFv2) + address entry= !($meth$$method) ? NULL : (address)$meth$$method; + __ call_c(entry, relocInfo::runtime_call_type); +#else + // The function we're going to call. + FunctionDescriptor fdtemp; + const FunctionDescriptor* fd = !($meth$$method) ? &fdtemp : (FunctionDescriptor*)$meth$$method; + + Register Rtoc = R12_scratch2; + // Calculate the method's TOC. + __ calculate_address_from_global_toc(Rtoc, __ method_toc()); + // Put entry, env, toc into the constant pool, this needs up to 3 constant + // pool entries; call_c_using_toc will optimize the call. + __ call_c_using_toc(fd, relocInfo::runtime_call_type, Rtoc); +#endif + + // Check the ret_addr_offset. + assert(((MachCallRuntimeNode*)this)->ret_addr_offset() == __ last_calls_return_pc() - start_pc, + "Fix constant in ret_addr_offset()"); + %} + + // A Java static call or a runtime call. + // + // Branch-and-link relative to a trampoline. + // The trampoline loads the target address and does a long branch to there. + // In case we call java, the trampoline branches to a interpreter_stub + // which loads the inline cache and the real call target from the constant pool. + // + // This basically looks like this: + // + // >>>> consts -+ -+ + // | |- offset1 + // [call target1] | <-+ + // [IC cache] |- offset2 + // [call target2] <--+ + // + // <<<< consts + // >>>> insts + // + // bl offset16 -+ -+ ??? // How many bits available? + // | | + // <<<< insts | | + // >>>> stubs | | + // | |- trampoline_stub_Reloc + // trampoline stub: | <-+ + // r2 = toc | + // r2 = [r2 + offset1] | // Load call target1 from const section + // mtctr r2 | + // bctr |- static_stub_Reloc + // comp_to_interp_stub: <---+ + // r1 = toc + // ICreg = [r1 + IC_offset] // Load IC from const section + // r1 = [r1 + offset2] // Load call target2 from const section + // mtctr r1 + // bctr + // + // <<<< stubs + // + // The call instruction in the code either + // - Branches directly to a compiled method if the offset is encodable in instruction. + // - Branches to the trampoline stub if the offset to the compiled method is not encodable. + // - Branches to the compiled_to_interp stub if the target is interpreted. + // + // Further there are three relocations from the loads to the constants in + // the constant section. + // + // Usage of r1 and r2 in the stubs allows to distinguish them. + enc_class enc_java_static_call(method meth) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_bl); + + MacroAssembler _masm(&cbuf); + relocInfo::relocType reloc; + + if (!_method) { + // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap. + reloc = relocInfo::runtime_call_type; + } else if (_optimized_virtual) { + reloc = relocInfo::opt_virtual_call_type; + } else { + reloc = relocInfo::static_call_type; + } + + // Puts 1 constant into the constant pool (call target). + const EmitCallOffsets offsets = emit_call_with_trampoline_stub(_masm, (address)$meth$$method, reloc); + + if (ra_->C->env()->failing()) + return; + + assert(offsets.insts_call_instruction_offset != -1, "must be initialized"); + + if (_method) { + // Create stub for static call. Puts 1 or 2 constants into constant + // pool (inline cache, call target). + emit_java_to_interp_stub(_masm, offsets.insts_call_instruction_offset); + } + %} + + // Emit a method handle call. + // + // Method handle calls from compiled to compiled are going thru a + // c2i -> i2c adapter, extending the frame for their arguments. The + // caller however, returns directly to the compiled callee, that has + // to cope with the extended frame. We restore the original frame by + // loading the callers sp and adding the calculated framesize. + enc_class enc_java_handle_call(method meth) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + relocInfo::relocType reloc; + assert(_optimized_virtual, "methodHandle call should be a virtual call"); + reloc = relocInfo::opt_virtual_call_type; + + const EmitCallOffsets offsets = emit_call_with_trampoline_stub(_masm, (address)$meth$$method, reloc); + // restore original sp + __ ld(R11_scratch1, 0, R1_SP); // load caller sp + Compile* C = ra_->C; + const long framesize = C->frame_slots() << LogBytesPerInt; + unsigned int bytes = (unsigned int)framesize; + long offset = Assembler::align_addr(bytes, frame::alignment_in_bytes); + if (Assembler::is_simm(-offset, 16)) { + __ addi(R1_SP, R11_scratch1, -offset); + } else { + __ load_const_optimized(R12_scratch2, -offset); + __ add(R1_SP, R11_scratch1, R12_scratch2); + } +#ifdef ASSERT + __ ld(R12_scratch2, 0, R1_SP); // Load from unextended_sp. + __ cmpd(CCR0, R11_scratch1, R12_scratch2); + __ asm_assert_eq("backlink changed", 0x8000); +#endif + // If fails should store backlink before unextending. + + if (ra_->C->env()->failing()) + return; + + assert(offsets.insts_call_instruction_offset != -1, "must be initialized"); + + if (_method) { + // Create stub for static call. + emit_java_to_interp_stub(_masm, offsets.insts_call_instruction_offset); + } + %} + + // Late expand emitter for virtual calls. + enc_class lateExpand_java_dynamic_call_sched(method meth) %{ + // Toc is in return address field, though not accessible via lateExpand + // functionaliy. + Node *toc = in(TypeFunc::ReturnAdr); + + // Create the nodes for loading the IC from the TOC. + loadConLNodesTuple loadConLNodes_IC = + loadConLNodesTuple_create(C, ra_, toc, new (C) immLOper((jlong)Universe::non_oop_word()), + true, OptoReg::Name(R19_H_num), OptoReg::Name(R19_num)); + + // Create the call node. + CallDynamicJavaDirectSchedNode *call = new (C) CallDynamicJavaDirectSchedNode(); + call->_method_handle_invoke = _method_handle_invoke; + call->_vtable_index = _vtable_index; + call->_method = _method; + call->_bci = _bci; + call->_optimized_virtual = _optimized_virtual; + call->_tf = _tf; + call->_entry_point = _entry_point; + call->_cnt = _cnt; + call->_argsize = _argsize; + call->_oop_map = _oop_map; + call->_jvms = _jvms; + call->_jvmadj = _jvmadj; + call->_in_rms = _in_rms; + call->_nesting = _nesting; + + // New call needs all inputs of old call. + // Req... + for (uint i = 0; i < req(); ++i) { + if (i != TypeFunc::ReturnAdr) { + call->add_req(in(i)); + } else { + // The expanded node does not need toc any more. + call->add_req(C->top()); + } + } + // ...as well as prec + for (uint i = req(); i < len() ; ++i) { + call->add_prec(in(i)); + } + + // The cache must come before the call, but it's not a req edge. + call->add_prec(loadConLNodes_IC._last); + // Remember nodes loading the inline cache into r19. + call->_load_ic_hi_node = loadConLNodes_IC._large_hi; + call->_load_ic_node = loadConLNodes_IC._small; + + // Operands for new nodes. + call->_opnds[0] = _opnds[0]; + call->_opnds[1] = _opnds[1]; + + // Only the inline cache is associated with a register. + assert(Matcher::inline_cache_reg() == OptoReg::Name(R19_num), "ic reg should be R19"); + + // Push new nodes. + if (loadConLNodes_IC._large_hi) nodes->push(loadConLNodes_IC._large_hi); + if (loadConLNodes_IC._last) nodes->push(loadConLNodes_IC._last); + nodes->push(call); + %} + + // Second node of expanded dynamic call - the call. + enc_class enc_java_dynamic_call_sched(method meth) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_bl); + + MacroAssembler _masm(&cbuf); + + if (!ra_->C->in_scratch_emit_size()) { + // Create a call trampoline stub for the given method. + const address entry_point = !($meth$$method) ? 0 : (address)$meth$$method; + const address entry_point_const = __ address_constant(entry_point, RelocationHolder::none); + const int entry_point_const_toc_offset = __ offset_to_method_toc(entry_point_const); + emit_trampoline_stub(_masm, entry_point_const_toc_offset, __ offset()); + + if (ra_->C->env()->failing()) + return; + + // Build relocation at call site with ic position as data. + assert((_load_ic_hi_node != NULL && _load_ic_node == NULL) || + (_load_ic_hi_node == NULL && _load_ic_node != NULL), + "must have one, but can't have both"); + assert((_load_ic_hi_node != NULL && _load_ic_hi_node->_cbuf_insts_offset != -1) || + (_load_ic_node != NULL && _load_ic_node->_cbuf_insts_offset != -1), + "must contain instruction offset"); + const int virtual_call_oop_addr_offset = _load_ic_hi_node != NULL + ? _load_ic_hi_node->_cbuf_insts_offset + : _load_ic_node->_cbuf_insts_offset; + const address virtual_call_oop_addr = __ addr_at(virtual_call_oop_addr_offset); + assert(MacroAssembler::is_load_const_from_method_toc_at(virtual_call_oop_addr), + "should be load from TOC"); + + __ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr)); + } + + // At this point I do not have the address of the trampoline stub, + // and the entry point might be too far away for bl. Pc() serves + // as dummy and bl will be patched later. + __ bl((address) __ pc()); + %} + + // Compound version of call dynamic + enc_class enc_java_dynamic_call(method meth) %{ + // TODO: PPC port // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + int start_offset = __ offset(); + + Register Rtoc = (ra_) + ? as_Register(Matcher::_regEncode[ra_->get_reg_first(in(4))]) + : R2_TOC; + + int vtable_index = this->_vtable_index; +#if 0 // TODO: PPC port + if (!methodOopDesc::is_valid_virtual_vtable_index(vtable_index)) { + // Must be invalid_vtable_index, not nonvirtual_vtable_index. + assert(vtable_index == methodOopDesc::invalid_vtable_index, "correct sentinel value"); + Register ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode()); + AddressLiteral oop = __ allocate_oop_address((jobject)Universe::non_oop_word()); + + address virtual_call_oop_addr = __ pc(); + __ load_const_from_method_toc(ic_reg, oop, Rtoc); + // CALL to fixup routine. Fixup routine uses ScopeDesc info + // to determine who we intended to call. + __ relocate(virtual_call_Relocation::spec(virtual_call_oop_addr)); + + EmitCallOffsets offsets = { -1, -1 }; + int ret_addr_offset = __ offset(); + offsets = emit_call_with_trampoline_stub(_masm, (address)$meth$$method, relocInfo::none); + ret_addr_offset += offsets.ret_addr_offset; + set_lazy_constant(MachCallDynamicJavaNode, ret_addr_offset_no_vtable, + ret_addr_offset - start_offset); + assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == ret_addr_offset - start_offset, + "Fix constant in ret_addr_offset()"); + } else { + assert(!UseInlineCaches, "expect vtable calls only if not using ICs"); + // Go thru the vtable. Get receiver klass. Receiver already + // checked for non-null. If we'll go thru a C2I adapter, the + // interpreter expects method in R19_method. + + __ load_heap_oop_not_null(R11_scratch1, oopDesc::klass_offset_in_bytes(), R3); + + int entry_offset = instanceKlass::vtable_start_offset() + + vtable_index * vtableEntry::size(); + int v_off = entry_offset * wordSize + + vtableEntry::method_offset_in_bytes(); + __ li(R19_method, v_off); + __ ldx(R19_method/*method oop*/, + R19_method/*method offset*/, + R11_scratch1/*class*/); + // NOTE: for vtable dispatches, the vtable entry will never be + // null. However it may very well end up in handle_wrong_method + // if the method is abstract for the particular class. + __ ld(R11_scratch1, + in_bytes(methodOopDesc::from_compiled_offset()), + R19_method); + // Call target. Either compiled code or C2I adapter. + __ mtctr(R11_scratch1); + __ bctrl(); + int return_pc_offset = __ offset(); + assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == return_pc_offset - start_offset, + "Fix constant in ret_addr_offset()"); + } +#endif + Unimplemented(); + %} + + enc_class enc_cmove_bne_neg(iRegIdst dst, flagsReg crx, iRegIsrc src1) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + + Label done; + __ bne(Rcrx, done); + __ neg(Rdst, Rsrc1); + +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + + __ bind(done); + %} + + enc_class enc_divw(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_divw); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ divw(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_divd(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_divd); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc1 = reg_to_register_object($src1$$reg); + Register Rsrc2 = reg_to_register_object($src2$$reg); + __ divd(Rdst, Rsrc1, Rsrc2); + %} + + enc_class enc_compiler_fast_lock(flagsReg pcc_arg, iRegPdst oop_arg, iRegPdst box_arg, iRegPdst tmp1, + iRegPdst tmp2, iRegPdst tmp3) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + + Register displaced_header = reg_to_register_object($tmp1$$reg); + Register current_header = reg_to_register_object($tmp2$$reg); + Register temp = reg_to_register_object($tmp3$$reg); + Register box = reg_to_register_object($box_arg$$reg); + Register oop = reg_to_register_object($oop_arg$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($pcc_arg$$reg); + + __ compiler_fast_lock_object(Rcrx, oop, box, temp, displaced_header, current_header); + + // if locking was successfull, Rcrx should indicate 'EQ'. + // the compiler generates a branch to the runtime call to + // _complete_monitor_locking_Java for the case where Rcrx is 'NE'. + %} + + enc_class enc_compiler_fast_unlock(flagsReg pcc_arg, iRegPdst oop_arg, iRegPdst box_arg, iRegPdst tmp1, + iRegPdst tmp2, iRegPdst tmp3) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + + Register displaced_header = reg_to_register_object($tmp1$$reg); + Register current_header = reg_to_register_object($tmp2$$reg); + Register temp = reg_to_register_object($tmp3$$reg); + Register box = reg_to_register_object($box_arg$$reg); + Register oop = reg_to_register_object($oop_arg$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($pcc_arg$$reg); + + __ compiler_fast_unlock_object(Rcrx, oop, box, temp, displaced_header, current_header); + + // If unlocking was successfull, Rcrx should indicate 'EQ'. + // the compiler generates a branch to the runtime call to + // _complete_monitor_unlocking_Java for the case where Rcrx is 'NE'. + %} + + enc_class enc_cmove_bns_less(flagsReg crx) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmovecr); + + MacroAssembler _masm(&cbuf); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + Label done; + __ bns(Rcrx, done); // not unordered -> keep crx + __ li(R0, 0); + __ cmpwi(Rcrx, R0, 1); // unordered -> set crx to 'less' +#if 0 // TODO: PPC port + if (_size == 16) { + // Bundler will have changed our size if stop is not required + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class lateExpand_cmpF_reg_reg(flagsReg crx, regF src1, regF src2) %{ + // + // replaces + // + // region src1 src2 + // \ | | + // crx=cmpF_reg_reg + // + // with + // + // region src1 src2 + // \ | | + // crx=cmpFUnordered_reg_reg + // | + // ^ region + // | \ + // crx=cmov_bns_less + // + + // Create new nodes. + MachNode *m1 = new (C) cmpFUnordered_reg_regNode(); + MachNode *m2 = new (C) cmov_bns_lessNode(); + + // inputs for new nodes + m1->add_req(n_region, n_src1, n_src2); + m2->add_req(n_region); + + // precedences for new nodes + m2->add_prec(m1); + + // operands for new nodes + m1->_opnds[0] = op_crx; + m1->_opnds[1] = op_src1; + m1->_opnds[2] = op_src2; + + m2->_opnds[0] = op_crx; + + // registers for new nodes + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx + + // Insert new nodes. + nodes->push(m1); + nodes->push(m2); + %} + + enc_class lateExpand_cmpD_reg_reg(flagsReg crx, regD src1, regD src2) %{ + // + // replaces + // + // region src1 src2 + // \ | | + // crx=cmpD_reg_reg + // + // with + // + // region src1 src2 + // \ | | + // crx=cmpDUnordered_reg_reg + // | + // ^ region + // | \ + // crx=cmov_bns_less + // + + // create new nodes + MachNode *m1 = new (C) cmpDUnordered_reg_regNode(); + MachNode *m2 = new (C) cmov_bns_lessNode(); + + // inputs for new nodes + m1->add_req(n_region, n_src1, n_src2); + m2->add_req(n_region); + + // precedences for new nodes + m2->add_prec(m1); + + // operands for new nodes + m1->_opnds[0] = op_crx; + m1->_opnds[1] = op_src1; + m1->_opnds[2] = op_src2; + + m2->_opnds[0] = op_crx; + + // registers for new nodes + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx + + // Insert new nodes. + nodes->push(m1); + nodes->push(m2); + %} + + enc_class lateExpand_cmovI_conIvalueMinus1_conIvalue0_conIvalue1(iRegIdst dst, flagsReg crx) %{ + // + // replaces + // + // region crx + // \ | + // dst=cmovI_conIvalueMinus1_conIvalue0_conIvalue1 + // + // with + // + // region + // \ + // dst=loadConI16(0) + // | + // ^ region crx + // | \ | + // dst=cmovI_conIvalueMinus1_conIvalue1 + // + + // Create new nodes. + MachNode *m1 = new (C) loadConI16Node(); + MachNode *m2 = new (C) cmovI_conIvalueMinus1_conIvalue1Node(); + + // inputs for new nodes + m1->add_req(n_region); + m2->add_req(n_region, n_crx); + + // precedences for new nodes + m2->add_prec(m1); + + // operands for new nodes + m1->_opnds[0] = op_dst; + m1->_opnds[1] = new (C) immI16Oper(0); + + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_crx; + + // registers for new nodes + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst + + // Insert new nodes. + nodes->push(m1); + nodes->push(m2); + %} + + enc_class enc_cmovI_conIvalueMinus1_conIvalue1(iRegIdst dst, flagsReg crx) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + Label done; + // li(Rdst, 0); // equal -> 0 + __ beq(Rcrx, done); + __ li(Rdst, 1); // greater -> +1 + __ bgt(Rcrx, done); + __ li(Rdst, -1); // unordered or less -> -1 +#if 0 // TODO: PPC port + if (_size == 20) { + // Bundler will have changed our size to 16 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class ShouldNotEncodeThis () %{ + ShouldNotCallThis(); + %} + + // ClearArray loop. + // The inlineCallClearArray instruction enforces an alignment. + // Compiler ensures base is doubleword aligned and cnt is count of doublewords. + enc_class enc_ClearArray_reg_reg(Register cnt, Register base) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register cnt_dwords_reg = reg_to_register_object($cnt$$reg); + Register base_ptr_reg = reg_to_register_object($base$$reg); + + __ clear_memory_doubleword(base_ptr_reg, cnt_dwords_reg); // kills cnt, base, R0 + %} + + enc_class enc_CopyRawMemoryXAlignedDisjoint_regP_regP_regL(iRegPdst src, iRegPdst dst, iRegLdst cnt, + iRegPdst tmp1, iRegPdst tmp2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + Register cnt_reg = reg_to_register_object($cnt$$reg); + Register tmp1_reg = reg_to_register_object($tmp1$$reg); + Register tmp2_reg = reg_to_register_object($tmp2$$reg); + + Label loop; + Label done; + + __ cmpdi(CCR0, cnt_reg, 0); + __ beq(CCR0, done); + + __ load_const_optimized(tmp1_reg, 0L); // init index reg + __ mtctr(cnt_reg); + + __ bind(loop); + __ ldx(R0, src_reg, tmp1_reg); + __ stdx(R0, dst_reg, tmp1_reg); + __ addi(tmp1_reg, tmp1_reg, 8); + __ bdnz(loop); + __ bind(done); + %} + + // Precompute the needle if constant here, as node is available. + enc_class enc_String_IndexOf_imm1_char(iRegIdst result, iRegPsrc haystack, iRegIdst haycnt, immP needleImm, immL offsetImm, + iRegIdst tmp1, iRegIdst tmp2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + + immPOper *needleOper = (immPOper *)$needleImm; + const TypeOopPtr *t = needleOper->type()->isa_oopptr(); + ciTypeArray* needle_values = t->const_oop()->as_type_array(); // Pointer to live char * + assert(needle_values, ""); + + immLOper *offsetOper = (immLOper *)$offsetImm; + long off = offsetOper->constant(); + guarantee(!UseCompressedOops || off == 16, "not beginning of char[]"); + + __ string_indexof_1($result$$Register, + $haystack$$Register, $haycnt$$Register, + R0, needle_values->char_at(0), + $tmp1$$Register, $tmp2$$Register); + %} + + // Precompute the needle if constant here, as node is available. + enc_class enc_String_IndexOf_imm1(iRegIdst result, iRegPsrc haystack, iRegIdst haycnt, iRegPsrc needle, + iRegIdst tmp1, iRegIdst tmp2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + + Node *ndl = in(operand_index($needle)); // The node that defines needle. + ciTypeArray* needle_values = ndl->bottom_type()->is_aryptr()->const_oop()->as_type_array(); + guarantee(needle_values, "sanity"); + if (needle_values != NULL) { + __ string_indexof_1($result$$Register, + $haystack$$Register, $haycnt$$Register, + R0, needle_values->char_at(0), + $tmp1$$Register, $tmp2$$Register); + } else { + __ string_indexof_1($result$$Register, + $haystack$$Register, $haycnt$$Register, + $needle$$Register, 0, + $tmp1$$Register, $tmp2$$Register); + } + %} + + enc_class enc_String_IndexOf_imm(iRegIdst result, iRegPsrc haystack, iRegIdst haycnt, iRegPsrc needle, uimmI16 needlecntImm, + iRegIdst tmp1, iRegIdst tmp2, iRegIdst tmp3, iRegIdst tmp4, iRegIdst tmp5) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + int needlecnt = $needlecntImm$$constant; + Node *ndl = in(operand_index($needle)); // The node that defines needle. + ciTypeArray* needle_values = ndl->bottom_type()->is_aryptr()->const_oop()->as_type_array(); + + __ string_indexof($result$$Register, + $haystack$$Register, $haycnt$$Register, + $needle$$Register, needle_values, $tmp5$$Register, needlecnt, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); + %} + + enc_class enc_String_IndexOf(iRegIsrc result, iRegPsrc haystack, iRegIsrc haycnt, iRegPsrc needle, iRegIsrc needlecnt, + iRegIdst tmp1, iRegIsrc tmp2, iRegIsrc tmp3, iRegIsrc tmp4) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + __ string_indexof($result$$Register, + $haystack$$Register, $haycnt$$Register, + $needle$$Register, NULL, $needlecnt$$Register, 0, // needlecnt not constant. + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); + %} + + enc_class enc_String_EqualsImm(iRegPsrc str1, iRegPsrc str2, uimmI15 cntImm, iRegIdst result, + iRegPdst tmp1, iRegPdst tmp2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + int cntval = $cntImm$$constant; + MacroAssembler _masm(&cbuf); + __ char_arrays_equalsImm($str1$$Register, $str2$$Register, cntval, $result$$Register, + $tmp1$$Register, $tmp2$$Register); + %} + + enc_class enc_String_Equals(iRegPsrc str1, iRegPsrc str2, iRegIsrc cnt, iRegIdst result, + iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, iRegPdst tmp4, iRegPdst tmp5) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + __ char_arrays_equals($str1$$Register, $str2$$Register, $cnt$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, $tmp5$$Register); + %} + + enc_class enc_String_Compare(rarg1RegP str1, rarg2RegP str2, rarg3RegI cnt1, rarg4RegI cnt2, iRegIdst result, + iRegPdst tmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + __ string_compare($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, + $result$$Register, $tmp$$Register); + %} + + enc_class enc_cmove_bso_stackSlotL(iRegLdst dst, flagsReg crx, stackSlotL mem ) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + Register Rbase= reg_to_register_object($mem$$base); + int Idisp = $mem$$disp; + if ($mem$$base == 1) { + Idisp += 0 /* TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes()*/; + } + Label done; + __ bso(Rcrx, done); + __ ld(Rdst, Idisp, Rbase); +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class lateExpand_cmovI_bso_stackSlotL_conLValue0(iRegIdst dst, flagsReg crx, stackSlotL mem) %{ + // + // replaces + // + // region dst crx mem + // \ | | / + // dst=cmovI_bso_stackSlotL_conLvalue0 + // + // with + // + // region dst + // \ / + // dst=loadConI16(0) + // | + // ^ region dst crx mem + // | \ | | / + // dst=cmovI_bso_stackSlotL + // + + // Create new nodes. + MachNode *m1 = new (C) loadConI16Node(); + MachNode *m2 = new (C) cmovI_bso_stackSlotLNode(); + + // inputs for new nodes + m1->add_req(n_region); + m2->add_req(n_region, n_crx, n_mem); + + // precedences for new nodes + m2->add_prec(m1); + + // operands for new nodes + m1->_opnds[0] = op_dst; + m1->_opnds[1] = new (C) immI16Oper(0); + + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_crx; + m2->_opnds[2] = op_mem; + + // registers for new nodes + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst + + // Insert new nodes. + nodes->push(m1); + nodes->push(m2); + %} + + enc_class lateExpand_cmovL_bso_stackSlotL_conLvalue0(iRegLdst dst, flagsReg crx, stackSlotL mem) %{ + // + // replaces + // + // region dst crx mem + // \ | | / + // dst=cmovL_bso_stackSlotL_conLvalue0 + // + // with + // + // region dst + // \ / + // dst=loadConL16(0) + // | + // ^ region dst crx mem + // | \ | | / + // dst=cmovL_bso_stackSlotL + // + + // Create new nodes. + MachNode *m1 = new (C) loadConL16Node(); + MachNode *m2 = new (C) cmovL_bso_stackSlotLNode(); + + // inputs for new nodes + m1->add_req(n_region); + m2->add_req(n_region, n_crx, n_mem); + + // precedences for new nodes + m2->add_prec(m1); + + // operands for new nodes + m1->_opnds[0] = op_dst; + m1->_opnds[1] = new (C) immL16Oper(0); + + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_crx; + m2->_opnds[2] = op_mem; + + // registers for new nodes + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // dst + + // Insert new nodes. + nodes->push(m1); + nodes->push(m2); + %} + + enc_class enc_cmove_reg(iRegIdst dst, flagsReg crx, iRegIsrc src, cmpOp cmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + int cc = $cmp$$cmpcode; + int flags_reg = $crx$$reg; + Label done; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + // Branch if not (cmp crx). + __ bc(cc_to_inverse_boint(cc), cc_to_biint(cc, flags_reg), done); + __ mr(Rdst, Rsrc); +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + // isel for cmove + enc_class enc_isel(iRegIdst dst, flagsReg crx, iRegIsrc src, cmpOp cmp) %{ + // This is a Power7 instruction for which no machine description + // exists. Anyways, the scheduler should be off on Power7. + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + ConditionRegister Rc = reg_to_ConditionRegister_object($crx$$reg); + int cc = $cmp$$cmpcode; + __ isel(Rdst, Rc, (Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), Rsrc); + %} + + // Set dst = 0 if crx is equal, otherwise dst = src + enc_class enc_isel_set_to_0_or_value(iRegPdst dst, flagsReg crx, iRegPsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); // see above + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + + __ isel_0(Rdst, Rcrx, Assembler::equal, Rsrc); + %} + + enc_class enc_cond_add_base(iRegPdst dst, flagsReg crx, iRegPsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + Label done; + + __ beq(Rcrx, done); + __ add(Rdst, Rsrc, R30); +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class enc_cond_sub_base(iRegPdst dst, flagsReg crx, iRegPsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + ConditionRegister Rcrx = reg_to_ConditionRegister_object($crx$$reg); + Label done; + + __ beq(Rcrx, done); + __ sub(Rdst, Rsrc, R30); +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class enc_cmove_imm(iRegIdst dst, flagsReg crx, immI16 src, cmpOp cmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Register Rdst = reg_to_register_object($dst$$reg); + int Csrc = $src$$constant; + int cc = $cmp$$cmpcode; + int flags_reg = $crx$$reg; + Label done; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + // Branch if not (cmp crx). + __ bc(cc_to_inverse_boint(cc), cc_to_biint(cc, flags_reg), done); + __ li(Rdst, Csrc); +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required. + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class enc_cmovef_reg(regF dst, flagsReg crx, regF src, cmpOp cmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmovef); + + MacroAssembler _masm(&cbuf); + FloatRegister Rdst = reg_to_FloatRegister_object($dst$$reg); + FloatRegister Rsrc = reg_to_FloatRegister_object($src$$reg); + int cc = $cmp$$cmpcode; + int flags_reg = $crx$$reg; + Label done; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + // Branch if not (cmp crx). + __ bc(cc_to_inverse_boint(cc), cc_to_biint(cc, flags_reg), done); + __ fmr(Rdst, Rsrc); +#if 0 // TODO: PPC port + if (_size == 12) { + // Bundler will have changed our size to 8 if stop is not required + __ endgroup(); + } +#endif + if (false /* TODO: PPC PORT ra_->C->do_hb_scheduling()*/) Unimplemented(); + __ bind(done); + %} + + enc_class enc_compareAndSwapI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdummy = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rold = reg_to_register_object($src1$$reg); + Register Rnew = reg_to_register_object($src2$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + + // CmpxchgX sets CCR0 to cmpX(Rval, Rcomp) and Rres to 'true'/'false'. + __ cmpxchgw(CCR0, Rdummy, Rold, Rnew, Rptr, MacroAssembler::MemBarNone, + MacroAssembler::cmpxchgx_hint_atomic_update(), Rres, true); + %} + + enc_class enc_compareAndSwapN(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdummy = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rold = reg_to_register_object($src1$$reg); + Register Rnew = reg_to_register_object($src2$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + + // CmpxchgX sets CCR0 to cmpX(Rval, Rcomp) and Rres to 'true'/'false'. + __ cmpxchgw(CCR0, Rdummy, Rold, Rnew, Rptr, MacroAssembler::MemBarNone, + MacroAssembler::cmpxchgx_hint_atomic_update(), Rres, true); + %} + + // As enc_compareAndSwapLP, but return flag register instead of boolean value in + // int register. + enc_class enc_storeConditionalLP(flagsReg crx, iRegPdst mem_ptr, iRegLsrc oldVal, iRegLsrc newVal) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + ConditionRegister flag = reg_to_ConditionRegister_object($crx$$reg); + Register Rdummy = R0; + Register Rres = R0; + Register Rold = reg_to_register_object($oldVal$$reg); + Register Rnew = reg_to_register_object($newVal$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + __ cmpxchgd(flag, Rdummy, Rold, Rnew, Rptr, MacroAssembler::MemBarNone, + MacroAssembler::cmpxchgx_hint_atomic_update(), noreg, NULL, true); + %} + + enc_class enc_compareAndSwapLP(iRegIdst res, iRegPdst mem_ptr, iRegLsrc src1, iRegLsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rdummy = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rold = reg_to_register_object($src1$$reg); + Register Rnew = reg_to_register_object($src2$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + + // CmpxchgX sets CCR0 to cmpX(Rval, Rcomp) and Rres to 'true'/'false'. + __ cmpxchgd(CCR0, Rdummy, Rold, Rnew, Rptr, MacroAssembler::MemBarNone, + MacroAssembler::cmpxchgx_hint_atomic_update(), Rres, NULL, true); + %} + + enc_class enc_GetAndAddI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rtmp = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ lwarx(Rold, Rptr, /*hint*/ false); + __ add(Rtmp, Rsrc, Rold); + __ stwcx_(Rtmp, Rptr); + __ bne(CCR0, Lretry); + if (RegCollision) __ subf(Rres, Rsrc, Rtmp); + %} + + enc_class enc_GetAndAddL(iRegLdst res, iRegPdst mem_ptr, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rtmp = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ ldarx(Rold, Rptr, /*hint*/ false); + __ add(Rtmp, Rsrc, Rold); + __ stdcx_(Rtmp, Rptr); + __ bne(CCR0, Lretry); + if (RegCollision) __ subf(Rres, Rsrc, Rtmp); + %} + + enc_class enc_GetAndSetI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rtmp = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ lwarx(Rold, Rptr, /*hint*/ false); + __ stwcx_(Rsrc, Rptr); + __ bne(CCR0, Lretry); + if (RegCollision) __ mr(Rres, Rtmp); + %} + + enc_class enc_GetAndSetL(iRegLdst res, iRegPdst mem_ptr, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + Register Rtmp = R0; + Register Rres = reg_to_register_object($res$$reg); + Register Rsrc = reg_to_register_object($src$$reg); + Register Rptr = reg_to_register_object($mem_ptr$$reg); + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ ldarx(Rold, Rptr, /*hint*/ false); + __ stdcx_(Rsrc, Rptr); + __ bne(CCR0, Lretry); + if (RegCollision) __ mr(Rres, Rtmp); + %} + + // End a group. + enc_class enc_end_bundle() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_endgroup); + MacroAssembler _masm(&cbuf); + __ endgroup(); + %} + + // Nop emitters. + enc_class enc_fxnop() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + MacroAssembler _masm(&cbuf); + __ nop(); + %} + + enc_class enc_fpnop0() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + MacroAssembler _masm(&cbuf); + __ fpnop0(); + %} + + enc_class enc_fpnop1() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + MacroAssembler _masm(&cbuf); + __ fpnop1(); + %} + + enc_class enc_brnop0() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mcrf); + MacroAssembler _masm(&cbuf); + __ brnop0(); + %} + + enc_class enc_brnop1() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mcrf); + MacroAssembler _masm(&cbuf); + __ brnop1(); + %} + + enc_class enc_brnop2() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mcrf); + MacroAssembler _masm(&cbuf); + __ brnop2(); + %} + + enc_class enc_rethrow() %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + cbuf.set_insts_mark(); + __ b64_patchable((address)OptoRuntime::rethrow_stub(), + relocInfo::runtime_call_type); + %} + + enc_class enc_membar_acquire %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwsync); + MacroAssembler _masm(&cbuf); + __ acquire(); + %} + + enc_class enc_membar_release %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwsync); + MacroAssembler _masm(&cbuf); + __ release(); + %} + + enc_class enc_membar_volatile %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sync); + MacroAssembler _masm(&cbuf); + __ fence(); + %} + + enc_class enc_poll(immI dst, iRegLdst poll) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + // Fake operand dst needed for PPC scheduler. + assert($dst$$constant == 0x0, "dst must be 0x0"); + + MacroAssembler _masm(&cbuf); + // Mark the code position where the load from the safepoint + // polling page was emitted as relocInfo::poll_type. + __ relocate(relocInfo::poll_type); + __ load_from_polling_page($poll$$Register); + %} + + enc_class enc_loadConPollAddr(iRegPdst poll) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + + Register Rpoll = reg_to_register_object($poll$$reg); + __ load_const_optimized(Rpoll, (long)(address) os::get_polling_page()); // TODO: PPC port get_standard_polling_page()); + %} + + enc_class enc_insrdi(iRegLdst dst, immI16 pos, iRegLsrc src, immI16 shift) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldimi); + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + + __ insrdi(dst_reg, src_reg, $shift$$constant, $pos$$constant); + %} + + enc_class enc_insrwi(iRegLdst dst, immI16 pos, iRegLsrc src, immI16 shift) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwimi); + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + + __ insrwi(dst_reg, src_reg, $shift$$constant, $pos$$constant); + %} + + enc_class enc_popcntw(iRegLdst dst, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_popcntb); + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ popcntw(dst_reg, src_reg); + %} + + enc_class enc_popcntd(iRegLdst dst, iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_popcntb); + MacroAssembler _masm(&cbuf); + Register src_reg = reg_to_register_object($src$$reg); + Register dst_reg = reg_to_register_object($dst$$reg); + __ popcntd(dst_reg, src_reg); + %} + +%} + +//----------FRAME-------------------------------------------------------------- +// Definition of frame structure and management information. + +frame %{ + // What direction does stack grow in (assumed to be same for native & Java). + stack_direction(TOWARDS_LOW); + + // These two registers define part of the calling convention between + // compiled code and the interpreter. + + // Inline Cache Register or methodOop for I2C. + inline_cache_reg(R19); // R19_method + + // Method Oop Register when calling interpreter. + interpreter_method_oop_reg(R19); // R19_method + + // Optional: name the operand used by cisc-spilling to access + // [stack_pointer + offset]. + cisc_spilling_operand_name(indOffset); + + // Number of stack slots consumed by a Monitor enter. + sync_stack_slots((frame::jit_monitor_size / VMRegImpl::stack_slot_size)); + + // Compiled code's Frame Pointer. + frame_pointer(R1); // R1_SP + + // Interpreter stores its frame pointer in a register which is + // stored to the stack by I2CAdaptors. I2CAdaptors convert from + // interpreted java to compiled java. + // + // R14_state holds pointer to caller's cInterpreter. + interpreter_frame_pointer(R14); // R14_state + + stack_alignment(frame::alignment_in_bytes); + + in_preserve_stack_slots((frame::jit_in_preserve_size / VMRegImpl::stack_slot_size)); + + // Number of outgoing stack slots killed above the + // out_preserve_stack_slots for calls to C. Supports the var-args + // backing area for register parms. + // + varargs_C_out_slots_killed(((frame::abi_reg_args_size - frame::jit_out_preserve_size) / VMRegImpl::stack_slot_size)); + + // The after-PROLOG location of the return address. Location of + // return address specifies a type (REG or STACK) and a number + // representing the register number (i.e. - use a register name) or + // stack slot. + // + // A: Link register is stored in stack slot ... + // M: ... but it's in the caller's frame according to PPC-64 ABI. + // J: Therefore, we make sure that the link register is also in R11_scratch1 + // at the end of the prolog. + // B: We use R20, now. + //return_addr(REG R20); + + // G: After reading the comments made by all the luminaries on their + // failure to tell the compiler where the return address really is, + // I hardly dare to try myself. However, I'm convinced it's in slot + // 4 what apparently works and saves us some spills. + return_addr(STACK 4); + + // This is the body of the function + // + // void Matcher::calling_convention(OptoRegPair* sig, // array of ideal regs + // uint length, // length of array + // bool is_outgoing) + // + // The `sig' array is to be updated. sig[j] represents the location + // of the j-th argument, either a register or a stack slot. + + // Comment taken from i486.ad: + // Body of function which returns an integer array locating + // arguments either in registers or in stack slots. Passed an array + // of ideal registers called "sig" and a "length" count. Stack-slot + // offsets are based on outgoing arguments, i.e. a CALLER setting up + // arguments for a CALLEE. Incoming stack arguments are + // automatically biased by the preserve_stack_slots field above. + calling_convention %{ + // No difference between ingoing/outgoing. Just pass false. + SharedRuntime::java_calling_convention(sig_bt, regs, length, false); + %} + + // Comment taken from i486.ad: + // Body of function which returns an integer array locating + // arguments either in registers or in stack slots. Passed an array + // of ideal registers called "sig" and a "length" count. Stack-slot + // offsets are based on outgoing arguments, i.e. a CALLER setting up + // arguments for a CALLEE. Incoming stack arguments are + // automatically biased by the preserve_stack_slots field above. + c_calling_convention %{ + // This is obviously always outgoing. + // C argument in register AND stack slot. + (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); + %} + + // Location of native (C/C++) and interpreter return values. This + // is specified to be the same as Java. In the 32-bit VM, long + // values are actually returned from native calls in O0:O1 and + // returned to the interpreter in I0:I1. The copying to and from + // the register pairs is done by the appropriate call and epilog + // opcodes. This simplifies the register allocator. + c_return_value %{ + assert((ideal_reg >= Op_RegI && ideal_reg <= Op_RegL) || + (ideal_reg == Op_RegN && Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0), + "only return normal values"); + // enum names from opcodes.hpp: Op_Node Op_Set Op_RegN Op_RegI Op_RegP Op_RegF Op_RegD Op_RegL + static int typeToRegLo[Op_RegL+1] = { 0, 0, R3_num, R3_num, R3_num, F1_num, F1_num, R3_num }; + static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, R3_H_num, R3_H_num, OptoReg::Bad, F1_H_num, R3_H_num }; + return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]); + %} + + // Location of compiled Java return values. Same as C + return_value %{ + assert((ideal_reg >= Op_RegI && ideal_reg <= Op_RegL) || + (ideal_reg == Op_RegN && Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0), + "only return normal values"); + // enum names from opcodes.hpp: Op_Node Op_Set Op_RegN Op_RegI Op_RegP Op_RegF Op_RegD Op_RegL + static int typeToRegLo[Op_RegL+1] = { 0, 0, R3_num, R3_num, R3_num, F1_num, F1_num, R3_num }; + static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, R3_H_num, R3_H_num, OptoReg::Bad, F1_H_num, R3_H_num }; + return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]); + %} +%} + + +//----------ATTRIBUTES--------------------------------------------------------- + +//----------Operand Attributes------------------------------------------------- +op_attrib op_cost(1); // Required cost attribute. + +//----------Instruction Attributes--------------------------------------------- + +// Cost attribute. required. +ins_attrib ins_cost(DEFAULT_COST); + +// Is this instruction a non-matching short branch variant of some +// long branch? Not required. +ins_attrib ins_short_branch(0); + +// This instruction does implicit checks at the given machine-instruction offset +// (optional attribute). +ins_attrib ins_implicit_check_offset(-1); // TODO: PPC port + +ins_attrib ins_implicit_check_follows_matched_true_path(true); +ins_attrib ins_is_TrapBasedCheckNode(true); + +// Number of constants. +// This instruction uses the given number of constants +// (optional attribute). +// This is needed to determine in time whether the constant pool will +// exceed 4000 entries. Before lateExpand the overall number of constants +// is determined. It's also used to compute the constant pool size +// in Output(). +ins_attrib ins_num_consts(0); + +// Required alignment attribute (must be a power of 2) specifies the +// alignment that some part of the instruction (not necessarily the +// start) requires. If > 1, a compute_padding() function must be +// provided for the instruction. +ins_attrib ins_alignment(1); + +// Enforce/prohibit rematerializations. +// - If an instruction is attributed with 'ins_cannot_rematerialize(true)' +// then rematerialization of that instruction is prohibited and the +// instruction's value will be spilled if necessary. +// Causes that MachNode::rematerialize() returns false. +// - If an instruction is attributed with 'ins_should_rematerialize(true)' +// then rematerialization should be enforced and a copy of the instruction +// should be inserted if possible; rematerialization is not guaranteed. +// Note: this may result in rematerializations in front of every use. +// Causes that MachNode::rematerialize() can return true. +// (optional attribute) +ins_attrib ins_cannot_rematerialize(false); +ins_attrib ins_should_rematerialize(false); + +// Instruction has variable size depending on alignment. +ins_attrib ins_variable_size_depending_on_alignment(false); + +// Instruction requires the toc register. +ins_attrib ins_requires_toc(false); + +// Instruction is a nop. +ins_attrib ins_is_nop(false); + +// Instruction is mapped to a MachIfFastLock node (instead of MachFastLock). +ins_attrib ins_use_mach_if_fast_lock_node(false); + +// Field for the toc offset of a constant. +// +// This is needed if the toc offset is not encodable as an immediate in +// the PPC load instruction. If so, the upper (hi) bits of the offset are +// added to the toc, and from this a load with immediate is performed. +// With late expand, we get two nodes that require the same offset +// but which don't know about each other. The offset is only known +// when the constant is added to the constant pool during emitting. +// It is generated in the 'hi'-node adding the upper bits, and saved +// in this node. The 'lo'-node has a link to the 'hi'-node and reads +// the offset from there when it gets encoded. +ins_attrib ins_field_const_toc_offset(0); +ins_attrib ins_field_const_toc_offset_hi_node(0); + +// A field that can hold the instructions offset in the code buffer. +// Set in the nodes emitter. +ins_attrib ins_field_cbuf_insts_offset(-1); + +// Fields for referencing a call's load-IC-node. +// If the toc offset can not be encoded as an immediate in a load, we +// use two nodes. +ins_attrib ins_field_load_ic_hi_node(0); +ins_attrib ins_field_load_ic_node(0); + +//----------OPERANDS----------------------------------------------------------- +// Operand definitions must precede instruction definitions for correct +// parsing in the ADLC because operands constitute user defined types +// which are used in instruction definitions. +// +// Formats are generated automatically for constants and base registers. + +//----------Simple Operands---------------------------------------------------- +// Immediate Operands + +// Integer Immediate: 32-bit +operand immI() %{ + match(ConI); + op_cost(40); + format %{ %} + interface(CONST_INTER); +%} + +operand immI8() %{ + predicate(Assembler::is_simm(n->get_int(), 8)); + op_cost(0); + match(ConI); + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: 16-bit +operand immI16() %{ + predicate(Assembler::is_simm(n->get_int(), 16)); + op_cost(0); + match(ConI); + format %{ %} + interface(CONST_INTER); +%} + +// Integer Immediate: 32-bit, where lowest 16 bits are 0x0000. +operand immIhi16() %{ + predicate(((n->get_int() & 0xffff0000) != 0) && ((n->get_int() & 0xffff) == 0)); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immInegpow2() %{ + predicate(is_power_of_2_long((jlong) (julong) (juint) (-(n->get_int())))); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIpow2minus1() %{ + predicate(is_power_of_2_long((((jlong) (n->get_int()))+1))); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIpowerOf2() %{ + predicate(is_power_of_2_long((((jlong) (julong) (juint) (n->get_int()))))); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned Integer Immediate: the values 0-31 +operand uimmI5() %{ + predicate(Assembler::is_uimm(n->get_int(), 5)); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned Integer Immediate: 6-bit +operand uimmI6() %{ + predicate(Assembler::is_uimm(n->get_int(), 6)); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned Integer Immediate: 6-bit int, greater than 32 +operand uimmI6_ge32() %{ + predicate(Assembler::is_uimm(n->get_int(), 6) && n->get_int() >= 32); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned Integer Immediate: 15-bit +operand uimmI15() %{ + predicate(Assembler::is_uimm(n->get_int(), 15)); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned Integer Immediate: 16-bit +operand uimmI16() %{ + predicate(Assembler::is_uimm(n->get_int(), 16)); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// constant 'int 0'. +operand immI_0() %{ + predicate(n->get_int() == 0); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// constant 'int 1'. +operand immI_1() %{ + predicate(n->get_int() == 1); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// constant 'int -1'. +operand immI_minus1() %{ + predicate(n->get_int() == -1); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// int value 16. +operand immI_16() %{ + predicate(n->get_int() == 16); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// int value 24. +operand immI_24() %{ + predicate(n->get_int() == 24); + match(ConI); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Compressed oops constants +// Pointer Immediate +operand immN() %{ + match(ConN); + + op_cost(10); + format %{ %} + interface(CONST_INTER); +%} + +// NULL Pointer Immediate +operand immN_0() %{ + predicate(n->get_narrowcon() == 0); + match(ConN); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +//// Compressed klass constants +//operand immNKlass() %{ +// match(ConNKlass); +// +// op_cost(0); +// format %{ %} +// interface(CONST_INTER); +//%} + +// Pointer Immediate: 64-bit +operand immP() %{ + match(ConP); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Operand to avoid match of loadConP. +// This operand can be used to avoid matching of an instruct +// with chain rule. +operand immP_NM() %{ + match(ConP); + predicate(false); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// costant 'pointer 0'. +operand immP_0() %{ + predicate(n->get_ptr() == 0); + match(ConP); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// pointer 0x0 or 0x1 +operand immP_0or1() %{ + predicate((n->get_ptr() == 0) || (n->get_ptr() == 1)); + match(ConP); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immP_poll() %{ + // Use standard_polling_page. + predicate(n->get_ptr() != 0 && n->get_ptr() == (intptr_t)os::get_polling_page()); // TODO: PPC port get_standard_polling_page()); + match(ConP); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immL() %{ + match(ConL); + op_cost(40); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: 16-bit +operand immL16() %{ + predicate(Assembler::is_simm(n->get_long(), 16)); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: 16-bit, 4-aligned +operand immL16Alg4() %{ + predicate(Assembler::is_simm(n->get_long(), 16) && ((n->get_long() & 0x3) == 0)); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: 32-bit, where lowest 16 bits are 0x0000. +operand immL32hi16() %{ + predicate(Assembler::is_simm(n->get_long(), 32) && ((n->get_long() & 0xffffL) == 0L)); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: 32-bit +operand immL32() %{ + predicate(Assembler::is_simm(n->get_long(), 32)); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: 64-bit, where highest 16 bits are not 0x0000. +operand immLhighest16() %{ + predicate((n->get_long() & 0xffff000000000000L) != 0L && (n->get_long() & 0x0000ffffffffffffL) == 0L); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLnegpow2() %{ + predicate(is_power_of_2_long((jlong)-(n->get_long()))); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLpow2minus1() %{ + predicate(is_power_of_2_long((((jlong) (n->get_long()))+1)) && + (n->get_long() != (jlong)0xffffffffffffffffL)); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// constant 'long 0'. +operand immL_0() %{ + predicate(n->get_long() == 0L); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// constat ' long -1'. +operand immL_minus1() %{ + predicate(n->get_long() == -1L); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Long Immediate: low 32-bit mask +operand immL_32bits() %{ + predicate(n->get_long() == 0xFFFFFFFFL); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Unsigned Long Immediate: 16-bit +operand uimmL16() %{ + predicate(Assembler::is_uimm(n->get_long(), 16)); + match(ConL); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Float Immediate +operand immF() %{ + match(ConF); + op_cost(40); + format %{ %} + interface(CONST_INTER); +%} + +// constant 'float +0.0'. +operand immF_0() %{ + predicate((n->getf() == 0) && + (fpclassify(n->getf()) == FP_ZERO) && (signbit(n->getf()) == 0)); + match(ConF); + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +// Double Immediate +operand immD() %{ + match(ConD); + op_cost(40); + format %{ %} + interface(CONST_INTER); +%} + +// Integer Register Operands +// Integer Destination Register +// See definition of reg_class bits32_reg_rw. +operand iRegIdst() %{ + constraint(ALLOC_IN_RC(bits32_reg_rw)); + match(RegI); + match(rscratch1RegI); + match(rscratch2RegI); + match(rarg1RegI); + match(rarg2RegI); + match(rarg3RegI); + match(rarg4RegI); + format %{ %} + interface(REG_INTER); +%} + +// Integer Source Register +// See definition of reg_class bits32_reg_ro. +operand iRegIsrc() %{ + constraint(ALLOC_IN_RC(bits32_reg_ro)); + match(RegI); + match(rscratch1RegI); + match(rscratch2RegI); + match(rarg1RegI); + match(rarg2RegI); + match(rarg3RegI); + match(rarg4RegI); + format %{ %} + interface(REG_INTER); +%} + +operand rscratch1RegI() %{ + constraint(ALLOC_IN_RC(rscratch1_bits32_reg)); + match(iRegIdst); + format %{ %} + interface(REG_INTER); +%} + +operand rscratch2RegI() %{ + constraint(ALLOC_IN_RC(rscratch2_bits32_reg)); + match(iRegIdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg1RegI() %{ + constraint(ALLOC_IN_RC(rarg1_bits32_reg)); + match(iRegIdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg2RegI() %{ + constraint(ALLOC_IN_RC(rarg2_bits32_reg)); + match(iRegIdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg3RegI() %{ + constraint(ALLOC_IN_RC(rarg3_bits32_reg)); + match(iRegIdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg4RegI() %{ + constraint(ALLOC_IN_RC(rarg4_bits32_reg)); + match(iRegIdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg1RegL() %{ + constraint(ALLOC_IN_RC(rarg1_bits64_reg)); + match(iRegLdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg2RegL() %{ + constraint(ALLOC_IN_RC(rarg2_bits64_reg)); + match(iRegLdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg3RegL() %{ + constraint(ALLOC_IN_RC(rarg3_bits64_reg)); + match(iRegLdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg4RegL() %{ + constraint(ALLOC_IN_RC(rarg4_bits64_reg)); + match(iRegLdst); + format %{ %} + interface(REG_INTER); +%} + +// Pointer Destination Register +// See definition of reg_class bits64_reg_rw. +operand iRegPdst() %{ + constraint(ALLOC_IN_RC(bits64_reg_rw)); + match(RegP); + match(rscratch1RegP); + match(rscratch2RegP); + match(rarg1RegP); + match(rarg2RegP); + match(rarg3RegP); + match(rarg4RegP); + format %{ %} + interface(REG_INTER); +%} + +// Pointer Destination Register +// Operand not using r11 and r12 (killed in epilog). +operand iRegPdstNoScratch() %{ + constraint(ALLOC_IN_RC(bits64_reg_leaf_call)); + match(RegP); + match(rarg1RegP); + match(rarg2RegP); + match(rarg3RegP); + match(rarg4RegP); + format %{ %} + interface(REG_INTER); +%} + +// Pointer Source Register +// See definition of reg_class bits64_reg_ro. +operand iRegPsrc() %{ + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(RegP); + match(iRegPdst); + match(rscratch1RegP); + match(rscratch2RegP); + match(rarg1RegP); + match(rarg2RegP); + match(rarg3RegP); + match(rarg4RegP); + match(threadRegP); + format %{ %} + interface(REG_INTER); +%} + +// Thread operand. +operand threadRegP() %{ + constraint(ALLOC_IN_RC(thread_bits64_reg)); + match(iRegPdst); + format %{ "R16" %} + interface(REG_INTER); +%} + +operand rscratch1RegP() %{ + constraint(ALLOC_IN_RC(rscratch1_bits64_reg)); + match(iRegPdst); + format %{ "R11" %} + interface(REG_INTER); +%} + +operand rscratch2RegP() %{ + constraint(ALLOC_IN_RC(rscratch2_bits64_reg)); + match(iRegPdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg1RegP() %{ + constraint(ALLOC_IN_RC(rarg1_bits64_reg)); + match(iRegPdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg2RegP() %{ + constraint(ALLOC_IN_RC(rarg2_bits64_reg)); + match(iRegPdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg3RegP() %{ + constraint(ALLOC_IN_RC(rarg3_bits64_reg)); + match(iRegPdst); + format %{ %} + interface(REG_INTER); +%} + +operand rarg4RegP() %{ + constraint(ALLOC_IN_RC(rarg4_bits64_reg)); + match(iRegPdst); + format %{ %} + interface(REG_INTER); +%} + +operand iRegNsrc() %{ + constraint(ALLOC_IN_RC(bits32_reg_ro)); + match(RegN); + match(iRegNdst); + + format %{ %} + interface(REG_INTER); +%} + +operand iRegNdst() %{ + constraint(ALLOC_IN_RC(bits32_reg_rw)); + match(RegN); + + format %{ %} + interface(REG_INTER); +%} + +// Long Destination Register +// See definition of reg_class bits64_reg_rw. +operand iRegLdst() %{ + constraint(ALLOC_IN_RC(bits64_reg_rw)); + match(RegL); + match(rscratch1RegL); + match(rscratch2RegL); + format %{ %} + interface(REG_INTER); +%} + +// Long Source Register +// See definition of reg_class bits64_reg_ro. +operand iRegLsrc() %{ + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(RegL); + match(iRegLdst); + match(rscratch1RegL); + match(rscratch2RegL); + format %{ %} + interface(REG_INTER); +%} + +// Special operand for ConvL2I. +operand iRegL2Isrc(iRegLsrc reg) %{ + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(ConvL2I reg); + format %{ "ConvL2I($reg)" %} + interface(REG_INTER) +%} + +operand rscratch1RegL() %{ + constraint(ALLOC_IN_RC(rscratch1_bits64_reg)); + match(RegL); + format %{ %} + interface(REG_INTER); +%} + +operand rscratch2RegL() %{ + constraint(ALLOC_IN_RC(rscratch2_bits64_reg)); + match(RegL); + format %{ %} + interface(REG_INTER); +%} + +// Condition Code Flag Registers +operand flagsReg() %{ + constraint(ALLOC_IN_RC(int_flags)); + match(RegFlags); + format %{ %} + interface(REG_INTER); +%} + +// Condition Code Flag Register CR0 +operand flagsRegCR0() %{ + constraint(ALLOC_IN_RC(int_flags_CR0)); + match(RegFlags); + format %{ "CR0" %} + interface(REG_INTER); +%} + +operand flagsRegCR1() %{ + constraint(ALLOC_IN_RC(int_flags_CR1)); + match(RegFlags); + format %{ "CR1" %} + interface(REG_INTER); +%} + +operand flagsRegCR6() %{ + constraint(ALLOC_IN_RC(int_flags_CR6)); + match(RegFlags); + format %{ "CR6" %} + interface(REG_INTER); +%} + +operand regCTR() %{ + constraint(ALLOC_IN_RC(ctr_reg)); + // RegFlags should work. Introducing a RegSpecial type would cause a + // lot of changes. + match(RegFlags); + format %{"SR_CTR" %} + interface(REG_INTER); +%} + +operand regD() %{ + constraint(ALLOC_IN_RC(dbl_reg)); + match(RegD); + format %{ %} + interface(REG_INTER); +%} + +operand regF() %{ + constraint(ALLOC_IN_RC(flt_reg)); + match(RegF); + format %{ %} + interface(REG_INTER); +%} + +// Special Registers + +// Method Register +operand inline_cache_regP(iRegPdst reg) %{ + constraint(ALLOC_IN_RC(r19_bits64_reg)); // inline_cache_reg + match(reg); + format %{ %} + interface(REG_INTER); +%} + +operand compiler_method_oop_regP(iRegPdst reg) %{ + constraint(ALLOC_IN_RC(rscratch1_bits64_reg)); // compiler_method_oop_reg + match(reg); + format %{ %} + interface(REG_INTER); +%} + +operand interpreter_method_oop_regP(iRegPdst reg) %{ + constraint(ALLOC_IN_RC(r19_bits64_reg)); // interpreter_method_oop_reg + match(reg); + format %{ %} + interface(REG_INTER); +%} + +// Operands to remove register moves in unscaled mode. +// Match read/write registers with an EncodeP node if neither shift nor add are required. +operand iRegP2N(iRegPsrc reg) %{ + predicate(false /* TODO: PPC port MatchDecodeNodes*/&& Universe::narrow_oop_shift() == 0); + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(EncodeP reg); + format %{ "$reg" %} + interface(REG_INTER) +%} + +operand iRegN2P(iRegNsrc reg) %{ + predicate(false /* TODO: PPC port MatchDecodeNodes*/); + constraint(ALLOC_IN_RC(bits32_reg_ro)); + match(DecodeN reg); + format %{ "$reg" %} + interface(REG_INTER) +%} + +//----------Complex Operands--------------------------------------------------- +// Indirect Memory Reference +operand indirect(iRegPsrc reg) %{ + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(reg); + op_cost(100); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect with Offset +operand indOffset16(iRegPsrc reg, immL16 offset) %{ + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(AddP reg offset); + op_cost(100); + format %{ "[$reg + $offset]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp($offset); + %} +%} + +// Indirect with 4-aligned Offset +operand indOffset16Alg4(iRegPsrc reg, immL16Alg4 offset) %{ + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(AddP reg offset); + op_cost(100); + format %{ "[$reg + $offset]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp($offset); + %} +%} + +//----------Complex Operands for Compressed OOPs------------------------------- +// Compressed OOPs with narrow_oop_shift == 0. + +// Indirect Memory Reference, compressed OOP +operand indirectNarrow(iRegNsrc reg) %{ + predicate(false /* TODO: PPC port MatchDecodeNodes*/); + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(DecodeN reg); + op_cost(100); + format %{ "[$reg]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp(0x0); + %} +%} + +// Indirect with Offset, compressed OOP +operand indOffset16Narrow(iRegNsrc reg, immL16 offset) %{ + predicate(false /* TODO: PPC port MatchDecodeNodes*/); + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(AddP (DecodeN reg) offset); + op_cost(100); + format %{ "[$reg + $offset]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp($offset); + %} +%} + +// Indirect with 4-aligned Offset, compressed OOP +operand indOffset16NarrowAlg4(iRegNsrc reg, immL16Alg4 offset) %{ + predicate(false /* TODO: PPC port MatchDecodeNodes*/); + constraint(ALLOC_IN_RC(bits64_reg_ro)); + match(AddP (DecodeN reg) offset); + op_cost(100); + format %{ "[$reg + $offset]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0x0); + scale(0x0); + disp($offset); + %} +%} + +//----------Special Memory Operands-------------------------------------------- +// Stack Slot Operand +// +// This operand is used for loading and storing temporary values on +// the stack where a match requires a value to flow through memory. +operand stackSlotI(sRegI reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegI); + format %{ "[sp+$reg]" %} + interface(MEMORY_INTER) %{ + base(0x1); // R1_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +operand stackSlotL(sRegL reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegL); + format %{ "[sp+$reg]" %} + interface(MEMORY_INTER) %{ + base(0x1); // R1_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +operand stackSlotP(sRegP reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegP); + format %{ "[sp+$reg]" %} + interface(MEMORY_INTER) %{ + base(0x1); // R1_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +operand stackSlotF(sRegF reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegF); + format %{ "[sp+$reg]" %} + interface(MEMORY_INTER) %{ + base(0x1); // R1_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +operand stackSlotD(sRegD reg) %{ + constraint(ALLOC_IN_RC(stack_slots)); + op_cost(100); + //match(RegD); + format %{ "[sp+$reg]" %} + interface(MEMORY_INTER) %{ + base(0x1); // R1_SP + index(0x0); + scale(0x0); + disp($reg); // Stack Offset + %} +%} + +// Operands for expressing Control Flow +// NOTE: Label is a predefined operand which should not be redefined in +// the AD file. It is generically handled within the ADLC. + +//----------Conditional Branch Operands---------------------------------------- +// Comparison Op +// +// This is the operation of the comparison, and is limited to the +// following set of codes: L (<), LE (<=), G (>), GE (>=), E (==), NE +// (!=). +// +// Other attributes of the comparison, such as unsignedness, are specified +// by the comparison instruction that sets a condition code flags register. +// That result is represented by a flags operand whose subtype is appropriate +// to the unsignedness (etc.) of the comparison. +// +// Later, the instruction which matches both the Comparison Op (a Bool) and +// the flags (produced by the Cmp) specifies the coding of the comparison op +// by matching a specific subtype of Bool operand below. + +// When used for floating point comparisons: unordered same as less. +operand cmpOp() %{ + match(Bool); + format %{ "" %} + interface(COND_INTER) %{ + // BO only encodes bit 4 of bcondCRbiIsX, as bits 1-3 are always '100'. + // BO & BI + equal(0xA); // 10 10: bcondCRbiIs1 & Condition::equal + not_equal(0x2); // 00 10: bcondCRbiIs0 & Condition::equal + less(0x8); // 10 00: bcondCRbiIs1 & Condition::less + greater_equal(0x0); // 00 00: bcondCRbiIs0 & Condition::less + less_equal(0x1); // 00 01: bcondCRbiIs0 & Condition::greater + greater(0x9); // 10 01: bcondCRbiIs1 & Condition::greater + //overflow(0xB); // 10 11: bcondCRbiIs1 & Condition::summary_overflow + //no_overflow(0x3); // 00 11: bcondCRbiIs0 & Condition::summary_overflow + %} +%} + +//----------OPERAND CLASSES---------------------------------------------------- +// Operand Classes are groups of operands that are used to simplify +// instruction definitions by not requiring the AD writer to specify +// seperate instructions for every form of operand when the +// instruction accepts multiple operand types with the same basic +// encoding and format. The classic case of this is memory operands. +// Indirect is not included since its use is limited to Compare & Swap. + +opclass memory(indirect, indOffset16 /*, indIndex, tlsReference*/, indirectNarrow, indOffset16Narrow); +// Memory operand where offsets are 4-aligned. Required for ld, std. +opclass memoryAlg4(indirect, indOffset16Alg4, indirectNarrow, indOffset16NarrowAlg4); +opclass indirectMemory(indirect, indirectNarrow); + +// Special opclass for I and ConvL2I. +opclass iRegIsrc_iRegL2Isrc(iRegIsrc, iRegL2Isrc); + +// Operand classes to match encode and decode. iRegN_P2N is only used +// for storeN. I have never seen an encode node elsewhere. +opclass iRegN_P2N(iRegNsrc, iRegP2N); +opclass iRegP_N2P(iRegPsrc, iRegN2P); + +//----------PIPELINE----------------------------------------------------------- + +pipeline %{ + +// See J.M.Tendler et al. "Power4 system microarchitecture", IBM +// J. Res. & Dev., No. 1, Jan. 2002. + +//----------ATTRIBUTES--------------------------------------------------------- +attributes %{ + + // Power4 instructions are of fixed length. + fixed_size_instructions; + + // TODO: if `bundle' means number of instructions fetched + // per cycle, this is 8. If `bundle' means Power4 `group', that is + // max instructions issued per cycle, this is 5. + max_instructions_per_bundle = 8; + + // A Power4 instruction is 4 bytes long. + instruction_unit_size = 4; + + // The Power4 processor fetches 64 bytes... + instruction_fetch_unit_size = 64; + + // ...in one line + instruction_fetch_units = 1 + + // Unused, list one so that array generated by adlc is not empty. + // Aix compiler chokes if _nop_count = 0. + nops(fxNop); +%} + +//----------RESOURCES---------------------------------------------------------- +// Resources are the functional units available to the machine +resources( + PPC_BR, // branch unit + PPC_CR, // condition unit + PPC_FX1, // integer arithmetic unit 1 + PPC_FX2, // integer arithmetic unit 2 + PPC_LDST1, // load/store unit 1 + PPC_LDST2, // load/store unit 2 + PPC_FP1, // float arithmetic unit 1 + PPC_FP2, // float arithmetic unit 2 + PPC_LDST = PPC_LDST1 | PPC_LDST2, + PPC_FX = PPC_FX1 | PPC_FX2, + PPC_FP = PPC_FP1 | PPC_FP2 + ); + +//----------PIPELINE DESCRIPTION----------------------------------------------- +// Pipeline Description specifies the stages in the machine's pipeline +pipe_desc( + // Power4 longest pipeline path + PPC_IF, // instruction fetch + PPC_IC, + //PPC_BP, // branch prediction + PPC_D0, // decode + PPC_D1, // decode + PPC_D2, // decode + PPC_D3, // decode + PPC_Xfer1, + PPC_GD, // group definition + PPC_MP, // map + PPC_ISS, // issue + PPC_RF, // resource fetch + PPC_EX1, // execute (all units) + PPC_EX2, // execute (FP, LDST) + PPC_EX3, // execute (FP, LDST) + PPC_EX4, // execute (FP) + PPC_EX5, // execute (FP) + PPC_EX6, // execute (FP) + PPC_WB, // write back + PPC_Xfer2, + PPC_CP + ); + +//----------PIPELINE CLASSES--------------------------------------------------- +// Pipeline Classes describe the stages in which input and output are +// referenced by the hardware pipeline. + +// Simple pipeline classes. + +// Default pipeline class. +pipe_class pipe_class_default() %{ + single_instruction; + fixed_latency(2); +%} + +// Pipeline class for empty instructions. +pipe_class pipe_class_empty() %{ + single_instruction; + fixed_latency(0); +%} + +// Pipeline class for compares. +pipe_class pipe_class_compare() %{ + single_instruction; + fixed_latency(16); +%} + +// Pipeline class for traps. +pipe_class pipe_class_trap() %{ + single_instruction; + fixed_latency(100); +%} + +// Pipeline class for memory operations. +pipe_class pipe_class_memory() %{ + single_instruction; + fixed_latency(16); +%} + +// Pipeline class for call. +pipe_class pipe_class_call() %{ + single_instruction; + fixed_latency(100); +%} + +// Define the class for the Nop node. +define %{ + MachNop = pipe_class_default; +%} + +%} + +//----------INSTRUCTIONS------------------------------------------------------- + +// Naming of instructions: +// opA_operB / opA_operB_operC: +// Operation 'op' with one or two source operands 'oper'. Result +// type is A, source operand types are B and C. +// Iff A == B == C, B and C are left out. +// +// The instructions are ordered according to the following scheme: +// - loads +// - load constants +// - prefetch +// - store +// - encode/decode +// - membar +// - conditional moves +// - compare & swap +// - arithmetic and logic operations +// * int: Add, Sub, Mul, Div, Mod +// * int: lShift, arShift, urShift, rot +// * float: Add, Sub, Mul, Div +// * and, or, xor ... +// - register moves: float <-> int, reg <-> stack, repl +// - cast (high level type cast, XtoP, castPP, castII, not_null etc. +// - conv (low level type cast requiring bit changes (sign extend etc) +// - compares, range & zero checks. +// - branches +// - complex operations, intrinsics, min, max, replicate +// - lock +// - Calls +// +// If there are similar instructions with different types they are sorted: +// int before float +// small before big +// signed before unsigned +// e.g., loadS before loadUS before loadI before loadF. + + +//----------Load/Store Instructions-------------------------------------------- + +//----------Load Instructions-------------------------------------------------- + +// Converts byte to int. +// As convB2I_reg, but without match rule. The match rule of convB2I_reg +// reuses the 'amount' operand, but adlc expects that operand specification +// and operands in match rule are equivalent. +instruct convB2I_reg_2(iRegIdst dst, iRegIsrc src) %{ + effect(DEF dst, USE src); + format %{ "EXTSB $dst, $src \t// byte->int" %} + size(4); + ins_encode( enc_extsb(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct loadUB_indirect(iRegIdst dst, indirectMemory mem) %{ + // match-rule, false predicate + match(Set dst (LoadB mem)); + predicate(false); + + format %{ "LBZ $dst, $mem" %} + size(4); + ins_encode( enc_lbz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +instruct loadUB_indirect_ac(iRegIdst dst, indirectMemory mem) %{ + // match-rule, false predicate + match(Set dst (LoadB mem)); + predicate(false); + + format %{ "LBZ $dst, $mem\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lbz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Byte (8bit signed). LoadB = LoadUB + ConvUB2B. +instruct loadB_indirect_Ex(iRegIdst dst, indirectMemory mem) %{ + match(Set dst (LoadB mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST + DEFAULT_COST); + expand %{ + iRegIdst tmp; + loadUB_indirect(tmp, mem); + convB2I_reg_2(dst, tmp); + %} +%} + +instruct loadB_indirect_ac_Ex(iRegIdst dst, indirectMemory mem) %{ + match(Set dst (LoadB mem)); + ins_cost(3*MEMORY_REF_COST + DEFAULT_COST); + expand %{ + iRegIdst tmp; + loadUB_indirect_ac(tmp, mem); + convB2I_reg_2(dst, tmp); + %} +%} + +instruct loadUB_indOffset16(iRegIdst dst, indOffset16 mem) %{ + // match-rule, false predicate + match(Set dst (LoadB mem)); + predicate(false); + + format %{ "LBZ $dst, $mem" %} + size(4); + ins_encode( enc_lbz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +instruct loadUB_indOffset16_ac(iRegIdst dst, indOffset16 mem) %{ + // match-rule, false predicate + match(Set dst (LoadB mem)); + predicate(false); + + format %{ "LBZ $dst, $mem\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lbz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Byte (8bit signed). LoadB = LoadUB + ConvUB2B. +instruct loadB_indOffset16_Ex(iRegIdst dst, indOffset16 mem) %{ + match(Set dst (LoadB mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST + DEFAULT_COST); + + expand %{ + iRegIdst tmp; + loadUB_indOffset16(tmp, mem); + convB2I_reg_2(dst, tmp); + %} +%} + +instruct loadB_indOffset16_ac_Ex(iRegIdst dst, indOffset16 mem) %{ + match(Set dst (LoadB mem)); + ins_cost(3*MEMORY_REF_COST + DEFAULT_COST); + + expand %{ + iRegIdst tmp; + loadUB_indOffset16_ac(tmp, mem); + convB2I_reg_2(dst, tmp); + %} +%} + +// Load Unsigned Byte (8bit UNsigned) into an int reg. +instruct loadUB(iRegIdst dst, memory mem) %{ + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + match(Set dst (LoadUB mem)); + ins_cost(MEMORY_REF_COST); + + format %{ "LBZ $dst, $mem \t// byte, zero-extend to int" %} + size(4); + ins_encode( enc_lbz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Unsigned Byte (8bit UNsigned) acquire. +instruct loadUB_ac(iRegIdst dst, memory mem) %{ + match(Set dst (LoadUB mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LBZ $dst, $mem \t// byte, zero-extend to int, acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lbz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Unsigned Byte (8bit UNsigned) into a Long Register. +instruct loadUB2L(iRegLdst dst, memory mem) %{ + match(Set dst (ConvI2L (LoadUB mem))); + predicate(_kids[0]->_leaf->as_Load()->is_unordered() || followed_by_acquire(_kids[0]->_leaf)); + ins_cost(MEMORY_REF_COST); + + format %{ "LBZ $dst, $mem \t// byte, zero-extend to long" %} + size(4); + ins_encode( enc_lbz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +instruct loadUB2L_ac(iRegLdst dst, memory mem) %{ + match(Set dst (ConvI2L (LoadUB mem))); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LBZ $dst, $mem \t// byte, zero-extend to long, acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lbz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Short (16bit signed) +instruct loadS(iRegIdst dst, memory mem) %{ + match(Set dst (LoadS mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LHA $dst, $mem" %} + size(4); + ins_encode( enc_lha(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Short (16bit signed) acquire. +instruct loadS_ac(iRegIdst dst, memory mem) %{ + match(Set dst (LoadS mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LHA $dst, $mem\t acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lha_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Char (16bit unsigned) +instruct loadUS(iRegIdst dst, memory mem) %{ + match(Set dst (LoadUS mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LHZ $dst, $mem" %} + size(4); + ins_encode( enc_lhz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Char (16bit unsigned) acquire. +instruct loadUS_ac(iRegIdst dst, memory mem) %{ + match(Set dst (LoadUS mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LHZ $dst, $mem \t// acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lhz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Unsigned Short/Char (16bit UNsigned) into a Long Register. +instruct loadUS2L(iRegLdst dst, memory mem) %{ + match(Set dst (ConvI2L (LoadUS mem))); + predicate(_kids[0]->_leaf->as_Load()->is_unordered() || followed_by_acquire(_kids[0]->_leaf)); + ins_cost(MEMORY_REF_COST); + + format %{ "LHZ $dst, $mem \t// short, zero-extend to long" %} + size(4); + ins_encode( enc_lhz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Unsigned Short/Char (16bit UNsigned) into a Long Register acquire. +instruct loadUS2L_ac(iRegLdst dst, memory mem) %{ + match(Set dst (ConvI2L (LoadUS mem))); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LHZ $dst, $mem \t// short, zero-extend to long, acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lhz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Integer. +instruct loadI(iRegIdst dst, memory mem) %{ + match(Set dst (LoadI mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem" %} + size(4); + ins_encode( enc_lwz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Integer acquire. +instruct loadI_ac(iRegIdst dst, memory mem) %{ + match(Set dst (LoadI mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// load acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lwz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Match loading integer and casting it to unsigned int in +// long register. +// LoadI + ConvI2L + AndL 0xffffffff. +instruct loadUI2L(iRegLdst dst, memory mem, immL_32bits mask) %{ + match(Set dst (AndL (ConvI2L (LoadI mem)) mask)); + predicate(_kids[0]->_kids[0]->_leaf->as_Load()->is_unordered()); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// zero-extend to long" %} + size(4); + ins_encode( enc_lwz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// SAPJVM GL 2013-10-29 Match ConvI2L(LoadI) +// Match loading integer and casting it to long. +instruct loadI2L(iRegLdst dst, memoryAlg4 mem) %{ + match(Set dst (ConvI2L (LoadI mem))); + predicate(_kids[0]->_leaf->as_Load()->is_unordered()); + ins_cost(MEMORY_REF_COST); + + format %{ "LWA $dst, $mem \t// loadI2L" %} + size(4); + ins_encode( enc_lwa(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Match loading integer and casting it to long - acquire. +instruct loadI2L_ac(iRegLdst dst, memoryAlg4 mem) %{ + match(Set dst (ConvI2L (LoadI mem))); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LWA $dst, $mem \t// loadI2L acquire" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lwa_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Long - aligned +instruct loadL(iRegLdst dst, memoryAlg4 mem) %{ + match(Set dst (LoadL mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// long" %} + size(4); + ins_encode( enc_ld(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Long - aligned acquire. +instruct loadL_ac(iRegLdst dst, memoryAlg4 mem) %{ + match(Set dst (LoadL mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// long acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_ld_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Long - UNaligned +instruct loadL_unaligned(iRegLdst dst, memoryAlg4 mem) %{ + match(Set dst (LoadL_unaligned mem)); + // predicate(...) // Unaligned_ac is not needed (and wouldn't make sense). + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// unaligned long" %} + size(4); + ins_encode( enc_ld(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load nodes for superwords + +// Load Aligned Packed Byte +instruct loadV8(iRegLdst dst, memoryAlg4 mem) %{ + predicate(n->as_LoadVector()->memory_size() == 8); + match(Set dst (LoadVector mem)); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// load 8-byte Vector" %} + size(4); + ins_encode( enc_ld(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Range, range = array length (=jint) +instruct loadRange(iRegIdst dst, memory mem) %{ + match(Set dst (LoadRange mem)); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// range" %} + size(4); + ins_encode( enc_lwz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Compressed Pointer +instruct loadN(iRegNdst dst, memory mem) %{ + match(Set dst (LoadN mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// load compressed ptr" %} + size(4); + ins_encode( enc_lwz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Compressed Pointer acquire. +instruct loadN_ac(iRegNdst dst, memory mem) %{ + match(Set dst (LoadN mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// load acquire compressed ptr\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_lwz_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Compressed Pointer and decode it if narrow_oop_shift == 0. +instruct loadN2P_unscaled(iRegPdst dst, memory mem) %{ + match(Set dst (DecodeN (LoadN mem))); + predicate(_kids[0]->_leaf->as_Load()->is_unordered() && Universe::narrow_oop_shift() == 0); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// DecodeN (unscaled)" %} + size(4); + ins_encode( enc_lwz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Pointer +instruct loadP(iRegPdst dst, memoryAlg4 mem) %{ + match(Set dst (LoadP mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// ptr" %} + size(4); + ins_encode( enc_ld(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Pointer acquire. +instruct loadP_ac(iRegPdst dst, memoryAlg4 mem) %{ + match(Set dst (LoadP mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// ptr acquire\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_ld_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// LoadP + CastP2L +instruct loadP2X(iRegLdst dst, memoryAlg4 mem) %{ + match(Set dst (CastP2X (LoadP mem))); + predicate(_kids[0]->_leaf->as_Load()->is_unordered()); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// ptr + p2x" %} + size(4); + ins_encode( enc_ld(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load compressed klass pointer. +instruct loadNKlass(iRegNdst dst, memory mem) %{ + match(Set dst (LoadNKlass mem)); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $mem \t// compressed klass ptr" %} + size(4); + ins_encode( enc_lwz(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +//// Load compressed klass and decode it if narrow_klass_shift == 0. +//// TODO: will narrow_klass_shift ever be 0? +//instruct decodeNKlass2Klass(iRegPdst dst, memory mem) %{ +// match(Set dst (DecodeNKlass (LoadNKlass mem))); +// predicate(false /* TODO: PPC port Universe::narrow_klass_shift() == 0*); +// ins_cost(MEMORY_REF_COST); +// +// format %{ "LWZ $dst, $mem \t// DecodeNKlass (unscaled)" %} +// size(4); +// ins_encode( enc_lwz(dst, mem) ); +// ins_pipe(pipe_class_memory); +//%} + +// Load Klass Pointer +instruct loadKlass(iRegPdst dst, memoryAlg4 mem) %{ + match(Set dst (LoadKlass mem)); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// klass ptr" %} + size(4); + ins_encode( enc_ld(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Float +instruct loadF(regF dst, memory mem) %{ + match(Set dst (LoadF mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LFS $dst, $mem" %} + size(4); + ins_encode( enc_lfs(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Float acquire. +instruct loadF_ac(regF dst, memory mem) %{ + match(Set dst (LoadF mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LFS $dst, $mem \t// acquire\n\t" + "FCMPU cr0, $dst, $dst\n\t" + "BNE cr0, next\n" + "next:\n\t" + "ISYNC" %} + size(16); + ins_encode( enc_lfs_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Double - aligned +instruct loadD(regD dst, memory mem) %{ + match(Set dst (LoadD mem)); + predicate(n->as_Load()->is_unordered() || followed_by_acquire(n)); + ins_cost(MEMORY_REF_COST); + + format %{ "LFD $dst, $mem" %} + size(4); + ins_encode( enc_lfd(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Double - aligned acquire. +instruct loadD_ac(regD dst, memory mem) %{ + match(Set dst (LoadD mem)); + ins_cost(3*MEMORY_REF_COST); + + format %{ "LFD $dst, $mem \t// acquire\n\t" + "FCMPU cr0, $dst, $dst\n\t" + "BNE cr0, next\n" + "next:\n\t" + "ISYNC" %} + size(16); + ins_encode( enc_lfd_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Load Double - UNaligned +instruct loadD_unaligned(regD dst, memory mem) %{ + match(Set dst (LoadD_unaligned mem)); + // predicate(...) // Unaligned_ac is not needed (and wouldn't make sense). + ins_cost(MEMORY_REF_COST); + + format %{ "LFD $dst, $mem" %} + size(4); + ins_encode( enc_lfd(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +//----------Constants-------------------------------------------------------- + +// Load MachConstantTableBase: add hi offset to global toc. +// TODO: Handle hidden register r29 in bundler! +instruct loadToc_hi(iRegLdst dst) %{ + effect(DEF dst); + ins_cost(DEFAULT_COST); + + format %{ "ADDIS $dst, R29, DISP.hi \t// load TOC hi" %} + size(4); + ins_encode( enc_load_toc1(dst) ); + ins_pipe(pipe_class_default); +%} + +// Load MachConstantTableBase: add lo offset to global toc. +instruct loadToc_lo(iRegLdst dst, iRegLdst src) %{ + effect(DEF dst, USE src); + ins_cost(DEFAULT_COST); + + format %{ "ADDI $dst, $src, DISP.lo \t// load TOC lo" %} + size(4); + ins_encode( enc_load_toc2(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Load 16-bit integer constant 0xssss???? +instruct loadConI16(iRegIdst dst, immI16 src) %{ + match(Set dst src); + + format %{ "LI $dst, $src" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Load integer constant 0x????0000 +instruct loadConIhi16(iRegIdst dst, immIhi16 src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST); + + format %{ "LIS $dst, $src.hi" %} + size(4); + ins_encode( enc_lis_32(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Part 2 of loading 32 bit constant: hi16 is is src1 (properly shifted +// and sign extended), this adds the low 16 bits. +instruct loadConI32_lo16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "ORI $dst, $src1.hi, $src2.lo" %} + size(4); + ins_encode( enc_ori(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct loadConI_Ex(iRegIdst dst, immI src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST*2); + + expand %{ + // Would like to use $src$$constant. + immI16 srcLo %{ _opnds[1]->constant() %} + // srcHi can be 0000 if srcLo sign-extends to a negative number. + immIhi16 srcHi %{ _opnds[1]->constant() %} + iRegIdst tmpI; + loadConIhi16(tmpI, srcHi); + loadConI32_lo16(dst, tmpI, srcLo); + %} +%} + +// No constant pool entries required. +instruct loadConL16(iRegLdst dst, immL16 src) %{ + match(Set dst src); + + format %{ "LI $dst, $src \t// long" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Load long constant 0xssssssss????0000 +instruct loadConL32hi16(iRegLdst dst, immL32hi16 src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST); + + format %{ "LIS $dst, $src.hi \t// long" %} + size(4); + ins_encode( enc_lis_32(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// To load a 32 bit constant: merge lower 16 bits into already loaded +// high 16 bits. +instruct loadConL32_lo16(iRegLdst dst, iRegLsrc src1, immL16 src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "ORI $dst, $src1, $src2.lo" %} + size(4); + ins_encode( enc_ori(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Load 32-bit long constant +instruct loadConL32_Ex(iRegLdst dst, immL32 src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST*2); + + expand %{ + // Would like to use $src$$constant. + immL16 srcLo %{ _opnds[1]->constant() /*& 0x0000FFFFL */%} + // srcHi can be 0000 if srcLo sign-extends to a negative number. + immL32hi16 srcHi %{ _opnds[1]->constant() /*& 0xFFFF0000L */%} + iRegLdst tmpL; + loadConL32hi16(tmpL, srcHi); + loadConL32_lo16(dst, tmpL, srcLo); + %} +%} + +// Load long constant 0x????000000000000. +instruct loadConLhighest16_Ex(iRegLdst dst, immLhighest16 src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST); + + expand %{ + immL32hi16 srcHi %{ _opnds[1]->constant() >> 32 /*& 0xFFFF0000L */%} + immI shift32 %{ 32 %} + iRegLdst tmpL; + loadConL32hi16(tmpL, srcHi); + lshiftL_regL_immI(dst, tmpL, shift32); + %} +%} + +// Expand node for constant pool load: small offset. +instruct loadConL(iRegLdst dst, immL src, iRegLdst toc, immI isoop) %{ + effect(DEF dst, USE src, USE toc, USE isoop); + ins_cost(MEMORY_REF_COST); + + ins_num_consts(1); + // Needed so that CallDynamicJavaDirect can compute the address of this + // instruction for relocation. + ins_field_cbuf_insts_offset(int); + + format %{ "LD $dst, offset, $toc \t// load long(isoop=$isoop) $src from TOC" %} + size(4); + ins_encode( enc_load_long_constL(dst, src, toc, isoop) ); + ins_pipe(pipe_class_memory); +%} + +// Expand node for constant pool load: large offset. +instruct loadConL_hi(iRegLdst dst, immL src, iRegLdst toc, immI isoop) %{ + effect(DEF dst, USE src, USE toc, USE isoop); + predicate(false); + + ins_num_consts(1); + ins_field_const_toc_offset(int); + // Needed so that CallDynamicJavaDirect can compute the address of this + // instruction for relocation. + ins_field_cbuf_insts_offset(int); + + format %{ "ADDIS $dst, $toc, offset \t// load long(isoop=$isoop) $src from TOC (hi)" %} + size(4); + ins_encode( enc_load_long_constL_hi(dst, toc, src, isoop) ); + ins_pipe(pipe_class_default); +%} + +// Expand node for constant pool load: large offset. +instruct loadConL_lo(iRegLdst dst, immL src, iRegLdst base, immI isoop) %{ + effect(DEF dst, USE src, USE base, USE isoop); + predicate(false); + + ins_field_const_toc_offset_hi_node(loadConL_hiNode*); + + format %{ "LD $dst, offset, $base \t// load long(isoop=$isoop) $src from TOC (lo)" %} + size(4); + ins_encode( enc_load_long_const_lo(dst, src, base, isoop) ); + ins_pipe(pipe_class_memory); +%} + +// Load long constant from constant table. Expand in case of +// offset > 16 bit is needed. +// Adlc adds toc node MachConstantTableBase. +instruct loadConL_Ex(iRegLdst dst, immL src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, offset, $constanttablebase\t// load long $src from table, late expanded" %} + lateExpand( lateExpand_load_long_constant(dst, src, constanttablebase) ); +%} + +// Load NULL as compressed oop. +instruct loadConN0(iRegNdst dst, immN_0 src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST); + + format %{ "LI $dst, $src \t// compressed ptr" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Load hi part of compressed oop constant. +instruct loadConN1(iRegNdst dst, immN src) %{ + effect(DEF dst, USE src); + ins_cost(DEFAULT_COST); + + format %{ "LIS $dst, $src \t// narrow oop hi" %} + size(4); + ins_encode( enc_load_con_narrow1(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Add lo part of compressed oop constant to already loaded hi part. +instruct loadConN2(iRegNdst dst, iRegNsrc src1, immN src2) %{ + effect(DEF dst, USE src1, USE src2); + ins_cost(DEFAULT_COST); + + format %{ "ADDI $dst, $src1, $src2 \t// narrow oop lo" %} + size(4); + ins_encode( enc_load_con_narrow2(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Needed to late expand loadConN: ConN is loaded as ConI +// leaving the upper 32 bits with sign-extension bits. +// This clears these bits: dst = src & 0xFFFFFFFF. +// TODO: Eventually call this maskN_regN_FFFFFFFF. +instruct clearMs32b(iRegNdst dst, iRegNsrc src) %{ + effect(DEF dst, USE src); + predicate(false); + + format %{ "MASK $dst, $src, 0xFFFFFFFF" %} // mask + size(4); + ins_encode( enc_clrldi(dst, src, 0x20) ); + ins_pipe(pipe_class_default); +%} + +// Loading ConN must be late expanded so that edges between +// the nodes are safe. They may not interfere with a safepoint. +// GL TODO: This needs three instructions: better put this into the constant pool. +instruct loadConN_Ex(iRegNdst dst, immN src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST*2); + + format %{ "LoadN $dst, $src \t// late expanded" %} // mask + lateExpand( lateExpand_load_conN(dst, src) ); +%} + +// 0x1 is used in object initialization (initial object header). +// No constant pool entries required. +instruct loadConP0or1(iRegPdst dst, immP_0or1 src) %{ + match(Set dst src); + + format %{ "LI $dst, $src \t// ptr" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Expand node for constant pool load: small offset. +// The match rule is needed to generate the correct bottom_type(), +// however this node should never match. The use of predicate is not +// possible since ADLC forbids predicates for chain rules. The higher +// costs do not prevent matching in this case. For that reason the +// operand immP_NM with predicate(false) is used. +instruct loadConP(iRegPdst dst, immP_NM src, iRegLdst toc, immI isoop) %{ + match(Set dst src); + effect(TEMP toc, USE isoop); + + ins_num_consts(1); + + format %{ "LD $dst, offset, $toc \t// load ptr(isoop=$isoop) $src from TOC" %} + size(4); + ins_encode( enc_load_long_constP(dst, src, toc, isoop) ); + ins_pipe(pipe_class_memory); +%} + +// Expand node for constant pool load: large offset. +instruct loadConP_hi(iRegPdst dst, immP_NM src, iRegLdst toc, immI isoop) %{ + effect(DEF dst, USE src, USE toc, USE isoop); + predicate(false); + + ins_num_consts(1); + ins_field_const_toc_offset(int); + + format %{ "ADDIS $dst, $toc, offset \t// load ptr(isoop=$isoop) $src from TOC (hi)" %} + size(4); + ins_encode( enc_load_long_constP_hi(dst, toc, src, isoop) ); + ins_pipe(pipe_class_default); +%} + +// Expand node for constant pool load: large offset. +instruct loadConP_lo(iRegPdst dst, immP_NM src, iRegLdst base, immI isoop) %{ + match(Set dst src); + effect(TEMP base, USE isoop); + + ins_field_const_toc_offset_hi_node(loadConP_hiNode*); + + format %{ "LD $dst, offset, $base \t// load ptr(isoop=$isoop) $src from TOC (lo)" %} + size(4); + ins_encode( enc_load_long_const_lo(dst, src, base, isoop) ); + ins_pipe(pipe_class_memory); +%} + +// Load pointer constant from constant table. Expand in case an +// offset > 16 bit is needed. +// Adlc adds toc node MachConstantTableBase. +instruct loadConP_Ex(iRegPdst dst, immP src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + // This rule does not use "expand" because then + // the result type is not known to be an Oop. An ADLC + // enhancement will be needed to make that work - not worth it! + + // If this instruction rematerializes, it prolongs the live range + // of the toc node, causing illegal graphs. + // assert(edge_from_to(_reg_node[reg_lo],def)) fails in verify_good_schedule(). + ins_cannot_rematerialize(true); + + format %{ "LD $dst, offset, $constanttablebase \t// load ptr $src from table, late expanded" %} + lateExpand( lateExpand_load_ptr_constant(dst, src, constanttablebase) ); +%} + +// Expand node for constant pool load: small offset. +instruct loadConF(regF dst, immF src, iRegLdst toc) %{ + effect(DEF dst, USE src, USE toc); + ins_cost(MEMORY_REF_COST); + + ins_num_consts(1); + + format %{ "LFS $dst, offset, $toc \t// load float $src from TOC" %} + size(4); + ins_encode( enc_load_float_const(dst, src, toc) ); + ins_pipe(pipe_class_memory); +%} + +// Expand node for constant pool load: large offset. +instruct loadConFComp(regF dst, immF src, iRegLdst toc) %{ + effect(DEF dst, USE src, USE toc); + ins_cost(MEMORY_REF_COST); + + ins_num_consts(1); + + format %{ "ADDIS $toc, $toc, offset_hi\n\t" + "LFS $dst, offset_lo, $toc \t// load float $src from TOC (hi/lo)\n\t" + "ADDIS $toc, $toc, -offset_hi"%} + size(12); + ins_encode( enc_load_float_const_comp(dst, src, toc) ); + ins_pipe(pipe_class_memory); +%} + +// Adlc adds toc node MachConstantTableBase. +instruct loadConF_Ex(regF dst, immF src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + // See loadConP. + ins_cannot_rematerialize(true); + + format %{ "LFS $dst, offset, $constanttablebase \t// load $src from table, late expanded" %} + lateExpand( lateExpand_load_float_constant(dst, src, constanttablebase) ); +%} + +// Expand node for constant pool load: small offset. +instruct loadConD(regD dst, immD src, iRegLdst toc) %{ + effect(DEF dst, USE src, USE toc); + ins_cost(MEMORY_REF_COST); + + ins_num_consts(1); + + format %{ "LFD $dst, offset, $toc \t// load double $src from TOC" %} + size(4); + ins_encode( enc_load_double_const(dst, src, toc) ); + ins_pipe(pipe_class_memory); +%} + +// Expand node for constant pool load: large offset. +instruct loadConDComp(regD dst, immD src, iRegLdst toc) %{ + effect(DEF dst, USE src, USE toc); + ins_cost(MEMORY_REF_COST); + + ins_num_consts(1); + + format %{ "ADDIS $toc, $toc, offset_hi\n\t" + "LFD $dst, offset_lo, $toc \t// load double $src from TOC (hi/lo)\n\t" + "ADDIS $toc, $toc, -offset_hi" %} + size(12); + ins_encode( enc_load_double_const_comp(dst, src, toc) ); + ins_pipe(pipe_class_memory); +%} + +// Adlc adds toc node MachConstantTableBase. +instruct loadConD_Ex(regD dst, immD src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + // See loadConP. + ins_cannot_rematerialize(true); + + format %{ "ConD $dst, offset, $constanttablebase \t// load $src from table, late expanded" %} + lateExpand( lateExpand_load_double_constant(dst, src, constanttablebase) ); +%} + +// Prefetch instructions. +// Must be safe to execute with invalid address (cannot fault). + +instruct prefetchr(indirectMemory mem, iRegLsrc src) %{ + match(PrefetchRead (AddP mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem, 0, $src \t// Prefetch read-many" %} + size(4); + ins_encode( enc_mem_load_prefetch(mem, src) ); + ins_pipe(pipe_class_memory); +%} + +instruct prefetchr_no_offset(indirectMemory mem) %{ + match(PrefetchRead mem); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem" %} + size(4); + ins_encode( enc_mem_load_prefetch_no_offset(mem) ); + ins_pipe(pipe_class_memory); +%} + +instruct prefetchw(indirectMemory mem, iRegLsrc src) %{ + match(PrefetchWrite (AddP mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many (and read)" %} + size(4); + ins_encode( enc_mem_store_prefetch(mem, src) ); + ins_pipe(pipe_class_memory); +%} + +instruct prefetchw_no_offset(indirectMemory mem) %{ + match(PrefetchWrite mem); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem" %} + size(4); + ins_encode( enc_mem_store_prefetch_no_offset(mem) ); + ins_pipe(pipe_class_memory); +%} + +// Special prefetch versions which use the dcbz instruction. +instruct prefetch_alloc_zero(indirectMemory mem, iRegLsrc src) %{ + match(PrefetchAllocation (AddP mem src)); + predicate(AllocatePrefetchStyle==3); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many with zero" %} + size(4); + ins_encode( enc_mem_store_prefetch_zero(mem, src) ); + ins_pipe(pipe_class_memory); +%} + +instruct prefetch_alloc_zero_no_offset(indirectMemory mem) %{ + match(PrefetchAllocation mem); + predicate(AllocatePrefetchStyle==3); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem, 2 \t// Prefetch write-many with zero" %} + size(4); + ins_encode( enc_mem_store_prefetch_zero_no_offset(mem) ); + ins_pipe(pipe_class_memory); +%} + +instruct prefetch_alloc(indirectMemory mem, iRegLsrc src) %{ + match(PrefetchAllocation (AddP mem src)); + predicate(AllocatePrefetchStyle!=3); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem, 2, $src \t// Prefetch write-many" %} + size(4); + ins_encode( enc_mem_store_prefetch(mem, src) ); + ins_pipe(pipe_class_memory); +%} + +instruct prefetch_alloc_no_offset(indirectMemory mem) %{ + match(PrefetchAllocation mem); + predicate(AllocatePrefetchStyle!=3); + ins_cost(MEMORY_REF_COST); + + format %{ "PREFETCH $mem, 2 \t// Prefetch write-many" %} + size(4); + ins_encode( enc_mem_store_prefetch_no_offset(mem) ); + ins_pipe(pipe_class_memory); +%} + +//----------Store Instructions------------------------------------------------- + +// Store Byte +instruct storeB(memory mem, iRegIsrc src) %{ + match(Set mem (StoreB mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STB $src, $mem \t// byte" %} + size(4); + ins_encode( enc_stb(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Store Char/Short +instruct storeC(memory mem, iRegIsrc src) %{ + match(Set mem (StoreC mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STH $src, $mem \t// short" %} + size(4); + ins_encode( enc_sth(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Store Integer +instruct storeI(memory mem, iRegIsrc src) %{ + match(Set mem (StoreI mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STW $src, $mem" %} + size(4); + ins_encode( enc_stw(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// ConvL2I + StoreI. +instruct storeI_convL2I(memory mem, iRegLsrc src) %{ + match(Set mem (StoreI mem (ConvL2I src))); + ins_cost(MEMORY_REF_COST); + + format %{ "STW l2i($src), $mem" %} + size(4); + ins_encode( enc_stw(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Store Long +instruct storeL(memoryAlg4 mem, iRegLsrc src) %{ + match(Set mem (StoreL mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STD $src, $mem \t// long" %} + size(4); + ins_encode( enc_std(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Store super word nodes. + +// Store Aligned Packed Byte long register to memory +instruct storeA8B(memoryAlg4 mem, iRegLsrc src) %{ + predicate(n->as_StoreVector()->memory_size() == 8); + match(Set mem (StoreVector mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STD $mem, $src \t// packed8B" %} + size(4); + ins_encode( enc_std(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Store Compressed Oop +instruct storeN(memory dst, iRegN_P2N src) %{ + match(Set dst (StoreN dst src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STW $src, $dst \t// compressed oop" %} + size(4); + ins_encode( enc_stw(src, dst) ); + ins_pipe(pipe_class_memory); +%} + +// Store Pointer +instruct storeP(memoryAlg4 dst, iRegPsrc src) %{ + match(Set dst (StoreP dst src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STD $src, $dst \t// ptr" %} + size(4); + ins_encode( enc_std(src, dst) ); + ins_pipe(pipe_class_memory); +%} + +// Store Float +instruct storeF(memory mem, regF src) %{ + match(Set mem (StoreF mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STFS $src, $mem" %} + size(4); + ins_encode( enc_stfs(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +// Store Double +instruct storeD(memory mem, regD src) %{ + match(Set mem (StoreD mem src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STFD $src, $mem" %} + size(4); + ins_encode( enc_stfd(src, mem) ); + ins_pipe(pipe_class_memory); +%} + +//----------Store Instructions With Zeros-------------------------------------- + +// Card-mark for CMS garbage collection. +// This cardmark does an optimization so that it must not always +// do a releasing store. For this, it gets the address of +// CMSCollectorCardTableModRefBSExt::_requires_release as input. +// (Using releaseFieldAddr in the match rule is a hack.) +instruct storeCM_CMS(memory mem, iRegLdst releaseFieldAddr) %{ + match(Set mem (StoreCM mem releaseFieldAddr)); + predicate(false); + ins_cost(MEMORY_REF_COST); + + // See loadConP. + ins_cannot_rematerialize(true); + + format %{ "STB #0, $mem \t// CMS card-mark byte (must be 0!), checking requires_release in [$releaseFieldAddr]" %} + ins_encode( enc_cms_card_mark(mem, releaseFieldAddr) ); + ins_pipe(pipe_class_memory); +%} + +// Card-mark for CMS garbage collection. +// This cardmark does an optimization so that it must not always +// do a releasing store. For this, it needs the constant address of +// CMSCollectorCardTableModRefBSExt::_requires_release. +// This constant address is split off here by expand so we can use +// adlc / matcher functionality to load it from the constant section. +instruct storeCM_CMS_ExEx(memory mem, immI_0 zero) %{ + match(Set mem (StoreCM mem zero)); + predicate(UseConcMarkSweepGC); + + expand %{ + immL baseImm %{ 0 /* TODO: PPC port (jlong)CMSCollectorCardTableModRefBSExt::requires_release_address() */ %} + iRegLdst releaseFieldAddress; + loadConL_Ex(releaseFieldAddress, baseImm); + storeCM_CMS(mem, releaseFieldAddress); + %} +%} + +instruct storeCM_G1(memory mem, immI_0 zero) %{ + match(Set mem (StoreCM mem zero)); + predicate(UseG1GC); + ins_cost(MEMORY_REF_COST); + + ins_cannot_rematerialize(true); + + format %{ "STB #0, $mem \t// CMS card-mark byte store (G1)" %} + size(8); + ins_encode( enc_stb0(mem) ); + ins_pipe(pipe_class_memory); +%} + +// Convert oop pointer into compressed form. + +// Nodes for late expand. + +// Shift node for expand. +instruct encodeP_shift(iRegNdst dst, iRegNsrc src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (EncodeP src)); + predicate(false); + + format %{ "SRDI $dst, $src, 3 \t// encode" %} + size(4); + ins_encode( enc_srdi(dst, src, (Universe::narrow_oop_shift())) ); + ins_pipe(pipe_class_default); +%} + +// Add node for expand. +instruct encodeP_sub(iRegPdst dst, iRegPdst src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (EncodeP src)); + predicate(false); + + format %{ "SUB $dst, $src, oop_base \t// encode" %} + size(4); + ins_encode( enc_subf(dst, R30, src) ); + ins_pipe(pipe_class_default); +%} + +// Conditional sub base. +instruct cond_sub_base(iRegNdst dst, flagsReg crx, iRegPsrc src1) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (EncodeP (Binary crx src1))); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "BEQ $crx, done\n\t" + "SUB $dst, $src1, R30 \t// encode: subtract base if != NULL\n" + "done:" %} + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cond_sub_base(dst,crx, src1) ); + ins_pipe(pipe_class_default); +%} + +// Power 7 can use isel instruction +instruct cond_set_0_oop(iRegNdst dst, flagsReg crx, iRegPsrc src1) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (EncodeP (Binary crx src1))); + predicate(false); + + format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// encode: preserve 0" %} + size(4); + ins_encode( enc_isel_set_to_0_or_value(dst, crx, src1) ); + ins_pipe(pipe_class_default); +%} + +// base != 0 +// 32G aligned narrow oop base. +instruct encodeP_32GAligned(iRegNdst dst, flagsReg crx, iRegPsrc src) %{ + match(Set dst (EncodeP src)); + predicate(false /* TODO: PPC port Universe::narrow_oop_base_disjoint()*/); + + format %{ "EXTRDI $dst, $src, #32, #3 \t// encode with 32G aligned base" %} + size(4); + ins_encode( enc_extrdi_encode(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// shift != 0, base != 0 +instruct encodeP_Ex(iRegNdst dst, flagsReg crx, iRegPsrc src) %{ + match(Set dst (EncodeP src)); + effect(TEMP crx); + predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull && + Universe::narrow_oop_shift() != 0 && + true /* TODO: PPC port Universe::narrow_oop_base_overlaps()*/); + + format %{ "EncodeP $dst, $crx, $src \t// late expanded" %} + lateExpand( lateExpand_encode_oop(dst, src, crx)); +%} + +// shift != 0, base != 0 +instruct encodeP_not_null_Ex(iRegNdst dst, iRegPsrc src) %{ + match(Set dst (EncodeP src)); + predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull && + Universe::narrow_oop_shift() != 0 && + true /* TODO: PPC port Universe::narrow_oop_base_overlaps()*/); + + format %{ "EncodeP $dst, $src\t// $src != Null, late expanded" %} + lateExpand( lateExpand_encode_oop_not_null(dst, src) ); +%} + +// shift != 0, base == 0 +instruct encodeP_not_null_base_null(iRegNdst dst, iRegPsrc src) %{ + match(Set dst (EncodeP src)); + predicate(Universe::narrow_oop_shift() != 0 && + Universe::narrow_oop_base() ==0); + + format %{ "SRDI $dst, $src, #3 \t// encodeP, $src != NULL" %} + size(4); + ins_encode( enc_srdi(dst, src, (Universe::narrow_oop_shift())) ); + ins_pipe(pipe_class_default); +%} + +// Compressed OOPs with narrow_oop_shift == 0. +// shift == 0, base == 0 +instruct encodeP_narrow_oop_shift_0(iRegNdst dst, iRegPsrc src) %{ + match(Set dst (EncodeP src)); + predicate(Universe::narrow_oop_shift() == 0); + + format %{ "MR $dst, $src \t// Ptr->Narrow" %} + // variable size, 0 or 4. + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Decode nodes. + +// Shift node for expand. +instruct decodeN_shift(iRegPdst dst, iRegPsrc src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (DecodeN src)); + predicate(false); + + format %{ "SLDI $dst, $src, #3 \t// DecodeN" %} + size(4); + ins_encode( enc_sldi(dst, src, (Universe::narrow_oop_shift())) ); + ins_pipe(pipe_class_default); +%} + +// Add node for expand. +instruct decodeN_add(iRegPdst dst, iRegPdst src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (DecodeN src)); + predicate(false); + + format %{ "ADD $dst, $src, R30 \t// DecodeN, add oop base" %} + size(4); + ins_encode( enc_add(dst, src, R30) ); + ins_pipe(pipe_class_default); +%} + +// conditianal add base for expand +instruct cond_add_base(iRegPdst dst, flagsReg crx, iRegPsrc src1) %{ + // The match rule is needed to make it a 'MachTypeNode'! + // NOTICE that the rule is nonsense - we just have to make sure that: + // - _matrule->_rChild->_opType == "DecodeN" (see InstructForm::captures_bottom_type() in formssel.cpp) + // - we have to match 'crx' to avoid an "illegal USE of non-input: flagsReg crx" error in ADLC. + match(Set dst (DecodeN (Binary crx src1))); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "BEQ $crx, done\n\t" + "ADD $dst, $src1, R30 \t// DecodeN: add oop base if $src1 != NULL\n" + "done:" %} + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cond_add_base(dst, crx, src1) ); + ins_pipe(pipe_class_default); +%} + +instruct cond_set_0_ptr(iRegPdst dst, flagsReg crx, iRegPsrc src1) %{ + // The match rule is needed to make it a 'MachTypeNode'! + // NOTICE that the rule is nonsense - we just have to make sure that: + // - _matrule->_rChild->_opType == "DecodeN" (see InstructForm::captures_bottom_type() in formssel.cpp) + // - we have to match 'crx' to avoid an "illegal USE of non-input: flagsReg crx" error in ADLC. + match(Set dst (DecodeN (Binary crx src1))); + predicate(false); + + format %{ "CMOVE $dst, $crx eq, 0, $src1 \t// decode: preserve 0" %} + size(4); + ins_encode( enc_isel_set_to_0_or_value(dst, crx, src1) ); + ins_pipe(pipe_class_default); +%} + +// shift != 0, base != 0 +instruct decodeN_Ex(iRegPdst dst, iRegNsrc src, flagsReg crx) %{ + match(Set dst (DecodeN src)); + predicate((n->bottom_type()->is_oopptr()->ptr() != TypePtr::NotNull && + n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant) && + Universe::narrow_oop_shift() != 0 && + Universe::narrow_oop_base() != 0); + effect(TEMP crx); + + format %{ "DecodeN $dst, $src \t// Kills $crx, late expanded" %} + lateExpand( lateExpand_decode_oop(dst, src, crx) ); +%} + +// shift != 0, base == 0 +instruct decodeN_nullBase(iRegPdst dst, iRegNsrc src) %{ + match(Set dst (DecodeN src)); + predicate(Universe::narrow_oop_shift() != 0 && + Universe::narrow_oop_base() == 0); + + format %{ "SLDI $dst, $src, #3 \t// DecodeN (zerobased)" %} + size(4); + ins_encode( enc_sldi(dst, src, (Universe::narrow_oop_shift()))); + ins_pipe(pipe_class_default); +%} + +// src != 0, shift != 0, base != 0 +instruct decodeN_notNull_addBase_Ex(iRegPdst dst, iRegNsrc src) %{ + match(Set dst (DecodeN src)); + predicate((n->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull || + n->bottom_type()->is_oopptr()->ptr() == TypePtr::Constant) && + Universe::narrow_oop_shift() != 0 && + Universe::narrow_oop_base() != 0); + + format %{ "DecodeN $dst, $src \t// $src != NULL, late expanded" %} + lateExpand( lateExpand_decode_oop_not_null(dst, src)); +%} + +// Compressed OOPs with narrow_oop_shift == 0. +instruct decodeN_unscaled(iRegPdst dst, iRegNsrc src) %{ + match(Set dst (DecodeN src)); + predicate(Universe::narrow_oop_shift() == 0); + ins_cost(DEFAULT_COST); + + format %{ "MR $dst, $src \t// DecodeN (unscaled)" %} + // variable size, 0 or 4. + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Convert compressed oop into int for vectors alignment masking. +instruct decodeN2I_unscaled(iRegIdst dst, iRegNsrc src) %{ + match(Set dst (ConvL2I (CastP2X (DecodeN src)))); + predicate(Universe::narrow_oop_shift() == 0); + ins_cost(DEFAULT_COST); + + format %{ "MR $dst, $src \t// (int)DecodeN (unscaled)" %} + // variable size, 0 or 4. + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +//----------MemBar Instructions----------------------------------------------- +// Memory barrier flavors + +// We don't need this as we do load.acq. +//instruct membar_acquire() %{ +// match(MemBarAcquire); +// ins_cost(4*MEMORY_REF_COST); +// +// format %{ "MEMBAR-acquire" %} +// size(4); +// ins_encode( enc_membar_acquire ); +// ins_pipe(pipe_class_default); +//%} + +instruct unnecessary_membar_acquire() %{ + match(MemBarAcquire); + ins_cost(0); + + format %{ " -- \t// redundant MEMBAR-acquire - empty" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +instruct membar_acquire_lock() %{ + match(MemBarAcquireLock); + ins_cost(0); + + format %{ " -- \t// redundant MEMBAR-acquire - empty (acquire as part of CAS in prior FastLock)" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +instruct membar_release() %{ + match(MemBarRelease); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-release" %} + size(4); + ins_encode( enc_membar_release ); + ins_pipe(pipe_class_default); +%} + +instruct membar_release_lock() %{ + match(MemBarReleaseLock); + ins_cost(0); + + format %{ " -- \t// redundant MEMBAR-release - empty (release in FastUnlock)" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +instruct membar_volatile() %{ + match(MemBarVolatile); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-volatile" %} + size(4); + ins_encode( enc_membar_volatile ); + ins_pipe(pipe_class_default); +%} + +// This optimization is wrong on PPC. The following pattern is not supported: +// MemBarVolatile +// ^ ^ +// | | +// CtrlProj MemProj +// ^ ^ +// | | +// | Load +// | +// MemBarVolatile +// +// The first MemBarVolatile could get optimized out! According to +// Vladimir, this pattern can not occur on Oracle platforms. +// However, it does occur on PPC64 (because of membars in +// inline_unsafe_load_store). +// +// Add this node again if we found a good solution for inline_unsafe_load_store(). +// Don't forget to look at the implementation of post_store_load_barrier again, +// we did other fixes in that method. +//instruct unnecessary_membar_volatile() %{ +// match(MemBarVolatile); +// predicate(Matcher::post_store_load_barrier(n)); +// ins_cost(0); +// +// format %{ " -- \t// redundant MEMBAR-volatile - empty" %} +// size(0); +// ins_encode( /*empty*/ ); +// ins_pipe(pipe_class_default); +//%} + +instruct membar_CPUOrder() %{ + match(MemBarCPUOrder); + ins_cost(0); + + format %{ " -- \t// MEMBAR-CPUOrder - empty: PPC64 processors are self-consistent." %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +// New MembarNode in HS23: Replaces lwsync in InitializeNode +instruct membar_storestore() %{ + match(MemBarStoreStore); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-store-store" %} + size(4); + ins_encode( enc_membar_release ); + ins_pipe(pipe_class_default); +%} + +//----------Conditional Move--------------------------------------------------- + +// Cmove using isel. +instruct cmovI_reg_isel(cmpOp cmp, flagsReg crx, iRegIdst dst, iRegIsrc src) %{ + match(Set dst (CMoveI (Binary cmp crx) (Binary dst src))); + predicate(VM_Version::has_isel()); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + size(4); + ins_encode( enc_isel(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovI_reg(cmpOp cmp, flagsReg crx, iRegIdst dst, iRegIsrc src) %{ + match(Set dst (CMoveI (Binary cmp crx) (Binary dst src))); + predicate(!VM_Version::has_isel()); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_reg(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovI_imm(cmpOp cmp, flagsReg crx, iRegIdst dst, immI16 src) %{ + match(Set dst (CMoveI (Binary cmp crx) (Binary dst src))); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_imm(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +// Cmove using isel. +instruct cmovL_reg_isel(cmpOp cmp, flagsReg crx, iRegLdst dst, iRegLsrc src) %{ + match(Set dst (CMoveL (Binary cmp crx) (Binary dst src))); + predicate(VM_Version::has_isel()); + ins_cost(DEFAULT_COST); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + size(4); + ins_encode( enc_isel(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovL_reg(cmpOp cmp, flagsReg crx, iRegLdst dst, iRegLsrc src) %{ + match(Set dst (CMoveL (Binary cmp crx) (Binary dst src))); + predicate(!VM_Version::has_isel()); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_reg(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovL_imm(cmpOp cmp, flagsReg crx, iRegLdst dst, immL16 src) %{ + match(Set dst (CMoveL (Binary cmp crx) (Binary dst src))); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_imm(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +// Cmove using isel. +instruct cmovN_reg_isel(cmpOp cmp, flagsReg crx, iRegNdst dst, iRegNsrc src) %{ + match(Set dst (CMoveN (Binary cmp crx) (Binary dst src))); + predicate(VM_Version::has_isel()); + ins_cost(DEFAULT_COST); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + size(4); + ins_encode( enc_isel(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +// Conditional move for RegN. Only cmov(reg, reg). +instruct cmovN_reg(cmpOp cmp, flagsReg crx, iRegNdst dst, iRegNsrc src) %{ + match(Set dst (CMoveN (Binary cmp crx) (Binary dst src))); + predicate(!VM_Version::has_isel()); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_reg(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovN_imm(cmpOp cmp, flagsReg crx, iRegNdst dst, immN_0 src) %{ + match(Set dst (CMoveN (Binary cmp crx) (Binary dst src))); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_imm(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +// Cmove using isel. +instruct cmovP_reg_isel(cmpOp cmp, flagsReg crx, iRegPdst dst, iRegPsrc src) %{ + match(Set dst (CMoveP (Binary cmp crx) (Binary dst src))); + predicate(VM_Version::has_isel()); + ins_cost(DEFAULT_COST); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + size(4); + ins_encode( enc_isel(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovP_reg(cmpOp cmp, flagsReg crx, iRegPdst dst, iRegP_N2P src) %{ + match(Set dst (CMoveP (Binary cmp crx) (Binary dst src))); + predicate(!VM_Version::has_isel()); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_reg(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovP_imm(cmpOp cmp, flagsReg crx, iRegPdst dst, immP_0 src) %{ + match(Set dst (CMoveP (Binary cmp crx) (Binary dst src))); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_imm(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovF_reg(cmpOp cmp, flagsReg crx, regF dst, regF src) %{ + match(Set dst (CMoveF (Binary cmp crx) (Binary dst src))); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmovef_reg(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovD_reg(cmpOp cmp, flagsReg crx, regD dst, regD src) %{ + match(Set dst (CMoveD (Binary cmp crx) (Binary dst src))); + ins_cost(DEFAULT_COST+BRANCH_COST); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmovef_reg(dst, crx, src, cmp) ); + ins_pipe(pipe_class_default); +%} + +//----------Conditional_store-------------------------------------------------- +// Conditional-store of the updated heap-top. +// Used during allocation of the shared heap. +// Sets flags (EQ) on success. Implemented with a CASA on Sparc. + +// As compareAndSwapL, but return flag register instead of boolean value in +// int register. +// Used by sun/misc/AtomicLongCSImpl.java. +// Mem_ptr must be a memory operand, else this node does not get +// Flag_needs_anti_dependence_check set by adlc. If this is not set this node +// can be rematerialized which leads to errors. +instruct storeLConditional_regP_regL_regL(flagsReg crx, indirect mem_ptr, iRegLsrc oldVal, iRegLsrc newVal) %{ + match(Set crx (StoreLConditional mem_ptr (Binary oldVal newVal))); + format %{ "CMPXCHGD if ($crx = ($oldVal == *$mem_ptr)) *mem_ptr = $newVal; as bool" %} + ins_encode( enc_storeConditionalLP(crx, mem_ptr, oldVal, newVal) ); + ins_pipe(pipe_class_default); +%} + +// As compareAndSwapP, but return flag register instead of boolean value in +// int register. +// This instruction is matched if UseTLAB is off. +// Mem_ptr must be a memory operand, else this node does not get +// Flag_needs_anti_dependence_check set by adlc. If this is not set this node +// can be rematerialized which leads to errors. +instruct storePConditional_regP_regP_regP(flagsReg crx, indirect mem_ptr, iRegPsrc oldVal, iRegPsrc newVal) %{ + match(Set crx (StorePConditional mem_ptr (Binary oldVal newVal))); + format %{ "CMPXCHGD if ($crx = ($oldVal == *$mem_ptr)) *mem_ptr = $newVal; as bool" %} + ins_encode( enc_storeConditionalLP(crx, mem_ptr, oldVal, newVal) ); + ins_pipe(pipe_class_default); +%} + +// Implement LoadPLocked. Must be ordered against changes of the memory location +// by storePConditional. +// Don't know whether this is ever used. +instruct loadPLocked(iRegPdst dst, memory mem) %{ + match(Set dst (LoadPLocked mem)); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $mem \t// loadPLocked\n\t" + "TWI $dst\n\t" + "ISYNC" %} + size(12); + ins_encode( enc_ld_ac(dst, mem) ); + ins_pipe(pipe_class_memory); +%} + +//----------Compare-And-Swap--------------------------------------------------- + +// CompareAndSwap{P,I,L} have more than one output, therefore "CmpI +// (CompareAndSwap ...)" or "If (CmpI (CompareAndSwap ..))" cannot be +// matched. + +instruct compareAndSwapI_regP_regI_regI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src1, iRegIsrc src2) %{ + match(Set res (CompareAndSwapI mem_ptr (Binary src1 src2))); + format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_compareAndSwapI(res, mem_ptr, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct compareAndSwapN_regP_regN_regN(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2) %{ + match(Set res (CompareAndSwapN mem_ptr (Binary src1 src2))); + format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_compareAndSwapN(res, mem_ptr, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct compareAndSwapL_regP_regL_regL(iRegIdst res, iRegPdst mem_ptr, iRegLsrc src1, iRegLsrc src2) %{ + match(Set res (CompareAndSwapL mem_ptr (Binary src1 src2))); + format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_compareAndSwapLP(res, mem_ptr, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct compareAndSwapP_regP_regP_regP(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2) %{ + match(Set res (CompareAndSwapP mem_ptr (Binary src1 src2))); + format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_compareAndSwapLP(res, mem_ptr, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct getAndAddI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src) %{ + match(Set res (GetAndAddI mem_ptr src)); + format %{ "GetAndAddI $res, $mem_ptr, $src" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_GetAndAddI(res, mem_ptr, src) ); + ins_pipe(pipe_class_default); +%} + +instruct getAndAddL(iRegLdst res, iRegPdst mem_ptr, iRegLsrc src) %{ + match(Set res (GetAndAddL mem_ptr src)); + format %{ "GetAndAddL $res, $mem_ptr, $src" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_GetAndAddL(res, mem_ptr, src) ); + ins_pipe(pipe_class_default); +%} + +instruct getAndSetI(iRegIdst res, iRegPdst mem_ptr, iRegIsrc src) %{ + match(Set res (GetAndSetI mem_ptr src)); + format %{ "GetAndSetI $res, $mem_ptr, $src" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_GetAndSetI(res, mem_ptr, src) ); + ins_pipe(pipe_class_default); +%} + +instruct getAndSetL(iRegLdst res, iRegPdst mem_ptr, iRegLsrc src) %{ + match(Set res (GetAndSetL mem_ptr src)); + format %{ "GetAndSetL $res, $mem_ptr, $src" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_GetAndSetL(res, mem_ptr, src) ); + ins_pipe(pipe_class_default); +%} + +instruct getAndSetP(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src) %{ + match(Set res (GetAndSetP mem_ptr src)); + format %{ "GetAndSetP $res, $mem_ptr, $src" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_GetAndSetL(res, mem_ptr, src) ); + ins_pipe(pipe_class_default); +%} + +instruct getAndSetN(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src) %{ + match(Set res (GetAndSetN mem_ptr src)); + format %{ "GetAndSetN $res, $mem_ptr, $src" %} + // Variable size: instruction count smaller if regs are disjoint. + ins_encode( enc_GetAndSetI(res, mem_ptr, src) ); + ins_pipe(pipe_class_default); +%} + +//----------Arithmetic Instructions-------------------------------------------- +// Addition Instructions + +// PPC has no instruction setting overflow of 32-bit integer. +//instruct addExactI_rReg(rarg4RegI dst, rRegI src, flagsReg cr) %{ +// match(AddExactI dst src); +// effect(DEF cr); +// +// format %{ "ADD $dst, $dst, $src \t// addExact int, sets $cr" %} +// ins_encode( enc_add(dst, dst, src) ); +// ins_pipe(pipe_class_default); +//%} + +// Register Addition +instruct addI_reg_reg(iRegIdst dst, iRegIsrc_iRegL2Isrc src1, iRegIsrc_iRegL2Isrc src2) %{ + match(Set dst (AddI src1 src2)); + format %{ "ADD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_add(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Expand does not work with above instruct. (??) +instruct addI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // no match-rule + effect(DEF dst, USE src1, USE src2); + format %{ "ADD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_add(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct tree_addI_addI_addI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, iRegIsrc src3, iRegIsrc src4) %{ + match(Set dst (AddI (AddI (AddI src1 src2) src3) src4)); + ins_cost(DEFAULT_COST*3); + + expand %{ + // FIXME: we should do this in the ideal world. + iRegIdst tmp1; + iRegIdst tmp2; + addI_reg_reg(tmp1, src1, src2); + addI_reg_reg_2(tmp2, src3, src4); // Adlc complains about addI_reg_reg. + addI_reg_reg(dst, tmp1, tmp2); + %} +%} + +// Immediate Addition +instruct addI_reg_imm16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + match(Set dst (AddI src1 src2)); + format %{ "ADDI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_addi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Addition with 16-bit shifted operand +instruct addI_reg_immhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2) %{ + match(Set dst (AddI src1 src2)); + format %{ "ADDIS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_addis(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Long Addition +instruct addL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (AddL src1 src2)); + format %{ "ADD $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_add(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Expand does not work with above instruct. (??) +instruct addL_reg_reg_2(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + // no match-rule + effect(DEF dst, USE src1, USE src2); + format %{ "ADD $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_add(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct tree_addL_addL_addL_reg_reg_Ex(iRegLdst dst, iRegLsrc src1, iRegLsrc src2, iRegLsrc src3, iRegLsrc src4) %{ + match(Set dst (AddL (AddL (AddL src1 src2) src3) src4)); + ins_cost(DEFAULT_COST*3); + + expand %{ + // FIXME: we should do this in the ideal world. + iRegLdst tmp1; + iRegLdst tmp2; + addL_reg_reg(tmp1, src1, src2); + addL_reg_reg_2(tmp2, src3, src4); // Adlc complains about orI_reg_reg. + addL_reg_reg(dst, tmp1, tmp2); + %} +%} + +// AddL + ConvL2I. +instruct addI_regL_regL(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (ConvL2I (AddL src1 src2))); + + format %{ "ADD $dst, $src1, $src2 \t// long + l2i" %} + size(4); + ins_encode( enc_add(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// No constant pool entries required. +instruct addL_reg_imm16(iRegLdst dst, iRegLsrc src1, immL16 con) %{ + match(Set dst (AddL src1 con)); + + format %{ "ADDI $dst, $src1, $con" %} + size(4); + ins_encode( enc_addi(dst, src1, con) ); + ins_pipe(pipe_class_default); +%} + +// Long Immediate Addition with 16-bit shifted operand. +// No constant pool entries required. +instruct addL_reg_immhi16(iRegLdst dst, iRegLsrc src1, immL32hi16 src2) %{ + match(Set dst (AddL src1 src2)); + + format %{ "ADDIS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_addis(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Pointer Register Addition +instruct addP_reg_reg(iRegPdst dst, iRegP_N2P src1, iRegLsrc src2) %{ + match(Set dst (AddP src1 src2)); + format %{ "ADD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_add(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Pointer Immediate Addition +// No constant pool entries required. +instruct addP_reg_imm16(iRegPdst dst, iRegP_N2P src1, immL16 src2) %{ + match(Set dst (AddP src1 src2)); + + format %{ "ADDI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_addi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Pointer Immediate Addition with 16-bit shifted operand. +// No constant pool entries required. +instruct addP_reg_immhi16(iRegPdst dst, iRegP_N2P src1, immL32hi16 src2) %{ + match(Set dst (AddP src1 src2)); + + format %{ "ADDIS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_addis(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +//--------------------- +// Subtraction Instructions + +// Register Subtraction +instruct subI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (SubI src1 src2)); + format %{ "SUBF $dst, $src2, $src1" %} + size(4); + ins_encode( enc_subf(dst, src2, src1) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Subtraction +// The compiler converts "x-c0" into "x+ -c0" (see SubINode::Ideal), +// so this rule seems to be unused. +instruct subI_reg_imm16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + match(Set dst (SubI src1 src2)); + format %{ "SUBI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_addi_neg(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// SubI from constant (using subfic). +instruct subI_imm16_reg(iRegIdst dst, immI16 src1, iRegIsrc src2) %{ + match(Set dst (SubI src1 src2)); + format %{ "SUBI $dst, $src1, $src2" %} + + size(4); + ins_encode( enc_subfic(dst, src2, src1) ); + ins_pipe(pipe_class_default); +%} + +// Turn the sign-bit of an integer into a 32-bit mask, 0x0...0 for +// positive integers and 0xF...F for negative ones. +instruct signmask32I_regI(iRegIdst dst, iRegIsrc src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "SRAWI $dst, $src, #31" %} + size(4); + ins_encode( enc_srawi(dst, src, 0x1f) ); + ins_pipe(pipe_class_default); +%} + +instruct absI_reg_Ex(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (AbsI src)); + ins_cost(DEFAULT_COST*3); + + expand %{ + iRegIdst tmp1; + iRegIdst tmp2; + signmask32I_regI(tmp1, src); + xorI_reg_reg(tmp2, tmp1, src); + subI_reg_reg(dst, tmp2, tmp1); + %} +%} + +instruct negI_regI(iRegIdst dst, immI_0 zero, iRegIsrc src2) %{ + match(Set dst (SubI zero src2)); + format %{ "NEG $dst, $src2" %} + size(4); + ins_encode( enc_neg(dst, src2) ); + ins_pipe(pipe_class_default); +%} + +// Long subtraction +instruct subL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (SubL src1 src2)); + format %{ "SUBF $dst, $src2, $src1 \t// long" %} + size(4); + ins_encode( enc_subf(dst, src2, src1) ); + ins_pipe(pipe_class_default); +%} + +// SubL + convL2I. +instruct subI_regL_regL(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (ConvL2I (SubL src1 src2))); + + format %{ "SUBF $dst, $src2, $src1 \t// long + l2i" %} + size(4); + ins_encode( enc_subf(dst, src2, src1) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Subtraction +// The compiler converts "x-c0" into "x+ -c0" (see SubLNode::Ideal), +// so this rule seems to be unused. +// No constant pool entries required. +instruct subL_reg_imm16(iRegLdst dst, iRegLsrc src1, immL16 src2) %{ + match(Set dst (SubL src1 src2)); + + format %{ "SUBI $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_addi_neg(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Turn the sign-bit of a long into a 64-bit mask, 0x0...0 for +// positive longs and 0xF...F for negative ones. +instruct signmask64I_regL(iRegIdst dst, iRegLsrc src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "SRADI $dst, $src, #63" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sradi); + __ sradi($dst$$Register, $src$$Register, 0x3f); + %} + ins_pipe(pipe_class_default); +%} + +// Turn the sign-bit of a long into a 64-bit mask, 0x0...0 for +// positive longs and 0xF...F for negative ones. +instruct signmask64L_regL(iRegLdst dst, iRegLsrc src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "SRADI $dst, $src, #63" %} + size(4); + ins_encode( enc_sradi(dst, src, 0x3f) ); + ins_pipe(pipe_class_default); +%} + +// Long negation +instruct negL_reg_reg(iRegLdst dst, immL_0 zero, iRegLsrc src2) %{ + match(Set dst (SubL zero src2)); + format %{ "NEG $dst, $src2 \t// long" %} + size(4); + ins_encode( enc_neg(dst, src2) ); + ins_pipe(pipe_class_default); +%} + +// NegL + ConvL2I. +instruct negI_con0_regL(iRegIdst dst, immL_0 zero, iRegLsrc src2) %{ + match(Set dst (ConvL2I (SubL zero src2))); + + format %{ "NEG $dst, $src2 \t// long + l2i" %} + size(4); + ins_encode( enc_neg(dst, src2) ); + ins_pipe(pipe_class_default); +%} + +// Multiplication Instructions +// Integer Multiplication + +// Register Multiplication +instruct mulI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (MulI src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "MULLW $dst, $src1, $src2" %} + size(4); + ins_encode( enc_mullw(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Multiplication +instruct mulI_reg_imm16(iRegIdst dst, iRegIsrc src1, immI16 src2) %{ + match(Set dst (MulI src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "MULLI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_mulli(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct mulL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (MulL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "MULLD $dst $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_mulld(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Multiply high for optimized long division by constant. +instruct mulHighL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (MulHiL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "MULHD $dst $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_mulhd(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Multiplication +instruct mulL_reg_imm16(iRegLdst dst, iRegLsrc src1, immL16 src2) %{ + match(Set dst (MulL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "MULLI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_mulli(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Integer Division with Immediate -1: Negate. +instruct divI_reg_immIvalueMinus1(iRegIdst dst, iRegIsrc src1, immI_minus1 src2) %{ + match(Set dst (DivI src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "NEG $dst, $src1 \t// /-1" %} + size(4); + ins_encode( enc_neg(dst, src1) ); + ins_pipe(pipe_class_default); +%} + +// Integer Division with constant, but not -1. +// We should be able to improve this by checking the type of src2. +// It might well be that src2 is known to be positive. +instruct divI_reg_regnotMinus1(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (DivI src1 src2)); + predicate(n->in(2)->find_int_con(-1) != -1); // src2 is a constant, but not -1 + ins_cost(2*DEFAULT_COST); + + format %{ "DIVW $dst, $src1, $src2 \t// /not-1" %} + size(4); + ins_encode( enc_divw(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovI_bne_negI_reg(iRegIdst dst, flagsReg crx, iRegIsrc src1) %{ + effect(USE_DEF dst, USE src1, USE crx); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $dst, neg($src1), $crx" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_bne_neg(dst, crx, src1) ); + ins_pipe(pipe_class_default); +%} + +// Integer Division with Registers not containing constants. +instruct divI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (DivI src1 src2)); + ins_cost(10*DEFAULT_COST); + + expand %{ + immI16 imm %{ (int)-1 %} + flagsReg tmp1; + cmpI_reg_imm16(tmp1, src2, imm); // check src2 == -1 + divI_reg_regnotMinus1(dst, src1, src2); // dst = src1 / src2 + cmovI_bne_negI_reg(dst, tmp1, src1); // cmove dst = neg(src1) if src2 == -1 + %} +%} + +// Long Division with Immediate -1: Negate. +instruct divL_reg_immLvalueMinus1(iRegLdst dst, iRegLsrc src1, immL_minus1 src2) %{ + match(Set dst (DivL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "NEG $dst, $src1 \t// /-1, long" %} + size(4); + ins_encode( enc_neg(dst, src1) ); + ins_pipe(pipe_class_default); +%} + +// Long Division with constant, but not -1. +instruct divL_reg_regnotMinus1(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (DivL src1 src2)); + predicate(n->in(2)->find_long_con(-1L) != -1L); // Src2 is a constant, but not -1. + ins_cost(2*DEFAULT_COST); + + format %{ "DIVD $dst, $src1, $src2 \t// /not-1, long" %} + size(4); + ins_encode( enc_divd(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovL_bne_negL_reg(iRegLdst dst, flagsReg crx, iRegLsrc src1) %{ + effect(USE_DEF dst, USE src1, USE crx); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "CMOVE $dst, neg($src1), $crx" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_bne_neg(dst, crx, src1) ); + ins_pipe(pipe_class_default); +%} + +// Long Division with Registers not containing constants. +instruct divL_reg_reg_Ex(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (DivL src1 src2)); + ins_cost(10*DEFAULT_COST); + + expand %{ + immL16 imm %{ (int)-1 %} + flagsReg tmp1; + cmpL_reg_imm16(tmp1, src2, imm); // check src2 == -1 + divL_reg_regnotMinus1(dst, src1, src2); // dst = src1 / src2 + cmovL_bne_negL_reg(dst, tmp1, src1); // cmove dst = neg(src1) if src2 == -1 + %} +%} + +// Integer Remainder with registers. +instruct modI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (ModI src1 src2)); + ins_cost(10*DEFAULT_COST); + + expand %{ + immI16 imm %{ (int)-1 %} + flagsReg tmp1; + iRegIdst tmp2; + iRegIdst tmp3; + cmpI_reg_imm16(tmp1, src2, imm); // check src2 == -1 + divI_reg_regnotMinus1(tmp2, src1, src2); // tmp2 = src1 / src2 + cmovI_bne_negI_reg(tmp2, tmp1, src1); // cmove tmp2 = neg(src1) if src2 == -1 + mulI_reg_reg(tmp3, src2, tmp2); // tmp3 = src2 * tmp2 + subI_reg_reg(dst, src1, tmp3); // dst = src1 - tmp3 + %} +%} + +// Long Remainder with registers +instruct modL_reg_reg_Ex(iRegLdst dst, iRegLsrc src1, iRegLsrc src2, flagsRegCR0 cr0) %{ + match(Set dst (ModL src1 src2)); + ins_cost(10*DEFAULT_COST); + + expand %{ + immL16 imm %{ (int)-1 %} + flagsReg tmp1; + iRegLdst tmp2; + iRegLdst tmp3; + cmpL_reg_imm16(tmp1, src2, imm); // check src2 == -1 + divL_reg_regnotMinus1(tmp2, src1, src2); // tmp2 = src1 / src2 + cmovL_bne_negL_reg(tmp2, tmp1, src1); // cmove tmp2 = neg(src1) if src2 == -1 + mulL_reg_reg(tmp3, src2, tmp2); // tmp3 = src2 * tmp2 + subL_reg_reg(dst, src1, tmp3); // dst = src1 - tmp3 + %} +%} + +// Integer Shift Instructions + +// Register Shift Left + +// Clear all but the lowest #mask bits. +// Used to normalize shift amounts in registers. +instruct maskI_reg_imm(iRegIdst dst, iRegIsrc src, uimmI6 mask) %{ + // no match-rule, false predicate + effect(DEF dst, USE src, USE mask); + predicate(false); + + format %{ "MASK $dst, $src, $mask \t// clear $mask upper bits" %} + size(4); + ins_encode( enc_clrldi(dst, src, mask) ); + ins_pipe(pipe_class_default); +%} + +instruct lShiftI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "SLW $dst, $src1, $src2" %} + size(4); + ins_encode( enc_slw(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct lShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (LShiftI src1 src2)); + ins_cost(DEFAULT_COST*2); + expand %{ + uimmI6 mask %{ 0x3b /* clear 59 bits, keep 5 */ %} + iRegIdst tmpI; + maskI_reg_imm(tmpI, src2, mask); + lShiftI_reg_reg(dst, src1, tmpI); + %} +%} + +// Register Shift Left Immediate +instruct lShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ + match(Set dst (LShiftI src1 src2)); + + format %{ "SLWI $dst, $src1, ($src2 & 0x1f)" %} + size(4); + ins_encode( enc_slwi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// AndI with negpow2-constant + LShiftI +instruct lShiftI_andI_immInegpow2_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 src2, uimmI5 src3) %{ + match(Set dst (LShiftI (AndI src1 src2) src3)); + predicate(UseRotateAndMaskInstructionsPPC64); + + format %{ "RLWINM $dst, lShiftI(AndI($src1, $src2), $src3)" %} + size(4); + ins_encode( enc_lShiftI_andI_immInegpow2_imm5(dst, src1, src2, src3) ); + ins_pipe(pipe_class_default); +%} + +// RShiftI + AndI with negpow2-constant + LShiftI +instruct lShiftI_andI_immInegpow2_rShiftI_imm5(iRegIdst dst, iRegIsrc src1, immInegpow2 src2, uimmI5 src3) %{ + match(Set dst (LShiftI (AndI (RShiftI src1 src3) src2) src3)); + predicate(UseRotateAndMaskInstructionsPPC64); + + format %{ "RLWINM $dst, lShiftI(AndI(RShiftI($src1, $src3), $src2), $src3)" %} + size(4); + ins_encode( enc_lShiftI_andI_immInegpow2_rShiftI_imm5(dst, src1, src2, src3) ); + ins_pipe(pipe_class_default); +%} + +instruct lShiftL_regL_regI(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "SLD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_sld(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Shift Left +instruct lShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + match(Set dst (LShiftL src1 src2)); + ins_cost(DEFAULT_COST*2); + expand %{ + uimmI6 mask %{ 0x3a /* clear 58 bits, keep 6 */ %} + iRegIdst tmpI; + maskI_reg_imm(tmpI, src2, mask); + lShiftL_regL_regI(dst, src1, tmpI); + %} +%} + +// Register Shift Left Immediate +instruct lshiftL_regL_immI(iRegLdst dst, iRegLsrc src1, immI src2) %{ + match(Set dst (LShiftL src1 src2)); + format %{ "SLDI $dst, $src1, ($src2 & 0x3f)" %} + size(4); + ins_encode( enc_sldi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// If we shift more than 32 bits, we need not convert I2L. +instruct lShiftL_regI_immGE32(iRegLdst dst, iRegIsrc src1, uimmI6_ge32 src2) %{ + match(Set dst (LShiftL (ConvI2L src1) src2)); + ins_cost(DEFAULT_COST); + + size(4); + format %{ "SLDI $dst, i2l($src1), $src2" %} + ins_encode( enc_sldi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Shift a postivie int to the left. +// Clrlsldi clears the upper 32 bits and shifts. +instruct scaledPositiveI2L_lShiftL_convI2L_reg_imm6(iRegLdst dst, iRegIsrc src1, uimmI6 src2) %{ + match(Set dst (LShiftL (ConvI2L src1) src2)); + predicate(((ConvI2LNode*)(_kids[0]->_leaf))->type()->is_long()->is_positive_int()); + + format %{ "SLDI $dst, i2l(positive_int($src1)), $src2" %} + size(4); + ins_encode( enc_clrlsldi(dst, src1, 0x20, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct arShiftI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "SRAW $dst, $src1, $src2" %} + size(4); + ins_encode( enc_sraw(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Arithmetic Shift Right +instruct arShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (RShiftI src1 src2)); + ins_cost(DEFAULT_COST*2); + expand %{ + uimmI6 mask %{ 0x3b /* clear 59 bits, keep 5 */ %} + iRegIdst tmpI; + maskI_reg_imm(tmpI, src2, mask); + arShiftI_reg_reg(dst, src1, tmpI); + %} +%} + +// Register Arithmetic Shift Right Immediate +instruct arShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ + match(Set dst (RShiftI src1 src2)); + + format %{ "SRAWI $dst, $src1, ($src2 & 0x1f)" %} + size(4); + ins_encode( enc_srawi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct arShiftL_regL_regI(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "SRAD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_srad(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Shift Right Arithmetic Long +instruct arShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + match(Set dst (RShiftL src1 src2)); + ins_cost(DEFAULT_COST*2); + + expand %{ + uimmI6 mask %{ 0x3a /* clear 58 bits, keep 6 */ %} + iRegIdst tmpI; + maskI_reg_imm(tmpI, src2, mask); + arShiftL_regL_regI(dst, src1, tmpI); + %} +%} + +// Register Shift Right Immediate +instruct arShiftL_regL_immI(iRegLdst dst, iRegLsrc src1, immI src2) %{ + match(Set dst (RShiftL src1 src2)); + + format %{ "SRADI $dst, $src1, ($src2 & 0x3f)" %} + size(4); + ins_encode( enc_sradi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// RShiftL + ConvL2I +instruct convL2I_arShiftL_regL_immI(iRegIdst dst, iRegLsrc src1, immI src2) %{ + match(Set dst (ConvL2I (RShiftL src1 src2))); + + format %{ "SRADI $dst, $src1, ($src2 & 0x3f) \t// long + l2i" %} + size(4); + ins_encode( enc_sradi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct urShiftI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "SRW $dst, $src1, $src2" %} + size(4); + ins_encode( enc_srw(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Shift Right +instruct urShiftI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (URShiftI src1 src2)); + ins_cost(DEFAULT_COST*2); + + expand %{ + uimmI6 mask %{ 0x3b /* clear 59 bits, keep 5 */ %} + iRegIdst tmpI; + maskI_reg_imm(tmpI, src2, mask); + urShiftI_reg_reg(dst, src1, tmpI); + %} +%} + +// Register Shift Right Immediate +instruct urShiftI_reg_imm(iRegIdst dst, iRegIsrc src1, immI src2) %{ + match(Set dst (URShiftI src1 src2)); + + format %{ "SRWI $dst, $src1, ($src2 & 0x1f)" %} + size(4); + ins_encode( enc_srwi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct urShiftL_regL_regI(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "SRD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_srd(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Shift Right +instruct urShiftL_regL_regI_Ex(iRegLdst dst, iRegLsrc src1, iRegIsrc src2) %{ + match(Set dst (URShiftL src1 src2)); + ins_cost(DEFAULT_COST*2); + + expand %{ + uimmI6 mask %{ 0x3a /* clear 58 bits, keep 6 */ %} + iRegIdst tmpI; + maskI_reg_imm(tmpI, src2, mask); + urShiftL_regL_regI(dst, src1, tmpI); + %} +%} + +// Register Shift Right Immediate +instruct urShiftL_regL_immI(iRegLdst dst, iRegLsrc src1, immI src2) %{ + match(Set dst (URShiftL src1 src2)); + + format %{ "SRDI $dst, $src1, ($src2 & 0x3f)" %} + size(4); + ins_encode( enc_srdi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// URShiftL + ConvL2I. +instruct convL2I_urShiftL_regL_immI(iRegIdst dst, iRegLsrc src1, immI src2) %{ + match(Set dst (ConvL2I (URShiftL src1 src2))); + + format %{ "SRDI $dst, $src1, ($src2 & 0x3f) \t// long + l2i" %} + size(4); + ins_encode( enc_srdi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Shift Right Immediate with a CastP2X +instruct shrP_convP2X_reg_imm6(iRegLdst dst, iRegP_N2P src1, uimmI6 src2) %{ + match(Set dst (URShiftL (CastP2X src1) src2)); + + format %{ "SRDI $dst, $src1, $src2 \t// Cast ptr $src1 to long and shift" %} + size(4); + ins_encode( enc_srdi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct sxtI_reg(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (ConvL2I (ConvI2L src))); + + format %{ "EXTSW $dst, $src \t// int->int" %} + size(4); + ins_encode( enc_extswII(dst, src) ); + ins_pipe(pipe_class_default); +%} + +//----------Rotate Instructions------------------------------------------------ + +// Rotate Left by 8-bit immediate +instruct rotlI_reg_immi8(iRegIdst dst, iRegIsrc src, immI8 lshift, immI8 rshift) %{ + match(Set dst (OrI (LShiftI src lshift) (URShiftI src rshift))); + predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); + + format %{ "ROTLWI $dst, $src, $lshift" %} + size(4); + ins_encode( enc_rotlwi(dst, src, lshift) ); + ins_pipe(pipe_class_default); +%} + +// Rotate Right by 8-bit immediate +instruct rotrI_reg_immi8(iRegIdst dst, iRegIsrc src, immI8 rshift, immI8 lshift) %{ + match(Set dst (OrI (URShiftI src rshift) (LShiftI src lshift))); + predicate(0 == ((n->in(1)->in(2)->get_int() + n->in(2)->in(2)->get_int()) & 0x1f)); + + format %{ "ROTRWI $dst, $rshift" %} + size(4); + ins_encode( enc_rotrwi(dst, src, rshift) ); + ins_pipe(pipe_class_default); +%} + +//----------Floating Point Arithmetic Instructions----------------------------- + +// Add float single precision +instruct addF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (AddF src1 src2)); + + format %{ "FADDS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fadds(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Add float double precision +instruct addD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (AddD src1 src2)); + + format %{ "FADD $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fadd(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Sub float single precision +instruct subF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (SubF src1 src2)); + + format %{ "FSUBS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fsubs(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Sub float double precision +instruct subD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (SubD src1 src2)); + format %{ "FSUB $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fsub(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Mul float single precision +instruct mulF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (MulF src1 src2)); + format %{ "FMULS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fmuls(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Mul float double precision +instruct mulD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (MulD src1 src2)); + format %{ "FMUL $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fmul(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Div float single precision +instruct divF_reg_reg(regF dst, regF src1, regF src2) %{ + match(Set dst (DivF src1 src2)); + format %{ "FDIVS $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fdivs(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Div float double precision +instruct divD_reg_reg(regD dst, regD src1, regD src2) %{ + match(Set dst (DivD src1 src2)); + format %{ "FDIV $dst, $src1, $src2" %} + size(4); + ins_encode( enc_fdiv(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Absolute float single precision +instruct absF_reg(regF dst, regF src) %{ + match(Set dst (AbsF src)); + format %{ "FABS $dst, $src \t// float" %} + size(4); + ins_encode( enc_fabs(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Absolute float double precision +instruct absD_reg(regD dst, regD src) %{ + match(Set dst (AbsD src)); + format %{ "FABS $dst, $src \t// double" %} + size(4); + ins_encode( enc_fabs(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct negF_reg(regF dst, regF src) %{ + match(Set dst (NegF src)); + format %{ "FNEG $dst, $src \t// float" %} + size(4); + ins_encode( enc_fneg(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct negD_reg(regD dst, regD src) %{ + match(Set dst (NegD src)); + format %{ "FNEG $dst, $src \t// double" %} + size(4); + ins_encode( enc_fneg(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// AbsF + NegF. +instruct negF_absF_reg(regF dst, regF src) %{ + match(Set dst (NegF (AbsF src))); + format %{ "FNABS $dst, $src \t// float" %} + size(4); + ins_encode( enc_fnabs(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// AbsD + NegD. +instruct negD_absD_reg(regD dst, regD src) %{ + match(Set dst (NegD (AbsD src))); + format %{ "FNABS $dst, $src \t// double" %} + size(4); + ins_encode( enc_fnabs(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// VM_Version::has_fsqrt() decides if this node will be used. +// Sqrt float double precision +instruct sqrtD_reg(regD dst, regD src) %{ + match(Set dst (SqrtD src)); + format %{ "FSQRT $dst, $src" %} + size(4); + ins_encode( enc_fsqrt(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Single-precision sqrt. +instruct sqrtF_reg(regF dst, regF src) %{ + match(Set dst (ConvD2F (SqrtD (ConvF2D src)))); + predicate(VM_Version::has_fsqrts()); + ins_cost(DEFAULT_COST); + + format %{ "FSQRTS $dst, $src" %} + size(4); + ins_encode( enc_fsqrts(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct roundDouble_nop(regD dst) %{ + match(Set dst (RoundDouble dst)); + ins_cost(0); + + format %{ " -- \t// RoundDouble not needed - empty" %} + size(0); + // PPC results are already "rounded" (i.e., normal-format IEEE). + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +instruct roundFloat_nop(regF dst) %{ + match(Set dst (RoundFloat dst)); + ins_cost(0); + + format %{ " -- \t// RoundFloat not needed - empty" %} + size(0); + // PPC results are already "rounded" (i.e., normal-format IEEE). + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +//----------Logical Instructions----------------------------------------------- + +// And Instructions + +// Register And +instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (AndI src1 src2)); + format %{ "AND $dst, $src1, $src2" %} + size(4); + ins_encode( enc_and(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate And +instruct andI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2, flagsRegCR0 cr0) %{ + match(Set dst (AndI src1 src2)); + effect(KILL cr0); + + format %{ "ANDI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_andi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate And where the immediate is a negative power of 2. +instruct andI_reg_immInegpow2(iRegIdst dst, iRegIsrc src1, immInegpow2 src2) %{ + match(Set dst (AndI src1 src2)); + format %{ "ANDWI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_andI_reg_immInegpow2(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct andI_reg_immIpow2minus1(iRegIdst dst, iRegIsrc src1, immIpow2minus1 src2) %{ + match(Set dst (AndI src1 src2)); + format %{ "ANDWI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_and_reg_immpow2minus1(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{ + match(Set dst (AndI src1 src2)); + predicate(UseRotateAndMaskInstructionsPPC64); + format %{ "ANDWI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_andI_reg_immIpowerOf2(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register And Long +instruct andL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (AndL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "AND $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_and(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate And long +instruct andL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2, flagsRegCR0 cr0) %{ + match(Set dst (AndL src1 src2)); + effect(KILL cr0); + ins_cost(DEFAULT_COST); + + format %{ "ANDI $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_andi(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate And Long where the immediate is a negative power of 2. +instruct andL_reg_immLnegpow2(iRegLdst dst, iRegLsrc src1, immLnegpow2 src2) %{ + match(Set dst (AndL src1 src2)); + format %{ "ANDDI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_andL_reg_immLnegpow2(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct andL_reg_immLpow2minus1(iRegLdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ + match(Set dst (AndL src1 src2)); + format %{ "ANDDI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_and_reg_immpow2minus1(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// AndL + ConvL2I. +instruct convL2I_andL_reg_immLpow2minus1(iRegIdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ + match(Set dst (ConvL2I (AndL src1 src2))); + ins_cost(DEFAULT_COST); + + format %{ "ANDDI $dst, $src1, $src2 \t// long + l2i" %} + size(4); + ins_encode( enc_and_reg_immpow2minus1(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Or Instructions + +// Register Or +instruct orI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (OrI src1 src2)); + format %{ "OR $dst, $src1, $src2" %} + size(4); + ins_encode( enc_or(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Expand does not work with above instruct. (??) +instruct orI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // no match-rule + effect(DEF dst, USE src1, USE src2); + format %{ "OR $dst, $src1, $src2" %} + size(4); + ins_encode( enc_or(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct tree_orI_orI_orI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, iRegIsrc src3, iRegIsrc src4) %{ + match(Set dst (OrI (OrI (OrI src1 src2) src3) src4)); + ins_cost(DEFAULT_COST*3); + + expand %{ + // FIXME: we should do this in the ideal world. + iRegIdst tmp1; + iRegIdst tmp2; + orI_reg_reg(tmp1, src1, src2); + orI_reg_reg_2(tmp2, src3, src4); // Adlc complains about orI_reg_reg. + orI_reg_reg(dst, tmp1, tmp2); + %} +%} + +// Immediate Or +instruct orI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2) %{ + match(Set dst (OrI src1 src2)); + format %{ "ORI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_ori(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Or Long +instruct orL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (OrL src1 src2)); + ins_cost(DEFAULT_COST); + + size(4); + format %{ "OR $dst, $src1, $src2 \t// long" %} + ins_encode( enc_or(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// OrL + ConvL2I. +instruct orI_regL_regL(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (ConvL2I (OrL src1 src2))); + ins_cost(DEFAULT_COST); + + format %{ "OR $dst, $src1, $src2 \t// long + l2i" %} + size(4); + ins_encode( enc_or(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Or long +instruct orL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 con) %{ + match(Set dst (OrL src1 con)); + ins_cost(DEFAULT_COST); + + format %{ "ORI $dst, $src1, $con \t// long" %} + size(4); + ins_encode( enc_ori(dst, src1, con) ); + ins_pipe(pipe_class_default); +%} + +// Xor Instructions + +// Register Xor +instruct xorI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (XorI src1 src2)); + format %{ "XOR $dst, $src1, $src2" %} + size(4); + ins_encode( enc_xor(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Expand does not work with above instruct. (??) +instruct xorI_reg_reg_2(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // no match-rule + effect(DEF dst, USE src1, USE src2); + format %{ "XOR $dst, $src1, $src2" %} + size(4); + ins_encode( enc_xor(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct tree_xorI_xorI_xorI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, iRegIsrc src3, iRegIsrc src4) %{ + match(Set dst (XorI (XorI (XorI src1 src2) src3) src4)); + ins_cost(DEFAULT_COST*3); + + expand %{ + // FIXME: we should do this in the ideal world. + iRegIdst tmp1; + iRegIdst tmp2; + xorI_reg_reg(tmp1, src1, src2); + xorI_reg_reg_2(tmp2, src3, src4); // Adlc complains about xorI_reg_reg. + xorI_reg_reg(dst, tmp1, tmp2); + %} +%} + +// Immediate Xor +instruct xorI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2) %{ + match(Set dst (XorI src1 src2)); + format %{ "XORI $dst, $src1, $src2" %} + size(4); + ins_encode( enc_xori(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Register Xor Long +instruct xorL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (XorL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "XOR $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode( enc_xor(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// XorL + ConvL2I. +instruct xorI_regL_regL(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (ConvL2I (XorL src1 src2))); + ins_cost(DEFAULT_COST); + + format %{ "XOR $dst, $src1, $src2 \t// long + l2i" %} + size(4); + ins_encode( enc_xor(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +// Immediate Xor Long +instruct xorL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 con) %{ + match(Set dst (XorL src1 con)); + ins_cost(DEFAULT_COST); + + format %{ "XORI $dst, $src1, $con \t// long" %} + size(4); + ins_encode( enc_xori(dst, src1, con) ); + ins_pipe(pipe_class_default); +%} + +instruct notI_reg(iRegIdst dst, iRegIsrc src1, immI_minus1 src2) %{ + match(Set dst (XorI src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "NOT $dst, $src1 ($src2)" %} + size(4); + ins_encode( enc_not(dst, src1) ); + ins_pipe(pipe_class_default); +%} + +instruct notL_reg(iRegLdst dst, iRegLsrc src1, immL_minus1 src2) %{ + match(Set dst (XorL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "NOT $dst, $src1 ($src2) \t// long" %} + size(4); + ins_encode( enc_not(dst, src1) ); + ins_pipe(pipe_class_default); +%} + +// And-complement +instruct andcI_reg_reg(iRegIdst dst, iRegIsrc src1, immI_minus1 src2, iRegIsrc src3) %{ + match(Set dst (AndI (XorI src1 src2) src3)); + ins_cost(DEFAULT_COST); + + format %{ "ANDW $dst, xori($src1, $src2), $src3" %} + size(4); + ins_encode( enc_andc(dst, src3, src1) ); + ins_pipe(pipe_class_default); +%} + +// And-complement +instruct andcL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ + // no match-rule, false predicate + effect(DEF dst, USE src1, USE src2); + predicate(false); + + format %{ "ANDC $dst, $src1, $src2" %} + size(4); + ins_encode( enc_andc(dst, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +//----------Moves between int/long and float/double---------------------------- +// +// The following rules move values from int/long registers/stack-locations +// to float/double registers/stack-locations and vice versa, without doing any +// conversions. These rules are used to implement the bit-conversion methods +// of java.lang.Float etc., e.g. +// int floatToIntBits(float value) +// float intBitsToFloat(int bits) +// +// Notes on the implementation on ppc64: +// We only provide rules which move between a register and a stack-location, +// because we always have to go through memory when moving between a float +// register and an integer register. + +//---------- Chain stack slots between similar types -------- + +// These are needed so that the rules below can match. + +// Load integer from stack slot +instruct stkI_to_regI(iRegIdst dst, stackSlotI src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $src" %} + size(4); + ins_encode( enc_lwz(dst, src) ); + ins_pipe(pipe_class_memory); +%} + +// Store integer to stack slot +instruct regI_to_stkI(stackSlotI dst, iRegIsrc src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + format %{ "STW $src, $dst \t// stk" %} + size(4); + ins_encode( enc_stw(src, dst) ); // rs=rt + ins_pipe(pipe_class_memory); +%} + +// Load long from stack slot +instruct stkL_to_regL(iRegLdst dst, stackSlotL src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + format %{ "LD $dst, $src \t// long" %} + size(4); + ins_encode( enc_ld(dst, src) ); + ins_pipe(pipe_class_memory); +%} + +// Store long to stack slot +instruct regL_to_stkL(stackSlotL dst, iRegLsrc src) %{ + match(Set dst src); + ins_cost(MEMORY_REF_COST); + + format %{ "STD $src, $dst \t// long" %} + size(4); + ins_encode( enc_std(src, dst) ); // rs=rt + ins_pipe(pipe_class_memory); +%} + +//----------Moves between int and float + +// Move float value from float stack-location to integer register. +instruct moveF2I_stack_reg(iRegIdst dst, stackSlotF src) %{ + match(Set dst (MoveF2I src)); + ins_cost(MEMORY_REF_COST); + + format %{ "LWZ $dst, $src \t// MoveF2I" %} + size(4); + ins_encode( enc_lwz(dst, src) ); + ins_pipe(pipe_class_memory); +%} + +// Move float value from float register to integer stack-location. +instruct moveF2I_reg_stack(stackSlotI dst, regF src) %{ + match(Set dst (MoveF2I src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STFS $src, $dst \t// MoveF2I" %} + size(4); + ins_encode( enc_stfs(src, dst) ); + ins_pipe(pipe_class_memory); +%} + +// Move integer value from integer stack-location to float register. +instruct moveI2F_stack_reg(regF dst, stackSlotI src) %{ + match(Set dst (MoveI2F src)); + ins_cost(MEMORY_REF_COST); + + format %{ "LFS $dst, $src \t// MoveI2F" %} + size(4); + ins_encode( enc_lfs(dst, src) ); + ins_pipe(pipe_class_memory); +%} + +// Move integer value from integer register to float stack-location. +instruct moveI2F_reg_stack(stackSlotF dst, iRegIsrc src) %{ + match(Set dst (MoveI2F src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STW $src, $dst \t// MoveI2F" %} + size(4); + ins_encode( enc_stw(src, dst) ); + ins_pipe(pipe_class_memory); +%} + +//----------Moves between long and float + +instruct moveF2L_reg_stack(stackSlotL dst, regF src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "storeD $src, $dst \t// STACK" %} + size(4); + ins_encode( enc_stfd(src, dst) ); + ins_pipe(pipe_class_default); +%} + +//----------Moves between long and double + +// Move double value from double stack-location to long register. +instruct moveD2L_stack_reg(iRegLdst dst, stackSlotD src) %{ + match(Set dst (MoveD2L src)); + ins_cost(MEMORY_REF_COST); + size(4); + format %{ "LD $dst, $src \t// MoveD2L" %} + ins_encode( enc_ld(dst, src) ); + ins_pipe(pipe_class_memory); +%} + +// Move double value from double register to long stack-location. +instruct moveD2L_reg_stack(stackSlotL dst, regD src) %{ + match(Set dst (MoveD2L src)); + effect(DEF dst, USE src); + ins_cost(MEMORY_REF_COST); + + format %{ "STFD $src, $dst \t// MoveD2L" %} + size(4); + ins_encode( enc_stfd(src, dst) ); + ins_pipe(pipe_class_memory); +%} + +// Move long value from long stack-location to double register. +instruct moveL2D_stack_reg(regD dst, stackSlotL src) %{ + match(Set dst (MoveL2D src)); + ins_cost(MEMORY_REF_COST); + + format %{ "LFD $dst, $src \t// MoveL2D" %} + size(4); + ins_encode( enc_lfd(dst, src) ); + ins_pipe(pipe_class_memory); +%} + +// Move long value from long register to double stack-location. +instruct moveL2D_reg_stack(stackSlotD dst, iRegLsrc src) %{ + match(Set dst (MoveL2D src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STD $src, $dst \t// MoveL2D" %} + size(4); + ins_encode( enc_std(src, dst) ); + ins_pipe(pipe_class_memory); +%} + +//----------Register Move Instructions----------------------------------------- + +// Replicate for Superword + +instruct moveReg(iRegLdst dst, iRegIsrc src) %{ + predicate(false); + effect(DEF dst, USE src); + + format %{ "MR $dst, $src \t// replicate " %} + // variable size, 0 or 4. + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +//----------Cast instructions (Java-level type cast)--------------------------- + +// Cast Long to Pointer for unsafe natives. +instruct castX2P(iRegPdst dst, iRegLsrc src) %{ + match(Set dst (CastX2P src)); + + format %{ "MR $dst, $src \t// Long->Ptr" %} + // variable size, 0 or 4. + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Cast Pointer to Long for unsafe natives. +instruct castP2X(iRegLdst dst, iRegP_N2P src) %{ + match(Set dst (CastP2X src)); + + format %{ "MR $dst, $src \t// Ptr->Long" %} + // variable size, 0 or 4. + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct castPP(iRegPdst dst) %{ + match(Set dst (CastPP dst)); + format %{ " -- \t// castPP of $dst" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +instruct castII(iRegIdst dst) %{ + match(Set dst (CastII dst)); + format %{ " -- \t// castII of $dst" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +instruct checkCastPP(iRegPdst dst) %{ + match(Set dst (CheckCastPP dst)); + format %{ " -- \t// checkcastPP of $dst" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +//----------Convert instructions----------------------------------------------- + +// Convert to boolean. + +// int_to_bool(src) : { 1 if src != 0 +// { 0 else +// +// strategy: +// 1) Count leading zeros of 32 bit-value src, +// this returns 32 (0b10.0000) iff src == 0 and <32 otherwise. +// 2) Shift 5 bits to the right, result is 0b1 iff src == 0, 0b0 otherwise. +// 3) Xori the result to get 0b1 if src != 0 and 0b0 if src == 0. + +// convI2Bool +instruct convI2Bool_reg__cntlz_Ex(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (Conv2B src)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immI shiftAmount %{ 0x5 %} + uimmI16 mask %{ 0x1 %} + iRegIdst tmp1; + iRegIdst tmp2; + countLeadingZerosI(tmp1, src); + urShiftI_reg_imm(tmp2, tmp1, shiftAmount); + xorI_reg_uimm16(dst, tmp2, mask); + %} +%} + +instruct convI2Bool_reg__cmove(iRegIdst dst, iRegIsrc src, flagsReg crx) %{ + match(Set dst (Conv2B src)); + effect(TEMP crx); + predicate(!UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + format %{ "CMPWI $crx, $src, #0 \t// convI2B" + "LI $dst, #0\n\t" + "BEQ $crx, done\n\t" + "LI $dst, #1\n" + "done:" %} + size(16); + ins_encode( enc_convI2B_regI__cmove(dst, src, crx, 0x0, 0x1) ); + ins_pipe(pipe_class_compare); +%} + +// ConvI2B + XorI +instruct xorI_convI2Bool_reg_immIvalue1__cntlz_Ex(iRegIdst dst, iRegIsrc src, immI_1 mask) %{ + match(Set dst (XorI (Conv2B src) mask)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immI shiftAmount %{ 0x5 %} + iRegIdst tmp1; + countLeadingZerosI(tmp1, src); + urShiftI_reg_imm(dst, tmp1, shiftAmount); + %} +%} + +instruct xorI_convI2Bool_reg_immIvalue1__cmove(iRegIdst dst, iRegIsrc src, flagsReg crx, immI_1 mask) %{ + match(Set dst (XorI (Conv2B src) mask)); + effect(TEMP crx); + predicate(!UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + format %{ "CMPWI $crx, $src, #0 \t// Xor(convI2B($src), $mask)" + "LI $dst, #1\n\t" + "BEQ $crx, done\n\t" + "LI $dst, #0\n" + "done:" %} + size(16); + ins_encode( enc_convI2B_regI__cmove(dst, src, crx, 0x1, 0x0) ); + ins_pipe(pipe_class_compare); +%} + +// AndI 0b0..010..0 + ConvI2B +instruct convI2Bool_andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src, immIpowerOf2 mask) %{ + match(Set dst (Conv2B (AndI src mask))); + predicate(UseRotateAndMaskInstructionsPPC64); + ins_cost(DEFAULT_COST); + + format %{ "RLWINM $dst, $src, $mask \t// convI2B(AndI($src, $mask))" %} + size(4); + ins_encode( enc_convI2B_andI_reg_immIpowerOf2(dst, src, mask) ); + ins_pipe(pipe_class_default); +%} + +// Convert pointer to boolean. +// +// ptr_to_bool(src) : { 1 if src != 0 +// { 0 else +// +// strategy: +// 1) Count leading zeros of 64 bit-value src, +// this returns 64 (0b100.0000) iff src == 0 and <64 otherwise. +// 2) Shift 6 bits to the right, result is 0b1 iff src == 0, 0b0 otherwise. +// 3) Xori the result to get 0b1 if src != 0 and 0b0 if src == 0. + +// ConvP2B +instruct convP2Bool_reg__cntlz_Ex(iRegIdst dst, iRegP_N2P src) %{ + match(Set dst (Conv2B src)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immI shiftAmount %{ 0x6 %} + uimmI16 mask %{ 0x1 %} + iRegIdst tmp1; + iRegIdst tmp2; + countLeadingZerosP(tmp1, src); + urShiftI_reg_imm(tmp2, tmp1, shiftAmount); + xorI_reg_uimm16(dst, tmp2, mask); + %} +%} + +instruct convP2Bool_reg__cmove(iRegIdst dst, iRegP_N2P src, flagsReg crx) %{ + match(Set dst (Conv2B src)); + effect(TEMP crx); + predicate(!UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + format %{ "CMPDI $crx, $src, #0 \t// convP2B" + "LI $dst, #0\n\t" + "BEQ $crx, done\n\t" + "LI $dst, #1\n" + "done:" %} + size(16); + ins_encode( enc_convP2B_regP__cmove(dst, src, crx, 0x0, 0x1) ); + ins_pipe(pipe_class_compare); +%} + +// ConvP2B + XorI +instruct xorI_convP2Bool_reg__cntlz_Ex(iRegIdst dst, iRegP_N2P src, immI_1 mask) %{ + match(Set dst (XorI (Conv2B src) mask)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immI shiftAmount %{ 0x6 %} + iRegIdst tmp1; + countLeadingZerosP(tmp1, src); + urShiftI_reg_imm(dst, tmp1, shiftAmount); + %} +%} + +instruct xorI_convP2Bool_reg_immIvalue1__cmove(iRegIdst dst, iRegP_N2P src, flagsReg crx, immI_1 mask) %{ + match(Set dst (XorI (Conv2B src) mask)); + effect(TEMP crx); + predicate(!UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + format %{ "CMPDI $crx, $src, #0 \t// XorI(convP2B($src), $mask)" + "LI $dst, #1\n\t" + "BEQ $crx, done\n\t" + "LI $dst, #0\n" + "done:" %} + size(16); + ins_encode( enc_convP2B_regP__cmove(dst, src, crx, 0x1, 0x0) ); + ins_pipe(pipe_class_compare); +%} + +// if src1 < src2, return -1 else return 0 +instruct cmpLTMask_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (CmpLTMask src1 src2)); + ins_cost(DEFAULT_COST*4); + + expand %{ + iRegLdst src1s; + iRegLdst src2s; + iRegLdst diff; + convI2L_reg(src1s, src1); // Ensure proper sign extension. + convI2L_reg(src2s, src2); // Ensure proper sign extension. + subL_reg_reg(diff, src1s, src2s); + // Need to consider >=33 bit result, therefore we need signmaskL. + signmask64I_regL(dst, diff); + %} +%} + +instruct cmpLTMask_reg_immI0(iRegIdst dst, iRegIsrc src1, immI_0 src2) %{ + match(Set dst (CmpLTMask src1 src2)); // if src1 < src2, return -1 else return 0 + format %{ "SRAWI $dst, $src1, $src2 \t// CmpLTMask" %} + size(4); + ins_encode( enc_srawi(dst, src1, 0x1f) ); + ins_pipe(pipe_class_default); +%} + +//----------Arithmetic Conversion Instructions--------------------------------- + +// Convert to Byte -- nop +// Convert to Short -- nop + +// Convert to Int + +instruct convB2I_reg(iRegIdst dst, iRegIsrc src, immI_24 amount) %{ + match(Set dst (RShiftI (LShiftI src amount) amount)); + format %{ "EXTSB $dst, $src \t// byte->int" %} + size(4); + ins_encode( enc_extsb(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// LShiftI 16 + RShiftI 16 converts short to int. +instruct convS2I_reg(iRegIdst dst, iRegIsrc src, immI_16 amount) %{ + match(Set dst (RShiftI (LShiftI src amount) amount)); + format %{ "EXTSH $dst, $src \t// short->int" %} + size(4); + ins_encode( enc_extsh(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// ConvL2I + ConvI2L: Sign extend int in long register. +instruct sxtI_L2L_reg(iRegLdst dst, iRegLsrc src) %{ + match(Set dst (ConvI2L (ConvL2I src))); + + format %{ "EXTSW $dst, $src \t// long->long" %} + size(4); + ins_encode( enc_extswLL(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct convL2I_reg(iRegIdst dst, iRegLsrc src) %{ + match(Set dst (ConvL2I src)); + format %{ "MR $dst, $src \t// long->int" %} + // variable size, 0 or 4 + ins_encode( enc_mr_if_needed(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct convD2IRaw_regD(regD dst, regD src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "FCTIWZ $dst, $src \t// convD2I, $src != NaN" %} + size(4); + ins_encode( enc_fctiwz(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsReg crx, stackSlotL src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "cmovI $crx, $dst, $src" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovI_bso_stackSlotL_conLvalue0_Ex(iRegIdst dst, flagsReg crx, stackSlotL src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + format %{ "CmovI $dst, $crx, $src \t// late expanded" %} + lateExpand( lateExpand_cmovI_bso_stackSlotL_conLValue0(dst, crx, src) ); +%} + +// Double to Int conversion, NaN is mapped to 0. +instruct convD2I_reg_ExEx(iRegIdst dst, regD src) %{ + match(Set dst (ConvD2I src)); + ins_cost(DEFAULT_COST); + + expand %{ + regD tmpD; + stackSlotL tmpS; + flagsReg crx; + cmpDUnordered_reg_reg(crx, src, src); // Check whether src is NaN. + convD2IRaw_regD(tmpD, src); // Convert float to int (speculated). + moveD2L_reg_stack(tmpS, tmpD); // Store float to stack (speculated). + cmovI_bso_stackSlotL_conLvalue0_Ex(dst, crx, tmpS); // Cmove based on NaN check. + %} +%} + +instruct convF2IRaw_regF(regF dst, regF src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "FCTIWZ $dst, $src \t// convF2I, $src != NaN" %} + size(4); + ins_encode( enc_fctiwz(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Float to Int conversion, NaN is mapped to 0. +instruct convF2I_regF_ExEx(iRegIdst dst, regF src) %{ + match(Set dst (ConvF2I src)); + ins_cost(DEFAULT_COST); + + expand %{ + regF tmpF; + stackSlotL tmpS; + flagsReg crx; + cmpFUnordered_reg_reg(crx, src, src); // Check whether src is NaN. + convF2IRaw_regF(tmpF, src); // Convert float to int (speculated). + moveF2L_reg_stack(tmpS, tmpF); // Store float to stack (speculated). + cmovI_bso_stackSlotL_conLvalue0_Ex(dst, crx, tmpS); // Cmove based on NaN check. + %} +%} + +// Convert to Long + +instruct convI2L_reg(iRegLdst dst, iRegIsrc src) %{ + match(Set dst (ConvI2L src)); + format %{ "EXTSW $dst, $src \t// int->long" %} + size(4); + ins_encode( enc_extsw(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Zero-extend: convert unsigned int to long (convUI2L). +instruct zeroExtendL_regI(iRegLdst dst, iRegIsrc src, immL_32bits mask) %{ + match(Set dst (AndL (ConvI2L src) mask)); + ins_cost(DEFAULT_COST); + + format %{ "CLRLDI $dst, $src, #32 \t// zero-extend int to long" %} + size(4); + ins_encode( enc_zeroextendw(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Zero-extend: convert unsigned int to long in long register. +instruct zeroExtendL_regL(iRegLdst dst, iRegLsrc src, immL_32bits mask) %{ + match(Set dst (AndL src mask)); + ins_cost(DEFAULT_COST); + + format %{ "CLRLDI $dst, $src, #32 \t// zero-extend int to long" %} + size(4); + ins_encode( enc_zeroextendw(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct convF2LRaw_regF(regF dst, regF src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "FCTIDZ $dst, $src \t// convF2L, $src != NaN" %} + size(4); + ins_encode( enc_fctidz(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsReg crx, stackSlotL src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "cmovL $crx, $dst, $src" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 12 : 8); + ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); + ins_pipe(pipe_class_default); +%} + +instruct cmovL_bso_stackSlotL_conLvalue0_Ex(iRegLdst dst, flagsReg crx, stackSlotL src) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE src); + predicate(false); + + format %{ "CmovL $dst, $crx, $src \t// late expanded" %} + lateExpand( lateExpand_cmovL_bso_stackSlotL_conLvalue0(dst, crx, src) ); +%} + +// Float to Long conversion, NaN is mapped to 0. +instruct convF2L_reg_ExEx(iRegLdst dst, regF src) %{ + match(Set dst (ConvF2L src)); + ins_cost(DEFAULT_COST); + + expand %{ + regF tmpF; + stackSlotL tmpS; + flagsReg crx; + cmpFUnordered_reg_reg(crx, src, src); // Check whether src is NaN. + convF2LRaw_regF(tmpF, src); // Convert float to long (speculated). + moveF2L_reg_stack(tmpS, tmpF); // Store float to stack (speculated). + cmovL_bso_stackSlotL_conLvalue0_Ex(dst, crx, tmpS); // Cmove based on NaN check. + %} +%} + +instruct convD2LRaw_regD(regD dst, regD src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "FCTIDZ $dst, $src \t// convD2L $src != NaN" %} + size(4); + ins_encode( enc_fctidz(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Double to Long conversion, NaN is mapped to 0. +instruct convD2L_reg_ExEx(iRegLdst dst, regD src) %{ + match(Set dst (ConvD2L src)); + ins_cost(DEFAULT_COST); + + expand %{ + regD tmpD; + stackSlotL tmpS; + flagsReg crx; + cmpDUnordered_reg_reg(crx, src, src); // Check whether src is NaN. + convD2LRaw_regD(tmpD, src); // Convert float to long (speculated). + moveD2L_reg_stack(tmpS, tmpD); // Store float to stack (speculated). + cmovL_bso_stackSlotL_conLvalue0_Ex(dst, crx, tmpS); // Cmove based on NaN check. + %} +%} + +// Convert to Float + +// Placed here as needed in expand. +instruct convL2DRaw_regD(regD dst, regD src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "FCFID $dst, $src \t// convL2D" %} + size(4); + ins_encode( enc_fcfid(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Placed here as needed in expand. +instruct convD2F_reg(regF dst, regD src) %{ + match(Set dst (ConvD2F src)); + format %{ "FRSP $dst, $src \t// convD2F" %} + size(4); + ins_encode( enc_frsp(dst, src)); + ins_pipe(pipe_class_default); +%} + +// Integer to Float conversion. +instruct convI2F_ireg_Ex(regF dst, iRegIsrc src) %{ + match(Set dst (ConvI2F src)); + predicate(!VM_Version::has_fcfids()); + ins_cost(DEFAULT_COST); + + expand %{ + iRegLdst tmpL; + stackSlotL tmpS; + regD tmpD; + regD tmpD2; + convI2L_reg(tmpL, src); // Sign-extension int to long. + regL_to_stkL(tmpS, tmpL); // Store long to stack. + moveL2D_stack_reg(tmpD, tmpS); // Load long into double register. + convL2DRaw_regD(tmpD2, tmpD); // Convert to double. + convD2F_reg(dst, tmpD2); // Convert double to float. + %} +%} + +instruct convL2FRaw_regF(regF dst, regD src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "FCFIDS $dst, $src \t// convL2F" %} + size(4); + ins_encode( enc_fcfids(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Integer to Float conversion. Special version for Power7. +instruct convI2F_ireg_fcfids_Ex(regF dst, iRegIsrc src) %{ + match(Set dst (ConvI2F src)); + predicate(VM_Version::has_fcfids()); + ins_cost(DEFAULT_COST); + + expand %{ + iRegLdst tmpL; + stackSlotL tmpS; + regD tmpD; + convI2L_reg(tmpL, src); // Sign-extension int to long. + regL_to_stkL(tmpS, tmpL); // Store long to stack. + moveL2D_stack_reg(tmpD, tmpS); // Load long into double register. + convL2FRaw_regF(dst, tmpD); // Convert to float. + %} +%} + +// L2F to avoid runtime call. +instruct convL2F_ireg_fcfids_Ex(regF dst, iRegLsrc src) %{ + match(Set dst (ConvL2F src)); + predicate(VM_Version::has_fcfids()); + ins_cost(DEFAULT_COST); + + expand %{ + stackSlotL tmpS; + regD tmpD; + regL_to_stkL(tmpS, src); // Store long to stack. + moveL2D_stack_reg(tmpD, tmpS); // Load long into double register. + convL2FRaw_regF(dst, tmpD); // Convert to float. + %} +%} + +// Moved up as used in expand. +//instruct convD2F_reg(regF dst, regD src) %{%} + +// Convert to Double + +// Integer to Double conversion. +instruct convI2D_reg_Ex(regD dst, iRegIsrc src) %{ + match(Set dst (ConvI2D src)); + ins_cost(DEFAULT_COST); + + expand %{ + iRegLdst tmpL; + stackSlotL tmpS; + regD tmpD; + convI2L_reg(tmpL, src); // Sign-extension int to long. + regL_to_stkL(tmpS, tmpL); // Store long to stack. + moveL2D_stack_reg(tmpD, tmpS); // Load long into double register. + convL2DRaw_regD(dst, tmpD); // Convert to double. + %} +%} + +// Long to Double conversion +instruct convL2D_reg_Ex(regD dst, stackSlotL src) %{ + match(Set dst (ConvL2D src)); + ins_cost(DEFAULT_COST + MEMORY_REF_COST); + + expand %{ + regD tmpD; + moveL2D_stack_reg(tmpD, src); + convL2DRaw_regD(dst, tmpD); + %} +%} + +instruct convF2D_reg(regD dst, regF src) %{ + match(Set dst (ConvF2D src)); + format %{ "FMR $dst, $src \t// float->double" %} + // variable size, 0 or 4 + ins_encode( enc_fmr_if_needed(dst, src )); + ins_pipe(pipe_class_default); +%} + +//----------Control Flow Instructions------------------------------------------ +// Compare Instructions + +// Compare Integers +instruct cmpI_reg_reg(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ + match(Set crx (CmpI src1 src2)); + size(4); + format %{ "CMPW $crx, $src1, $src2" %} + ins_encode( enc_cmpw(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmpI_reg_imm16(flagsReg crx, iRegIsrc src1, immI16 src2) %{ + match(Set crx (CmpI src1 src2)); + format %{ "CMPWI $crx, $src1, $src2" %} + size(4); + ins_encode( enc_cmpwi(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +// (src1 & src2) == 0? +instruct testI_reg_imm(flagsRegCR0 cr0, iRegIsrc src1, uimmI16 src2, immI_0 zero) %{ + match(Set cr0 (CmpI (AndI src1 src2) zero)); + // r0 is killed + format %{ "ANDI R0, $src1, $src2 \t// BTST int" %} + size(4); + ins_encode( enc_andi(R0, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmpL_reg_reg(flagsReg crx, iRegLsrc src1, iRegLsrc src2) %{ + match(Set crx (CmpL src1 src2)); + format %{ "CMPD $crx, $src1, $src2" %} + size(4); + ins_encode( enc_cmpd(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmpL_reg_imm16(flagsReg crx, iRegLsrc src1, immL16 con) %{ + match(Set crx (CmpL src1 con)); + format %{ "CMPDI $crx, $src1, $con" %} + size(4); + ins_encode( enc_cmpdi(crx, src1, con) ); + ins_pipe(pipe_class_compare); +%} + +instruct testL_reg_reg(flagsRegCR0 cr0, iRegLsrc src1, iRegLsrc src2, immL_0 zero) %{ + match(Set cr0 (CmpL (AndL src1 src2) zero)); + // r0 is killed + format %{ "AND R0, $src1, $src2 \t// BTST long" %} + size(4); + ins_encode( enc_btst_reg(src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct testL_reg_imm(flagsRegCR0 cr0, iRegLsrc src1, uimmL16 src2, immL_0 zero) %{ + match(Set cr0 (CmpL (AndL src1 src2) zero)); + // r0 is killed + format %{ "ANDI R0, $src1, $src2 \t// BTST long" %} + size(4); + ins_encode( enc_andi(R0, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmovI_conIvalueMinus1_conIvalue1(iRegIdst dst, flagsReg crx) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "cmovI $crx, $dst, -1, 0, +1" %} + // Worst case is branch + move + branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 20 : 16); + ins_encode( enc_cmovI_conIvalueMinus1_conIvalue1(dst, crx) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmovI_conIvalueMinus1_conIvalue0_conIvalue1_Ex(iRegIdst dst, flagsReg crx) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx); + predicate(false); + + format %{ "CmovI $crx, $dst, -1, 0, +1 \t// late expanded" %} + lateExpand( lateExpand_cmovI_conIvalueMinus1_conIvalue0_conIvalue1(dst, crx) ); +%} + +// Manifest a CmpL3 result in an integer register. Very painful. +// This is the test to avoid. +// (src1 < src2) ? -1 : ((src1 > src2) ? 1 : 0) +instruct cmpL3_reg_reg_ExEx(iRegIdst dst, iRegLsrc src1, iRegLsrc src2) %{ + match(Set dst (CmpL3 src1 src2)); + ins_cost(DEFAULT_COST*5+BRANCH_COST); + + expand %{ + flagsReg tmp1; + cmpL_reg_reg(tmp1, src1, src2); + cmovI_conIvalueMinus1_conIvalue0_conIvalue1_Ex(dst, tmp1); + %} +%} + +// Implicit range checks. +// A range check in the ideal world has one of the following shapes: +// - (If le (CmpU length index)), (IfTrue throw exception) +// - (If lt (CmpU index length)), (IfFalse throw exception) +// +// Match range check 'If le (CmpU length index)'. +instruct rangeCheck_iReg_uimm15(cmpOp cmp, iRegIsrc src_length, uimmI15 index, label labl) %{ + match(If cmp (CmpU src_length index)); + effect(USE labl); + predicate(TrapBasedRangeChecks && + _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::le && + PROB_UNLIKELY(_leaf->as_If()->_prob) >= PROB_ALWAYS && + (Matcher::branches_to_uncommon_trap(_leaf))); + + ins_is_TrapBasedCheckNode(true); + + format %{ "TWI $index $cmp $src_length \t// RangeCheck => trap $labl" %} + size(4); + ins_encode( enc_rangeCheck_le_iReg_uimm15(cmp, src_length, index, labl) ); + ins_pipe(pipe_class_trap); +%} + +// Match range check 'If lt (CmpU index length)'. +instruct rangeCheck_iReg_iReg(cmpOp cmp, iRegIsrc src_index, iRegIsrc src_length, label labl) %{ + match(If cmp (CmpU src_index src_length)); + effect(USE labl); + predicate(TrapBasedRangeChecks && + _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt && + _leaf->as_If()->_prob >= PROB_ALWAYS && + (Matcher::branches_to_uncommon_trap(_leaf))); + + ins_is_TrapBasedCheckNode(true); + + format %{ "TW $src_index $cmp $src_length \t// RangeCheck => trap $labl" %} + size(4); + ins_encode( enc_rangeCheck_ge_iReg_iReg(cmp, src_index, src_length, labl) ); + ins_pipe(pipe_class_trap); +%} + +// Match range check 'If lt (CmpU index length)'. +instruct rangeCheck_uimm15_iReg(cmpOp cmp, iRegIsrc src_index, uimmI15 length, label labl) %{ + match(If cmp (CmpU src_index length)); + effect(USE labl); + predicate(TrapBasedRangeChecks && + _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::lt && + _leaf->as_If()->_prob >= PROB_ALWAYS && + (Matcher::branches_to_uncommon_trap(_leaf))); + + ins_is_TrapBasedCheckNode(true); + + format %{ "TWI $src_index $cmp $length \t// RangeCheck => trap $labl" %} + size(4); + ins_encode( enc_rangeCheck_ge_iReg_uimm15(cmp, src_index, length, labl) ); + ins_pipe(pipe_class_trap); +%} + +instruct compU_reg_reg(flagsReg crx, iRegIsrc src1, iRegIsrc src2) %{ + match(Set crx (CmpU src1 src2)); + format %{ "CMPLW $crx, $src1, $src2 \t// unsigned" %} + size(4); + ins_encode( enc_cmplw(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct compU_reg_uimm16(flagsReg crx, iRegIsrc src1, uimmI16 src2) %{ + match(Set crx (CmpU src1 src2)); + size(4); + format %{ "CMPLWI $crx, $src1, $src2" %} + ins_encode( enc_cmplwi(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +// Implicit zero checks (more implicit null checks). +// No constant pool entries required. +instruct zeroCheckN_iReg_imm0(cmpOp cmp, iRegNsrc value, immN_0 zero, label labl) %{ + match(If cmp (CmpN value zero)); + effect(USE labl); + predicate(TrapBasedNullChecks && + _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne && + _leaf->as_If()->_prob >= PROB_LIKELY_MAG(4) && + Matcher::branches_to_uncommon_trap(_leaf)); + ins_cost(1); + + ins_is_TrapBasedCheckNode(true); + + format %{ "TDI $value $cmp $zero \t// ZeroCheckN => trap $labl" %} + size(4); + ins_encode( enc_zeroCheckP_eq_iReg(cmp, value, zero, labl) ); + ins_pipe(pipe_class_trap); +%} + +// Compare narrow oops. +instruct cmpN_reg_reg(flagsReg crx, iRegNsrc src1, iRegNsrc src2) %{ + match(Set crx (CmpN src1 src2)); + + size(4); + ins_cost(DEFAULT_COST); + format %{ "CMPLW $crx, $src1, $src2 \t// compressed ptr" %} + ins_encode( enc_cmplw(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmpN_reg_imm0(flagsReg crx, iRegNsrc src1, immN_0 src2) %{ + match(Set crx (CmpN src1 src2)); + // Make this more expensive than zeroCheckN_iReg_imm0. + ins_cost(DEFAULT_COST); + + format %{ "CMPLWI $crx, $src1, $src2 \t// compressed ptr" %} + size(4); + ins_encode( enc_cmplwi(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +// Implicit zero checks (more implicit null checks). +// No constant pool entries required. +instruct zeroCheckP_reg_imm0(cmpOp cmp, iRegP_N2P value, immP_0 zero, label labl) %{ + match(If cmp (CmpP value zero)); + effect(USE labl); + predicate(TrapBasedNullChecks && + _kids[0]->_leaf->as_Bool()->_test._test == BoolTest::ne && + _leaf->as_If()->_prob >= PROB_LIKELY_MAG(4) && + Matcher::branches_to_uncommon_trap(_leaf)); + + ins_is_TrapBasedCheckNode(true); + + format %{ "TDI $value $cmp $zero \t// ZeroCheckP => trap $labl" %} + size(4); + ins_encode( enc_zeroCheckP_eq_iReg(cmp, value, zero, labl) ); + ins_pipe(pipe_class_trap); +%} + +// Compare Pointers +instruct cmpP_reg_reg(flagsReg crx, iRegP_N2P src1, iRegP_N2P src2) %{ + match(Set crx (CmpP src1 src2)); + format %{ "CMPLD $crx, $src1, $src2 \t// ptr" %} + size(4); + ins_encode( enc_cmpld(crx, src1, src2) ); + ins_pipe(pipe_class_compare); +%} + +// Used in late expand. +instruct cmpP_reg_imm16(flagsReg crx, iRegPsrc src1, immL16 con) %{ + // This match rule prevents reordering of node before a safepoint. + // This only makes sense if this instructions is used exclusively + // for the expansion of EncodeP! + match(Set crx (CmpP src1 con)); + predicate(false); + + format %{ "CMPDI $crx, $src1, $con" %} + size(4); + ins_encode( enc_cmpdi(crx, src1, con) ); + ins_pipe(pipe_class_compare); +%} + +//----------Float Compares---------------------------------------------------- + +instruct cmpFUnordered_reg_reg(flagsReg crx, regF src1, regF src2) %{ + // no match-rule, false predicate + effect(DEF crx, USE src1, USE src2); + predicate(false); + + format %{ "cmpFUrd $crx, $src1, $src2" %} + size(4); + ins_encode( enc_fcmpu(crx, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct cmov_bns_less(flagsReg crx) %{ + // no match-rule, false predicate + effect(DEF crx); + predicate(false); + + ins_variable_size_depending_on_alignment(true); + + format %{ "cmov $crx" %} + // Worst case is branch + move + stop, no stop without scheduler. + size(false /* TODO: PPC PORT Compile::current()->do_hb_scheduling()*/ ? 16 : 12); + ins_encode( enc_cmove_bns_less(crx) ); + ins_pipe(pipe_class_default); +%} + +// Compare floating, generate condition code. +instruct cmpF_reg_reg_Ex(flagsReg crx, regF src1, regF src2) %{ + // FIXME: should we match 'If cmp (CmpF src1 src2))' ?? + // + // The following code sequence occurs a lot in mpegaudio: + // + // block BXX: + // 0: instruct cmpFUnordered_reg_reg (cmpF_reg_reg-0): + // cmpFUrd CCR6, F11, F9 + // 4: instruct cmov_bns_less (cmpF_reg_reg-1): + // cmov CCR6 + // 8: instruct branchConSched: + // B_FARle CCR6, B56 P=0.500000 C=-1.000000 + match(Set crx (CmpF src1 src2)); + ins_cost(DEFAULT_COST+BRANCH_COST); + + format %{ "CmpF $crx, $src1, $src2 \t// late expanded" %} + lateExpand( lateExpand_cmpF_reg_reg(crx, src1, src2) ); +%} + +// Compare float, generate -1,0,1 +instruct cmpF3_reg_reg_ExEx(iRegIdst dst, regF src1, regF src2) %{ + match(Set dst (CmpF3 src1 src2)); + ins_cost(DEFAULT_COST*5+BRANCH_COST); + + expand %{ + flagsReg tmp1; + cmpFUnordered_reg_reg(tmp1, src1, src2); + cmovI_conIvalueMinus1_conIvalue0_conIvalue1_Ex(dst, tmp1); + %} +%} + +instruct cmpDUnordered_reg_reg(flagsReg crx, regD src1, regD src2) %{ + // no match-rule, false predicate + effect(DEF crx, USE src1, USE src2); + predicate(false); + + format %{ "cmpFUrd $crx, $src1, $src2" %} + size(4); + ins_encode( enc_fcmpu(crx, src1, src2) ); + ins_pipe(pipe_class_default); +%} + +instruct cmpD_reg_reg_Ex(flagsReg crx, regD src1, regD src2) %{ + match(Set crx (CmpD src1 src2)); + ins_cost(DEFAULT_COST+BRANCH_COST); + + format %{ "CmpD $crx, $src1, $src2 \t// late expanded" %} + lateExpand( lateExpand_cmpD_reg_reg(crx, src1, src2) ); +%} + +// Compare double, generate -1,0,1 +instruct cmpD3_reg_reg_ExEx(iRegIdst dst, regD src1, regD src2) %{ + match(Set dst (CmpD3 src1 src2)); + ins_cost(DEFAULT_COST*5+BRANCH_COST); + + expand %{ + flagsReg tmp1; + cmpDUnordered_reg_reg(tmp1, src1, src2); + cmovI_conIvalueMinus1_conIvalue0_conIvalue1_Ex(dst, tmp1); + %} +%} + +//----------Branches--------------------------------------------------------- +// Jump + +// Direct Branch. +instruct branch(label labl) %{ + match(Goto); + effect(USE labl); + ins_cost(BRANCH_COST); + + format %{ "B $labl" %} + size(4); + ins_encode( enc_b(labl) ); + ins_pipe(pipe_class_default); +%} + +// Conditional Near Branch +instruct branchCon(cmpOp cmp, flagsReg crx, label lbl) %{ + // Same match rule as `branchConFar'. + match(If cmp crx); + effect(USE lbl); + ins_cost(BRANCH_COST); + + // If set to 1 this indicates that the current instruction is a + // short variant of a long branch. This avoids using this + // instruction in first-pass matching. It will then only be used in + // the `Shorten_branches' pass. + ins_short_branch(1); + + format %{ "B$cmp $crx, $lbl" %} + size(4); + ins_encode( enc_bc(crx, cmp, lbl) ); + ins_pipe(pipe_class_default); +%} + +// This is for cases when the ppc64 `bc' instruction does not +// reach far enough. So we emit a far branch here, which is more +// expensive. +// +// Conditional Far Branch +instruct branchConFar(cmpOp cmp, flagsReg crx, label lbl) %{ + // Same match rule as `branchCon'. + match(If cmp crx); + effect(USE crx, USE lbl); + predicate(!false /* TODO: PPC port HB_Schedule*/); + // Higher cost than `branchCon'. + ins_cost(5*BRANCH_COST); + + // This is not a short variant of a branch, but the long variant. + ins_short_branch(0); + + format %{ "B_FAR$cmp $crx, $lbl" %} + size(8); + ins_encode( enc_bc_far(crx, cmp, lbl) ); + ins_pipe(pipe_class_default); +%} + +// Conditional Branch used with Power6 scheduler (can be far or short). +instruct branchConSched(cmpOp cmp, flagsReg crx, label lbl) %{ + // Same match rule as `branchCon'. + match(If cmp crx); + effect(USE crx, USE lbl); + predicate(false /* TODO: PPC port HB_Schedule*/); + // Higher cost than `branchCon'. + ins_cost(5*BRANCH_COST); + + // Actually size doesn't depend on alignment but on shortening. + ins_variable_size_depending_on_alignment(true); + // long variant. + ins_short_branch(0); + + format %{ "B_FAR$cmp $crx, $lbl" %} + size(8); // worst case + ins_encode( enc_bc_short_far(crx, cmp, lbl) ); + ins_pipe(pipe_class_default); +%} + +instruct branchLoopEnd(cmpOp cmp, flagsReg crx, label labl) %{ + match(CountedLoopEnd cmp crx); + effect(USE labl); + ins_cost(BRANCH_COST); + + // short variant. + ins_short_branch(1); + + format %{ "B$cmp $crx, $labl \t// counted loop end" %} + size(4); + ins_encode( enc_bc(crx, cmp, labl) ); + ins_pipe(pipe_class_default); +%} + +instruct branchLoopEndFar(cmpOp cmp, flagsReg crx, label labl) %{ + match(CountedLoopEnd cmp crx); + effect(USE labl); + predicate(!false /* TODO: PPC port HB_Schedule */); + ins_cost(BRANCH_COST); + + // Long variant. + ins_short_branch(0); + + format %{ "B_FAR$cmp $crx, $labl \t// counted loop end" %} + size(8); + ins_encode( enc_bc_far(crx, cmp, labl) ); + ins_pipe(pipe_class_default); +%} + +// Conditional Branch used with Power6 scheduler (can be far or short). +instruct branchLoopEndSched(cmpOp cmp, flagsReg crx, label labl) %{ + match(CountedLoopEnd cmp crx); + effect(USE labl); + predicate(false /* TODO: PPC port HB_Schedule */); + // Higher cost than `branchCon'. + ins_cost(5*BRANCH_COST); + + // Actually size doesn't depend on alignment but on shortening. + ins_variable_size_depending_on_alignment(true); + // Long variant. + ins_short_branch(0); + + format %{ "B_FAR$cmp $crx, $labl \t// counted loop end" %} + size(8); // worst case + ins_encode( enc_bc_short_far(crx, cmp, labl) ); + ins_pipe(pipe_class_default); +%} + +// ============================================================================ +// Java runtime operations, intrinsics and other complex operations. + +// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass +// array for an instance of the superklass. Set a hidden internal cache on a +// hit (cache is checked with exposed code in gen_subtype_check()). Return +// not zero for a miss or zero for a hit. The encoding ALSO sets flags. +// +// GL TODO: Improve this. +// - result should not be a TEMP +// - Add match rule as on sparc avoiding additional Cmp. +instruct partialSubtypeCheck(iRegPdst result, iRegP_N2P subklass, iRegP_N2P superklass, + iRegPdst tmp_klass, iRegPdst tmp_arrayptr) %{ + match(Set result (PartialSubtypeCheck subklass superklass)); + effect(TEMP result, TEMP tmp_klass, TEMP tmp_arrayptr); + ins_cost(DEFAULT_COST*10); + + format %{ "PartialSubtypeCheck $result = ($subklass instanceOf $superklass) tmp: $tmp_klass, $tmp_arrayptr" %} + ins_encode( enc_PartialSubtypeCheck(result, subklass, superklass, tmp_klass, tmp_arrayptr) ); + ins_pipe(pipe_class_default); +%} + +// inlined locking and unlocking + +instruct cmpFastLock(flagsReg pcc, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3) %{ + match(Set pcc (FastLock oop box)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + + format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2, $tmp3" %} + ins_encode( enc_compiler_fast_lock(pcc, oop, box, tmp1, tmp2, tmp3) ); + ins_pipe(pipe_class_compare); +%} + +instruct cmpFastUnlock(flagsReg pcc, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3) %{ + match(Set pcc (FastUnlock oop box)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + + format %{ "FASTUNLOCK $oop, $box, $tmp1, $tmp2" %} + ins_encode( enc_compiler_fast_unlock(pcc, oop, box, tmp1, tmp2, tmp3) ); + ins_pipe(pipe_class_compare); +%} + +// Align address. +instruct align_addr(iRegPdst dst, iRegPsrc src, immLnegpow2 mask) %{ + match(Set dst (CastX2P (AndL (CastP2X src) mask))); + + format %{ "ANDDI $dst, $src, $mask \t// next aligned address" %} + size(4); + ins_encode( enc_andL_reg_immLnegpow2(dst, src, mask) ); + ins_pipe(pipe_class_default); +%} + +// Array size computation. +instruct array_size(iRegLdst dst, iRegPsrc end, iRegPsrc start) %{ + match(Set dst (SubL (CastP2X end) (CastP2X start))); + + format %{ "SUB $dst, $end, $start \t// array size in bytes" %} + size(4); + ins_encode( enc_subf(dst, start, end)); + ins_pipe(pipe_class_default); +%} + +// Clear-array with dynamic array-size. +instruct inlineCallClearArray(rarg1RegL cnt, rarg2RegP base, Universe dummy, regCTR ctr) %{ + match(Set dummy (ClearArray cnt base)); + effect(USE_KILL cnt, USE_KILL base, KILL ctr); + ins_cost(MEMORY_REF_COST); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "ClearArray $cnt, $base" %} + ins_encode( enc_ClearArray_reg_reg(cnt, base) ); + ins_pipe(pipe_class_default); +%} + +// String_IndexOf for needle of length 1. +// +// Match needle into immediate operands: no loadConP node needed. Saves one +// register and two instructions over string_indexOf_imm1Node. +// +// Assumes register result differs from all input registers. +// +// Preserves registers haystack, haycnt +// Kills registers tmp1, tmp2 +// Defines registers result +// +// Use dst register classes if register gets killed, as it is the case for tmp registers! +// +// Unfortunately this does not match too often. In many situations the AddP is used +// by several nodes, even several StrIndexOf nodes, breaking the match tree. +instruct string_indexOf_imm1_char(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, + immP needleImm, immL offsetImm, immI_1 needlecntImm, + iRegIdst tmp1, iRegIdst tmp2, + flagsRegCR0 cr0, flagsRegCR1 cr1) %{ + predicate(SpecialStringIndexOf); // type check implicit by parameter type, See Matcher::match_rule_supported + match(Set result (StrIndexOf (Binary haystack haycnt) (Binary (AddP needleImm offsetImm) needlecntImm))); + + effect(TEMP result, TEMP tmp1, TEMP tmp2, KILL cr0, KILL cr1); + + ins_cost(150); + format %{ "String IndexOf CSCL1 $haystack[0..$haycnt], $needleImm+$offsetImm[0..$needlecntImm]" + "-> $result \t// KILL $haycnt, $tmp1, $tmp2, $cr0, $cr1" %} + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted + ins_encode( enc_String_IndexOf_imm1_char(result, haystack, haycnt, needleImm, offsetImm, tmp1, tmp2) ); + ins_pipe(pipe_class_compare); +%} + +// String_IndexOf for needle of length 1. +// +// Special case requires less registers and emits less instructions. +// +// Assumes register result differs from all input registers. +// +// Preserves registers haystack, haycnt +// Kills registers tmp1, tmp2, needle +// Defines registers result +// +// Use dst register classes if register gets killed, as it is the case for tmp registers! +instruct string_indexOf_imm1(iRegIdst result, iRegPsrc haystack, iRegIsrc haycnt, + rscratch2RegP needle, immI_1 needlecntImm, + iRegIdst tmp1, iRegIdst tmp2, + flagsRegCR0 cr0, flagsRegCR1 cr1) %{ + match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecntImm))); + effect(USE_KILL needle, /* TDEF needle, */ TEMP result, + TEMP tmp1, TEMP tmp2); + // Required for EA: check if it is still a type_array. + predicate(SpecialStringIndexOf && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && + n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop()->is_type_array()); + ins_cost(180); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String IndexOf SCL1 $haystack[0..$haycnt], $needle[0..$needlecntImm]" + " -> $result \t// KILL $haycnt, $needle, $tmp1, $tmp2, $cr0, $cr1" %} + ins_encode( enc_String_IndexOf_imm1(result, haystack, haycnt, needle, tmp1, tmp2) ); + ins_pipe(pipe_class_compare); +%} + +// String_IndexOf. +// +// Length of needle as immediate. This saves instruction loading constant needle +// length. +// @@@ TODO Specify rules for length < 8 or so, and roll out comparison of needle +// completely or do it in vector instruction. This should save registers for +// needlecnt and needle. +// +// Assumes register result differs from all input registers. +// Overwrites haycnt, needlecnt. +// Use dst register classes if register gets killed, as it is the case for tmp registers! +instruct string_indexOf_imm(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt, + iRegPsrc needle, uimmI15 needlecntImm, + iRegIdst tmp1, iRegIdst tmp2, iRegIdst tmp3, iRegIdst tmp4, iRegIdst tmp5, + flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6) %{ + match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecntImm))); + effect(USE_KILL haycnt, /* better: TDEF haycnt, */ TEMP result, + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, KILL cr0, KILL cr1, KILL cr6); + // Required for EA: check if it is still a type_array. + predicate(SpecialStringIndexOf && n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop() && + n->in(3)->in(1)->bottom_type()->is_aryptr()->const_oop()->is_type_array()); + ins_cost(250); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String IndexOf SCL $haystack[0..$haycnt], $needle[0..$needlecntImm]" + " -> $result \t// KILL $haycnt, $tmp1, $tmp2, $tmp3, $tmp4, $tmp5, $cr0, $cr1" %} + ins_encode( enc_String_IndexOf_imm(result, haystack, haycnt, needle, needlecntImm, tmp1, tmp2, tmp3, tmp4, tmp5) ); + ins_pipe(pipe_class_compare); +%} + +// StrIndexOf node. +// +// Assumes register result differs from all input registers. +// Overwrites haycnt, needlecnt. +// Use dst register classes if register gets killed, as it is the case for tmp registers! +instruct string_indexOf(iRegIdst result, iRegPsrc haystack, rscratch1RegI haycnt, iRegPsrc needle, rscratch2RegI needlecnt, + iRegLdst tmp1, iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, + flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6) %{ + match(Set result (StrIndexOf (Binary haystack haycnt) (Binary needle needlecnt))); + effect(USE_KILL haycnt, USE_KILL needlecnt, /*better: TDEF haycnt, TDEF needlecnt,*/ + TEMP result, + TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr0, KILL cr1, KILL cr6); + predicate(SpecialStringIndexOf); // See Matcher::match_rule_supported. + ins_cost(300); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String IndexOf $haystack[0..$haycnt], $needle[0..$needlecnt]" + " -> $result \t// KILL $haycnt, $needlecnt, $tmp1, $tmp2, $tmp3, $tmp4, $cr0, $cr1" %} + ins_encode( enc_String_IndexOf(result, haystack, haycnt, needle, needlecnt, tmp1, tmp2, tmp3, tmp4) ); + ins_pipe(pipe_class_compare); +%} + +// String equals with immediate. +instruct string_equals_imm(iRegPsrc str1, iRegPsrc str2, uimmI15 cntImm, iRegIdst result, + iRegPdst tmp1, iRegPdst tmp2, + flagsRegCR0 cr0, flagsRegCR6 cr6, regCTR ctr) %{ + match(Set result (StrEquals (Binary str1 str2) cntImm)); + effect(TEMP result, TEMP tmp1, TEMP tmp2, + KILL cr0, KILL cr6, KILL ctr); + predicate(SpecialStringEquals); // See Matcher::match_rule_supported. + ins_cost(250); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String Equals SCL [0..$cntImm]($str1),[0..$cntImm]($str2)" + " -> $result \t// KILL $cr0, $cr6, $ctr, TEMP $result, $tmp1, $tmp2" %} + ins_encode( enc_String_EqualsImm(str1, str2, cntImm, result, tmp1, tmp2) ); + ins_pipe(pipe_class_compare); +%} + +// String equals. +// Use dst register classes if register gets killed, as it is the case for TEMP operands! +instruct string_equals(iRegPsrc str1, iRegPsrc str2, iRegIsrc cnt, iRegIdst result, + iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, iRegPdst tmp4, iRegPdst tmp5, + flagsRegCR0 cr0, flagsRegCR1 cr1, flagsRegCR6 cr6, regCTR ctr) %{ + match(Set result (StrEquals (Binary str1 str2) cnt)); + effect(TEMP result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, + KILL cr0, KILL cr1, KILL cr6, KILL ctr); + predicate(SpecialStringEquals); // See Matcher::match_rule_supported. + ins_cost(300); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String Equals [0..$cnt]($str1),[0..$cnt]($str2) -> $result" + " \t// KILL $cr0, $cr1, $cr6, $ctr, TEMP $result, $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} + ins_encode( enc_String_Equals(str1, str2, cnt, result, tmp1, tmp2, tmp3, tmp4, tmp5) ); + ins_pipe(pipe_class_compare); +%} + +// String compare. +// Char[] pointers are passed in. +// Use dst register classes if register gets killed, as it is the case for TEMP operands! +instruct string_compare(rarg1RegP str1, rarg2RegP str2, rarg3RegI cnt1, rarg4RegI cnt2, iRegIdst result, + iRegPdst tmp, flagsRegCR0 cr0, regCTR ctr) %{ + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(USE_KILL cnt1, USE_KILL cnt2, USE_KILL str1, USE_KILL str2, TEMP result, TEMP tmp, KILL cr0, KILL ctr); + ins_cost(300); + + ins_alignment(8); // 'compute_padding()' gets called, up to this number-1 nops will get inserted. + + format %{ "String Compare $str1[0..$cnt1], $str2[0..$cnt2] -> $result" + " \t// TEMP $tmp, $result KILLs $str1, $cnt1, $str2, $cnt2, $cr0, $ctr" %} + ins_encode( enc_String_Compare(str1, str2, cnt1, cnt2, result, tmp) ); + ins_pipe(pipe_class_compare); +%} + +//---------- Min/Max Instructions --------------------------------------------- + +instruct minI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (MinI src1 src2)); + ins_cost(DEFAULT_COST*6); + + expand %{ + iRegLdst src1s; + iRegLdst src2s; + iRegLdst diff; + iRegLdst sm; + iRegLdst doz; // difference or zero + convI2L_reg(src1s, src1); // Ensure proper sign extension. + convI2L_reg(src2s, src2); // Ensure proper sign extension. + subL_reg_reg(diff, src2s, src1s); + // Need to consider >=33 bit result, therefore we need signmaskL. + signmask64L_regL(sm, diff); + andL_reg_reg(doz, diff, sm); // <=0 + addI_regL_regL(dst, doz, src1s); + %} +%} + +instruct maxI_reg_reg_Ex(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + match(Set dst (MaxI src1 src2)); + ins_cost(DEFAULT_COST*6); + + expand %{ + iRegLdst src1s; + iRegLdst src2s; + iRegLdst diff; + iRegLdst sm; + iRegLdst doz; // difference or zero + convI2L_reg(src1s, src1); // Ensure proper sign extension. + convI2L_reg(src2s, src2); // Ensure proper sign extension. + subL_reg_reg(diff, src2s, src1s); + // Need to consider >=33 bit result, therefore we need signmaskL. + signmask64L_regL(sm, diff); + andcL_reg_reg(doz, diff, sm); // >=0 + addI_regL_regL(dst, doz, src1s); + %} +%} + +//---------- Population Count Instructions ------------------------------------ + +// Popcnt for Power7. +instruct popCountI(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (PopCountI src)); + predicate(UsePopCountInstruction && VM_Version::has_popcntw()); + ins_cost(DEFAULT_COST); + + format %{ "POPCNTW $dst, $src" %} + size(4); + ins_encode( enc_popcntw(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Popcnt for Power7. +instruct popCountL(iRegIdst dst, iRegLsrc src) %{ + predicate(UsePopCountInstruction && VM_Version::has_popcntw()); + match(Set dst (PopCountL src)); + ins_cost(DEFAULT_COST); + + format %{ "POPCNTD $dst, $src" %} + size(4); + ins_encode( enc_popcntd(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct countLeadingZerosI(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (CountLeadingZerosI src)); + predicate(UseCountLeadingZerosInstructionsPPC64); // See Matcher::match_rule_supported. + ins_cost(DEFAULT_COST); + + format %{ "CNTLZW $dst, $src" %} + size(4); + ins_encode( enc_cntlzw(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct countLeadingZerosL(iRegIdst dst, iRegLsrc src) %{ + match(Set dst (CountLeadingZerosL src)); + predicate(UseCountLeadingZerosInstructionsPPC64); // See Matcher::match_rule_supported. + ins_cost(DEFAULT_COST); + + format %{ "CNTLZD $dst, $src" %} + size(4); + ins_encode( enc_cntlzd(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct countLeadingZerosP(iRegIdst dst, iRegPsrc src) %{ + // no match-rule, false predicate + effect(DEF dst, USE src); + predicate(false); + + format %{ "CNTLZD $dst, $src" %} + size(4); + ins_encode( enc_cntlzd(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct countTrailingZerosI_Ex(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (CountTrailingZerosI src)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immI16 imm1 %{ (int)-1 %} + immI16 imm2 %{ (int)32 %} + immI_minus1 m1 %{ -1 %} + iRegIdst tmpI1; + iRegIdst tmpI2; + iRegIdst tmpI3; + addI_reg_imm16(tmpI1, src, imm1); + andcI_reg_reg(tmpI2, src, m1, tmpI1); + countLeadingZerosI(tmpI3, tmpI2); + subI_imm16_reg(dst, imm2, tmpI3); + %} +%} + +instruct countTrailingZerosL_Ex(iRegIdst dst, iRegLsrc src) %{ + match(Set dst (CountTrailingZerosL src)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immL16 imm1 %{ (long)-1 %} + immI16 imm2 %{ (int)64 %} + iRegLdst tmpL1; + iRegLdst tmpL2; + iRegIdst tmpL3; + addL_reg_imm16(tmpL1, src, imm1); + andcL_reg_reg(tmpL2, tmpL1, src); + countLeadingZerosL(tmpL3, tmpL2); + subI_imm16_reg(dst, imm2, tmpL3); + %} +%} + +// Expand nodes for byte_reverse_int. +instruct insrwi_a(iRegIdst dst, iRegIsrc src, immI16 imm1, immI16 imm2) %{ + effect(DEF dst, USE src, USE imm1, USE imm2); + predicate(false); + + format %{ "INSRWI $dst, $src, $imm1, $imm2" %} + size(4); + ins_encode( enc_insrwi(dst, imm1, src, imm2) ); + ins_pipe(pipe_class_default); +%} + +// As insrwi_a, but with USE_DEF. +instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 imm1, immI16 imm2) %{ + effect(USE_DEF dst, USE src, USE imm1, USE imm2); + predicate(false); + + format %{ "INSRWI $dst, $src, $imm1, $imm2" %} + size(4); + ins_encode( enc_insrwi(dst, imm1, src, imm2) ); + ins_pipe(pipe_class_default); +%} + +// Just slightly faster than java implementation. +instruct bytes_reverse_int_Ex(iRegIdst dst, iRegIsrc src) %{ + match(Set dst (ReverseBytesI src)); + predicate(UseCountLeadingZerosInstructionsPPC64); + ins_cost(DEFAULT_COST); + + expand %{ + immI16 imm24 %{ (int) 24 %} + immI16 imm16 %{ (int) 16 %} + immI16 imm8 %{ (int) 8 %} + immI16 imm4 %{ (int) 4 %} + immI16 imm0 %{ (int) 0 %} + iRegLdst tmpI1; + iRegLdst tmpI2; + iRegLdst tmpI3; + + urShiftI_reg_imm(tmpI1, src, imm24); + insrwi_a(dst, tmpI1, imm24, imm8); + urShiftI_reg_imm(tmpI2, src, imm16); + insrwi(dst, tmpI2, imm8, imm16); + urShiftI_reg_imm(tmpI3, src, imm8); + insrwi(dst, tmpI3, imm8, imm8); + insrwi(dst, src, imm0, imm8); + %} +%} + +//---------- Replicate Vector Instructions ------------------------------------ + +// Insrdi does replicate if src == dst. +instruct repl32(iRegLdst dst) %{ + predicate(false); + effect(USE_DEF dst); + + format %{ "INSRDI $dst, #0, $dst, #32 \t// replicate" %} + size(4); + ins_encode( enc_insrdi(dst, (0), dst, (32)) ); + ins_pipe(pipe_class_default); +%} + +// Insrdi does replicate if src == dst. +instruct repl48(iRegLdst dst) %{ + predicate(false); + effect(USE_DEF dst); + + format %{ "INSRDI $dst, #0, $dst, #48 \t// replicate" %} + size(4); + ins_encode( enc_insrdi(dst, (0), dst, (48)) ); + ins_pipe(pipe_class_default); +%} + +// Insrdi does replicate if src == dst. +instruct repl56(iRegLdst dst) %{ + predicate(false); + effect(USE_DEF dst); + + format %{ "INSRDI $dst, #0, $dst, #56 \t// replicate" %} + size(4); + ins_encode( enc_insrdi(dst, (0), dst, (56)) ); + ins_pipe(pipe_class_default); +%} + +instruct repl8B_reg_Ex(iRegLdst dst, iRegIsrc src) %{ + match(Set dst (ReplicateB src)); + predicate(n->as_Vector()->length() == 8); + expand %{ + moveReg(dst, src); + repl56(dst); + repl48(dst); + repl32(dst); + %} +%} + +instruct repl8B_immI0(iRegLdst dst, immI_0 zero) %{ + match(Set dst (ReplicateB zero)); + predicate(n->as_Vector()->length() == 8); + format %{ "LI $dst, #0 \t// replicate8B" %} + size(4); + ins_encode( enc_li(dst, zero) ); + ins_pipe(pipe_class_default); +%} + +instruct repl8B_immIminus1(iRegLdst dst, immI_minus1 src) %{ + match(Set dst (ReplicateB src)); + predicate(n->as_Vector()->length() == 8); + format %{ "LI $dst, #-1 \t// replicate8B" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct repl4S_reg_Ex(iRegLdst dst, iRegIsrc src) %{ + match(Set dst (ReplicateS src)); + predicate(n->as_Vector()->length() == 4); + expand %{ + moveReg(dst, src); + repl48(dst); + repl32(dst); + %} +%} + +instruct repl4S_immI0(iRegLdst dst, immI_0 zero) %{ + match(Set dst (ReplicateS zero)); + predicate(n->as_Vector()->length() == 4); + format %{ "LI $dst, #0 \t// replicate4C" %} + size(4); + ins_encode( enc_li(dst, zero) ); + ins_pipe(pipe_class_default); +%} + +instruct repl4S_immIminus1(iRegLdst dst, immI_minus1 src) %{ + match(Set dst (ReplicateS src)); + predicate(n->as_Vector()->length() == 4); + format %{ "LI $dst, -1 \t// replicate4C" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +instruct repl2I_reg_Ex(iRegLdst dst, iRegIsrc src) %{ + match(Set dst (ReplicateI src)); + predicate(n->as_Vector()->length() == 2); + ins_cost(2 * DEFAULT_COST); + expand %{ + moveReg(dst, src); + repl32(dst); + %} +%} + +instruct repl2I_immI0(iRegLdst dst, immI_0 zero) %{ + match(Set dst (ReplicateI zero)); + predicate(n->as_Vector()->length() == 2); + format %{ "LI $dst, #0 \t// replicate4C" %} + size(4); + ins_encode( enc_li(dst, zero) ); + ins_pipe(pipe_class_default); +%} + +instruct repl2I_immIminus1(iRegLdst dst, immI_minus1 src) %{ + match(Set dst (ReplicateI src)); + predicate(n->as_Vector()->length() == 2); + format %{ "LI $dst, -1 \t// replicate4C" %} + size(4); + ins_encode( enc_li(dst, src) ); + ins_pipe(pipe_class_default); +%} + +// Move float to int register via stack, replicate. +instruct repl2F_reg_Ex(iRegLdst dst, regF src) %{ + match(Set dst (ReplicateF src)); + predicate(n->as_Vector()->length() == 2); + ins_cost(2 * MEMORY_REF_COST + DEFAULT_COST); + expand %{ + stackSlotL tmpS; + iRegIdst tmpI; + moveF2I_reg_stack(tmpS, src); // Move float to stack. + moveF2I_stack_reg(tmpI, tmpS); // Move stack to int reg. + moveReg(dst, tmpI); // Move int to long reg. + repl32(dst); // Replicate bitpattern. + %} +%} + +// Replicate scalar constant to packed float values in Double register +instruct repl2F_immF_Ex(iRegLdst dst, immF src) %{ + match(Set dst (ReplicateF src)); + predicate(n->as_Vector()->length() == 2); + ins_cost(5 * DEFAULT_COST); + + format %{ "LD $dst, offset, $constanttablebase\t// load replicated float $src $src from table, late expanded" %} + lateExpand( lateExpand_load_replF_constant(dst, src, constanttablebase) ); +%} + +// Replicate scalar zero constant to packed float values in Double register +instruct repl2F_immF0(iRegLdst dst, immF_0 zero) %{ + match(Set dst (ReplicateF zero)); + predicate(n->as_Vector()->length() == 2); + + format %{ "LI $dst, #0 \t// replicate2F" %} + ins_encode( enc_li(dst, 0x0) ); + ins_pipe(pipe_class_default); +%} + +// ============================================================================ +// Safepoint Instruction + +instruct safePoint_poll(iRegPdst poll) %{ + match(SafePoint poll); + predicate(LoadPollAddressFromThread); + + // It caused problems to add the effect that r0 is killed, but this + // effect no longer needs to be mentioned, since r0 is not contained + // in a reg_class. + + format %{ "LD R0, #0, $poll \t// Safepoint poll for GC" %} + size(4); + ins_encode( enc_poll(0x0, poll) ); + ins_pipe(pipe_class_default); +%} + +// Load constant address of polling page into register. +// This node shall not match: use immP_NM! +instruct loadConPollAddr(rscratch2RegP poll, immP_NM src) %{ + match(Set poll src); + format %{ "LD $poll, addr of polling page \t// load constant optimized for safepoint.\n" %} + + // Size depends on constant. + ins_encode( enc_loadConPollAddr(poll) ); + ins_pipe(pipe_class_default); +%} + +// Safepoint without per-thread support. Load address of page to poll +// as constant. +// Rscratch2RegP is R12. +// LoadConPollAddr node is added in pd_post_matching_hook(). It must be +// a seperate node so that the oop map is at the right location. +instruct safePoint_poll_conPollAddr(rscratch2RegP poll) %{ + match(SafePoint poll); + predicate(!LoadPollAddressFromThread); + + // It caused problems to add the effect that r0 is killed, but this + // effect no longer needs to be mentioned, since r0 is not contained + // in a reg_class. + + format %{ "LD R12, addr of polling page\n\t" + "LD R0, #0, R12 \t// Safepoint poll for GC" %} + ins_encode( enc_poll(0x0, poll) ); + ins_pipe(pipe_class_default); +%} + +// ============================================================================ +// Call Instructions + +// Call Java Static Instruction + +// Schedulable version of call static node. +instruct CallStaticJavaDirect(method meth) %{ + match(CallStaticJava); + effect(USE meth); + predicate(!((CallStaticJavaNode*)n)->is_method_handle_invoke()); + ins_cost(CALL_COST); + + ins_num_consts(3 /* up to 3 patchable constants: inline cache, 2 call targets. */); + + format %{ "CALL,static $meth \t// ==> " %} + size(4); + ins_encode( enc_java_static_call(meth) ); + ins_pipe(pipe_class_call); +%} + +// Schedulable version of call static node. +instruct CallStaticJavaDirectHandle(method meth) %{ + match(CallStaticJava); + effect(USE meth); + predicate(((CallStaticJavaNode*)n)->is_method_handle_invoke()); + ins_cost(CALL_COST); + + ins_num_consts(3 /* up to 3 patchable constants: inline cache, 2 call targets. */); + + format %{ "CALL,static $meth \t// ==> " %} + ins_encode( enc_java_handle_call(meth) ); + ins_pipe(pipe_class_call); +%} + +// Call Java Dynamic Instruction + +// Used by late expand of CallDynamicJavaDirectSchedEx (actual call). +// Loading of IC was late expanded. The nodes loading the IC are reachable +// via fields ins_field_load_ic_hi_node and ins_field_load_ic_node. +// The call destination must still be placed in the constant pool. +instruct CallDynamicJavaDirectSched(method meth) %{ + match(CallDynamicJava); // To get all the data fields we need ... + effect(USE meth); + predicate(false); // ... but never match. + + ins_field_load_ic_hi_node(loadConL_hiNode*); + ins_field_load_ic_node(loadConLNode*); + ins_num_consts(1 /* 1 patchable constant: call destination */); + + format %{ "BL \t// dynamic $meth ==> " %} + size(4); + ins_encode( enc_java_dynamic_call_sched(meth) ); + ins_pipe(pipe_class_call); +%} + +// Schedulable (i.e. late expanded) version of call dynamic java. +// We use late expanded calls if we use inline caches +// and do not update method data. +// +// This instruction has two constants: inline cache (IC) and call destination. +// Loading the inline cache will be late expanded, thus leaving a call with +// one constant. +instruct CallDynamicJavaDirectSched_Ex(method meth) %{ + match(CallDynamicJava); + effect(USE meth); + predicate(UseInlineCaches); + ins_cost(CALL_COST); + + ins_requires_toc(true); + ins_num_consts(2 /* 2 patchable constants: inline cache, call destination. */); + + format %{ "CALL,dynamic $meth \t// late expanded" %} + lateExpand( lateExpand_java_dynamic_call_sched(meth) ); +%} + +// Compound version of call dynamic java +// We use late expanded calls if we use inline caches +// and do not update method data. +instruct CallDynamicJavaDirect(method meth) %{ + match(CallDynamicJava); + effect(USE meth); + predicate(!UseInlineCaches); + ins_cost(CALL_COST); + + // Enc_java_to_runtime_call needs up to 4 constants (method data oop). + ins_num_consts(4); + ins_requires_toc(true); + + format %{ "CALL,dynamic $meth \t// ==> " %} + ins_encode( enc_java_dynamic_call(meth) ); + ins_pipe(pipe_class_call); +%} + +// Call Runtime Instruction + +instruct CallRuntimeDirect(method meth) %{ + match(CallRuntime); + effect(USE meth); + ins_cost(CALL_COST); + + // Enc_java_to_runtime_call needs up to 3 constants: call target, + // env for callee, C-toc. + ins_num_consts(3); + + format %{ "CALL,runtime" %} + ins_encode( enc_java_to_runtime_call(meth) ); + ins_pipe(pipe_class_call); +%} + +// Call Leaf + +// Used by late expand of CallLeafDirectSched (mtctr). +instruct CallLeafDirect_mtctr(iRegLdst dst, iRegLsrc src) %{ + effect(DEF dst, USE src); + + ins_num_consts(1); + + format %{ "MTCTR $src" %} + size(4); + ins_encode( enc_leaf_call_mtctr(src) ); + ins_pipe(pipe_class_default); +%} + +// Used by late expand of CallLeafDirectSched (actual call). +instruct CallLeafDirect(method meth) %{ + match(CallLeaf); // To get the data all the data fields we need ... + effect(USE meth); + predicate(false); // but never match. + + format %{ "BCTRL \t// leaf call $meth ==> " %} + size(4); + ins_encode( enc_leaf_call(meth) ); + ins_pipe(pipe_class_call); +%} + +// Late expand of CallLeafDirect. +// Load adress to call from TOC, then bl to it. +instruct CallLeafDirect_Ex(method meth) %{ + match(CallLeaf); + effect(USE meth); + ins_cost(CALL_COST); + + // Enc_java_to_runtime_call needs up to 3 constants: call target, + // env for callee, C-toc. + ins_num_consts(3); + ins_requires_toc(true); + + format %{ "CALL,runtime leaf $meth \t// late expanded" %} + lateExpand( lateExpand_java_to_runtime_call(meth) ); +%} + +// Call runtime without safepoint - same as CallLeaf. +// Late expand of CallLeafNoFPDirect. +// Load adress to call from TOC, then bl to it. +instruct CallLeafNoFPDirect_Ex(method meth) %{ + match(CallLeafNoFP); + effect(USE meth); + ins_cost(CALL_COST); + + // Enc_java_to_runtime_call needs up to 3 constants: call target, + // env for callee, C-toc. + ins_num_consts(3); + ins_requires_toc(true); + + format %{ "CALL,runtime leaf nofp $meth \t// late expanded" %} + lateExpand( lateExpand_java_to_runtime_call(meth) ); +%} + +// Tail Call; Jump from runtime stub to Java code. +// Also known as an 'interprocedural jump'. +// Target of jump will eventually return to caller. +// TailJump below removes the return address. +instruct TailCalljmpInd(iRegPdstNoScratch jump_target, inline_cache_regP method_oop) %{ + match(TailCall jump_target method_oop); + ins_cost(CALL_COST); + + format %{ "MTCTR $jump_target \t// $method_oop holds method oop\n\t" + "BCTR \t// tail call" %} + size(8); + ins_encode( enc_form_jmpl(jump_target) ); + ins_pipe(pipe_class_call); +%} + +// Return Instruction +instruct Ret() %{ + match(Return); + format %{ "BLR \t// branch to link register" %} + size(4); + ins_encode( enc_Ret() ); + ins_pipe(pipe_class_default); +%} + +// Tail Jump; remove the return address; jump to target. +// TailCall above leaves the return address around. +// TailJump is used in only one place, the rethrow_Java stub (fancy_jump=2). +// ex_oop (Exception Oop) is needed in %o0 at the jump. As there would be a +// "restore" before this instruction (in Epilogue), we need to materialize it +// in %i0. +instruct tailjmpInd(iRegPdstNoScratch jump_target, rarg1RegP ex_oop) %{ + match(TailJump jump_target ex_oop); + ins_cost(CALL_COST); + + format %{ "LD R4_ARG2 = LR\n\t" + "MTCTR $jump_target\n\t" + "BCTR \t// TailJump, exception oop: $ex_oop" %} + size(12); + ins_encode( enc_jump_set_exception_pc(jump_target) ); + ins_pipe(pipe_class_call); +%} + +// Create exception oop: created by stack-crawling runtime code. +// Created exception is now available to this handler, and is setup +// just prior to jumping to this handler. No code emitted. +instruct CreateException(rarg1RegP ex_oop) %{ + match(Set ex_oop (CreateEx)); + ins_cost(0); + + format %{ " -- \t// exception oop; no code emitted" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_default); +%} + +// Rethrow exception: The exception oop will come in the first +// argument position. Then JUMP (not call) to the rethrow stub code. +instruct RethrowException() %{ + match(Rethrow); + ins_cost(CALL_COST); + + format %{ "Jmp rethrow_stub" %} + ins_encode( enc_rethrow() ); + ins_pipe(pipe_class_call); +%} + +// Die now. +instruct ShouldNotReachHere() %{ + match(Halt); + ins_cost(CALL_COST); + + format %{ "ShouldNotReachHere" %} + size(4); + ins_encode( enc_shouldnotreachhere() ); + ins_pipe(pipe_class_default); +%} + +// This name is KNOWN by the ADLC and cannot be changed. The ADLC +// forces a 'TypeRawPtr::BOTTOM' output type for this guy. +// Get a DEF on threadRegP, no costs, no encoding, use +// 'ins_should_rematerialize(true)' to avoid spilling. +instruct tlsLoadP(threadRegP dst) %{ + match(Set dst (ThreadLocal)); + ins_cost(0); + + ins_should_rematerialize(true); + + format %{ " -- \t// $dst=Thread::current(), empty" %} + size(0); + ins_encode( /*empty*/ ); + ins_pipe(pipe_class_empty); +%} + +//---Some PPC specific nodes--------------------------------------------------- + +// Stop a group. +instruct endGroup() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "End Bundle (ori r1, r1, 0)" %} + size(4); + ins_encode( enc_end_bundle() ); + ins_pipe(pipe_class_default); +%} + +// Nop instructions + +instruct fxNop() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "fxNop" %} + size(4); + ins_encode( enc_fxnop() ); + ins_pipe(pipe_class_default); +%} + +instruct fpNop0() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "fpNop0" %} + size(4); + ins_encode( enc_fpnop0() ); + ins_pipe(pipe_class_default); +%} + +instruct fpNop1() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "fpNop1" %} + size(4); + ins_encode( enc_fpnop1() ); + ins_pipe(pipe_class_default); +%} + +instruct brNop0() %{ + ins_cost(0); + size(4); + format %{ "brNop0" %} + ins_encode( enc_brnop0() ); + ins_is_nop(true); + ins_pipe(pipe_class_default); +%} + +instruct brNop1() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "brNop1" %} + size(4); + ins_encode( enc_brnop1() ); + ins_pipe(pipe_class_default); +%} + +instruct brNop2() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "brNop2" %} + size(4); + ins_encode( enc_brnop2() ); + ins_pipe(pipe_class_default); +%} + +//----------PEEPHOLE RULES----------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. +// +// peepmatch ( root_instr_name [preceeding_instruction]* ); +// +// peepconstraint %{ +// (instruction_number.operand_name relational_op instruction_number.operand_name +// [, ...] ); +// // instruction numbers are zero-based using left to right order in peepmatch +// +// peepreplace ( instr_name ( [instruction_number.operand_name]* ) ); +// // provide an instruction_number.operand_name for each operand that appears +// // in the replacement instruction's match rule +// +// ---------VM FLAGS--------------------------------------------------------- +// +// All peephole optimizations can be turned off using -XX:-OptoPeephole +// +// Each peephole rule is given an identifying number starting with zero and +// increasing by one in the order seen by the parser. An individual peephole +// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=# +// on the command-line. +// +// ---------CURRENT LIMITATIONS---------------------------------------------- +// +// Only match adjacent instructions in same basic block +// Only equality constraints +// Only constraints between operands, not (0.dest_reg == EAX_enc) +// Only one replacement instruction +// +// ---------EXAMPLE---------------------------------------------------------- +// +// // pertinent parts of existing instructions in architecture description +// instruct movI(eRegI dst, eRegI src) %{ +// match(Set dst (CopyI src)); +// %} +// +// instruct incI_eReg(eRegI dst, immI1 src, eFlagsReg cr) %{ +// match(Set dst (AddI dst src)); +// effect(KILL cr); +// %} +// +// // Change (inc mov) to lea +// peephole %{ +// // increment preceeded by register-register move +// peepmatch ( incI_eReg movI ); +// // require that the destination register of the increment +// // match the destination register of the move +// peepconstraint ( 0.dst == 1.dst ); +// // construct a replacement instruction that sets +// // the destination to ( move's source register + one ) +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// Implementation no longer uses movX instructions since +// machine-independent system no longer uses CopyX nodes. +// +// peephole %{ +// peepmatch ( incI_eReg movI ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// peephole %{ +// peepmatch ( decI_eReg movI ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// peephole %{ +// peepmatch ( addI_eReg_imm movI ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaI_eReg_immI( 0.dst 1.src 0.src ) ); +// %} +// +// peephole %{ +// peepmatch ( addP_eReg_imm movP ); +// peepconstraint ( 0.dst == 1.dst ); +// peepreplace ( leaP_eReg_immI( 0.dst 1.src 0.src ) ); +// %} + +// // Change load of spilled value to only a spill +// instruct storeI(memory mem, eRegI src) %{ +// match(Set mem (StoreI mem src)); +// %} +// +// instruct loadI(eRegI dst, memory mem) %{ +// match(Set dst (LoadI mem)); +// %} +// +peephole %{ + peepmatch ( loadI storeI ); + peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); + peepreplace ( storeI( 1.mem 1.mem 1.src ) ); +%} + +peephole %{ + peepmatch ( loadL storeL ); + peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem ); + peepreplace ( storeL( 1.mem 1.mem 1.src ) ); +%} + +peephole %{ + peepmatch ( loadP storeP ); + peepconstraint ( 1.src == 0.dst, 1.dst == 0.mem ); + peepreplace ( storeP( 1.dst 1.dst 1.src ) ); +%} + +//----------SMARTSPILL RULES--------------------------------------------------- +// These must follow all instruction definitions as they use the names +// defined in the instructions definitions. diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/ppc_64.ad --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/ppc_64.ad Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,24 @@ +// +// Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +// Copyright 2012, 2013 SAP AG. 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. +// +// diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/registerMap_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/registerMap_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_REGISTERMAP_PPC_HPP +#define CPU_PPC_VM_REGISTERMAP_PPC_HPP + +// machine-dependent implemention for register maps + friend class frame; + + private: + // This is the hook for finding a register in an "well-known" location, + // such as a register block of a predetermined format. + // Since there is none, we just return NULL. + // See registerMap_sparc.hpp for an example of grabbing registers + // from register save areas of a standard layout. + address pd_location(VMReg reg) const { return NULL; } + + // no PD state to clear or copy: + void pd_clear() {} + void pd_initialize() {} + void pd_initialize_from(const RegisterMap* map) {} + +#endif // CPU_PPC_VM_REGISTERMAP_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/register_definitions_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/register_definitions_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// make sure the defines don't screw up the declarations later on in this file +#define DONT_USE_REGISTER_DEFINES + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "asm/register.hpp" +#include "register_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "interp_masm_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "interp_masm_ppc_64.hpp" +#endif + +REGISTER_DEFINITION(Register, noreg); + +REGISTER_DEFINITION(FloatRegister, fnoreg); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/register_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/register_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "register_ppc.hpp" + +const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers * 2; +const int ConcreteRegisterImpl::max_fpr = ConcreteRegisterImpl::max_gpr + + FloatRegisterImpl::number_of_registers * 2; +const int ConcreteRegisterImpl::max_cnd = ConcreteRegisterImpl::max_fpr + + ConditionRegisterImpl::number_of_registers; + +const char* RegisterImpl::name() const { + const char* names[number_of_registers] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23", + "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31" + }; + return is_valid() ? names[encoding()] : "noreg"; +} + +const char* ConditionRegisterImpl::name() const { + const char* names[number_of_registers] = { + "CR0", "CR1", "CR2", "CR3", "CR4", "CR5", "CR6", "CR7" + }; + return is_valid() ? names[encoding()] : "cnoreg"; +} + +const char* FloatRegisterImpl::name() const { + const char* names[number_of_registers] = { + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", + "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", + "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", + "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31" + }; + return is_valid() ? names[encoding()] : "fnoreg"; +} + +const char* SpecialRegisterImpl::name() const { + const char* names[number_of_registers] = { + "SR_XER", "SR_LR", "SR_CTR", "SR_VRSAVE", "SR_SPEFSCR", "SR_PPR" + }; + return is_valid() ? names[encoding()] : "snoreg"; +} + +const char* VectorRegisterImpl::name() const { + const char* names[number_of_registers] = { + "VR0", "VR1", "VR2", "VR3", "VR4", "VR5", "VR6", "VR7", + "VR8", "VR9", "VR10", "VR11", "VR12", "VR13", "VR14", "VR15", + "VR16", "VR17", "VR18", "VR19", "VR20", "VR21", "VR22", "VR23", + "VR24", "VR25", "VR26", "VR27", "VR28", "VR29", "VR30", "VR31" + }; + return is_valid() ? names[encoding()] : "vnoreg"; +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/register_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/register_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_REGISTER_PPC_HPP +#define CPU_PPC_VM_REGISTER_PPC_HPP + +#include "asm/register.hpp" +#include "vm_version_ppc.hpp" + +// forward declaration +class Address; +class VMRegImpl; +typedef VMRegImpl* VMReg; + +// PPC64 registers +// +// See "64-bit PowerPC ELF ABI Supplement 1.7", IBM Corp. (2003-10-29). +// (http://math-atlas.sourceforge.net/devel/assembly/PPC-elf64abi-1.7.pdf) +// +// r0 Register used in function prologs (volatile) +// r1 Stack pointer (nonvolatile) +// r2 TOC pointer (volatile) +// r3 Parameter and return value (volatile) +// r4-r10 Function parameters (volatile) +// r11 Register used in calls by pointer and as an environment pointer for languages which require one (volatile) +// r12 Register used for exception handling and glink code (volatile) +// r13 Reserved for use as system thread ID +// r14-r31 Local variables (nonvolatile) +// +// f0 Scratch register (volatile) +// f1-f4 Floating point parameters and return value (volatile) +// f5-f13 Floating point parameters (volatile) +// f14-f31 Floating point values (nonvolatile) +// +// LR Link register for return address (volatile) +// CTR Loop counter (volatile) +// XER Fixed point exception register (volatile) +// FPSCR Floating point status and control register (volatile) +// +// CR0-CR1 Condition code fields (volatile) +// CR2-CR4 Condition code fields (nonvolatile) +// CR5-CR7 Condition code fields (volatile) +// +// ---------------------------------------------- +// On processors with the VMX feature: +// v0-v1 Volatile scratch registers +// v2-v13 Volatile vector parameters registers +// v14-v19 Volatile scratch registers +// v20-v31 Non-volatile registers +// vrsave Non-volatile 32-bit register + + +// Use Register as shortcut +class RegisterImpl; +typedef RegisterImpl* Register; + +inline Register as_Register(int encoding) { + assert(encoding >= 0 && encoding < 32, "bad register encoding"); + return (Register)(intptr_t)encoding; +} + +// The implementation of integer registers for the Power architecture +class RegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 32 + }; + + // general construction + inline friend Register as_Register(int encoding); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + VMReg as_VMReg(); + Register successor() const { return as_Register(encoding() + 1); } + + // testers + bool is_valid() const { return ( 0 <= (value()&0x7F) && (value()&0x7F) < number_of_registers); } + bool is_volatile() const { return ( 0 <= (value()&0x7F) && (value()&0x7F) <= 13 ); } + bool is_nonvolatile() const { return (14 <= (value()&0x7F) && (value()&0x7F) <= 31 ); } + + const char* name() const; +}; + +// The integer registers of the PPC architecture +CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); + +CONSTANT_REGISTER_DECLARATION(Register, R0, (0)); +CONSTANT_REGISTER_DECLARATION(Register, R1, (1)); +CONSTANT_REGISTER_DECLARATION(Register, R2, (2)); +CONSTANT_REGISTER_DECLARATION(Register, R3, (3)); +CONSTANT_REGISTER_DECLARATION(Register, R4, (4)); +CONSTANT_REGISTER_DECLARATION(Register, R5, (5)); +CONSTANT_REGISTER_DECLARATION(Register, R6, (6)); +CONSTANT_REGISTER_DECLARATION(Register, R7, (7)); +CONSTANT_REGISTER_DECLARATION(Register, R8, (8)); +CONSTANT_REGISTER_DECLARATION(Register, R9, (9)); +CONSTANT_REGISTER_DECLARATION(Register, R10, (10)); +CONSTANT_REGISTER_DECLARATION(Register, R11, (11)); +CONSTANT_REGISTER_DECLARATION(Register, R12, (12)); +CONSTANT_REGISTER_DECLARATION(Register, R13, (13)); +CONSTANT_REGISTER_DECLARATION(Register, R14, (14)); +CONSTANT_REGISTER_DECLARATION(Register, R15, (15)); +CONSTANT_REGISTER_DECLARATION(Register, R16, (16)); +CONSTANT_REGISTER_DECLARATION(Register, R17, (17)); +CONSTANT_REGISTER_DECLARATION(Register, R18, (18)); +CONSTANT_REGISTER_DECLARATION(Register, R19, (19)); +CONSTANT_REGISTER_DECLARATION(Register, R20, (20)); +CONSTANT_REGISTER_DECLARATION(Register, R21, (21)); +CONSTANT_REGISTER_DECLARATION(Register, R22, (22)); +CONSTANT_REGISTER_DECLARATION(Register, R23, (23)); +CONSTANT_REGISTER_DECLARATION(Register, R24, (24)); +CONSTANT_REGISTER_DECLARATION(Register, R25, (25)); +CONSTANT_REGISTER_DECLARATION(Register, R26, (26)); +CONSTANT_REGISTER_DECLARATION(Register, R27, (27)); +CONSTANT_REGISTER_DECLARATION(Register, R28, (28)); +CONSTANT_REGISTER_DECLARATION(Register, R29, (29)); +CONSTANT_REGISTER_DECLARATION(Register, R30, (30)); +CONSTANT_REGISTER_DECLARATION(Register, R31, (31)); + + +// +// Because Power has many registers, #define'ing values for them is +// beneficial in code size and is worth the cost of some of the +// dangers of defines. If a particular file has a problem with these +// defines then it's possible to turn them off in that file by +// defining DONT_USE_REGISTER_DEFINES. Register_definition_ppc.cpp +// does that so that it's able to provide real definitions of these +// registers for use in debuggers and such. +// + +#ifndef DONT_USE_REGISTER_DEFINES +#define noreg ((Register)(noreg_RegisterEnumValue)) + +#define R0 ((Register)(R0_RegisterEnumValue)) +#define R1 ((Register)(R1_RegisterEnumValue)) +#define R2 ((Register)(R2_RegisterEnumValue)) +#define R3 ((Register)(R3_RegisterEnumValue)) +#define R4 ((Register)(R4_RegisterEnumValue)) +#define R5 ((Register)(R5_RegisterEnumValue)) +#define R6 ((Register)(R6_RegisterEnumValue)) +#define R7 ((Register)(R7_RegisterEnumValue)) +#define R8 ((Register)(R8_RegisterEnumValue)) +#define R9 ((Register)(R9_RegisterEnumValue)) +#define R10 ((Register)(R10_RegisterEnumValue)) +#define R11 ((Register)(R11_RegisterEnumValue)) +#define R12 ((Register)(R12_RegisterEnumValue)) +#define R13 ((Register)(R13_RegisterEnumValue)) +#define R14 ((Register)(R14_RegisterEnumValue)) +#define R15 ((Register)(R15_RegisterEnumValue)) +#define R16 ((Register)(R16_RegisterEnumValue)) +#define R17 ((Register)(R17_RegisterEnumValue)) +#define R18 ((Register)(R18_RegisterEnumValue)) +#define R19 ((Register)(R19_RegisterEnumValue)) +#define R20 ((Register)(R20_RegisterEnumValue)) +#define R21 ((Register)(R21_RegisterEnumValue)) +#define R22 ((Register)(R22_RegisterEnumValue)) +#define R23 ((Register)(R23_RegisterEnumValue)) +#define R24 ((Register)(R24_RegisterEnumValue)) +#define R25 ((Register)(R25_RegisterEnumValue)) +#define R26 ((Register)(R26_RegisterEnumValue)) +#define R27 ((Register)(R27_RegisterEnumValue)) +#define R28 ((Register)(R28_RegisterEnumValue)) +#define R29 ((Register)(R29_RegisterEnumValue)) +#define R30 ((Register)(R30_RegisterEnumValue)) +#define R31 ((Register)(R31_RegisterEnumValue)) +#endif + +// Use ConditionRegister as shortcut +class ConditionRegisterImpl; +typedef ConditionRegisterImpl* ConditionRegister; + +inline ConditionRegister as_ConditionRegister(int encoding) { + assert(encoding >= 0 && encoding < 8, "bad condition register encoding"); + return (ConditionRegister)(intptr_t)encoding; +} + +// The implementation of condition register(s) for the PPC architecture +class ConditionRegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 8 + }; + + // construction. + inline friend ConditionRegister as_ConditionRegister(int encoding); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + VMReg as_VMReg(); + + // testers + bool is_valid() const { return (0 <= value() && value() < number_of_registers); } + bool is_nonvolatile() const { return (2 <= (value()&0x7F) && (value()&0x7F) <= 4 ); } + + const char* name() const; +}; + +// The (parts of the) condition register(s) of the PPC architecture +// sys/ioctl.h on AIX defines CR0-CR3, so I name these CCR. +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR0, (0)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR1, (1)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR2, (2)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR3, (3)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR4, (4)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR5, (5)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR6, (6)); +CONSTANT_REGISTER_DECLARATION(ConditionRegister, CCR7, (7)); + +#ifndef DONT_USE_REGISTER_DEFINES + +#define CCR0 ((ConditionRegister)(CCR0_ConditionRegisterEnumValue)) +#define CCR1 ((ConditionRegister)(CCR1_ConditionRegisterEnumValue)) +#define CCR2 ((ConditionRegister)(CCR2_ConditionRegisterEnumValue)) +#define CCR3 ((ConditionRegister)(CCR3_ConditionRegisterEnumValue)) +#define CCR4 ((ConditionRegister)(CCR4_ConditionRegisterEnumValue)) +#define CCR5 ((ConditionRegister)(CCR5_ConditionRegisterEnumValue)) +#define CCR6 ((ConditionRegister)(CCR6_ConditionRegisterEnumValue)) +#define CCR7 ((ConditionRegister)(CCR7_ConditionRegisterEnumValue)) + +#endif // DONT_USE_REGISTER_DEFINES + + +// Use FloatRegister as shortcut +class FloatRegisterImpl; +typedef FloatRegisterImpl* FloatRegister; + +inline FloatRegister as_FloatRegister(int encoding) { + assert(encoding >= 0 && encoding < 32, "bad float register encoding"); + return (FloatRegister)(intptr_t)encoding; +} + +// The implementation of float registers for the PPC architecture +class FloatRegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 32 + }; + + // construction + inline friend FloatRegister as_FloatRegister(int encoding); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + VMReg as_VMReg(); + FloatRegister successor() const { return as_FloatRegister(encoding() + 1); } + + // testers + bool is_valid() const { return (0 <= value() && value() < number_of_registers); } + + const char* name() const; +}; + +// The float registers of the PPC architecture +CONSTANT_REGISTER_DECLARATION(FloatRegister, fnoreg, (-1)); + +CONSTANT_REGISTER_DECLARATION(FloatRegister, F0, ( 0)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F1, ( 1)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F2, ( 2)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F3, ( 3)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F4, ( 4)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F5, ( 5)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F6, ( 6)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F7, ( 7)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F8, ( 8)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F9, ( 9)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F10, (10)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F11, (11)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F12, (12)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F13, (13)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F14, (14)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F15, (15)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F16, (16)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F17, (17)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F18, (18)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F19, (19)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F20, (20)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F21, (21)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F22, (22)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F23, (23)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F24, (24)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F25, (25)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F26, (26)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F27, (27)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F28, (28)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F29, (29)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F30, (30)); +CONSTANT_REGISTER_DECLARATION(FloatRegister, F31, (31)); + +#ifndef DONT_USE_REGISTER_DEFINES +#define fnoreg ((FloatRegister)(fnoreg_FloatRegisterEnumValue)) +#define F0 ((FloatRegister)( F0_FloatRegisterEnumValue)) +#define F1 ((FloatRegister)( F1_FloatRegisterEnumValue)) +#define F2 ((FloatRegister)( F2_FloatRegisterEnumValue)) +#define F3 ((FloatRegister)( F3_FloatRegisterEnumValue)) +#define F4 ((FloatRegister)( F4_FloatRegisterEnumValue)) +#define F5 ((FloatRegister)( F5_FloatRegisterEnumValue)) +#define F6 ((FloatRegister)( F6_FloatRegisterEnumValue)) +#define F7 ((FloatRegister)( F7_FloatRegisterEnumValue)) +#define F8 ((FloatRegister)( F8_FloatRegisterEnumValue)) +#define F9 ((FloatRegister)( F9_FloatRegisterEnumValue)) +#define F10 ((FloatRegister)( F10_FloatRegisterEnumValue)) +#define F11 ((FloatRegister)( F11_FloatRegisterEnumValue)) +#define F12 ((FloatRegister)( F12_FloatRegisterEnumValue)) +#define F13 ((FloatRegister)( F13_FloatRegisterEnumValue)) +#define F14 ((FloatRegister)( F14_FloatRegisterEnumValue)) +#define F15 ((FloatRegister)( F15_FloatRegisterEnumValue)) +#define F16 ((FloatRegister)( F16_FloatRegisterEnumValue)) +#define F17 ((FloatRegister)( F17_FloatRegisterEnumValue)) +#define F18 ((FloatRegister)( F18_FloatRegisterEnumValue)) +#define F19 ((FloatRegister)( F19_FloatRegisterEnumValue)) +#define F20 ((FloatRegister)( F20_FloatRegisterEnumValue)) +#define F21 ((FloatRegister)( F21_FloatRegisterEnumValue)) +#define F22 ((FloatRegister)( F22_FloatRegisterEnumValue)) +#define F23 ((FloatRegister)( F23_FloatRegisterEnumValue)) +#define F24 ((FloatRegister)( F24_FloatRegisterEnumValue)) +#define F25 ((FloatRegister)( F25_FloatRegisterEnumValue)) +#define F26 ((FloatRegister)( F26_FloatRegisterEnumValue)) +#define F27 ((FloatRegister)( F27_FloatRegisterEnumValue)) +#define F28 ((FloatRegister)( F28_FloatRegisterEnumValue)) +#define F29 ((FloatRegister)( F29_FloatRegisterEnumValue)) +#define F30 ((FloatRegister)( F30_FloatRegisterEnumValue)) +#define F31 ((FloatRegister)( F31_FloatRegisterEnumValue)) +#endif // DONT_USE_REGISTER_DEFINES + +// Use SpecialRegister as shortcut +class SpecialRegisterImpl; +typedef SpecialRegisterImpl* SpecialRegister; + +inline SpecialRegister as_SpecialRegister(int encoding) { + return (SpecialRegister)(intptr_t)encoding; +} + +// The implementation of special registers for the Power architecture (LR, CTR and friends) +class SpecialRegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 6 + }; + + // construction + inline friend SpecialRegister as_SpecialRegister(int encoding); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + VMReg as_VMReg(); + + // testers + bool is_valid() const { return 0 <= value() && value() < number_of_registers; } + + const char* name() const; +}; + +// The special registers of the PPC architecture +CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_XER, (0)); +CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_LR, (1)); +CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_CTR, (2)); +CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_VRSAVE, (3)); +CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_SPEFSCR, (4)); +CONSTANT_REGISTER_DECLARATION(SpecialRegister, SR_PPR, (5)); + +#ifndef DONT_USE_REGISTER_DEFINES +#define SR_XER ((SpecialRegister)(SR_XER_SpecialRegisterEnumValue)) +#define SR_LR ((SpecialRegister)(SR_LR_SpecialRegisterEnumValue)) +#define SR_CTR ((SpecialRegister)(SR_CTR_SpecialRegisterEnumValue)) +#define SR_VRSAVE ((SpecialRegister)(SR_VRSAVE_SpecialRegisterEnumValue)) +#define SR_SPEFSCR ((SpecialRegister)(SR_SPEFSCR_SpecialRegisterEnumValue)) +#define SR_PPR ((SpecialRegister)(SR_PPR_SpecialRegisterEnumValue)) +#endif // DONT_USE_REGISTER_DEFINES + + +// Use VectorRegister as shortcut +class VectorRegisterImpl; +typedef VectorRegisterImpl* VectorRegister; + +inline VectorRegister as_VectorRegister(int encoding) { + return (VectorRegister)(intptr_t)encoding; +} + +// The implementation of vector registers for the Power architecture +class VectorRegisterImpl: public AbstractRegisterImpl { + public: + enum { + number_of_registers = 32 + }; + + // construction + inline friend VectorRegister as_VectorRegister(int encoding); + + // accessors + int encoding() const { assert(is_valid(), "invalid register"); return value(); } + + // testers + bool is_valid() const { return 0 <= value() && value() < number_of_registers; } + + const char* name() const; +}; + +// The Vector registers of the Power architecture + +CONSTANT_REGISTER_DECLARATION(VectorRegister, vnoreg, (-1)); + +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR0, ( 0)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR1, ( 1)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR2, ( 2)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR3, ( 3)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR4, ( 4)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR5, ( 5)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR6, ( 6)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR7, ( 7)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR8, ( 8)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR9, ( 9)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR10, (10)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR11, (11)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR12, (12)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR13, (13)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR14, (14)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR15, (15)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR16, (16)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR17, (17)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR18, (18)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR19, (19)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR20, (20)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR21, (21)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR22, (22)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR23, (23)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR24, (24)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR25, (25)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR26, (26)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR27, (27)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR28, (28)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR29, (29)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR30, (30)); +CONSTANT_REGISTER_DECLARATION(VectorRegister, VR31, (31)); + +#ifndef DONT_USE_REGISTER_DEFINES +#define vnoreg ((VectorRegister)(vnoreg_VectorRegisterEnumValue)) +#define VR0 ((VectorRegister)( VR0_VectorRegisterEnumValue)) +#define VR1 ((VectorRegister)( VR1_VectorRegisterEnumValue)) +#define VR2 ((VectorRegister)( VR2_VectorRegisterEnumValue)) +#define VR3 ((VectorRegister)( VR3_VectorRegisterEnumValue)) +#define VR4 ((VectorRegister)( VR4_VectorRegisterEnumValue)) +#define VR5 ((VectorRegister)( VR5_VectorRegisterEnumValue)) +#define VR6 ((VectorRegister)( VR6_VectorRegisterEnumValue)) +#define VR7 ((VectorRegister)( VR7_VectorRegisterEnumValue)) +#define VR8 ((VectorRegister)( VR8_VectorRegisterEnumValue)) +#define VR9 ((VectorRegister)( VR9_VectorRegisterEnumValue)) +#define VR10 ((VectorRegister)( VR10_VectorRegisterEnumValue)) +#define VR11 ((VectorRegister)( VR11_VectorRegisterEnumValue)) +#define VR12 ((VectorRegister)( VR12_VectorRegisterEnumValue)) +#define VR13 ((VectorRegister)( VR13_VectorRegisterEnumValue)) +#define VR14 ((VectorRegister)( VR14_VectorRegisterEnumValue)) +#define VR15 ((VectorRegister)( VR15_VectorRegisterEnumValue)) +#define VR16 ((VectorRegister)( VR16_VectorRegisterEnumValue)) +#define VR17 ((VectorRegister)( VR17_VectorRegisterEnumValue)) +#define VR18 ((VectorRegister)( VR18_VectorRegisterEnumValue)) +#define VR19 ((VectorRegister)( VR19_VectorRegisterEnumValue)) +#define VR20 ((VectorRegister)( VR20_VectorRegisterEnumValue)) +#define VR21 ((VectorRegister)( VR21_VectorRegisterEnumValue)) +#define VR22 ((VectorRegister)( VR22_VectorRegisterEnumValue)) +#define VR23 ((VectorRegister)( VR23_VectorRegisterEnumValue)) +#define VR24 ((VectorRegister)( VR24_VectorRegisterEnumValue)) +#define VR25 ((VectorRegister)( VR25_VectorRegisterEnumValue)) +#define VR26 ((VectorRegister)( VR26_VectorRegisterEnumValue)) +#define VR27 ((VectorRegister)( VR27_VectorRegisterEnumValue)) +#define VR28 ((VectorRegister)( VR28_VectorRegisterEnumValue)) +#define VR29 ((VectorRegister)( VR29_VectorRegisterEnumValue)) +#define VR30 ((VectorRegister)( VR30_VectorRegisterEnumValue)) +#define VR31 ((VectorRegister)( VR31_VectorRegisterEnumValue)) +#endif // DONT_USE_REGISTER_DEFINES + + +// Maximum number of incoming arguments that can be passed in i registers. +const int PPC_ARGS_IN_REGS_NUM = 8; + + +// Need to know the total number of registers of all sorts for SharedInfo. +// Define a class that exports it. +class ConcreteRegisterImpl : public AbstractRegisterImpl { + public: + enum { + // This number must be large enough to cover REG_COUNT (defined by c2) registers. + // There is no requirement that any ordering here matches any ordering c2 gives + // it's optoregs. + number_of_registers = + ( RegisterImpl::number_of_registers + + FloatRegisterImpl::number_of_registers ) + * 2 // register halves + + ConditionRegisterImpl::number_of_registers // condition code registers + + SpecialRegisterImpl::number_of_registers // special registers + + VectorRegisterImpl::number_of_registers // vector registers + }; + + static const int max_gpr; + static const int max_fpr; + static const int max_cnd; +}; + +// Common register declarations used in assembler code. +REGISTER_DECLARATION(Register, R0_SCRATCH, R0); // volatile +REGISTER_DECLARATION(Register, R1_SP, R1); // non-volatile +REGISTER_DECLARATION(Register, R2_TOC, R2); // volatile +REGISTER_DECLARATION(Register, R3_RET, R3); // volatile +REGISTER_DECLARATION(Register, R3_ARG1, R3); // volatile +REGISTER_DECLARATION(Register, R4_ARG2, R4); // volatile +REGISTER_DECLARATION(Register, R5_ARG3, R5); // volatile +REGISTER_DECLARATION(Register, R6_ARG4, R6); // volatile +REGISTER_DECLARATION(Register, R7_ARG5, R7); // volatile +REGISTER_DECLARATION(Register, R8_ARG6, R8); // volatile +REGISTER_DECLARATION(Register, R9_ARG7, R9); // volatile +REGISTER_DECLARATION(Register, R10_ARG8, R10); // volatile +REGISTER_DECLARATION(FloatRegister, F0_SCRATCH, F0); // volatile +REGISTER_DECLARATION(FloatRegister, F1_RET, F1); // volatile +REGISTER_DECLARATION(FloatRegister, F1_ARG1, F1); // volatile +REGISTER_DECLARATION(FloatRegister, F2_ARG2, F2); // volatile +REGISTER_DECLARATION(FloatRegister, F3_ARG3, F3); // volatile +REGISTER_DECLARATION(FloatRegister, F4_ARG4, F4); // volatile +REGISTER_DECLARATION(FloatRegister, F5_ARG5, F5); // volatile +REGISTER_DECLARATION(FloatRegister, F6_ARG6, F6); // volatile +REGISTER_DECLARATION(FloatRegister, F7_ARG7, F7); // volatile +REGISTER_DECLARATION(FloatRegister, F8_ARG8, F8); // volatile +REGISTER_DECLARATION(FloatRegister, F9_ARG9, F9); // volatile +REGISTER_DECLARATION(FloatRegister, F10_ARG10, F10); // volatile +REGISTER_DECLARATION(FloatRegister, F11_ARG11, F11); // volatile +REGISTER_DECLARATION(FloatRegister, F12_ARG12, F12); // volatile +REGISTER_DECLARATION(FloatRegister, F13_ARG13, F13); // volatile + +#ifndef DONT_USE_REGISTER_DEFINES +#define R0_SCRATCH AS_REGISTER(Register, R0) +#define R1_SP AS_REGISTER(Register, R1) +#define R2_TOC AS_REGISTER(Register, R2) +#define R3_RET AS_REGISTER(Register, R3) +#define R3_ARG1 AS_REGISTER(Register, R3) +#define R4_ARG2 AS_REGISTER(Register, R4) +#define R5_ARG3 AS_REGISTER(Register, R5) +#define R6_ARG4 AS_REGISTER(Register, R6) +#define R7_ARG5 AS_REGISTER(Register, R7) +#define R8_ARG6 AS_REGISTER(Register, R8) +#define R9_ARG7 AS_REGISTER(Register, R9) +#define R10_ARG8 AS_REGISTER(Register, R10) +#define F0_SCRATCH AS_REGISTER(FloatRegister, F0) +#define F1_RET AS_REGISTER(FloatRegister, F1) +#define F1_ARG1 AS_REGISTER(FloatRegister, F1) +#define F2_ARG2 AS_REGISTER(FloatRegister, F2) +#define F3_ARG3 AS_REGISTER(FloatRegister, F3) +#define F4_ARG4 AS_REGISTER(FloatRegister, F4) +#define F5_ARG5 AS_REGISTER(FloatRegister, F5) +#define F6_ARG6 AS_REGISTER(FloatRegister, F6) +#define F7_ARG7 AS_REGISTER(FloatRegister, F7) +#define F8_ARG8 AS_REGISTER(FloatRegister, F8) +#define F9_ARG9 AS_REGISTER(FloatRegister, F9) +#define F10_ARG10 AS_REGISTER(FloatRegister, F10) +#define F11_ARG11 AS_REGISTER(FloatRegister, F11) +#define F12_ARG12 AS_REGISTER(FloatRegister, F12) +#define F13_ARG13 AS_REGISTER(FloatRegister, F13) +#endif + +// Register declarations to be used in frame manager assembly code. +// Use only non-volatile registers in order to keep values across C-calls. +#ifdef CC_INTERP +REGISTER_DECLARATION(Register, R14_state, R14); // address of new cInterpreter. +REGISTER_DECLARATION(Register, R15_prev_state, R15); // address of old cInterpreter +#else // CC_INTERP +REGISTER_DECLARATION(Register, R14_bcp, R14); +REGISTER_DECLARATION(Register, R15_esp, R15); +REGISTER_DECLARATION(FloatRegister, F15_ftos, F15); +#endif // CC_INTERP +REGISTER_DECLARATION(Register, R16_thread, R16); // address of current thread +REGISTER_DECLARATION(Register, R17_tos, R17); // address of Java tos (prepushed). +REGISTER_DECLARATION(Register, R18_locals, R18); // address of first param slot (receiver). +REGISTER_DECLARATION(Register, R19_method, R19); // address of current method +#ifndef DONT_USE_REGISTER_DEFINES +#ifdef CC_INTERP +#define R14_state AS_REGISTER(Register, R14) +#define R15_prev_state AS_REGISTER(Register, R15) +#else // CC_INTERP +#define R14_bcp AS_REGISTER(Register, R14) +#define R15_esp AS_REGISTER(Register, R15) +#define F15_ftos AS_REGISTER(FloatRegister, F15) +#endif // CC_INTERP +#define R16_thread AS_REGISTER(Register, R16) +#define R17_tos AS_REGISTER(Register, R17) +#define R18_locals AS_REGISTER(Register, R18) +#define R19_method AS_REGISTER(Register, R19) +#define R21_sender_SP AS_REGISTER(Register, R21) +#endif + +// Temporary registers to be used within frame manager. We can use +// the non-volatiles because the call stub has saved them. +// Use only non-volatile registers in order to keep values across C-calls. +REGISTER_DECLARATION(Register, R21_tmp1, R21); +REGISTER_DECLARATION(Register, R22_tmp2, R22); +REGISTER_DECLARATION(Register, R23_tmp3, R23); +REGISTER_DECLARATION(Register, R24_tmp4, R24); +REGISTER_DECLARATION(Register, R25_tmp5, R25); +REGISTER_DECLARATION(Register, R26_tmp6, R26); +REGISTER_DECLARATION(Register, R27_tmp7, R27); +REGISTER_DECLARATION(Register, R28_tmp8, R28); +REGISTER_DECLARATION(Register, R29_tmp9, R29); +#ifndef CC_INTERP +REGISTER_DECLARATION(Register, R24_dispatch_addr, R24); +REGISTER_DECLARATION(Register, R25_templateTableBase, R25); +REGISTER_DECLARATION(Register, R26_monitor, R26); +REGISTER_DECLARATION(Register, R27_constPoolCache, R27); +REGISTER_DECLARATION(Register, R28_mdx, R28); +#endif // CC_INTERP + +#ifndef DONT_USE_REGISTER_DEFINES +#define R21_tmp1 AS_REGISTER(Register, R21) +#define R22_tmp2 AS_REGISTER(Register, R22) +#define R23_tmp3 AS_REGISTER(Register, R23) +#define R24_tmp4 AS_REGISTER(Register, R24) +#define R25_tmp5 AS_REGISTER(Register, R25) +#define R26_tmp6 AS_REGISTER(Register, R26) +#define R27_tmp7 AS_REGISTER(Register, R27) +#define R28_tmp8 AS_REGISTER(Register, R28) +#define R29_tmp9 AS_REGISTER(Register, R29) +#ifndef CC_INTERP +// Lmonitors : monitor pointer +// LcpoolCache: constant pool cache +// mdx: method data index +#define R24_dispatch_addr AS_REGISTER(Register, R24) +#define R25_templateTableBase AS_REGISTER(Register, R25) +#define R26_monitor AS_REGISTER(Register, R26) +#define R27_constPoolCache AS_REGISTER(Register, R27) +#define R28_mdx AS_REGISTER(Register, R28) +#endif + +#define CCR4_is_synced AS_REGISTER(ConditionRegister, CCR4) +#endif + +// Scratch registers are volatile. +REGISTER_DECLARATION(Register, R11_scratch1, R11); +REGISTER_DECLARATION(Register, R12_scratch2, R12); +#ifndef DONT_USE_REGISTER_DEFINES +#define R11_scratch1 AS_REGISTER(Register, R11) +#define R12_scratch2 AS_REGISTER(Register, R12) +#endif + +#endif // CPU_PPC_VM_REGISTER_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/relocInfo_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/relocInfo_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.inline.hpp" +#include "assembler_ppc.inline.hpp" +#include "code/relocInfo.hpp" +#include "nativeInst_ppc.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/safepoint.hpp" + +void Relocation::pd_set_data_value(address x, intptr_t o, bool verify_only) { + bool copy_back_to_oop_pool = true; // TODO: PPC port + // The following comment is from the declaration of DataRelocation: + // + // "The "o" (displacement) argument is relevant only to split relocations + // on RISC machines. In some CPUs (SPARC), the set-hi and set-lo ins'ns + // can encode more than 32 bits between them. This allows compilers to + // share set-hi instructions between addresses that differ by a small + // offset (e.g., different static variables in the same class). + // On such machines, the "x" argument to set_value on all set-lo + // instructions must be the same as the "x" argument for the + // corresponding set-hi instructions. The "o" arguments for the + // set-hi instructions are ignored, and must not affect the high-half + // immediate constant. The "o" arguments for the set-lo instructions are + // added into the low-half immediate constant, and must not overflow it." + // + // Currently we don't support splitting of relocations, so o must be + // zero: + assert(o == 0, "tried to split relocations"); + + if (!verify_only) { + if (format() != 1) { + nativeMovConstReg_at(addr())->set_data_plain(((intptr_t)x), code()); + } else { + nativeMovConstReg_at(addr())->set_narrow_oop(((intptr_t)x), code()); + } + } else { + assert((address) (nativeMovConstReg_at(addr())->data()) == x, "data must match"); + } +} + +address Relocation::pd_call_destination(address orig_addr) { + intptr_t adj = 0; + address inst_loc = addr(); + + if (orig_addr != NULL) { + // We just moved this call instruction from orig_addr to addr(). + // This means its target will appear to have grown by addr() - orig_addr. + adj = -(inst_loc - orig_addr); + } + if (NativeFarCall::is_far_call_at(inst_loc)) { + NativeFarCall* call = nativeFarCall_at(inst_loc); + return call->destination() + (intptr_t)(call->is_pcrelative() ? adj : 0); + } else if (NativeJump::is_jump_at(inst_loc)) { + NativeJump* jump = nativeJump_at(inst_loc); + return jump->jump_destination() + (intptr_t)(jump->is_pcrelative() ? adj : 0); + } else if (NativeConditionalFarBranch::is_conditional_far_branch_at(inst_loc)) { + NativeConditionalFarBranch* branch = NativeConditionalFarBranch_at(inst_loc); + return branch->branch_destination(); + } else { + // There are two instructions at the beginning of a stub, therefore we + // load at orig_addr + 8. + orig_addr = nativeCall_at(inst_loc)->get_trampoline(); + if (orig_addr == NULL) { + return (address) -1; + } else { + return (address) nativeMovConstReg_at(orig_addr + 8)->data(); + } + } +} + +void Relocation::pd_set_call_destination(address x) { + address inst_loc = addr(); + + if (NativeFarCall::is_far_call_at(inst_loc)) { + NativeFarCall* call = nativeFarCall_at(inst_loc); + call->set_destination(x); + } else if (NativeJump::is_jump_at(inst_loc)) { + NativeJump* jump= nativeJump_at(inst_loc); + jump->set_jump_destination(x); + } else if (NativeConditionalFarBranch::is_conditional_far_branch_at(inst_loc)) { + NativeConditionalFarBranch* branch = NativeConditionalFarBranch_at(inst_loc); + branch->set_branch_destination(x); + } else { + NativeCall* call = nativeCall_at(inst_loc); + call->set_destination_mt_safe(x, false); + } +} + +address* Relocation::pd_address_in_code() { + ShouldNotReachHere(); + return 0; +} + +address Relocation::pd_get_address_from_code() { + return (address)(nativeMovConstReg_at(addr())->data()); +} + +int Relocation::pd_breakpoint_size() { + ShouldNotReachHere(); // TODO: PPC port + return 0; +} + +void Relocation::pd_swap_in_breakpoint(address x, short* instrs, int instrlen) { + ShouldNotReachHere(); // TODO: PPC port +} + +void Relocation::pd_swap_out_breakpoint(address x, short* instrs, int instrlen) { + ShouldNotReachHere(); // TODO: PPC port +} + +void poll_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { +} + +void poll_return_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/relocInfo_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/relocInfo_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_RELOCINFO_PPC_HPP +#define CPU_PPC_VM_RELOCINFO_PPC_HPP + + // machine-dependent parts of class relocInfo + private: + enum { + // Since Power instructions are whole words, + // the two low-order offset bits can always be discarded. + offset_unit = 4, + + // There is no need for format bits; the instructions are + // sufficiently self-identifying. +#ifndef _LP64 + format_width = 0 +#else + // Except narrow oops in 64-bits VM. + format_width = 1 +#endif + }; + +#endif // CPU_PPC_VM_RELOCINFO_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/runtime_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/runtime_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,191 @@ +/* + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#ifdef COMPILER2 +#include "asm/assembler.hpp" +#include "assembler_ppc.inline.hpp" +#include "classfile/systemDictionary.hpp" +#include "code/vmreg.hpp" +#include "interpreter/interpreter.hpp" +#include "nativeInst_ppc.hpp" +#include "opto/runtime.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/vframeArray.hpp" +#include "utilities/globalDefinitions.hpp" +#include "vmreg_ppc.inline.hpp" +#endif + +#define __ masm-> + + +#ifdef COMPILER2 + +// SP adjustment (must use unextended SP) for method handle call sites +// during exception handling. +static intptr_t adjust_SP_for_methodhandle_callsite(JavaThread *thread) { + RegisterMap map(thread, false); + // The frame constructor will do the correction for us (see frame::adjust_unextended_SP). + frame mh_caller_frame = thread->last_frame().sender(&map); + assert(mh_caller_frame.is_compiled_frame(), "Only may reach here for compiled MH call sites"); + return (intptr_t) mh_caller_frame.unextended_sp(); +} + +//------------------------------generate_exception_blob--------------------------- +// Creates exception blob at the end. +// Using exception blob, this code is jumped from a compiled method. +// +// Given an exception pc at a call we call into the runtime for the +// handler in this method. This handler might merely restore state +// (i.e. callee save registers) unwind the frame and jump to the +// exception handler for the nmethod if there is no Java level handler +// for the nmethod. +// +// This code is entered with a jmp. +// +// Arguments: +// R3_ARG1: exception oop +// R4_ARG2: exception pc +// +// Results: +// R3_ARG1: exception oop +// R4_ARG2: exception pc in caller +// destination: exception handler of caller +// +// Note: the exception pc MUST be at a call (precise debug information) +// +void OptoRuntime::generate_exception_blob() { + // Allocate space for the code. + ResourceMark rm; + // Setup code generation tools. + CodeBuffer buffer("exception_blob", 2048, 1024); + InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); + + address start = __ pc(); + + int frame_size_in_bytes = frame::abi_reg_args_size; + OopMap* map = new OopMap(frame_size_in_bytes / sizeof(jint), 0); + + // Exception pc is 'return address' for stack walker. + __ std(R4_ARG2/*exception pc*/, _abi(lr), R1_SP); + + // Store the exception in the Thread object. + __ std(R3_ARG1/*exception oop*/, in_bytes(JavaThread::exception_oop_offset()), R16_thread); + __ std(R4_ARG2/*exception pc*/, in_bytes(JavaThread::exception_pc_offset()), R16_thread); + + // Save callee-saved registers. + // Push a C frame for the exception blob. It is needed for the C call later on. + __ push_frame_reg_args(0, R11_scratch1); + + // This call does all the hard work. It checks if an exception handler + // exists in the method. + // If so, it returns the handler address. + // If not, it prepares for stack-unwinding, restoring the callee-save + // registers of the frame being removed. + __ set_last_Java_frame(/*sp=*/R1_SP, noreg); + + __ mr(R3_ARG1, R16_thread); +#if defined(ABI_ELFv2) + __ call_c((address) OptoRuntime::handle_exception_C, relocInfo::none); +#else + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, OptoRuntime::handle_exception_C), + relocInfo::none); +#endif + address calls_return_pc = __ last_calls_return_pc(); +# ifdef ASSERT + __ cmpdi(CCR0, R3_RET, 0); + __ asm_assert_ne("handle_exception_C must not return NULL", 0x601); +# endif + + // Set an oopmap for the call site. This oopmap will only be used if we + // are unwinding the stack. Hence, all locations will be dead. + // Callee-saved registers will be the same as the frame above (i.e., + // handle_exception_stub), since they were restored when we got the + // exception. + OopMapSet* oop_maps = new OopMapSet(); + oop_maps->add_gc_map(calls_return_pc - start, map); + + // Get unextended_sp for method handle call sites. + Label mh_callsite, mh_done; // Use a 2nd c call if it's a method handle call site. + __ lwa(R4_ARG2, in_bytes(JavaThread::is_method_handle_return_offset()), R16_thread); + __ cmpwi(CCR0, R4_ARG2, 0); + __ bne(CCR0, mh_callsite); + + __ mtctr(R3_RET); // Move address of exception handler to SR_CTR. + __ reset_last_Java_frame(); + __ pop_frame(); + + __ bind(mh_done); + // We have a handler in register SR_CTR (could be deopt blob). + + // Get the exception oop. + __ ld(R3_ARG1, in_bytes(JavaThread::exception_oop_offset()), R16_thread); + + // Get the exception pc in case we are deoptimized. + __ ld(R4_ARG2, in_bytes(JavaThread::exception_pc_offset()), R16_thread); + + // Reset thread values. + __ li(R0, 0); +#ifdef ASSERT + __ std(R0, in_bytes(JavaThread::exception_handler_pc_offset()), R16_thread); + __ std(R0, in_bytes(JavaThread::exception_pc_offset()), R16_thread); +#endif + // Clear the exception oop so GC no longer processes it as a root. + __ std(R0, in_bytes(JavaThread::exception_oop_offset()), R16_thread); + + // Move exception pc into SR_LR. + __ mtlr(R4_ARG2); + __ bctr(); + + + // Same as above, but also set sp to unextended_sp. + __ bind(mh_callsite); + __ mr(R31, R3_RET); // Save branch address. + __ mr(R3_ARG1, R16_thread); +#if defined(ABI_ELFv2) + __ call_c((address) adjust_SP_for_methodhandle_callsite, relocInfo::none); +#else + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, adjust_SP_for_methodhandle_callsite), relocInfo::none); +#endif + // Returns unextended_sp in R3_RET. + + __ mtctr(R31); // Move address of exception handler to SR_CTR. + __ reset_last_Java_frame(); + + __ mr(R1_SP, R3_RET); // Set sp to unextended_sp. + __ b(mh_done); + + + // Make sure all code is generated. + masm->flush(); + + // Set exception blob. + _exception_blob = ExceptionBlob::create(&buffer, oop_maps, + frame_size_in_bytes/wordSize); +} + +#endif // COMPILER2 diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/sharedRuntime_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/sharedRuntime_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,3278 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "assembler_ppc.inline.hpp" +#include "code/debugInfoRec.hpp" +#include "code/icBuffer.hpp" +#include "code/vtableStubs.hpp" +#include "interpreter/interpreter.hpp" +#include "oops/compiledICHolderOop.hpp" +#include "prims/jvmtiRedefineClassesTrace.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/vframeArray.hpp" +#include "vmreg_ppc.inline.hpp" +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#endif +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif + +#define __ masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + + +// Used by generate_deopt_blob. Defined in .ad file. +extern uint size_deopt_handler(); + + +class RegisterSaver { + // Used for saving volatile registers. + public: + + // Support different return pc locations. + enum ReturnPCLocation { + return_pc_is_lr, + return_pc_is_r4, + return_pc_is_thread_saved_exception_pc + }; + + static OopMap* push_frame_reg_args_and_save_live_registers(MacroAssembler* masm, + int* out_frame_size_in_bytes, + bool generate_oop_map, + int return_pc_adjustment, + ReturnPCLocation return_pc_location); + static void restore_live_registers_and_pop_frame(MacroAssembler* masm, + int frame_size_in_bytes, + bool restore_ctr); + + static void push_frame_and_save_argument_registers(MacroAssembler* masm, + Register r_temp, + int frame_size, + int total_args, + const VMRegPair *regs, const VMRegPair *regs2 = NULL); + static void restore_argument_registers_and_pop_frame(MacroAssembler*masm, + int frame_size, + int total_args, + const VMRegPair *regs, const VMRegPair *regs2 = NULL); + + // During deoptimization only the result registers need to be restored + // all the other values have already been extracted. + static void restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes); + + // Constants and data structures: + + typedef enum { + int_reg = 0, + float_reg = 1, + special_reg = 2 + } RegisterType; + + typedef enum { + reg_size = 8, + half_reg_size = reg_size / 2, + } RegisterConstants; + + typedef struct { + RegisterType reg_type; + int reg_num; + VMReg vmreg; + } LiveRegType; +}; + + +#define RegisterSaver_LiveSpecialReg(regname) \ + { RegisterSaver::special_reg, regname->encoding(), regname->as_VMReg() } + +#define RegisterSaver_LiveIntReg(regname) \ + { RegisterSaver::int_reg, regname->encoding(), regname->as_VMReg() } + +#define RegisterSaver_LiveFloatReg(regname) \ + { RegisterSaver::float_reg, regname->encoding(), regname->as_VMReg() } + +static const RegisterSaver::LiveRegType RegisterSaver_LiveRegs[] = { + // Live registers which get spilled to the stack. Register + // positions in this array correspond directly to the stack layout. + + // + // live special registers: + // + RegisterSaver_LiveSpecialReg(SR_CTR), + // + // live float registers: + // + RegisterSaver_LiveFloatReg( F0 ), + RegisterSaver_LiveFloatReg( F1 ), + RegisterSaver_LiveFloatReg( F2 ), + RegisterSaver_LiveFloatReg( F3 ), + RegisterSaver_LiveFloatReg( F4 ), + RegisterSaver_LiveFloatReg( F5 ), + RegisterSaver_LiveFloatReg( F6 ), + RegisterSaver_LiveFloatReg( F7 ), + RegisterSaver_LiveFloatReg( F8 ), + RegisterSaver_LiveFloatReg( F9 ), + RegisterSaver_LiveFloatReg( F10 ), + RegisterSaver_LiveFloatReg( F11 ), + RegisterSaver_LiveFloatReg( F12 ), + RegisterSaver_LiveFloatReg( F13 ), + RegisterSaver_LiveFloatReg( F14 ), + RegisterSaver_LiveFloatReg( F15 ), + RegisterSaver_LiveFloatReg( F16 ), + RegisterSaver_LiveFloatReg( F17 ), + RegisterSaver_LiveFloatReg( F18 ), + RegisterSaver_LiveFloatReg( F19 ), + RegisterSaver_LiveFloatReg( F20 ), + RegisterSaver_LiveFloatReg( F21 ), + RegisterSaver_LiveFloatReg( F22 ), + RegisterSaver_LiveFloatReg( F23 ), + RegisterSaver_LiveFloatReg( F24 ), + RegisterSaver_LiveFloatReg( F25 ), + RegisterSaver_LiveFloatReg( F26 ), + RegisterSaver_LiveFloatReg( F27 ), + RegisterSaver_LiveFloatReg( F28 ), + RegisterSaver_LiveFloatReg( F29 ), + RegisterSaver_LiveFloatReg( F30 ), + RegisterSaver_LiveFloatReg( F31 ), + // + // live integer registers: + // + RegisterSaver_LiveIntReg( R0 ), + //RegisterSaver_LiveIntReg( R1 ), // stack pointer + RegisterSaver_LiveIntReg( R2 ), + RegisterSaver_LiveIntReg( R3 ), + RegisterSaver_LiveIntReg( R4 ), + RegisterSaver_LiveIntReg( R5 ), + RegisterSaver_LiveIntReg( R6 ), + RegisterSaver_LiveIntReg( R7 ), + RegisterSaver_LiveIntReg( R8 ), + RegisterSaver_LiveIntReg( R9 ), + RegisterSaver_LiveIntReg( R10 ), + RegisterSaver_LiveIntReg( R11 ), + RegisterSaver_LiveIntReg( R12 ), + //RegisterSaver_LiveIntReg( R13 ), // system thread id + RegisterSaver_LiveIntReg( R14 ), + RegisterSaver_LiveIntReg( R15 ), + RegisterSaver_LiveIntReg( R16 ), + RegisterSaver_LiveIntReg( R17 ), + RegisterSaver_LiveIntReg( R18 ), + RegisterSaver_LiveIntReg( R19 ), + RegisterSaver_LiveIntReg( R20 ), + RegisterSaver_LiveIntReg( R21 ), + RegisterSaver_LiveIntReg( R22 ), + RegisterSaver_LiveIntReg( R23 ), + RegisterSaver_LiveIntReg( R24 ), + RegisterSaver_LiveIntReg( R25 ), + RegisterSaver_LiveIntReg( R26 ), + RegisterSaver_LiveIntReg( R27 ), + RegisterSaver_LiveIntReg( R28 ), + RegisterSaver_LiveIntReg( R29 ), + RegisterSaver_LiveIntReg( R31 ), + RegisterSaver_LiveIntReg( R30 ), // r30 must be the last register +}; + +OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssembler* masm, + int* out_frame_size_in_bytes, + bool generate_oop_map, + int return_pc_adjustment, + ReturnPCLocation return_pc_location) { + // Push an abi_reg_args-frame and store all registers which may be live. + // If requested, create an OopMap: Record volatile registers as + // callee-save values in an OopMap so their save locations will be + // propagated to the RegisterMap of the caller frame during + // StackFrameStream construction (needed for deoptimization; see + // compiledVFrame::create_stack_value). + // If return_pc_adjustment != 0 adjust the return pc by return_pc_adjustment. + + int i; + int offset; + + // calcualte frame size + const int regstosave_num = sizeof(RegisterSaver_LiveRegs) / + sizeof(RegisterSaver::LiveRegType); + const int register_save_size = regstosave_num * reg_size; + const int frame_size_in_bytes = round_to(register_save_size, frame::alignment_in_bytes) + + frame::abi_reg_args_size; + *out_frame_size_in_bytes = frame_size_in_bytes; + const int frame_size_in_slots = frame_size_in_bytes / sizeof(jint); + const int register_save_offset = frame_size_in_bytes - register_save_size; + + // OopMap frame size is in c2 stack slots (sizeof(jint)) not bytes or words. + OopMap* map = generate_oop_map ? new OopMap(frame_size_in_slots, 0) : NULL; + + BLOCK_COMMENT("push_frame_reg_args_and_save_live_registers {"); + + // Save r30 in the last slot of the not yet pushed frame so that we + // can use it as scratch reg. + __ std(R30, -reg_size, R1_SP); + assert(-reg_size == register_save_offset - frame_size_in_bytes + ((regstosave_num-1)*reg_size), + "consistency check"); + + // save the flags + // Do the save_LR_CR by hand and adjust the return pc if requested. + __ mfcr(R30); + __ std(R30, _abi(cr), R1_SP); + switch (return_pc_location) { + case return_pc_is_lr: __ mflr(R30); break; + case return_pc_is_r4: __ mr(R30, R4); break; + case return_pc_is_thread_saved_exception_pc: + __ ld(R30, thread_(saved_exception_pc)); break; + default: ShouldNotReachHere(); + } + if (return_pc_adjustment != 0) + __ addi(R30, R30, return_pc_adjustment); + __ std(R30, _abi(lr), R1_SP); + + // push a new frame + __ push_frame(frame_size_in_bytes, R30); + + // save all registers (ints and floats) + offset = register_save_offset; + for (int i = 0; i < regstosave_num; i++) { + int reg_num = RegisterSaver_LiveRegs[i].reg_num; + int reg_type = RegisterSaver_LiveRegs[i].reg_type; + + switch (reg_type) { + case RegisterSaver::int_reg: { + if (reg_num != 30) { // We spilled R30 right at the beginning. + __ std(as_Register(reg_num), offset, R1_SP); + } + break; + } + case RegisterSaver::float_reg: { + __ stfd(as_FloatRegister(reg_num), offset, R1_SP); + break; + } + case RegisterSaver::special_reg: { + if (reg_num == SR_CTR_SpecialRegisterEnumValue) { + __ mfctr(R30); + __ std(R30, offset, R1_SP); + } else { + Unimplemented(); + } + break; + } + default: + ShouldNotReachHere(); + } + + if (generate_oop_map) { + map->set_callee_saved(VMRegImpl::stack2reg(offset>>2), + RegisterSaver_LiveRegs[i].vmreg); + map->set_callee_saved(VMRegImpl::stack2reg((offset + half_reg_size)>>2), + RegisterSaver_LiveRegs[i].vmreg->next()); + } + offset += reg_size; + } + + BLOCK_COMMENT("} push_frame_reg_args_and_save_live_registers"); + + // And we're done. + return map; +} + + +// Pop the current frame and restore all the registers that we +// saved. +void RegisterSaver::restore_live_registers_and_pop_frame(MacroAssembler* masm, + int frame_size_in_bytes, + bool restore_ctr) { + int i; + int offset; + const int regstosave_num = sizeof(RegisterSaver_LiveRegs) / + sizeof(RegisterSaver::LiveRegType); + const int register_save_size = regstosave_num * reg_size; + const int register_save_offset = frame_size_in_bytes - register_save_size; + + BLOCK_COMMENT("restore_live_registers_and_pop_frame {"); + + // restore all registers (ints and floats) + offset = register_save_offset; + for (int i = 0; i < regstosave_num; i++) { + int reg_num = RegisterSaver_LiveRegs[i].reg_num; + int reg_type = RegisterSaver_LiveRegs[i].reg_type; + + switch (reg_type) { + case RegisterSaver::int_reg: { + if (reg_num != 30) // R30 restored at the end, it's the tmp reg! + __ ld(as_Register(reg_num), offset, R1_SP); + break; + } + case RegisterSaver::float_reg: { + __ lfd(as_FloatRegister(reg_num), offset, R1_SP); + break; + } + case RegisterSaver::special_reg: { + if (reg_num == SR_CTR_SpecialRegisterEnumValue) { + if (restore_ctr) { // Nothing to do here if ctr already contains the next address. + __ ld(R30, offset, R1_SP); + __ mtctr(R30); + } + } else { + Unimplemented(); + } + break; + } + default: + ShouldNotReachHere(); + } + offset += reg_size; + } + + // pop the frame + __ pop_frame(); + + // restore the flags + __ restore_LR_CR(R30); + + // restore scratch register's value + __ ld(R30, -reg_size, R1_SP); + + BLOCK_COMMENT("} restore_live_registers_and_pop_frame"); +} + +void RegisterSaver::push_frame_and_save_argument_registers(MacroAssembler* masm, Register r_temp, + int frame_size,int total_args, const VMRegPair *regs, + const VMRegPair *regs2) { + __ push_frame(frame_size, r_temp); + int st_off = frame_size - wordSize; + for (int i = 0; i < total_args; i++) { + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_Register()) { + Register r = r_1->as_Register(); + __ std(r, st_off, R1_SP); + st_off -= wordSize; + } else if (r_1->is_FloatRegister()) { + FloatRegister f = r_1->as_FloatRegister(); + __ stfd(f, st_off, R1_SP); + st_off -= wordSize; + } + } + if (regs2 != NULL) { + for (int i = 0; i < total_args; i++) { + VMReg r_1 = regs2[i].first(); + VMReg r_2 = regs2[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_Register()) { + Register r = r_1->as_Register(); + __ std(r, st_off, R1_SP); + st_off -= wordSize; + } else if (r_1->is_FloatRegister()) { + FloatRegister f = r_1->as_FloatRegister(); + __ stfd(f, st_off, R1_SP); + st_off -= wordSize; + } + } + } +} + +void RegisterSaver::restore_argument_registers_and_pop_frame(MacroAssembler*masm, int frame_size, + int total_args, const VMRegPair *regs, + const VMRegPair *regs2) { + int st_off = frame_size - wordSize; + for (int i = 0; i < total_args; i++) { + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (r_1->is_Register()) { + Register r = r_1->as_Register(); + __ ld(r, st_off, R1_SP); + st_off -= wordSize; + } else if (r_1->is_FloatRegister()) { + FloatRegister f = r_1->as_FloatRegister(); + __ lfd(f, st_off, R1_SP); + st_off -= wordSize; + } + } + if (regs2 != NULL) + for (int i = 0; i < total_args; i++) { + VMReg r_1 = regs2[i].first(); + VMReg r_2 = regs2[i].second(); + if (r_1->is_Register()) { + Register r = r_1->as_Register(); + __ ld(r, st_off, R1_SP); + st_off -= wordSize; + } else if (r_1->is_FloatRegister()) { + FloatRegister f = r_1->as_FloatRegister(); + __ lfd(f, st_off, R1_SP); + st_off -= wordSize; + } + } + __ pop_frame(); +} + +// Restore the registers that might be holding a result. +void RegisterSaver::restore_result_registers(MacroAssembler* masm, int frame_size_in_bytes) { + int i; + int offset; + const int regstosave_num = sizeof(RegisterSaver_LiveRegs) / + sizeof(RegisterSaver::LiveRegType); + const int register_save_size = regstosave_num * reg_size; + const int register_save_offset = frame_size_in_bytes - register_save_size; + + // restore all result registers (ints and floats) + offset = register_save_offset; + for (int i = 0; i < regstosave_num; i++) { + int reg_num = RegisterSaver_LiveRegs[i].reg_num; + int reg_type = RegisterSaver_LiveRegs[i].reg_type; + switch (reg_type) { + case RegisterSaver::int_reg: { + if (as_Register(reg_num)==R3_RET) // int result_reg + __ ld(as_Register(reg_num), offset, R1_SP); + break; + } + case RegisterSaver::float_reg: { + if (as_FloatRegister(reg_num)==F1_RET) // float result_reg + __ lfd(as_FloatRegister(reg_num), offset, R1_SP); + break; + } + case RegisterSaver::special_reg: { + // Special registers don't hold a result. + break; + } + default: + ShouldNotReachHere(); + } + offset += reg_size; + } +} + +// Is vector's size (in bytes) bigger than a size saved by default? +bool SharedRuntime::is_wide_vector(int size) { + ResourceMark rm; + // Note, MaxVectorSize == 8 on PPC64. + assert(size <= 8, err_msg_res("%d bytes vectors are not supported", size)); + return size > 8; +} +#ifdef COMPILER2 +static int reg2slot(VMReg r) { + return r->reg2stack() + SharedRuntime::out_preserve_stack_slots(); +} + +static int reg2offset(VMReg r) { + return (r->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; +} +#endif + +// --------------------------------------------------------------------------- +// Read the array of BasicTypes from a signature, and compute where the +// arguments should go. Values in the VMRegPair regs array refer to 4-byte +// quantities. Values less than VMRegImpl::stack0 are registers, those above +// refer to 4-byte stack slots. All stack slots are based off of the stack pointer +// as framesizes are fixed. +// VMRegImpl::stack0 refers to the first slot 0(sp). +// and VMRegImpl::stack0+1 refers to the memory word 4-bytes higher. Register +// up to RegisterImpl::number_of_registers) are the 64-bit +// integer registers. + +// Note: the INPUTS in sig_bt are in units of Java argument words, which are +// either 32-bit or 64-bit depending on the build. The OUTPUTS are in 32-bit +// units regardless of build. Of course for i486 there is no 64 bit build + +// The Java calling convention is a "shifted" version of the C ABI. +// By skipping the first C ABI register we can call non-static jni methods +// with small numbers of arguments without having to shuffle the arguments +// at all. Since we control the java ABI we ought to at least get some +// advantage out of it. + +const VMReg java_iarg_reg[8] = { + R3->as_VMReg(), + R4->as_VMReg(), + R5->as_VMReg(), + R6->as_VMReg(), + R7->as_VMReg(), + R8->as_VMReg(), + R9->as_VMReg(), + R10->as_VMReg() +}; + +const VMReg java_farg_reg[13] = { + F1->as_VMReg(), + F2->as_VMReg(), + F3->as_VMReg(), + F4->as_VMReg(), + F5->as_VMReg(), + F6->as_VMReg(), + F7->as_VMReg(), + F8->as_VMReg(), + F9->as_VMReg(), + F10->as_VMReg(), + F11->as_VMReg(), + F12->as_VMReg(), + F13->as_VMReg() +}; + +const int num_java_iarg_registers = sizeof(java_iarg_reg) / sizeof(java_iarg_reg[0]); +const int num_java_farg_registers = sizeof(java_farg_reg) / sizeof(java_farg_reg[0]); + +int SharedRuntime::java_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed, + int is_outgoing) { + // C2c calling conventions for compiled-compiled calls. + // Put 8 ints/longs into registers _AND_ 13 float/doubles into + // registers _AND_ put the rest on the stack. + + const int inc_stk_for_intfloat = 1; // 1 slots for ints and floats + const int inc_stk_for_longdouble = 2; // 2 slots for longs and doubles + + int i; + VMReg reg; + int stk = 0; + int ireg = 0; + int freg = 0; + + // We put the first 8 arguments into registers and the rest on the + // stack, float arguments are already in their argument registers + // due to c2c calling conventions (see calling_convention). + for (int i = 0; i < total_args_passed; ++i) { + switch(sig_bt[i]) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + if (ireg < num_java_iarg_registers) { + // Put int/ptr in register + reg = java_iarg_reg[ireg]; + ++ireg; + } else { + // Put int/ptr on stack. + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_intfloat; + } + regs[i].set1(reg); + break; + case T_LONG: + assert(sig_bt[i+1] == T_VOID, "expecting half"); + if (ireg < num_java_iarg_registers) { + // Put long in register. + reg = java_iarg_reg[ireg]; + ++ireg; + } else { + // Put long on stack. They must be aligned to 2 slots. + if (stk & 0x1) ++stk; + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_longdouble; + } + regs[i].set2(reg); + break; + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: + if (ireg < num_java_iarg_registers) { + // Put ptr in register. + reg = java_iarg_reg[ireg]; + ++ireg; + } else { + // Put ptr on stack. Objects must be aligned to 2 slots too, + // because "64-bit pointers record oop-ishness on 2 aligned + // adjacent registers." (see OopFlow::build_oop_map). + if (stk & 0x1) ++stk; + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_longdouble; + } + regs[i].set2(reg); + break; + case T_FLOAT: + if (freg < num_java_farg_registers) { + // Put float in register. + reg = java_farg_reg[freg]; + ++freg; + } else { + // Put float on stack. + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_intfloat; + } + regs[i].set1(reg); + break; + case T_DOUBLE: + assert(sig_bt[i+1] == T_VOID, "expecting half"); + if (freg < num_java_farg_registers) { + // Put double in register. + reg = java_farg_reg[freg]; + ++freg; + } else { + // Put double on stack. They must be aligned to 2 slots. + if (stk & 0x1) ++stk; + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_longdouble; + } + regs[i].set2(reg); + break; + case T_VOID: + // Do not count halves. + regs[i].set_bad(); + break; + default: + ShouldNotReachHere(); + } + } + return round_to(stk, 2); +} + +#ifdef COMPILER2 +// Calling convention for calling C code. +int SharedRuntime::c_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + VMRegPair *regs2, + int total_args_passed) { + // Calling conventions for C runtime calls and calls to JNI native methods. + // + // PPC64 convention: Hoist the first 8 int/ptr/long's in the first 8 + // int regs, leaving int regs undefined if the arg is flt/dbl. Hoist + // the first 13 flt/dbl's in the first 13 fp regs but additionally + // copy flt/dbl to the stack if they are beyond the 8th argument. + + const VMReg iarg_reg[8] = { + R3->as_VMReg(), + R4->as_VMReg(), + R5->as_VMReg(), + R6->as_VMReg(), + R7->as_VMReg(), + R8->as_VMReg(), + R9->as_VMReg(), + R10->as_VMReg() + }; + + const VMReg farg_reg[13] = { + F1->as_VMReg(), + F2->as_VMReg(), + F3->as_VMReg(), + F4->as_VMReg(), + F5->as_VMReg(), + F6->as_VMReg(), + F7->as_VMReg(), + F8->as_VMReg(), + F9->as_VMReg(), + F10->as_VMReg(), + F11->as_VMReg(), + F12->as_VMReg(), + F13->as_VMReg() + }; + + // Check calling conventions consistency. + assert(sizeof(iarg_reg) / sizeof(iarg_reg[0]) == Argument::n_int_register_parameters_c && + sizeof(farg_reg) / sizeof(farg_reg[0]) == Argument::n_float_register_parameters_c, + "consistency"); + + // `Stk' counts stack slots. Due to alignment, 32 bit values occupy + // 2 such slots, like 64 bit values do. + const int inc_stk_for_intfloat = 2; // 2 slots for ints and floats + const int inc_stk_for_longdouble = 2; // 2 slots for longs and doubles + + int i; + VMReg reg; + // Leave room for C-compatible ABI_REG_ARGS. + int stk = (frame::abi_reg_args_size - frame::jit_out_preserve_size) / VMRegImpl::stack_slot_size; + int arg = 0; + int freg = 0; + + // Avoid passing C arguments in the wrong stack slots. +#if defined(ABI_ELFv2) + assert((SharedRuntime::out_preserve_stack_slots() + stk) * VMRegImpl::stack_slot_size == 96, + "passing C arguments in wrong stack slots"); +#else + assert((SharedRuntime::out_preserve_stack_slots() + stk) * VMRegImpl::stack_slot_size == 112, + "passing C arguments in wrong stack slots"); +#endif + // We fill-out regs AND regs2 if an argument must be passed in a + // register AND in a stack slot. If regs2 is NULL in such a + // situation, we bail-out with a fatal error. + for (int i = 0; i < total_args_passed; ++i, ++arg) { + // Initialize regs2 to BAD. + if (regs2 != NULL) regs2[i].set_bad(); + + switch(sig_bt[i]) { + + // + // If arguments 0-7 are integers, they are passed in integer registers. + // Argument i is placed in iarg_reg[i]. + // + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + // We must cast ints to longs and use full 64 bit stack slots + // here. We do the cast in GraphKit::gen_stub() and just guard + // here against loosing that change. + assert(CCallingConventionRequiresIntsAsLongs, + "argument of type int should be promoted to type long"); + guarantee(i > 0 && sig_bt[i-1] == T_LONG, + "argument of type (bt) should have been promoted to type (T_LONG,bt) for bt in " + "{T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}"); + // Do not count halves. + regs[i].set_bad(); + --arg; + break; + case T_LONG: + guarantee(sig_bt[i+1] == T_VOID || + sig_bt[i+1] == T_BOOLEAN || sig_bt[i+1] == T_CHAR || + sig_bt[i+1] == T_BYTE || sig_bt[i+1] == T_SHORT || + sig_bt[i+1] == T_INT, + "expecting type (T_LONG,half) or type (T_LONG,bt) with bt in {T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}"); + case T_OBJECT: + case T_ARRAY: + case T_ADDRESS: + // Oops are already boxed if required (JNI). + if (arg < Argument::n_int_register_parameters_c) { + reg = iarg_reg[arg]; + } else { + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_longdouble; + } + regs[i].set2(reg); + break; + + // + // Floats are treated differently from int regs: The first 13 float arguments + // are passed in registers (not the float args among the first 13 args). + // Thus argument i is NOT passed in farg_reg[i] if it is float. It is passed + // in farg_reg[j] if argument i is the j-th float argument of this call. + // + case T_FLOAT: +#if defined(LINUX) + // Linux uses ELF ABI. Both original ELF and ELFv2 ABIs have float + // in the least significant word of an argument slot. +#if defined(VM_LITTLE_ENDIAN) +#define FLOAT_WORD_OFFSET_IN_SLOT 0 +#else +#define FLOAT_WORD_OFFSET_IN_SLOT 1 +#endif +#elif defined(AIX) + // Although AIX runs on big endian CPU, float is in the most + // significant word of an argument slot. +#define FLOAT_WORD_OFFSET_IN_SLOT 0 +#else +#error "unknown OS" +#endif + if (freg < Argument::n_float_register_parameters_c) { + // Put float in register ... + reg = farg_reg[freg]; + ++freg; + + // Argument i for i > 8 is placed on the stack even if it's + // placed in a register (if it's a float arg). Aix disassembly + // shows that xlC places these float args on the stack AND in + // a register. This is not documented, but we follow this + // convention, too. + if (arg >= Argument::n_regs_not_on_stack_c) { + // ... and on the stack. + guarantee(regs2 != NULL, "must pass float in register and stack slot"); + VMReg reg2 = VMRegImpl::stack2reg(stk + FLOAT_WORD_OFFSET_IN_SLOT); + regs2[i].set1(reg2); + stk += inc_stk_for_intfloat; + } + + } else { + // Put float on stack. + reg = VMRegImpl::stack2reg(stk + FLOAT_WORD_OFFSET_IN_SLOT); + stk += inc_stk_for_intfloat; + } + regs[i].set1(reg); + break; + case T_DOUBLE: + assert(sig_bt[i+1] == T_VOID, "expecting half"); + if (freg < Argument::n_float_register_parameters_c) { + // Put double in register ... + reg = farg_reg[freg]; + ++freg; + + // Argument i for i > 8 is placed on the stack even if it's + // placed in a register (if it's a double arg). Aix disassembly + // shows that xlC places these float args on the stack AND in + // a register. This is not documented, but we follow this + // convention, too. + if (arg >= Argument::n_regs_not_on_stack_c) { + // ... and on the stack. + guarantee(regs2 != NULL, "must pass float in register and stack slot"); + VMReg reg2 = VMRegImpl::stack2reg(stk); + regs2[i].set2(reg2); + stk += inc_stk_for_longdouble; + } + } else { + // Put double on stack. + reg = VMRegImpl::stack2reg(stk); + stk += inc_stk_for_longdouble; + } + regs[i].set2(reg); + break; + + case T_VOID: + // Do not count halves. + regs[i].set_bad(); + --arg; + break; + default: + ShouldNotReachHere(); + } + } + + return round_to(stk, 2); +} +#endif // COMPILER2 + +// Do we need to convert ints to longs for c calls? +bool SharedRuntime::c_calling_convention_requires_ints_as_longs() { + return true; +} + +static address gen_c2i_adapter(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + Label& call_interpreter, + const Register& ientry) { + + address c2i_entrypoint; + + const Register sender_SP = R21_sender_SP; // == R21_tmp1 + const Register code = R22_tmp2; + //const Register ientry = R23_tmp3; + const Register value_regs[] = { R24_tmp4, R25_tmp5, R26_tmp6 }; + const int num_value_regs = sizeof(value_regs) / sizeof(Register); + int value_regs_index = 0; + + const Register return_pc = R27_tmp7; + const Register tmp = R28_tmp8; + + assert_different_registers(sender_SP, code, ientry, return_pc, tmp); + + // Adapter needs TOP_IJAVA_FRAME_ABI. + const int adapter_size = frame::top_ijava_frame_abi_size + + round_to(total_args_passed * wordSize, frame::alignment_in_bytes); + + // regular (verified) c2i entry point + c2i_entrypoint = __ pc(); + + // Does compiled code exists? If yes, patch the caller's callsite. + __ ld(code, method_(code)); + __ cmpdi(CCR0, code, 0); + __ ld(ientry, method_(interpreter_entry)); // preloaded + __ beq(CCR0, call_interpreter); + + + // Patch caller's callsite, method_(code) was not NULL which means that + // compiled code exists. + __ mflr(return_pc); + __ std(return_pc, _abi(lr), R1_SP); + RegisterSaver::push_frame_and_save_argument_registers(masm, tmp, adapter_size, total_args_passed, regs); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite), R19_method, return_pc); + + RegisterSaver::restore_argument_registers_and_pop_frame(masm, adapter_size, total_args_passed, regs); + __ ld(return_pc, _abi(lr), R1_SP); + __ ld(ientry, method_(interpreter_entry)); // preloaded + __ mtlr(return_pc); + + + // Call the interpreter. + __ BIND(call_interpreter); + __ mtctr(ientry); + + // Get a copy of the current SP for loading caller's arguments. + __ mr(sender_SP, R1_SP); + + // Add space for the adapter. + __ resize_frame(-adapter_size, R12_scratch2); + + int st_off = adapter_size - wordSize; + + // Write the args into the outgoing interpreter space. + for (int i = 0; i < total_args_passed; i++) { + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_stack()) { + Register tmp_reg = value_regs[value_regs_index]; + value_regs_index = (value_regs_index + 1) % num_value_regs; + // The calling convention produces OptoRegs that ignore the out + // preserve area (JIT's ABI). We must account for it here. + int ld_off = (r_1->reg2stack() + SharedRuntime::out_preserve_stack_slots()) * VMRegImpl::stack_slot_size; + if (!r_2->is_valid()) { + __ lwz(tmp_reg, ld_off, sender_SP); + } else { + __ ld(tmp_reg, ld_off, sender_SP); + } + // Pretend stack targets were loaded into tmp_reg. + r_1 = tmp_reg->as_VMReg(); + } + + if (r_1->is_Register()) { + Register r = r_1->as_Register(); + if (!r_2->is_valid()) { + __ stw(r, st_off, R1_SP); + st_off-=wordSize; + } else { + // Longs are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. + if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + DEBUG_ONLY( __ li(tmp, 0); __ std(tmp, st_off, R1_SP); ) + st_off-=wordSize; + } + __ std(r, st_off, R1_SP); + st_off-=wordSize; + } + } else { + assert(r_1->is_FloatRegister(), ""); + FloatRegister f = r_1->as_FloatRegister(); + if (!r_2->is_valid()) { + __ stfs(f, st_off, R1_SP); + st_off-=wordSize; + } else { + // In 64bit, doubles are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. + // One of these should get known junk... + DEBUG_ONLY( __ li(tmp, 0); __ std(tmp, st_off, R1_SP); ) + st_off-=wordSize; + __ stfd(f, st_off, R1_SP); + st_off-=wordSize; + } + } + } + + // Jump to the interpreter just as if interpreter was doing it. + +#ifdef CC_INTERP + const Register tos = R17_tos; +#else + const Register tos = R15_esp; + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1); +#endif + + // load TOS + __ addi(tos, R1_SP, st_off); + + // Frame_manager expects initial_caller_sp (= SP without resize by c2i) in R21_tmp1. + assert(sender_SP == R21_sender_SP, "passing initial caller's SP in wrong register"); + __ bctr(); + + return c2i_entrypoint; +} + +static void gen_i2c_adapter(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs) { + + // Load method's entry-point from methodOop. + __ ld(R12_scratch2, in_bytes(methodOopDesc::from_compiled_offset()), R19_method); + __ mtctr(R12_scratch2); + + // We will only enter here from an interpreted frame and never from after + // passing thru a c2i. Azul allowed this but we do not. If we lose the + // race and use a c2i we will remain interpreted for the race loser(s). + // This removes all sorts of headaches on the x86 side and also eliminates + // the possibility of having c2i -> i2c -> c2i -> ... endless transitions. + + // Note: r13 contains the senderSP on entry. We must preserve it since + // we may do a i2c -> c2i transition if we lose a race where compiled + // code goes non-entrant while we get args ready. + // In addition we use r13 to locate all the interpreter args as + // we must align the stack to 16 bytes on an i2c entry else we + // lose alignment we expect in all compiled code and register + // save code can segv when fxsave instructions find improperly + // aligned stack pointer. + +#ifdef CC_INTERP + const Register ld_ptr = R17_tos; +#else + const Register ld_ptr = R15_esp; +#endif + + const Register value_regs[] = { R22_tmp2, R23_tmp3, R24_tmp4, R25_tmp5, R26_tmp6 }; + const int num_value_regs = sizeof(value_regs) / sizeof(Register); + int value_regs_index = 0; + + int ld_offset = total_args_passed*wordSize; + + // Cut-out for having no stack args. Since up to 2 int/oop args are passed + // in registers, we will occasionally have no stack args. + int comp_words_on_stack = 0; + if (comp_args_on_stack) { + // Sig words on the stack are greater-than VMRegImpl::stack0. Those in + // registers are below. By subtracting stack0, we either get a negative + // number (all values in registers) or the maximum stack slot accessed. + + // Convert 4-byte c2 stack slots to words. + comp_words_on_stack = round_to(comp_args_on_stack*VMRegImpl::stack_slot_size, wordSize)>>LogBytesPerWord; + // Round up to miminum stack alignment, in wordSize. + comp_words_on_stack = round_to(comp_words_on_stack, 2); + __ resize_frame(-comp_words_on_stack * wordSize, R11_scratch1); + } + + // Now generate the shuffle code. Pick up all register args and move the + // rest through register value=Z_R12. + BLOCK_COMMENT("Shuffle arguments"); + for (int i = 0; i < total_args_passed; i++) { + if (sig_bt[i] == T_VOID) { + assert(i > 0 && (sig_bt[i-1] == T_LONG || sig_bt[i-1] == T_DOUBLE), "missing half"); + continue; + } + + // Pick up 0, 1 or 2 words from ld_ptr. + assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), + "scrambled load targets?"); + VMReg r_1 = regs[i].first(); + VMReg r_2 = regs[i].second(); + if (!r_1->is_valid()) { + assert(!r_2->is_valid(), ""); + continue; + } + if (r_1->is_FloatRegister()) { + if (!r_2->is_valid()) { + __ lfs(r_1->as_FloatRegister(), ld_offset, ld_ptr); + ld_offset-=wordSize; + } else { + // Skip the unused interpreter slot. + __ lfd(r_1->as_FloatRegister(), ld_offset-wordSize, ld_ptr); + ld_offset-=2*wordSize; + } + } else { + Register r; + if (r_1->is_stack()) { + // Must do a memory to memory move thru "value". + r = value_regs[value_regs_index]; + value_regs_index = (value_regs_index + 1) % num_value_regs; + } else { + r = r_1->as_Register(); + } + if (!r_2->is_valid()) { + // Not sure we need to do this but it shouldn't hurt. + if (sig_bt[i] == T_OBJECT || sig_bt[i] == T_ADDRESS || sig_bt[i] == T_ARRAY) { + __ ld(r, ld_offset, ld_ptr); + ld_offset-=wordSize; + } else { + __ lwz(r, ld_offset, ld_ptr); + ld_offset-=wordSize; + } + } else { + // In 64bit, longs are given 2 64-bit slots in the interpreter, but the + // data is passed in only 1 slot. + if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { + ld_offset-=wordSize; + } + __ ld(r, ld_offset, ld_ptr); + ld_offset-=wordSize; + } + + if (r_1->is_stack()) { + // Now store value where the compiler expects it + int st_off = (r_1->reg2stack() + SharedRuntime::out_preserve_stack_slots())*VMRegImpl::stack_slot_size; + + if (sig_bt[i] == T_INT || sig_bt[i] == T_FLOAT ||sig_bt[i] == T_BOOLEAN || + sig_bt[i] == T_SHORT || sig_bt[i] == T_CHAR || sig_bt[i] == T_BYTE) { + __ stw(r, st_off, R1_SP); + } else { + __ std(r, st_off, R1_SP); + } + } + } + } + + BLOCK_COMMENT("Store method"); + // Store method into thread->callee_target. + // We might end up in handle_wrong_method if the callee is + // deoptimized as we race thru here. If that happens we don't want + // to take a safepoint because the caller frame will look + // interpreted and arguments are now "compiled" so it is much better + // to make this transition invisible to the stack walking + // code. Unfortunately if we try and find the callee by normal means + // a safepoint is possible. So we stash the desired callee in the + // thread and the vm will find there should this case occur. + __ std(R19_method, thread_(callee_target)); + + // Jump to the compiled code just as if compiled code was doing it. + __ bctr(); +} + +AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm, + int total_args_passed, + int comp_args_on_stack, + const BasicType *sig_bt, + const VMRegPair *regs, + AdapterFingerPrint* fingerprint) { + address i2c_entry; + address c2i_unverified_entry; + address c2i_entry; + + + // entry: i2c + + __ align(CodeEntryAlignment); + i2c_entry = __ pc(); + gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); + + + // entry: c2i unverified + + __ align(CodeEntryAlignment); + BLOCK_COMMENT("c2i unverified entry"); + c2i_unverified_entry = __ pc(); + + // inline_cache contains a compiledICHolder + const Register ic = R19_method; + const Register ic_klass = R11_scratch1; + const Register receiver_klass = R12_scratch2; + const Register code = R21_tmp1; + const Register ientry = R23_tmp3; + + assert_different_registers(ic, ic_klass, receiver_klass, R3_ARG1, code, ientry); + assert(R11_scratch1 == R11, "need prologue scratch register"); + + Label call_interpreter; + + assert(!MacroAssembler::needs_explicit_null_check(oopDesc::klass_offset_in_bytes()), + "klass offset should reach into any page"); + // Check for NULL argument if we don't have implicit null checks. + if (!ImplicitNullChecks || !os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + __ trap_null_check(R3_ARG1); + } else { + Label valid; + __ cmpdi(CCR0, R3_ARG1, 0); + __ bne_predict_taken(CCR0, valid); + // We have a null argument, branch to ic_miss_stub. + __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(), + relocInfo::runtime_call_type); + __ BIND(valid); + } + } + // Assume argument is not NULL, load klass from receiver. + __ load_klass(receiver_klass, R3_ARG1); + + __ ld(ic_klass, compiledICHolderOopDesc::holder_klass_offset(), ic); + + if (TrapBasedICMissChecks) { + __ trap_ic_miss_check(receiver_klass, ic_klass); + } else { + Label valid; + __ cmpd(CCR0, receiver_klass, ic_klass); + __ beq_predict_taken(CCR0, valid); + // We have an unexpected klass, branch to ic_miss_stub. + __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(), + relocInfo::runtime_call_type); + __ BIND(valid); + } + + // Argument is valid and klass is as expected, continue. + + // Extract method oop from inline cache, verified entry point needs it. + __ ld(R19_method, compiledICHolderOopDesc::holder_method_offset(), ic); + assert(R19_method == ic, "the inline cache register is dead here"); + + __ ld(code, method_(code)); + __ cmpdi(CCR0, code, 0); + __ ld(ientry, method_(interpreter_entry)); // preloaded + __ beq_predict_taken(CCR0, call_interpreter); + + // Branch to ic_miss_stub. + __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(), relocInfo::runtime_call_type); + + // entry: c2i + + c2i_entry = gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, call_interpreter, ientry); + + return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); +} + +#ifdef COMPILER2 +// An oop arg. Must pass a handle not the oop itself. +static void object_move(MacroAssembler* masm, + int frame_size_in_slots, + OopMap* oop_map, int oop_handle_offset, + bool is_receiver, int* receiver_offset, + VMRegPair src, VMRegPair dst, + Register r_caller_sp, Register r_temp_1, Register r_temp_2) { + assert(!is_receiver || (is_receiver && (*receiver_offset == -1)), + "receiver has already been moved"); + + // We must pass a handle. First figure out the location we use as a handle. + + if (src.first()->is_stack()) { + // stack to stack or reg + + const Register r_handle = dst.first()->is_stack() ? r_temp_1 : dst.first()->as_Register(); + Label skip; + const int oop_slot_in_callers_frame = reg2slot(src.first()); + + guarantee(!is_receiver, "expecting receiver in register"); + oop_map->set_oop(VMRegImpl::stack2reg(oop_slot_in_callers_frame + frame_size_in_slots)); + + __ addi(r_handle, r_caller_sp, reg2offset(src.first())); + __ ld( r_temp_2, reg2offset(src.first()), r_caller_sp); + __ cmpdi(CCR0, r_temp_2, 0); + __ bne(CCR0, skip); + // Use a NULL handle if oop is NULL. + __ li(r_handle, 0); + __ bind(skip); + + if (dst.first()->is_stack()) { + // stack to stack + __ std(r_handle, reg2offset(dst.first()), R1_SP); + } else { + // stack to reg + // Nothing to do, r_handle is already the dst register. + } + } else { + // reg to stack or reg + const Register r_oop = src.first()->as_Register(); + const Register r_handle = dst.first()->is_stack() ? r_temp_1 : dst.first()->as_Register(); + const int oop_slot = (r_oop->encoding()-R3_ARG1->encoding()) * VMRegImpl::slots_per_word + + oop_handle_offset; // in slots + const int oop_offset = oop_slot * VMRegImpl::stack_slot_size; + Label skip; + + if (is_receiver) { + *receiver_offset = oop_offset; + } + oop_map->set_oop(VMRegImpl::stack2reg(oop_slot)); + + __ std( r_oop, oop_offset, R1_SP); + __ addi(r_handle, R1_SP, oop_offset); + + __ cmpdi(CCR0, r_oop, 0); + __ bne(CCR0, skip); + // Use a NULL handle if oop is NULL. + __ li(r_handle, 0); + __ bind(skip); + + if (dst.first()->is_stack()) { + // reg to stack + __ std(r_handle, reg2offset(dst.first()), R1_SP); + } else { + // reg to reg + // Nothing to do, r_handle is already the dst register. + } + } +} + +static void int_move(MacroAssembler*masm, + VMRegPair src, VMRegPair dst, + Register r_caller_sp, Register r_temp) { + assert(src.first()->is_valid() && src.second() == src.first()->next(), "incoming must be long-int"); + assert(dst.first()->is_valid() && dst.second() == dst.first()->next(), "outgoing must be long"); + + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ lwa(r_temp, reg2offset(src.first()), r_caller_sp); + __ std(r_temp, reg2offset(dst.first()), R1_SP); + } else { + // stack to reg + __ lwa(dst.first()->as_Register(), reg2offset(src.first()), r_caller_sp); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ extsw(r_temp, src.first()->as_Register()); + __ std(r_temp, reg2offset(dst.first()), R1_SP); + } else { + // reg to reg + __ extsw(dst.first()->as_Register(), src.first()->as_Register()); + } +} + +static void long_move(MacroAssembler*masm, + VMRegPair src, VMRegPair dst, + Register r_caller_sp, Register r_temp) { + assert(src.first()->is_valid() && src.second() == src.first()->next(), "incoming must be long"); + assert(dst.first()->is_valid() && dst.second() == dst.first()->next(), "outgoing must be long"); + + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ ld( r_temp, reg2offset(src.first()), r_caller_sp); + __ std(r_temp, reg2offset(dst.first()), R1_SP); + } else { + // stack to reg + __ ld(dst.first()->as_Register(), reg2offset(src.first()), r_caller_sp); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ std(src.first()->as_Register(), reg2offset(dst.first()), R1_SP); + } else { + // reg to reg + if (dst.first()->as_Register() != src.first()->as_Register()) + __ mr(dst.first()->as_Register(), src.first()->as_Register()); + } +} + +static void float_move(MacroAssembler*masm, + VMRegPair src, VMRegPair dst, + Register r_caller_sp, Register r_temp) { + assert(src.first()->is_valid() && !src.second()->is_valid(), "incoming must be float"); + assert(dst.first()->is_valid() && !dst.second()->is_valid(), "outgoing must be float"); + + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ lwz(r_temp, reg2offset(src.first()), r_caller_sp); + __ stw(r_temp, reg2offset(dst.first()), R1_SP); + } else { + // stack to reg + __ lfs(dst.first()->as_FloatRegister(), reg2offset(src.first()), r_caller_sp); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ stfs(src.first()->as_FloatRegister(), reg2offset(dst.first()), R1_SP); + } else { + // reg to reg + if (dst.first()->as_FloatRegister() != src.first()->as_FloatRegister()) + __ fmr(dst.first()->as_FloatRegister(), src.first()->as_FloatRegister()); + } +} + +static void double_move(MacroAssembler*masm, + VMRegPair src, VMRegPair dst, + Register r_caller_sp, Register r_temp) { + assert(src.first()->is_valid() && src.second() == src.first()->next(), "incoming must be double"); + assert(dst.first()->is_valid() && dst.second() == dst.first()->next(), "outgoing must be double"); + + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ ld( r_temp, reg2offset(src.first()), r_caller_sp); + __ std(r_temp, reg2offset(dst.first()), R1_SP); + } else { + // stack to reg + __ lfd(dst.first()->as_FloatRegister(), reg2offset(src.first()), r_caller_sp); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ stfd(src.first()->as_FloatRegister(), reg2offset(dst.first()), R1_SP); + } else { + // reg to reg + if (dst.first()->as_FloatRegister() != src.first()->as_FloatRegister()) + __ fmr(dst.first()->as_FloatRegister(), src.first()->as_FloatRegister()); + } +} + +void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + switch (ret_type) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + __ stw (R3_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_ARRAY: + case T_OBJECT: + case T_LONG: + __ std (R3_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_FLOAT: + __ stfs(F1_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_DOUBLE: + __ stfd(F1_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_VOID: + break; + default: + ShouldNotReachHere(); + break; + } +} + +void SharedRuntime::restore_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { + switch (ret_type) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + __ lwz(R3_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_ARRAY: + case T_OBJECT: + case T_LONG: + __ ld (R3_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_FLOAT: + __ lfs(F1_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_DOUBLE: + __ lfd(F1_RET, frame_slots*VMRegImpl::stack_slot_size, R1_SP); + break; + case T_VOID: + break; + default: + ShouldNotReachHere(); + break; + } +} + +static void save_or_restore_arguments(MacroAssembler* masm, + const int stack_slots, + const int total_in_args, + const int arg_save_area, + OopMap* map, + VMRegPair* in_regs, + BasicType* in_sig_bt) { + // If map is non-NULL then the code should store the values, + // otherwise it should load them. + int slot = arg_save_area; + // Save down double word first. + for (int i = 0; i < total_in_args; i++) { + if (in_regs[i].first()->is_FloatRegister() && in_sig_bt[i] == T_DOUBLE) { + int offset = slot * VMRegImpl::stack_slot_size; + slot += VMRegImpl::slots_per_word; + assert(slot <= stack_slots, "overflow (after DOUBLE stack slot)"); + if (map != NULL) { + __ stfd(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); + } else { + __ lfd(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); + } + } else if (in_regs[i].first()->is_Register() && + (in_sig_bt[i] == T_LONG || in_sig_bt[i] == T_ARRAY)) { + int offset = slot * VMRegImpl::stack_slot_size; + if (map != NULL) { + __ std(in_regs[i].first()->as_Register(), offset, R1_SP); + if (in_sig_bt[i] == T_ARRAY) { + map->set_oop(VMRegImpl::stack2reg(slot)); + } + } else { + __ ld(in_regs[i].first()->as_Register(), offset, R1_SP); + } + slot += VMRegImpl::slots_per_word; + assert(slot <= stack_slots, "overflow (after LONG/ARRAY stack slot)"); + } + } + // Save or restore single word registers. + for (int i = 0; i < total_in_args; i++) { + // PPC64: pass ints as longs: must only deal with floats here. + if (in_regs[i].first()->is_FloatRegister()) { + if (in_sig_bt[i] == T_FLOAT) { + int offset = slot * VMRegImpl::stack_slot_size; + slot++; + assert(slot <= stack_slots, "overflow (after FLOAT stack slot)"); + if (map != NULL) { + __ stfs(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); + } else { + __ lfs(in_regs[i].first()->as_FloatRegister(), offset, R1_SP); + } + } + } else if (in_regs[i].first()->is_stack()) { + if (in_sig_bt[i] == T_ARRAY && map != NULL) { + int offset_in_older_frame = in_regs[i].first()->reg2stack() + SharedRuntime::out_preserve_stack_slots(); + map->set_oop(VMRegImpl::stack2reg(offset_in_older_frame + stack_slots)); + } + } + } +} + +// Check GC_locker::needs_gc and enter the runtime if it's true. This +// keeps a new JNI critical region from starting until a GC has been +// forced. Save down any oops in registers and describe them in an +// OopMap. +static void check_needs_gc_for_critical_native(MacroAssembler* masm, + const int stack_slots, + const int total_in_args, + const int arg_save_area, + OopMapSet* oop_maps, + VMRegPair* in_regs, + BasicType* in_sig_bt, + Register tmp_reg ) { + __ block_comment("check GC_locker::needs_gc"); + Label cont; + __ lbz(tmp_reg, (RegisterOrConstant)(intptr_t)GC_locker::needs_gc_address()); + __ cmplwi(CCR0, tmp_reg, 0); + __ beq(CCR0, cont); + + // Save down any values that are live in registers and call into the + // runtime to halt for a GC. + OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + save_or_restore_arguments(masm, stack_slots, total_in_args, + arg_save_area, map, in_regs, in_sig_bt); + + __ mr(R3_ARG1, R16_thread); + __ set_last_Java_frame(R1_SP, noreg); + + __ block_comment("block_for_jni_critical"); + address entry_point = CAST_FROM_FN_PTR(address, SharedRuntime::block_for_jni_critical); +#if defined(ABI_ELFv2) + __ call_c(entry_point, relocInfo::runtime_call_type); +#else + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, entry_point), relocInfo::runtime_call_type); +#endif + address start = __ pc() - __ offset(), + calls_return_pc = __ last_calls_return_pc(); + oop_maps->add_gc_map(calls_return_pc - start, map); + + __ reset_last_Java_frame(); + + // Reload all the register arguments. + save_or_restore_arguments(masm, stack_slots, total_in_args, + arg_save_area, NULL, in_regs, in_sig_bt); + + __ BIND(cont); + +#ifdef ASSERT + if (StressCriticalJNINatives) { + // Stress register saving. + OopMap* map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + save_or_restore_arguments(masm, stack_slots, total_in_args, + arg_save_area, map, in_regs, in_sig_bt); + // Destroy argument registers. + for (int i = 0; i < total_in_args; i++) { + if (in_regs[i].first()->is_Register()) { + const Register reg = in_regs[i].first()->as_Register(); + __ neg(reg, reg); + } else if (in_regs[i].first()->is_FloatRegister()) { + __ fneg(in_regs[i].first()->as_FloatRegister(), in_regs[i].first()->as_FloatRegister()); + } + } + + save_or_restore_arguments(masm, stack_slots, total_in_args, + arg_save_area, NULL, in_regs, in_sig_bt); + } +#endif +} + +static void move_ptr(MacroAssembler* masm, VMRegPair src, VMRegPair dst, Register r_caller_sp, Register r_temp) { + if (src.first()->is_stack()) { + if (dst.first()->is_stack()) { + // stack to stack + __ ld(r_temp, reg2offset(src.first()), r_caller_sp); + __ std(r_temp, reg2offset(dst.first()), R1_SP); + } else { + // stack to reg + __ ld(dst.first()->as_Register(), reg2offset(src.first()), r_caller_sp); + } + } else if (dst.first()->is_stack()) { + // reg to stack + __ std(src.first()->as_Register(), reg2offset(dst.first()), R1_SP); + } else { + if (dst.first() != src.first()) { + __ mr(dst.first()->as_Register(), src.first()->as_Register()); + } + } +} + +// Unpack an array argument into a pointer to the body and the length +// if the array is non-null, otherwise pass 0 for both. +static void unpack_array_argument(MacroAssembler* masm, VMRegPair reg, BasicType in_elem_type, + VMRegPair body_arg, VMRegPair length_arg, Register r_caller_sp, + Register tmp_reg, Register tmp2_reg) { + assert(!body_arg.first()->is_Register() || body_arg.first()->as_Register() != tmp_reg, + "possible collision"); + assert(!length_arg.first()->is_Register() || length_arg.first()->as_Register() != tmp_reg, + "possible collision"); + + // Pass the length, ptr pair. + Label set_out_args; + VMRegPair tmp, tmp2; + tmp.set_ptr(tmp_reg->as_VMReg()); + tmp2.set_ptr(tmp2_reg->as_VMReg()); + if (reg.first()->is_stack()) { + // Load the arg up from the stack. + move_ptr(masm, reg, tmp, r_caller_sp, /*unused*/ R0); + reg = tmp; + } + __ li(tmp2_reg, 0); // Pass zeros if Array=null. + if (tmp_reg != reg.first()->as_Register()) __ li(tmp_reg, 0); + __ cmpdi(CCR0, reg.first()->as_Register(), 0); + __ beq(CCR0, set_out_args); + __ lwa(tmp2_reg, arrayOopDesc::length_offset_in_bytes(), reg.first()->as_Register()); + __ addi(tmp_reg, reg.first()->as_Register(), arrayOopDesc::base_offset_in_bytes(in_elem_type)); + __ bind(set_out_args); + move_ptr(masm, tmp, body_arg, r_caller_sp, /*unused*/ R0); + move_ptr(masm, tmp2, length_arg, r_caller_sp, /*unused*/ R0); // Same as move32_64 on PPC64. +} + +static void verify_oop_args(MacroAssembler* masm, + methodHandle method, + const BasicType* sig_bt, + const VMRegPair* regs) { + Register temp_reg = R19_method; // not part of any compiled calling seq + if (VerifyOops) { + for (int i = 0; i < method->size_of_parameters(); i++) { + if (sig_bt[i] == T_OBJECT || + sig_bt[i] == T_ARRAY) { + VMReg r = regs[i].first(); + assert(r->is_valid(), "bad oop arg"); + if (r->is_stack()) { + __ ld(temp_reg, reg2offset(r), R1_SP); + __ verify_oop(temp_reg); + } else { + __ verify_oop(r->as_Register()); + } + } + } + } +} + +static void gen_special_dispatch(MacroAssembler* masm, + methodHandle method, + const BasicType* sig_bt, + const VMRegPair* regs) { + verify_oop_args(masm, method, sig_bt, regs); + vmIntrinsics::ID iid = method->intrinsic_id(); + + // Now write the args into the outgoing interpreter space + bool has_receiver = false; + Register receiver_reg = noreg; + int member_arg_pos = -1; + Register member_reg = noreg; + int ref_kind = MethodHandles::signature_polymorphic_intrinsic_ref_kind(iid); + if (ref_kind != 0) { + member_arg_pos = method->size_of_parameters() - 1; // trailing MemberName argument + member_reg = R19_method; // known to be free at this point + has_receiver = MethodHandles::ref_kind_has_receiver(ref_kind); + } else if (iid == vmIntrinsics::_invokeBasic) { + has_receiver = true; + } else { + fatal(err_msg_res("unexpected intrinsic id %d", iid)); + } + + if (member_reg != noreg) { + // Load the member_arg into register, if necessary. + SharedRuntime::check_member_name_argument_is_last_argument(method, sig_bt, regs); + VMReg r = regs[member_arg_pos].first(); + if (r->is_stack()) { + __ ld(member_reg, reg2offset(r), R1_SP); + } else { + // no data motion is needed + member_reg = r->as_Register(); + } + } + + if (has_receiver) { + // Make sure the receiver is loaded into a register. + assert(method->size_of_parameters() > 0, "oob"); + assert(sig_bt[0] == T_OBJECT, "receiver argument must be an object"); + VMReg r = regs[0].first(); + assert(r->is_valid(), "bad receiver arg"); + if (r->is_stack()) { + // Porting note: This assumes that compiled calling conventions always + // pass the receiver oop in a register. If this is not true on some + // platform, pick a temp and load the receiver from stack. + fatal("receiver always in a register"); + receiver_reg = R11_scratch1; // TODO (hs24): is R11_scratch1 really free at this point? + __ ld(receiver_reg, reg2offset(r), R1_SP); + } else { + // no data motion is needed + receiver_reg = r->as_Register(); + } + } + + // Figure out which address we are really jumping to: + MethodHandles::generate_method_handle_dispatch(masm, iid, + receiver_reg, member_reg, /*for_compiler_entry:*/ true); +} + +#endif // COMPILER2 + +// --------------------------------------------------------------------------- +// Generate a native wrapper for a given method. The method takes arguments +// in the Java compiled code convention, marshals them to the native +// convention (handlizes oops, etc), transitions to native, makes the call, +// returns to java state (possibly blocking), unhandlizes any result and +// returns. +// +// Critical native functions are a shorthand for the use of +// GetPrimtiveArrayCritical and disallow the use of any other JNI +// functions. The wrapper is expected to unpack the arguments before +// passing them to the callee and perform checks before and after the +// native call to ensure that they GC_locker +// lock_critical/unlock_critical semantics are followed. Some other +// parts of JNI setup are skipped like the tear down of the JNI handle +// block and the check for pending exceptions it's impossible for them +// to be thrown. +// +// They are roughly structured like this: +// if (GC_locker::needs_gc()) +// SharedRuntime::block_for_jni_critical(); +// tranistion to thread_in_native +// unpack arrray arguments and call native entry point +// check for safepoint in progress +// check if any thread suspend flags are set +// call into JVM and possible unlock the JNI critical +// if a GC was suppressed while in the critical native. +// transition back to thread_in_Java +// return to caller +// +nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, + methodHandle method, + int compile_id, + BasicType *in_sig_bt, + VMRegPair *in_regs, + BasicType ret_type) { +#ifdef COMPILER2 + if (method->is_method_handle_intrinsic()) { + vmIntrinsics::ID iid = method->intrinsic_id(); + intptr_t start = (intptr_t)__ pc(); + int vep_offset = ((intptr_t)__ pc()) - start; + gen_special_dispatch(masm, + method, + in_sig_bt, + in_regs); + int frame_complete = ((intptr_t)__ pc()) - start; // not complete, period + __ flush(); + int stack_slots = SharedRuntime::out_preserve_stack_slots(); // no out slots at all, actually + return nmethod::new_native_nmethod(method, + compile_id, + masm->code(), + vep_offset, + frame_complete, + stack_slots / VMRegImpl::slots_per_word, + in_ByteSize(-1), + in_ByteSize(-1), + (OopMapSet*)NULL); + } + + bool is_critical_native = true; + address native_func = method->critical_native_function(); + if (native_func == NULL) { + native_func = method->native_function(); + is_critical_native = false; + } + assert(native_func != NULL, "must have function"); + + // First, create signature for outgoing C call + // -------------------------------------------------------------------------- + + int total_in_args = method->size_of_parameters(); + // We have received a description of where all the java args are located + // on entry to the wrapper. We need to convert these args to where + // the jni function will expect them. To figure out where they go + // we convert the java signature to a C signature by inserting + // the hidden arguments as arg[0] and possibly arg[1] (static method) + // + // Additionally, on ppc64 we must convert integers to longs in the C + // signature. We do this in advance in order to have no trouble with + // indexes into the bt-arrays. + // So convert the signature and registers now, and adjust the total number + // of in-arguments accordingly. + int i2l_argcnt = convert_ints_to_longints_argcnt(total_in_args, in_sig_bt); // PPC64: pass ints as longs. + + // Calculate the total number of C arguments and create arrays for the + // signature and the outgoing registers. + // On ppc64, we have two arrays for the outgoing registers, because + // some floating-point arguments must be passed in registers _and_ + // in stack locations. + bool method_is_static = method->is_static(); + int total_c_args = i2l_argcnt; + + if (!is_critical_native) { + int n_hidden_args = method_is_static ? 2 : 1; + total_c_args += n_hidden_args; + } else { + // No JNIEnv*, no this*, but unpacked arrays (base+length). + for (int i = 0; i < total_in_args; i++) { + if (in_sig_bt[i] == T_ARRAY) { + total_c_args += 2; // PPC64: T_LONG, T_INT, T_ADDRESS (see convert_ints_to_longints and c_calling_convention) + } + } + } + + BasicType *out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_c_args); + VMRegPair *out_regs = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); + VMRegPair *out_regs2 = NEW_RESOURCE_ARRAY(VMRegPair, total_c_args); + BasicType* in_elem_bt = NULL; + + // Create the signature for the C call: + // 1) add the JNIEnv* + // 2) add the class if the method is static + // 3) copy the rest of the incoming signature (shifted by the number of + // hidden arguments). + + int argc = 0; + if (!is_critical_native) { + convert_ints_to_longints(i2l_argcnt, total_in_args, in_sig_bt, in_regs); // PPC64: pass ints as longs. + + out_sig_bt[argc++] = T_ADDRESS; + if (method->is_static()) { + out_sig_bt[argc++] = T_OBJECT; + } + + for (int i = 0; i < total_in_args ; i++ ) { + out_sig_bt[argc++] = in_sig_bt[i]; + } + } else { + Thread* THREAD = Thread::current(); + in_elem_bt = NEW_RESOURCE_ARRAY(BasicType, i2l_argcnt); + SignatureStream ss(method->signature()); + int o = 0; + for (int i = 0; i < total_in_args ; i++, o++) { + if (in_sig_bt[i] == T_ARRAY) { + // Arrays are passed as int, elem* pair + Symbol* atype = ss.as_symbol(CHECK_NULL); + const char* at = atype->as_C_string(); + if (strlen(at) == 2) { + assert(at[0] == '[', "must be"); + switch (at[1]) { + case 'B': in_elem_bt[o] = T_BYTE; break; + case 'C': in_elem_bt[o] = T_CHAR; break; + case 'D': in_elem_bt[o] = T_DOUBLE; break; + case 'F': in_elem_bt[o] = T_FLOAT; break; + case 'I': in_elem_bt[o] = T_INT; break; + case 'J': in_elem_bt[o] = T_LONG; break; + case 'S': in_elem_bt[o] = T_SHORT; break; + case 'Z': in_elem_bt[o] = T_BOOLEAN; break; + default: ShouldNotReachHere(); + } + } + } else { + in_elem_bt[o] = T_VOID; + switch(in_sig_bt[i]) { // PPC64: pass ints as longs. + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: in_elem_bt[++o] = T_VOID; break; + default: break; + } + } + if (in_sig_bt[i] != T_VOID) { + assert(in_sig_bt[i] == ss.type(), "must match"); + ss.next(); + } + } + assert(i2l_argcnt==o, "must match"); + + convert_ints_to_longints(i2l_argcnt, total_in_args, in_sig_bt, in_regs); // PPC64: pass ints as longs. + + for (int i = 0; i < total_in_args ; i++ ) { + if (in_sig_bt[i] == T_ARRAY) { + // Arrays are passed as int, elem* pair. + out_sig_bt[argc++] = T_LONG; // PPC64: pass ints as longs. + out_sig_bt[argc++] = T_INT; + out_sig_bt[argc++] = T_ADDRESS; + } else { + out_sig_bt[argc++] = in_sig_bt[i]; + } + } + } + + + // Compute the wrapper's frame size. + // -------------------------------------------------------------------------- + + // Now figure out where the args must be stored and how much stack space + // they require. + // + // Compute framesize for the wrapper. We need to handlize all oops in + // incoming registers. + // + // Calculate the total number of stack slots we will need: + // 1) abi requirements + // 2) outgoing arguments + // 3) space for inbound oop handle area + // 4) space for handlizing a klass if static method + // 5) space for a lock if synchronized method + // 6) workspace for saving return values, int <-> float reg moves, etc. + // 7) alignment + // + // Layout of the native wrapper frame: + // (stack grows upwards, memory grows downwards) + // + // NW [ABI_REG_ARGS] <-- 1) R1_SP + // [outgoing arguments] <-- 2) R1_SP + out_arg_slot_offset + // [oopHandle area] <-- 3) R1_SP + oop_handle_offset (save area for critical natives) + // klass <-- 4) R1_SP + klass_offset + // lock <-- 5) R1_SP + lock_offset + // [workspace] <-- 6) R1_SP + workspace_offset + // [alignment] (optional) <-- 7) + // caller [JIT_TOP_ABI_48] <-- r_callers_sp + // + // - *_slot_offset Indicates offset from SP in number of stack slots. + // - *_offset Indicates offset from SP in bytes. + + int stack_slots = c_calling_convention(out_sig_bt, out_regs, out_regs2, total_c_args) // 1+2) + + SharedRuntime::out_preserve_stack_slots(); // See c_calling_convention. + + // Now the space for the inbound oop handle area. + int total_save_slots = num_java_iarg_registers * VMRegImpl::slots_per_word; + if (is_critical_native) { + // Critical natives may have to call out so they need a save area + // for register arguments. + int double_slots = 0; + int single_slots = 0; + for (int i = 0; i < total_in_args; i++) { + if (in_regs[i].first()->is_Register()) { + const Register reg = in_regs[i].first()->as_Register(); + switch (in_sig_bt[i]) { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_CHAR: + case T_INT: /*single_slots++;*/ break; // PPC64: pass ints as longs. + case T_ARRAY: + case T_LONG: double_slots++; break; + default: ShouldNotReachHere(); + } + } else if (in_regs[i].first()->is_FloatRegister()) { + switch (in_sig_bt[i]) { + case T_FLOAT: single_slots++; break; + case T_DOUBLE: double_slots++; break; + default: ShouldNotReachHere(); + } + } + } + total_save_slots = double_slots * 2 + round_to(single_slots, 2); // round to even + } + + int oop_handle_slot_offset = stack_slots; + stack_slots += total_save_slots; // 3) + + int klass_slot_offset = 0; + int klass_offset = -1; + if (method_is_static && !is_critical_native) { // 4) + klass_slot_offset = stack_slots; + klass_offset = klass_slot_offset * VMRegImpl::stack_slot_size; + stack_slots += VMRegImpl::slots_per_word; + } + + int lock_slot_offset = 0; + int lock_offset = -1; + if (method->is_synchronized()) { // 5) + lock_slot_offset = stack_slots; + lock_offset = lock_slot_offset * VMRegImpl::stack_slot_size; + stack_slots += VMRegImpl::slots_per_word; + } + + int workspace_slot_offset = stack_slots; // 6) + stack_slots += 2; + + // Now compute actual number of stack words we need. + // Rounding to make stack properly aligned. + stack_slots = round_to(stack_slots, // 7) + frame::alignment_in_bytes / VMRegImpl::stack_slot_size); + int frame_size_in_bytes = stack_slots * VMRegImpl::stack_slot_size; + + + // Now we can start generating code. + // -------------------------------------------------------------------------- + + intptr_t start_pc = (intptr_t)__ pc(); + intptr_t vep_start_pc; + intptr_t frame_done_pc; + intptr_t oopmap_pc; + + Label ic_miss; + Label handle_pending_exception; + + Register r_callers_sp = R21; + Register r_temp_1 = R22; + Register r_temp_2 = R23; + Register r_temp_3 = R24; + Register r_temp_4 = R25; + Register r_temp_5 = R26; + Register r_temp_6 = R27; + Register r_return_pc = R28; + + Register r_carg1_jnienv = noreg; + Register r_carg2_classorobject = noreg; + if (!is_critical_native) { + r_carg1_jnienv = out_regs[0].first()->as_Register(); + r_carg2_classorobject = out_regs[1].first()->as_Register(); + } + + + // Generate the Unverified Entry Point (UEP). + // -------------------------------------------------------------------------- + assert(start_pc == (intptr_t)__ pc(), "uep must be at start"); + + // Check ic: object class == cached class? + if (!method_is_static) { + Register ic = as_Register(Matcher::inline_cache_reg_encode()); + Register receiver_klass = r_temp_1; + + __ cmpdi(CCR0, R3_ARG1, 0); + __ beq(CCR0, ic_miss); + __ verify_oop(R3_ARG1); + __ load_heap_oop_not_null(receiver_klass, oopDesc::klass_offset_in_bytes(), R3_ARG1); + + __ cmpd(CCR0, receiver_klass, ic); + __ bne(CCR0, ic_miss); + } + + + // Generate the Verified Entry Point (VEP). + // -------------------------------------------------------------------------- + vep_start_pc = (intptr_t)__ pc(); + + __ save_LR_CR(r_temp_1); + __ generate_stack_overflow_check(frame_size_in_bytes); // Check before creating frame. + __ mr(r_callers_sp, R1_SP); // Remember frame pointer. + __ push_frame(frame_size_in_bytes, r_temp_1); // Push the c2n adapter's frame. + frame_done_pc = (intptr_t)__ pc(); + + // Native nmethod wrappers never take possesion of the oop arguments. + // So the caller will gc the arguments. + // The only thing we need an oopMap for is if the call is static. + // + // An OopMap for lock (and class if static), and one for the VM call itself. + OopMapSet *oop_maps = new OopMapSet(); + OopMap *oop_map = new OopMap(stack_slots * 2, 0 /* arg_slots*/); + + if (is_critical_native) { + check_needs_gc_for_critical_native(masm, stack_slots, total_in_args, oop_handle_slot_offset, oop_maps, in_regs, in_sig_bt, r_temp_1); + } + + // Move arguments from register/stack to register/stack. + // -------------------------------------------------------------------------- + // + // We immediately shuffle the arguments so that for any vm call we have + // to make from here on out (sync slow path, jvmti, etc.) we will have + // captured the oops from our caller and have a valid oopMap for them. + // + // Natives require 1 or 2 extra arguments over the normal ones: the JNIEnv* + // (derived from JavaThread* which is in R16_thread) and, if static, + // the class mirror instead of a receiver. This pretty much guarantees that + // register layout will not match. We ignore these extra arguments during + // the shuffle. The shuffle is described by the two calling convention + // vectors we have in our possession. We simply walk the java vector to + // get the source locations and the c vector to get the destinations. + + // Record sp-based slot for receiver on stack for non-static methods. + int receiver_offset = -1; + + // We move the arguments backward because the floating point registers + // destination will always be to a register with a greater or equal + // register number or the stack. + // in is the index of the incoming Java arguments + // out is the index of the outgoing C arguments + +#ifdef ASSERT + bool reg_destroyed[RegisterImpl::number_of_registers]; + bool freg_destroyed[FloatRegisterImpl::number_of_registers]; + for (int r = 0 ; r < RegisterImpl::number_of_registers ; r++) { + reg_destroyed[r] = false; + } + for (int f = 0 ; f < FloatRegisterImpl::number_of_registers ; f++) { + freg_destroyed[f] = false; + } +#endif // ASSERT + + for (int in = total_in_args - 1, out = total_c_args - 1; in >= 0 ; in--, out--) { + +#ifdef ASSERT + if (in_regs[in].first()->is_Register()) { + assert(!reg_destroyed[in_regs[in].first()->as_Register()->encoding()], "ack!"); + } else if (in_regs[in].first()->is_FloatRegister()) { + assert(!freg_destroyed[in_regs[in].first()->as_FloatRegister()->encoding()], "ack!"); + } + if (out_regs[out].first()->is_Register()) { + reg_destroyed[out_regs[out].first()->as_Register()->encoding()] = true; + } else if (out_regs[out].first()->is_FloatRegister()) { + freg_destroyed[out_regs[out].first()->as_FloatRegister()->encoding()] = true; + } + if (out_regs2[out].first()->is_Register()) { + reg_destroyed[out_regs2[out].first()->as_Register()->encoding()] = true; + } else if (out_regs2[out].first()->is_FloatRegister()) { + freg_destroyed[out_regs2[out].first()->as_FloatRegister()->encoding()] = true; + } +#endif // ASSERT + + switch (in_sig_bt[in]) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + guarantee(in > 0 && in_sig_bt[in-1] == T_LONG, + "expecting type (T_LONG,bt) for bt in {T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}"); + break; + case T_LONG: + if (in_sig_bt[in+1] == T_VOID) { + long_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1); + } else { + guarantee(in_sig_bt[in+1] == T_BOOLEAN || in_sig_bt[in+1] == T_CHAR || + in_sig_bt[in+1] == T_BYTE || in_sig_bt[in+1] == T_SHORT || + in_sig_bt[in+1] == T_INT, + "expecting type (T_LONG,bt) for bt in {T_BOOLEAN, T_CHAR, T_BYTE, T_SHORT, T_INT}"); + int_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1); + } + break; + case T_ARRAY: + if (is_critical_native) { + int body_arg = out; + out -= 2; // Point to length arg. PPC64: pass ints as longs. + unpack_array_argument(masm, in_regs[in], in_elem_bt[in], out_regs[body_arg], out_regs[out], + r_callers_sp, r_temp_1, r_temp_2); + break; + } + case T_OBJECT: + assert(!is_critical_native, "no oop arguments"); + object_move(masm, stack_slots, + oop_map, oop_handle_slot_offset, + ((in == 0) && (!method_is_static)), &receiver_offset, + in_regs[in], out_regs[out], + r_callers_sp, r_temp_1, r_temp_2); + break; + case T_VOID: + break; + case T_FLOAT: + float_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1); + if (out_regs2[out].first()->is_valid()) { + float_move(masm, in_regs[in], out_regs2[out], r_callers_sp, r_temp_1); + } + break; + case T_DOUBLE: + double_move(masm, in_regs[in], out_regs[out], r_callers_sp, r_temp_1); + if (out_regs2[out].first()->is_valid()) { + double_move(masm, in_regs[in], out_regs2[out], r_callers_sp, r_temp_1); + } + break; + case T_ADDRESS: + fatal("found type (T_ADDRESS) in java args"); + break; + default: + ShouldNotReachHere(); + break; + } + } + + // Pre-load a static method's oop into ARG2. + // Used both by locking code and the normal JNI call code. + if (method_is_static && !is_critical_native) { + __ set_oop_constant(JNIHandles::make_local(Klass::cast(method->method_holder())->java_mirror()), + r_carg2_classorobject); + + // Now handlize the static class mirror in carg2. It's known not-null. + __ std(r_carg2_classorobject, klass_offset, R1_SP); + oop_map->set_oop(VMRegImpl::stack2reg(klass_slot_offset)); + __ addi(r_carg2_classorobject, R1_SP, klass_offset); + } + + // Get JNIEnv* which is first argument to native. + if (!is_critical_native) { + __ addi(r_carg1_jnienv, R16_thread, in_bytes(JavaThread::jni_environment_offset())); + } + + // NOTE: + // + // We have all of the arguments setup at this point. + // We MUST NOT touch any outgoing regs from this point on. + // So if we must call out we must push a new frame. + + // Get current pc for oopmap, and load it patchable relative to global toc. + oopmap_pc = (intptr_t) __ pc(); + __ calculate_address_from_global_toc(r_return_pc, (address)oopmap_pc, true, true, true, true); + + // We use the same pc/oopMap repeatedly when we call out. + oop_maps->add_gc_map(oopmap_pc - start_pc, oop_map); + + // r_return_pc now has the pc loaded that we will use when we finally call + // to native. + + // Make sure that thread is non-volatile; it crosses a bunch of VM calls below. + assert(R16_thread->is_nonvolatile(), "thread must be in non-volatile register"); + + +# if 0 + // DTrace method entry +# endif + + // Lock a synchronized method. + // -------------------------------------------------------------------------- + + if (method->is_synchronized()) { + assert(!is_critical_native, "unhandled"); + ConditionRegister r_flag = CCR1; + Register r_oop = r_temp_4; + const Register r_box = r_temp_5; + Label done, locked; + + // Load the oop for the object or class. r_carg2_classorobject contains + // either the handlized oop from the incoming arguments or the handlized + // class mirror (if the method is static). + __ ld(r_oop, 0, r_carg2_classorobject); + + // Get the lock box slot's address. + __ addi(r_box, R1_SP, lock_offset); + +# ifdef ASSERT + if (UseBiasedLocking) { + // Making the box point to itself will make it clear it went unused + // but also be obviously invalid. + __ std(r_box, 0, r_box); + } +# endif // ASSERT + + // Try fastpath for locking. + // fast_lock kills r_temp_1, r_temp_2, r_temp_3. + __ compiler_fast_lock_object(r_flag, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); + __ beq(r_flag, locked); + + // None of the above fast optimizations worked so we have to get into the + // slow case of monitor enter. Inline a special case of call_VM that + // disallows any pending_exception. + + // Save argument registers and leave room for C-compatible ABI_REG_ARGS. + int frame_size = frame::abi_reg_args_size + + round_to(total_c_args * wordSize, frame::alignment_in_bytes); + __ mr(R11_scratch1, R1_SP); + RegisterSaver::push_frame_and_save_argument_registers(masm, R12_scratch2, frame_size, total_c_args, out_regs, out_regs2); + + // Do the call. + __ set_last_Java_frame(R11_scratch1, r_return_pc); + assert(r_return_pc->is_nonvolatile(), "expecting return pc to be in non-volatile register"); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_locking_C), r_oop, r_box, R16_thread); + __ reset_last_Java_frame(); + + RegisterSaver::restore_argument_registers_and_pop_frame(masm, frame_size, total_c_args, out_regs, out_regs2); + + __ asm_assert_mem8_is_zero(thread_(pending_exception), + "no pending exception allowed on exit from SharedRuntime::complete_monitor_locking_C", 0); + + __ bind(locked); + } + + + // Publish thread state + // -------------------------------------------------------------------------- + + // Use that pc we placed in r_return_pc a while back as the current frame anchor. + __ set_last_Java_frame(R1_SP, r_return_pc); + + // Transition from _thread_in_Java to _thread_in_native. + __ li(R0, _thread_in_native); + __ release(); + // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + if (UseMembar) { + __ fence(); + } + + + // The JNI call + // -------------------------------------------------------------------------- +#if defined(ABI_ELFv2) + __ call_c(native_func, relocInfo::runtime_call_type); +#else + FunctionDescriptor* fd_native_method = (FunctionDescriptor*) native_func; + __ call_c(fd_native_method, relocInfo::runtime_call_type); +#endif + + + // Now, we are back from the native code. + + + // Unpack the native result. + // -------------------------------------------------------------------------- + + // For int-types, we do any needed sign-extension required. + // Care must be taken that the return values (R3_RET and F1_RET) + // will survive any VM calls for blocking or unlocking. + // An OOP result (handle) is done specially in the slow-path code. + + switch (ret_type) { + case T_VOID: break; // Nothing to do! + case T_FLOAT: break; // Got it where we want it (unless slow-path). + case T_DOUBLE: break; // Got it where we want it (unless slow-path). + case T_LONG: break; // Got it where we want it (unless slow-path). + case T_OBJECT: break; // Really a handle. + // Cannot de-handlize until after reclaiming jvm_lock. + case T_ARRAY: break; + + case T_BOOLEAN: { // 0 -> false(0); !0 -> true(1) + Label skip_modify; + __ cmpwi(CCR0, R3_RET, 0); + __ beq(CCR0, skip_modify); + __ li(R3_RET, 1); + __ bind(skip_modify); + break; + } + case T_BYTE: { // sign extension + __ extsb(R3_RET, R3_RET); + break; + } + case T_CHAR: { // unsigned result + __ andi(R3_RET, R3_RET, 0xffff); + break; + } + case T_SHORT: { // sign extension + __ extsh(R3_RET, R3_RET); + break; + } + case T_INT: // nothing to do + break; + default: + ShouldNotReachHere(); + break; + } + + + // Publish thread state + // -------------------------------------------------------------------------- + + // Switch thread to "native transition" state before reading the + // synchronization state. This additional state is necessary because reading + // and testing the synchronization state is not atomic w.r.t. GC, as this + // scenario demonstrates: + // - Java thread A, in _thread_in_native state, loads _not_synchronized + // and is preempted. + // - VM thread changes sync state to synchronizing and suspends threads + // for GC. + // - Thread A is resumed to finish this native method, but doesn't block + // here since it didn't see any synchronization in progress, and escapes. + + // Transition from _thread_in_native to _thread_in_native_trans. + __ li(R0, _thread_in_native_trans); + __ release(); + // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + + + // Must we block? + // -------------------------------------------------------------------------- + + // Block, if necessary, before resuming in _thread_in_Java state. + // In order for GC to work, don't clear the last_Java_sp until after blocking. + Label after_transition; + { + Label no_block, sync; + + if (os::is_MP()) { + if (UseMembar) { + // Force this write out before the read below. + __ fence(); + } else { + // Write serialization page so VM thread can do a pseudo remote membar. + // We use the current thread pointer to calculate a thread specific + // offset to write to within the page. This minimizes bus traffic + // due to cache line collision. + __ serialize_memory(R16_thread, r_temp_4, r_temp_5); + } + } + + Register sync_state_addr = r_temp_4; + Register sync_state = r_temp_5; + Register suspend_flags = r_temp_6; + + __ load_const(sync_state_addr, SafepointSynchronize::address_of_state(), /*temp*/ sync_state); + + // TODO: PPC port assert(4 == SafepointSynchronize::sz_state(), "unexpected field size"); + __ lwz(sync_state, 0, sync_state_addr); + + // TODO: PPC port assert(4 == Thread::sz_suspend_flags(), "unexpected field size"); + __ lwz(suspend_flags, thread_(suspend_flags)); + + __ acquire(); + + Label do_safepoint; + // No synchronization in progress nor yet synchronized. + __ cmpwi(CCR0, sync_state, SafepointSynchronize::_not_synchronized); + // Not suspended. + __ cmpwi(CCR1, suspend_flags, 0); + + __ bne(CCR0, sync); + __ beq(CCR1, no_block); + + // Block. Save any potential method result value before the operation and + // use a leaf call to leave the last_Java_frame setup undisturbed. Doing this + // lets us share the oopMap we used when we went native rather than create + // a distinct one for this pc. + __ bind(sync); + + address entry_point = is_critical_native + ? CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans_and_transition) + : CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans); + save_native_result(masm, ret_type, workspace_slot_offset); + __ call_VM_leaf(entry_point, R16_thread); + restore_native_result(masm, ret_type, workspace_slot_offset); + + if (is_critical_native) { + __ b(after_transition); // No thread state transition here. + } + __ bind(no_block); + } + + // Publish thread state. + // -------------------------------------------------------------------------- + + // Thread state is thread_in_native_trans. Any safepoint blocking has + // already happened so we can now change state to _thread_in_Java. + + // Transition from _thread_in_native_trans to _thread_in_Java. + __ li(R0, _thread_in_Java); + __ release(); + // TODO: PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + if (UseMembar) { + __ fence(); + } + __ bind(after_transition); + + // Reguard any pages if necessary. + // -------------------------------------------------------------------------- + + Label no_reguard; + __ lwz(r_temp_1, thread_(stack_guard_state)); + __ cmpwi(CCR0, r_temp_1, JavaThread::stack_guard_yellow_disabled); + __ bne(CCR0, no_reguard); + + save_native_result(masm, ret_type, workspace_slot_offset); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); + restore_native_result(masm, ret_type, workspace_slot_offset); + + __ bind(no_reguard); + + + // Unlock + // -------------------------------------------------------------------------- + + if (method->is_synchronized()) { + + ConditionRegister r_flag = CCR1; + const Register r_oop = r_temp_4; + const Register r_box = r_temp_5; + const Register r_exception = r_temp_6; + Label done; + + // Get oop and address of lock object box. + if (method_is_static) { + assert(klass_offset != -1, ""); + __ ld(r_oop, klass_offset, R1_SP); + } else { + assert(receiver_offset != -1, ""); + __ ld(r_oop, receiver_offset, R1_SP); + } + __ addi(r_box, R1_SP, lock_offset); + + // Try fastpath for unlocking. + __ compiler_fast_unlock_object(r_flag, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3); + __ beq(r_flag, done); + + // Save and restore any potential method result value around the unlocking operation. + save_native_result(masm, ret_type, workspace_slot_offset); + + // Must save pending exception around the slow-path VM call. Since it's a + // leaf call, the pending exception (if any) can be kept in a register. + __ ld(r_exception, thread_(pending_exception)); + assert(r_exception->is_nonvolatile(), "exception register must be non-volatile"); + __ li(R0, 0); + __ std(R0, thread_(pending_exception)); + + // Slow case of monitor enter. + // Inline a special case of call_VM that disallows any pending_exception. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), r_oop, r_box); + + __ asm_assert_mem8_is_zero(thread_(pending_exception), + "no pending exception allowed on exit from SharedRuntime::complete_monitor_unlocking_C", 0); + + restore_native_result(masm, ret_type, workspace_slot_offset); + + // Check_forward_pending_exception jump to forward_exception if any pending + // exception is set. The forward_exception routine expects to see the + // exception in pending_exception and not in a register. Kind of clumsy, + // since all folks who branch to forward_exception must have tested + // pending_exception first and hence have it in a register already. + __ std(r_exception, thread_(pending_exception)); + + __ bind(done); + } + +# if 0 + // DTrace method exit +# endif + + // Clear "last Java frame" SP and PC. + // -------------------------------------------------------------------------- + + __ reset_last_Java_frame(); + + // Unpack oop result. + // -------------------------------------------------------------------------- + + if (ret_type == T_OBJECT || ret_type == T_ARRAY) { + Label skip_unboxing; + __ cmpdi(CCR0, R3_RET, 0); + __ beq(CCR0, skip_unboxing); + __ ld(R3_RET, 0, R3_RET); + __ bind(skip_unboxing); + __ verify_oop(R3_RET); + } + + + // Reset handle block. + // -------------------------------------------------------------------------- + if (!is_critical_native) { + __ ld(r_temp_1, thread_(active_handles)); + // TODO: PPC port assert(4 == JNIHandleBlock::top_size_in_bytes(), "unexpected field size"); + __ li(r_temp_2, 0); + __ stw(r_temp_2, JNIHandleBlock::top_offset_in_bytes(), r_temp_1); + + + // Check for pending exceptions. + // -------------------------------------------------------------------------- + __ ld(r_temp_2, thread_(pending_exception)); + __ cmpdi(CCR0, r_temp_2, 0); + __ bne(CCR0, handle_pending_exception); + } + + // Return + // -------------------------------------------------------------------------- + + __ pop_frame(); + __ restore_LR_CR(R11); + __ blr(); + + + // Handler for pending exceptions (out-of-line). + // -------------------------------------------------------------------------- + + // Since this is a native call, we know the proper exception handler + // is the empty function. We just pop this frame and then jump to + // forward_exception_entry. + if (!is_critical_native) { + __ align(InteriorEntryAlignment); + __ bind(handle_pending_exception); + + __ pop_frame(); + __ restore_LR_CR(R11); + __ b64_patchable((address)StubRoutines::forward_exception_entry(), + relocInfo::runtime_call_type); + } + + // Handler for a cache miss (out-of-line). + // -------------------------------------------------------------------------- + + if (!method_is_static) { + __ align(InteriorEntryAlignment); + __ bind(ic_miss); + + __ b64_patchable((address)SharedRuntime::get_ic_miss_stub(), + relocInfo::runtime_call_type); + } + + // Done. + // -------------------------------------------------------------------------- + + __ flush(); + + nmethod *nm = nmethod::new_native_nmethod(method, + compile_id, + masm->code(), + vep_start_pc-start_pc, + frame_done_pc-start_pc, + stack_slots / VMRegImpl::slots_per_word, + (method_is_static ? in_ByteSize(klass_offset) : in_ByteSize(receiver_offset)), + in_ByteSize(lock_offset), + oop_maps); + + if (is_critical_native) { + nm->set_lazy_critical_native(true); + } + + return nm; +#else + ShouldNotReachHere(); + return NULL; +#endif // COMPILER2 +} + +// This function returns the adjust size (in number of words) to a c2i adapter +// activation for use during deoptimization. +int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) { + return round_to((callee_locals - callee_parameters) * Interpreter::stackElementWords, frame::alignment_in_bytes); +} + +uint SharedRuntime::out_preserve_stack_slots() { +#ifdef COMPILER2 + return frame::jit_out_preserve_size / VMRegImpl::stack_slot_size; +#else + return 0; +#endif +} + +#ifdef COMPILER2 +// Frame generation for deopt and uncommon trap blobs. +static void push_skeleton_frame(MacroAssembler* masm, bool deopt, + /* Read */ + Register unroll_block_reg, + /* Update */ + Register frame_sizes_reg, + Register number_of_frames_reg, + Register pcs_reg, + /* Invalidate */ + Register frame_size_reg, + Register pc_reg) { + + __ ld(pc_reg, 0, pcs_reg); + __ ld(frame_size_reg, 0, frame_sizes_reg); + __ std(pc_reg, _abi(lr), R1_SP); + __ push_frame(frame_size_reg, R0/*tmp*/); +#ifdef CC_INTERP + __ std(R1_SP, _parent_ijava_frame_abi(initial_caller_sp), R1_SP); +#else +#ifdef ASSERT + __ load_const_optimized(pc_reg, 0x5afe); + __ std(pc_reg, _ijava_state_neg(ijava_reserved), R1_SP); +#endif + __ std(R1_SP, _ijava_state_neg(sender_sp), R1_SP); +#endif // CC_INTERP + __ addi(number_of_frames_reg, number_of_frames_reg, -1); + __ addi(frame_sizes_reg, frame_sizes_reg, wordSize); + __ addi(pcs_reg, pcs_reg, wordSize); +} + +// Loop through the UnrollBlock info and create new frames. +static void push_skeleton_frames(MacroAssembler* masm, bool deopt, + /* read */ + Register unroll_block_reg, + /* invalidate */ + Register frame_sizes_reg, + Register number_of_frames_reg, + Register pcs_reg, + Register frame_size_reg, + Register pc_reg) { + Label loop; + + // _number_of_frames is of type int (deoptimization.hpp) + __ lwa(number_of_frames_reg, + Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes(), + unroll_block_reg); + __ ld(pcs_reg, + Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes(), + unroll_block_reg); + __ ld(frame_sizes_reg, + Deoptimization::UnrollBlock::frame_sizes_offset_in_bytes(), + unroll_block_reg); + + // stack: (caller_of_deoptee, ...). + + // At this point we either have an interpreter frame or a compiled + // frame on top of stack. If it is a compiled frame we push a new c2i + // adapter here + + // Memorize top-frame stack-pointer. + __ mr(frame_size_reg/*old_sp*/, R1_SP); + + // Resize interpreter top frame OR C2I adapter. + + // At this moment, the top frame (which is the caller of the deoptee) is + // an interpreter frame or a newly pushed C2I adapter or an entry frame. + // The top frame has a TOP_IJAVA_FRAME_ABI and the frame contains the + // outgoing arguments. + // + // In order to push the interpreter frame for the deoptee, we need to + // resize the top frame such that we are able to place the deoptee's + // locals in the frame. + // Additionally, we have to turn the top frame's TOP_IJAVA_FRAME_ABI + // into a valid PARENT_IJAVA_FRAME_ABI. + + __ lwa(R11_scratch1, + Deoptimization::UnrollBlock::caller_adjustment_offset_in_bytes(), + unroll_block_reg); + __ neg(R11_scratch1, R11_scratch1); + + // R11_scratch1 contains size of locals for frame resizing. + // R12_scratch2 contains top frame's lr. + + // Resize frame by complete frame size prevents TOC from being + // overwritten by locals. A more stack space saving way would be + // to copy the TOC to its location in the new abi. + __ addi(R11_scratch1, R11_scratch1, - frame::parent_ijava_frame_abi_size); + + // now, resize the frame + __ resize_frame(R11_scratch1, pc_reg/*tmp*/); + + // In the case where we have resized a c2i frame above, the optional + // alignment below the locals has size 32 (why?). + __ std(R12_scratch2, _abi(lr), R1_SP); + + // Initialize initial_caller_sp. +#ifdef CC_INTERP + __ std(frame_size_reg/*old_sp*/, _parent_ijava_frame_abi(initial_caller_sp), R1_SP); +#else +#ifdef ASSERT + __ load_const_optimized(pc_reg, 0x5afe); + __ std(pc_reg, _ijava_state_neg(ijava_reserved), R1_SP); +#endif + __ std(frame_size_reg, _ijava_state_neg(sender_sp), R1_SP); +#endif // CC_INTERP + +#ifdef ASSERT + // Make sure that there is at least one entry in the array. + __ cmpdi(CCR0, number_of_frames_reg, 0); + __ asm_assert_ne("array_size must be > 0", 0x205); +#endif + + // Now push the new interpreter frames. + // + __ bind(loop); + // Allocate a new frame, fill in the pc. + push_skeleton_frame(masm, deopt, + unroll_block_reg, + frame_sizes_reg, + number_of_frames_reg, + pcs_reg, + frame_size_reg, + pc_reg); + __ cmpdi(CCR0, number_of_frames_reg, 0); + __ bne(CCR0, loop); + + // Get the return address pointing into the frame manager. + __ ld(R0, 0, pcs_reg); + // Store it in the top interpreter frame. + __ std(R0, _abi(lr), R1_SP); + // Initialize frame_manager_lr of interpreter top frame. +#ifdef CC_INTERP + __ std(R0, _top_ijava_frame_abi(frame_manager_lr), R1_SP); +#endif +} +#endif + +void SharedRuntime::generate_deopt_blob() { + // Allocate space for the code + ResourceMark rm; + // Setup code generation tools + CodeBuffer buffer("deopt_blob", 2048, 1024); + InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); + Label exec_mode_initialized; + int frame_size_in_words; + OopMap* map = NULL; + OopMapSet *oop_maps = new OopMapSet(); + + // size of ABI112 plus spill slots for R3_RET and F1_RET. + const int frame_size_in_bytes = frame::abi_reg_args_spill_size; + const int frame_size_in_slots = frame_size_in_bytes / sizeof(jint); + int first_frame_size_in_bytes = 0; // frame size of "unpack frame" for call to fetch_unroll_info. + + const Register exec_mode_reg = R21_tmp1; + + const address start = __ pc(); + +#ifdef COMPILER2 + // -------------------------------------------------------------------------- + // Prolog for non exception case! + + // We have been called from the deopt handler of the deoptee. + // + // deoptee: + // ... + // call X + // ... + // deopt_handler: call_deopt_stub + // cur. return pc --> ... + // + // So currently SR_LR points behind the call in the deopt handler. + // We adjust it such that it points to the start of the deopt handler. + // The return_pc has been stored in the frame of the deoptee and + // will replace the address of the deopt_handler in the call + // to Deoptimization::fetch_unroll_info below. + // We can't grab a free register here, because all registers may + // contain live values, so let the RegisterSaver do the adjustment + // of the return pc. + const int return_pc_adjustment_no_exception = -size_deopt_handler(); + + // Push the "unpack frame" + // Save everything in sight. + map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, + &first_frame_size_in_bytes, + /*generate_oop_map=*/ true, + return_pc_adjustment_no_exception, + RegisterSaver::return_pc_is_lr); + assert(map != NULL, "OopMap must have been created"); + + __ li(exec_mode_reg, Deoptimization::Unpack_deopt); + // Save exec mode for unpack_frames. + __ b(exec_mode_initialized); + + // -------------------------------------------------------------------------- + // Prolog for exception case + + // An exception is pending. + // We have been called with a return (interpreter) or a jump (exception blob). + // + // - R3_ARG1: exception oop + // - R4_ARG2: exception pc + + int exception_offset = __ pc() - start; + + BLOCK_COMMENT("Prolog for exception case"); + + // The RegisterSaves doesn't need to adjust the return pc for this situation. + const int return_pc_adjustment_exception = 0; + + // Push the "unpack frame". + // Save everything in sight. + assert(R4 == R4_ARG2, "exception pc must be in r4"); + RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, + &first_frame_size_in_bytes, + /*generate_oop_map=*/ false, + return_pc_adjustment_exception, + RegisterSaver::return_pc_is_r4); + + // Deopt during an exception. Save exec mode for unpack_frames. + __ li(exec_mode_reg, Deoptimization::Unpack_exception); + + // Store exception oop and pc in thread (location known to GC). + // This is needed since the call to "fetch_unroll_info()" may safepoint. + __ std(R3_ARG1, in_bytes(JavaThread::exception_oop_offset()), R16_thread); + __ std(R4_ARG2, in_bytes(JavaThread::exception_pc_offset()), R16_thread); + + // fall through + + // -------------------------------------------------------------------------- + __ BIND(exec_mode_initialized); + + { + const Register unroll_block_reg = R22_tmp2; + + // We need to set `last_Java_frame' because `fetch_unroll_info' will + // call `last_Java_frame()'. The value of the pc in the frame is not + // particularly important. It just needs to identify this blob. + __ set_last_Java_frame(R1_SP, noreg); + + // With EscapeAnalysis turned on, this call may safepoint! + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::fetch_unroll_info), R16_thread); + address calls_return_pc = __ last_calls_return_pc(); + // Set an oopmap for the call site that describes all our saved registers. + oop_maps->add_gc_map(calls_return_pc - start, map); + + __ reset_last_Java_frame(); + // Save the return value. + __ mr(unroll_block_reg, R3_RET); + + // Restore only the result registers that have been saved + // by save_volatile_registers(...). + RegisterSaver::restore_result_registers(masm, first_frame_size_in_bytes); + + // In excp_deopt_mode, restore and clear exception oop which we + // stored in the thread during exception entry above. The exception + // oop will be the return value of this stub. + Label skip_restore_excp; + __ cmpdi(CCR0, exec_mode_reg, Deoptimization::Unpack_exception); + __ bne(CCR0, skip_restore_excp); + __ ld(R3_RET, in_bytes(JavaThread::exception_oop_offset()), R16_thread); + __ ld(R4_ARG2, in_bytes(JavaThread::exception_pc_offset()), R16_thread); + __ li(R0, 0); + __ std(R0, in_bytes(JavaThread::exception_pc_offset()), R16_thread); + __ std(R0, in_bytes(JavaThread::exception_oop_offset()), R16_thread); + __ BIND(skip_restore_excp); + + // reload narrro_oop_base + if (UseCompressedOops && Universe::narrow_oop_base() != 0) { + __ load_const_optimized(R30, Universe::narrow_oop_base()); + } + + __ pop_frame(); + + // stack: (deoptee, optional i2c, caller of deoptee, ...). + + // pop the deoptee's frame + __ pop_frame(); + + // stack: (caller_of_deoptee, ...). + + // Loop through the `UnrollBlock' info and create interpreter frames. + push_skeleton_frames(masm, true/*deopt*/, + unroll_block_reg, + R23_tmp3, + R24_tmp4, + R25_tmp5, + R26_tmp6, + R27_tmp7); + + // stack: (skeletal interpreter frame, ..., optional skeletal + // interpreter frame, optional c2i, caller of deoptee, ...). + } + + // push an `unpack_frame' taking care of float / int return values. + __ push_frame(frame_size_in_bytes, R0/*tmp*/); + + // stack: (unpack frame, skeletal interpreter frame, ..., optional + // skeletal interpreter frame, optional c2i, caller of deoptee, + // ...). + + // Spill live volatile registers since we'll do a call. + __ std( R3_RET, _abi_reg_args_spill(spill_ret), R1_SP); + __ stfd(F1_RET, _abi_reg_args_spill(spill_fret), R1_SP); + + // Let the unpacker layout information in the skeletal frames just + // allocated. + __ get_PC_trash_LR(R3_RET); + __ set_last_Java_frame(/*sp*/R1_SP, /*pc*/R3_RET); + // This is a call to a LEAF method, so no oop map is required. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), + R16_thread/*thread*/, exec_mode_reg/*exec_mode*/); + __ reset_last_Java_frame(); + + // Restore the volatiles saved above. + __ ld( R3_RET, _abi_reg_args_spill(spill_ret), R1_SP); + __ lfd(F1_RET, _abi_reg_args_spill(spill_fret), R1_SP); + + // Pop the unpack frame. + __ pop_frame(); + __ restore_LR_CR(R0); + + // stack: (top interpreter frame, ..., optional interpreter frame, + // optional c2i, caller of deoptee, ...). + + // Initialize R14_state. +#ifdef CC_INTERP + __ ld(R14_state, 0, R1_SP); + __ addi(R14_state, R14_state, -frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + // Also inititialize R15_prev_state. + __ restore_prev_state(); +#else + __ restore_interpreter_state(R11_scratch1); + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1); +#endif // CC_INTERP + + + // Return to the interpreter entry point. + __ blr(); + __ flush(); +#else // COMPILER2 + __ unimplemented("deopt blob needed only with compiler"); + int exception_offset = __ pc() - start; +#endif // COMPILER2 + + _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, 0, first_frame_size_in_bytes / wordSize); +} + +#ifdef COMPILER2 +void SharedRuntime::generate_uncommon_trap_blob() { + // Allocate space for the code. + ResourceMark rm; + // Setup code generation tools. + CodeBuffer buffer("uncommon_trap_blob", 2048, 1024); + InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); + address start = __ pc(); + + Register unroll_block_reg = R21_tmp1; + Register klass_index_reg = R22_tmp2; + Register unc_trap_reg = R23_tmp3; + + OopMapSet* oop_maps = new OopMapSet(); + int frame_size_in_bytes = frame::abi_reg_args_size; + OopMap* map = new OopMap(frame_size_in_bytes / sizeof(jint), 0); + + // stack: (deoptee, optional i2c, caller_of_deoptee, ...). + + // Push a dummy `unpack_frame' and call + // `Deoptimization::uncommon_trap' to pack the compiled frame into a + // vframe array and return the `UnrollBlock' information. + + // Save LR to compiled frame. + __ save_LR_CR(R11_scratch1); + + // Push an "uncommon_trap" frame. + __ push_frame_reg_args(0, R11_scratch1); + + // stack: (unpack frame, deoptee, optional i2c, caller_of_deoptee, ...). + + // Set the `unpack_frame' as last_Java_frame. + // `Deoptimization::uncommon_trap' expects it and considers its + // sender frame as the deoptee frame. + // Remember the offset of the instruction whose address will be + // moved to R11_scratch1. + address gc_map_pc = __ get_PC_trash_LR(R11_scratch1); + + __ set_last_Java_frame(/*sp*/R1_SP, /*pc*/R11_scratch1); + + __ mr(klass_index_reg, R3); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::uncommon_trap), + R16_thread, klass_index_reg); + + // Set an oopmap for the call site. + oop_maps->add_gc_map(gc_map_pc - start, map); + + __ reset_last_Java_frame(); + + // Pop the `unpack frame'. + __ pop_frame(); + + // stack: (deoptee, optional i2c, caller_of_deoptee, ...). + + // Save the return value. + __ mr(unroll_block_reg, R3_RET); + + // Pop the uncommon_trap frame. + __ pop_frame(); + + // stack: (caller_of_deoptee, ...). + + // Allocate new interpreter frame(s) and possibly a c2i adapter + // frame. + push_skeleton_frames(masm, false/*deopt*/, + unroll_block_reg, + R22_tmp2, + R23_tmp3, + R24_tmp4, + R25_tmp5, + R26_tmp6); + + // stack: (skeletal interpreter frame, ..., optional skeletal + // interpreter frame, optional c2i, caller of deoptee, ...). + + // Push a dummy `unpack_frame' taking care of float return values. + // Call `Deoptimization::unpack_frames' to layout information in the + // interpreter frames just created. + + // Push a simple "unpack frame" here. + __ push_frame_reg_args(0, R11_scratch1); + + // stack: (unpack frame, skeletal interpreter frame, ..., optional + // skeletal interpreter frame, optional c2i, caller of deoptee, + // ...). + + // Set the "unpack_frame" as last_Java_frame. + __ get_PC_trash_LR(R11_scratch1); + __ set_last_Java_frame(/*sp*/R1_SP, /*pc*/R11_scratch1); + + // Indicate it is the uncommon trap case. + __ li(unc_trap_reg, Deoptimization::Unpack_uncommon_trap); + // Let the unpacker layout information in the skeletal frames just + // allocated. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), + R16_thread, unc_trap_reg); + + __ reset_last_Java_frame(); + // Pop the `unpack frame'. + __ pop_frame(); + // Restore LR from top interpreter frame. + __ restore_LR_CR(R11_scratch1); + + // stack: (top interpreter frame, ..., optional interpreter frame, + // optional c2i, caller of deoptee, ...). + +#ifdef CC_INTERP + // Initialize R14_state, ... + __ ld(R11_scratch1, 0, R1_SP); + __ addi(R14_state, R11_scratch1, -frame::interpreter_frame_cinterpreterstate_size_in_bytes()); + // also initialize R15_prev_state. + __ restore_prev_state(); +#else + __ restore_interpreter_state(R11_scratch1); + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1); +#endif // CC_INTERP + + // Return to the interpreter entry point. + __ blr(); + + masm->flush(); + + _uncommon_trap_blob = UncommonTrapBlob::create(&buffer, oop_maps, frame_size_in_bytes/wordSize); +} +#endif // COMPILER2 + +// Generate a special Compile2Runtime blob that saves all registers, and setup oopmap. +SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { + assert(StubRoutines::forward_exception_entry() != NULL, + "must be generated before"); + + ResourceMark rm; + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map; + + // Allocate space for the code. Setup code generation tools. + CodeBuffer buffer("handler_blob", 2048, 1024); + MacroAssembler* masm = new MacroAssembler(&buffer); + + address start = __ pc(); + int frame_size_in_bytes = 0; + + RegisterSaver::ReturnPCLocation return_pc_location; + bool cause_return = (poll_type == POLL_AT_RETURN); + if (cause_return) { + // Nothing to do here. The frame has already been popped in MachEpilogNode. + // Register LR already contains the return pc. + return_pc_location = RegisterSaver::return_pc_is_lr; + } else { + // Use thread()->saved_exception_pc() as return pc. + return_pc_location = RegisterSaver::return_pc_is_thread_saved_exception_pc; + } + + // Save registers, fpu state, and flags. + map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, + &frame_size_in_bytes, + /*generate_oop_map=*/ true, + /*return_pc_adjustment=*/0, + return_pc_location); + + // The following is basically a call_VM. However, we need the precise + // address of the call in order to generate an oopmap. Hence, we do all the + // work outselves. + __ set_last_Java_frame(/*sp=*/R1_SP, /*pc=*/noreg); + + // The return address must always be correct so that the frame constructor + // never sees an invalid pc. + + // Do the call + __ call_VM_leaf(call_ptr, R16_thread); + address calls_return_pc = __ last_calls_return_pc(); + + // Set an oopmap for the call site. This oopmap will map all + // oop-registers and debug-info registers as callee-saved. This + // will allow deoptimization at this safepoint to find all possible + // debug-info recordings, as well as let GC find all oops. + oop_maps->add_gc_map(calls_return_pc - start, map); + + Label noException; + + // Clear the last Java frame. + __ reset_last_Java_frame(); + + BLOCK_COMMENT(" Check pending exception."); + const Register pending_exception = R0; + __ ld(pending_exception, thread_(pending_exception)); + __ cmpdi(CCR0, pending_exception, 0); + __ beq(CCR0, noException); + + // Exception pending + RegisterSaver::restore_live_registers_and_pop_frame(masm, + frame_size_in_bytes, + /*restore_ctr=*/true); + + BLOCK_COMMENT(" Jump to forward_exception_entry."); + // Jump to forward_exception_entry, with the issuing PC in LR + // so it looks like the original nmethod called forward_exception_entry. + __ b64_patchable(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + + // No exception case. + __ BIND(noException); + + + // Normal exit, restore registers and exit. + RegisterSaver::restore_live_registers_and_pop_frame(masm, + frame_size_in_bytes, + /*restore_ctr=*/true); + + __ blr(); + + // Make sure all code is generated + masm->flush(); + + // Fill-out other meta info + // CodeBlob frame size is in words. + return SafepointBlob::create(&buffer, oop_maps, frame_size_in_bytes / wordSize); +} + +// generate_resolve_blob - call resolution (static/virtual/opt-virtual/ic-miss) +// +// Generate a stub that calls into the vm to find out the proper destination +// of a java call. All the argument registers are live at this point +// but since this is generic code we don't know what they are and the caller +// must do any gc of the args. +// +RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { + + // allocate space for the code + ResourceMark rm; + + CodeBuffer buffer(name, 1000, 512); + MacroAssembler* masm = new MacroAssembler(&buffer); + + int frame_size_in_bytes; + + OopMapSet *oop_maps = new OopMapSet(); + OopMap* map = NULL; + + address start = __ pc(); + + map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, + &frame_size_in_bytes, + /*generate_oop_map*/ true, + /*return_pc_adjustment*/ 0, + RegisterSaver::return_pc_is_lr); + + // Use noreg as last_Java_pc, the return pc will be reconstructed + // from the physical frame. + __ set_last_Java_frame(/*sp*/R1_SP, noreg); + + int frame_complete = __ offset(); + + // Pass R19_method as 2nd (optional) argument, used by + // counter_overflow_stub. + __ call_VM_leaf(destination, R16_thread, R19_method); + address calls_return_pc = __ last_calls_return_pc(); + // Set an oopmap for the call site. + // We need this not only for callee-saved registers, but also for volatile + // registers that the compiler might be keeping live across a safepoint. + // Create the oopmap for the call's return pc. + oop_maps->add_gc_map(calls_return_pc - start, map); + + // R3_RET contains the address we are going to jump to assuming no exception got installed. + + // clear last_Java_sp + __ reset_last_Java_frame(); + + // Check for pending exceptions. + BLOCK_COMMENT("Check for pending exceptions."); + Label pending; + __ ld(R11_scratch1, thread_(pending_exception)); + __ cmpdi(CCR0, R11_scratch1, 0); + __ bne(CCR0, pending); + + __ mtctr(R3_RET); // Ctr will not be touched by restore_live_registers_and_pop_frame. + + RegisterSaver::restore_live_registers_and_pop_frame(masm, frame_size_in_bytes, /*restore_ctr*/ false); + + // Get the returned methodOop. + __ ld(R19_method, in_bytes(JavaThread::vm_result_offset()), R16_thread); + + __ bctr(); + + + // Pending exception after the safepoint. + __ BIND(pending); + + RegisterSaver::restore_live_registers_and_pop_frame(masm, frame_size_in_bytes, /*restore_ctr*/ true); + + // exception pending => remove activation and forward to exception handler + + __ li(R11_scratch1, 0); + __ ld(R3_ARG1, thread_(pending_exception)); + __ std(R11_scratch1, in_bytes(JavaThread::vm_result_offset()), R16_thread); + __ b64_patchable(StubRoutines::forward_exception_entry(), relocInfo::runtime_call_type); + + // ------------- + // Make sure all code is generated. + masm->flush(); + + // return the blob + // frame_size_words or bytes?? + return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_bytes/wordSize, + oop_maps, true); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/stubGenerator_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/stubGenerator_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,2119 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "assembler_ppc.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "nativeInst_ppc.hpp" +#include "oops/instanceOop.hpp" +#include "oops/methodOop.hpp" +#include "oops/objArrayKlass.hpp" +#include "oops/oop.inline.hpp" +#include "prims/methodHandles.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubCodeGenerator.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/top.hpp" +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif +#ifdef TARGET_OS_FAMILY_linux +# include "thread_linux.inline.hpp" +#endif + +#define __ _masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +class StubGenerator: public StubCodeGenerator { + private: + + // Call stubs are used to call Java from C + // + // Arguments: + // + // R3 - call wrapper address : address + // R4 - result : intptr_t* + // R5 - result type : BasicType + // R6 - method : methodOop + // R7 - frame mgr entry point : address + // R8 - parameter block : intptr_t* + // R9 - parameter count in words : int + // R10 - thread : Thread* + // + address generate_call_stub(address& return_address) { + // Setup a new c frame, copy java arguments, call frame manager or + // native_entry, and process result. + + StubCodeMark mark(this, "StubRoutines", "call_stub"); + + address start = __ function_entry(); + + // some sanity checks + assert((sizeof(frame::abi_minframe) % 16) == 0, "unaligned"); + assert((sizeof(frame::abi_reg_args) % 16) == 0, "unaligned"); + assert((sizeof(frame::spill_nonvolatiles) % 16) == 0, "unaligned"); + assert((sizeof(frame::parent_ijava_frame_abi) % 16) == 0, "unaligned"); + assert((sizeof(frame::entry_frame_locals) % 16) == 0, "unaligned"); + + Register r_arg_call_wrapper_addr = R3; + Register r_arg_result_addr = R4; + Register r_arg_result_type = R5; + Register r_arg_method = R6; + Register r_arg_entry = R7; + Register r_arg_thread = R10; + + Register r_temp = R24; + Register r_top_of_arguments_addr = R25; + Register r_entryframe_fp = R26; + + { + // Stack on entry to call_stub: + // + // F1 [C_FRAME] + // ... + + Register r_arg_argument_addr = R8; + Register r_arg_argument_count = R9; + Register r_frame_alignment_in_bytes = R27; + Register r_argument_addr = R28; + Register r_argumentcopy_addr = R29; + Register r_argument_size_in_bytes = R30; + Register r_frame_size = R23; + + Label arguments_copied; + + // Save LR/CR to caller's C_FRAME. + __ save_LR_CR(R0); + + // Zero extend arg_argument_count. + __ clrldi(r_arg_argument_count, r_arg_argument_count, 32); + + // Save non-volatiles GPRs to ENTRY_FRAME (not yet pushed, but it's safe). + __ save_nonvolatile_gprs(R1_SP, _spill_nonvolatiles_neg(r14)); + + // Keep copy of our frame pointer (caller's SP). + __ mr(r_entryframe_fp, R1_SP); + + BLOCK_COMMENT("Push ENTRY_FRAME including arguments"); + // Push ENTRY_FRAME including arguments: + // + // F0 [TOP_IJAVA_FRAME_ABI] + // alignment (optional) + // [outgoing Java arguments] + // [ENTRY_FRAME_LOCALS] + // F1 [C_FRAME] + // ... + + // calculate frame size + + // unaligned size of arguments + __ sldi(r_argument_size_in_bytes, + r_arg_argument_count, Interpreter::logStackElementSize); + // arguments alignment (max 1 slot) + // FIXME: use round_to() here + __ andi_(r_frame_alignment_in_bytes, r_arg_argument_count, 1); + __ sldi(r_frame_alignment_in_bytes, + r_frame_alignment_in_bytes, Interpreter::logStackElementSize); + + // size = unaligned size of arguments + top abi's size + __ addi(r_frame_size, r_argument_size_in_bytes, + frame::top_ijava_frame_abi_size); + // size += arguments alignment + __ add(r_frame_size, + r_frame_size, r_frame_alignment_in_bytes); + // size += size of call_stub locals + __ addi(r_frame_size, + r_frame_size, frame::entry_frame_locals_size); + + // push ENTRY_FRAME + __ push_frame(r_frame_size, r_temp); + + // initialize call_stub locals (step 1) + __ std(r_arg_call_wrapper_addr, + _entry_frame_locals_neg(call_wrapper_address), r_entryframe_fp); + __ std(r_arg_result_addr, + _entry_frame_locals_neg(result_address), r_entryframe_fp); + __ std(r_arg_result_type, + _entry_frame_locals_neg(result_type), r_entryframe_fp); + // we will save arguments_tos_address later + + + BLOCK_COMMENT("Copy Java arguments"); + // copy Java arguments + + // Calculate top_of_arguments_addr which will be R17_tos (not prepushed) later. + // FIXME: why not simply use SP+frame::top_ijava_frame_size? + __ addi(r_top_of_arguments_addr, + R1_SP, frame::top_ijava_frame_abi_size); + __ add(r_top_of_arguments_addr, + r_top_of_arguments_addr, r_frame_alignment_in_bytes); + + // any arguments to copy? + __ cmpdi(CCR0, r_arg_argument_count, 0); + __ beq(CCR0, arguments_copied); + + // prepare loop and copy arguments in reverse order + { + // init CTR with arg_argument_count + __ mtctr(r_arg_argument_count); + + // let r_argumentcopy_addr point to last outgoing Java arguments P + __ mr(r_argumentcopy_addr, r_top_of_arguments_addr); + + // let r_argument_addr point to last incoming java argument + __ add(r_argument_addr, + r_arg_argument_addr, r_argument_size_in_bytes); + __ addi(r_argument_addr, r_argument_addr, -BytesPerWord); + + // now loop while CTR > 0 and copy arguments + { + Label next_argument; + __ bind(next_argument); + + __ ld(r_temp, 0, r_argument_addr); + // argument_addr--; + __ addi(r_argument_addr, r_argument_addr, -BytesPerWord); + __ std(r_temp, 0, r_argumentcopy_addr); + // argumentcopy_addr++; + __ addi(r_argumentcopy_addr, r_argumentcopy_addr, BytesPerWord); + + __ bdnz(next_argument); + } + } + + // Arguments copied, continue. + __ bind(arguments_copied); + } + + { + BLOCK_COMMENT("Call frame manager or native entry."); + // Call frame manager or native entry. + Register r_new_arg_entry = R14; // PPC_state; + assert_different_registers(r_new_arg_entry, r_top_of_arguments_addr, + r_arg_method, r_arg_thread); + + __ mr(r_new_arg_entry, r_arg_entry); + + // Register state on entry to frame manager / native entry: + // + // tos - intptr_t* sender tos (prepushed) Lesp = (SP) + copied_arguments_offset - 8 + // R19_method - methodOop* + // R16_thread - JavaThread* + + // Tos must point to last argument - element_size. +#ifdef CC_INTERP + const Register tos = R17_tos; +#else + const Register tos = R15_esp; +#endif + __ addi(tos, r_top_of_arguments_addr, -Interpreter::stackElementSize); + + // initialize call_stub locals (step 2) + // now save tos as arguments_tos_address + __ std(tos, _entry_frame_locals_neg(arguments_tos_address), r_entryframe_fp); + + // load argument registers for call + __ mr(R19_method, r_arg_method); + __ mr(R16_thread, r_arg_thread); + assert(tos != r_arg_method, "trashed r_arg_method"); + assert(tos != r_arg_thread && R19_method != r_arg_thread, "trashed r_arg_thread"); + + // Set R15_prev_state to 0 for simplifying checks in callee. +#ifdef CC_INTERP + __ li(R15_prev_state, 0); +#else + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1); +#endif + // Stack on entry to frame manager / native entry: + // + // F0 [TOP_IJAVA_FRAME_ABI] + // alignment (optional) + // [outgoing Java arguments] + // [ENTRY_FRAME_LOCALS] + // F1 [C_FRAME] + // ... + // + + // global toc register + __ load_const(R29, MacroAssembler::global_toc(), R11_scratch1); + + // Load narrow oop base. + int oop_base_offs = __ load_const_optimized(R30, Universe::narrow_oop_base_addr(), R11_scratch1, true); + __ ld(R30, oop_base_offs, R30); + + // Remember the senderSP so we interpreter can pop c2i arguments off of the stack + // when called via a c2i. + + // Pass initial_caller_sp to framemanager. + __ mr(R21_tmp1, R1_SP); + + // Do a light-weight C-call here, r_new_arg_entry holds the address + // of the interpreter entry point (frame manager or native entry) + // and save runtime-value of LR in return_address. + assert(r_new_arg_entry != tos && r_new_arg_entry != R19_method && r_new_arg_entry != R16_thread, + "trashed r_new_arg_entry"); + return_address = __ call_stub(r_new_arg_entry); + } + + { + BLOCK_COMMENT("Returned from frame manager or native entry."); + // Returned from frame manager or native entry. + // Now pop frame, process result, and return to caller. + + // Stack on exit from frame manager / native entry: + // + // F0 [ABI] + // ... + // [ENTRY_FRAME_LOCALS] + // F1 [C_FRAME] + // ... + // + // Just pop the topmost frame ... + // + + Label ret_is_object; + Label ret_is_long; + Label ret_is_float; + Label ret_is_double; + + Register r_entryframe_fp = R30; + Register r_lr = R7_ARG5; + Register r_cr = R8_ARG6; + + // Reload some volatile registers which we've spilled before the call + // to frame manager / native entry. + // Access all locals via frame pointer, because we know nothing about + // the topmost frame's size. + __ ld(r_entryframe_fp, _abi(callers_sp), R1_SP); + assert_different_registers(r_entryframe_fp, R3_RET, r_arg_result_addr, r_arg_result_type, r_cr, r_lr); + __ ld(r_arg_result_addr, + _entry_frame_locals_neg(result_address), r_entryframe_fp); + __ ld(r_arg_result_type, + _entry_frame_locals_neg(result_type), r_entryframe_fp); + __ ld(r_cr, _abi(cr), r_entryframe_fp); + __ ld(r_lr, _abi(lr), r_entryframe_fp); + + // pop frame and restore non-volatiles, LR and CR + __ mr(R1_SP, r_entryframe_fp); + __ mtcr(r_cr); + __ mtlr(r_lr); + + // Store result depending on type. Everything that is not + // T_OBJECT, T_LONG, T_FLOAT, or T_DOUBLE is treated as T_INT. + __ cmpwi(CCR0, r_arg_result_type, T_OBJECT); + __ cmpwi(CCR1, r_arg_result_type, T_LONG); + __ cmpwi(CCR5, r_arg_result_type, T_FLOAT); + __ cmpwi(CCR6, r_arg_result_type, T_DOUBLE); + + // restore non-volatile registers + __ restore_nonvolatile_gprs(R1_SP, _spill_nonvolatiles_neg(r14)); + + + // Stack on exit from call_stub: + // + // 0 [C_FRAME] + // ... + // + // no call_stub frames left. + + // All non-volatiles have been restored at this point!! + assert(R3_RET == R3, "R3_RET should be R3"); + + __ beq(CCR0, ret_is_object); + __ beq(CCR1, ret_is_long); + __ beq(CCR5, ret_is_float); + __ beq(CCR6, ret_is_double); + + // default: + __ stw(R3_RET, 0, r_arg_result_addr); + __ blr(); // return to caller + + // case T_OBJECT: + __ bind(ret_is_object); + __ std(R3_RET, 0, r_arg_result_addr); + __ blr(); // return to caller + + // case T_LONG: + __ bind(ret_is_long); + __ std(R3_RET, 0, r_arg_result_addr); + __ blr(); // return to caller + + // case T_FLOAT: + __ bind(ret_is_float); + __ stfs(F1_RET, 0, r_arg_result_addr); + __ blr(); // return to caller + + // case T_DOUBLE: + __ bind(ret_is_double); + __ stfd(F1_RET, 0, r_arg_result_addr); + __ blr(); // return to caller + } + + return start; + } + + // Return point for a Java call if there's an exception thrown in + // Java code. The exception is caught and transformed into a + // pending exception stored in JavaThread that can be tested from + // within the VM. + // + address generate_catch_exception() { + StubCodeMark mark(this, "StubRoutines", "catch_exception"); + + address start = __ pc(); + + // Registers alive + // + // R16_thread + // R3_ARG1 - address of pending exception + // R4_ARG2 - return address in call stub + + const Register exception_file = R21_tmp1; + const Register exception_line = R22_tmp2; + + __ load_const(exception_file, (void*)__FILE__); + __ load_const(exception_line, (void*)__LINE__); + + __ std(R3_ARG1, thread_(pending_exception)); + // store into `char *' + __ std(exception_file, thread_(exception_file)); + // store into `int' + __ stw(exception_line, thread_(exception_line)); + + // complete return to VM + assert(StubRoutines::_call_stub_return_address != NULL, "must have been generated before"); + + __ mtlr(R4_ARG2); + // continue in call stub + __ blr(); + + return start; + } + + // Continuation point for runtime calls returning with a pending + // exception. The pending exception check happened in the runtime + // or native call stub. The pending exception in Thread is + // converted into a Java-level exception. + // + address generate_forward_exception() { + StubCodeMark mark(this, "StubRoutines", "forward_exception"); + address start = __ pc(); + +#if !defined(PRODUCT) + if (VerifyOops) { + // Get pending exception oop. + __ ld(R3_ARG1, + in_bytes(Thread::pending_exception_offset()), + R16_thread); + // Make sure that this code is only executed if there is a pending exception. + { + Label L; + __ cmpdi(CCR0, R3_ARG1, 0); + __ bne(CCR0, L); + __ stop("StubRoutines::forward exception: no pending exception (1)"); + __ bind(L); + } + __ verify_oop(R3_ARG1, "StubRoutines::forward exception: not an oop"); + } +#endif + + // Save LR/CR and copy exception pc (LR) into R4_ARG2. + __ save_LR_CR(R4_ARG2); + __ push_frame_reg_args(0, R0); + // Find exception handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, + SharedRuntime::exception_handler_for_return_address), + R16_thread, + R4_ARG2); + // Copy handler's address. + __ mtctr(R3_RET); + __ pop_frame(); + __ restore_LR_CR(R0); + + // Set up the arguments for the exception handler: + // - R3_ARG1: exception oop + // - R4_ARG2: exception pc. + + // Load pending exception oop. + __ ld(R3_ARG1, + in_bytes(Thread::pending_exception_offset()), + R16_thread); + + // The exception pc is the return address in the caller. + // Must load it into R4_ARG2. + __ mflr(R4_ARG2); + +#ifdef ASSERT + // Make sure exception is set. + { + Label L; + __ cmpdi(CCR0, R3_ARG1, 0); + __ bne(CCR0, L); + __ stop("StubRoutines::forward exception: no pending exception (2)"); + __ bind(L); + } +#endif + + // Clear the pending exception. + __ li(R0, 0); + __ std(R0, + in_bytes(Thread::pending_exception_offset()), + R16_thread); + // Jump to exception handler. + __ bctr(); + + return start; + } + +#undef __ +#define __ masm-> + // Continuation point for throwing of implicit exceptions that are + // not handled in the current activation. Fabricates an exception + // oop and initiates normal exception dispatching in this + // frame. Only callee-saved registers are preserved (through the + // normal register window / RegisterMap handling). If the compiler + // needs all registers to be preserved between the fault point and + // the exception handler then it must assume responsibility for that + // in AbstractCompiler::continuation_for_implicit_null_exception or + // continuation_for_implicit_division_by_zero_exception. All other + // implicit exceptions (e.g., NullPointerException or + // AbstractMethodError on entry) are either at call sites or + // otherwise assume that stack unwinding will be initiated, so + // caller saved registers were assumed volatile in the compiler. + // + // Note that we generate only this stub into a RuntimeStub, because + // it needs to be properly traversed and ignored during GC, so we + // change the meaning of the "__" macro within this method. + // + // Note: the routine set_pc_not_at_call_for_caller in + // SharedRuntime.cpp requires that this code be generated into a + // RuntimeStub. + address generate_throw_exception(const char* name, address runtime_entry, bool restore_saved_exception_pc, + Register arg1 = noreg, Register arg2 = noreg) { + CodeBuffer code(name, 1024 DEBUG_ONLY(+ 512), 0); + MacroAssembler* masm = new MacroAssembler(&code); + + OopMapSet* oop_maps = new OopMapSet(); + int frame_size_in_bytes = frame::abi_reg_args_size; + OopMap* map = new OopMap(frame_size_in_bytes / sizeof(jint), 0); + + StubCodeMark mark(this, "StubRoutines", "throw_exception"); + + address start = __ pc(); + + __ save_LR_CR(R11_scratch1); + + // Push a frame. + __ push_frame_reg_args(0, R11_scratch1); + + address frame_complete_pc = __ pc(); + + if (restore_saved_exception_pc) { + __ unimplemented("StubGenerator::throw_exception with restore_saved_exception_pc", 74); + } + + // Note that we always have a runtime stub frame on the top of + // stack by this point. Remember the offset of the instruction + // whose address will be moved to R11_scratch1. + address gc_map_pc = __ get_PC_trash_LR(R11_scratch1); + + __ set_last_Java_frame(/*sp*/R1_SP, /*pc*/R11_scratch1); + + __ mr(R3_ARG1, R16_thread); + if (arg1 != noreg) { + __ mr(R4_ARG2, arg1); + } + if (arg2 != noreg) { + __ mr(R5_ARG3, arg2); + } +#if defined(ABI_ELFv2) + __ call_c(runtime_entry, relocInfo::none); +#else + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, runtime_entry), relocInfo::none); +#endif + + // Set an oopmap for the call site. + oop_maps->add_gc_map((int)(gc_map_pc - start), map); + + __ reset_last_Java_frame(); + +#ifdef ASSERT + // Make sure that this code is only executed if there is a pending + // exception. + { + Label L; + __ ld(R0, + in_bytes(Thread::pending_exception_offset()), + R16_thread); + __ cmpdi(CCR0, R0, 0); + __ bne(CCR0, L); + __ stop("StubRoutines::throw_exception: no pending exception"); + __ bind(L); + } +#endif + + // Pop frame. + __ pop_frame(); + + __ restore_LR_CR(R11_scratch1); + + __ load_const(R11_scratch1, StubRoutines::forward_exception_entry()); + __ mtctr(R11_scratch1); + __ bctr(); + + // Create runtime stub with OopMap. + RuntimeStub* stub = + RuntimeStub::new_runtime_stub(name, &code, + /*frame_complete=*/ (int)(frame_complete_pc - start), + frame_size_in_bytes/wordSize, + oop_maps, + false); + return stub->entry_point(); + } +#undef __ +#define __ _masm-> + + // Generate G1 pre-write barrier for array. + // + // Input: + // from - register containing src address (only needed for spilling) + // to - register containing starting address + // count - register containing element count + // tmp - scratch register + // + // Kills: + // nothing + // + void gen_write_ref_array_pre_barrier(Register from, Register to, Register count, bool dest_uninitialized, Register Rtmp1) { + BarrierSet* const bs = Universe::heap()->barrier_set(); + switch (bs->kind()) { + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + // With G1, don't generate the call if we statically know that the target in uninitialized + if (!dest_uninitialized) { + const int spill_slots = 4 * wordSize; + const int frame_size = frame::abi_reg_args_size + spill_slots; + Label filtered; + + // Is marking active? + if (in_bytes(PtrQueue::byte_width_of_active()) == 4) { + __ lwz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + } else { + guarantee(in_bytes(PtrQueue::byte_width_of_active()) == 1, "Assumption"); + __ lbz(Rtmp1, in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_active()), R16_thread); + } + __ cmpdi(CCR0, Rtmp1, 0); + __ beq(CCR0, filtered); + + __ save_LR_CR(R0); + __ push_frame_reg_args(spill_slots, R0); + __ std(from, frame_size - 1 * wordSize, R1_SP); + __ std(to, frame_size - 2 * wordSize, R1_SP); + __ std(count, frame_size - 3 * wordSize, R1_SP); + + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre), to, count); + + __ ld(from, frame_size - 1 * wordSize, R1_SP); + __ ld(to, frame_size - 2 * wordSize, R1_SP); + __ ld(count, frame_size - 3 * wordSize, R1_SP); + __ pop_frame(); + __ restore_LR_CR(R0); + + __ bind(filtered); + } + break; + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + case BarrierSet::ModRef: + break; + default: + ShouldNotReachHere(); + } + } + + // Generate CMS/G1 post-write barrier for array. + // + // Input: + // addr - register containing starting address + // count - register containing element count + // tmp - scratch register + // + // The input registers and R0 are overwritten. + // + void gen_write_ref_array_post_barrier(Register addr, Register count, Register tmp, bool branchToEnd) { + BarrierSet* const bs = Universe::heap()->barrier_set(); + + switch (bs->kind()) { + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + { + if (branchToEnd) { + __ save_LR_CR(R0); + // We need this frame only to spill LR. + __ push_frame_reg_args(0, R0); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post), addr, count); + __ pop_frame(); + __ restore_LR_CR(R0); + } else { + // Tail call: fake call from stub caller by branching without linking. + address entry_point = (address)CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post); + __ mr_if_needed(R3_ARG1, addr); + __ mr_if_needed(R4_ARG2, count); + __ load_const(R11, entry_point, R0); + __ call_c_and_return_to_caller(R11); + } + } + break; + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + { + Label Lskip_loop, Lstore_loop; + if (UseConcMarkSweepGC) { + // TODO PPC port: contribute optimization / requires shared changes + __ release(); + } + + CardTableModRefBS* const ct = (CardTableModRefBS*)bs; + assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code"); + assert_different_registers(addr, count, tmp); + + __ sldi(count, count, LogBytesPerHeapOop); + __ addi(count, count, -BytesPerHeapOop); + __ add(count, addr, count); + // Use two shifts to clear out those low order two bits! (Cannot opt. into 1.) + __ srdi(addr, addr, CardTableModRefBS::card_shift); + __ srdi(count, count, CardTableModRefBS::card_shift); + __ subf(count, addr, count); + assert_different_registers(R0, addr, count, tmp); + __ load_const(tmp, (address)ct->byte_map_base); + __ addic_(count, count, 1); + __ beq(CCR0, Lskip_loop); + __ li(R0, 0); + __ mtctr(count); + // Byte store loop + __ bind(Lstore_loop); + __ stbx(R0, tmp, addr); + __ addi(addr, addr, 1); + __ bdnz(Lstore_loop); + __ bind(Lskip_loop); + + if (!branchToEnd) __ blr(); + } + break; + case BarrierSet::ModRef: + if (!branchToEnd) __ blr(); + break; + default: + ShouldNotReachHere(); + } + } + + // Support for void zero_words_aligned8(HeapWord* to, size_t count) + // + // Arguments: + // to: + // count: + // + // Destroys: + // + address generate_zero_words_aligned8() { + StubCodeMark mark(this, "StubRoutines", "zero_words_aligned8"); + + // Implemented as in ClearArray. + address start = __ function_entry(); + + Register base_ptr_reg = R3_ARG1; // tohw (needs to be 8b aligned) + Register cnt_dwords_reg = R4_ARG2; // count (in dwords) + Register tmp1_reg = R5_ARG3; + Register tmp2_reg = R6_ARG4; + Register zero_reg = R7_ARG5; + + // Procedure for large arrays (uses data cache block zero instruction). + Label dwloop, fast, fastloop, restloop, lastdword, done; + int cl_size=VM_Version::get_cache_line_size(), cl_dwords=cl_size>>3, cl_dwordaddr_bits=exact_log2(cl_dwords); + int min_dcbz=2; // Needs to be positive, apply dcbz only to at least min_dcbz cache lines. + + // Clear up to 128byte boundary if long enough, dword_cnt=(16-(base>>3))%16. + __ dcbtst(base_ptr_reg); // Indicate write access to first cache line ... + __ andi(tmp2_reg, cnt_dwords_reg, 1); // to check if number of dwords is even. + __ srdi_(tmp1_reg, cnt_dwords_reg, 1); // number of double dwords + __ load_const_optimized(zero_reg, 0L); // Use as zero register. + + __ cmpdi(CCR1, tmp2_reg, 0); // cnt_dwords even? + __ beq(CCR0, lastdword); // size <= 1 + __ mtctr(tmp1_reg); // Speculatively preload counter for rest loop (>0). + __ cmpdi(CCR0, cnt_dwords_reg, (min_dcbz+1)*cl_dwords-1); // Big enough to ensure >=min_dcbz cache lines are included? + __ neg(tmp1_reg, base_ptr_reg); // bit 0..58: bogus, bit 57..60: (16-(base>>3))%16, bit 61..63: 000 + + __ blt(CCR0, restloop); // Too small. (<31=(2*cl_dwords)-1 is sufficient, but bigger performs better.) + __ rldicl_(tmp1_reg, tmp1_reg, 64-3, 64-cl_dwordaddr_bits); // Extract number of dwords to 128byte boundary=(16-(base>>3))%16. + + __ beq(CCR0, fast); // already 128byte aligned + __ mtctr(tmp1_reg); // Set ctr to hit 128byte boundary (00 since size>=256-8) + + // Clear in first cache line dword-by-dword if not already 128byte aligned. + __ bind(dwloop); + __ std(zero_reg, 0, base_ptr_reg); // Clear 8byte aligned block. + __ addi(base_ptr_reg, base_ptr_reg, 8); + __ bdnz(dwloop); + + // clear 128byte blocks + __ bind(fast); + __ srdi(tmp1_reg, cnt_dwords_reg, cl_dwordaddr_bits); // loop count for 128byte loop (>0 since size>=256-8) + __ andi(tmp2_reg, cnt_dwords_reg, 1); // to check if rest even + + __ mtctr(tmp1_reg); // load counter + __ cmpdi(CCR1, tmp2_reg, 0); // rest even? + __ rldicl_(tmp1_reg, cnt_dwords_reg, 63, 65-cl_dwordaddr_bits); // rest in double dwords + + __ bind(fastloop); + __ dcbz(base_ptr_reg); // Clear 128byte aligned block. + __ addi(base_ptr_reg, base_ptr_reg, cl_size); + __ bdnz(fastloop); + + //__ dcbtst(base_ptr_reg); // Indicate write access to last cache line. + __ beq(CCR0, lastdword); // rest<=1 + __ mtctr(tmp1_reg); // load counter + + // Clear rest. + __ bind(restloop); + __ std(zero_reg, 0, base_ptr_reg); // Clear 8byte aligned block. + __ std(zero_reg, 8, base_ptr_reg); // Clear 8byte aligned block. + __ addi(base_ptr_reg, base_ptr_reg, 16); + __ bdnz(restloop); + + __ bind(lastdword); + __ beq(CCR1, done); + __ std(zero_reg, 0, base_ptr_reg); + __ bind(done); + __ blr(); // return + + return start; + } + + // The following routine generates a subroutine to throw an asynchronous + // UnknownError when an unsafe access gets a fault that could not be + // reasonably prevented by the programmer. (Example: SIGBUS/OBJERR.) + // + address generate_handler_for_unsafe_access() { + StubCodeMark mark(this, "StubRoutines", "handler_for_unsafe_access"); + address start = __ function_entry(); + __ unimplemented("StubRoutines::handler_for_unsafe_access", 86); + return start; + } + +#if !defined(PRODUCT) + // Wrapper which calls oopDesc::is_oop_or_null() + // Only called by MacroAssembler::verify_oop + static void verify_oop_helper(const char* message, oop o) { + if (!o->is_oop_or_null()) { + fatal(message); + } + ++ StubRoutines::_verify_oop_count; + } +#endif + + // Return address of code to be called from code generated by + // MacroAssembler::verify_oop. + // + // Don't generate, rather use C++ code. + address generate_verify_oop() { + StubCodeMark mark(this, "StubRoutines", "verify_oop"); + + // this is actually a `FunctionDescriptor*'. + address start = 0; + +#if !defined(PRODUCT) + start = CAST_FROM_FN_PTR(address, verify_oop_helper); +#endif + + return start; + } + + // Fairer handling of safepoints for native methods. + // + // Generate code which reads from the polling page. This special handling is needed as the + // linux-ppc64 kernel before 2.6.6 doesn't set si_addr on some segfaults in 64bit mode + // (cf. http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.6), especially when we try + // to read from the safepoint polling page. + address generate_load_from_poll() { + StubCodeMark mark(this, "StubRoutines", "generate_load_from_poll"); + address start = __ function_entry(); + __ unimplemented("StubRoutines::verify_oop", 95); // TODO PPC port + return start; + } + + // -XX:+OptimizeFill : convert fill/copy loops into intrinsic + // + // The code is implemented(ported from sparc) as we believe it benefits JVM98, however + // tracing(-XX:+TraceOptimizeFill) shows the intrinsic replacement doesn't happen at all! + // + // Source code in function is_range_check_if() shows that OptimizeFill relaxed the condition + // for turning on loop predication optimization, and hence the behavior of "array range check" + // and "loop invariant check" could be influenced, which potentially boosted JVM98. + // + // Generate stub for disjoint short fill. If "aligned" is true, the + // "to" address is assumed to be heapword aligned. + // + // Arguments for generated stub: + // to: R3_ARG1 + // value: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_fill(BasicType t, bool aligned, const char* name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + + const Register to = R3_ARG1; // source array address + const Register value = R4_ARG2; // fill value + const Register count = R5_ARG3; // elements count + const Register temp = R6_ARG4; // temp register + + //assert_clean_int(count, O3); // Make sure 'count' is clean int. + + Label L_exit, L_skip_align1, L_skip_align2, L_fill_byte; + Label L_fill_2_bytes, L_fill_4_bytes, L_fill_elements, L_fill_32_bytes; + + int shift = -1; + switch (t) { + case T_BYTE: + shift = 2; + // Clone bytes (zero extend not needed because store instructions below ignore high order bytes). + __ rldimi(value, value, 8, 48); // 8 bit -> 16 bit + __ cmpdi(CCR0, count, 2< 32 bit + break; + case T_SHORT: + shift = 1; + // Clone bytes (zero extend not needed because store instructions below ignore high order bytes). + __ rldimi(value, value, 16, 32); // 16 bit -> 32 bit + __ cmpdi(CCR0, count, 2<long as above. + __ rldimi(value, value, 32, 0); // 32 bit -> 64 bit + + Label L_check_fill_8_bytes; + // Fill 32-byte chunks. + __ subf_(count, temp, count); + __ blt(CCR0, L_check_fill_8_bytes); + + Label L_fill_32_bytes_loop; + __ align(32); + __ bind(L_fill_32_bytes_loop); + + __ std(value, 0, to); + __ std(value, 8, to); + __ subf_(count, temp, count); // Update count. + __ std(value, 16, to); + __ std(value, 24, to); + + __ addi(to, to, 32); + __ bge(CCR0, L_fill_32_bytes_loop); + + __ bind(L_check_fill_8_bytes); + __ add_(count, temp, count); + __ beq(CCR0, L_exit); + __ addic_(count, count, -(2 << shift)); + __ blt(CCR0, L_fill_4_bytes); + + // + // Length is too short, just fill 8 bytes at a time. + // + Label L_fill_8_bytes_loop; + __ bind(L_fill_8_bytes_loop); + __ std(value, 0, to); + __ addic_(count, count, -(2 << shift)); + __ addi(to, to, 8); + __ bge(CCR0, L_fill_8_bytes_loop); + + // Fill trailing 4 bytes. + __ bind(L_fill_4_bytes); + __ andi_(temp, count, 1< to or from is aligned -> copy 8 + + // copy a 2-element word if necessary to align to 8 bytes + __ andi_(R0, R3_ARG1, 7); + __ beq(CCR0, l_7); + + __ lwzx(tmp2, R3_ARG1, tmp3); + __ addi(R5_ARG3, R5_ARG3, -4); + __ stwx(tmp2, R4_ARG2, tmp3); + { // FasterArrayCopy + __ addi(R3_ARG1, R3_ARG1, 4); + __ addi(R4_ARG2, R4_ARG2, 4); + } + __ bind(l_7); + + { // FasterArrayCopy + __ cmpwi(CCR0, R5_ARG3, 31); + __ ble(CCR0, l_6); // copy 2 at a time if less than 32 elements remain + + __ srdi(tmp1, R5_ARG3, 5); + __ andi_(R5_ARG3, R5_ARG3, 31); + __ mtctr(tmp1); + + __ bind(l_8); + // Use unrolled version for mass copying (copy 32 elements a time) + // Load feeding store gets zero latency on Power6, however not on Power5. + // Therefore, the following sequence is made for the good of both. + __ ld(tmp1, 0, R3_ARG1); + __ ld(tmp2, 8, R3_ARG1); + __ ld(tmp3, 16, R3_ARG1); + __ ld(tmp4, 24, R3_ARG1); + __ std(tmp1, 0, R4_ARG2); + __ std(tmp2, 8, R4_ARG2); + __ std(tmp3, 16, R4_ARG2); + __ std(tmp4, 24, R4_ARG2); + __ addi(R3_ARG1, R3_ARG1, 32); + __ addi(R4_ARG2, R4_ARG2, 32); + __ bdnz(l_8); + } + + __ bind(l_6); + + // copy 4 elements at a time + __ cmpwi(CCR0, R5_ARG3, 4); + __ blt(CCR0, l_1); + __ srdi(tmp1, R5_ARG3, 2); + __ mtctr(tmp1); // is > 0 + __ andi_(R5_ARG3, R5_ARG3, 3); + + { // FasterArrayCopy + __ addi(R3_ARG1, R3_ARG1, -4); + __ addi(R4_ARG2, R4_ARG2, -4); + __ bind(l_3); + __ lwzu(tmp2, 4, R3_ARG1); + __ stwu(tmp2, 4, R4_ARG2); + __ bdnz(l_3); + __ addi(R3_ARG1, R3_ARG1, 4); + __ addi(R4_ARG2, R4_ARG2, 4); + } + + // do single element copy + __ bind(l_1); + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_4); + + { // FasterArrayCopy + __ mtctr(R5_ARG3); + __ addi(R3_ARG1, R3_ARG1, -1); + __ addi(R4_ARG2, R4_ARG2, -1); + + __ bind(l_5); + __ lbzu(tmp2, 1, R3_ARG1); + __ stbu(tmp2, 1, R4_ARG2); + __ bdnz(l_5); + } + + __ bind(l_4); + __ blr(); + + return start; + } + + // Generate stub for conjoint byte copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_conjoint_byte_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + +#if defined(ABI_ELFv2) + address nooverlap_target = aligned ? + StubRoutines::arrayof_jbyte_disjoint_arraycopy() : + StubRoutines::jbyte_disjoint_arraycopy(); +#else + address nooverlap_target = aligned ? + ((FunctionDescriptor*)StubRoutines::arrayof_jbyte_disjoint_arraycopy())->entry() : + ((FunctionDescriptor*)StubRoutines::jbyte_disjoint_arraycopy())->entry(); +#endif + + array_overlap_test(nooverlap_target, 0); + // Do reverse copy. We assume the case of actual overlap is rare enough + // that we don't have to optimize it. + Label l_1, l_2; + + __ b(l_2); + __ bind(l_1); + __ stbx(tmp1, R4_ARG2, R5_ARG3); + __ bind(l_2); + __ addic_(R5_ARG3, R5_ARG3, -1); + __ lbzx(tmp1, R3_ARG1, R5_ARG3); + __ bge(CCR0, l_1); + + __ blr(); + + return start; + } + + // Generate stub for disjoint short copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // elm.count: R5_ARG3 treated as signed + // + // Strategy for aligned==true: + // + // If length <= 9: + // 1. copy 2 elements at a time (l_6) + // 2. copy last element if original element count was odd (l_1) + // + // If length > 9: + // 1. copy 4 elements at a time until less than 4 elements are left (l_7) + // 2. copy 2 elements at a time until less than 2 elements are left (l_6) + // 3. copy last element if one was left in step 2. (l_1) + // + // + // Strategy for aligned==false: + // + // If length <= 9: same as aligned==true case, but NOTE: load/stores + // can be unaligned (see comment below) + // + // If length > 9: + // 1. continue with step 6. if the alignment of from and to mod 4 + // is different. + // 2. align from and to to 4 bytes by copying 1 element if necessary + // 3. at l_2 from and to are 4 byte aligned; continue with + // 5. if they cannot be aligned to 8 bytes because they have + // got different alignment mod 8. + // 4. at this point we know that both, from and to, have the same + // alignment mod 8, now copy one element if necessary to get + // 8 byte alignment of from and to. + // 5. copy 4 elements at a time until less than 4 elements are + // left; depending on step 3. all load/stores are aligned or + // either all loads or all stores are unaligned. + // 6. copy 2 elements at a time until less than 2 elements are + // left (l_6); arriving here from step 1., there is a chance + // that all accesses are unaligned. + // 7. copy last element if one was left in step 6. (l_1) + // + // There are unaligned data accesses using integer load/store + // instructions in this stub. POWER allows such accesses. + // + // According to the manuals (PowerISA_V2.06_PUBLIC, Book II, + // Chapter 2: Effect of Operand Placement on Performance) unaligned + // integer load/stores have good performance. Only unaligned + // floating point load/stores can have poor performance. + // + // TODO: + // + // 1. check if aligning the backbranch target of loops is beneficial + // + address generate_disjoint_short_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + Register tmp4 = R9_ARG7; + + address start = __ function_entry(); + + Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8; + // don't try anything fancy if arrays don't have many elements + __ li(tmp3, 0); + __ cmpwi(CCR0, R5_ARG3, 9); + __ ble(CCR0, l_6); // copy 2 at a time + + if (!aligned) { + __ xorr(tmp1, R3_ARG1, R4_ARG2); + __ andi_(tmp1, tmp1, 3); + __ bne(CCR0, l_6); // if arrays don't have the same alignment mod 4, do 2 element copy + + // At this point it is guaranteed that both, from and to have the same alignment mod 4. + + // Copy 1 element if necessary to align to 4 bytes. + __ andi_(tmp1, R3_ARG1, 3); + __ beq(CCR0, l_2); + + __ lhz(tmp2, 0, R3_ARG1); + __ addi(R3_ARG1, R3_ARG1, 2); + __ sth(tmp2, 0, R4_ARG2); + __ addi(R4_ARG2, R4_ARG2, 2); + __ addi(R5_ARG3, R5_ARG3, -1); + __ bind(l_2); + + // At this point the positions of both, from and to, are at least 4 byte aligned. + + // Copy 4 elements at a time. + // Align to 8 bytes, but only if both, from and to, have same alignment mod 8. + __ xorr(tmp2, R3_ARG1, R4_ARG2); + __ andi_(tmp1, tmp2, 7); + __ bne(CCR0, l_7); // not same alignment mod 8 -> copy 4, either from or to will be unaligned + + // Copy a 2-element word if necessary to align to 8 bytes. + __ andi_(R0, R3_ARG1, 7); + __ beq(CCR0, l_7); + + __ lwzx(tmp2, R3_ARG1, tmp3); + __ addi(R5_ARG3, R5_ARG3, -2); + __ stwx(tmp2, R4_ARG2, tmp3); + { // FasterArrayCopy + __ addi(R3_ARG1, R3_ARG1, 4); + __ addi(R4_ARG2, R4_ARG2, 4); + } + } + + __ bind(l_7); + + // Copy 4 elements at a time; either the loads or the stores can + // be unaligned if aligned == false. + + { // FasterArrayCopy + __ cmpwi(CCR0, R5_ARG3, 15); + __ ble(CCR0, l_6); // copy 2 at a time if less than 16 elements remain + + __ srdi(tmp1, R5_ARG3, 4); + __ andi_(R5_ARG3, R5_ARG3, 15); + __ mtctr(tmp1); + + __ bind(l_8); + // Use unrolled version for mass copying (copy 16 elements a time). + // Load feeding store gets zero latency on Power6, however not on Power5. + // Therefore, the following sequence is made for the good of both. + __ ld(tmp1, 0, R3_ARG1); + __ ld(tmp2, 8, R3_ARG1); + __ ld(tmp3, 16, R3_ARG1); + __ ld(tmp4, 24, R3_ARG1); + __ std(tmp1, 0, R4_ARG2); + __ std(tmp2, 8, R4_ARG2); + __ std(tmp3, 16, R4_ARG2); + __ std(tmp4, 24, R4_ARG2); + __ addi(R3_ARG1, R3_ARG1, 32); + __ addi(R4_ARG2, R4_ARG2, 32); + __ bdnz(l_8); + } + __ bind(l_6); + + // copy 2 elements at a time + { // FasterArrayCopy + __ cmpwi(CCR0, R5_ARG3, 2); + __ blt(CCR0, l_1); + __ srdi(tmp1, R5_ARG3, 1); + __ andi_(R5_ARG3, R5_ARG3, 1); + + __ addi(R3_ARG1, R3_ARG1, -4); + __ addi(R4_ARG2, R4_ARG2, -4); + __ mtctr(tmp1); + + __ bind(l_3); + __ lwzu(tmp2, 4, R3_ARG1); + __ stwu(tmp2, 4, R4_ARG2); + __ bdnz(l_3); + + __ addi(R3_ARG1, R3_ARG1, 4); + __ addi(R4_ARG2, R4_ARG2, 4); + } + + // do single element copy + __ bind(l_1); + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_4); + + { // FasterArrayCopy + __ mtctr(R5_ARG3); + __ addi(R3_ARG1, R3_ARG1, -2); + __ addi(R4_ARG2, R4_ARG2, -2); + + __ bind(l_5); + __ lhzu(tmp2, 2, R3_ARG1); + __ sthu(tmp2, 2, R4_ARG2); + __ bdnz(l_5); + } + __ bind(l_4); + __ blr(); + + return start; + } + + // Generate stub for conjoint short copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_conjoint_short_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + +#if defined(ABI_ELFv2) + address nooverlap_target = aligned ? + StubRoutines::arrayof_jshort_disjoint_arraycopy() : + StubRoutines::jshort_disjoint_arraycopy(); +#else + address nooverlap_target = aligned ? + ((FunctionDescriptor*)StubRoutines::arrayof_jshort_disjoint_arraycopy())->entry() : + ((FunctionDescriptor*)StubRoutines::jshort_disjoint_arraycopy())->entry(); +#endif + + array_overlap_test(nooverlap_target, 1); + + Label l_1, l_2; + __ sldi(tmp1, R5_ARG3, 1); + __ b(l_2); + __ bind(l_1); + __ sthx(tmp2, R4_ARG2, tmp1); + __ bind(l_2); + __ addic_(tmp1, tmp1, -2); + __ lhzx(tmp2, R3_ARG1, tmp1); + __ bge(CCR0, l_1); + + __ blr(); + + return start; + } + + // Generate core code for disjoint int copy (and oop copy on 32-bit). If "aligned" + // is true, the "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + void generate_disjoint_int_copy_core(bool aligned) { + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + Register tmp4 = R0; + + Label l_1, l_2, l_3, l_4, l_5, l_6; + // for short arrays, just do single element copy + __ li(tmp3, 0); + __ cmpwi(CCR0, R5_ARG3, 5); + __ ble(CCR0, l_2); + + if (!aligned) { + // check if arrays have same alignment mod 8. + __ xorr(tmp1, R3_ARG1, R4_ARG2); + __ andi_(R0, tmp1, 7); + // Not the same alignment, but ld and std just need to be 4 byte aligned. + __ bne(CCR0, l_4); // to OR from is 8 byte aligned -> copy 2 at a time + + // copy 1 element to align to and from on an 8 byte boundary + __ andi_(R0, R3_ARG1, 7); + __ beq(CCR0, l_4); + + __ lwzx(tmp2, R3_ARG1, tmp3); + __ addi(R5_ARG3, R5_ARG3, -1); + __ stwx(tmp2, R4_ARG2, tmp3); + { // FasterArrayCopy + __ addi(R3_ARG1, R3_ARG1, 4); + __ addi(R4_ARG2, R4_ARG2, 4); + } + __ bind(l_4); + } + + { // FasterArrayCopy + __ cmpwi(CCR0, R5_ARG3, 7); + __ ble(CCR0, l_2); // copy 1 at a time if less than 8 elements remain + + __ srdi(tmp1, R5_ARG3, 3); + __ andi_(R5_ARG3, R5_ARG3, 7); + __ mtctr(tmp1); + + __ bind(l_6); + // Use unrolled version for mass copying (copy 8 elements a time). + // Load feeding store gets zero latency on power6, however not on power 5. + // Therefore, the following sequence is made for the good of both. + __ ld(tmp1, 0, R3_ARG1); + __ ld(tmp2, 8, R3_ARG1); + __ ld(tmp3, 16, R3_ARG1); + __ ld(tmp4, 24, R3_ARG1); + __ std(tmp1, 0, R4_ARG2); + __ std(tmp2, 8, R4_ARG2); + __ std(tmp3, 16, R4_ARG2); + __ std(tmp4, 24, R4_ARG2); + __ addi(R3_ARG1, R3_ARG1, 32); + __ addi(R4_ARG2, R4_ARG2, 32); + __ bdnz(l_6); + } + + // copy 1 element at a time + __ bind(l_2); + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_1); + + { // FasterArrayCopy + __ mtctr(R5_ARG3); + __ addi(R3_ARG1, R3_ARG1, -4); + __ addi(R4_ARG2, R4_ARG2, -4); + + __ bind(l_3); + __ lwzu(tmp2, 4, R3_ARG1); + __ stwu(tmp2, 4, R4_ARG2); + __ bdnz(l_3); + } + + __ bind(l_1); + return; + } + + // Generate stub for disjoint int copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_disjoint_int_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + generate_disjoint_int_copy_core(aligned); + __ blr(); + return start; + } + + // Generate core code for conjoint int copy (and oop copy on + // 32-bit). If "aligned" is true, the "from" and "to" addresses + // are assumed to be heapword aligned. + // + // Arguments: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + void generate_conjoint_int_copy_core(bool aligned) { + // Do reverse copy. We assume the case of actual overlap is rare enough + // that we don't have to optimize it. + + Label l_1, l_2, l_3, l_4, l_5, l_6; + + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + Register tmp4 = R0; + + { // FasterArrayCopy + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_6); + + __ sldi(R5_ARG3, R5_ARG3, 2); + __ add(R3_ARG1, R3_ARG1, R5_ARG3); + __ add(R4_ARG2, R4_ARG2, R5_ARG3); + __ srdi(R5_ARG3, R5_ARG3, 2); + + __ cmpwi(CCR0, R5_ARG3, 7); + __ ble(CCR0, l_5); // copy 1 at a time if less than 8 elements remain + + __ srdi(tmp1, R5_ARG3, 3); + __ andi(R5_ARG3, R5_ARG3, 7); + __ mtctr(tmp1); + + __ bind(l_4); + // Use unrolled version for mass copying (copy 4 elements a time). + // Load feeding store gets zero latency on Power6, however not on Power5. + // Therefore, the following sequence is made for the good of both. + __ addi(R3_ARG1, R3_ARG1, -32); + __ addi(R4_ARG2, R4_ARG2, -32); + __ ld(tmp4, 24, R3_ARG1); + __ ld(tmp3, 16, R3_ARG1); + __ ld(tmp2, 8, R3_ARG1); + __ ld(tmp1, 0, R3_ARG1); + __ std(tmp4, 24, R4_ARG2); + __ std(tmp3, 16, R4_ARG2); + __ std(tmp2, 8, R4_ARG2); + __ std(tmp1, 0, R4_ARG2); + __ bdnz(l_4); + + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_6); + + __ bind(l_5); + __ mtctr(R5_ARG3); + __ bind(l_3); + __ lwz(R0, -4, R3_ARG1); + __ stw(R0, -4, R4_ARG2); + __ addi(R3_ARG1, R3_ARG1, -4); + __ addi(R4_ARG2, R4_ARG2, -4); + __ bdnz(l_3); + + __ bind(l_6); + } + } + + // Generate stub for conjoint int copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_conjoint_int_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + +#if defined(ABI_ELFv2) + address nooverlap_target = aligned ? + StubRoutines::arrayof_jint_disjoint_arraycopy() : + StubRoutines::jint_disjoint_arraycopy(); +#else + address nooverlap_target = aligned ? + ((FunctionDescriptor*)StubRoutines::arrayof_jint_disjoint_arraycopy())->entry() : + ((FunctionDescriptor*)StubRoutines::jint_disjoint_arraycopy())->entry(); +#endif + + array_overlap_test(nooverlap_target, 2); + + generate_conjoint_int_copy_core(aligned); + + __ blr(); + + return start; + } + + // Generate core code for disjoint long copy (and oop copy on + // 64-bit). If "aligned" is true, the "from" and "to" addresses + // are assumed to be heapword aligned. + // + // Arguments: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + void generate_disjoint_long_copy_core(bool aligned) { + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + Register tmp4 = R0; + + Label l_1, l_2, l_3, l_4; + + { // FasterArrayCopy + __ cmpwi(CCR0, R5_ARG3, 3); + __ ble(CCR0, l_3); // copy 1 at a time if less than 4 elements remain + + __ srdi(tmp1, R5_ARG3, 2); + __ andi_(R5_ARG3, R5_ARG3, 3); + __ mtctr(tmp1); + + __ bind(l_4); + // Use unrolled version for mass copying (copy 4 elements a time). + // Load feeding store gets zero latency on Power6, however not on Power5. + // Therefore, the following sequence is made for the good of both. + __ ld(tmp1, 0, R3_ARG1); + __ ld(tmp2, 8, R3_ARG1); + __ ld(tmp3, 16, R3_ARG1); + __ ld(tmp4, 24, R3_ARG1); + __ std(tmp1, 0, R4_ARG2); + __ std(tmp2, 8, R4_ARG2); + __ std(tmp3, 16, R4_ARG2); + __ std(tmp4, 24, R4_ARG2); + __ addi(R3_ARG1, R3_ARG1, 32); + __ addi(R4_ARG2, R4_ARG2, 32); + __ bdnz(l_4); + } + + // copy 1 element at a time + __ bind(l_3); + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_1); + + { // FasterArrayCopy + __ mtctr(R5_ARG3); + __ addi(R3_ARG1, R3_ARG1, -8); + __ addi(R4_ARG2, R4_ARG2, -8); + + __ bind(l_2); + __ ldu(R0, 8, R3_ARG1); + __ stdu(R0, 8, R4_ARG2); + __ bdnz(l_2); + + } + __ bind(l_1); + } + + // Generate stub for disjoint long copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_disjoint_long_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + generate_disjoint_long_copy_core(aligned); + __ blr(); + + return start; + } + + // Generate core code for conjoint long copy (and oop copy on + // 64-bit). If "aligned" is true, the "from" and "to" addresses + // are assumed to be heapword aligned. + // + // Arguments: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + void generate_conjoint_long_copy_core(bool aligned) { + Register tmp1 = R6_ARG4; + Register tmp2 = R7_ARG5; + Register tmp3 = R8_ARG6; + Register tmp4 = R0; + + Label l_1, l_2, l_3, l_4, l_5; + + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_1); + + { // FasterArrayCopy + __ sldi(R5_ARG3, R5_ARG3, 3); + __ add(R3_ARG1, R3_ARG1, R5_ARG3); + __ add(R4_ARG2, R4_ARG2, R5_ARG3); + __ srdi(R5_ARG3, R5_ARG3, 3); + + __ cmpwi(CCR0, R5_ARG3, 3); + __ ble(CCR0, l_5); // copy 1 at a time if less than 4 elements remain + + __ srdi(tmp1, R5_ARG3, 2); + __ andi(R5_ARG3, R5_ARG3, 3); + __ mtctr(tmp1); + + __ bind(l_4); + // Use unrolled version for mass copying (copy 4 elements a time). + // Load feeding store gets zero latency on Power6, however not on Power5. + // Therefore, the following sequence is made for the good of both. + __ addi(R3_ARG1, R3_ARG1, -32); + __ addi(R4_ARG2, R4_ARG2, -32); + __ ld(tmp4, 24, R3_ARG1); + __ ld(tmp3, 16, R3_ARG1); + __ ld(tmp2, 8, R3_ARG1); + __ ld(tmp1, 0, R3_ARG1); + __ std(tmp4, 24, R4_ARG2); + __ std(tmp3, 16, R4_ARG2); + __ std(tmp2, 8, R4_ARG2); + __ std(tmp1, 0, R4_ARG2); + __ bdnz(l_4); + + __ cmpwi(CCR0, R5_ARG3, 0); + __ beq(CCR0, l_1); + + __ bind(l_5); + __ mtctr(R5_ARG3); + __ bind(l_3); + __ ld(R0, -8, R3_ARG1); + __ std(R0, -8, R4_ARG2); + __ addi(R3_ARG1, R3_ARG1, -8); + __ addi(R4_ARG2, R4_ARG2, -8); + __ bdnz(l_3); + + } + __ bind(l_1); + } + + // Generate stub for conjoint long copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // + address generate_conjoint_long_copy(bool aligned, const char * name) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + +#if defined(ABI_ELFv2) + address nooverlap_target = aligned ? + StubRoutines::arrayof_jlong_disjoint_arraycopy() : + StubRoutines::jlong_disjoint_arraycopy(); +#else + address nooverlap_target = aligned ? + ((FunctionDescriptor*)StubRoutines::arrayof_jlong_disjoint_arraycopy())->entry() : + ((FunctionDescriptor*)StubRoutines::jlong_disjoint_arraycopy())->entry(); +#endif + + array_overlap_test(nooverlap_target, 3); + generate_conjoint_long_copy_core(aligned); + + __ blr(); + + return start; + } + + // Generate stub for conjoint oop copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // dest_uninitialized: G1 support + // + address generate_conjoint_oop_copy(bool aligned, const char * name, bool dest_uninitialized) { + StubCodeMark mark(this, "StubRoutines", name); + + address start = __ function_entry(); + +#if defined(ABI_ELFv2) + address nooverlap_target = aligned ? + StubRoutines::arrayof_oop_disjoint_arraycopy() : + StubRoutines::oop_disjoint_arraycopy(); +#else + address nooverlap_target = aligned ? + ((FunctionDescriptor*)StubRoutines::arrayof_oop_disjoint_arraycopy())->entry() : + ((FunctionDescriptor*)StubRoutines::oop_disjoint_arraycopy())->entry(); +#endif + + gen_write_ref_array_pre_barrier(R3_ARG1, R4_ARG2, R5_ARG3, dest_uninitialized, R9_ARG7); + + // Save arguments. + __ mr(R9_ARG7, R4_ARG2); + __ mr(R10_ARG8, R5_ARG3); + + if (UseCompressedOops) { + array_overlap_test(nooverlap_target, 2); + generate_conjoint_int_copy_core(aligned); + } else { + array_overlap_test(nooverlap_target, 3); + generate_conjoint_long_copy_core(aligned); + } + + gen_write_ref_array_post_barrier(R9_ARG7, R10_ARG8, R11_scratch1, /*branchToEnd*/ false); + return start; + } + + // Generate stub for disjoint oop copy. If "aligned" is true, the + // "from" and "to" addresses are assumed to be heapword aligned. + // + // Arguments for generated stub: + // from: R3_ARG1 + // to: R4_ARG2 + // count: R5_ARG3 treated as signed + // dest_uninitialized: G1 support + // + address generate_disjoint_oop_copy(bool aligned, const char * name, bool dest_uninitialized) { + StubCodeMark mark(this, "StubRoutines", name); + address start = __ function_entry(); + + gen_write_ref_array_pre_barrier(R3_ARG1, R4_ARG2, R5_ARG3, dest_uninitialized, R9_ARG7); + + // save some arguments, disjoint_long_copy_core destroys them. + // needed for post barrier + __ mr(R9_ARG7, R4_ARG2); + __ mr(R10_ARG8, R5_ARG3); + + if (UseCompressedOops) { + generate_disjoint_int_copy_core(aligned); + } else { + generate_disjoint_long_copy_core(aligned); + } + + gen_write_ref_array_post_barrier(R9_ARG7, R10_ARG8, R11_scratch1, /*branchToEnd*/ false); + + return start; + } + + void generate_arraycopy_stubs() { + // Note: the disjoint stubs must be generated first, some of + // the conjoint stubs use them. + + // non-aligned disjoint versions + StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(false, "jbyte_disjoint_arraycopy"); + StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(false, "jshort_disjoint_arraycopy"); + StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_int_copy(false, "jint_disjoint_arraycopy"); + StubRoutines::_jlong_disjoint_arraycopy = generate_disjoint_long_copy(false, "jlong_disjoint_arraycopy"); + StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_oop_copy(false, "oop_disjoint_arraycopy", false); + StubRoutines::_oop_disjoint_arraycopy_uninit = generate_disjoint_oop_copy(false, "oop_disjoint_arraycopy_uninit", true); + + // aligned disjoint versions + StubRoutines::_arrayof_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(true, "arrayof_jbyte_disjoint_arraycopy"); + StubRoutines::_arrayof_jshort_disjoint_arraycopy = generate_disjoint_short_copy(true, "arrayof_jshort_disjoint_arraycopy"); + StubRoutines::_arrayof_jint_disjoint_arraycopy = generate_disjoint_int_copy(true, "arrayof_jint_disjoint_arraycopy"); + StubRoutines::_arrayof_jlong_disjoint_arraycopy = generate_disjoint_long_copy(true, "arrayof_jlong_disjoint_arraycopy"); + StubRoutines::_arrayof_oop_disjoint_arraycopy = generate_disjoint_oop_copy(true, "arrayof_oop_disjoint_arraycopy", false); + StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit = generate_disjoint_oop_copy(true, "oop_disjoint_arraycopy_uninit", true); + + // non-aligned conjoint versions + StubRoutines::_jbyte_arraycopy = generate_conjoint_byte_copy(false, "jbyte_arraycopy"); + StubRoutines::_jshort_arraycopy = generate_conjoint_short_copy(false, "jshort_arraycopy"); + StubRoutines::_jint_arraycopy = generate_conjoint_int_copy(false, "jint_arraycopy"); + StubRoutines::_jlong_arraycopy = generate_conjoint_long_copy(false, "jlong_arraycopy"); + StubRoutines::_oop_arraycopy = generate_conjoint_oop_copy(false, "oop_arraycopy", false); + StubRoutines::_oop_arraycopy_uninit = generate_conjoint_oop_copy(false, "oop_arraycopy_uninit", true); + + // aligned conjoint versions + StubRoutines::_arrayof_jbyte_arraycopy = generate_conjoint_byte_copy(true, "arrayof_jbyte_arraycopy"); + StubRoutines::_arrayof_jshort_arraycopy = generate_conjoint_short_copy(true, "arrayof_jshort_arraycopy"); + StubRoutines::_arrayof_jint_arraycopy = generate_conjoint_int_copy(true, "arrayof_jint_arraycopy"); + StubRoutines::_arrayof_jlong_arraycopy = generate_conjoint_long_copy(true, "arrayof_jlong_arraycopy"); + StubRoutines::_arrayof_oop_arraycopy = generate_conjoint_oop_copy(true, "arrayof_oop_arraycopy", false); + StubRoutines::_arrayof_oop_arraycopy_uninit = generate_conjoint_oop_copy(true, "arrayof_oop_arraycopy", true); + + // fill routines + StubRoutines::_jbyte_fill = generate_fill(T_BYTE, false, "jbyte_fill"); + StubRoutines::_jshort_fill = generate_fill(T_SHORT, false, "jshort_fill"); + StubRoutines::_jint_fill = generate_fill(T_INT, false, "jint_fill"); + StubRoutines::_arrayof_jbyte_fill = generate_fill(T_BYTE, true, "arrayof_jbyte_fill"); + StubRoutines::_arrayof_jshort_fill = generate_fill(T_SHORT, true, "arrayof_jshort_fill"); + StubRoutines::_arrayof_jint_fill = generate_fill(T_INT, true, "arrayof_jint_fill"); + } + + // Safefetch stubs. + void generate_safefetch(const char* name, int size, address* entry, address* fault_pc, address* continuation_pc) { + // safefetch signatures: + // int SafeFetch32(int* adr, int errValue); + // intptr_t SafeFetchN (intptr_t* adr, intptr_t errValue); + // + // arguments: + // R3_ARG1 = adr + // R4_ARG2 = errValue + // + // result: + // R3_RET = *adr or errValue + + StubCodeMark mark(this, "StubRoutines", name); + + // Entry point, pc or function descriptor. + *entry = __ function_entry(); + + // Load *adr into R4_ARG2, may fault. + *fault_pc = __ pc(); + switch (size) { + case 4: + // int32_t, signed extended + __ lwa(R4_ARG2, 0, R3_ARG1); + break; + case 8: + // int64_t + __ ld(R4_ARG2, 0, R3_ARG1); + break; + default: + ShouldNotReachHere(); + } + + // return errValue or *adr + *continuation_pc = __ pc(); + __ mr(R3_RET, R4_ARG2); + __ blr(); + } + + // Initialization + void generate_initial() { + // Generates all stubs and initializes the entry points + + // Entry points that exist in all platforms. + // Note: This is code that could be shared among different platforms - however the + // benefit seems to be smaller than the disadvantage of having a + // much more complicated generator structure. See also comment in + // stubRoutines.hpp. + + StubRoutines::_forward_exception_entry = generate_forward_exception(); + StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address); + StubRoutines::_catch_exception_entry = generate_catch_exception(); + + // Build this early so it's available for the interpreter. + StubRoutines::_throw_StackOverflowError_entry = + generate_throw_exception("StackOverflowError throw_exception", + CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError), false); + } + + void generate_all() { + // Generates all stubs and initializes the entry points + + // These entry points require SharedInfo::stack0 to be set up in + // non-core builds + StubRoutines::_throw_AbstractMethodError_entry = generate_throw_exception("AbstractMethodError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_AbstractMethodError), false); + // Handle IncompatibleClassChangeError in itable stubs. + StubRoutines::_throw_IncompatibleClassChangeError_entry= generate_throw_exception("IncompatibleClassChangeError throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_IncompatibleClassChangeError), false); + StubRoutines::_throw_NullPointerException_at_call_entry= generate_throw_exception("NullPointerException at call throw_exception", CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call), false); + + StubRoutines::_handler_for_unsafe_access_entry = generate_handler_for_unsafe_access(); + + // support for verify_oop (must happen after universe_init) + StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); + + // arraycopy stubs used by compilers + generate_arraycopy_stubs(); + + // Safefetch stubs. + generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry, + &StubRoutines::_safefetch32_fault_pc, + &StubRoutines::_safefetch32_continuation_pc); + generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, + &StubRoutines::_safefetchN_fault_pc, + &StubRoutines::_safefetchN_continuation_pc); + } + + public: + StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { + // replace the standard masm with a special one: + _masm = new MacroAssembler(code); + if (all) { + generate_all(); + } else { + generate_initial(); + } + } +}; + +void StubGenerator_generate(CodeBuffer* code, bool all) { + StubGenerator g(code, all); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/stubRoutines_ppc_64.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/stubRoutines_ppc_64.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +// Implementation of the platform-specific part of StubRoutines - for +// a description of how to extend it, see the stubRoutines.hpp file. + + diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/stubRoutines_ppc_64.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/stubRoutines_ppc_64.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_STUBROUTINES_PPC_64_HPP +#define CPU_PPC_VM_STUBROUTINES_PPC_64_HPP + +// This file holds the platform specific parts of the StubRoutines +// definition. See stubRoutines.hpp for a description on how to +// extend it. + +static bool returns_to_call_stub(address return_pc) { return return_pc == _call_stub_return_address; } + +enum platform_dependent_constants { + code_size1 = 20000, // simply increase if too small (assembler will crash if too small) + code_size2 = 20000 // simply increase if too small (assembler will crash if too small) +}; + +#endif // CPU_PPC_VM_STUBROUTINES_PPC_64_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_TEMPLATEINTERPRETERGENERATOR_PPC_HPP +#define CPU_PPC_VM_TEMPLATEINTERPRETERGENERATOR_PPC_HPP + + protected: + address generate_normal_entry(bool synchronized); + address generate_native_entry(bool synchronized); + address generate_math_entry(AbstractInterpreter::MethodKind kind); + address generate_empty_entry(void); + + void lock_method(Register Rflags, Register Rscratch1, Register Rscratch2, bool flags_preloaded=false); + void unlock_method(bool check_exceptions = true); + + void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); + void generate_counter_overflow(Label& continue_entry); + + void generate_fixed_frame(bool native_call, Register Rsize_of_parameters, Register Rsize_of_locals); + void generate_stack_overflow_check(Register Rframe_size, Register Rscratch1); + +#endif // CPU_PPC_VM_TEMPLATEINTERPRETERGENERATOR_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/templateInterpreter_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/templateInterpreter_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,1874 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#ifndef CC_INTERP +#include "macroAssembler_ppc.inline.hpp" +#include "interpreter/bytecodeHistogram.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterGenerator.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "interpreter/templateTable.hpp" +#include "oops/arrayOop.hpp" +#include "oops/methodDataOop.hpp" +#include "oops/methodOop.hpp" +#include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "runtime/arguments.hpp" +#include "runtime/deoptimization.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/globals.hpp" +#include "opto/c2_globals.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" +#include "runtime/timer.hpp" +#include "runtime/vframeArray.hpp" +#include "utilities/debug.hpp" +#include "utilities/macros.hpp" + +#undef __ +#define __ _masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) /* nothing */ +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif + +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +//----------------------------------------------------------------------------- + +// Actually we should never reach here since we do stack overflow checks before pushing any frame. +address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { + address entry = __ pc(); + __ unimplemented("generate_StackOverflowError_handler"); + return entry; +} + +address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { + address entry = __ pc(); + __ empty_expression_stack(); + __ load_const_optimized(R4_ARG2, (address) name); + // Index is in R17_tos. + __ mr(R5_ARG3, R17_tos); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException)); + return entry; +} + +#if 0 +// Call special ClassCastException constructor taking object to cast +// and target class as arguments. +address TemplateInterpreterGenerator::generate_ClassCastException_verbose_handler() { + address entry = __ pc(); + + // Expression stack must be empty before entering the VM if an + // exception happened. + __ empty_expression_stack(); + + // Thread will be loaded to R3_ARG1. + // Target class oop is in register R5_ARG3 by convention! + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException_verbose, R17_tos, R5_ARG3)); + // Above call must not return here since exception pending. + DEBUG_ONLY(__ should_not_reach_here();) + return entry; +} +#endif + +address TemplateInterpreterGenerator::generate_ClassCastException_handler() { + address entry = __ pc(); + // Expression stack must be empty before entering the VM if an + // exception happened. + __ empty_expression_stack(); + + // Load exception object. + // Thread will be loaded to R3_ARG1. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException), R17_tos); +#ifdef ASSERT + // Above call must not return here since exception pending. + __ should_not_reach_here(); +#endif + return entry; +} + +address TemplateInterpreterGenerator::generate_exception_handler_common(const char* name, const char* message, bool pass_oop) { + address entry = __ pc(); + //__ untested("generate_exception_handler_common"); + Register Rexception = R17_tos; + + // Expression stack must be empty before entering the VM if an exception happened. + __ empty_expression_stack(); + + __ load_const_optimized(R4_ARG2, (address) name, R11_scratch1); + if (pass_oop) { + __ mr(R5_ARG3, Rexception); + __ call_VM(Rexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_klass_exception), false); + } else { + __ load_const_optimized(R5_ARG3, (address) message, R11_scratch1); + __ call_VM(Rexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), false); + } + + // Throw exception. + __ mr(R3_ARG1, Rexception); + __ load_const_optimized(R11_scratch1, Interpreter::throw_exception_entry(), R12_scratch2); + __ mtctr(R11_scratch1); + __ bctr(); + + return entry; +} + +address TemplateInterpreterGenerator::generate_continuation_for(TosState state) { + address entry = __ pc(); + __ unimplemented("generate_continuation_for"); + return entry; +} + +// This entry is returned to when a call returns to the interpreter. +// When we arrive here, we expect that the callee stack frame is already popped. +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { + + address entry = __ pc(); + + // Move the value out of the return register back to the TOS cache of current frame. + switch (state) { + case ltos: + case btos: + case ztos: + case ctos: + case stos: + case atos: + case itos: __ mr(R17_tos, R3_RET); break; // RET -> TOS cache + case ftos: + case dtos: __ fmr(F15_ftos, F1_RET); break; // TOS cache -> GR_FRET + case vtos: break; // Nothing to do, this was a void return. + default : ShouldNotReachHere(); + } + + __ restore_interpreter_state(R11_scratch1); // Sets R11_scratch1 = fp. + __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1); + __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); + + // Compiled code destroys templateTableBase, reload. + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R12_scratch2); + + const Register cache = R11_scratch1; + const Register size = R12_scratch2; + Label Lgot_cache, Lgiant_index; + + if (EnableInvokeDynamic){ + __ lbz(R11_scratch1, 0, R14_bcp); + __ cmpdi(CCR0, R11_scratch1, Bytecodes::_invokedynamic); + __ beq(CCR0, Lgiant_index); + } + + __ get_cache_and_index_at_bcp(cache, 1); + + __ bind(Lgot_cache); + // Get least significant byte of 64 bit value: +#if defined(VM_LITTLE_ENDIAN) + __ lbz(size, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset()), cache); +#else + __ lbz(size, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset()) + 7, cache); +#endif + + __ sldi(size, size, Interpreter::logStackElementSize); + __ add(R15_esp, R15_esp, size); + __ dispatch_next(state, step); + + if (EnableInvokeDynamic){ + __ bind(Lgiant_index); + __ get_cache_and_index_at_bcp(cache, 1, sizeof(u4)); + __ b(Lgot_cache); + } + + return entry; +} + +address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, int step) { + address entry = __ pc(); + // If state != vtos, we're returning from a native method, which put it's result + // into the result register. So move the value out of the return register back + // to the TOS cache of current frame. + + switch (state) { + case ltos: + case btos: + case ztos: + case ctos: + case stos: + case atos: + case itos: __ mr(R17_tos, R3_RET); break; // GR_RET -> TOS cache + case ftos: + case dtos: __ fmr(F15_ftos, F1_RET); break; // TOS cache -> GR_FRET + case vtos: break; // Nothing to do, this was a void return. + default : ShouldNotReachHere(); + } + + // Load LcpoolCache @@@ should be already set! + __ get_constant_pool_cache(R27_constPoolCache); + + // Handle a pending exception, fall through if none. + __ check_and_forward_exception(R11_scratch1, R12_scratch2); + + // Start executing bytecodes. + __ dispatch_next(state, step); + + return entry; +} + +// A result handler converts the native result into java format. +// Use the shared code between c++ and template interpreter. +address TemplateInterpreterGenerator::generate_result_handler_for(BasicType type) { + return AbstractInterpreterGenerator::generate_result_handler_for(type); +} + +address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, address runtime_entry) { + address entry = __ pc(); + + __ push(state); + __ call_VM(noreg, runtime_entry); + __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos)); + + return entry; +} + +// Helpers for commoning out cases in the various type of method entries. + +// Increment invocation count & check for overflow. +// +// Note: checking for negative value instead of overflow +// so we have a 'sticky' overflow test. +// +void TemplateInterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) { + // Note: In tiered we increment either counters in methodOop or in MDO depending if we're profiling or not. + Register Rscratch1 = R11_scratch1; + Register Rscratch2 = R12_scratch2; + Register R3_counters = R3_ARG1; + Label done; + + if (TieredCompilation) { + const int increment = InvocationCounter::count_increment; + const int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift; + Label no_mdo, done; + if (ProfileInterpreter) { + const Register Rmdo = Rscratch1; + // If no method data exists, go to profile_continue. + __ ld(Rmdo, in_bytes(methodOopDesc::method_data_offset()), R19_method); + __ cmpdi(CCR0, Rmdo, 0); + __ beq(CCR0, no_mdo); + + // Increment backedge counter in the MDO. + const int mdo_bc_offs = in_bytes(methodDataOopDesc::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()); + __ lwz(Rscratch2, mdo_bc_offs, Rmdo); + __ addi(Rscratch2, Rscratch2, increment); + __ stw(Rscratch2, mdo_bc_offs, Rmdo); + __ load_const_optimized(Rscratch1, mask, R0); + __ and_(Rscratch1, Rscratch2, Rscratch1); + __ bne(CCR0, done); + __ b(*overflow); + } + + // Increment counter in methodOop + const int mo_bc_offs = in_bytes(methodOopDesc::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()); + __ bind(no_mdo); + __ lwz(Rscratch2, mo_bc_offs, R19_method); + __ addi(Rscratch2, Rscratch2, increment); + __ stw(Rscratch2, mo_bc_offs, R19_method); + __ load_const_optimized(Rscratch1, mask, R0); + __ and_(Rscratch1, Rscratch2, Rscratch1); + __ beq(CCR0, *overflow); + + __ bind(done); + + } else { + + // Update standard invocation counters. + Register Rsum_ivc_bec = R4_ARG2; + __ increment_invocation_counter(Rsum_ivc_bec, R12_scratch2); + // Increment interpreter invocation counter. + if (ProfileInterpreter) { // %%% Merge this into methodDataOop. + Register interpreter_invocation_counter_addr = R11_scratch1; + __ lwz(R12_scratch2,in_bytes(methodOopDesc::interpreter_invocation_counter_offset()), R19_method); + __ addi(R12_scratch2, R12_scratch2, 1); + __ stw(R12_scratch2,in_bytes(methodOopDesc::interpreter_invocation_counter_offset()), R19_method); + } + // Check if we must create a method data obj. + if (ProfileInterpreter && profile_method != NULL) { + const Register profile_limit = Rscratch1; + int pl_offs = __ load_const_optimized(profile_limit, &InvocationCounter::InterpreterProfileLimit, R0, true); + __ lwz(profile_limit, pl_offs, profile_limit); + // Test to see if we should create a method data oop. + __ cmpw(CCR0, Rsum_ivc_bec, profile_limit); + __ blt(CCR0, *profile_method_continue); + // If no method data exists, go to profile_method. + __ test_method_data_pointer(*profile_method); + } + // Finally check for counter overflow. + if (overflow) { + const Register invocation_limit = Rscratch1; + int il_offs = __ load_const_optimized(invocation_limit, &InvocationCounter::InterpreterInvocationLimit, R0, true); + __ lwz(invocation_limit, il_offs, invocation_limit); + assert(4 == sizeof(InvocationCounter::InterpreterInvocationLimit), "unexpected field size"); + __ cmpw(CCR0, Rsum_ivc_bec, invocation_limit); + __ bge(CCR0, *overflow); + } + } +} + +// Generate code to initiate compilation on invocation counter overflow. +void TemplateInterpreterGenerator::generate_counter_overflow(Label& continue_entry) { + // Generate code to initiate compilation on the counter overflow. + + // InterpreterRuntime::frequency_counter_overflow takes one arguments, + // which indicates if the counter overflow occurs at a backwards branch (NULL bcp) + // We pass zero in. + // The call returns the address of the verified entry point for the method or NULL + // if the compilation did not complete (either went background or bailed out). + // + // Unlike the C++ interpreter above: Check exceptions! + // Assumption: Caller must set the flag "do_not_unlock_if_sychronized" if the monitor of a sync'ed + // method has not yet been created. Thus, no unlocking of a non-existing monitor can occur. + + __ li(R4_ARG2, 0); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), R4_ARG2, true); + + // Returns verified_entry_point or NULL. + // We ignore it in any case. + __ b(continue_entry); +} + +void TemplateInterpreterGenerator::generate_stack_overflow_check(Register Rmem_frame_size, Register Rscratch1) { + assert_different_registers(Rmem_frame_size, Rscratch1); + __ generate_stack_overflow_check_with_compare_and_throw(Rmem_frame_size, Rscratch1); +} + +void TemplateInterpreterGenerator::unlock_method(bool check_exceptions) { + __ unlock_object(R26_monitor, check_exceptions); +} + +// Lock the current method, interpreter register window must be set up! +void TemplateInterpreterGenerator::lock_method(Register Rflags, Register Rscratch1, Register Rscratch2, bool flags_preloaded) { + const Register Robj_to_lock = Rscratch2; + + { + if (!flags_preloaded) { + __ lwz(Rflags, method_(access_flags)); + } + +#ifdef ASSERT + // Check if methods needs synchronization. + { + Label Lok; + __ testbitdi(CCR0, R0, Rflags, JVM_ACC_SYNCHRONIZED_BIT); + __ btrue(CCR0,Lok); + __ stop("method doesn't need synchronization"); + __ bind(Lok); + } +#endif // ASSERT + } + + // Get synchronization object to Rscratch2. + { + const int mirror_offset = in_bytes(Klass::java_mirror_offset()); + Label Lstatic; + Label Ldone; + + __ testbitdi(CCR0, R0, Rflags, JVM_ACC_STATIC_BIT); + __ btrue(CCR0, Lstatic); + + // Non-static case: load receiver obj from stack and we're done. + __ ld(Robj_to_lock, R18_locals); + __ b(Ldone); + + __ bind(Lstatic); // Static case: Lock the java mirror + __ ld(Robj_to_lock, in_bytes(methodOopDesc::const_offset()), R19_method); + __ ld(Robj_to_lock, in_bytes(constMethodOopDesc::constants_offset()), Robj_to_lock); + __ ld(Robj_to_lock, constantPoolOopDesc::pool_holder_offset_in_bytes(), Robj_to_lock); + __ ld(Robj_to_lock, mirror_offset, Robj_to_lock); + + __ bind(Ldone); + __ verify_oop(Robj_to_lock); + } + + // Got the oop to lock => execute! + __ add_monitor_to_stack(true, Rscratch1, R0); + + __ std(Robj_to_lock, BasicObjectLock::obj_offset_in_bytes(), R26_monitor); + __ lock_object(R26_monitor, Robj_to_lock); +} + +// Generate a fixed interpreter frame for pure interpreter +// and I2N native transition frames. +// +// Before (stack grows downwards): +// +// | ... | +// |------------- | +// | java arg0 | +// | ... | +// | java argn | +// | | <- R15_esp +// | | +// |--------------| +// | abi_112 | +// | | <- R1_SP +// |==============| +// +// +// After: +// +// | ... | +// | java arg0 |<- R18_locals +// | ... | +// | java argn | +// |--------------| +// | | +// | java locals | +// | | +// |--------------| +// | abi_48 | +// |==============| +// | | +// | istate | +// | | +// |--------------| +// | monitor |<- R26_monitor +// |--------------| +// | |<- R15_esp +// | expression | +// | stack | +// | | +// |--------------| +// | | +// | abi_112 |<- R1_SP +// |==============| +// +// The top most frame needs an abi space of 112 bytes. This space is needed, +// since we call to c. The c function may spill their arguments to the caller +// frame. When we call to java, we don't need these spill slots. In order to save +// space on the stack, we resize the caller. However, java local reside in +// the caller frame and the frame has to be increased. The frame_size for the +// current frame was calculated based on max_stack as size for the expression +// stack. At the call, just a part of the expression stack might be used. +// We don't want to waste this space and cut the frame back accordingly. +// The resulting amount for resizing is calculated as follows: +// resize = (number_of_locals - number_of_arguments) * slot_size +// + (R1_SP - R15_esp) + 48 +// +// The size for the callee frame is calculated: +// framesize = 112 + max_stack + monitor + state_size +// +// maxstack: Max number of slots on the expression stack, loaded from the methodOop. +// monitor: We statically reserve room for one monitor object. +// state_size: We save the current state of the interpreter to this area. +// +void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call, Register Rsize_of_parameters, Register Rsize_of_locals) { + Register parent_frame_resize = R6_ARG4, // Frame will grow by this number of bytes. + top_frame_size = R7_ARG5, + Rconst_method = R8_ARG6; + + assert_different_registers(Rsize_of_parameters, Rsize_of_locals, parent_frame_resize, top_frame_size); + + __ lhz(Rsize_of_parameters /* number of params */, method_(size_of_parameters)); + if (native_call) { + // If we're calling a native method, we reserve space for the worst-case signature + // handler varargs vector, which is max(Argument::n_register_parameters, parameter_count+2). + // We add two slots to the parameter_count, one for the jni + // environment and one for a possible native mirror. + Label skip_native_calculate_max_stack; + __ addi(top_frame_size, Rsize_of_parameters, 2); + __ cmpwi(CCR0, top_frame_size, Argument::n_register_parameters); + __ bge(CCR0, skip_native_calculate_max_stack); + __ li(top_frame_size, Argument::n_register_parameters); + __ bind(skip_native_calculate_max_stack); + __ sldi(Rsize_of_parameters, Rsize_of_parameters, Interpreter::logStackElementSize); + __ sldi(top_frame_size, top_frame_size, Interpreter::logStackElementSize); + __ sub(parent_frame_resize, R1_SP, R15_esp); // <0, off by Interpreter::stackElementSize! + __ mr(Rsize_of_locals, Rsize_of_parameters); + } else { + __ lhz(Rsize_of_locals, method_(size_of_locals)); + __ sldi(Rsize_of_parameters, Rsize_of_parameters, Interpreter::logStackElementSize); + __ sldi(Rsize_of_locals, Rsize_of_locals, Interpreter::logStackElementSize); + __ lhz(top_frame_size, method_(max_stack)); + __ sub(R11_scratch1, Rsize_of_locals, Rsize_of_parameters); // >=0 + __ sub(parent_frame_resize, R1_SP, R15_esp); // <0, off by Interpreter::stackElementSize! + __ sldi(top_frame_size, top_frame_size, Interpreter::logStackElementSize); + __ add(parent_frame_resize, parent_frame_resize, R11_scratch1); + } + + // Compute top frame size. + __ addi(top_frame_size, top_frame_size, frame::abi_reg_args_size + frame::ijava_state_size); + + // Cut back area between esp and max_stack. + __ addi(parent_frame_resize, parent_frame_resize, frame::abi_minframe_size - Interpreter::stackElementSize); + + __ round_to(top_frame_size, frame::alignment_in_bytes); + __ round_to(parent_frame_resize, frame::alignment_in_bytes); + // parent_frame_resize = (locals-parameters) - (ESP-SP-ABI48) Rounded to frame alignment size. + // Enlarge by locals-parameters (not in case of native_call), shrink by ESP-SP-ABI48. + + { + // -------------------------------------------------------------------------- + // Stack overflow check + + Label cont; + __ add(R11_scratch1, parent_frame_resize, top_frame_size); + generate_stack_overflow_check(R11_scratch1, R12_scratch2); + } + + // Set up interpreter state registers. + + __ add(R18_locals, R15_esp, Rsize_of_parameters); + // set up R27_constPoolCache + __ ld(R27_constPoolCache, in_bytes(methodOopDesc::const_offset()), R19_method); + __ ld(R27_constPoolCache, in_bytes(constMethodOopDesc::constants_offset()), R27_constPoolCache); + __ ld(R27_constPoolCache, constantPoolOopDesc::cache_offset_in_bytes(), R27_constPoolCache); + + // Set method data pointer. + if (ProfileInterpreter) { + Label zero_continue; + __ ld(R28_mdx, method_(method_data)); + __ cmpdi(CCR0, R28_mdx, 0); + __ beq(CCR0, zero_continue); + __ addi(R28_mdx, R28_mdx, in_bytes(methodDataOopDesc::data_offset())); + __ bind(zero_continue); + } + + if (native_call) { + __ li(R14_bcp, 0); // Must initialize. + } else { + Register Rconst_method = R11_scratch1; + __ ld(Rconst_method, method_(const)) ; + __ add(R14_bcp, in_bytes(constMethodOopDesc::codes_offset()), Rconst_method); + } + + // Resize parent frame. + __ mflr(R12_scratch2); + __ neg(parent_frame_resize, parent_frame_resize); + __ resize_frame(parent_frame_resize, R11_scratch1); + __ std(R12_scratch2, _abi(lr), R1_SP); + + __ addi(R26_monitor, R1_SP, - frame::ijava_state_size); + __ addi(R15_esp, R26_monitor, - Interpreter::stackElementSize); + + // Store values. + // R15_esp, R14_bcp, R26_monitor, R28_mdx are saved at java calls + // in InterpreterMacroAssembler::call_from_interpreter. + __ std(R19_method, _ijava_state_neg(method), R1_SP); + __ std(R21_sender_SP, _ijava_state_neg(sender_sp), R1_SP); + __ std(R27_constPoolCache, _ijava_state_neg(cpoolCache), R1_SP); + __ std(R18_locals, _ijava_state_neg(locals), R1_SP); + + // Note: esp, bcp, monitor, mdx live in registers. Hence, the correct version can only + // be found in the frame after save_interpreter_state is done. This is always true + // for non-top frames. But when a signal occurs, dumping the top frame can go wrong, + // because e.g. frame::interpreter_frame_bcp() will not access the correct value + // (Enhanced Stack Trace). + // The signal handler does not save the interpreter state into the frame. + __ li(R0, 0); +#ifdef ASSERT + // Fill remaining slots with constants. + __ load_const_optimized(R11_scratch1, 0x5afe); + __ load_const_optimized(R12_scratch2, 0xdead); +#endif + // We have to initialize some frame slots for native calls (accessed by GC). + if (native_call) { + __ std(R26_monitor, _ijava_state_neg(monitors), R1_SP); + __ std(R14_bcp, _ijava_state_neg(bcp), R1_SP); + if (ProfileInterpreter) { __ std(R28_mdx, _ijava_state_neg(mdx), R1_SP); } + } +#ifdef ASSERT + else { + __ std(R12_scratch2, _ijava_state_neg(monitors), R1_SP); + __ std(R12_scratch2, _ijava_state_neg(bcp), R1_SP); + __ std(R12_scratch2, _ijava_state_neg(mdx), R1_SP); + } + __ std(R11_scratch1, _ijava_state_neg(ijava_reserved), R1_SP); + __ std(R12_scratch2, _ijava_state_neg(esp), R1_SP); + __ std(R12_scratch2, _ijava_state_neg(lresult), R1_SP); + __ std(R12_scratch2, _ijava_state_neg(fresult), R1_SP); +#endif + __ subf(R12_scratch2, top_frame_size, R1_SP); + __ std(R0, _ijava_state_neg(oop_tmp), R1_SP); + __ std(R12_scratch2, _ijava_state_neg(top_frame_sp), R1_SP); + + // Push top frame. + __ push_frame(top_frame_size, R11_scratch1); +} + +// End of helpers + +// ============================================================================ +// Various method entries +// + +// Empty method, generate a very fast return. We must skip this entry if +// someone's debugging, indicated by the flag +// "interp_mode" in the Thread obj. +// Note: empty methods are generated mostly methods that do assertions, which are +// disabled in the "java opt build". +address TemplateInterpreterGenerator::generate_empty_entry(void) { + if (!UseFastEmptyMethods) { + NOT_PRODUCT(__ should_not_reach_here();) + return Interpreter::entry_for_kind(Interpreter::zerolocals); + } + + Label Lslow_path; + const Register Rjvmti_mode = R11_scratch1; + address entry = __ pc(); + + __ lwz(Rjvmti_mode, thread_(interp_only_mode)); + __ cmpwi(CCR0, Rjvmti_mode, 0); + __ bne(CCR0, Lslow_path); // jvmti_mode!=0 + + // Noone's debuggin: Simply return. + // Pop c2i arguments (if any) off when we return. +#ifdef ASSERT + __ ld(R9_ARG7, 0, R1_SP); + __ ld(R10_ARG8, 0, R21_sender_SP); + __ cmpd(CCR0, R9_ARG7, R10_ARG8); + __ asm_assert_eq("backlink", 0x545); +#endif // ASSERT + __ mr(R1_SP, R21_sender_SP); // Cut the stack back to where the caller started. + + // And we're done. + __ blr(); + + __ bind(Lslow_path); + assert(Interpreter::entry_for_kind(Interpreter::zerolocals), "Normal entry must have been generated by now"); + __ load_const_optimized(R11_scratch1, Interpreter::entry_for_kind(Interpreter::zerolocals), R12_scratch2); + __ mtctr(R11_scratch1); + __ bctr(); + __ flush(); + + return entry; +} + +// Support abs and sqrt like in compiler. +// For others we can use a normal (native) entry. + +inline bool math_entry_available(AbstractInterpreter::MethodKind kind) { + if (!InlineIntrinsics) return false; // Generate a vanilla entry. + + return ((kind==Interpreter::java_lang_math_sqrt && VM_Version::has_fsqrt()) || + (kind==Interpreter::java_lang_math_abs)); +} + +address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) { + if (!math_entry_available(kind)) return NULL; + + Label Lslow_path; + const Register Rjvmti_mode = R11_scratch1; + address entry = __ pc(); + + // Provide math entry with debugging on demand. + __ lwz(Rjvmti_mode, thread_(interp_only_mode)); + __ cmpwi(CCR0, Rjvmti_mode, 0); + __ bne(CCR0, Lslow_path); // jvmti_mode!=0 + + __ lfd(F1_RET, Interpreter::stackElementSize, R15_esp); + + // Pop c2i arguments (if any) off when we return. +#ifdef ASSERT + __ ld(R9_ARG7, 0, R1_SP); + __ ld(R10_ARG8, 0, R21_sender_SP); + __ cmpd(CCR0, R9_ARG7, R10_ARG8); + __ asm_assert_eq("backlink", 0x545); +#endif // ASSERT + __ mr(R1_SP, R21_sender_SP); // Cut the stack back to where the caller started. + + if (kind == Interpreter::java_lang_math_sqrt) { + __ fsqrt(F1_RET, F1_RET); + } else if (kind == Interpreter::java_lang_math_abs) { + __ fabs(F1_RET, F1_RET); + } else { + ShouldNotReachHere(); + } + + // And we're done. + __ blr(); + + // Provide slow path for JVMTI case. + __ bind(Lslow_path); + assert(Interpreter::entry_for_kind(Interpreter::zerolocals), "Normal entry must have been generated by now"); + __ load_const_optimized(R11_scratch1, Interpreter::entry_for_kind(Interpreter::zerolocals), R12_scratch2); + __ mtctr(R11_scratch1); + __ bctr(); + __ flush(); + + return entry; +} + +// Interpreter stub for calling a native method. (asm interpreter) +// This sets up a somewhat different looking stack for calling the +// native method than the typical interpreter frame setup. +// +// On entry: +// R19_method - methodOop +// R16_thread - JavaThread* +// R15_esp - intptr_t* sender tos +// +// abstract stack (grows up) +// [ IJava (caller of JNI callee) ] <-- ASP +// ... +address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { + + address entry = __ pc(); + + const bool inc_counter = UseCompiler || CountCompiledCalls; + __ verify_oop(R19_method); + + // ----------------------------------------------------------------------------- + // Allocate a new frame that represents the native callee (i2n frame). + // This is not a full-blown interpreter frame, but in particular, the + // following registers are valid after this: + // - R19_method + // - R18_local (points to start of argumuments to native function) + // + // abstract stack (grows up) + // [ IJava (caller of JNI callee) ] <-- ASP + // ... + + const Register signature_handler_fd = R11_scratch1; + const Register pending_exception = R0; + const Register result_handler_addr = R31; + const Register native_method_fd = R11_scratch1; + const Register access_flags = R22_tmp2; + const Register active_handles = R11_scratch1; // R26_monitor saved to state. + const Register sync_state = R12_scratch2; + const Register sync_state_addr = sync_state; // Address is dead after use. + const Register suspend_flags = R11_scratch1; + + //============================================================================= + // Allocate new frame and initialize interpreter state. + + Label exception_return; + Label exception_return_sync_check; + Label stack_overflow_return; + + // Generate new interpreter state and jump to stack_overflow_return in case of + // a stack overflow. + //generate_compute_interpreter_state(stack_overflow_return); + + Register size_of_parameters = R22_tmp2; + + generate_fixed_frame(true, size_of_parameters, R23_tmp3 /* out unused */); + + //============================================================================= + // Increment invocation counter. On overflow, entry to JNI method + // will be compiled. + Label invocation_counter_overflow, continue_after_compile; + if (inc_counter) { + if (synchronized) { + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. If any exception was thrown by + // runtime, exception handling i.e. unlock_if_synchronized_method will + // check this thread local flag. + // This flag has two effects, one is to force an unwind in the topmost + // interpreter frame and not perform an unlock while doing so. + __ li(R0, 1); + __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); + } + generate_counter_incr(&invocation_counter_overflow, NULL, NULL); + + __ BIND(continue_after_compile); + // Reset the _do_not_unlock_if_synchronized flag. + if (synchronized) { + __ li(R0, 0); + __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); + } + } + + // access_flags = method->access_flags(); + // Load access flags. + assert(access_flags->is_nonvolatile(), + "access_flags must be in a non-volatile register"); + // Type check. + // TODO ppc port assert(4 == methodOopDesc::sz_access_flags(), "unexpected field size"); + __ lwz(access_flags, method_(access_flags)); + + // We don't want to reload R19_method and access_flags after calls + // to some helper functions. + assert(R19_method->is_nonvolatile(), + "R19_method must be a non-volatile register"); + + // Check for synchronized methods. Must happen AFTER invocation counter + // check, so method is not locked if counter overflows. + + if (synchronized) { + lock_method(access_flags, R11_scratch1, R12_scratch2, true); + + // Update monitor in state. + __ ld(R11_scratch1, 0, R1_SP); + __ std(R26_monitor, _ijava_state_neg(monitors), R11_scratch1); + } + + // jvmti/jvmpi support + __ notify_method_entry(); + + //============================================================================= + // Get and call the signature handler. + + __ ld(signature_handler_fd, method_(signature_handler)); + Label call_signature_handler; + + __ cmpdi(CCR0, signature_handler_fd, 0); + __ bne(CCR0, call_signature_handler); + + // Method has never been called. Either generate a specialized + // handler or point to the slow one. + // + // Pass parameter 'false' to avoid exception check in call_VM. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), R19_method, false); + + // Check for an exception while looking up the target method. If we + // incurred one, bail. + __ ld(pending_exception, thread_(pending_exception)); + __ cmpdi(CCR0, pending_exception, 0); + __ bne(CCR0, exception_return_sync_check); // Has pending exception. + + // Reload signature handler, it may have been created/assigned in the meanwhile. + __ ld(signature_handler_fd, method_(signature_handler)); + __ twi_0(signature_handler_fd); // Order wrt. load of klass mirror and entry point (isync is below). + + __ BIND(call_signature_handler); + + // Before we call the signature handler we push a new frame to + // protect the interpreter frame volatile registers when we return + // from jni but before we can get back to Java. + + // First set the frame anchor while the SP/FP registers are + // convenient and the slow signature handler can use this same frame + // anchor. + + // We have a TOP_IJAVA_FRAME here, which belongs to us. + __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R12_scratch2/*tmp*/); + + // Now the interpreter frame (and its call chain) have been + // invalidated and flushed. We are now protected against eager + // being enabled in native code. Even if it goes eager the + // registers will be reloaded as clean and we will invalidate after + // the call so no spurious flush should be possible. + + // Call signature handler and pass locals address. + // + // Our signature handlers copy required arguments to the C stack + // (outgoing C args), R3_ARG1 to R10_ARG8, and FARG1 to FARG13. + __ mr(R3_ARG1, R18_locals); +#if !defined(ABI_ELFv2) + __ ld(signature_handler_fd, 0, signature_handler_fd); +#endif + + __ call_stub(signature_handler_fd); + // Reload method, it may have moved. + __ ld(R19_method, 0, R1_SP); + __ ld(R19_method, _ijava_state_neg(method), R19_method); + + // Remove the register parameter varargs slots we allocated in + // compute_interpreter_state. SP+16 ends up pointing to the ABI + // outgoing argument area. + // + // Not needed on PPC64. + //__ add(SP, SP, Argument::n_register_parameters*BytesPerWord); + + assert(result_handler_addr->is_nonvolatile(), "result_handler_addr must be in a non-volatile register"); + // Save across call to native method. + __ mr(result_handler_addr, R3_RET); + + __ isync(); // Acquire signature handler before trying to fetch the native entry point and klass mirror. + + // Set up fixed parameters and call the native method. + // If the method is static, get mirror into R4_ARG2. + { + Label method_is_not_static; + // Access_flags is non-volatile and still, no need to restore it. + + // Restore access flags. + __ testbitdi(CCR0, R0, access_flags, JVM_ACC_STATIC_BIT); + __ bfalse(CCR0, method_is_not_static); + + // constants = method->constants(); + __ ld(R11_scratch1, in_bytes(methodOopDesc::const_offset()), R19_method); + __ ld(R11_scratch1, in_bytes(constMethodOopDesc::constants_offset()), R11_scratch1); + // pool_holder = method->constants()->pool_holder(); + __ ld(R11_scratch1/*pool_holder*/, constantPoolOopDesc::pool_holder_offset_in_bytes(), + R11_scratch1/*constants*/); + + const int mirror_offset = in_bytes(Klass::java_mirror_offset()); + + // mirror = pool_holder->klass_part()->java_mirror(); + __ ld(R0/*mirror*/, mirror_offset, R11_scratch1/*pool_holder*/); + // state->_native_mirror = mirror; + + __ ld(R11_scratch1, 0, R1_SP); + __ std(R0/*mirror*/, _ijava_state_neg(oop_tmp), R11_scratch1); + // R4_ARG2 = &state->_oop_temp; + __ addi(R4_ARG2, R11_scratch1, _ijava_state_neg(oop_tmp)); + __ BIND(method_is_not_static); + } + + // At this point, arguments have been copied off the stack into + // their JNI positions. Oops are boxed in-place on the stack, with + // handles copied to arguments. The result handler address is in a + // register. + + // Pass JNIEnv address as first parameter. + __ addir(R3_ARG1, thread_(jni_environment)); + + // Load the native_method entry before we change the thread state. + __ ld(native_method_fd, method_(native_function)); + + //============================================================================= + // Transition from _thread_in_Java to _thread_in_native. As soon as + // we make this change the safepoint code needs to be certain that + // the last Java frame we established is good. The pc in that frame + // just needs to be near here not an actual return address. + + // We use release_store_fence to update values like the thread state, where + // we don't want the current thread to continue until all our prior memory + // accesses (including the new thread state) are visible to other threads. + __ li(R0, _thread_in_native); + __ release(); + + // TODO PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size"); + __ stw(R0, thread_(thread_state)); + + if (UseMembar) { + __ fence(); + } + + //============================================================================= + // Call the native method. Argument registers must not have been + // overwritten since "__ call_stub(signature_handler);" (except for + // ARG1 and ARG2 for static methods). + __ call_c(native_method_fd); + + __ li(R0, 0); + __ ld(R11_scratch1, 0, R1_SP); + __ std(R3_RET, _ijava_state_neg(lresult), R11_scratch1); + __ stfd(F1_RET, _ijava_state_neg(fresult), R11_scratch1); + __ std(R0/*mirror*/, _ijava_state_neg(oop_tmp), R11_scratch1); // reset + + // Note: C++ interpreter needs the following here: + // The frame_manager_lr field, which we use for setting the last + // java frame, gets overwritten by the signature handler. Restore + // it now. + //__ get_PC_trash_LR(R11_scratch1); + //__ std(R11_scratch1, _top_ijava_frame_abi(frame_manager_lr), R1_SP); + + // Because of GC R19_method may no longer be valid. + + // Block, if necessary, before resuming in _thread_in_Java state. + // In order for GC to work, don't clear the last_Java_sp until after + // blocking. + + //============================================================================= + // Switch thread to "native transition" state before reading the + // synchronization state. This additional state is necessary + // because reading and testing the synchronization state is not + // atomic w.r.t. GC, as this scenario demonstrates: Java thread A, + // in _thread_in_native state, loads _not_synchronized and is + // preempted. VM thread changes sync state to synchronizing and + // suspends threads for GC. Thread A is resumed to finish this + // native method, but doesn't block here since it didn't see any + // synchronization in progress, and escapes. + + // We use release_store_fence to update values like the thread state, where + // we don't want the current thread to continue until all our prior memory + // accesses (including the new thread state) are visible to other threads. + __ li(R0/*thread_state*/, _thread_in_native_trans); + __ release(); + __ stw(R0/*thread_state*/, thread_(thread_state)); + if (UseMembar) { + __ fence(); + } + // Write serialization page so that the VM thread can do a pseudo remote + // membar. We use the current thread pointer to calculate a thread + // specific offset to write to within the page. This minimizes bus + // traffic due to cache line collision. + else { + __ serialize_memory(R16_thread, R11_scratch1, R12_scratch2); + } + + // Now before we return to java we must look for a current safepoint + // (a new safepoint can not start since we entered native_trans). + // We must check here because a current safepoint could be modifying + // the callers registers right this moment. + + // Acquire isn't strictly necessary here because of the fence, but + // sync_state is declared to be volatile, so we do it anyway + // (cmp-br-isync on one path, release (same as acquire on PPC64) on the other path). + int sync_state_offs = __ load_const_optimized(sync_state_addr, SafepointSynchronize::address_of_state(), /*temp*/R0, true); + + // TODO PPC port assert(4 == SafepointSynchronize::sz_state(), "unexpected field size"); + __ lwz(sync_state, sync_state_offs, sync_state_addr); + + // TODO PPC port assert(4 == Thread::sz_suspend_flags(), "unexpected field size"); + __ lwz(suspend_flags, thread_(suspend_flags)); + + Label sync_check_done; + Label do_safepoint; + // No synchronization in progress nor yet synchronized. + __ cmpwi(CCR0, sync_state, SafepointSynchronize::_not_synchronized); + // Not suspended. + __ cmpwi(CCR1, suspend_flags, 0); + + __ bne(CCR0, do_safepoint); + __ beq(CCR1, sync_check_done); + __ bind(do_safepoint); + __ isync(); + // Block. We do the call directly and leave the current + // last_Java_frame setup undisturbed. We must save any possible + // native result across the call. No oop is present. + + __ mr(R3_ARG1, R16_thread); +#if defined(ABI_ELFv2) + __ call_c(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), + relocInfo::none); +#else + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, JavaThread::check_special_condition_for_native_trans), + relocInfo::none); +#endif + + __ bind(sync_check_done); + + //============================================================================= + // <<<<<< Back in Interpreter Frame >>>>> + + // We are in thread_in_native_trans here and back in the normal + // interpreter frame. We don't have to do anything special about + // safepoints and we can switch to Java mode anytime we are ready. + + // Note: frame::interpreter_frame_result has a dependency on how the + // method result is saved across the call to post_method_exit. For + // native methods it assumes that the non-FPU/non-void result is + // saved in _native_lresult and a FPU result in _native_fresult. If + // this changes then the interpreter_frame_result implementation + // will need to be updated too. + + // On PPC64, we have stored the result directly after the native call. + + //============================================================================= + // Back in Java + + // We use release_store_fence to update values like the thread state, where + // we don't want the current thread to continue until all our prior memory + // accesses (including the new thread state) are visible to other threads. + __ li(R0/*thread_state*/, _thread_in_Java); + __ release(); + __ stw(R0/*thread_state*/, thread_(thread_state)); + if (UseMembar) { + __ fence(); + } + + __ reset_last_Java_frame(); + + + // Reload GR27_method, call killed it. We can't look at + // state->_method until we're back in java state because in java + // state gc can't happen until we get to a safepoint. + // + // We've set thread_state to _thread_in_Java already, so restoring + // R19_method from state works; R19_method is invalid, because + // GC may have happened. + __ ld(R19_method, 0, R1_SP); + __ ld(R19_method, _ijava_state_neg(method), R19_method); + + // jvmdi/jvmpi support. Whether we've got an exception pending or + // not, and whether unlocking throws an exception or not, we notify + // on native method exit. If we do have an exception, we'll end up + // in the caller's context to handle it, so if we don't do the + // notify here, we'll drop it on the floor. + __ notify_method_exit(true/*native method*/, + ilgl /*illegal state (not used for native methods)*/, + InterpreterMacroAssembler::NotifyJVMTI, + false /*check_exceptions*/); + + //============================================================================= + // Handle exceptions + + if (synchronized) { + // Don't check for exceptions since we're still in the i2n frame. Do that + // manually afterwards. + unlock_method(false); + } + + // Reset active handles after returning from native. + // thread->active_handles()->clear(); + __ ld(active_handles, thread_(active_handles)); + // TODO PPC port assert(4 == JNIHandleBlock::top_size_in_bytes(), "unexpected field size"); + __ li(R0, 0); + __ stw(R0, JNIHandleBlock::top_offset_in_bytes(), active_handles); + + Label exception_return_sync_check_already_unlocked; + __ ld(R0/*pending_exception*/, thread_(pending_exception)); + __ cmpdi(CCR0, R0/*pending_exception*/, 0); + __ bne(CCR0, exception_return_sync_check_already_unlocked); + + //----------------------------------------------------------------------------- + // No exception pending. + + // Move native method result back into proper registers and return. + // Invoke result handler (may unbox/promote). + __ ld(R11_scratch1, 0, R1_SP); + __ ld(R3_RET, _ijava_state_neg(lresult), R11_scratch1); + __ lfd(F1_RET, _ijava_state_neg(fresult), R11_scratch1); + __ call_stub(result_handler_addr); + + __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2); + + // Must use the return pc which was loaded from the caller's frame + // as the VM uses return-pc-patching for deoptimization. + __ mtlr(R0); + __ blr(); + + //----------------------------------------------------------------------------- + // An exception is pending. We call into the runtime only if the + // caller was not interpreted. If it was interpreted the + // interpreter will do the correct thing. If it isn't interpreted + // (call stub/compiled code) we will change our return and continue. + + __ BIND(exception_return_sync_check); + + if (synchronized) { + // Don't check for exceptions since we're still in the i2n frame. Do that + // manually afterwards. + unlock_method(false); + } + __ BIND(exception_return_sync_check_already_unlocked); + + const Register return_pc = R31; + + __ ld(return_pc, 0, R1_SP); + __ ld(return_pc, _abi(lr), return_pc); + + // Get the address of the exception handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), + R16_thread, + return_pc /* return pc */); + __ merge_frames(/*top_frame_sp*/ R21_sender_SP, noreg, R11_scratch1, R12_scratch2); + + // Load the PC of the the exception handler into LR. + __ mtlr(R3_RET); + + // Load exception into R3_ARG1 and clear pending exception in thread. + __ ld(R3_ARG1/*exception*/, thread_(pending_exception)); + __ li(R4_ARG2, 0); + __ std(R4_ARG2, thread_(pending_exception)); + + // Load the original return pc into R4_ARG2. + __ mr(R4_ARG2/*issuing_pc*/, return_pc); + + // Return to exception handler. + __ blr(); + + //============================================================================= + // Counter overflow. + + if (inc_counter) { + // Handle invocation counter overflow. + __ bind(invocation_counter_overflow); + + generate_counter_overflow(continue_after_compile); + } + + return entry; +} + +// Generic interpreted method entry to (asm) interpreter. +// +address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) { + bool inc_counter = UseCompiler || CountCompiledCalls; + address entry = __ pc(); + // Generate the code to allocate the interpreter stack frame. + Register Rsize_of_parameters = R4_ARG2, // Written by generate_fixed_frame. + Rsize_of_locals = R5_ARG3; // Written by generate_fixed_frame. + + generate_fixed_frame(false, Rsize_of_parameters, Rsize_of_locals); + +#ifdef FAST_DISPATCH + __ unimplemented("Fast dispatch in generate_normal_entry"); +#if 0 + __ set((intptr_t)Interpreter::dispatch_table(), IdispatchTables); + // Set bytecode dispatch table base. +#endif +#endif + + // -------------------------------------------------------------------------- + // Zero out non-parameter locals. + // Note: *Always* zero out non-parameter locals as Sparc does. It's not + // worth to ask the flag, just do it. + Register Rslot_addr = R6_ARG4, + Rnum = R7_ARG5; + Label Lno_locals, Lzero_loop; + + // Set up the zeroing loop. + __ subf(Rnum, Rsize_of_parameters, Rsize_of_locals); + __ subf(Rslot_addr, Rsize_of_parameters, R18_locals); + __ srdi_(Rnum, Rnum, Interpreter::logStackElementSize); + __ beq(CCR0, Lno_locals); + __ li(R0, 0); + __ mtctr(Rnum); + + // The zero locals loop. + __ bind(Lzero_loop); + __ std(R0, 0, Rslot_addr); + __ addi(Rslot_addr, Rslot_addr, -Interpreter::stackElementSize); + __ bdnz(Lzero_loop); + + __ bind(Lno_locals); + + // -------------------------------------------------------------------------- + // Counter increment and overflow check. + Label invocation_counter_overflow, + profile_method, + profile_method_continue; + if (inc_counter || ProfileInterpreter) { + + Register Rdo_not_unlock_if_synchronized_addr = R11_scratch1; + if (synchronized) { + // Since at this point in the method invocation the exception handler + // would try to exit the monitor of synchronized methods which hasn't + // been entered yet, we set the thread local variable + // _do_not_unlock_if_synchronized to true. If any exception was thrown by + // runtime, exception handling i.e. unlock_if_synchronized_method will + // check this thread local flag. + // This flag has two effects, one is to force an unwind in the topmost + // interpreter frame and not perform an unlock while doing so. + __ li(R0, 1); + __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); + } + // Increment invocation counter and check for overflow. + if (inc_counter) { + generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue); + } + + __ bind(profile_method_continue); + + // Reset the _do_not_unlock_if_synchronized flag. + if (synchronized) { + __ li(R0, 0); + __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); + } + } + + // -------------------------------------------------------------------------- + // Locking of synchronized methods. Must happen AFTER invocation_counter + // check and stack overflow check, so method is not locked if overflows. + if (synchronized) { + lock_method(R3_ARG1, R4_ARG2, R5_ARG3); + } +#ifdef ASSERT + else { + Label Lok; + __ lwz(R0, in_bytes(methodOopDesc::access_flags_offset()), R19_method); + __ andi_(R0, R0, JVM_ACC_SYNCHRONIZED); + __ asm_assert_eq("method needs synchronization", 0x8521); + __ bind(Lok); + } +#endif // ASSERT + + __ verify_thread(); + + // -------------------------------------------------------------------------- + // JVMTI support + __ notify_method_entry(); + + // -------------------------------------------------------------------------- + // Start executing instructions. + __ dispatch_next(vtos); + + // -------------------------------------------------------------------------- + // Out of line counter overflow and MDO creation code. + if (ProfileInterpreter) { + // We have decided to profile this method in the interpreter. + __ bind(profile_method); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); + __ set_method_data_pointer_for_bcp(); + __ b(profile_method_continue); + } + + if (inc_counter) { + // Handle invocation counter overflow. + __ bind(invocation_counter_overflow); + generate_counter_overflow(profile_method_continue); + } + return entry; +} + +// ============================================================================= +// Entry points + +address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { + // Determine code generation flags. + bool synchronized = false; + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized: synchronized = true; break; + case Interpreter::native : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); break; + case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(true); break; + case Interpreter::empty : entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); break; + case Interpreter::accessor : entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break; + + case Interpreter::java_lang_math_sin : // fall thru + case Interpreter::java_lang_math_cos : // fall thru + case Interpreter::java_lang_math_tan : // fall thru + case Interpreter::java_lang_math_abs : // fall thru + case Interpreter::java_lang_math_log : // fall thru + case Interpreter::java_lang_math_log10 : // fall thru + case Interpreter::java_lang_math_sqrt : // fall thru + case Interpreter::java_lang_math_pow : // fall thru + case Interpreter::java_lang_math_exp : entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break; + case Interpreter::java_lang_ref_reference_get + : entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; + default : ShouldNotReachHere(); break; + } + + if (entry_point) { + return entry_point; + } + + return ((InterpreterGenerator*) this)->generate_normal_entry(synchronized); +} + +// These should never be compiled since the interpreter will prefer +// the compiled version to the intrinsic version. +bool AbstractInterpreter::can_be_compiled(methodHandle m) { + return !math_entry_available(method_kind(m)); +} + +// How much stack a method activation needs in stack slots. +// We must calc this exactly like in generate_fixed_frame. +// Note: This returns the conservative size assuming maximum alignment. +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + const int max_alignment_size = 2; + const int abi_scratch = frame::abi_reg_args_size; + return method->max_locals() + method->max_stack() + frame::interpreter_frame_monitor_size() + max_alignment_size + abi_scratch; +} + +// Fills a sceletal interpreter frame generated during deoptimizations +// and returns the frame size in slots. +// +// Parameters: +// +// interpreter_frame == NULL: +// Only calculate the size of an interpreter activation, no actual layout. +// Note: This calculation must exactly parallel the frame setup +// in TemplateInterpreter::generate_normal_entry. But it does not +// account for the SP alignment, that might further enhance the +// frame size, depending on FP. +// +// interpreter_frame != NULL: +// set up the method, locals, and monitors. +// The frame interpreter_frame, if not NULL, is guaranteed to be the +// right size, as determined by a previous call to this method. +// It is also guaranteed to be walkable even though it is in a skeletal state +// +// is_top_frame == true: +// We're processing the *oldest* interpreter frame! +// +// pop_frame_extra_args: +// If this is != 0 we are returning to a deoptimized frame by popping +// off the callee frame. We want to re-execute the call that called the +// callee interpreted, but since the return to the interpreter would pop +// the arguments off advance the esp by dummy popframe_extra_args slots. +// Popping off those will establish the stack layout as it was before the call. +// +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int caller_actual_parameters, + int callee_param_count, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame, + bool is_bottom_frame) { + + const int max_alignment_space = 2; + const int abi_scratch = is_top_frame ? (frame::abi_reg_args_size / Interpreter::stackElementSize) : + (frame::abi_minframe_size / Interpreter::stackElementSize) ; + const int conservative_framesize_in_slots = + method->max_stack() + callee_locals - callee_param_count + + (moncount * frame::interpreter_frame_monitor_size()) + max_alignment_space + + abi_scratch + frame::ijava_state_size / Interpreter::stackElementSize; + + assert(!is_top_frame || conservative_framesize_in_slots * 8 > frame::abi_reg_args_size + frame::ijava_state_size, "frame too small"); + + if (interpreter_frame == NULL) { + // Since we don't know the exact alignment, we return the conservative size. + return (conservative_framesize_in_slots & -2); + } else { + // Now we know our caller, calc the exact frame layout and size. + intptr_t* locals_base = (caller->is_interpreted_frame()) ? + caller->interpreter_frame_esp() + caller_actual_parameters : + caller->sp() + method->max_locals() - 1 + (frame::abi_minframe_size / Interpreter::stackElementSize) ; + + intptr_t* monitor_base = caller->sp() - frame::ijava_state_size / Interpreter::stackElementSize ; + intptr_t* monitor = monitor_base - (moncount * frame::interpreter_frame_monitor_size()); + intptr_t* esp_base = monitor - 1; + intptr_t* esp = esp_base - tempcount - popframe_extra_args; + intptr_t* sp = (intptr_t *) (((intptr_t) (esp_base- callee_locals + callee_param_count - method->max_stack()- abi_scratch)) & -StackAlignmentInBytes); + intptr_t* sender_sp = caller->sp() + (frame::abi_minframe_size - frame::abi_reg_args_size) / Interpreter::stackElementSize; + intptr_t* top_frame_sp = is_top_frame ? sp : sp + (frame::abi_minframe_size - frame::abi_reg_args_size) / Interpreter::stackElementSize; + + interpreter_frame->interpreter_frame_set_method(method); + interpreter_frame->interpreter_frame_set_locals(locals_base); + interpreter_frame->interpreter_frame_set_cpcache(method->constants()->cache()); + interpreter_frame->interpreter_frame_set_esp(esp); + interpreter_frame->interpreter_frame_set_monitor_end((BasicObjectLock *)monitor); + interpreter_frame->interpreter_frame_set_top_frame_sp(top_frame_sp); + if (!is_bottom_frame) { + interpreter_frame->interpreter_frame_set_sender_sp(sender_sp); + } + + int framesize_in_slots = caller->sp() - sp; + assert(!is_top_frame ||framesize_in_slots >= (frame::abi_reg_args_size / Interpreter::stackElementSize) + frame::ijava_state_size / Interpreter::stackElementSize, "frame too small"); + assert(framesize_in_slots <= conservative_framesize_in_slots, "exact frame size must be smaller than the convervative size!"); + return framesize_in_slots; + } +} + +// ============================================================================= +// Exceptions + +void TemplateInterpreterGenerator::generate_throw_exception() { + Register Rexception = R17_tos, + Rcontinuation = R3_RET; + + // -------------------------------------------------------------------------- + // Entry point if an method returns with a pending exception (rethrow). + Interpreter::_rethrow_exception_entry = __ pc(); + { + __ restore_interpreter_state(R11_scratch1); // Sets R11_scratch1 = fp. + __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1); + __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); + + // Compiled code destroys templateTableBase, reload. + __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1); + } + + // Entry point if a interpreted method throws an exception (throw). + Interpreter::_throw_exception_entry = __ pc(); + { + __ mr(Rexception, R3_RET); + + __ verify_thread(); + __ verify_oop(Rexception); + + // Expression stack must be empty before entering the VM in case of an exception. + __ empty_expression_stack(); + // Find exception handler address and preserve exception oop. + // Call C routine to find handler and jump to it. + __ call_VM(Rexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), Rexception); + __ mtctr(Rcontinuation); + // Push exception for exception handler bytecodes. + __ push_ptr(Rexception); + + // Jump to exception handler (may be remove activation entry!). + __ bctr(); + } + + // If the exception is not handled in the current frame the frame is + // removed and the exception is rethrown (i.e. exception + // continuation is _rethrow_exception). + // + // Note: At this point the bci is still the bxi for the instruction + // which caused the exception and the expression stack is + // empty. Thus, for any VM calls at this point, GC will find a legal + // oop map (with empty expression stack). + + // In current activation + // tos: exception + // bcp: exception bcp + + // -------------------------------------------------------------------------- + // JVMTI PopFrame support + + Interpreter::_remove_activation_preserving_args_entry = __ pc(); + { + // Set the popframe_processing bit in popframe_condition indicating that we are + // currently handling popframe, so that call_VMs that may happen later do not + // trigger new popframe handling cycles. + __ lwz(R11_scratch1, in_bytes(JavaThread::popframe_condition_offset()), R16_thread); + __ ori(R11_scratch1, R11_scratch1, JavaThread::popframe_processing_bit); + __ stw(R11_scratch1, in_bytes(JavaThread::popframe_condition_offset()), R16_thread); + + // Empty the expression stack, as in normal exception handling. + __ empty_expression_stack(); + __ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false, /* install_monitor_exception */ false); + + // Check to see whether we are returning to a deoptimized frame. + // (The PopFrame call ensures that the caller of the popped frame is + // either interpreted or compiled and deoptimizes it if compiled.) + // Note that we don't compare the return PC against the + // deoptimization blob's unpack entry because of the presence of + // adapter frames in C2. + Label Lcaller_not_deoptimized; + Register return_pc = R3_ARG1; + __ ld(return_pc, 0, R1_SP); + __ ld(return_pc, _abi(lr), return_pc); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), return_pc); + __ cmpdi(CCR0, R3_RET, 0); + __ bne(CCR0, Lcaller_not_deoptimized); + + // The deoptimized case. + // In this case, we can't call dispatch_next() after the frame is + // popped, but instead must save the incoming arguments and restore + // them after deoptimization has occurred. + __ lhz(R4_ARG2, in_bytes(methodOopDesc::size_of_parameters_offset()), R19_method); + __ slwi(R4_ARG2, R4_ARG2, Interpreter::logStackElementSize); + __ addi(R5_ARG3, R18_locals, Interpreter::stackElementSize); + __ subf(R5_ARG3, R4_ARG2, R5_ARG3); + // Save these arguments. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), R16_thread, R4_ARG2, R5_ARG3); + + // Inform deoptimization that it is responsible for restoring these arguments. + __ load_const_optimized(R11_scratch1, JavaThread::popframe_force_deopt_reexecution_bit); + __ stw(R11_scratch1, in_bytes(JavaThread::popframe_condition_offset()), R16_thread); + + // Return from the current method into the deoptimization blob. Will eventually + // end up in the deopt interpeter entry, deoptimization prepared everything that + // we will reexecute the call that called us. + __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*reload return_pc*/ return_pc, R11_scratch1, R12_scratch2); + __ mtlr(return_pc); + __ blr(); + + // The non-deoptimized case. + __ bind(Lcaller_not_deoptimized); + + // Clear the popframe condition flag. + __ li(R0, 0); + __ stw(R0, in_bytes(JavaThread::popframe_condition_offset()), R16_thread); + + // Get out of the current method and re-execute the call that called us. + __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ noreg, R11_scratch1, R12_scratch2); + __ restore_interpreter_state(R11_scratch1); + __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1); + __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); + if (ProfileInterpreter) { + __ set_method_data_pointer_for_bcp(); + } +#if INCLUDE_JVMTI + Label L_done; + + __ lbz(R11_scratch1, 0, R14_bcp); + __ cmpwi(CCR0, R11_scratch1, Bytecodes::_invokestatic); + __ bne(CCR0, L_done); + + // The member name argument must be restored if _invokestatic is re-executed after a PopFrame call. + // Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL. + __ ld(R4_ARG2, 0, R18_locals); + __ call_VM(R11_scratch1, CAST_FROM_FN_PTR(address, InterpreterRuntime::member_name_arg_or_null), + R4_ARG2, R19_method, R14_bcp); + + __ cmpdi(CCR0, R11_scratch1, 0); + __ beq(CCR0, L_done); + + __ std(R11_scratch1, wordSize, R15_esp); + __ bind(L_done); +#endif // INCLUDE_JVMTI + __ dispatch_next(vtos); + } + // end of JVMTI PopFrame support + + // -------------------------------------------------------------------------- + // Remove activation exception entry. + // This is jumped to if an interpreted method can't handle an exception itself + // (we come from the throw/rethrow exception entry above). We're going to call + // into the VM to find the exception handler in the caller, pop the current + // frame and return the handler we calculated. + Interpreter::_remove_activation_entry = __ pc(); + { + __ pop_ptr(Rexception); + __ verify_thread(); + __ verify_oop(Rexception); + __ std(Rexception, in_bytes(JavaThread::vm_result_offset()), R16_thread); + + __ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false, true); + __ notify_method_exit(false, vtos, InterpreterMacroAssembler::SkipNotifyJVMTI, false); + + __ get_vm_result(Rexception); + + // We are done with this activation frame; find out where to go next. + // The continuation point will be an exception handler, which expects + // the following registers set up: + // + // RET: exception oop + // ARG2: Issuing PC (see generate_exception_blob()), only used if the caller is compiled. + + Register return_pc = R31; // Needs to survive the runtime call. + __ ld(return_pc, 0, R1_SP); + __ ld(return_pc, _abi(lr), return_pc); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), R16_thread, return_pc); + + // Remove the current activation. + __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ noreg, R11_scratch1, R12_scratch2); + + __ mr(R4_ARG2, return_pc); + __ mtlr(R3_RET); + __ mr(R3_RET, Rexception); + __ blr(); + } +} + +// JVMTI ForceEarlyReturn support. +// Returns "in the middle" of a method with a "fake" return value. +address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) { + + Register Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2; + + address entry = __ pc(); + __ empty_expression_stack(); + + __ load_earlyret_value(state, Rscratch1); + + __ ld(Rscratch1, in_bytes(JavaThread::jvmti_thread_state_offset()), R16_thread); + // Clear the earlyret state. + __ li(R0, 0); + __ stw(R0, in_bytes(JvmtiThreadState::earlyret_state_offset()), Rscratch1); + + __ remove_activation(state, false, false); + // Copied from TemplateTable::_return. + // Restoration of lr done by remove_activation. + switch (state) { + // Narrow result if state is itos but result type is smaller. + case itos: __ narrow(R17_tos); /* fall through */ + case ltos: + case btos: + case ztos: + case ctos: + case stos: + case atos: __ mr(R3_RET, R17_tos); break; + case ftos: + case dtos: __ fmr(F1_RET, F15_ftos); break; + case vtos: // This might be a constructor. Final fields (and volatile fields on PPC64) need + // to get visible before the reference to the object gets stored anywhere. + __ membar(Assembler::StoreStore); break; + default : ShouldNotReachHere(); + } + __ blr(); + + return entry; +} // end of ForceEarlyReturn support + +//----------------------------------------------------------------------------- +// Helper for vtos entry point generation + +void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, + address& bep, + address& cep, + address& sep, + address& aep, + address& iep, + address& lep, + address& fep, + address& dep, + address& vep) { + assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); + Label L; + + aep = __ pc(); __ push_ptr(); __ b(L); + fep = __ pc(); __ push_f(); __ b(L); + dep = __ pc(); __ push_d(); __ b(L); + lep = __ pc(); __ push_l(); __ b(L); + __ align(32, 12, 24); // align L + bep = cep = sep = + iep = __ pc(); __ push_i(); + vep = __ pc(); + __ bind(L); + generate_and_dispatch(t); +} + +//----------------------------------------------------------------------------- +// Generation of individual instructions + +// helpers for generate_and_dispatch + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : TemplateInterpreterGenerator(code) { + generate_all(); // Down here so it can be "virtual". +} + +//----------------------------------------------------------------------------- + +// Non-product code +#ifndef PRODUCT +address TemplateInterpreterGenerator::generate_trace_code(TosState state) { + //__ flush_bundle(); + address entry = __ pc(); + + const char *bname = NULL; + uint tsize = 0; + switch(state) { + case ftos: + bname = "trace_code_ftos {"; + tsize = 2; + break; + case btos: + bname = "trace_code_btos {"; + tsize = 2; + break; + case ztos: + bname = "trace_code_ztos {"; + tsize = 2; + break; + case ctos: + bname = "trace_code_ctos {"; + tsize = 2; + break; + case stos: + bname = "trace_code_stos {"; + tsize = 2; + break; + case itos: + bname = "trace_code_itos {"; + tsize = 2; + break; + case ltos: + bname = "trace_code_ltos {"; + tsize = 3; + break; + case atos: + bname = "trace_code_atos {"; + tsize = 2; + break; + case vtos: + // Note: In case of vtos, the topmost of stack value could be a int or doubl + // In case of a double (2 slots) we won't see the 2nd stack value. + // Maybe we simply should print the topmost 3 stack slots to cope with the problem. + bname = "trace_code_vtos {"; + tsize = 2; + + break; + case dtos: + bname = "trace_code_dtos {"; + tsize = 3; + break; + default: + ShouldNotReachHere(); + } + BLOCK_COMMENT(bname); + + // Support short-cut for TraceBytecodesAt. + // Don't call into the VM if we don't want to trace to speed up things. + Label Lskip_vm_call; + if (TraceBytecodesAt > 0 && TraceBytecodesAt < max_intx) { + int offs1 = __ load_const_optimized(R11_scratch1, (address) &TraceBytecodesAt, R0, true); + int offs2 = __ load_const_optimized(R12_scratch2, (address) &BytecodeCounter::_counter_value, R0, true); + __ ld(R11_scratch1, offs1, R11_scratch1); + __ lwa(R12_scratch2, offs2, R12_scratch2); + __ cmpd(CCR0, R12_scratch2, R11_scratch1); + __ blt(CCR0, Lskip_vm_call); + } + + __ push(state); + // Load 2 topmost expression stack values. + __ ld(R6_ARG4, tsize*Interpreter::stackElementSize, R15_esp); + __ ld(R5_ARG3, Interpreter::stackElementSize, R15_esp); + __ mflr(R31); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), /* unused */ R4_ARG2, R5_ARG3, R6_ARG4, false); + __ mtlr(R31); + __ pop(state); + + if (TraceBytecodesAt > 0 && TraceBytecodesAt < max_intx) { + __ bind(Lskip_vm_call); + } + __ blr(); + BLOCK_COMMENT("} trace_code"); + return entry; +} + +void TemplateInterpreterGenerator::count_bytecode() { + int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeCounter::_counter_value, R12_scratch2, true); + __ lwz(R12_scratch2, offs, R11_scratch1); + __ addi(R12_scratch2, R12_scratch2, 1); + __ stw(R12_scratch2, offs, R11_scratch1); +} + +void TemplateInterpreterGenerator::histogram_bytecode(Template* t) { + int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeHistogram::_counters[t->bytecode()], R12_scratch2, true); + __ lwz(R12_scratch2, offs, R11_scratch1); + __ addi(R12_scratch2, R12_scratch2, 1); + __ stw(R12_scratch2, offs, R11_scratch1); +} + +void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) { + const Register addr = R11_scratch1, + tmp = R12_scratch2; + // Get index, shift out old bytecode, bring in new bytecode, and store it. + // _index = (_index >> log2_number_of_codes) | + // (bytecode << log2_number_of_codes); + int offs1 = __ load_const_optimized(addr, (address)&BytecodePairHistogram::_index, tmp, true); + __ lwz(tmp, offs1, addr); + __ srwi(tmp, tmp, BytecodePairHistogram::log2_number_of_codes); + __ ori(tmp, tmp, ((int) t->bytecode()) << BytecodePairHistogram::log2_number_of_codes); + __ stw(tmp, offs1, addr); + + // Bump bucket contents. + // _counters[_index] ++; + int offs2 = __ load_const_optimized(addr, (address)&BytecodePairHistogram::_counters, R0, true); + __ sldi(tmp, tmp, LogBytesPerInt); + __ add(addr, tmp, addr); + __ lwz(tmp, offs2, addr); + __ addi(tmp, tmp, 1); + __ stw(tmp, offs2, addr); +} + +void TemplateInterpreterGenerator::trace_bytecode(Template* t) { + // Call a little run-time stub to avoid blow-up for each bytecode. + // The run-time runtime saves the right registers, depending on + // the tosca in-state for the given template. + + assert(Interpreter::trace_code(t->tos_in()) != NULL, + "entry must have been generated"); + + // Note: we destroy LR here. + __ bl(Interpreter::trace_code(t->tos_in())); +} + +void TemplateInterpreterGenerator::stop_interpreter_at() { + Label L; + int offs1 = __ load_const_optimized(R11_scratch1, (address) &StopInterpreterAt, R0, true); + int offs2 = __ load_const_optimized(R12_scratch2, (address) &BytecodeCounter::_counter_value, R0, true); + __ ld(R11_scratch1, offs1, R11_scratch1); + __ lwa(R12_scratch2, offs2, R12_scratch2); + __ cmpd(CCR0, R12_scratch2, R11_scratch1); + __ bne(CCR0, L); + __ illtrap(); + __ bind(L); +} + +#endif // !PRODUCT +#endif // !CC_INTERP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/templateInterpreter_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/templateInterpreter_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_TEMPLATEINTERPRETER_PPC_HPP +#define CPU_PPC_VM_TEMPLATEINTERPRETER_PPC_HPP + + protected: + + // Size of interpreter code. Increase if too small. Interpreter will + // fail with a guarantee ("not enough space for interpreter generation"); + // if too small. + // Run with +PrintInterpreter to get the VM to print out the size. + // Max size with JVMTI + + const static int InterpreterCodeSize = 210*K; + +#endif // CPU_PPC_VM_TEMPLATEINTERPRETER_PPC_HPP + + diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/templateTable_ppc_64.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/templateTable_ppc_64.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,4322 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "macroAssembler_ppc.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "interpreter/templateTable.hpp" +#include "memory/universe.inline.hpp" +#include "oops/methodDataOop.hpp" +#include "oops/objArrayKlass.hpp" +#include "oops/oop.inline.hpp" +#include "prims/methodHandles.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" +#include "utilities/macros.hpp" + +#ifndef CC_INTERP + +#undef __ +#define __ _masm-> + +static const bool support_IRIW_for_not_multiple_copy_atomic_cpu = true; + +// ============================================================================ +// Misc helpers + +// Do an oop store like *(base + index) = val OR *(base + offset) = val +// (only one of both variants is possible at the same time). +// Index can be noreg. +// Kills: +// Rbase, Rtmp +static void do_oop_store(InterpreterMacroAssembler* _masm, + Register Rbase, + RegisterOrConstant offset, + Register Rval, // Noreg means always null. + Register Rtmp1, + Register Rtmp2, + Register Rtmp3, + BarrierSet::Name barrier, + bool precise, + bool check_null) { + assert_different_registers(Rtmp1, Rtmp2, Rtmp3, Rval, Rbase); + + switch (barrier) { +#ifndef SERIALGC + case BarrierSet::G1SATBCT: + case BarrierSet::G1SATBCTLogging: + { + // Load and record the previous value. + __ g1_write_barrier_pre(Rbase, offset, + Rtmp3, /* holder of pre_val ? */ + Rtmp1, Rtmp2, false /* frame */); + + Label Lnull, Ldone; + if (Rval != noreg) { + if (check_null) { + __ cmpdi(CCR0, Rval, 0); + __ beq(CCR0, Lnull); + } + __ store_heap_oop_not_null(Rval, offset, Rbase, /*Rval must stay uncompressed.*/ Rtmp1); + // Mark the card. + if (!(offset.is_constant() && offset.as_constant() == 0) && precise) { + __ add(Rbase, offset, Rbase); + } + __ g1_write_barrier_post(Rbase, Rval, Rtmp1, Rtmp2, Rtmp3, /*filtered (fast path)*/ &Ldone); + if (check_null) { __ b(Ldone); } + } + + if (Rval == noreg || check_null) { // Store null oop. + Register Rnull = Rval; + __ bind(Lnull); + if (Rval == noreg) { + Rnull = Rtmp1; + __ li(Rnull, 0); + } + if (UseCompressedOops) { + __ stw(Rnull, offset, Rbase); + } else { + __ std(Rnull, offset, Rbase); + } + } + __ bind(Ldone); + } + break; +#endif // SERIALGC + case BarrierSet::CardTableModRef: + case BarrierSet::CardTableExtension: + { + Label Lnull, Ldone; + if (Rval != noreg) { + if (check_null) { + __ cmpdi(CCR0, Rval, 0); + __ beq(CCR0, Lnull); + } + __ store_heap_oop_not_null(Rval, offset, Rbase, /*Rval should better stay uncompressed.*/ Rtmp1); + // Mark the card. + if (!(offset.is_constant() && offset.as_constant() == 0) && precise) { + __ add(Rbase, offset, Rbase); + } + __ card_write_barrier_post(Rbase, Rval, Rtmp1); + if (check_null) { + __ b(Ldone); + } + } + + if (Rval == noreg || check_null) { // Store null oop. + Register Rnull = Rval; + __ bind(Lnull); + if (Rval == noreg) { + Rnull = Rtmp1; + __ li(Rnull, 0); + } + if (UseCompressedOops) { + __ stw(Rnull, offset, Rbase); + } else { + __ std(Rnull, offset, Rbase); + } + } + __ bind(Ldone); + } + break; + case BarrierSet::ModRef: + case BarrierSet::Other: + ShouldNotReachHere(); + break; + default: + ShouldNotReachHere(); + } +} + +// ============================================================================ +// Platform-dependent initialization + +void TemplateTable::pd_initialize() { + // No ppc64 specific initialization. +} + +Address TemplateTable::at_bcp(int offset) { + // Not used on ppc. + ShouldNotReachHere(); + return Address(); +} + +// Patches the current bytecode (ptr to it located in bcp) +// in the bytecode stream with a new one. +void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Register Rtemp, bool load_bc_into_bc_reg /*=true*/, int byte_no) { + // With sharing on, may need to test methodOop flag. + if (!RewriteBytecodes) return; + Label L_patch_done; + + switch (new_bc) { + case Bytecodes::_fast_aputfield: + case Bytecodes::_fast_bputfield: + case Bytecodes::_fast_zputfield: + case Bytecodes::_fast_cputfield: + case Bytecodes::_fast_dputfield: + case Bytecodes::_fast_fputfield: + case Bytecodes::_fast_iputfield: + case Bytecodes::_fast_lputfield: + case Bytecodes::_fast_sputfield: + { + // We skip bytecode quickening for putfield instructions when + // the put_code written to the constant pool cache is zero. + // This is required so that every execution of this instruction + // calls out to InterpreterRuntime::resolve_get_put to do + // additional, required work. + assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); + assert(load_bc_into_bc_reg, "we use bc_reg as temp"); + __ get_cache_and_index_at_bcp(Rtemp /* dst = cache */, 1); + // ((*(cache+indices))>>((1+byte_no)*8))&0xFF: +#if defined(VM_LITTLE_ENDIAN) + __ lbz(Rnew_bc, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 1 + byte_no, Rtemp); +#else + __ lbz(Rnew_bc, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (1 + byte_no), Rtemp); +#endif + __ cmpwi(CCR0, Rnew_bc, 0); + __ li(Rnew_bc, (unsigned int)(unsigned char)new_bc); + __ beq(CCR0, L_patch_done); + // __ isync(); // acquire not needed + break; + } + + default: + assert(byte_no == -1, "sanity"); + if (load_bc_into_bc_reg) { + __ li(Rnew_bc, (unsigned int)(unsigned char)new_bc); + } + } + + if (JvmtiExport::can_post_breakpoint()) { + Label L_fast_patch; + __ lbz(Rtemp, 0, R14_bcp); + __ cmpwi(CCR0, Rtemp, (unsigned int)(unsigned char)Bytecodes::_breakpoint); + __ bne(CCR0, L_fast_patch); + // Perform the quickening, slowly, in the bowels of the breakpoint table. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), R19_method, R14_bcp, Rnew_bc); + __ b(L_patch_done); + __ bind(L_fast_patch); + } + + // Patch bytecode. + __ stb(Rnew_bc, 0, R14_bcp); + + __ bind(L_patch_done); +} + +// ============================================================================ +// Individual instructions + +void TemplateTable::nop() { + transition(vtos, vtos); + // Nothing to do. +} + +void TemplateTable::shouldnotreachhere() { + transition(vtos, vtos); + __ stop("shouldnotreachhere bytecode"); +} + +void TemplateTable::aconst_null() { + transition(vtos, atos); + __ li(R17_tos, 0); +} + +void TemplateTable::iconst(int value) { + transition(vtos, itos); + assert(value >= -1 && value <= 5, ""); + __ li(R17_tos, value); +} + +void TemplateTable::lconst(int value) { + transition(vtos, ltos); + assert(value >= -1 && value <= 5, ""); + __ li(R17_tos, value); +} + +void TemplateTable::fconst(int value) { + transition(vtos, ftos); + static float zero = 0.0; + static float one = 1.0; + static float two = 2.0; + switch (value) { + default: ShouldNotReachHere(); + case 0: { + int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&zero, R0, true); + __ lfs(F15_ftos, simm16_offset, R11_scratch1); + break; + } + case 1: { + int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&one, R0, true); + __ lfs(F15_ftos, simm16_offset, R11_scratch1); + break; + } + case 2: { + int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&two, R0, true); + __ lfs(F15_ftos, simm16_offset, R11_scratch1); + break; + } + } +} + +void TemplateTable::dconst(int value) { + transition(vtos, dtos); + static double zero = 0.0; + static double one = 1.0; + switch (value) { + case 0: { + int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&zero, R0, true); + __ lfd(F15_ftos, simm16_offset, R11_scratch1); + break; + } + case 1: { + int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&one, R0, true); + __ lfd(F15_ftos, simm16_offset, R11_scratch1); + break; + } + default: ShouldNotReachHere(); + } +} + +void TemplateTable::bipush() { + transition(vtos, itos); + __ lbz(R17_tos, 1, R14_bcp); + __ extsb(R17_tos, R17_tos); +} + +void TemplateTable::sipush() { + transition(vtos, itos); + __ get_2_byte_integer_at_bcp(1, R17_tos, InterpreterMacroAssembler::Signed); +} + +void TemplateTable::ldc(bool wide) { + Register Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2, + Rcpool = R3_ARG1; + + transition(vtos, vtos); + Label notInt, notString, notClass, exit; + + __ get_cpool_and_tags(Rcpool, Rscratch2); // Set Rscratch2 = &tags. + if (wide) { // Read index. + __ get_2_byte_integer_at_bcp(1, Rscratch1, InterpreterMacroAssembler::Unsigned); + } else { + __ lbz(Rscratch1, 1, R14_bcp); + } + + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + + // Get type from tags. + __ addi(Rscratch2, Rscratch2, tags_offset); + __ lbzx(Rscratch2, Rscratch2, Rscratch1); + + __ cmpwi(CCR0, Rscratch2, JVM_CONSTANT_UnresolvedString); // Unresolved string? + __ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_UnresolvedClass); // Unresolved class? + __ cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); + __ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_UnresolvedClassInError); // Unresolved class in error state? + __ cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); + + // Resolved class - need to call vm to get java mirror of the class. + __ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_Class); + __ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); // Neither resolved class nor unresolved case from above? + __ beq(CCR0, notClass); + + __ li(R4, wide ? 1 : 0); + call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), R4); + __ push(atos); + __ b(exit); + + __ align(32, 12); + __ bind(notClass); + __ addi(Rcpool, Rcpool, base_offset); + __ sldi(Rscratch1, Rscratch1, LogBytesPerWord); + __ cmpdi(CCR0, Rscratch2, JVM_CONSTANT_Integer); + __ bne(CCR0, notInt); + __ isync(); // Order load of constant wrt. tags. + __ lwax(R17_tos, Rcpool, Rscratch1); + __ push(itos); + __ b(exit); + + __ align(32, 12); + __ bind(notInt); + __ cmpdi(CCR0, Rscratch2, JVM_CONSTANT_String); + __ cmpdi(CCR1, Rscratch2, JVM_CONSTANT_Object); + __ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); + __ beq(CCR0, notString); + + __ isync(); // order load of constant wrt. tags + __ ldx(R17_tos, Rcpool, Rscratch1); + __ verify_oop(R17_tos); + __ push(atos); + __ b(exit); + + __ bind(notString); +#ifdef ASSERT + // String and Object are rewritten to fast_aldc + __ cmpdi(CCR0, Rscratch2, JVM_CONSTANT_Float); + __ asm_assert_eq("unexpected type", 0x8765); +#endif + __ isync(); // Order load of constant wrt. tags. + __ lfsx(F15_ftos, Rcpool, Rscratch1); + __ push(ftos); + + __ align(32, 12); + __ bind(exit); +} + +// Fast path for caching oop constants. +void TemplateTable::fast_aldc(bool wide) { + transition(vtos, atos); + + if (!EnableInvokeDynamic) { + // We should not encounter this bytecode if !EnableInvokeDynamic. + // The verifier will stop it. However, if we get past the verifier, + // this will stop the thread in a reasonable way, without crashing the JVM. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError)); + // The call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + return; + } + + Label Ldone, LRet0; + const Register Rcache = R3_ARG1, + Rscratch = R11_scratch1; + + resolve_cache_and_index(f12_oop, R17_tos, Rcache, Rscratch, wide ? sizeof(u2) : sizeof(u1)); + + __ verify_oop(R17_tos); + + const Register Rcon_klass = Rcache; + const Register Rarray_klass = R4_ARG2; + + int ary_kl_offs = __ load_const_optimized(Rarray_klass, (address)Universe::systemObjArrayKlassObj_addr(), Rscratch, true); + __ load_klass(Rcon_klass, R17_tos); + __ ld(Rarray_klass, ary_kl_offs, Rarray_klass); + + __ cmpd(CCR0, Rarray_klass, Rcon_klass); + __ bne(CCR0, Ldone); + + // Load array length. + __ lwz(Rcon_klass, arrayOopDesc::length_offset_in_bytes(), R17_tos); + __ cmpwi(CCR0, Rcon_klass, 0); + __ beq(CCR0, LRet0); + + __ load_heap_oop(R17_tos, arrayOopDesc::base_offset_in_bytes(T_OBJECT), R17_tos); + __ load_dispatch_table(R11_scratch1, (address*)Interpreter::throw_exception_entry()); + __ mtctr(R11_scratch1); + __ bctr(); + + __ bind(LRet0); + __ li(R17_tos, 0); + + __ bind(Ldone); +} + +void TemplateTable::ldc2_w() { + transition(vtos, vtos); + Label Llong, Lexit; + + Register Rindex = R11_scratch1, + Rcpool = R12_scratch2, + Rtag = R3_ARG1; + __ get_2_byte_integer_at_bcp(1, Rindex, InterpreterMacroAssembler::Unsigned); + __ get_cpool_and_tags(Rcpool, Rtag); + + const int base_offset = constantPoolOopDesc::header_size() * wordSize; + const int tags_offset = typeArrayOopDesc::header_size(T_BYTE) * wordSize; + // Get type from tags. + __ addi(Rcpool, Rcpool, base_offset); + __ addi(Rtag, Rtag, tags_offset); + + __ lbzx(Rtag, Rtag, Rindex); + + __ sldi(Rindex, Rindex, LogBytesPerWord); + __ cmpdi(CCR0, Rtag, JVM_CONSTANT_Double); + __ bne(CCR0, Llong); + // A double can be placed at word-aligned locations in the constant pool. + // Check out Conversions.java for an example. + // Also constantPoolOopDesc::header_size() is 20, which makes it very difficult + // to double-align double on the constant pool. SG, 11/7/97 + __ isync(); // Order load of constant wrt. tags. + __ lfdx(F15_ftos, Rcpool, Rindex); + __ push(dtos); + __ b(Lexit); + + __ bind(Llong); + __ isync(); // Order load of constant wrt. tags. + __ ldx(R17_tos, Rcpool, Rindex); + __ push(ltos); + + __ bind(Lexit); +} + +// Get the locals index located in the bytecode stream at bcp + offset. +void TemplateTable::locals_index(Register Rdst, int offset) { + __ lbz(Rdst, offset, R14_bcp); +} + +void TemplateTable::iload() { + transition(vtos, itos); + + // Get the local value into tos + const Register Rindex = R22_tmp2; + locals_index(Rindex); + + // Rewrite iload,iload pair into fast_iload2 + // iload,caload pair into fast_icaload + if (RewriteFrequentPairs) { + Label Lrewrite, Ldone; + Register Rnext_byte = R3_ARG1, + Rrewrite_to = R6_ARG4, + Rscratch = R11_scratch1; + + // get next byte + __ lbz(Rnext_byte, Bytecodes::length_for(Bytecodes::_iload), R14_bcp); + + // if _iload, wait to rewrite to iload2. We only want to rewrite the + // last two iloads in a pair. Comparing against fast_iload means that + // the next bytecode is neither an iload or a caload, and therefore + // an iload pair. + __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_iload); + __ beq(CCR0, Ldone); + + __ cmpwi(CCR1, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_iload); + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_iload2); + __ beq(CCR1, Lrewrite); + + __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_caload); + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_icaload); + __ beq(CCR0, Lrewrite); + + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_iload); + + __ bind(Lrewrite); + patch_bytecode(Bytecodes::_iload, Rrewrite_to, Rscratch, false); + __ bind(Ldone); + } + + __ load_local_int(R17_tos, Rindex, Rindex); +} + +// Load 2 integers in a row without dispatching +void TemplateTable::fast_iload2() { + transition(vtos, itos); + + __ lbz(R3_ARG1, 1, R14_bcp); + __ lbz(R17_tos, Bytecodes::length_for(Bytecodes::_iload) + 1, R14_bcp); + + __ load_local_int(R3_ARG1, R11_scratch1, R3_ARG1); + __ load_local_int(R17_tos, R12_scratch2, R17_tos); + __ push_i(R3_ARG1); +} + +void TemplateTable::fast_iload() { + transition(vtos, itos); + // Get the local value into tos + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ load_local_int(R17_tos, Rindex, Rindex); +} + +// Load a local variable type long from locals area to TOS cache register. +// Local index resides in bytecodestream. +void TemplateTable::lload() { + transition(vtos, ltos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ load_local_long(R17_tos, Rindex, Rindex); +} + +void TemplateTable::fload() { + transition(vtos, ftos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ load_local_float(F15_ftos, Rindex, Rindex); +} + +void TemplateTable::dload() { + transition(vtos, dtos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ load_local_double(F15_ftos, Rindex, Rindex); +} + +void TemplateTable::aload() { + transition(vtos, atos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ load_local_ptr(R17_tos, Rindex, Rindex); +} + +void TemplateTable::locals_index_wide(Register Rdst) { + // Offset is 2, not 1, because Lbcp points to wide prefix code. + __ get_2_byte_integer_at_bcp(2, Rdst, InterpreterMacroAssembler::Unsigned); +} + +void TemplateTable::wide_iload() { + // Get the local value into tos. + + const Register Rindex = R11_scratch1; + locals_index_wide(Rindex); + __ load_local_int(R17_tos, Rindex, Rindex); +} + +void TemplateTable::wide_lload() { + transition(vtos, ltos); + + const Register Rindex = R11_scratch1; + locals_index_wide(Rindex); + __ load_local_long(R17_tos, Rindex, Rindex); +} + +void TemplateTable::wide_fload() { + transition(vtos, ftos); + + const Register Rindex = R11_scratch1; + locals_index_wide(Rindex); + __ load_local_float(F15_ftos, Rindex, Rindex); +} + +void TemplateTable::wide_dload() { + transition(vtos, dtos); + + const Register Rindex = R11_scratch1; + locals_index_wide(Rindex); + __ load_local_double(F15_ftos, Rindex, Rindex); +} + +void TemplateTable::wide_aload() { + transition(vtos, atos); + + const Register Rindex = R11_scratch1; + locals_index_wide(Rindex); + __ load_local_ptr(R17_tos, Rindex, Rindex); +} + +void TemplateTable::iaload() { + transition(itos, itos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, LogBytesPerInt, Rtemp, Rload_addr); + __ lwa(R17_tos, arrayOopDesc::base_offset_in_bytes(T_INT), Rload_addr); +} + +void TemplateTable::laload() { + transition(itos, ltos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, LogBytesPerLong, Rtemp, Rload_addr); + __ ld(R17_tos, arrayOopDesc::base_offset_in_bytes(T_LONG), Rload_addr); +} + +void TemplateTable::faload() { + transition(itos, ftos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, LogBytesPerInt, Rtemp, Rload_addr); + __ lfs(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_FLOAT), Rload_addr); +} + +void TemplateTable::daload() { + transition(itos, dtos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, LogBytesPerLong, Rtemp, Rload_addr); + __ lfd(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_DOUBLE), Rload_addr); +} + +void TemplateTable::aaload() { + transition(itos, atos); + + // tos: index + // result tos: array + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, UseCompressedOops ? 2 : LogBytesPerWord, Rtemp, Rload_addr); + __ load_heap_oop(R17_tos, arrayOopDesc::base_offset_in_bytes(T_OBJECT), Rload_addr); + __ verify_oop(R17_tos); + //__ dcbt(R17_tos); // prefetch +} + +void TemplateTable::baload() { + transition(itos, itos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, 0, Rtemp, Rload_addr); + __ lbz(R17_tos, arrayOopDesc::base_offset_in_bytes(T_BYTE), Rload_addr); + __ extsb(R17_tos, R17_tos); +} + +void TemplateTable::caload() { + transition(itos, itos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R5_ARG3; + __ index_check(Rarray, R17_tos /* index */, LogBytesPerShort, Rtemp, Rload_addr); + __ lhz(R17_tos, arrayOopDesc::base_offset_in_bytes(T_CHAR), Rload_addr); +} + +// Iload followed by caload frequent pair. +void TemplateTable::fast_icaload() { + transition(vtos, itos); + + const Register Rload_addr = R3_ARG1, + Rarray = R4_ARG2, + Rtemp = R11_scratch1; + + locals_index(R17_tos); + __ load_local_int(R17_tos, Rtemp, R17_tos); + __ index_check(Rarray, R17_tos /* index */, LogBytesPerShort, Rtemp, Rload_addr); + __ lhz(R17_tos, arrayOopDesc::base_offset_in_bytes(T_CHAR), Rload_addr); +} + +void TemplateTable::saload() { + transition(itos, itos); + + const Register Rload_addr = R11_scratch1, + Rarray = R12_scratch2, + Rtemp = R3_ARG1; + __ index_check(Rarray, R17_tos /* index */, LogBytesPerShort, Rtemp, Rload_addr); + __ lha(R17_tos, arrayOopDesc::base_offset_in_bytes(T_SHORT), Rload_addr); +} + +void TemplateTable::iload(int n) { + transition(vtos, itos); + + __ lwz(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals); +} + +void TemplateTable::lload(int n) { + transition(vtos, ltos); + + __ ld(R17_tos, Interpreter::local_offset_in_bytes(n + 1), R18_locals); +} + +void TemplateTable::fload(int n) { + transition(vtos, ftos); + + __ lfs(F15_ftos, Interpreter::local_offset_in_bytes(n), R18_locals); +} + +void TemplateTable::dload(int n) { + transition(vtos, dtos); + + __ lfd(F15_ftos, Interpreter::local_offset_in_bytes(n + 1), R18_locals); +} + +void TemplateTable::aload(int n) { + transition(vtos, atos); + + __ ld(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals); +} + +void TemplateTable::aload_0() { + transition(vtos, atos); + // According to bytecode histograms, the pairs: + // + // _aload_0, _fast_igetfield + // _aload_0, _fast_agetfield + // _aload_0, _fast_fgetfield + // + // occur frequently. If RewriteFrequentPairs is set, the (slow) + // _aload_0 bytecode checks if the next bytecode is either + // _fast_igetfield, _fast_agetfield or _fast_fgetfield and then + // rewrites the current bytecode into a pair bytecode; otherwise it + // rewrites the current bytecode into _0 that doesn't do + // the pair check anymore. + // + // Note: If the next bytecode is _getfield, the rewrite must be + // delayed, otherwise we may miss an opportunity for a pair. + // + // Also rewrite frequent pairs + // aload_0, aload_1 + // aload_0, iload_1 + // These bytecodes with a small amount of code are most profitable + // to rewrite. + + if (RewriteFrequentPairs) { + + Label Lrewrite, Ldont_rewrite; + Register Rnext_byte = R3_ARG1, + Rrewrite_to = R6_ARG4, + Rscratch = R11_scratch1; + + // Get next byte. + __ lbz(Rnext_byte, Bytecodes::length_for(Bytecodes::_aload_0), R14_bcp); + + // If _getfield, wait to rewrite. We only want to rewrite the last two bytecodes in a pair. + __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_getfield); + __ beq(CCR0, Ldont_rewrite); + + __ cmpwi(CCR1, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_igetfield); + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_iaccess_0); + __ beq(CCR1, Lrewrite); + + __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_agetfield); + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_aaccess_0); + __ beq(CCR0, Lrewrite); + + __ cmpwi(CCR1, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_fgetfield); + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_faccess_0); + __ beq(CCR1, Lrewrite); + + __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_aload_0); + + __ bind(Lrewrite); + patch_bytecode(Bytecodes::_aload_0, Rrewrite_to, Rscratch, false); + __ bind(Ldont_rewrite); + } + + // Do actual aload_0 (must do this after patch_bytecode which might call VM and GC might change oop). + aload(0); +} + +void TemplateTable::istore() { + transition(itos, vtos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ store_local_int(R17_tos, Rindex); +} + +void TemplateTable::lstore() { + transition(ltos, vtos); + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ store_local_long(R17_tos, Rindex); +} + +void TemplateTable::fstore() { + transition(ftos, vtos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ store_local_float(F15_ftos, Rindex); +} + +void TemplateTable::dstore() { + transition(dtos, vtos); + + const Register Rindex = R11_scratch1; + locals_index(Rindex); + __ store_local_double(F15_ftos, Rindex); +} + +void TemplateTable::astore() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1; + __ pop_ptr(); + __ verify_oop_or_return_address(R17_tos, Rindex); + locals_index(Rindex); + __ store_local_ptr(R17_tos, Rindex); +} + +void TemplateTable::wide_istore() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1; + __ pop_i(); + locals_index_wide(Rindex); + __ store_local_int(R17_tos, Rindex); +} + +void TemplateTable::wide_lstore() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1; + __ pop_l(); + locals_index_wide(Rindex); + __ store_local_long(R17_tos, Rindex); +} + +void TemplateTable::wide_fstore() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1; + __ pop_f(); + locals_index_wide(Rindex); + __ store_local_float(F15_ftos, Rindex); +} + +void TemplateTable::wide_dstore() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1; + __ pop_d(); + locals_index_wide(Rindex); + __ store_local_double(F15_ftos, Rindex); +} + +void TemplateTable::wide_astore() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1; + __ pop_ptr(); + __ verify_oop_or_return_address(R17_tos, Rindex); + locals_index_wide(Rindex); + __ store_local_ptr(R17_tos, Rindex); +} + +void TemplateTable::iastore() { + transition(itos, vtos); + + const Register Rindex = R3_ARG1, + Rstore_addr = R4_ARG2, + Rarray = R5_ARG3, + Rtemp = R6_ARG4; + __ pop_i(Rindex); + __ index_check(Rarray, Rindex, LogBytesPerInt, Rtemp, Rstore_addr); + __ stw(R17_tos, arrayOopDesc::base_offset_in_bytes(T_INT), Rstore_addr); + } + +void TemplateTable::lastore() { + transition(ltos, vtos); + + const Register Rindex = R3_ARG1, + Rstore_addr = R4_ARG2, + Rarray = R5_ARG3, + Rtemp = R6_ARG4; + __ pop_i(Rindex); + __ index_check(Rarray, Rindex, LogBytesPerLong, Rtemp, Rstore_addr); + __ std(R17_tos, arrayOopDesc::base_offset_in_bytes(T_LONG), Rstore_addr); + } + +void TemplateTable::fastore() { + transition(ftos, vtos); + + const Register Rindex = R3_ARG1, + Rstore_addr = R4_ARG2, + Rarray = R5_ARG3, + Rtemp = R6_ARG4; + __ pop_i(Rindex); + __ index_check(Rarray, Rindex, LogBytesPerInt, Rtemp, Rstore_addr); + __ stfs(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_FLOAT), Rstore_addr); + } + +void TemplateTable::dastore() { + transition(dtos, vtos); + + const Register Rindex = R3_ARG1, + Rstore_addr = R4_ARG2, + Rarray = R5_ARG3, + Rtemp = R6_ARG4; + __ pop_i(Rindex); + __ index_check(Rarray, Rindex, LogBytesPerLong, Rtemp, Rstore_addr); + __ stfd(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_DOUBLE), Rstore_addr); + } + +// Pop 3 values from the stack and... +void TemplateTable::aastore() { + transition(vtos, vtos); + + Label Lstore_ok, Lis_null, Ldone; + const Register Rindex = R3_ARG1, + Rarray = R4_ARG2, + Rscratch = R11_scratch1, + Rscratch2 = R12_scratch2, + Rarray_klass = R5_ARG3, + Rarray_element_klass = Rarray_klass, + Rvalue_klass = R6_ARG4, + Rstore_addr = R31; // Use register which survives VM call. + + __ ld(R17_tos, Interpreter::expr_offset_in_bytes(0), R15_esp); // Get value to store. + __ lwz(Rindex, Interpreter::expr_offset_in_bytes(1), R15_esp); // Get index. + __ ld(Rarray, Interpreter::expr_offset_in_bytes(2), R15_esp); // Get array. + + __ verify_oop(R17_tos); + __ index_check_without_pop(Rarray, Rindex, UseCompressedOops ? 2 : LogBytesPerWord, Rscratch, Rstore_addr); + // Rindex is dead! + Register Rscratch3 = Rindex; + + // Do array store check - check for NULL value first. + __ cmpdi(CCR0, R17_tos, 0); + __ beq(CCR0, Lis_null); + + __ load_klass(Rarray_klass, Rarray); + __ load_klass(Rvalue_klass, R17_tos); + + // Do fast instanceof cache test. + __ ld(Rarray_element_klass, in_bytes(objArrayKlass::element_klass_offset()), Rarray_klass); + + // Generate a fast subtype check. Branch to store_ok if no failure. Throw if failure. + __ gen_subtype_check(Rvalue_klass /*subklass*/, Rarray_element_klass /*superklass*/, Rscratch, Rscratch2, Rscratch3, Lstore_ok); + + // Fell through: subtype check failed => throw an exception. + __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ArrayStoreException_entry); + __ mtctr(R11_scratch1); + __ bctr(); + + __ bind(Lis_null); + do_oop_store(_masm, Rstore_addr, arrayOopDesc::base_offset_in_bytes(T_OBJECT), noreg /* 0 */, + Rscratch, Rscratch2, Rscratch3, _bs->kind(), true /* precise */, false /* check_null */); + __ profile_null_seen(Rscratch, Rscratch2); + __ b(Ldone); + + // Store is OK. + __ bind(Lstore_ok); + do_oop_store(_masm, Rstore_addr, arrayOopDesc::base_offset_in_bytes(T_OBJECT), R17_tos /* value */, + Rscratch, Rscratch2, Rscratch3, _bs->kind(), true /* precise */, false /* check_null */); + + __ bind(Ldone); + // Adjust sp (pops array, index and value). + __ addi(R15_esp, R15_esp, 3 * Interpreter::stackElementSize); +} + +void TemplateTable::bastore() { + transition(itos, vtos); + + const Register Rindex = R11_scratch1, + Rarray = R12_scratch2, + Rscratch = R3_ARG1; + __ pop_i(Rindex); + __ pop_ptr(Rarray); + // tos: val + + // Need to check whether array is boolean or byte + // since both types share the bastore bytecode. + __ load_klass(Rscratch, Rarray); + __ lwz(Rscratch, in_bytes(Klass::layout_helper_offset()), Rscratch); + int diffbit = exact_log2(Klass::layout_helper_boolean_diffbit()); + __ testbitdi(CCR0, R0, Rscratch, diffbit); + Label L_skip; + __ bfalse(CCR0, L_skip); + __ andi(R17_tos, R17_tos, 1); // if it is a T_BOOLEAN array, mask the stored value to 0/1 + __ bind(L_skip); + + __ index_check_without_pop(Rarray, Rindex, 0, Rscratch, Rarray); + __ stb(R17_tos, arrayOopDesc::base_offset_in_bytes(T_BYTE), Rarray); +} + +void TemplateTable::castore() { + transition(itos, vtos); + + const Register Rindex = R11_scratch1, + Rarray = R12_scratch2, + Rscratch = R3_ARG1; + __ pop_i(Rindex); + // tos: val + // Rarray: array ptr (popped by index_check) + __ index_check(Rarray, Rindex, LogBytesPerShort, Rscratch, Rarray); + __ sth(R17_tos, arrayOopDesc::base_offset_in_bytes(T_CHAR), Rarray); +} + +void TemplateTable::sastore() { + castore(); +} + +void TemplateTable::istore(int n) { + transition(itos, vtos); + __ stw(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals); +} + +void TemplateTable::lstore(int n) { + transition(ltos, vtos); + __ std(R17_tos, Interpreter::local_offset_in_bytes(n + 1), R18_locals); +} + +void TemplateTable::fstore(int n) { + transition(ftos, vtos); + __ stfs(F15_ftos, Interpreter::local_offset_in_bytes(n), R18_locals); +} + +void TemplateTable::dstore(int n) { + transition(dtos, vtos); + __ stfd(F15_ftos, Interpreter::local_offset_in_bytes(n + 1), R18_locals); +} + +void TemplateTable::astore(int n) { + transition(vtos, vtos); + + __ pop_ptr(); + __ verify_oop_or_return_address(R17_tos, R11_scratch1); + __ std(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals); +} + +void TemplateTable::pop() { + transition(vtos, vtos); + + __ addi(R15_esp, R15_esp, Interpreter::stackElementSize); +} + +void TemplateTable::pop2() { + transition(vtos, vtos); + + __ addi(R15_esp, R15_esp, Interpreter::stackElementSize * 2); +} + +void TemplateTable::dup() { + transition(vtos, vtos); + + __ ld(R11_scratch1, Interpreter::stackElementSize, R15_esp); + __ push_ptr(R11_scratch1); +} + +void TemplateTable::dup_x1() { + transition(vtos, vtos); + + Register Ra = R11_scratch1, + Rb = R12_scratch2; + // stack: ..., a, b + __ ld(Rb, Interpreter::stackElementSize, R15_esp); + __ ld(Ra, Interpreter::stackElementSize * 2, R15_esp); + __ std(Rb, Interpreter::stackElementSize * 2, R15_esp); + __ std(Ra, Interpreter::stackElementSize, R15_esp); + __ push_ptr(Rb); + // stack: ..., b, a, b +} + +void TemplateTable::dup_x2() { + transition(vtos, vtos); + + Register Ra = R11_scratch1, + Rb = R12_scratch2, + Rc = R3_ARG1; + + // stack: ..., a, b, c + __ ld(Rc, Interpreter::stackElementSize, R15_esp); // load c + __ ld(Ra, Interpreter::stackElementSize * 3, R15_esp); // load a + __ std(Rc, Interpreter::stackElementSize * 3, R15_esp); // store c in a + __ ld(Rb, Interpreter::stackElementSize * 2, R15_esp); // load b + // stack: ..., c, b, c + __ std(Ra, Interpreter::stackElementSize * 2, R15_esp); // store a in b + // stack: ..., c, a, c + __ std(Rb, Interpreter::stackElementSize, R15_esp); // store b in c + __ push_ptr(Rc); // push c + // stack: ..., c, a, b, c +} + +void TemplateTable::dup2() { + transition(vtos, vtos); + + Register Ra = R11_scratch1, + Rb = R12_scratch2; + // stack: ..., a, b + __ ld(Rb, Interpreter::stackElementSize, R15_esp); + __ ld(Ra, Interpreter::stackElementSize * 2, R15_esp); + __ push_2ptrs(Ra, Rb); + // stack: ..., a, b, a, b +} + +void TemplateTable::dup2_x1() { + transition(vtos, vtos); + + Register Ra = R11_scratch1, + Rb = R12_scratch2, + Rc = R3_ARG1; + // stack: ..., a, b, c + __ ld(Rc, Interpreter::stackElementSize, R15_esp); + __ ld(Rb, Interpreter::stackElementSize * 2, R15_esp); + __ std(Rc, Interpreter::stackElementSize * 2, R15_esp); + __ ld(Ra, Interpreter::stackElementSize * 3, R15_esp); + __ std(Ra, Interpreter::stackElementSize, R15_esp); + __ std(Rb, Interpreter::stackElementSize * 3, R15_esp); + // stack: ..., b, c, a + __ push_2ptrs(Rb, Rc); + // stack: ..., b, c, a, b, c +} + +void TemplateTable::dup2_x2() { + transition(vtos, vtos); + + Register Ra = R11_scratch1, + Rb = R12_scratch2, + Rc = R3_ARG1, + Rd = R4_ARG2; + // stack: ..., a, b, c, d + __ ld(Rb, Interpreter::stackElementSize * 3, R15_esp); + __ ld(Rd, Interpreter::stackElementSize, R15_esp); + __ std(Rb, Interpreter::stackElementSize, R15_esp); // store b in d + __ std(Rd, Interpreter::stackElementSize * 3, R15_esp); // store d in b + __ ld(Ra, Interpreter::stackElementSize * 4, R15_esp); + __ ld(Rc, Interpreter::stackElementSize * 2, R15_esp); + __ std(Ra, Interpreter::stackElementSize * 2, R15_esp); // store a in c + __ std(Rc, Interpreter::stackElementSize * 4, R15_esp); // store c in a + // stack: ..., c, d, a, b + __ push_2ptrs(Rc, Rd); + // stack: ..., c, d, a, b, c, d +} + +void TemplateTable::swap() { + transition(vtos, vtos); + // stack: ..., a, b + + Register Ra = R11_scratch1, + Rb = R12_scratch2; + // stack: ..., a, b + __ ld(Rb, Interpreter::stackElementSize, R15_esp); + __ ld(Ra, Interpreter::stackElementSize * 2, R15_esp); + __ std(Rb, Interpreter::stackElementSize * 2, R15_esp); + __ std(Ra, Interpreter::stackElementSize, R15_esp); + // stack: ..., b, a +} + +void TemplateTable::iop2(Operation op) { + transition(itos, itos); + + Register Rscratch = R11_scratch1; + + __ pop_i(Rscratch); + // tos = number of bits to shift + // Rscratch = value to shift + switch (op) { + case add: __ add(R17_tos, Rscratch, R17_tos); break; + case sub: __ sub(R17_tos, Rscratch, R17_tos); break; + case mul: __ mullw(R17_tos, Rscratch, R17_tos); break; + case _and: __ andr(R17_tos, Rscratch, R17_tos); break; + case _or: __ orr(R17_tos, Rscratch, R17_tos); break; + case _xor: __ xorr(R17_tos, Rscratch, R17_tos); break; + case shl: __ rldicl(R17_tos, R17_tos, 0, 64-5); __ slw(R17_tos, Rscratch, R17_tos); break; + case shr: __ rldicl(R17_tos, R17_tos, 0, 64-5); __ sraw(R17_tos, Rscratch, R17_tos); break; + case ushr: __ rldicl(R17_tos, R17_tos, 0, 64-5); __ srw(R17_tos, Rscratch, R17_tos); break; + default: ShouldNotReachHere(); + } +} + +void TemplateTable::lop2(Operation op) { + transition(ltos, ltos); + + Register Rscratch = R11_scratch1; + __ pop_l(Rscratch); + switch (op) { + case add: __ add(R17_tos, Rscratch, R17_tos); break; + case sub: __ sub(R17_tos, Rscratch, R17_tos); break; + case _and: __ andr(R17_tos, Rscratch, R17_tos); break; + case _or: __ orr(R17_tos, Rscratch, R17_tos); break; + case _xor: __ xorr(R17_tos, Rscratch, R17_tos); break; + default: ShouldNotReachHere(); + } +} + +void TemplateTable::idiv() { + transition(itos, itos); + + Label Lnormal, Lexception, Ldone; + Register Rdividend = R11_scratch1; // Used by irem. + + __ addi(R0, R17_tos, 1); + __ cmplwi(CCR0, R0, 2); + __ bgt(CCR0, Lnormal); // divisor <-1 or >1 + + __ cmpwi(CCR1, R17_tos, 0); + __ beq(CCR1, Lexception); // divisor == 0 + + __ pop_i(Rdividend); + __ mullw(R17_tos, Rdividend, R17_tos); // div by +/-1 + __ b(Ldone); + + __ bind(Lexception); + __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ArithmeticException_entry); + __ mtctr(R11_scratch1); + __ bctr(); + + __ align(32, 12); + __ bind(Lnormal); + __ pop_i(Rdividend); + __ divw(R17_tos, Rdividend, R17_tos); // Can't divide minint/-1. + __ bind(Ldone); +} + +void TemplateTable::irem() { + transition(itos, itos); + + __ mr(R12_scratch2, R17_tos); + idiv(); + __ mullw(R17_tos, R17_tos, R12_scratch2); + __ subf(R17_tos, R17_tos, R11_scratch1); // Dividend set by idiv. +} + +void TemplateTable::lmul() { + transition(ltos, ltos); + + __ pop_l(R11_scratch1); + __ mulld(R17_tos, R11_scratch1, R17_tos); +} + +void TemplateTable::ldiv() { + transition(ltos, ltos); + + Label Lnormal, Lexception, Ldone; + Register Rdividend = R11_scratch1; // Used by lrem. + + __ addi(R0, R17_tos, 1); + __ cmpldi(CCR0, R0, 2); + __ bgt(CCR0, Lnormal); // divisor <-1 or >1 + + __ cmpdi(CCR1, R17_tos, 0); + __ beq(CCR1, Lexception); // divisor == 0 + + __ pop_l(Rdividend); + __ mulld(R17_tos, Rdividend, R17_tos); // div by +/-1 + __ b(Ldone); + + __ bind(Lexception); + __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ArithmeticException_entry); + __ mtctr(R11_scratch1); + __ bctr(); + + __ align(32, 12); + __ bind(Lnormal); + __ pop_l(Rdividend); + __ divd(R17_tos, Rdividend, R17_tos); // Can't divide minint/-1. + __ bind(Ldone); +} + +void TemplateTable::lrem() { + transition(ltos, ltos); + + __ mr(R12_scratch2, R17_tos); + ldiv(); + __ mulld(R17_tos, R17_tos, R12_scratch2); + __ subf(R17_tos, R17_tos, R11_scratch1); // Dividend set by ldiv. +} + +void TemplateTable::lshl() { + transition(itos, ltos); + + __ rldicl(R17_tos, R17_tos, 0, 64-6); // Extract least significant bits. + __ pop_l(R11_scratch1); + __ sld(R17_tos, R11_scratch1, R17_tos); +} + +void TemplateTable::lshr() { + transition(itos, ltos); + + __ rldicl(R17_tos, R17_tos, 0, 64-6); // Extract least significant bits. + __ pop_l(R11_scratch1); + __ srad(R17_tos, R11_scratch1, R17_tos); +} + +void TemplateTable::lushr() { + transition(itos, ltos); + + __ rldicl(R17_tos, R17_tos, 0, 64-6); // Extract least significant bits. + __ pop_l(R11_scratch1); + __ srd(R17_tos, R11_scratch1, R17_tos); +} + +void TemplateTable::fop2(Operation op) { + transition(ftos, ftos); + + switch (op) { + case add: __ pop_f(F0_SCRATCH); __ fadds(F15_ftos, F0_SCRATCH, F15_ftos); break; + case sub: __ pop_f(F0_SCRATCH); __ fsubs(F15_ftos, F0_SCRATCH, F15_ftos); break; + case mul: __ pop_f(F0_SCRATCH); __ fmuls(F15_ftos, F0_SCRATCH, F15_ftos); break; + case div: __ pop_f(F0_SCRATCH); __ fdivs(F15_ftos, F0_SCRATCH, F15_ftos); break; + case rem: + __ pop_f(F1_ARG1); + __ fmr(F2_ARG2, F15_ftos); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::frem)); + __ fmr(F15_ftos, F1_RET); + break; + + default: ShouldNotReachHere(); + } +} + +void TemplateTable::dop2(Operation op) { + transition(dtos, dtos); + + switch (op) { + case add: __ pop_d(F0_SCRATCH); __ fadd(F15_ftos, F0_SCRATCH, F15_ftos); break; + case sub: __ pop_d(F0_SCRATCH); __ fsub(F15_ftos, F0_SCRATCH, F15_ftos); break; + case mul: __ pop_d(F0_SCRATCH); __ fmul(F15_ftos, F0_SCRATCH, F15_ftos); break; + case div: __ pop_d(F0_SCRATCH); __ fdiv(F15_ftos, F0_SCRATCH, F15_ftos); break; + case rem: + __ pop_d(F1_ARG1); + __ fmr(F2_ARG2, F15_ftos); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::drem)); + __ fmr(F15_ftos, F1_RET); + break; + + default: ShouldNotReachHere(); + } +} + +// Negate the value in the TOS cache. +void TemplateTable::ineg() { + transition(itos, itos); + + __ neg(R17_tos, R17_tos); +} + +// Negate the value in the TOS cache. +void TemplateTable::lneg() { + transition(ltos, ltos); + + __ neg(R17_tos, R17_tos); +} + +void TemplateTable::fneg() { + transition(ftos, ftos); + + __ fneg(F15_ftos, F15_ftos); +} + +void TemplateTable::dneg() { + transition(dtos, dtos); + + __ fneg(F15_ftos, F15_ftos); +} + +// Increments a local variable in place. +void TemplateTable::iinc() { + transition(vtos, vtos); + + const Register Rindex = R11_scratch1, + Rincrement = R0, + Rvalue = R12_scratch2; + + locals_index(Rindex); // Load locals index from bytecode stream. + __ lbz(Rincrement, 2, R14_bcp); // Load increment from the bytecode stream. + __ extsb(Rincrement, Rincrement); + + __ load_local_int(Rvalue, Rindex, Rindex); // Puts address of local into Rindex. + + __ add(Rvalue, Rincrement, Rvalue); + __ stw(Rvalue, 0, Rindex); +} + +void TemplateTable::wide_iinc() { + transition(vtos, vtos); + + Register Rindex = R11_scratch1, + Rlocals_addr = Rindex, + Rincr = R12_scratch2; + locals_index_wide(Rindex); + __ get_2_byte_integer_at_bcp(4, Rincr, InterpreterMacroAssembler::Signed); + __ load_local_int(R17_tos, Rlocals_addr, Rindex); + __ add(R17_tos, Rincr, R17_tos); + __ stw(R17_tos, 0, Rlocals_addr); +} + +void TemplateTable::convert() { + // %%%%% Factor this first part accross platforms +#ifdef ASSERT + TosState tos_in = ilgl; + TosState tos_out = ilgl; + switch (bytecode()) { + case Bytecodes::_i2l: // fall through + case Bytecodes::_i2f: // fall through + case Bytecodes::_i2d: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_in = itos; break; + case Bytecodes::_l2i: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_l2d: tos_in = ltos; break; + case Bytecodes::_f2i: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_f2d: tos_in = ftos; break; + case Bytecodes::_d2i: // fall through + case Bytecodes::_d2l: // fall through + case Bytecodes::_d2f: tos_in = dtos; break; + default : ShouldNotReachHere(); + } + switch (bytecode()) { + case Bytecodes::_l2i: // fall through + case Bytecodes::_f2i: // fall through + case Bytecodes::_d2i: // fall through + case Bytecodes::_i2b: // fall through + case Bytecodes::_i2c: // fall through + case Bytecodes::_i2s: tos_out = itos; break; + case Bytecodes::_i2l: // fall through + case Bytecodes::_f2l: // fall through + case Bytecodes::_d2l: tos_out = ltos; break; + case Bytecodes::_i2f: // fall through + case Bytecodes::_l2f: // fall through + case Bytecodes::_d2f: tos_out = ftos; break; + case Bytecodes::_i2d: // fall through + case Bytecodes::_l2d: // fall through + case Bytecodes::_f2d: tos_out = dtos; break; + default : ShouldNotReachHere(); + } + transition(tos_in, tos_out); +#endif + + // Conversion + Label done; + switch (bytecode()) { + case Bytecodes::_i2l: + __ extsw(R17_tos, R17_tos); + break; + + case Bytecodes::_l2i: + // Nothing to do, we'll continue to work with the lower bits. + break; + + case Bytecodes::_i2b: + __ extsb(R17_tos, R17_tos); + break; + + case Bytecodes::_i2c: + __ rldicl(R17_tos, R17_tos, 0, 64-2*8); + break; + + case Bytecodes::_i2s: + __ extsh(R17_tos, R17_tos); + break; + + case Bytecodes::_i2d: + __ extsw(R17_tos, R17_tos); + case Bytecodes::_l2d: + __ push_l_pop_d(); + __ fcfid(F15_ftos, F15_ftos); + break; + + case Bytecodes::_i2f: + __ extsw(R17_tos, R17_tos); + __ push_l_pop_d(); + if (VM_Version::has_fcfids()) { // fcfids is >= Power7 only + // Comment: alternatively, load with sign extend could be done by lfiwax. + __ fcfids(F15_ftos, F15_ftos); + } else { + __ fcfid(F15_ftos, F15_ftos); + __ frsp(F15_ftos, F15_ftos); + } + break; + + case Bytecodes::_l2f: + if (VM_Version::has_fcfids()) { // fcfids is >= Power7 only + __ push_l_pop_d(); + __ fcfids(F15_ftos, F15_ftos); + } else { + // Avoid rounding problem when result should be 0x3f800001: need fixup code before fcfid+frsp. + __ mr(R3_ARG1, R17_tos); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::l2f)); + __ fmr(F15_ftos, F1_RET); + } + break; + + case Bytecodes::_f2d: + // empty + break; + + case Bytecodes::_d2f: + __ frsp(F15_ftos, F15_ftos); + break; + + case Bytecodes::_d2i: + case Bytecodes::_f2i: + __ fcmpu(CCR0, F15_ftos, F15_ftos); + __ li(R17_tos, 0); // 0 in case of NAN + __ bso(CCR0, done); + __ fctiwz(F15_ftos, F15_ftos); + __ push_d_pop_l(); + break; + + case Bytecodes::_d2l: + case Bytecodes::_f2l: + __ fcmpu(CCR0, F15_ftos, F15_ftos); + __ li(R17_tos, 0); // 0 in case of NAN + __ bso(CCR0, done); + __ fctidz(F15_ftos, F15_ftos); + __ push_d_pop_l(); + break; + + default: ShouldNotReachHere(); + } + __ bind(done); +} + +// Long compare +void TemplateTable::lcmp() { + transition(ltos, itos); + + const Register Rscratch = R11_scratch1; + __ pop_l(Rscratch); // first operand, deeper in stack + + __ cmpd(CCR0, Rscratch, R17_tos); // compare + __ mfcr(R17_tos); // set bit 32..33 as follows: <: 0b10, =: 0b00, >: 0b01 + __ srwi(Rscratch, R17_tos, 30); + __ srawi(R17_tos, R17_tos, 31); + __ orr(R17_tos, Rscratch, R17_tos); // set result as follows: <: -1, =: 0, >: 1 +} + +// fcmpl/fcmpg and dcmpl/dcmpg bytecodes +// unordered_result == -1 => fcmpl or dcmpl +// unordered_result == 1 => fcmpg or dcmpg +void TemplateTable::float_cmp(bool is_float, int unordered_result) { + const FloatRegister Rfirst = F0_SCRATCH, + Rsecond = F15_ftos; + const Register Rscratch = R11_scratch1; + + if (is_float) { + __ pop_f(Rfirst); + } else { + __ pop_d(Rfirst); + } + + Label Lunordered, Ldone; + __ fcmpu(CCR0, Rfirst, Rsecond); // compare + if (unordered_result) { + __ bso(CCR0, Lunordered); + } + __ mfcr(R17_tos); // set bit 32..33 as follows: <: 0b10, =: 0b00, >: 0b01 + __ srwi(Rscratch, R17_tos, 30); + __ srawi(R17_tos, R17_tos, 31); + __ orr(R17_tos, Rscratch, R17_tos); // set result as follows: <: -1, =: 0, >: 1 + if (unordered_result) { + __ b(Ldone); + __ bind(Lunordered); + __ load_const_optimized(R17_tos, unordered_result); + } + __ bind(Ldone); +} + +// Branch_conditional which takes TemplateTable::Condition. +void TemplateTable::branch_conditional(ConditionRegister crx, TemplateTable::Condition cc, Label& L, bool invert) { + bool positive = false; + Assembler::Condition cond = Assembler::equal; + switch (cc) { + case TemplateTable::equal: positive = true ; cond = Assembler::equal ; break; + case TemplateTable::not_equal: positive = false; cond = Assembler::equal ; break; + case TemplateTable::less: positive = true ; cond = Assembler::less ; break; + case TemplateTable::less_equal: positive = false; cond = Assembler::greater; break; + case TemplateTable::greater: positive = true ; cond = Assembler::greater; break; + case TemplateTable::greater_equal: positive = false; cond = Assembler::less ; break; + default: ShouldNotReachHere(); + } + int bo = (positive != invert) ? Assembler::bcondCRbiIs1 : Assembler::bcondCRbiIs0; + int bi = Assembler::bi0(crx, cond); + __ bc(bo, bi, L); +} + +void TemplateTable::branch(bool is_jsr, bool is_wide) { + + // Note: on SPARC, we use InterpreterMacroAssembler::if_cmp also. + __ verify_oop(R19_method); + __ verify_thread(); + + const Register Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2, + Rscratch3 = R3_ARG1, + R4_counters = R4_ARG2, + bumped_count = R31, + Rdisp = R22_tmp2; + + __ profile_taken_branch(Rscratch1, bumped_count); + + // Get (wide) offset. + if (is_wide) { + __ get_4_byte_integer_at_bcp(1, Rdisp, InterpreterMacroAssembler::Signed); + } else { + __ get_2_byte_integer_at_bcp(1, Rdisp, InterpreterMacroAssembler::Signed); + } + + // -------------------------------------------------------------------------- + // Handle all the JSR stuff here, then exit. + // It's much shorter and cleaner than intermingling with the + // non-JSR normal-branch stuff occurring below. + if (is_jsr) { + // Compute return address as bci in Otos_i. + __ ld(Rscratch1, in_bytes(methodOopDesc::const_offset()), R19_method); + __ addi(Rscratch2, R14_bcp, -in_bytes(constMethodOopDesc::codes_offset()) + (is_wide ? 5 : 3)); + __ subf(R17_tos, Rscratch1, Rscratch2); + + // Bump bcp to target of JSR. + __ add(R14_bcp, Rdisp, R14_bcp); + // Push returnAddress for "ret" on stack. + __ push_ptr(R17_tos); + // And away we go! + __ dispatch_next(vtos); + return; + } + + // -------------------------------------------------------------------------- + // Normal (non-jsr) branch handling + + const bool increment_invocation_counter_for_backward_branches = UseCompiler && UseLoopCounter; + if (increment_invocation_counter_for_backward_branches) { + //__ unimplemented("branch invocation counter"); + + Label Lforward; + __ add(R14_bcp, Rdisp, R14_bcp); // Add to bc addr. + + // Check branch direction. + __ cmpdi(CCR0, Rdisp, 0); + __ bgt(CCR0, Lforward); + + //__ get_method_counters(R19_method, R4_counters, Lforward); + + if (TieredCompilation) { + Label Lno_mdo, Loverflow; + const int increment = InvocationCounter::count_increment; + const int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift; + if (ProfileInterpreter) { + Register Rmdo = Rscratch1; + + // If no method data exists, go to profile_continue. + __ ld(Rmdo, in_bytes(methodOopDesc::method_data_offset()), R19_method); + __ cmpdi(CCR0, Rmdo, 0); + __ beq(CCR0, Lno_mdo); + + // Increment backedge counter in the MDO. + const int mdo_bc_offs = in_bytes(methodDataOopDesc::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()); + __ lwz(Rscratch2, mdo_bc_offs, Rmdo); + __ load_const_optimized(Rscratch3, mask, R0); + __ addi(Rscratch2, Rscratch2, increment); + __ stw(Rscratch2, mdo_bc_offs, Rmdo); + __ and_(Rscratch3, Rscratch2, Rscratch3); + __ bne(CCR0, Lforward); + __ b(Loverflow); + } + + // If there's no MDO, increment counter in methodOop. + const int mo_bc_offs = in_bytes(methodOopDesc::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()); + __ bind(Lno_mdo); + __ lwz(Rscratch2, mo_bc_offs, R19_method); + __ load_const_optimized(Rscratch3, mask, R0); + __ addi(Rscratch2, Rscratch2, increment); + __ stw(Rscratch2, mo_bc_offs, R19_method); + __ and_(Rscratch3, Rscratch2, Rscratch3); + __ bne(CCR0, Lforward); + + __ bind(Loverflow); + + // Notify point for loop, pass branch bytecode. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), R14_bcp, true); + + // Was an OSR adapter generated? + // O0 = osr nmethod + __ cmpdi(CCR0, R3_RET, 0); + __ beq(CCR0, Lforward); + + // Has the nmethod been invalidated already? + __ lwz(R0, nmethod::entry_bci_offset(), R3_RET); + __ cmpwi(CCR0, R0, InvalidOSREntryBci); + __ beq(CCR0, Lforward); + + // Migrate the interpreter frame off of the stack. + // We can use all registers because we will not return to interpreter from this point. + + // Save nmethod. + const Register osr_nmethod = R31; + __ mr(osr_nmethod, R3_RET); + __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R11_scratch1); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin), R16_thread); + __ reset_last_Java_frame(); + // OSR buffer is in ARG1. + + // Remove the interpreter frame. + __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2); + + // Jump to the osr code. + __ ld(R11_scratch1, nmethod::osr_entry_point_offset(), osr_nmethod); + __ mtlr(R0); + __ mtctr(R11_scratch1); + __ bctr(); + + } else { + + const Register invoke_ctr = Rscratch1; + // Update Backedge branch separately from invocations. + __ increment_backedge_counter(invoke_ctr, Rscratch2, Rscratch3); + + if (ProfileInterpreter) { + __ test_invocation_counter_for_mdp(invoke_ctr, Rscratch2, Lforward); + if (UseOnStackReplacement) { + __ test_backedge_count_for_osr(bumped_count, R14_bcp, Rscratch2); + } + } else { + if (UseOnStackReplacement) { + __ test_backedge_count_for_osr(invoke_ctr, R14_bcp, Rscratch2); + } + } + } + + __ bind(Lforward); + + } else { + // Bump bytecode pointer by displacement (take the branch). + __ add(R14_bcp, Rdisp, R14_bcp); // Add to bc addr. + } + // Continue with bytecode @ target. + // %%%%% Like Intel, could speed things up by moving bytecode fetch to code above, + // %%%%% and changing dispatch_next to dispatch_only. + __ dispatch_next(vtos); +} + +// Helper function for if_cmp* methods below. +// Factored out common compare and branch code. +void TemplateTable::if_cmp_common(Register Rfirst, Register Rsecond, Register Rscratch1, Register Rscratch2, Condition cc, bool is_jint, bool cmp0) { + Label Lnot_taken; + // Note: The condition code we get is the condition under which we + // *fall through*! So we have to inverse the CC here. + + if (is_jint) { + if (cmp0) { + __ cmpwi(CCR0, Rfirst, 0); + } else { + __ cmpw(CCR0, Rfirst, Rsecond); + } + } else { + if (cmp0) { + __ cmpdi(CCR0, Rfirst, 0); + } else { + __ cmpd(CCR0, Rfirst, Rsecond); + } + } + branch_conditional(CCR0, cc, Lnot_taken, /*invert*/ true); + + // Conition is false => Jump! + branch(false, false); + + // Condition is not true => Continue. + __ align(32, 12); + __ bind(Lnot_taken); + __ profile_not_taken_branch(Rscratch1, Rscratch2); +} + +// Compare integer values with zero and fall through if CC holds, branch away otherwise. +void TemplateTable::if_0cmp(Condition cc) { + transition(itos, vtos); + + if_cmp_common(R17_tos, noreg, R11_scratch1, R12_scratch2, cc, true, true); +} + +// Compare integer values and fall through if CC holds, branch away otherwise. +// +// Interface: +// - Rfirst: First operand (older stack value) +// - tos: Second operand (younger stack value) +void TemplateTable::if_icmp(Condition cc) { + transition(itos, vtos); + + const Register Rfirst = R0, + Rsecond = R17_tos; + + __ pop_i(Rfirst); + if_cmp_common(Rfirst, Rsecond, R11_scratch1, R12_scratch2, cc, true, false); +} + +void TemplateTable::if_nullcmp(Condition cc) { + transition(atos, vtos); + + if_cmp_common(R17_tos, noreg, R11_scratch1, R12_scratch2, cc, false, true); +} + +void TemplateTable::if_acmp(Condition cc) { + transition(atos, vtos); + + const Register Rfirst = R0, + Rsecond = R17_tos; + + __ pop_ptr(Rfirst); + if_cmp_common(Rfirst, Rsecond, R11_scratch1, R12_scratch2, cc, false, false); +} + +void TemplateTable::ret() { + locals_index(R11_scratch1); + __ load_local_ptr(R17_tos, R11_scratch1, R11_scratch1); + + __ profile_ret(vtos, R17_tos, R11_scratch1, R12_scratch2); + + __ ld(R11_scratch1, in_bytes(methodOopDesc::const_offset()), R19_method); + __ add(R11_scratch1, R17_tos, R11_scratch1); + __ addi(R14_bcp, R11_scratch1, in_bytes(constMethodOopDesc::codes_offset())); + __ dispatch_next(vtos); +} + +void TemplateTable::wide_ret() { + transition(vtos, vtos); + + const Register Rindex = R3_ARG1, + Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2; + + locals_index_wide(Rindex); + __ load_local_ptr(R17_tos, R17_tos, Rindex); + __ profile_ret(vtos, R17_tos, Rscratch1, R12_scratch2); + // Tos now contains the bci, compute the bcp from that. + __ ld(Rscratch1, in_bytes(methodOopDesc::const_offset()), R19_method); + __ addi(Rscratch2, R17_tos, in_bytes(constMethodOopDesc::codes_offset())); + __ add(R14_bcp, Rscratch1, Rscratch2); + __ dispatch_next(vtos); +} + +void TemplateTable::tableswitch() { + transition(itos, vtos); + + Label Ldispatch, Ldefault_case; + Register Rlow_byte = R3_ARG1, + Rindex = Rlow_byte, + Rhigh_byte = R4_ARG2, + Rdef_offset_addr = R5_ARG3, // is going to contain address of default offset + Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2, + Roffset = R6_ARG4; + + // Align bcp. + __ addi(Rdef_offset_addr, R14_bcp, BytesPerInt); + __ clrrdi(Rdef_offset_addr, Rdef_offset_addr, log2_long((jlong)BytesPerInt)); + + // Load lo & hi. + __ get_u4(Rlow_byte, Rdef_offset_addr, BytesPerInt, InterpreterMacroAssembler::Unsigned); + __ get_u4(Rhigh_byte, Rdef_offset_addr, 2 *BytesPerInt, InterpreterMacroAssembler::Unsigned); + + // Check for default case (=index outside [low,high]). + __ cmpw(CCR0, R17_tos, Rlow_byte); + __ cmpw(CCR1, R17_tos, Rhigh_byte); + __ blt(CCR0, Ldefault_case); + __ bgt(CCR1, Ldefault_case); + + // Lookup dispatch offset. + __ sub(Rindex, R17_tos, Rlow_byte); + __ extsw(Rindex, Rindex); + __ profile_switch_case(Rindex, Rhigh_byte /* scratch */, Rscratch1, Rscratch2); + __ sldi(Rindex, Rindex, LogBytesPerInt); + __ addi(Rindex, Rindex, 3 * BytesPerInt); +#if defined(VM_LITTLE_ENDIAN) + __ lwbrx(Roffset, Rdef_offset_addr, Rindex); + __ extsw(Roffset, Roffset); +#else + __ lwax(Roffset, Rdef_offset_addr, Rindex); +#endif + __ b(Ldispatch); + + __ bind(Ldefault_case); + __ profile_switch_default(Rhigh_byte, Rscratch1); + __ get_u4(Roffset, Rdef_offset_addr, 0, InterpreterMacroAssembler::Signed); + + __ bind(Ldispatch); + + __ add(R14_bcp, Roffset, R14_bcp); + __ dispatch_next(vtos); +} + +void TemplateTable::lookupswitch() { + transition(itos, itos); + __ stop("lookupswitch bytecode should have been rewritten"); +} + +// Table switch using linear search through cases. +// Bytecode stream format: +// Bytecode (1) | 4-byte padding | default offset (4) | count (4) | value/offset pair1 (8) | value/offset pair2 (8) | ... +// Note: Everything is big-endian format here. +void TemplateTable::fast_linearswitch() { + transition(itos, vtos); + + Label Lloop_entry, Lsearch_loop, Lcontinue_execution, Ldefault_case; + Register Rcount = R3_ARG1, + Rcurrent_pair = R4_ARG2, + Rdef_offset_addr = R5_ARG3, // Is going to contain address of default offset. + Roffset = R31, // Might need to survive C call. + Rvalue = R12_scratch2, + Rscratch = R11_scratch1, + Rcmp_value = R17_tos; + + // Align bcp. + __ addi(Rdef_offset_addr, R14_bcp, BytesPerInt); + __ clrrdi(Rdef_offset_addr, Rdef_offset_addr, log2_long((jlong)BytesPerInt)); + + // Setup loop counter and limit. + __ get_u4(Rcount, Rdef_offset_addr, BytesPerInt, InterpreterMacroAssembler::Unsigned); + __ addi(Rcurrent_pair, Rdef_offset_addr, 2 * BytesPerInt); // Rcurrent_pair now points to first pair. + + __ mtctr(Rcount); + __ cmpwi(CCR0, Rcount, 0); + __ bne(CCR0, Lloop_entry); + + // Default case + __ bind(Ldefault_case); + __ get_u4(Roffset, Rdef_offset_addr, 0, InterpreterMacroAssembler::Signed); + if (ProfileInterpreter) { + __ profile_switch_default(Rdef_offset_addr, Rcount/* scratch */); + } + __ b(Lcontinue_execution); + + // Next iteration + __ bind(Lsearch_loop); + __ bdz(Ldefault_case); + __ addi(Rcurrent_pair, Rcurrent_pair, 2 * BytesPerInt); + __ bind(Lloop_entry); + __ get_u4(Rvalue, Rcurrent_pair, 0, InterpreterMacroAssembler::Unsigned); + __ cmpw(CCR0, Rvalue, Rcmp_value); + __ bne(CCR0, Lsearch_loop); + + // Found, load offset. + __ get_u4(Roffset, Rcurrent_pair, BytesPerInt, InterpreterMacroAssembler::Signed); + // Calculate case index and profile + __ mfctr(Rcurrent_pair); + if (ProfileInterpreter) { + __ sub(Rcurrent_pair, Rcount, Rcurrent_pair); + __ profile_switch_case(Rcurrent_pair, Rcount /*scratch*/, Rdef_offset_addr/*scratch*/, Rscratch); + } + + __ bind(Lcontinue_execution); + __ add(R14_bcp, Roffset, R14_bcp); + __ dispatch_next(vtos); +} + +// Table switch using binary search (value/offset pairs are ordered). +// Bytecode stream format: +// Bytecode (1) | 4-byte padding | default offset (4) | count (4) | value/offset pair1 (8) | value/offset pair2 (8) | ... +// Note: Everything is big-endian format here. So on little endian machines, we have to revers offset and count and cmp value. +void TemplateTable::fast_binaryswitch() { + + transition(itos, vtos); + // Implementation using the following core algorithm: (copied from Intel) + // + // int binary_search(int key, LookupswitchPair* array, int n) { + // // Binary search according to "Methodik des Programmierens" by + // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985. + // int i = 0; + // int j = n; + // while (i+1 < j) { + // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q) + // // with Q: for all i: 0 <= i < n: key < a[i] + // // where a stands for the array and assuming that the (inexisting) + // // element a[n] is infinitely big. + // int h = (i + j) >> 1; + // // i < h < j + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + // } + // // R: a[i] <= key < a[i+1] or Q + // // (i.e., if key is within array, i is the correct index) + // return i; + // } + + // register allocation + const Register Rkey = R17_tos; // already set (tosca) + const Register Rarray = R3_ARG1; + const Register Ri = R4_ARG2; + const Register Rj = R5_ARG3; + const Register Rh = R6_ARG4; + const Register Rscratch = R11_scratch1; + + const int log_entry_size = 3; + const int entry_size = 1 << log_entry_size; + + Label found; + + // Find Array start, ... + __ addi(Rarray, R14_bcp, 3 * BytesPerInt); + __ clrrdi(Rarray, Rarray, log2_long((jlong)BytesPerInt)); + + // ... initialize i & j ... + __ li(Ri,0); + __ get_u4(Rj, Rarray, -BytesPerInt, InterpreterMacroAssembler::Unsigned); + + // ... and start. + Label entry; + __ b(entry); + + // binary search loop + { Label loop; + __ bind(loop); + // int h = (i + j) >> 1; + __ srdi(Rh, Rh, 1); + // if (key < array[h].fast_match()) { + // j = h; + // } else { + // i = h; + // } + __ sldi(Rscratch, Rh, log_entry_size); +#if defined(VM_LITTLE_ENDIAN) + __ lwbrx(Rscratch, Rscratch, Rarray); +#else + __ lwzx(Rscratch, Rscratch, Rarray); +#endif + + // if (key < current value) + // Rh = Rj + // else + // Rh = Ri + Label Lgreater; + __ cmpw(CCR0, Rkey, Rscratch); + __ bge(CCR0, Lgreater); + __ mr(Rj, Rh); + __ b(entry); + __ bind(Lgreater); + __ mr(Ri, Rh); + + // while (i+1 < j) + __ bind(entry); + __ addi(Rscratch, Ri, 1); + __ cmpw(CCR0, Rscratch, Rj); + __ add(Rh, Ri, Rj); // start h = i + j >> 1; + + __ blt(CCR0, loop); + } + + // End of binary search, result index is i (must check again!). + Label default_case; + Label continue_execution; + if (ProfileInterpreter) { + __ mr(Rh, Ri); // Save index in i for profiling. + } + // Ri = value offset + __ sldi(Ri, Ri, log_entry_size); + __ add(Ri, Ri, Rarray); + __ get_u4(Rscratch, Ri, 0, InterpreterMacroAssembler::Unsigned); + + Label not_found; + // Ri = offset offset + __ cmpw(CCR0, Rkey, Rscratch); + __ beq(CCR0, not_found); + // entry not found -> j = default offset + __ get_u4(Rj, Rarray, -2 * BytesPerInt, InterpreterMacroAssembler::Unsigned); + __ b(default_case); + + __ bind(not_found); + // entry found -> j = offset + __ profile_switch_case(Rh, Rj, Rscratch, Rkey); + __ get_u4(Rj, Ri, BytesPerInt, InterpreterMacroAssembler::Unsigned); + + if (ProfileInterpreter) { + __ b(continue_execution); + } + + __ bind(default_case); // fall through (if not profiling) + __ profile_switch_default(Ri, Rscratch); + + __ bind(continue_execution); + + __ extsw(Rj, Rj); + __ add(R14_bcp, Rj, R14_bcp); + __ dispatch_next(vtos); +} + +void TemplateTable::_return(TosState state) { + transition(state, state); + assert(_desc->calls_vm(), + "inconsistent calls_vm information"); // call in remove_activation + + if (_desc->bytecode() == Bytecodes::_return_register_finalizer) { + + Register Rscratch = R11_scratch1, + Rklass = R12_scratch2, + Rklass_flags = Rklass; + Label Lskip_register_finalizer; + + // Check if the method has the FINALIZER flag set and call into the VM to finalize in this case. + assert(state == vtos, "only valid state"); + __ ld(R17_tos, 0, R18_locals); + + // Load klass of this obj. + __ load_klass(Rklass, R17_tos); + __ lwz(Rklass_flags, in_bytes(Klass::access_flags_offset()), Rklass); + __ testbitdi(CCR0, R0, Rklass_flags, exact_log2(JVM_ACC_HAS_FINALIZER)); + __ bfalse(CCR0, Lskip_register_finalizer); + + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::register_finalizer), R17_tos /* obj */); + + __ align(32, 12); + __ bind(Lskip_register_finalizer); + } + + // Move the result value into the correct register and remove memory stack frame. + __ remove_activation(state, /* throw_monitor_exception */ true); + // Restoration of lr done by remove_activation. + switch (state) { + // Narrow result if state is itos but result type is smaller. + // Need to narrow in the return bytecode rather than in generate_return_entry + // since compiled code callers expect the result to already be narrowed. + case itos: __ narrow(R17_tos); /* fall through */ + case ltos: + case btos: + case ztos: + case ctos: + case stos: + case atos: __ mr(R3_RET, R17_tos); break; + case ftos: + case dtos: __ fmr(F1_RET, F15_ftos); break; + case vtos: // This might be a constructor. Final fields (and volatile fields on PPC64) need + // to get visible before the reference to the object gets stored anywhere. + __ membar(Assembler::StoreStore); break; + default : ShouldNotReachHere(); + } + __ blr(); +} + +// ============================================================================ +// Constant pool cache access +// +// Memory ordering: +// +// Like done in C++ interpreter, we load the fields +// - _indices +// - _f12_oop +// acquired, because these are asked if the cache is already resolved. We don't +// want to float loads above this check. +// See also comments in ConstantPoolCacheEntry::bytecode_1(), +// ConstantPoolCacheEntry::bytecode_2() and ConstantPoolCacheEntry::f1(); + +// Call into the VM if call site is not yet resolved +// +// Input regs: +// - None, all passed regs are outputs. +// +// Returns: +// - Rcache: The const pool cache entry that contains the resolved result. +// - Rresult: Either noreg or output for f1/f2. +// +// Kills: +// - Rscratch +void TemplateTable::resolve_cache_and_index(int byte_no, Register Rresult, Register Rcache, Register Rscratch, size_t index_size) { + + __ get_cache_and_index_at_bcp(Rcache, 1, index_size); + Label Lresolved, Ldone; + if (byte_no == f12_oop) { + // We are resolved if the f1 field contains a non-null object (CallSite, etc.). + // This kind of CP cache entry does not need to match the flags byte, because + // there is a 1-1 relation between bytecode type and CP entry type. + assert_different_registers(Rresult, Rcache); + __ ld(Rresult, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()), Rcache); + // Acquire by cmp-br-isync (see below). + __ cmpdi(CCR0, Rresult, 0); + __ bne(CCR0, Lresolved); + } else { + assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); + // We are resolved if the indices offset contains the current bytecode. +#if defined(VM_LITTLE_ENDIAN) + __ lbz(Rscratch, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()) + byte_no + 1, Rcache); +#else + __ lbz(Rscratch, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (byte_no + 1), Rcache); +#endif + // Acquire by cmp-br-isync (see below). + __ cmpdi(CCR0, Rscratch, (int)bytecode()); + __ beq(CCR0, Lresolved); + } + + address entry = NULL; + switch (bytecode()) { + case Bytecodes::_getstatic : // fall through + case Bytecodes::_putstatic : // fall through + case Bytecodes::_getfield : // fall through + case Bytecodes::_putfield : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); break; + case Bytecodes::_invokevirtual : // fall through + case Bytecodes::_invokespecial : // fall through + case Bytecodes::_invokestatic : // fall through + case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break; + case Bytecodes::_invokehandle : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle); break; + case Bytecodes::_invokedynamic : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break; + case Bytecodes::_fast_aldc : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break; + case Bytecodes::_fast_aldc_w : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); break; + default : ShouldNotReachHere(); break; + } + __ mr(R3_ARG1, R16_thread); + __ li(R4_ARG2, (int)bytecode()); + __ call_VM(noreg, entry, true); + + // Update registers with resolved info in case GC happened. + __ get_cache_and_index_at_bcp(Rcache, 1, index_size); + + if (Rresult != noreg && byte_no == f12_oop) { + // Need to reload resolved oop, GC could have happened during stepping back to java. + __ ld(Rresult, in_bytes(constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()), Rcache); + __ twi_0(Rresult); // Acquire by twi-isync. + } else { + __ b(Ldone); + } + + __ bind(Lresolved); + __ isync(); // Order load wrt. succeeding loads. + __ bind(Ldone); +} + +// Load the constant pool cache entry at field accesses into registers. +// The Rcache and Rindex registers must be set before call. +// Input: +// - Rcache, Rindex +// Output: +// - Robj, Roffset, Rflags +void TemplateTable::load_field_cp_cache_entry(Register Robj, + Register Rcache, + Register Rindex /* unused on PPC64 */, + Register Roffset, + Register Rflags, + bool is_static = false) { + assert_different_registers(Rcache, Rflags, Roffset); + // assert(Rindex == noreg, "parameter not used on PPC64"); + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + __ ld(Rflags, in_bytes(cp_base_offset) + in_bytes(ConstantPoolCacheEntry::flags_offset()), Rcache); + __ ld(Roffset, in_bytes(cp_base_offset) + in_bytes(ConstantPoolCacheEntry::f2_offset()), Rcache); + if (is_static) { + __ ld(Robj, in_bytes(cp_base_offset) + in_bytes(ConstantPoolCacheEntry::f1_offset()), Rcache); + //__ ld(Robj, in_bytes(Klass::java_mirror_offset()), Robj); + // Acquire not needed here. Following access has an address dependency on this value. + } +} + +// Load the constant pool cache entry at invokes into registers. +// Resolve if necessary. + +// Input Registers: +// - None, bcp is used, though +// +// Return registers: +// - Rmethod (f1 field or f2 if invokevirtual) +// - Ritable_index (f2 field) +// - Rflags (flags field) +// +// Kills: +// - R21 +// +void TemplateTable::load_invoke_cp_cache_entry(int byte_no, + Register Rmethod, + Register Ritable_index, + Register Rflags, + bool is_invokevirtual, + bool is_invokevfinal, + bool is_invokedynamic) { + + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + // Determine constant pool cache field offsets. + const int method_offset = in_bytes(cp_base_offset + (is_invokevirtual ? ConstantPoolCacheEntry::f2_offset() : ConstantPoolCacheEntry::f1_offset())); + const int flags_offset = in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset()); + // Access constant pool cache fields. + const int index_offset = in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset()); + + Register Rcache = R21_tmp1; // Note: same register as R21_sender_SP. + + if (is_invokevfinal) { + assert(Ritable_index == noreg, "register not used"); + // Already resolved. + __ get_cache_and_index_at_bcp(Rcache, 1); + __ ld(Rmethod, method_offset, Rcache); + __ ld(Rflags, flags_offset, Rcache); + } else if (byte_no == f12_oop) { + // Invokedynamic or plain methodhandle call. + const size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2)); + { + Register Rscratch = Rmethod; + resolve_cache_and_index(byte_no, Ritable_index, Rcache, Rscratch, index_size); + } + __ ld(Rmethod, index_offset, Rcache); + __ ld(Rflags, flags_offset, Rcache); + } else { + { + Register Rscratch = Rmethod; + resolve_cache_and_index(byte_no, noreg, Rcache, Rscratch, sizeof(u2)); + } + __ ld(Rmethod, method_offset, Rcache); + __ ld(Rflags, flags_offset, Rcache); + + if (Ritable_index != noreg) { + __ ld(Ritable_index, index_offset, Rcache); + } + } +} + +// ============================================================================ +// Field access + +// Volatile variables demand their effects be made known to all CPU's +// in order. Store buffers on most chips allow reads & writes to +// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode +// without some kind of memory barrier (i.e., it's not sufficient that +// the interpreter does not reorder volatile references, the hardware +// also must not reorder them). +// +// According to the new Java Memory Model (JMM): +// (1) All volatiles are serialized wrt to each other. ALSO reads & +// writes act as aquire & release, so: +// (2) A read cannot let unrelated NON-volatile memory refs that +// happen after the read float up to before the read. It's OK for +// non-volatile memory refs that happen before the volatile read to +// float down below it. +// (3) Similar a volatile write cannot let unrelated NON-volatile +// memory refs that happen BEFORE the write float down to after the +// write. It's OK for non-volatile memory refs that happen after the +// volatile write to float up before it. +// +// We only put in barriers around volatile refs (they are expensive), +// not _between_ memory refs (that would require us to track the +// flavor of the previous memory refs). Requirements (2) and (3) +// require some barriers before volatile stores and after volatile +// loads. These nearly cover requirement (1) but miss the +// volatile-store-volatile-load case. This final case is placed after +// volatile-stores although it could just as well go before +// volatile-loads. + +// The registers cache and index expected to be set before call. +// Correct values of the cache and index registers are preserved. +// Kills: +// Rcache (if has_tos) +// Rscratch +void TemplateTable::jvmti_post_field_access(Register Rcache, Register Rscratch, bool is_static, bool has_tos) { + + assert_different_registers(Rcache, Rscratch); + + if (JvmtiExport::can_post_field_access()) { + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + Label Lno_field_access_post; + + // Check if post field access in enabled. + int offs = __ load_const_optimized(Rscratch, JvmtiExport::get_field_access_count_addr(), R0, true); + __ lwz(Rscratch, offs, Rscratch); + + __ cmpwi(CCR0, Rscratch, 0); + __ beq(CCR0, Lno_field_access_post); + + // Post access enabled - do it! + __ addi(Rcache, Rcache, in_bytes(cp_base_offset)); + if (is_static) { + __ li(R17_tos, 0); + } else { + if (has_tos) { + // The fast bytecode versions have obj ptr in register. + // Thus, save object pointer before call_VM() clobbers it + // put object on tos where GC wants it. + __ push_ptr(R17_tos); + } else { + // Load top of stack (do not pop the value off the stack). + __ ld(R17_tos, Interpreter::expr_offset_in_bytes(0), R15_esp); + } + __ verify_oop(R17_tos); + } + // tos: object pointer or NULL if static + // cache: cache entry pointer + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), R17_tos, Rcache); + if (!is_static && has_tos) { + // Restore object pointer. + __ pop_ptr(R17_tos); + __ verify_oop(R17_tos); + } else { + // Cache is still needed to get class or obj. + __ get_cache_and_index_at_bcp(Rcache, 1); + } + + __ align(32, 12); + __ bind(Lno_field_access_post); + } +} + +// kills R11_scratch1 +void TemplateTable::pop_and_check_object(Register Roop) { + Register Rtmp = R11_scratch1; + + assert_different_registers(Rtmp, Roop); + __ pop_ptr(Roop); + // For field access must check obj. + __ null_check_throw(Roop, -1, Rtmp); + __ verify_oop(Roop); +} + +// PPC64: implement volatile loads as fence-store-acquire. +void TemplateTable::getfield_or_static(int byte_no, bool is_static) { + transition(vtos, vtos); + + Label Lexit, Lacquire; + + const Register Rcache = R3_ARG1, + Rclass_or_obj = R22_tmp2, + Roffset = R23_tmp3, + Rflags = R31, + Rbtable = R5_ARG3, + Rbc = R6_ARG4, + Rscratch = R12_scratch2; + + static address field_branch_table[number_of_states], + static_branch_table[number_of_states]; + + address* branch_table = is_static ? static_branch_table : field_branch_table; + + // Get field offset. + resolve_cache_and_index(byte_no, noreg, Rcache, Rscratch, sizeof(u2)); + + // JVMTI support + jvmti_post_field_access(Rcache, Rscratch, is_static, false); + + // Load after possible GC. + load_field_cp_cache_entry(Rclass_or_obj, Rcache, noreg, Roffset, Rflags, is_static); + + // Load pointer to branch table. + __ load_const_optimized(Rbtable, (address)branch_table, Rscratch); + + // Get volatile flag. + __ rldicl(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit. + // Note: sync is needed before volatile load on PPC64. + + // Check field type. + __ rldicl(Rflags, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + +#ifdef ASSERT + Label LFlagInvalid; + __ cmpldi(CCR0, Rflags, number_of_states); + __ bge(CCR0, LFlagInvalid); +#endif + + // Load from branch table and dispatch (volatile case: one instruction ahead). + __ sldi(Rflags, Rflags, LogBytesPerWord); + __ cmpwi(CCR6, Rscratch, 1); // Volatile? + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ sldi(Rscratch, Rscratch, exact_log2(BytesPerInstWord)); // Volatile ? size of 1 instruction : 0. + } + __ ldx(Rbtable, Rbtable, Rflags); + + // Get the obj from stack. + if (!is_static) { + pop_and_check_object(Rclass_or_obj); // Kills R11_scratch1. + } else { + __ verify_oop(Rclass_or_obj); + } + + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ subf(Rbtable, Rscratch, Rbtable); // Point to volatile/non-volatile entry point. + } + __ mtctr(Rbtable); + __ bctr(); + +#ifdef ASSERT + __ bind(LFlagInvalid); + __ stop("got invalid flag", 0x654); + + // __ bind(Lvtos); + address pc_before_fence = __ pc(); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(__ pc() - pc_before_fence == (ptrdiff_t)BytesPerInstWord, "must be single instruction"); + assert(branch_table[vtos] == 0, "can't compute twice"); + branch_table[vtos] = __ pc(); // non-volatile_entry point + __ stop("vtos unexpected", 0x655); +#endif + + __ align(32, 28, 28); // Align load. + // __ bind(Ldtos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[dtos] == 0, "can't compute twice"); + branch_table[dtos] = __ pc(); // non-volatile_entry point + __ lfdx(F15_ftos, Rclass_or_obj, Roffset); + __ push(dtos); + if (!is_static) patch_bytecode(Bytecodes::_fast_dgetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ beq_predict_taken(CCR0, Lacquire); + __ b(Lacquire); // In case of NAN. + + __ align(32, 28, 28); // Align load. + // __ bind(Lftos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ftos] == 0, "can't compute twice"); + branch_table[ftos] = __ pc(); // non-volatile_entry point + __ lfsx(F15_ftos, Rclass_or_obj, Roffset); + __ push(ftos); + if (!is_static) patch_bytecode(Bytecodes::_fast_fgetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ beq_predict_taken(CCR0, Lacquire); + __ b(Lacquire); // In case of NAN. + + __ align(32, 28, 28); // Align load. + // __ bind(Litos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[itos] == 0, "can't compute twice"); + branch_table[itos] = __ pc(); // non-volatile_entry point + __ lwax(R17_tos, Rclass_or_obj, Roffset); + __ push(itos); + if (!is_static) patch_bytecode(Bytecodes::_fast_igetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ twi_0(R17_tos); // Acquire by cmp-twi-isync. + __ b(Lacquire); + + __ align(32, 28, 28); // Align load. + // __ bind(Lltos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ltos] == 0, "can't compute twice"); + branch_table[ltos] = __ pc(); // non-volatile_entry point + __ ldx(R17_tos, Rclass_or_obj, Roffset); + __ push(ltos); + if (!is_static) patch_bytecode(Bytecodes::_fast_lgetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ twi_0(R17_tos); // Acquire by cmp-twi-isync. + __ b(Lacquire); + + __ align(32, 28, 28); // Align load. + // __ bind(Lbtos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[btos] == 0, "can't compute twice"); + branch_table[btos] = __ pc(); // non-volatile_entry point + __ lbzx(R17_tos, Rclass_or_obj, Roffset); + __ extsb(R17_tos, R17_tos); + __ push(btos); + if (!is_static) patch_bytecode(Bytecodes::_fast_bgetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ twi_0(R17_tos); // Acquire by cmp-twi-isync. + __ b(Lacquire); + + __ align(32, 28, 28); // Align load. + // __ bind(Lztos); (same code as btos) + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ztos] == 0, "can't compute twice"); + branch_table[ztos] = __ pc(); // non-volatile_entry point + __ lbzx(R17_tos, Rclass_or_obj, Roffset); + __ extsb(R17_tos, R17_tos); + __ push(ztos); + if (!is_static) { + // use btos rewriting, no truncating to t/f bit is needed for getfield. + patch_bytecode(Bytecodes::_fast_bgetfield, Rbc, Rscratch); + } + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ align(32, 28, 28); // Align load. + // __ bind(Lctos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ctos] == 0, "can't compute twice"); + branch_table[ctos] = __ pc(); // non-volatile_entry point + __ lhzx(R17_tos, Rclass_or_obj, Roffset); + __ push(ctos); + if (!is_static) patch_bytecode(Bytecodes::_fast_cgetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ twi_0(R17_tos); // Acquire by cmp-twi-isync. + __ b(Lacquire); + + __ align(32, 28, 28); // Align load. + // __ bind(Lstos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[stos] == 0, "can't compute twice"); + branch_table[stos] = __ pc(); // non-volatile_entry point + __ lhax(R17_tos, Rclass_or_obj, Roffset); + __ push(stos); + if (!is_static) patch_bytecode(Bytecodes::_fast_sgetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ twi_0(R17_tos); // Acquire by cmp-twi-isync. + __ b(Lacquire); + + __ align(32, 28, 28); // Align load. + // __ bind(Latos); + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[atos] == 0, "can't compute twice"); + branch_table[atos] = __ pc(); // non-volatile_entry point + __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj); + __ verify_oop(R17_tos); + __ push(atos); + //__ dcbt(R17_tos); // prefetch + if (!is_static) patch_bytecode(Bytecodes::_fast_agetfield, Rbc, Rscratch); + __ bne(CCR6, Lexit); // Non-volatile? + __ twi_0(R17_tos); // Acquire by cmp-twi-isync. + // fallthru __ b(Lacquire); + + __ align(32, 12, 28); // Align Lexit. + __ bind(Lacquire); + __ isync(); // acquire + __ bind(Lexit); + +#ifdef ASSERT + for (int i = 0; iprint_cr("get: %s_branch_table[%d] = 0x%llx (opcode 0x%llx)", + // is_static ? "static" : "field", i, branch_table[i], *((unsigned int*)branch_table[i])); + } +#endif +} + +void TemplateTable::getfield(int byte_no) { + getfield_or_static(byte_no, false); +} + +void TemplateTable::getstatic(int byte_no) { + getfield_or_static(byte_no, true); +} + +// The registers cache and index expected to be set before call. +// The function may destroy various registers, just not the cache and index registers. +void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rscratch, bool is_static) { + + assert_different_registers(Rcache, Rscratch, R6_ARG4); + + if (JvmtiExport::can_post_field_modification()) { + Label Lno_field_mod_post; + + // Check if post field access in enabled. + int offs = __ load_const_optimized(Rscratch, JvmtiExport::get_field_modification_count_addr(), R0, true); + __ lwz(Rscratch, offs, Rscratch); + + __ cmpwi(CCR0, Rscratch, 0); + __ beq(CCR0, Lno_field_mod_post); + + // Do the post + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + const Register Robj = Rscratch; + + __ addi(Rcache, Rcache, in_bytes(cp_base_offset)); + if (is_static) { + // Life is simple. Null out the object pointer. + __ li(Robj, 0); + } else { + // In case of the fast versions, value lives in registers => put it back on tos. + int offs = Interpreter::expr_offset_in_bytes(0); + Register base = R15_esp; + switch(bytecode()) { + case Bytecodes::_fast_aputfield: __ push_ptr(); offs += Interpreter::stackElementSize; break; + case Bytecodes::_fast_iputfield: // Fall through + case Bytecodes::_fast_bputfield: // Fall through + case Bytecodes::_fast_zputfield: // Fall through + case Bytecodes::_fast_cputfield: // Fall through + case Bytecodes::_fast_sputfield: __ push_i(); offs += Interpreter::stackElementSize; break; + case Bytecodes::_fast_lputfield: __ push_l(); offs += 2*Interpreter::stackElementSize; break; + case Bytecodes::_fast_fputfield: __ push_f(); offs += Interpreter::stackElementSize; break; + case Bytecodes::_fast_dputfield: __ push_d(); offs += 2*Interpreter::stackElementSize; break; + default: { + offs = 0; + base = Robj; + const Register Rflags = Robj; + Label is_one_slot; + // Life is harder. The stack holds the value on top, followed by the + // object. We don't know the size of the value, though; it could be + // one or two words depending on its type. As a result, we must find + // the type to determine where the object is. + __ ld(Rflags, in_bytes(ConstantPoolCacheEntry::flags_offset()), Rcache); // Big Endian + __ rldicl(Rflags, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + + __ cmpwi(CCR0, Rflags, ltos); + __ cmpwi(CCR1, Rflags, dtos); + __ addi(base, R15_esp, Interpreter::expr_offset_in_bytes(1)); + __ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); + __ beq(CCR0, is_one_slot); + __ addi(base, R15_esp, Interpreter::expr_offset_in_bytes(2)); + __ bind(is_one_slot); + break; + } + } + __ ld(Robj, offs, base); + __ verify_oop(Robj); + } + + __ addi(R6_ARG4, R15_esp, Interpreter::expr_offset_in_bytes(0)); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), Robj, Rcache, R6_ARG4); + __ get_cache_and_index_at_bcp(Rcache, 1); + + // In case of the fast versions, value lives in registers => put it back on tos. + switch(bytecode()) { + case Bytecodes::_fast_aputfield: __ pop_ptr(); break; + case Bytecodes::_fast_iputfield: // Fall through + case Bytecodes::_fast_bputfield: // Fall through + case Bytecodes::_fast_zputfield: // Fall through + case Bytecodes::_fast_cputfield: // Fall through + case Bytecodes::_fast_sputfield: __ pop_i(); break; + case Bytecodes::_fast_lputfield: __ pop_l(); break; + case Bytecodes::_fast_fputfield: __ pop_f(); break; + case Bytecodes::_fast_dputfield: __ pop_d(); break; + default: break; // Nothin' to do. + } + + __ align(32, 12); + __ bind(Lno_field_mod_post); + } +} + +// PPC64: implement volatile stores as release-store (return bytecode contains an additional release). +void TemplateTable::putfield_or_static(int byte_no, bool is_static) { + Label Lexit; + + const Register Rcache = R5_ARG3, // Do not use ARG1/2 (causes trouble in jvmti_post_field_mod). + Rclass_or_obj = R31, // Needs to survive C call. + Roffset = R22_tmp2, // Needs to survive C call. + Rflags = R3_ARG1, + Rbtable = R4_ARG2, + Rscratch = R11_scratch1, + Rscratch2 = R12_scratch2, + Rscratch3 = R6_ARG4, + Rbc = Rscratch3; + const ConditionRegister CR_is_vol = CCR2; // Non-volatile condition register (survives runtime call in do_oop_store). + + static address field_branch_table[number_of_states], + static_branch_table[number_of_states]; + + address* branch_table = is_static ? static_branch_table : field_branch_table; + + // Stack (grows up): + // value + // obj + + // Load the field offset. + resolve_cache_and_index(byte_no, noreg, Rcache, Rscratch, sizeof(u2)); + jvmti_post_field_mod(Rcache, Rscratch, is_static); + load_field_cp_cache_entry(Rclass_or_obj, Rcache, noreg, Roffset, Rflags, is_static); + + // Load pointer to branch table. + __ load_const_optimized(Rbtable, (address)branch_table, Rscratch); + + // Get volatile flag. + __ rldicl(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit. + + // Check the field type. + __ rldicl(Rflags, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + +#ifdef ASSERT + Label LFlagInvalid; + __ cmpldi(CCR0, Rflags, number_of_states); + __ bge(CCR0, LFlagInvalid); +#endif + + // Load from branch table and dispatch (volatile case: one instruction ahead). + __ sldi(Rflags, Rflags, LogBytesPerWord); + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { __ cmpwi(CR_is_vol, Rscratch, 1); } // Volatile? + __ sldi(Rscratch, Rscratch, exact_log2(BytesPerInstWord)); // Volatile? size of instruction 1 : 0. + __ ldx(Rbtable, Rbtable, Rflags); + + __ subf(Rbtable, Rscratch, Rbtable); // Point to volatile/non-volatile entry point. + __ mtctr(Rbtable); + __ bctr(); + +#ifdef ASSERT + __ bind(LFlagInvalid); + __ stop("got invalid flag", 0x656); + + // __ bind(Lvtos); + address pc_before_release = __ pc(); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(__ pc() - pc_before_release == (ptrdiff_t)BytesPerInstWord, "must be single instruction"); + assert(branch_table[vtos] == 0, "can't compute twice"); + branch_table[vtos] = __ pc(); // non-volatile_entry point + __ stop("vtos unexpected", 0x657); +#endif + + __ align(32, 28, 28); // Align pop. + // __ bind(Ldtos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[dtos] == 0, "can't compute twice"); + branch_table[dtos] = __ pc(); // non-volatile_entry point + __ pop(dtos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ stfdx(F15_ftos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_dputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Lftos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ftos] == 0, "can't compute twice"); + branch_table[ftos] = __ pc(); // non-volatile_entry point + __ pop(ftos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ stfsx(F15_ftos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_fputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Litos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[itos] == 0, "can't compute twice"); + branch_table[itos] = __ pc(); // non-volatile_entry point + __ pop(itos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ stwx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_iputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Lltos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ltos] == 0, "can't compute twice"); + branch_table[ltos] = __ pc(); // non-volatile_entry point + __ pop(ltos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ stdx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_lputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Lbtos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[btos] == 0, "can't compute twice"); + branch_table[btos] = __ pc(); // non-volatile_entry point + __ pop(btos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ stbx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_bputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Lztos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ztos] == 0, "can't compute twice"); + branch_table[ztos] = __ pc(); // non-volatile_entry point + __ pop(ztos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ andi(R17_tos, R17_tos, 0x1); + __ stbx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_zputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Lctos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ctos] == 0, "can't compute twice"); + branch_table[ctos] = __ pc(); // non-volatile_entry point + __ pop(ctos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.. + __ sthx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_cputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Lstos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[stos] == 0, "can't compute twice"); + branch_table[stos] = __ pc(); // non-volatile_entry point + __ pop(stos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ sthx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_sputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + __ b(Lexit); + + __ align(32, 28, 28); // Align pop. + // __ bind(Latos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[atos] == 0, "can't compute twice"); + branch_table[atos] = __ pc(); // Non-volatile_entry point. + __ pop(atos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // kills R11_scratch1 + do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, _bs->kind(), false /* precise */, true /* check null */); + if (!is_static) { patch_bytecode(Bytecodes::_fast_aputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ bne(CR_is_vol, Lexit); // Non-volatile? + __ fence(); + } + // fallthru: __ b(Lexit); + + __ align(32, 12); + __ bind(Lexit); +#ifdef ASSERT + for (int i = 0; iprint_cr("put: %s_branch_table[%d] = 0x%llx (opcode 0x%llx)", + // is_static ? "static" : "field", i, branch_table[i], *((unsigned int*)branch_table[i])); + } +#endif +} + +void TemplateTable::putfield(int byte_no) { + putfield_or_static(byte_no, false); +} + +void TemplateTable::putstatic(int byte_no) { + putfield_or_static(byte_no, true); +} + +// See SPARC. On PPC64, we have a different jvmti_post_field_mod which does the job. +void TemplateTable::jvmti_post_fast_field_mod() { + __ should_not_reach_here(); +} + +void TemplateTable::fast_storefield(TosState state) { + transition(state, vtos); + + const Register Rcache = R5_ARG3, // Do not use ARG1/2 (causes trouble in jvmti_post_field_mod). + Rclass_or_obj = R31, // Needs to survive C call. + Roffset = R22_tmp2, // Needs to survive C call. + Rflags = R3_ARG1, + Rscratch = R11_scratch1, + Rscratch2 = R12_scratch2, + Rscratch3 = R4_ARG2; + const ConditionRegister CR_is_vol = CCR2; // Non-volatile condition register (survives runtime call in do_oop_store). + + // Constant pool already resolved => Load flags and offset of field. + __ get_cache_and_index_at_bcp(Rcache, 1); + jvmti_post_field_mod(Rcache, Rscratch, false /* not static */); + load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false); + + // Get the obj and the final store addr. + pop_and_check_object(Rclass_or_obj); // Kills R11_scratch1. + + // Get volatile flag. + __ rldicl_(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit. + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { __ cmpdi(CR_is_vol, Rscratch, 1); } + { + Label LnotVolatile; + __ beq(CCR0, LnotVolatile); + __ release(); + __ align(32, 12); + __ bind(LnotVolatile); + } + + // Do the store and fencing. + switch(bytecode()) { + case Bytecodes::_fast_aputfield: + // Store into the field. + do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, _bs->kind(), false /* precise */, true /* check null */); + break; + + case Bytecodes::_fast_iputfield: + __ stwx(R17_tos, Rclass_or_obj, Roffset); + break; + + case Bytecodes::_fast_lputfield: + __ stdx(R17_tos, Rclass_or_obj, Roffset); + break; + + case Bytecodes::_fast_zputfield: + __ andi(R17_tos, R17_tos, 0x1); // boolean is true if LSB is 1 + // fall through to bputfield + case Bytecodes::_fast_bputfield: + __ stbx(R17_tos, Rclass_or_obj, Roffset); + break; + + case Bytecodes::_fast_cputfield: + case Bytecodes::_fast_sputfield: + __ sthx(R17_tos, Rclass_or_obj, Roffset); + break; + + case Bytecodes::_fast_fputfield: + __ stfsx(F15_ftos, Rclass_or_obj, Roffset); + break; + + case Bytecodes::_fast_dputfield: + __ stfdx(F15_ftos, Rclass_or_obj, Roffset); + break; + + default: ShouldNotReachHere(); + } + + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + Label LnotVolatile; + __ bne(CR_is_vol, LnotVolatile); + __ fence(); + __ align(32, 12); + __ bind(LnotVolatile); + } +} + +void TemplateTable::fast_accessfield(TosState state) { + transition(atos, state); + + Label Lexit, LisVolatile; + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + + const Register Rcache = R3_ARG1, + Rclass_or_obj = R17_tos, + Roffset = R22_tmp2, + Rflags = R23_tmp3, + Rscratch = R12_scratch2; + + // Constant pool already resolved. Get the field offset. + __ get_cache_and_index_at_bcp(Rcache, 1); + load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false); + + // JVMTI support + jvmti_post_field_access(Rcache, Rscratch, false, true); + + // Get the load address. + __ null_check_throw(Rclass_or_obj, -1, Rscratch); + + // Get volatile flag. + __ rldicl_(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit. + __ bne(CCR0, LisVolatile); + + switch(bytecode()) { + case Bytecodes::_fast_agetfield: + { + __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj); + __ verify_oop(R17_tos); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj); + __ verify_oop(R17_tos); + __ twi_0(R17_tos); + __ isync(); + break; + } + case Bytecodes::_fast_igetfield: + { + __ lwax(R17_tos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lwax(R17_tos, Rclass_or_obj, Roffset); + __ twi_0(R17_tos); + __ isync(); + break; + } + case Bytecodes::_fast_lgetfield: + { + __ ldx(R17_tos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ ldx(R17_tos, Rclass_or_obj, Roffset); + __ twi_0(R17_tos); + __ isync(); + break; + } + case Bytecodes::_fast_bgetfield: + { + __ lbzx(R17_tos, Rclass_or_obj, Roffset); + __ extsb(R17_tos, R17_tos); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lbzx(R17_tos, Rclass_or_obj, Roffset); + __ twi_0(R17_tos); + __ extsb(R17_tos, R17_tos); + __ isync(); + break; + } + case Bytecodes::_fast_cgetfield: + { + __ lhzx(R17_tos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lhzx(R17_tos, Rclass_or_obj, Roffset); + __ twi_0(R17_tos); + __ isync(); + break; + } + case Bytecodes::_fast_sgetfield: + { + __ lhax(R17_tos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lhax(R17_tos, Rclass_or_obj, Roffset); + __ twi_0(R17_tos); + __ isync(); + break; + } + case Bytecodes::_fast_fgetfield: + { + __ lfsx(F15_ftos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + Label Ldummy; + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lfsx(F15_ftos, Rclass_or_obj, Roffset); + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ bne_predict_not_taken(CCR0, Ldummy); + __ bind(Ldummy); + __ isync(); + break; + } + case Bytecodes::_fast_dgetfield: + { + __ lfdx(F15_ftos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + Label Ldummy; + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lfdx(F15_ftos, Rclass_or_obj, Roffset); + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ bne_predict_not_taken(CCR0, Ldummy); + __ bind(Ldummy); + __ isync(); + break; + } + default: ShouldNotReachHere(); + } + __ align(32, 12); + __ bind(Lexit); +} + +void TemplateTable::fast_xaccess(TosState state) { + transition(vtos, state); + + Label Lexit, LisVolatile; + ByteSize cp_base_offset = constantPoolCacheOopDesc::base_offset(); + const Register Rcache = R3_ARG1, + Rclass_or_obj = R17_tos, + Roffset = R22_tmp2, + Rflags = R23_tmp3, + Rscratch = R12_scratch2; + + __ ld(Rclass_or_obj, 0, R18_locals); + + // Constant pool already resolved. Get the field offset. + __ get_cache_and_index_at_bcp(Rcache, 2); + load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false); + + // JVMTI support not needed, since we switch back to single bytecode as soon as debugger attaches. + + // Needed to report exception at the correct bcp. + __ addi(R14_bcp, R14_bcp, 1); + + // Get the load address. + __ null_check_throw(Rclass_or_obj, -1, Rscratch); + + // Get volatile flag. + __ rldicl_(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit. + __ bne(CCR0, LisVolatile); + + switch(state) { + case atos: + { + __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj); + __ verify_oop(R17_tos); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj); + __ verify_oop(R17_tos); + __ twi_0(R17_tos); + __ isync(); + break; + } + case itos: + { + __ lwax(R17_tos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lwax(R17_tos, Rclass_or_obj, Roffset); + __ twi_0(R17_tos); + __ isync(); + break; + } + case ftos: + { + __ lfsx(F15_ftos, Rclass_or_obj, Roffset); + __ b(Lexit); + + __ bind(LisVolatile); + Label Ldummy; + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); } + __ lfsx(F15_ftos, Rclass_or_obj, Roffset); + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ bne_predict_not_taken(CCR0, Ldummy); + __ bind(Ldummy); + __ isync(); + break; + } + default: ShouldNotReachHere(); + } + __ align(32, 12); + __ bind(Lexit); + __ addi(R14_bcp, R14_bcp, -1); +} + +// ============================================================================ +// Calls +#if 0 // HS25? +// Common code for invoke +// +// Input: +// - byte_no +// +// Output: +// - Rmethod: The method to invoke next. +// - Rret_addr: The return address to return to. +// - Rindex: MethodType (invokehandle) or CallSite obj (invokedynamic) +// - Rrecv: Cache for "this" pointer, might be noreg if static call. +// - Rflags: Method flags from const pool cache. +// +// Kills: +// - Rscratch1 +// +void TemplateTable::prepare_invoke(int byte_no, + Register Rmethod, // linked method (or i-klass) + Register Rret_addr,// return address + Register Rindex, // itable index, MethodType, etc. + Register Rrecv, // If caller wants to see it. + Register Rflags, // If caller wants to test it. + Register Rscratch + ) { + // Determine flags. + const Bytecodes::Code code = bytecode(); + const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokedynamic = code == Bytecodes::_invokedynamic; + const bool is_invokehandle = code == Bytecodes::_invokehandle; + const bool is_invokevirtual = code == Bytecodes::_invokevirtual; + const bool is_invokespecial = code == Bytecodes::_invokespecial; + const bool load_receiver = (Rrecv != noreg); + assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), ""); + + assert_different_registers(Rmethod, Rindex, Rflags, Rscratch); + assert_different_registers(Rmethod, Rrecv, Rflags, Rscratch); + assert_different_registers(Rret_addr, Rscratch); + + load_invoke_cp_cache_entry(byte_no, Rmethod, Rindex, Rflags, is_invokevirtual, false, is_invokedynamic); + + // Saving of SP done in call_from_interpreter. + + // Maybe push "appendix" to arguments. + if (is_invokedynamic || is_invokehandle) { + Label Ldone; + __ rldicl_(R0, Rflags, 64-ConstantPoolCacheEntry::has_appendix_shift, 63); + __ beq(CCR0, Ldone); + // Push "appendix" (MethodType, CallSite, etc.). + // This must be done before we get the receiver, + // since the parameter_size includes it. + __ load_resolved_reference_at_index(Rscratch, Rindex); + __ verify_oop(Rscratch); + __ push_ptr(Rscratch); + __ bind(Ldone); + } + + // Load receiver if needed (after appendix is pushed so parameter size is correct). + if (load_receiver) { + const Register Rparam_count = Rscratch; + __ andi(Rparam_count, Rflags, ConstantPoolCacheEntry::parameter_size_mask); + __ load_receiver(Rparam_count, Rrecv); + __ verify_oop(Rrecv); + } + + // Get return address. + { + Register Rtable_addr = Rscratch; + Register Rret_type = Rret_addr; + address table_addr = (address) Interpreter::invoke_return_entry_table_for(code); + + // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, (address*)table_addr); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + // Get return address. + __ ldx(Rret_addr, Rtable_addr, Rret_type); + } +} +#endif + +// Helper for virtual calls. Load target out of vtable and jump off! +// Kills all passed registers. +void TemplateTable::generate_vtable_call(Register Rrecv_klass, Register Rindex, Register Rret, Register Rtemp) { + + assert_different_registers(Rrecv_klass, Rtemp, Rret); + const Register Rtarget_method = Rindex; + + // Get target methodOop & entry point. + const int base = instanceKlass::vtable_start_offset() * wordSize; + // Calc vtable addr scale the vtable index by 8. + __ sldi(Rindex, Rindex, exact_log2(vtableEntry::size() * wordSize)); + // Load target. + __ addi(Rrecv_klass, Rrecv_klass, base + vtableEntry::method_offset_in_bytes()); + __ ldx(Rtarget_method, Rindex, Rrecv_klass); + __ call_from_interpreter(Rtarget_method, Rret, Rrecv_klass /* scratch1 */, Rtemp /* scratch2 */); +} + +// Virtual or final call. Final calls are rewritten on the fly to run through "fast_finalcall" next time. +void TemplateTable::invokevirtual(int byte_no) { + transition(vtos, vtos); + + Register Rtable_addr = R11_scratch1, + Rret_type = R12_scratch2, + Rret_addr = R5_ARG3, + Rflags = R22_tmp2, // Should survive C call. + Rrecv = R3_ARG1, + Rrecv_klass = Rrecv, + Rvtableindex_or_method = R31, // Should survive C call. + Rnum_params = R4_ARG2, + Rnew_bc = R6_ARG4; + + Label LnotFinal; + + load_invoke_cp_cache_entry(byte_no, Rvtableindex_or_method, noreg, Rflags, /*virtual*/ true, false, false); + + __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_vfinal_shift); + __ bfalse(CCR0, LnotFinal); + + patch_bytecode(Bytecodes::_fast_invokevfinal, Rnew_bc, R12_scratch2); + invokevfinal_helper(Rvtableindex_or_method, Rflags, R11_scratch1, R12_scratch2); + + __ align(32, 12); + __ bind(LnotFinal); + // Load "this" pointer (receiver). + __ rldicl(Rnum_params, Rflags, 64, 48); + __ load_receiver(Rnum_params, Rrecv); + __ verify_oop(Rrecv); + + // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, Interpreter::return_3_addrs_by_index_table()); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + __ ldx(Rret_addr, Rret_type, Rtable_addr); + __ null_check_throw(Rrecv, oopDesc::klass_offset_in_bytes(), R11_scratch1); + __ load_heap_oop_not_null(Rrecv_klass, oopDesc::klass_offset_in_bytes(), Rrecv); + __ verify_oop(Rrecv_klass); + __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2, false); + + generate_vtable_call(Rrecv_klass, Rvtableindex_or_method, Rret_addr, R11_scratch1); +} + +void TemplateTable::fast_invokevfinal(int byte_no) { + transition(vtos, vtos); + + assert(byte_no == f2_byte, "use this argument"); + Register Rflags = R22_tmp2, + Rmethod = R31; + load_invoke_cp_cache_entry(byte_no, Rmethod, noreg, Rflags, /*virtual*/ true, /*is_invokevfinal*/ true, false); + invokevfinal_helper(Rmethod, Rflags, R11_scratch1, R12_scratch2); +} + +void TemplateTable::invokevfinal_helper(Register RmethodOop, Register Rflags, Register Rscratch1, Register Rscratch2) { + + assert_different_registers(RmethodOop, Rflags, Rscratch1, Rscratch2); + __ verify_oop(RmethodOop); + + // Load receiver from stack slot. + Register Rrecv = Rscratch2; + Register Rnum_params = Rrecv; + + __ lhz(Rnum_params, in_bytes(methodOopDesc::size_of_parameters_offset()), RmethodOop); + + // Get return address. + Register Rtable_addr = Rscratch1, + Rret_addr = Rflags, + Rret_type = Rret_addr; + // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, Interpreter::return_3_addrs_by_index_table()); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + __ ldx(Rret_addr, Rret_type, Rtable_addr); + + // Load receiver and receiver NULL check. + __ load_receiver(Rnum_params, Rrecv); + __ null_check_throw(Rrecv, -1, Rscratch1); + + __ profile_final_call(Rrecv, Rscratch1); + + // Do the call. + __ call_from_interpreter(RmethodOop, Rret_addr, Rscratch1, Rscratch2); +} + +void TemplateTable::invokespecial(int byte_no) { + assert(byte_no == f1_byte, "use this argument"); + transition(vtos, vtos); + + Register Rtable_addr = R3_ARG1, + Rret_type = R4_ARG2, + Rret_addr = R5_ARG3, + Rflags = R6_ARG4, + Rreceiver = R7_ARG5, + Rmethod = R31; + + load_invoke_cp_cache_entry(byte_no, Rmethod, R11_scratch1, Rflags, /*virtual*/ false, false, false); + + __ verify_oop(Rmethod); + __ lhz(R11_scratch1, in_bytes(methodOopDesc::size_of_parameters_offset()), Rmethod); + __ load_receiver(R11_scratch1, Rreceiver); + + // Receiver NULL check. + __ null_check_throw(Rreceiver, -1, R11_scratch1); + + __ profile_call(R11_scratch1,R12_scratch2); + + // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, Interpreter::return_3_addrs_by_index_table()); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + __ ldx(Rret_addr,Rret_type, Rtable_addr); + + __ call_from_interpreter(Rmethod, Rret_addr, R11_scratch1, R12_scratch2); +} + +void TemplateTable::invokestatic(int byte_no) { + assert(byte_no == f1_byte, "use this argument"); + transition(vtos, vtos); + + Register Rtable_addr = R3_ARG1, + Rret_type = R4_ARG2, + Rret_addr = R5_ARG3, + Rflags = R6_ARG4; + + load_invoke_cp_cache_entry(byte_no, R19_method, R11_scratch1, Rflags, /*virtual*/ false, false, false); + + __ verify_oop(R19_method); + + __ profile_call(R11_scratch1, R12_scratch2); + + // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, Interpreter::return_3_addrs_by_index_table()); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + __ ldx(Rret_addr,Rret_type, Rtable_addr); + + __ call_from_interpreter(R19_method, Rret_addr, R11_scratch1, R12_scratch2); +} + +void TemplateTable::invokeinterface_object_method(Register Rrecv_klass, + Register Rret, + Register Rflags, + Register Rindex, + Register Rtemp1, + Register Rtemp2) { + + assert_different_registers(Rindex, Rret, Rrecv_klass, Rflags, Rtemp1, Rtemp2); + Label LnotFinal; + + // Check for vfinal. + __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_vfinal_shift); + __ bfalse(CCR0, LnotFinal); + + Register Rscratch = Rflags; // Rflags is dead now. + + // Final call case. + __ profile_final_call(Rtemp1, Rscratch); + // Do the final call - the index (f2) contains the methodOop. + __ call_from_interpreter(Rindex, Rret, Rscratch, Rrecv_klass /* scratch */); + + // Non-final callc case. + __ bind(LnotFinal); + __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch, false); + generate_vtable_call(Rrecv_klass, Rindex, Rret, Rscratch); +} + +void TemplateTable::invokeinterface(int byte_no) { + assert(byte_no == f1_byte, "use this argument"); + transition(vtos, vtos); + + const Register Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2, + Rscratch3 = R9_ARG7, + Rscratch4 = R10_ARG8, + Rtable_addr = Rscratch2, + Rinterface_klass = R5_ARG3, + Rret_type = R8_ARG6, + Rret_addr = Rret_type, + Rindex = R6_ARG4, + Rreceiver = R4_ARG2, + Rrecv_klass = Rreceiver, + Rflags = R7_ARG5; + + load_invoke_cp_cache_entry(byte_no, Rinterface_klass, Rindex, Rflags, /*virtual*/ false, false, false); + // Get receiver from expression stack. + Register Rparam_count = Rscratch1; + __ andi(Rparam_count, Rflags, ConstantPoolCacheEntry::parameter_size_mask); + __ load_receiver(Rparam_count, Rreceiver); + __ verify_oop(Rreceiver); + + // Get return address. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, Interpreter::return_5_addrs_by_index_table()); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + __ ldx(Rret_addr, Rret_type, Rtable_addr); + + // Get receiver klass. + __ null_check_throw(Rreceiver, oopDesc::klass_offset_in_bytes(), Rscratch3); + __ load_klass(Rrecv_klass, Rreceiver); + __ verify_oop(Rrecv_klass); + + // Check corner case object method. + Label LobjectMethod; + + __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift); + __ btrue(CCR0, LobjectMethod); + + // Fallthrough: The normal invokeinterface case. + __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2, false); + + // Find entry point to call. + Label Lthrow_icc, Lthrow_ame; + // Result will be returned in Rindex. + __ mr(Rscratch4, Rrecv_klass); + __ mr(Rscratch3, Rindex); + __ lookup_interface_method(Rrecv_klass, Rinterface_klass, Rindex, Rindex, Rscratch1, Rscratch2, Lthrow_icc); + + __ cmpdi(CCR0, Rindex, 0); + __ beq(CCR0, Lthrow_ame); + // Found entry. Jump off! + __ call_from_interpreter(Rindex, Rret_addr, Rscratch1, Rscratch2); + + // Vtable entry was NULL => Throw abstract method error. + __ bind(Lthrow_ame); + __ mr(Rrecv_klass, Rscratch4); + __ mr(Rindex, Rscratch3); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); + + // Interface was not found => Throw incompatible class change error. + __ bind(Lthrow_icc); + __ mr(Rrecv_klass, Rscratch4); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError)); + + __ should_not_reach_here(); + + // Special case of invokeinterface called for virtual method of + // java.lang.Object. See ConstantPoolCacheEntry::set_method() for details: + // The invokeinterface was rewritten to a invokevirtual, hence we have + // to handle this corner case. This code isn't produced by javac, but could + // be produced by another compliant java compiler. + __ bind(LobjectMethod); + invokeinterface_object_method(Rrecv_klass, Rret_addr, Rflags, Rindex, Rscratch1, Rscratch2); +} + +// Common code for invokedynamic and invokehandle. +// +// Input: +// - byte_no +// +// Output: +// - Rmethod: The method oop to invoke next. +// - Rret_addr: The return address to return to. +// - Runextended_SP: The register the unextended SP will be saved in. +// - Rappendix: MethodType (invokehandle) or CallSite obj (invokedynamic). +// - Rflags: Method flags from const pool cache. +// - Rrecv: Cache for "this" pointer, might be noreg if static call. +// +// Kills: +// - Rscratch1 +void TemplateTable::prepare_indy_invoke(int byte_no, Register Rmethod, Register Rret_addr, Register Runextended_SP, + Register Rappendix, Register Rflags, Register Rrecv, Register Rscratch1) { + // Determine flags. + const Bytecodes::Code code = bytecode(); + const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokedynamic = code == Bytecodes::_invokedynamic; + const bool is_invokehandle = code == Bytecodes::_invokehandle; + const bool is_invokevirtual = code == Bytecodes::_invokevirtual; + const bool is_invokespecial = code == Bytecodes::_invokespecial; + const bool load_receiver = (Rrecv != noreg); + + load_invoke_cp_cache_entry(byte_no, Rmethod, Rappendix, Rflags, is_invokevirtual, false, is_invokedynamic); + + // Saving of SP done in call_from_interpreter. + + // Maybe push "appendix" to arguments. + if (is_invokedynamic || is_invokehandle) { + Label Ldone; + __ verify_oop(Rappendix); + __ rldicl_(R0, Rflags, 64-ConstantPoolCacheEntry::has_appendix_shift, 63); + __ beq(CCR0, Ldone); + // Push "appendix" (MethodType, CallSite, etc.). + // This must be done before we get the receiver, + // since the parameter_size includes it. + __ push_ptr(Rappendix); + __ bind(Ldone); + } + + // Load receiver if needed (after appendix is pushed so parameter size is correct). + if (load_receiver) { + const Register Rparam_count = Rscratch1; + __ andi(Rparam_count, Rflags, ConstantPoolCacheEntry::parameter_size_mask); + __ load_receiver(Rparam_count, Rrecv); + __ verify_oop(Rrecv); + } + + // Get return address. + { + Register Rtable_addr = Rscratch1; + Register Rret_type = Rret_addr; + address table_addr = (is_invokeinterface || is_invokedynamic) ? + (address) Interpreter::return_5_addrs_by_index_table() : (address) Interpreter::return_3_addrs_by_index_table(); + + // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value. + __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits); + __ load_dispatch_table(Rtable_addr, (address*)table_addr); + __ sldi(Rret_type, Rret_type, LogBytesPerWord); + // Get return address. + __ ldx(Rret_addr, Rtable_addr, Rret_type); + } +} + +void TemplateTable::invokedynamic(int byte_no) { + transition(vtos, vtos); + + assert(byte_no == f12_oop, "use this argument"); + + const Register Rcallsite = R3_ARG1, + Rret_addr = R4_ARG2, + Rflags = R5_ARG3, + Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2; + + if (!EnableInvokeDynamic) { + // We should not encounter this bytecode if !EnableInvokeDynamic. + // The verifier will stop it. However, if we get past the verifier, + // this will stop the thread in a reasonable way, without crashing the JVM. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError)); + // The call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + return; + } + + prepare_indy_invoke(byte_no, R19_method, Rret_addr, R21_sender_SP, Rcallsite, Rflags, noreg, Rscratch1); + + // Profile this call. + __ profile_call(Rscratch1, Rscratch2); + + // Off we go. With the new method handles, we don't jump to a method handle + // entry any more. Instead, we pushed an "appendix" in prepare invoke, which happens + // to be the callsite object the bootstrap method returned. This is passed to a + // "link" method which does the dispatch (Most likely just grabs the MH stored + // inside the callsite and does an invokehandle). + __ call_from_interpreter(R19_method, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */); +} + +void TemplateTable::invokehandle(int byte_no) { + transition(vtos, vtos); + + assert(byte_no == f12_oop, "use this argument"); + + const Register Rmethod_type = R3_ARG1, + Rret_addr = R4_ARG2, + Rflags = R5_ARG3, + Rrecv = R6_ARG4, + Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2, + Rmethod = R31; + + if (!EnableInvokeDynamic) { + // Rewriter does not generate this bytecode. + __ should_not_reach_here(); + return; + } + + prepare_indy_invoke(byte_no, Rmethod, Rret_addr, R21_sender_SP, Rmethod_type, Rflags, Rrecv, Rscratch1); + __ null_check_throw(Rrecv, -1, Rscratch2); + + __ profile_final_call(Rrecv, Rscratch1); + + // Still no call from handle => We call the method handle interpreter here. + __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */); +} + +// ============================================================================= +// Allocation + +// Puts allocated obj ref onto the expression stack. +void TemplateTable::_new() { + transition(vtos, atos); + + Label Lslow_case, + Ldone, + Linitialize_header, + Lallocate_shared, + Linitialize_object; // Including clearing the fields. + + const Register RallocatedObject = R17_tos, + RinstanceKlass = R9_ARG7, + Rscratch = R11_scratch1, + Roffset = R8_ARG6, + Rinstance_size = Roffset, + Rcpool = R4_ARG2, + Rtags = R3_ARG1, + Rindex = R5_ARG3; + + // -------------------------------------------------------------------------- + // Check if fast case is possible. + + // Load index of constant pool entry. + __ get_2_byte_integer_at_bcp(1, Rindex, InterpreterMacroAssembler::Unsigned); + // Load pointers to const pool and const pool's tags array. + __ get_cpool_and_tags(Rcpool, Rtags); + + // Make sure the class we're about to instantiate has been resolved + // This is done before loading instanceKlass to be consistent with the order + // how Constant Pool is updated (see constantPoolOopDesc::klass_at_put). + __ addi(Rtags, Rtags, typeArrayOopDesc::header_size(T_BYTE) * wordSize); + __ lbzx(Rtags, Rindex, Rtags); + + __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class); + __ bne(CCR0, Lslow_case); + + // Get instanceKlass. + __ sldi(Roffset, Rindex, LogBytesPerWord); + __ addi(Roffset, Roffset, sizeof(constantPoolOopDesc)); + __ isync(); // Order load of instance Klass wrt. tags. + __ ldx(RinstanceKlass, Roffset, Rcpool); + + // Make sure klass is fully initialized and get instance_size. + __ lbz(Rscratch, in_bytes(instanceKlass::init_state_offset()), RinstanceKlass); + __ lwz(Rinstance_size, in_bytes(Klass::layout_helper_offset()), RinstanceKlass); + + __ cmpdi(CCR0, Rscratch, instanceKlass::fully_initialized); + __ bne(CCR0, Lslow_case); + + // Make sure klass does not have has_finalizer, or is abstract, or interface or java/lang/Class. + __ testbitdi(CCR0, R0, Rinstance_size, Klass::_lh_instance_slow_path_bit); + __ bne(CCR0, Lslow_case); + + // -------------------------------------------------------------------------- + // Fast case: + // Allocate the instance. + // 1) Try to allocate in the TLAB. + // 2) If fail, and the TLAB is not full enough to discard, allocate in the shared Eden. + // 3) If the above fails (or is not applicable), go to a slow case (creates a new TLAB, etc.). + const bool allow_shared_alloc = Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; + + if (UseTLAB) { + Register RoldTopValue = RallocatedObject; // Object will be allocated here if it fits. + Register RnewTopValue = R6_ARG4; + Register RendValue = R7_ARG5; + + // Check if we can allocate in the TLAB. + __ ld(RoldTopValue, in_bytes(JavaThread::tlab_top_offset()), R16_thread); + __ ld(RendValue, in_bytes(JavaThread::tlab_end_offset()), R16_thread); + + __ add(RnewTopValue, Rinstance_size, RoldTopValue); + + // If there is enough space, we do not CAS and do not clear. + __ cmpld(CCR0, RnewTopValue, RendValue); + __ bgt(CCR0, allow_shared_alloc ? Lallocate_shared : Lslow_case); + + __ std(RnewTopValue, in_bytes(JavaThread::tlab_top_offset()), R16_thread); + + if (ZeroTLAB) { + // The fields have already been cleared. + __ b(Linitialize_header); + } else { + // Initialize both the header and fields. + __ b(Linitialize_object); + } + + // Fall through: TLAB was too small. + if (allow_shared_alloc) { + Register RtlabWasteLimitValue = R10_ARG8; + Register RfreeValue = RnewTopValue; + + __ bind(Lallocate_shared); + // Check if tlab should be discarded (refill_waste_limit >= free). + __ ld(RtlabWasteLimitValue, in_bytes(JavaThread::tlab_refill_waste_limit_offset()), R16_thread); + __ subf(RfreeValue, RoldTopValue, RendValue); + __ srdi(RfreeValue, RfreeValue, LogHeapWordSize); // in dwords + __ cmpld(CCR0, RtlabWasteLimitValue, RfreeValue); + __ bge(CCR0, Lslow_case); + + // Increment waste limit to prevent getting stuck on this slow path. + __ addi(RtlabWasteLimitValue, RtlabWasteLimitValue, (int)ThreadLocalAllocBuffer::refill_waste_limit_increment()); + __ std(RtlabWasteLimitValue, in_bytes(JavaThread::tlab_refill_waste_limit_offset()), R16_thread); + } + // else: No allocation in the shared eden. // fallthru: __ b(Lslow_case); + } + // else: Always go the slow path. + + // -------------------------------------------------------------------------- + // slow case + __ bind(Lslow_case); + call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), Rcpool, Rindex); + + if (UseTLAB) { + __ b(Ldone); + // -------------------------------------------------------------------------- + // Init1: Zero out newly allocated memory. + + if (!ZeroTLAB || allow_shared_alloc) { + // Clear object fields. + __ bind(Linitialize_object); + + // Initialize remaining object fields. + Register Rbase = Rtags; + __ addi(Rinstance_size, Rinstance_size, 7 - (int)sizeof(oopDesc)); + __ addi(Rbase, RallocatedObject, sizeof(oopDesc)); + __ srdi(Rinstance_size, Rinstance_size, 3); + + // Clear out object skipping header. Takes also care of the zero length case. + __ clear_memory_doubleword(Rbase, Rinstance_size); + // fallthru: __ b(Linitialize_header); + } + + // -------------------------------------------------------------------------- + // Init2: Initialize the header: mark, klass + __ bind(Linitialize_header); + + // Init mark. + if (UseBiasedLocking) { + __ ld(Rscratch, in_bytes(Klass::prototype_header_offset()), RinstanceKlass); + } else { + __ load_const_optimized(Rscratch, markOopDesc::prototype(), R0); + } + __ std(Rscratch, oopDesc::mark_offset_in_bytes(), RallocatedObject); + + // Init klass. + if (UseCompressedOops) { + __ li(R0, 0); + __ stw(R0, oopDesc::klass_gap_offset_in_bytes(), RallocatedObject); // klass gap if compressed + } + __ store_heap_oop_not_null(RinstanceKlass, oopDesc::klass_offset_in_bytes(), RallocatedObject); // klass (last for cms) + + // Check and trigger dtrace event. + { + SkipIfEqualZero skip_if(_masm, Rscratch, &DTraceAllocProbes); + __ push(atos); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc)); + __ pop(atos); + } + } + + // continue + __ bind(Ldone); + + // Must prevent reordering of stores for object initialization with stores that publish the new object. + __ membar(Assembler::StoreStore); +} + +void TemplateTable::newarray() { + transition(itos, atos); + + __ lbz(R4, 1, R14_bcp); + __ extsw(R5, R17_tos); + call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), R4, R5 /* size */); + + // Must prevent reordering of stores for object initialization with stores that publish the new object. + __ membar(Assembler::StoreStore); +} + +void TemplateTable::anewarray() { + transition(itos, atos); + + __ get_constant_pool(R4); + __ get_2_byte_integer_at_bcp(1, R5, InterpreterMacroAssembler::Unsigned); + __ extsw(R6, R17_tos); // size + call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), R4 /* pool */, R5 /* index */, R6 /* size */); + + // Must prevent reordering of stores for object initialization with stores that publish the new object. + __ membar(Assembler::StoreStore); +} + +// Allocate a multi dimensional array +void TemplateTable::multianewarray() { + transition(vtos, atos); + + Register Rptr = R31; // Needs to survive C call. + + // Put ndims * wordSize into frame temp slot + __ lbz(Rptr, 3, R14_bcp); + __ sldi(Rptr, Rptr, Interpreter::logStackElementSize); + // Esp points past last_dim, so set to R4 to first_dim address. + __ add(R4, Rptr, R15_esp); + call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), R4 /* first_size_address */); + // Pop all dimensions off the stack. + __ add(R15_esp, Rptr, R15_esp); + + // Must prevent reordering of stores for object initialization with stores that publish the new object. + __ membar(Assembler::StoreStore); +} + +void TemplateTable::arraylength() { + transition(atos, itos); + + Label LnoException; + __ verify_oop(R17_tos); + __ null_check_throw(R17_tos, arrayOopDesc::length_offset_in_bytes(), R11_scratch1); + __ lwa(R17_tos, arrayOopDesc::length_offset_in_bytes(), R17_tos); +} + +// ============================================================================ +// Typechecks + +void TemplateTable::checkcast() { + transition(atos, atos); + + Label Ldone, Lis_null, Lquicked, Lresolved; + Register Roffset = R6_ARG4, + RobjKlass = R4_ARG2, + RspecifiedKlass = R5_ARG3, // Generate_ClassCastException_verbose_handler will read value from this register. + Rcpool = R11_scratch1, + Rtags = R12_scratch2; + + // Null does not pass. + __ cmpdi(CCR0, R17_tos, 0); + __ beq(CCR0, Lis_null); + + // Get constant pool tag to find out if the bytecode has already been "quickened". + __ get_cpool_and_tags(Rcpool, Rtags); + + __ get_2_byte_integer_at_bcp(1, Roffset, InterpreterMacroAssembler::Unsigned); + + __ addi(Rtags, Rtags, typeArrayOopDesc::header_size(T_BYTE) * wordSize); + __ lbzx(Rtags, Rtags, Roffset); + + __ sldi(Roffset, Roffset, LogBytesPerWord); + + __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class); + __ beq(CCR0, Lquicked); + + // Call into the VM to "quicken" instanceof. + __ push_ptr(); // for GC + call_VM(RspecifiedKlass, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); + __ pop_ptr(); // Restore receiver. + __ b(Lresolved); + + // Extract target class from constant pool. + __ bind(Lquicked); + __ isync(); // Order load of specified Klass wrt. tags. + __ addi(Roffset, Roffset, sizeof(constantPoolOopDesc)); + __ ldx(RspecifiedKlass, Rcpool, Roffset); + + // Do the checkcast. + __ bind(Lresolved); + // Get value klass in RobjKlass. + __ load_klass(RobjKlass, R17_tos); + // Generate a fast subtype check. Branch to cast_ok if no failure. Return 0 if failure. + __ gen_subtype_check(RobjKlass, RspecifiedKlass, /*3 temp regs*/ Roffset, Rcpool, Rtags, /*target if subtype*/ Ldone); + + // Not a subtype; so must throw exception + // Target class oop is in register R6_ARG4 == RspecifiedKlass by convention. + __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ClassCastException_entry); + __ mtctr(R11_scratch1); + __ bctr(); + + // Profile the null case. + __ align(32, 12); + __ bind(Lis_null); + __ profile_null_seen(R11_scratch1, Rtags); // Rtags used as scratch. + + __ align(32, 12); + __ bind(Ldone); +} + +// Output: +// - tos == 0: Obj was null or not an instance of class. +// - tos == 1: Obj was an instance of class. +void TemplateTable::instanceof() { + transition(atos, itos); + + Label Ldone, Lis_null, Lquicked, Lresolved; + Register Roffset = R5_ARG3, + RobjKlass = R4_ARG2, + RspecifiedKlass = R6_ARG4, // Generate_ClassCastException_verbose_handler will expect the value in this register. + Rcpool = R11_scratch1, + Rtags = R12_scratch2; + + // Null does not pass. + __ cmpdi(CCR0, R17_tos, 0); + __ beq(CCR0, Lis_null); + + // Get constant pool tag to find out if the bytecode has already been "quickened". + __ get_cpool_and_tags(Rcpool, Rtags); + + __ get_2_byte_integer_at_bcp(1, Roffset, InterpreterMacroAssembler::Unsigned); + + __ addi(Rtags, Rtags, typeArrayOopDesc::header_size(T_BYTE) * wordSize); + __ lbzx(Rtags, Rtags, Roffset); + + __ sldi(Roffset, Roffset, LogBytesPerWord); + + __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class); + __ beq(CCR0, Lquicked); + + // Call into the VM to "quicken" instanceof. + __ push_ptr(); // for GC + call_VM(RspecifiedKlass, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); + __ pop_ptr(); // Restore receiver. + __ b(Lresolved); + + // Extract target class from constant pool. + __ bind(Lquicked); + __ isync(); // Order load of specified Klass wrt. tags. + __ addi(Roffset, Roffset, sizeof(constantPoolOopDesc)); + __ ldx(RspecifiedKlass, Rcpool, Roffset); + + // Do the checkcast. + __ bind(Lresolved); + // Get value klass in RobjKlass. + __ load_klass(RobjKlass, R17_tos); + // Generate a fast subtype check. Branch to cast_ok if no failure. Return 0 if failure. + __ li(R17_tos, 1); + __ gen_subtype_check(RobjKlass, RspecifiedKlass, /*3 temp regs*/ Roffset, Rcpool, Rtags, /*target if subtype*/ Ldone); + __ li(R17_tos, 0); + + if (ProfileInterpreter) { + __ b(Ldone); + } + + // Profile the null case. + __ align(32, 12); + __ bind(Lis_null); + __ profile_null_seen(Rcpool, Rtags); // Rcpool and Rtags used as scratch. + + __ align(32, 12); + __ bind(Ldone); +} + +// ============================================================================= +// Breakpoints + +void TemplateTable::_breakpoint() { + transition(vtos, vtos); + + // Get the unpatched byte code. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::get_original_bytecode_at), R19_method, R14_bcp); + __ mr(R31, R3_RET); + + // Post the breakpoint event. + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), R19_method, R14_bcp); + + // Complete the execution of original bytecode. + __ dispatch_Lbyte_code(vtos, R31, Interpreter::normal_table(vtos)); +} + +// ============================================================================= +// Exceptions + +void TemplateTable::athrow() { + transition(atos, vtos); + + // Exception oop is in tos + __ verify_oop(R17_tos); + + __ null_check_throw(R17_tos, -1, R11_scratch1); + + // Throw exception interpreter entry expects exception oop to be in R3. + __ mr(R3_RET, R17_tos); + __ load_dispatch_table(R11_scratch1, (address*)Interpreter::throw_exception_entry()); + __ mtctr(R11_scratch1); + __ bctr(); +} + +// ============================================================================= +// Synchronization +// Searches the basic object lock list on the stack for a free slot +// and uses it to lock the obect in tos. +// +// Recursive locking is enabled by exiting the search if the same +// object is already found in the list. Thus, a new basic lock obj lock +// is allocated "higher up" in the stack and thus is found first +// at next monitor exit. +void TemplateTable::monitorenter() { + transition(atos, vtos); + + __ verify_oop(R17_tos); + + Register Rcurrent_monitor = R11_scratch1, + Rcurrent_obj = R12_scratch2, + Robj_to_lock = R17_tos, + Rscratch1 = R3_ARG1, + Rscratch2 = R4_ARG2, + Rscratch3 = R5_ARG3, + Rcurrent_obj_addr = R6_ARG4; + + // ------------------------------------------------------------------------------ + // Null pointer exception. + __ null_check_throw(Robj_to_lock, -1, R11_scratch1); + + // Try to acquire a lock on the object. + // Repeat until succeeded (i.e., until monitorenter returns true). + + // ------------------------------------------------------------------------------ + // Find a free slot in the monitor block. + Label Lfound, Lexit, Lallocate_new; + ConditionRegister found_free_slot = CCR0, + found_same_obj = CCR1, + reached_limit = CCR6; + { + Label Lloop, Lentry; + Register Rlimit = Rcurrent_monitor; + + // Set up search loop - start with topmost monitor. + __ add(Rcurrent_obj_addr, BasicObjectLock::obj_offset_in_bytes(), R26_monitor); + + __ ld(Rlimit, 0, R1_SP); + __ addi(Rlimit, Rlimit, - (frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes() - BasicObjectLock::obj_offset_in_bytes())); // Monitor base + + // Check if any slot is present => short cut to allocation if not. + __ cmpld(reached_limit, Rcurrent_obj_addr, Rlimit); + __ bgt(reached_limit, Lallocate_new); + + // Pre-load topmost slot. + __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr); + __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize); + // The search loop. + __ bind(Lloop); + // Found free slot? + __ cmpdi(found_free_slot, Rcurrent_obj, 0); + // Is this entry for same obj? If so, stop the search and take the found + // free slot or allocate a new one to enable recursive locking. + __ cmpd(found_same_obj, Rcurrent_obj, Robj_to_lock); + __ cmpld(reached_limit, Rcurrent_obj_addr, Rlimit); + __ beq(found_free_slot, Lexit); + __ beq(found_same_obj, Lallocate_new); + __ bgt(reached_limit, Lallocate_new); + // Check if last allocated BasicLockObj reached. + __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr); + __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize); + // Next iteration if unchecked BasicObjectLocks exist on the stack. + __ b(Lloop); + } + + // ------------------------------------------------------------------------------ + // Check if we found a free slot. + __ bind(Lexit); + + __ addi(Rcurrent_monitor, Rcurrent_obj_addr, -(frame::interpreter_frame_monitor_size() * wordSize) - BasicObjectLock::obj_offset_in_bytes()); + __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, - frame::interpreter_frame_monitor_size() * wordSize); + __ b(Lfound); + + // We didn't find a free BasicObjLock => allocate one. + __ align(32, 12); + __ bind(Lallocate_new); + __ add_monitor_to_stack(false, Rscratch1, Rscratch2); + __ mr(Rcurrent_monitor, R26_monitor); + __ addi(Rcurrent_obj_addr, R26_monitor, BasicObjectLock::obj_offset_in_bytes()); + + // ------------------------------------------------------------------------------ + // We now have a slot to lock. + __ bind(Lfound); + + // Increment bcp to point to the next bytecode, so exception handling for async. exceptions work correctly. + // The object has already been poped from the stack, so the expression stack looks correct. + __ addi(R14_bcp, R14_bcp, 1); + + __ std(Robj_to_lock, 0, Rcurrent_obj_addr); + __ lock_object(Rcurrent_monitor, Robj_to_lock); + + // Check if there's enough space on the stack for the monitors after locking. + Label Lskip_stack_check; + // Optimization: If the monitors stack section is less then a std page size (4K) don't run + // the stack check. There should be enough shadow pages to fit that in. + __ ld(Rscratch3, 0, R1_SP); + __ sub(Rscratch3, Rscratch3, R26_monitor); + __ cmpdi(CCR0, Rscratch3, 4*K); + __ blt(CCR0, Lskip_stack_check); + + DEBUG_ONLY(__ untested("stack overflow check during monitor enter");) + __ li(Rscratch1, 0); + __ generate_stack_overflow_check_with_compare_and_throw(Rscratch1, Rscratch2); + + __ align(32, 12); + __ bind(Lskip_stack_check); + + // The bcp has already been incremented. Just need to dispatch to next instruction. + __ dispatch_next(vtos); +} + +void TemplateTable::monitorexit() { + transition(atos, vtos); + __ verify_oop(R17_tos); + + Register Rcurrent_monitor = R11_scratch1, + Rcurrent_obj = R12_scratch2, + Robj_to_lock = R17_tos, + Rcurrent_obj_addr = R3_ARG1, + Rlimit = R4_ARG2; + Label Lfound, Lillegal_monitor_state; + + // Check corner case: unbalanced monitorEnter / Exit. + __ ld(Rlimit, 0, R1_SP); + __ addi(Rlimit, Rlimit, - (frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes())); // Monitor base + + // Null pointer check. + __ null_check_throw(Robj_to_lock, -1, R11_scratch1); + + __ cmpld(CCR0, R26_monitor, Rlimit); + __ bgt(CCR0, Lillegal_monitor_state); + + // Find the corresponding slot in the monitors stack section. + { + Label Lloop; + + // Start with topmost monitor. + __ addi(Rcurrent_obj_addr, R26_monitor, BasicObjectLock::obj_offset_in_bytes()); + __ addi(Rlimit, Rlimit, BasicObjectLock::obj_offset_in_bytes()); + __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr); + __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize); + + __ bind(Lloop); + // Is this entry for same obj? + __ cmpd(CCR0, Rcurrent_obj, Robj_to_lock); + __ beq(CCR0, Lfound); + + // Check if last allocated BasicLockObj reached. + + __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr); + __ cmpld(CCR0, Rcurrent_obj_addr, Rlimit); + __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize); + + // Next iteration if unchecked BasicObjectLocks exist on the stack. + __ ble(CCR0, Lloop); + } + + // Fell through without finding the basic obj lock => throw up! + __ bind(Lillegal_monitor_state); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + __ align(32, 12); + __ bind(Lfound); + __ addi(Rcurrent_monitor, Rcurrent_obj_addr, + -(frame::interpreter_frame_monitor_size() * wordSize) - BasicObjectLock::obj_offset_in_bytes()); + __ unlock_object(Rcurrent_monitor); +} + +// ============================================================================ +// Wide bytecodes + +// Wide instructions. Simply redirects to the wide entry point for that instruction. +void TemplateTable::wide() { + transition(vtos, vtos); + + const Register Rtable = R11_scratch1, + Rindex = R12_scratch2, + Rtmp = R0; + + __ lbz(Rindex, 1, R14_bcp); + + __ load_dispatch_table(Rtable, Interpreter::_wentry_point); + + __ slwi(Rindex, Rindex, LogBytesPerWord); + __ ldx(Rtmp, Rtable, Rindex); + __ mtctr(Rtmp); + __ bctr(); + // Note: the bcp increment step is part of the individual wide bytecode implementations. +} +#endif // !CC_INTERP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/templateTable_ppc_64.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/templateTable_ppc_64.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_TEMPLATETABLE_PPC_64_HPP +#define CPU_PPC_VM_TEMPLATETABLE_PPC_64_HPP + + static void invokevfinal_helper(Register RmethodOop, Register Rflags, Register Rscratch1, Register Rscratch2); + static void generate_vtable_call(Register Rrecv_klass, Register Rindex, Register Rret, Register Rtemp); + static void invokeinterface_object_method(Register Rrecv_klass, Register Rret, Register Rflags, Register Rindex, Register Rtemp, Register Rtemp2); + static void prepare_indy_invoke(int byte_no, Register Rmethod, Register Rret_addr, Register Runextended_SP, Register Rmethod_type, Register Rflags, Register Rrecv, Register Rscratch1); + + // Branch_conditional which takes TemplateTable::Condition. + static void branch_conditional(ConditionRegister crx, TemplateTable::Condition cc, Label& L, bool invert = false); + static void if_cmp_common(Register Rfirst, Register Rsecond, Register Rscratch1, Register Rscratch2, Condition cc, bool is_jint, bool cmp0); + +#endif // CPU_PPC_VM_TEMPLATETABLE_PPC_64_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vmStructs_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vmStructs_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_VMSTRUCTS_PPC_HPP +#define CPU_PPC_VM_VMSTRUCTS_PPC_HPP + +// These are the CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) + +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) + +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) + +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) + +#endif // CPU_PPC_VM_VMSTRUCTS_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vm_version_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vm_version_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,487 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "assembler_ppc.inline.hpp" +#include "macroAssembler_ppc.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/java.hpp" +#include "runtime/stubCodeGenerator.hpp" +#include "utilities/defaultStream.hpp" +#include "vm_version_ppc.hpp" +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif +#ifdef TARGET_OS_FAMILY_linux +# include "os_linux.inline.hpp" +#endif + +# include + +int VM_Version::_features = VM_Version::unknown_m; +int VM_Version::_measured_cache_line_size = 128; // default value +const char* VM_Version::_features_str = ""; +bool VM_Version::_is_determine_features_test_running = false; + + +#define MSG(flag) \ + if (flag && !FLAG_IS_DEFAULT(flag)) \ + jio_fprintf(defaultStream::error_stream(), \ + "warning: -XX:+" #flag " requires -XX:+UseSIGTRAP\n" \ + " -XX:+" #flag " will be disabled!\n"); + +void VM_Version::initialize() { + + // Test which instructions are supported and measure cache line size. + determine_features(); + + // If PowerArchitecturePPC64 hasn't been specified explicitly determine from features. + if (FLAG_IS_DEFAULT(PowerArchitecturePPC64)) { + if (VM_Version::has_popcntw()) { + FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 7); + } else if (VM_Version::has_cmpb()) { + FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 6); + } else if (VM_Version::has_popcntb()) { + FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 5); + } else { + FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 0); + } + } + guarantee(PowerArchitecturePPC64 == 0 || PowerArchitecturePPC64 == 5 || + PowerArchitecturePPC64 == 6 || PowerArchitecturePPC64 == 7, + "PowerArchitecturePPC64 should be 0, 5, 6 or 7"); + + if (!UseSIGTRAP) { + MSG(TrapBasedICMissChecks); + MSG(TrapBasedNotEntrantChecks); + MSG(TrapBasedNullChecks); + MSG(TrapBasedRangeChecks); + FLAG_SET_ERGO(bool, TrapBasedNotEntrantChecks, false); + FLAG_SET_ERGO(bool, TrapBasedNullChecks, false); + FLAG_SET_ERGO(bool, TrapBasedICMissChecks, false); + FLAG_SET_ERGO(bool, TrapBasedRangeChecks, false); + } + +#ifdef COMPILER2 + // On Power6 test for section size. + if (PowerArchitecturePPC64 == 6) { + determine_section_size(); + // TODO: PPC port } else { + // TODO: PPC port PdScheduling::power6SectorSize = 0x20; + } + + MaxVectorSize = 8; +#endif + + // Create and print feature-string. + char buf[(num_features+1) * 16]; // Max 16 chars per feature. + jio_snprintf(buf, sizeof(buf), + "ppc64%s%s%s%s%s%s%s%s", + (has_fsqrt() ? " fsqrt" : ""), + (has_isel() ? " isel" : ""), + (has_lxarxeh() ? " lxarxeh" : ""), + (has_cmpb() ? " cmpb" : ""), + //(has_mftgpr()? " mftgpr" : ""), + (has_popcntb() ? " popcntb" : ""), + (has_popcntw() ? " popcntw" : ""), + (has_fcfids() ? " fcfids" : ""), + (has_vand() ? " vand" : "") + // Make sure number of %s matches num_features! + ); + _features_str = strdup(buf); + NOT_PRODUCT(if (Verbose) print_features();); + + // PPC64 supports 8-byte compare-exchange operations (see + // Atomic::cmpxchg and StubGenerator::generate_atomic_cmpxchg_ptr) + // and 'atomic long memory ops' (see Unsafe_GetLongVolatile). + _supports_cx8 = true; + + UseSSE = 0; // Only on x86 and x64 + + intx cache_line_size = _measured_cache_line_size; + + if (FLAG_IS_DEFAULT(AllocatePrefetchStyle)) AllocatePrefetchStyle = 1; + + if (AllocatePrefetchStyle == 4) { + AllocatePrefetchStepSize = cache_line_size; // Need exact value. + if (FLAG_IS_DEFAULT(AllocatePrefetchLines)) AllocatePrefetchLines = 12; // Use larger blocks by default. + if (AllocatePrefetchDistance < 0) AllocatePrefetchDistance = 2*cache_line_size; // Default is not defined? + } else { + if (cache_line_size > AllocatePrefetchStepSize) AllocatePrefetchStepSize = cache_line_size; + if (FLAG_IS_DEFAULT(AllocatePrefetchLines)) AllocatePrefetchLines = 3; // Optimistic value. + if (AllocatePrefetchDistance < 0) AllocatePrefetchDistance = 3*cache_line_size; // Default is not defined? + } + + assert(AllocatePrefetchLines > 0, "invalid value"); + if (AllocatePrefetchLines < 1) // Set valid value in product VM. + AllocatePrefetchLines = 1; // Conservative value. + + if (AllocatePrefetchStyle == 3 && AllocatePrefetchDistance < cache_line_size) + AllocatePrefetchStyle = 1; // Fall back if inappropriate. + + assert(AllocatePrefetchStyle >= 0, "AllocatePrefetchStyle should be positive"); +} + +void VM_Version::print_features() { + tty->print_cr("Version: %s cache_line_size = %d", cpu_features(), get_cache_line_size()); +} + +#ifdef COMPILER2 +// Determine section size on power6: If section size is 8 instructions, +// there should be a difference between the two testloops of ~15 %. If +// no difference is detected the section is assumed to be 32 instructions. +void VM_Version::determine_section_size() { + + int unroll = 80; + + const int code_size = (2* unroll * 32 + 100)*BytesPerInstWord; + + // Allocate space for the code. + ResourceMark rm; + CodeBuffer cb("detect_section_size", code_size, 0); + MacroAssembler* a = new MacroAssembler(&cb); + + uint32_t *code = (uint32_t *)a->pc(); + // Emit code. + void (*test1)() = (void(*)())(void *)a->function_entry(); + + Label l1; + + a->li(R4, 1); + a->sldi(R4, R4, 28); + a->b(l1); + a->align(CodeEntryAlignment); + + a->bind(l1); + + for (int i = 0; i < unroll; i++) { + // Schleife 1 + // ------- sector 0 ------------ + // ;; 0 + a->nop(); // 1 + a->fpnop0(); // 2 + a->fpnop1(); // 3 + a->addi(R4,R4, -1); // 4 + + // ;; 1 + a->nop(); // 5 + a->fmr(F6, F6); // 6 + a->fmr(F7, F7); // 7 + a->endgroup(); // 8 + // ------- sector 8 ------------ + + // ;; 2 + a->nop(); // 9 + a->nop(); // 10 + a->fmr(F8, F8); // 11 + a->fmr(F9, F9); // 12 + + // ;; 3 + a->nop(); // 13 + a->fmr(F10, F10); // 14 + a->fmr(F11, F11); // 15 + a->endgroup(); // 16 + // -------- sector 16 ------------- + + // ;; 4 + a->nop(); // 17 + a->nop(); // 18 + a->fmr(F15, F15); // 19 + a->fmr(F16, F16); // 20 + + // ;; 5 + a->nop(); // 21 + a->fmr(F17, F17); // 22 + a->fmr(F18, F18); // 23 + a->endgroup(); // 24 + // ------- sector 24 ------------ + + // ;; 6 + a->nop(); // 25 + a->nop(); // 26 + a->fmr(F19, F19); // 27 + a->fmr(F20, F20); // 28 + + // ;; 7 + a->nop(); // 29 + a->fmr(F21, F21); // 30 + a->fmr(F22, F22); // 31 + a->brnop0(); // 32 + + // ------- sector 32 ------------ + } + + // ;; 8 + a->cmpdi(CCR0, R4, unroll); // 33 + a->bge(CCR0, l1); // 34 + a->blr(); + + // Emit code. + void (*test2)() = (void(*)())(void *)a->function_entry(); + // uint32_t *code = (uint32_t *)a->pc(); + + Label l2; + + a->li(R4, 1); + a->sldi(R4, R4, 28); + a->b(l2); + a->align(CodeEntryAlignment); + + a->bind(l2); + + for (int i = 0; i < unroll; i++) { + // Schleife 2 + // ------- sector 0 ------------ + // ;; 0 + a->brnop0(); // 1 + a->nop(); // 2 + //a->cmpdi(CCR0, R4, unroll); + a->fpnop0(); // 3 + a->fpnop1(); // 4 + a->addi(R4,R4, -1); // 5 + + // ;; 1 + + a->nop(); // 6 + a->fmr(F6, F6); // 7 + a->fmr(F7, F7); // 8 + // ------- sector 8 --------------- + + // ;; 2 + a->endgroup(); // 9 + + // ;; 3 + a->nop(); // 10 + a->nop(); // 11 + a->fmr(F8, F8); // 12 + + // ;; 4 + a->fmr(F9, F9); // 13 + a->nop(); // 14 + a->fmr(F10, F10); // 15 + + // ;; 5 + a->fmr(F11, F11); // 16 + // -------- sector 16 ------------- + + // ;; 6 + a->endgroup(); // 17 + + // ;; 7 + a->nop(); // 18 + a->nop(); // 19 + a->fmr(F15, F15); // 20 + + // ;; 8 + a->fmr(F16, F16); // 21 + a->nop(); // 22 + a->fmr(F17, F17); // 23 + + // ;; 9 + a->fmr(F18, F18); // 24 + // -------- sector 24 ------------- + + // ;; 10 + a->endgroup(); // 25 + + // ;; 11 + a->nop(); // 26 + a->nop(); // 27 + a->fmr(F19, F19); // 28 + + // ;; 12 + a->fmr(F20, F20); // 29 + a->nop(); // 30 + a->fmr(F21, F21); // 31 + + // ;; 13 + a->fmr(F22, F22); // 32 + } + + // -------- sector 32 ------------- + // ;; 14 + a->cmpdi(CCR0, R4, unroll); // 33 + a->bge(CCR0, l2); // 34 + + a->blr(); + uint32_t *code_end = (uint32_t *)a->pc(); + a->flush(); + + double loop1_seconds,loop2_seconds, rel_diff; + uint64_t start1, stop1; + + start1 = os::current_thread_cpu_time(false); + (*test1)(); + stop1 = os::current_thread_cpu_time(false); + loop1_seconds = (stop1- start1) / (1000 *1000 *1000.0); + + + start1 = os::current_thread_cpu_time(false); + (*test2)(); + stop1 = os::current_thread_cpu_time(false); + + loop2_seconds = (stop1 - start1) / (1000 *1000 *1000.0); + + rel_diff = (loop2_seconds - loop1_seconds) / loop1_seconds *100; + + if (PrintAssembly) { + ttyLocker ttyl; + tty->print_cr("Decoding section size detection stub at " INTPTR_FORMAT " before execution:", code); + Disassembler::decode((u_char*)code, (u_char*)code_end, tty); + tty->print_cr("Time loop1 :%f", loop1_seconds); + tty->print_cr("Time loop2 :%f", loop2_seconds); + tty->print_cr("(time2 - time1) / time1 = %f %%", rel_diff); + + if (rel_diff > 12.0) { + tty->print_cr("Section Size 8 Instructions"); + } else{ + tty->print_cr("Section Size 32 Instructions or Power5"); + } + } + +#if 0 // TODO: PPC port + // Set sector size (if not set explicitly). + if (FLAG_IS_DEFAULT(Power6SectorSize128PPC64)) { + if (rel_diff > 12.0) { + PdScheduling::power6SectorSize = 0x20; + } else { + PdScheduling::power6SectorSize = 0x80; + } + } else if (Power6SectorSize128PPC64) { + PdScheduling::power6SectorSize = 0x80; + } else { + PdScheduling::power6SectorSize = 0x20; + } +#endif + if (UsePower6SchedulerPPC64) Unimplemented(); +} +#endif // COMPILER2 + +void VM_Version::determine_features() { +#if defined(ABI_ELFv2) + const int code_size = (num_features+1+2*7)*BytesPerInstWord; // TODO(asmundak): calculation is incorrect. +#else + // 7 InstWords for each call (function descriptor + blr instruction). + const int code_size = (num_features+1+2*7)*BytesPerInstWord; +#endif + int features = 0; + + // create test area + enum { BUFFER_SIZE = 2*4*K }; // Needs to be >=2* max cache line size (cache line size can't exceed min page size). + char test_area[BUFFER_SIZE]; + char *mid_of_test_area = &test_area[BUFFER_SIZE>>1]; + + // Allocate space for the code. + ResourceMark rm; + CodeBuffer cb("detect_cpu_features", code_size, 0); + MacroAssembler* a = new MacroAssembler(&cb); + + // Must be set to true so we can generate the test code. + _features = VM_Version::all_features_m; + + // Must be set to true so we can generate the test code. + _features = VM_Version::all_features_m; + + // Emit code. + void (*test)(address addr, uint64_t offset)=(void(*)(address addr, uint64_t offset))(void *)a->function_entry(); + uint32_t *code = (uint32_t *)a->pc(); + // Don't use R0 in ldarx. + // Keep R3_ARG1 unmodified, it contains &field (see below). + // Keep R4_ARG2 unmodified, it contains offset = 0 (see below). + a->fsqrt(F3, F4); // code[0] -> fsqrt_m + a->fsqrts(F3, F4); // code[1] -> fsqrts_m + a->isel(R7, R5, R6, 0); // code[2] -> isel_m + a->ldarx_unchecked(R7, R3_ARG1, R4_ARG2, 1); // code[3] -> lxarx_m + a->cmpb(R7, R5, R6); // code[4] -> bcmp + //a->mftgpr(R7, F3); // code[5] -> mftgpr + a->popcntb(R7, R5); // code[6] -> popcntb + a->popcntw(R7, R5); // code[7] -> popcntw + a->fcfids(F3, F4); // code[8] -> fcfids + a->vand(VR0, VR0, VR0); // code[9] -> vand + a->blr(); + + // Emit function to set one cache line to zero. Emit function descriptor and get pointer to it. + void (*zero_cacheline_func_ptr)(char*) = (void(*)(char*))(void *)a->function_entry(); + a->dcbz(R3_ARG1); // R3_ARG1 = addr + a->blr(); + + uint32_t *code_end = (uint32_t *)a->pc(); + a->flush(); + _features = VM_Version::unknown_m; + + // Print the detection code. + if (PrintAssembly) { + ttyLocker ttyl; + tty->print_cr("Decoding cpu-feature detection stub at " INTPTR_FORMAT " before execution:", code); + Disassembler::decode((u_char*)code, (u_char*)code_end, tty); + } + + // Measure cache line size. + memset(test_area, 0xFF, BUFFER_SIZE); // Fill test area with 0xFF. + (*zero_cacheline_func_ptr)(mid_of_test_area); // Call function which executes dcbz to the middle. + int count = 0; // count zeroed bytes + for (int i = 0; i < BUFFER_SIZE; i++) if (test_area[i] == 0) count++; + guarantee(is_power_of_2(count), "cache line size needs to be a power of 2"); + _measured_cache_line_size = count; + + // Execute code. Illegal instructions will be replaced by 0 in the signal handler. + VM_Version::_is_determine_features_test_running = true; + (*test)((address)mid_of_test_area, (uint64_t)0); + VM_Version::_is_determine_features_test_running = false; + + // determine which instructions are legal. + int feature_cntr = 0; + if (code[feature_cntr++]) features |= fsqrt_m; + if (code[feature_cntr++]) features |= fsqrts_m; + if (code[feature_cntr++]) features |= isel_m; + if (code[feature_cntr++]) features |= lxarxeh_m; + if (code[feature_cntr++]) features |= cmpb_m; + //if(code[feature_cntr++])features |= mftgpr_m; + if (code[feature_cntr++]) features |= popcntb_m; + if (code[feature_cntr++]) features |= popcntw_m; + if (code[feature_cntr++]) features |= fcfids_m; + if (code[feature_cntr++]) features |= vand_m; + + // Print the detection code. + if (PrintAssembly) { + ttyLocker ttyl; + tty->print_cr("Decoding cpu-feature detection stub at " INTPTR_FORMAT " after execution:", code); + Disassembler::decode((u_char*)code, (u_char*)code_end, tty); + } + + _features = features; +} + + +static int saved_features = 0; + +void VM_Version::allow_all() { + saved_features = _features; + _features = all_features_m; +} + +void VM_Version::revert() { + _features = saved_features; +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vm_version_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vm_version_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_VM_VERSION_PPC_HPP +#define CPU_PPC_VM_VM_VERSION_PPC_HPP + +#include "runtime/globals_extension.hpp" +#include "runtime/vm_version.hpp" + +class VM_Version: public Abstract_VM_Version { +protected: + enum Feature_Flag { + fsqrt, + fsqrts, + isel, + lxarxeh, + cmpb, + popcntb, + popcntw, + fcfids, + vand, + dcba, + num_features // last entry to count features + }; + enum Feature_Flag_Set { + unknown_m = 0, + fsqrt_m = (1 << fsqrt ), + fsqrts_m = (1 << fsqrts ), + isel_m = (1 << isel ), + lxarxeh_m = (1 << lxarxeh), + cmpb_m = (1 << cmpb ), + popcntb_m = (1 << popcntb), + popcntw_m = (1 << popcntw), + fcfids_m = (1 << fcfids ), + vand_m = (1 << vand ), + dcba_m = (1 << dcba ), + all_features_m = -1 + }; + static int _features; + static int _measured_cache_line_size; + static const char* _features_str; + static bool _is_determine_features_test_running; + + static void print_features(); + static void determine_features(); // also measures cache line size + static void determine_section_size(); + static void power6_micro_bench(); +public: + // Initialization + static void initialize(); + + static bool is_determine_features_test_running() { return _is_determine_features_test_running; } + // CPU instruction support + static bool has_fsqrt() { return (_features & fsqrt_m) != 0; } + static bool has_fsqrts() { return (_features & fsqrts_m) != 0; } + static bool has_isel() { return (_features & isel_m) != 0; } + static bool has_lxarxeh() { return (_features & lxarxeh_m) !=0; } + static bool has_cmpb() { return (_features & cmpb_m) != 0; } + static bool has_popcntb() { return (_features & popcntb_m) != 0; } + static bool has_popcntw() { return (_features & popcntw_m) != 0; } + static bool has_fcfids() { return (_features & fcfids_m) != 0; } + static bool has_vand() { return (_features & vand_m) != 0; } + static bool has_dcba() { return (_features & dcba_m) != 0; } + + static const char* cpu_features() { return _features_str; } + + static int get_cache_line_size() { return _measured_cache_line_size; } + + // Assembler testing + static void allow_all(); + static void revert(); +}; + +#endif // CPU_PPC_VM_VM_VERSION_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vmreg_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vmreg_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "code/vmreg.hpp" + +void VMRegImpl::set_regName() { + Register reg = ::as_Register(0); + int i; + for (i = 0; i < ConcreteRegisterImpl::max_gpr; ) { + regName[i++] = reg->name(); + regName[i++] = reg->name(); + if (reg->encoding() < RegisterImpl::number_of_registers-1) + reg = reg->successor(); + } + + FloatRegister freg = ::as_FloatRegister(0); + for ( ; i < ConcreteRegisterImpl::max_fpr; ) { + regName[i++] = freg->name(); + regName[i++] = freg->name(); + if (reg->encoding() < FloatRegisterImpl::number_of_registers-1) + freg = freg->successor(); + } + for ( ; i < ConcreteRegisterImpl::number_of_registers; i++) { + regName[i] = "NON-GPR-FPR"; + } +} + diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vmreg_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vmreg_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_VMREG_PPC_HPP +#define CPU_PPC_VM_VMREG_PPC_HPP + + bool is_Register(); + Register as_Register(); + + bool is_FloatRegister(); + FloatRegister as_FloatRegister(); + +#endif // CPU_PPC_VM_VMREG_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vmreg_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vmreg_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef CPU_PPC_VM_VMREG_PPC_INLINE_HPP +#define CPU_PPC_VM_VMREG_PPC_INLINE_HPP + +inline VMReg RegisterImpl::as_VMReg() { + if (this == noreg) return VMRegImpl::Bad(); + return VMRegImpl::as_VMReg(encoding() << 1); +} + +// Since we don't have two halfs here, don't multiply by 2. +inline VMReg ConditionRegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_fpr); +} + +inline VMReg FloatRegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg((encoding() << 1) + ConcreteRegisterImpl::max_gpr); +} + +inline VMReg SpecialRegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg((encoding()) + ConcreteRegisterImpl::max_cnd); +} + +inline bool VMRegImpl::is_Register() { + return (unsigned int)value() < (unsigned int)ConcreteRegisterImpl::max_gpr; +} + +inline bool VMRegImpl::is_FloatRegister() { + return value() >= ConcreteRegisterImpl::max_gpr && + value() < ConcreteRegisterImpl::max_fpr; +} + +inline Register VMRegImpl::as_Register() { + assert(is_Register() && is_even(value()), "even-aligned GPR name"); + return ::as_Register(value()>>1); +} + +inline FloatRegister VMRegImpl::as_FloatRegister() { + assert(is_FloatRegister() && is_even(value()), "must be"); + return ::as_FloatRegister((value() - ConcreteRegisterImpl::max_gpr) >> 1); +} + +inline bool VMRegImpl::is_concrete() { + assert(is_reg(), "must be"); + return is_even(value()); +} + +#endif // CPU_PPC_VM_VMREG_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/ppc/vm/vtableStubs_ppc_64.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/vtableStubs_ppc_64.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,269 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "assembler_ppc.inline.hpp" +#include "code/vtableStubs.hpp" +#include "interp_masm_ppc_64.hpp" +#include "memory/resourceArea.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klassVtable.hpp" +#include "runtime/sharedRuntime.hpp" +#include "vmreg_ppc.inline.hpp" +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif + +#define __ masm-> + +#ifdef PRODUCT +#define BLOCK_COMMENT(str) // nothing +#else +#define BLOCK_COMMENT(str) __ block_comment(str) +#endif +#define BIND(label) bind(label); BLOCK_COMMENT(#label ":") + +#ifndef PRODUCT +extern "C" void bad_compiled_vtable_index(JavaThread* thread, oopDesc* receiver, int index); +#endif + +// Used by compiler only; may use only caller saved, non-argument +// registers. +VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { + // PPC port: use fixed size. + const int code_length = VtableStub::pd_code_size_limit(true); + VtableStub* s = new (code_length) VtableStub(true, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + address start_pc; + +#ifndef PRODUCT + if (CountCompiledCalls) { + __ load_const(R11_scratch1, SharedRuntime::nof_megamorphic_calls_addr()); + __ lwz(R12_scratch2, 0, R11_scratch1); + __ addi(R12_scratch2, R12_scratch2, 1); + __ stw(R12_scratch2, 0, R11_scratch1); + } +#endif + + assert(VtableStub::receiver_location() == R3_ARG1->as_VMReg(), "receiver expected in R3_ARG1"); + + // Get receiver klass. + const Register rcvr_klass = R11_scratch1; + + // We might implicit NULL fault here. + address npe_addr = __ pc(); // npe = null pointer exception + __ load_heap_oop_with_trap_null_check(rcvr_klass, oopDesc::klass_offset_in_bytes(), R3); + + // Set methodOop (in case of interpreted method), and destination address. + int entry_offset = instanceKlass::vtable_start_offset() + vtable_index*vtableEntry::size(); + +#ifndef PRODUCT + if (DebugVtables) { + Label L; + // Check offset vs vtable length. + const Register vtable_len = R12_scratch2; + __ lwz(vtable_len, instanceKlass::vtable_length_offset()*wordSize, rcvr_klass); + __ cmpwi(CCR0, vtable_len, vtable_index*vtableEntry::size()); + __ bge(CCR0, L); + __ li(R12_scratch2, vtable_index); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, bad_compiled_vtable_index), R3_ARG1, R12_scratch2, false); + __ bind(L); + } +#endif + + int v_off = entry_offset*wordSize + vtableEntry::method_offset_in_bytes(); + + __ ld(R19_method, v_off, rcvr_klass); + +#ifndef PRODUCT + if (DebugVtables) { + Label L; + __ cmpdi(CCR0, R19_method, 0); + __ bne(CCR0, L); + __ stop("Vtable entry is ZERO", 102); + __ bind(L); + } +#endif + + // If the vtable entry is null, the method is abstract. + address ame_addr = __ pc(); // ame = abstract method error + + __ load_with_trap_null_check(R12_scratch2, in_bytes(methodOopDesc::from_compiled_offset()), R19_method); + __ mtctr(R12_scratch2); + __ bctr(); + masm->flush(); + + guarantee(__ pc() <= s->code_end(), "overflowed buffer"); + + s->set_exception_points(npe_addr, ame_addr); + + return s; +} + +VtableStub* VtableStubs::create_itable_stub(int vtable_index) { + // PPC port: use fixed size. + const int code_length = VtableStub::pd_code_size_limit(false); + VtableStub* s = new (code_length) VtableStub(false, vtable_index); + ResourceMark rm; + CodeBuffer cb(s->entry_point(), code_length); + MacroAssembler* masm = new MacroAssembler(&cb); + address start_pc; + +#ifndef PRODUCT + if (CountCompiledCalls) { + __ load_const(R11_scratch1, SharedRuntime::nof_megamorphic_calls_addr()); + __ lwz(R12_scratch2, 0, R11_scratch1); + __ addi(R12_scratch2, R12_scratch2, 1); + __ stw(R12_scratch2, 0, R11_scratch1); + } +#endif + + assert(VtableStub::receiver_location() == R3_ARG1->as_VMReg(), "receiver expected in R3_ARG1"); + + // Entry arguments: + // R19_method: Interface + // R3_ARG1: Receiver + // + + const Register rcvr_klass = R11_scratch1; + const Register vtable_len = R12_scratch2; + const Register itable_entry_addr = R21_tmp1; + const Register itable_interface = R22_tmp2; + + // Get receiver klass. + + // We might implicit NULL fault here. + address npe_addr = __ pc(); // npe = null pointer exception + __ load_heap_oop_with_trap_null_check(rcvr_klass, oopDesc::klass_offset_in_bytes(), R3_ARG1); + + //__ ld(rcvr_klass, oopDesc::klass_offset_in_bytes(), R3_ARG1); + + BLOCK_COMMENT("Load start of itable entries into itable_entry."); + __ lwz(vtable_len, instanceKlass::vtable_length_offset() * wordSize, rcvr_klass); + __ slwi(vtable_len, vtable_len, exact_log2(vtableEntry::size() * wordSize)); + __ add(itable_entry_addr, vtable_len, rcvr_klass); + + // Loop over all itable entries until desired interfaceOop(Rinterface) found. + BLOCK_COMMENT("Increment itable_entry_addr in loop."); + const int vtable_base_offset = instanceKlass::vtable_start_offset() * wordSize; + __ addi(itable_entry_addr, itable_entry_addr, vtable_base_offset + itableOffsetEntry::interface_offset_in_bytes()); + + const int itable_offset_search_inc = itableOffsetEntry::size() * wordSize; + Label search; + __ bind(search); + __ ld(itable_interface, 0, itable_entry_addr); + + // Handle IncompatibleClassChangeError in itable stubs. + // If the entry is NULL then we've reached the end of the table + // without finding the expected interface, so throw an exception. + BLOCK_COMMENT("Handle IncompatibleClassChangeError in itable stubs."); + Label throw_icce; + __ cmpdi(CCR1, itable_interface, 0); + __ cmpd(CCR0, itable_interface, R19_method); + __ addi(itable_entry_addr, itable_entry_addr, itable_offset_search_inc); + __ beq(CCR1, throw_icce); + __ bne(CCR0, search); + + // Entry found and itable_entry_addr points to it, get offset of vtable for interface. + + const Register vtable_offset = R12_scratch2; + const Register itable_method = R11_scratch1; + + const int vtable_offset_offset = (itableOffsetEntry::offset_offset_in_bytes() - + itableOffsetEntry::interface_offset_in_bytes()) - + itable_offset_search_inc; + __ lwz(vtable_offset, vtable_offset_offset, itable_entry_addr); + + // Compute itableMethodEntry and get methodOop and entry point for compiler. + const int method_offset = (itableMethodEntry::size() * wordSize * vtable_index) + + itableMethodEntry::method_offset_in_bytes(); + + __ add(itable_method, rcvr_klass, vtable_offset); + __ ld(R19_method, method_offset, itable_method); + +#ifndef PRODUCT + if (DebugVtables) { + Label ok; + __ cmpd(CCR0, R19_method, 0); + __ bne(CCR0, ok); + __ stop("methodOop is null", 103); + __ bind(ok); + } +#endif + + // If the vtable entry is null, the method is abstract. + address ame_addr = __ pc(); // ame = abstract method error + + // Must do an explicit check if implicit checks are disabled. + assert(!MacroAssembler::needs_explicit_null_check(in_bytes(methodOopDesc::from_compiled_offset())), "sanity"); + if (!ImplicitNullChecks || !os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + __ trap_null_check(R19_method); + } else { + __ cmpdi(CCR0, R19_method, 0); + __ beq(CCR0, throw_icce); + } + } + __ ld(R12_scratch2, in_bytes(methodOopDesc::from_compiled_offset()), R19_method); + __ mtctr(R12_scratch2); + __ bctr(); + + // Handle IncompatibleClassChangeError in itable stubs. + // More detailed error message. + // We force resolving of the call site by jumping to the "handle + // wrong method" stub, and so let the interpreter runtime do all the + // dirty work. + __ bind(throw_icce); + __ load_const(R11_scratch1, SharedRuntime::get_handle_wrong_method_stub()); + __ mtctr(R11_scratch1); + __ bctr(); + + masm->flush(); + + guarantee(__ pc() <= s->code_end(), "overflowed buffer"); + + s->set_exception_points(npe_addr, ame_addr); + return s; +} + +int VtableStub::pd_code_size_limit(bool is_vtable_stub) { + if (TraceJumps || DebugVtables || CountCompiledCalls || VerifyOops) { + return 1000; + } else { + if (is_vtable_stub) { + return 20 + 16 + 8; // Plain + (cOops & Traps) + safety + } else { + return 16 + 96; + } + } +} + +int VtableStub::pd_code_alignment() { + const unsigned int icache_line_size = 32; + return icache_line_size; +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp --- a/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Sun May 21 11:12:48 2017 -0700 @@ -2135,6 +2135,27 @@ __ delayed()->nop(); } + // If the compiler was not able to prove that exact type of the source or the destination + // of the arraycopy is an array type, check at runtime if the source or the destination is + // an instance type. + if (flags & LIR_OpArrayCopy::type_check) { + if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::dst_objarray)) { + __ load_klass(dst, tmp); + __ lduw(tmp, in_bytes(Klass::layout_helper_offset()), tmp2); + __ cmp(tmp2, Klass::_lh_neutral_value); + __ br(Assembler::greaterEqual, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + + if (!(flags & LIR_OpArrayCopy::LIR_OpArrayCopy::src_objarray)) { + __ load_klass(src, tmp); + __ lduw(tmp, in_bytes(Klass::layout_helper_offset()), tmp2); + __ cmp(tmp2, Klass::_lh_neutral_value); + __ br(Assembler::greaterEqual, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + } + } + if (flags & LIR_OpArrayCopy::src_pos_positive_check) { // test src_pos register __ cmp_zero_and_br(Assembler::less, src_pos, *stub->entry()); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/compile_sparc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/sparc/vm/compile_sparc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 SAP AG. 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. + * + */ + +// Name space for methods with platform dependent extensions of compile +// (this is friend of compile). + +#ifndef CPU_SPARC_VM_COMPILE_SPARC_HPP +#define CPU_SPARC_VM_COMPILE_SPARC_HPP + +class Compile; + +class PdCompile { +public: + static void pd_post_matching_hook(Compile* C) { } // empty on sparc +}; + +#endif // CPU_SPARC_VM_COMPILE_SPARC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/frame_sparc.inline.hpp --- a/src/cpu/sparc/vm/frame_sparc.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/sparc/vm/frame_sparc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -235,6 +235,10 @@ inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { return (constantPoolCacheOop*)sp_addr_at( LcpoolCache->sp_offset_in_saved_window()); } + +inline oop* frame::interpreter_frame_temp_oop_addr() const { + return (oop *)(fp() + interpreter_frame_oop_temp_offset); +} #endif // CC_INTERP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/globals_sparc.hpp --- a/src/cpu/sparc/vm/globals_sparc.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/sparc/vm/globals_sparc.hpp Sun May 21 11:12:48 2017 -0700 @@ -82,6 +82,11 @@ #define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ \ + develop(bool, TrapBasedNullChecks, false, \ + "Not supported on this platform.") \ + develop(bool, TrapBasedRangeChecks, false, \ + "Not supported on this platform.") \ + \ product(intx, UseVIS, 99, \ "Highest supported VIS instructions set on Sparc") \ \ diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/methodHandles_sparc.hpp --- a/src/cpu/sparc/vm/methodHandles_sparc.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/sparc/vm/methodHandles_sparc.hpp Sun May 21 11:12:48 2017 -0700 @@ -26,9 +26,9 @@ // These definitions are inlined into class MethodHandles. // Adapters -enum /* platform_dependent_constants */ { - adapter_code_size = NOT_LP64(23000 DEBUG_ONLY(+ 40000)) LP64_ONLY(35000 DEBUG_ONLY(+ 50000)) -}; +static unsigned int adapter_code_size() { + return NOT_LP64(23000 DEBUG_ONLY(+ 40000)) LP64_ONLY(35000 DEBUG_ONLY(+ 50000)); +} // Additional helper methods for MethodHandles code generation: public: diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/sharedRuntime_sparc.cpp --- a/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Sun May 21 11:12:48 2017 -0700 @@ -1122,7 +1122,9 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, + VMRegPair *regs2, int total_args_passed) { + assert(regs2 == NULL, "not needed on sparc"); // Return the number of VMReg stack_slots needed for the args. // This value does not include an abi space (like register window @@ -1261,6 +1263,10 @@ } +// Do we need to convert ints to longs for c calls? +bool SharedRuntime::c_calling_convention_requires_ints_as_longs() { + return false; +} // --------------------------------------------------------------------------- void SharedRuntime::save_native_result(MacroAssembler *masm, BasicType ret_type, int frame_slots) { @@ -2128,7 +2134,7 @@ // the 1st six register arguments). It's weird see int_stk_helper. // int out_arg_slots; - out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); if (is_critical_native) { // Critical natives may have to call out so they need a save area @@ -2875,7 +2881,7 @@ // the 1st six register arguments). It's weird see int_stk_helper. // int out_arg_slots; - out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); // Calculate the total number of stack slots we will need. diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/sparc/vm/sparc.ad Sun May 21 11:12:48 2017 -0700 @@ -1037,6 +1037,11 @@ } } +bool MachConstantBaseNode::requires_late_expand() const { return false; } +void MachConstantBaseNode::lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { Compile* C = ra_->C; Compile::ConstantTable& constant_table = C->constant_table(); @@ -1657,6 +1662,10 @@ //============================================================================= +// Offset from start of compiled java to interpreter stub to the load +// constant that loads the inline cache (IC) (0 on sparc). +const int CompiledStaticCall::comp_to_int_load_offset = 0; + // emit call stub, compiled java to interpretor void emit_java_to_interp(CodeBuffer &cbuf ) { @@ -1939,6 +1948,9 @@ return (VM_Version::is_T4() || VM_Version::is_sparc64()) ? ConditionalMoveLimit : 0; } +// Does the CPU require late expand (see block.cpp for description of late expand)? +const bool Matcher::require_late_expand = false; + // Should the Matcher clone shifts on addressing modes, expecting them to // be subsumed into complex addressing expressions or compute them into // registers? True for Intel but false for most RISCs @@ -3286,7 +3298,7 @@ // C. c_calling_convention %{ // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, length); + (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); %} // Location of native (C/C++) and interpreter return values. This is specified to @@ -9133,7 +9145,7 @@ size(4); ins_cost(BRANCH_COST); format %{ "BA $labl\t! short branch" %} - ins_encode %{ + ins_encode %{ Label* L = $labl$$label; assert(__ use_cbcond(*L), "back to back cbcond"); __ ba_short(*L); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/c1_LIRAssembler_x86.cpp --- a/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Sun May 21 11:12:48 2017 -0700 @@ -3222,6 +3222,23 @@ __ jcc(Assembler::zero, *stub->entry()); } + // If the compiler was not able to prove that exact type of the source or the destination + // of the arraycopy is an array type, check at runtime if the source or the destination is + // an instance type. + if (flags & LIR_OpArrayCopy::type_check) { + if (!(flags & LIR_OpArrayCopy::dst_objarray)) { + __ load_klass(tmp, dst); + __ cmpl(Address(tmp, in_bytes(Klass::layout_helper_offset())), Klass::_lh_neutral_value); + __ jcc(Assembler::greaterEqual, *stub->entry()); + } + + if (!(flags & LIR_OpArrayCopy::src_objarray)) { + __ load_klass(tmp, src); + __ cmpl(Address(tmp, in_bytes(Klass::layout_helper_offset())), Klass::_lh_neutral_value); + __ jcc(Assembler::greaterEqual, *stub->entry()); + } + } + // check if negative if (flags & LIR_OpArrayCopy::src_pos_positive_check) { __ testl(src_pos, src_pos); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/compile_x86.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/x86/vm/compile_x86.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 SAP AG. 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. + * + */ + +// Name space for methods with platform dependent extensions of compile +// (this is friend of compile). + +#ifndef CPU_X86_VM_COMPILE_X86_HPP +#define CPU_X86_VM_COMPILE_X86_HPP + +class Compile; + +class PdCompile { +public: + static void pd_post_matching_hook(Compile* C) { } // empty on x86 +}; + +#endif // CPU_X86_VM_COMPILE_X86_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/frame_x86.inline.hpp --- a/src/cpu/x86/vm/frame_x86.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/frame_x86.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -245,6 +245,10 @@ } } +inline oop* frame::interpreter_frame_temp_oop_addr() const { + return (oop *)(fp() + interpreter_frame_oop_temp_offset); +} + #endif /* CC_INTERP */ inline int frame::pd_oop_map_offset_adjustment() const { diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/globals_x86.hpp --- a/src/cpu/x86/vm/globals_x86.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/globals_x86.hpp Sun May 21 11:12:48 2017 -0700 @@ -87,9 +87,14 @@ product(intx, FenceInstruction, 0, \ "(Unsafe,Unstable) Experimental") \ \ - product(intx, ReadPrefetchInstr, 0, \ + product(intx, ReadPrefetchInstr, 0, \ "Prefetch instruction to prefetch ahead") \ \ + develop(bool, TrapBasedNullChecks, false, \ + "Not supported on this platform.") \ + develop(bool, TrapBasedRangeChecks, false, \ + "Not supported on this platform.") \ + \ product(bool, UseStoreImmI16, true, \ "Use store immediate 16-bits value instruction on x86") \ \ diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/methodHandles_x86.hpp --- a/src/cpu/x86/vm/methodHandles_x86.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/methodHandles_x86.hpp Sun May 21 11:12:48 2017 -0700 @@ -26,9 +26,9 @@ // These definitions are inlined into class MethodHandles. // Adapters -enum /* platform_dependent_constants */ { - adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 25000)) LP64_ONLY(32000 DEBUG_ONLY(+ 150000)) -}; +static unsigned int adapter_code_size() { + return NOT_LP64(16000 DEBUG_ONLY(+ 25000)) LP64_ONLY(32000 DEBUG_ONLY(+ 150000)); +} // Additional helper methods for MethodHandles code generation: public: diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/sharedRuntime_x86_32.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Sun May 21 11:12:48 2017 -0700 @@ -983,7 +983,9 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, + VMRegPair *regs2, int total_args_passed) { + assert(regs2 == NULL, "not needed on x86"); // We return the amount of VMRegImpl stack slots we need to reserve for all // the arguments NOT counting out_preserve_stack_slots. @@ -1020,6 +1022,11 @@ return stack; } +// Do we need to convert ints to longs for c calls? +bool SharedRuntime::c_calling_convention_requires_ints_as_longs() { + return false; +} + // A simple move of integer like type static void simple_move32(MacroAssembler* masm, VMRegPair src, VMRegPair dst) { if (src.first()->is_stack()) { @@ -1629,7 +1636,7 @@ // Now figure out where the args must be stored and how much stack space // they require. int out_arg_slots; - out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); // Compute framesize for the wrapper. We need to handlize all oops in // registers a max of 2 on x86. @@ -2500,7 +2507,7 @@ // they require (neglecting out_preserve_stack_slots). int out_arg_slots; - out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); // Calculate the total number of stack slots we will need. diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/sharedRuntime_x86_64.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Sun May 21 11:12:48 2017 -0700 @@ -895,7 +895,9 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, + VMRegPair *regs2, int total_args_passed) { + assert(regs2 == NULL, "not needed on x86"); // We return the amount of VMRegImpl stack slots we need to reserve for all // the arguments NOT counting out_preserve_stack_slots. @@ -1005,6 +1007,11 @@ return stk_args; } +// Do we need to convert ints to longs for c calls? +bool SharedRuntime::c_calling_convention_requires_ints_as_longs() { + return false; +} + // On 64 bit we will store integer like items to the stack as // 64 bits items (sparc abi) even though java would only store // 32bits for a parameter. On 32bit it will simply be 32 bits @@ -1862,7 +1869,7 @@ // Now figure out where the args must be stored and how much stack space // they require. int out_arg_slots; - out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); // Compute framesize for the wrapper. We need to handlize all oops in // incoming registers @@ -2766,7 +2773,7 @@ // the 1st six register arguments). It's weird see int_stk_helper. int out_arg_slots; - out_arg_slots = c_calling_convention(out_sig_bt, out_regs, total_c_args); + out_arg_slots = c_calling_convention(out_sig_bt, out_regs, NULL, total_c_args); // Calculate the total number of stack slots we will need. diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/stubGenerator_x86_32.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_32.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/stubGenerator_x86_32.cpp Sun May 21 11:12:48 2017 -0700 @@ -94,7 +94,7 @@ private: #ifdef PRODUCT -#define inc_counter_np(counter) (0) +#define inc_counter_np(counter) ((void)0) #else void inc_counter_np_(int& counter) { __ incrementl(ExternalAddress((address)&counter)); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/stubGenerator_x86_64.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_64.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/stubGenerator_x86_64.cpp Sun May 21 11:12:48 2017 -0700 @@ -92,7 +92,7 @@ private: #ifdef PRODUCT -#define inc_counter_np(counter) (0) +#define inc_counter_np(counter) ((void)0) #else void inc_counter_np_(int& counter) { // This can destroy rscratch1 if counter is far from the code cache diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/x86_32.ad Sun May 21 11:12:48 2017 -0700 @@ -487,6 +487,11 @@ return 0; // absolute addressing, no offset } +bool MachConstantBaseNode::requires_late_expand() const { return false; } +void MachConstantBaseNode::lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { // Empty encoding } @@ -1258,6 +1263,10 @@ //============================================================================= +// Offset from start of compiled java to interpreter stub to the load +// constant that loads the inline cache (IC) (0 on i486). +const int CompiledStaticCall::comp_to_int_load_offset = 0; + // emit call stub, compiled java to interpreter void emit_java_to_interp(CodeBuffer &cbuf ) { // Stub is fixed up when the corresponding call is converted from calling @@ -1426,6 +1435,9 @@ // No CMOVF/CMOVD with SSE/SSE2 const int Matcher::float_cmove_cost() { return (UseSSE>=1) ? ConditionalMoveLimit : 0; } +// Does the CPU require late expand (see block.cpp for description of late expand)? +const bool Matcher::require_late_expand = false; + // Should the Matcher clone shifts on addressing modes, expecting them to // be subsumed into complex addressing expressions or compute them into // registers? True for Intel but false for most RISCs @@ -3798,7 +3810,7 @@ // automatically biased by the preserve_stack_slots field above. c_calling_convention %{ // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, length); + (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); %} // Location of C & interpreter return values diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/x86/vm/x86_64.ad Sun May 21 11:12:48 2017 -0700 @@ -686,6 +686,11 @@ return 0; // absolute addressing, no offset } +bool MachConstantBaseNode::requires_late_expand() const { return false; } +void MachConstantBaseNode::lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { // Empty encoding } @@ -1387,6 +1392,10 @@ //============================================================================= +// Offset from start of compiled java to interpreter stub to the load +// constant that loads the inline cache (IC) (0 on amd64). +const int CompiledStaticCall::comp_to_int_load_offset = 0; + // emit call stub, compiled java to interpreter void emit_java_to_interp(CodeBuffer& cbuf) { @@ -1584,6 +1593,9 @@ // No CMOVF/CMOVD with SSE2 const int Matcher::float_cmove_cost() { return ConditionalMoveLimit; } +// Does the CPU require late expand (see block.cpp for description of late expand)? +const bool Matcher::require_late_expand = false; + // Should the Matcher clone shifts on addressing modes, expecting them // to be subsumed into complex addressing expressions or compute them // into registers? True for Intel but false for most RISCs @@ -3009,7 +3021,7 @@ c_calling_convention %{ // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, length); + (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); %} // Location of compiled Java return values. Same as C for now. diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/zero/vm/compile_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/compile_zero.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 SAP AG. 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. + * + */ + +// Name space for methods with platform dependent extensions of compile +// (this is friend of compile). + + +#ifndef CPU_ZERO_VM_COMPILE_ZERO_HPP +#define CPU_ZERO_VM_COMPILE_ZERO_HPP + +class Compile; + +class PdCompile { +public: + static void pd_post_matching_hook(Compile* C) { } // empty on zero +}; + +#endif // CPU_ZERO_VM_COMPILE_ZERO_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/zero/vm/cppInterpreter_zero.cpp --- a/src/cpu/zero/vm/cppInterpreter_zero.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/zero/vm/cppInterpreter_zero.cpp Sun May 21 11:12:48 2017 -0700 @@ -250,7 +250,7 @@ if ((UseCompiler || CountCompiledCalls) && !method->is_synchronized()) { InvocationCounter *counter = method->invocation_counter(); counter->increment(); - if (counter->reached_InvocationLimit()) { + if (counter->reached_InvocationLimit(mcs->backedge_counter())) { CALL_VM_NOCHECK( InterpreterRuntime::frequency_counter_overflow(thread, NULL)); if (HAS_PENDING_EXCEPTION) diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/zero/vm/globals_zero.hpp --- a/src/cpu/zero/vm/globals_zero.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/zero/vm/globals_zero.hpp Sun May 21 11:12:48 2017 -0700 @@ -43,6 +43,7 @@ define_pd_global(intx, CodeEntryAlignment, 32); define_pd_global(intx, OptoLoopAlignment, 16); define_pd_global(intx, InlineFrequencyCount, 100); +define_pd_global(intx, InlineSmallCode, 1000 ); define_pd_global(intx, PreInflateSpin, 10); define_pd_global(intx, StackYellowPages, 2); @@ -61,6 +62,13 @@ // GC Ergo Flags define_pd_global(intx, CMSYoungGenPerWorker, 16*M); // default max size of CMS young gen, per GC worker thread -#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) +// Platform dependent flag handling: flags only defined on this platform. +#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ + \ + develop(bool, TrapBasedNullChecks, false, \ + "Not supported on this platform.") \ + develop(bool, TrapBasedRangeChecks, false, \ + "Not supported on this platform.") \ + #endif // CPU_ZERO_VM_GLOBALS_ZERO_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/zero/vm/methodHandles_zero.hpp --- a/src/cpu/zero/vm/methodHandles_zero.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/zero/vm/methodHandles_zero.hpp Sun May 21 11:12:48 2017 -0700 @@ -25,9 +25,9 @@ // Adapters -enum /* platform_dependent_constants */ { - adapter_code_size = sizeof(ZeroEntry) * (Interpreter::method_handle_invoke_LAST - Interpreter::method_handle_invoke_FIRST + 1) -}; +static unsigned int adapter_code_size() { + return sizeof(ZeroEntry) * (Interpreter::method_handle_invoke_LAST - Interpreter::method_handle_invoke_FIRST + 1); +} private: static oop popFromStack(TRAPS); diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/zero/vm/sharedRuntime_zero.cpp --- a/src/cpu/zero/vm/sharedRuntime_zero.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/zero/vm/sharedRuntime_zero.cpp Sun May 21 11:12:48 2017 -0700 @@ -135,6 +135,12 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, + VMRegPair *regs2, int total_args_passed) { ShouldNotCallThis(); } + +// Do we need to convert ints to longs for c calls? +bool SharedRuntime::c_calling_convention_requires_ints_as_longs() { + ShouldNotCallThis(); +} diff -r 368df6a4b403 -r d982b0f5b59a src/cpu/zero/vm/shark_globals_zero.hpp --- a/src/cpu/zero/vm/shark_globals_zero.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/cpu/zero/vm/shark_globals_zero.hpp Sun May 21 11:12:48 2017 -0700 @@ -50,7 +50,6 @@ define_pd_global(intx, OnStackReplacePercentage, 933 ); define_pd_global(intx, FreqInlineSize, 325 ); -define_pd_global(intx, InlineSmallCode, 1000 ); define_pd_global(intx, NewRatio, 12 ); define_pd_global(intx, NewSizeThreadIncrease, 4*K ); define_pd_global(intx, InitialCodeCacheSize, 160*K); diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/attachListener_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/attachListener_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/os.hpp" +#include "services/attachListener.hpp" +#include "services/dtraceAttacher.hpp" + +#include +#include +#include +#include +#include +#include + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path) +#endif + +// The attach mechanism on Linux uses a UNIX domain socket. An attach listener +// thread is created at startup or is created on-demand via a signal from +// the client tool. The attach listener creates a socket and binds it to a file +// in the filesystem. The attach listener then acts as a simple (single- +// threaded) server - it waits for a client to connect, reads the request, +// executes it, and returns the response to the client via the socket +// connection. +// +// As the socket is a UNIX domain socket it means that only clients on the +// local machine can connect. In addition there are two other aspects to +// the security: +// 1. The well known file that the socket is bound to has permission 400 +// 2. When a client connect, the SO_PEERID socket option is used to +// obtain the credentials of client. We check that the effective uid +// of the client matches this process. + +// forward reference +class AixAttachOperation; + +class AixAttachListener: AllStatic { + private: + // the path to which we bind the UNIX domain socket + static char _path[UNIX_PATH_MAX]; + static bool _has_path; + // Shutdown marker to prevent accept blocking during clean-up. + static bool _shutdown; + + // the file descriptor for the listening socket + static int _listener; + + static void set_path(char* path) { + if (path == NULL) { + _has_path = false; + } else { + strncpy(_path, path, UNIX_PATH_MAX); + _path[UNIX_PATH_MAX-1] = '\0'; + _has_path = true; + } + } + + static void set_listener(int s) { _listener = s; } + + // reads a request from the given connected socket + static AixAttachOperation* read_request(int s); + + public: + enum { + ATTACH_PROTOCOL_VER = 1 // protocol version + }; + enum { + ATTACH_ERROR_BADVERSION = 101 // error codes + }; + + // initialize the listener, returns 0 if okay + static int init(); + + static char* path() { return _path; } + static bool has_path() { return _has_path; } + static int listener() { return _listener; } + // Shutdown marker to prevent accept blocking during clean-up + static void set_shutdown(bool shutdown) { _shutdown = shutdown; } + static bool is_shutdown() { return _shutdown; } + + // write the given buffer to a socket + static int write_fully(int s, char* buf, int len); + + static AixAttachOperation* dequeue(); +}; + +class AixAttachOperation: public AttachOperation { + private: + // the connection to the client + int _socket; + + public: + void complete(jint res, bufferedStream* st); + + void set_socket(int s) { _socket = s; } + int socket() const { return _socket; } + + AixAttachOperation(char* name) : AttachOperation(name) { + set_socket(-1); + } +}; + +// statics +char AixAttachListener::_path[UNIX_PATH_MAX]; +bool AixAttachListener::_has_path; +int AixAttachListener::_listener = -1; +// Shutdown marker to prevent accept blocking during clean-up +bool AixAttachListener::_shutdown = false; + +// Supporting class to help split a buffer into individual components +class ArgumentIterator : public StackObj { + private: + char* _pos; + char* _end; + public: + ArgumentIterator(char* arg_buffer, size_t arg_size) { + _pos = arg_buffer; + _end = _pos + arg_size - 1; + } + char* next() { + if (*_pos == '\0') { + return NULL; + } + char* res = _pos; + char* next_pos = strchr(_pos, '\0'); + if (next_pos < _end) { + next_pos++; + } + _pos = next_pos; + return res; + } +}; + +// On AIX if sockets block until all data has been transmitted +// successfully in some communication domains a socket "close" may +// never complete. We have to take care that after the socket shutdown +// the listener never enters accept state. + +// atexit hook to stop listener and unlink the file that it is +// bound too. + +// Some modifications to the listener logic to prevent deadlocks on exit. +// 1. We Shutdown the socket here instead. AixAttachOperation::complete() is not the right place +// since more than one agent in a sequence in JPLIS live tests wouldn't work (Listener thread +// would be dead after the first operation completion). +// 2. close(s) may never return if the listener thread is in socket accept(). Unlinking the file +// should be sufficient for cleanup. +extern "C" { + static void listener_cleanup() { + static int cleanup_done; + if (!cleanup_done) { + cleanup_done = 1; + AixAttachListener::set_shutdown(true); + int s = AixAttachListener::listener(); + if (s != -1) { + ::shutdown(s, 2); + } + if (AixAttachListener::has_path()) { + ::unlink(AixAttachListener::path()); + } + } + } +} + +// Initialization - create a listener socket and bind it to a file + +int AixAttachListener::init() { + char path[UNIX_PATH_MAX]; // socket file + char initial_path[UNIX_PATH_MAX]; // socket file during setup + int listener; // listener socket (file descriptor) + + // register function to cleanup + ::atexit(listener_cleanup); + + int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d", + os::get_temp_directory(), os::current_process_id()); + if (n < (int)UNIX_PATH_MAX) { + n = snprintf(initial_path, UNIX_PATH_MAX, "%s.tmp", path); + } + if (n >= (int)UNIX_PATH_MAX) { + return -1; + } + + // create the listener socket + listener = ::socket(PF_UNIX, SOCK_STREAM, 0); + if (listener == -1) { + return -1; + } + + // bind socket + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, initial_path); + ::unlink(initial_path); + // We must call bind with the actual socketaddr length. This is obligatory for AS400. + int res = ::bind(listener, (struct sockaddr*)&addr, SUN_LEN(&addr)); + if (res == -1) { + RESTARTABLE(::close(listener), res); + return -1; + } + + // put in listen mode, set permissions, and rename into place + res = ::listen(listener, 5); + if (res == 0) { + RESTARTABLE(::chmod(initial_path, (S_IREAD|S_IWRITE) & ~(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)), res); + if (res == 0) { + res = ::rename(initial_path, path); + } + } + if (res == -1) { + RESTARTABLE(::close(listener), res); + ::unlink(initial_path); + return -1; + } + set_path(path); + set_listener(listener); + set_shutdown(false); + + return 0; +} + +// Given a socket that is connected to a peer we read the request and +// create an AttachOperation. As the socket is blocking there is potential +// for a denial-of-service if the peer does not response. However this happens +// after the peer credentials have been checked and in the worst case it just +// means that the attach listener thread is blocked. +// +AixAttachOperation* AixAttachListener::read_request(int s) { + char ver_str[8]; + sprintf(ver_str, "%d", ATTACH_PROTOCOL_VER); + + // The request is a sequence of strings so we first figure out the + // expected count and the maximum possible length of the request. + // The request is: + // 00000 + // where is the protocol version (1), is the command + // name ("load", "datadump", ...), and is an argument + int expected_str_count = 2 + AttachOperation::arg_count_max; + const int max_len = (sizeof(ver_str) + 1) + (AttachOperation::name_length_max + 1) + + AttachOperation::arg_count_max*(AttachOperation::arg_length_max + 1); + + char buf[max_len]; + int str_count = 0; + + // Read until all (expected) strings have been read, the buffer is + // full, or EOF. + + int off = 0; + int left = max_len; + + do { + int n; + // Don't block on interrupts because this will + // hang in the clean-up when shutting down. + n = read(s, buf+off, left); + if (n == -1) { + return NULL; // reset by peer or other error + } + if (n == 0) { // end of file reached + break; + } + for (int i=0; i so check it now to + // check for protocol mis-match + if (str_count == 1) { + if ((strlen(buf) != strlen(ver_str)) || + (atoi(buf) != ATTACH_PROTOCOL_VER)) { + char msg[32]; + sprintf(msg, "%d\n", ATTACH_ERROR_BADVERSION); + write_fully(s, msg, strlen(msg)); + return NULL; + } + } + } + } + off += n; + left -= n; + } while (left > 0 && str_count < expected_str_count); + + if (str_count != expected_str_count) { + return NULL; // incomplete request + } + + // parse request + + ArgumentIterator args(buf, (max_len)-left); + + // version already checked + char* v = args.next(); + + char* name = args.next(); + if (name == NULL || strlen(name) > AttachOperation::name_length_max) { + return NULL; + } + + AixAttachOperation* op = new AixAttachOperation(name); + + for (int i=0; iset_arg(i, NULL); + } else { + if (strlen(arg) > AttachOperation::arg_length_max) { + delete op; + return NULL; + } + op->set_arg(i, arg); + } + } + + op->set_socket(s); + return op; +} + + +// Dequeue an operation +// +// In the Linux implementation there is only a single operation and clients +// cannot queue commands (except at the socket level). +// +AixAttachOperation* AixAttachListener::dequeue() { + for (;;) { + int s; + + // wait for client to connect + struct sockaddr addr; + socklen_t len = sizeof(addr); + memset(&addr, 0, len); + // We must prevent accept blocking on the socket if it has been shut down. + // Therefore we allow interrups and check whether we have been shut down already. + if (AixAttachListener::is_shutdown()) { + return NULL; + } + s=::accept(listener(), &addr, &len); + if (s == -1) { + return NULL; // log a warning? + } + + // Added timeouts for read and write. If we get no request within the + // next AttachListenerTimeout milliseconds we just finish the connection. + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = AttachListenerTimeout * 1000; + ::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv)); + ::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv)); + + // get the credentials of the peer and check the effective uid/guid + // - check with jeff on this. + struct peercred_struct cred_info; + socklen_t optlen = sizeof(cred_info); + if (::getsockopt(s, SOL_SOCKET, SO_PEERID, (void*)&cred_info, &optlen) == -1) { + int res; + RESTARTABLE(::close(s), res); + continue; + } + uid_t euid = geteuid(); + gid_t egid = getegid(); + + if (cred_info.euid != euid || cred_info.egid != egid) { + int res; + RESTARTABLE(::close(s), res); + continue; + } + + // peer credential look okay so we read the request + AixAttachOperation* op = read_request(s); + if (op == NULL) { + int res; + RESTARTABLE(::close(s), res); + continue; + } else { + return op; + } + } +} + +// write the given buffer to the socket +int AixAttachListener::write_fully(int s, char* buf, int len) { + do { + int n = ::write(s, buf, len); + if (n == -1) { + if (errno != EINTR) return -1; + } else { + buf += n; + len -= n; + } + } + while (len > 0); + return 0; +} + +// Complete an operation by sending the operation result and any result +// output to the client. At this time the socket is in blocking mode so +// potentially we can block if there is a lot of data and the client is +// non-responsive. For most operations this is a non-issue because the +// default send buffer is sufficient to buffer everything. In the future +// if there are operations that involves a very big reply then it the +// socket could be made non-blocking and a timeout could be used. + +void AixAttachOperation::complete(jint result, bufferedStream* st) { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + // write operation result + char msg[32]; + sprintf(msg, "%d\n", result); + int rc = AixAttachListener::write_fully(this->socket(), msg, strlen(msg)); + + // write any result data + if (rc == 0) { + // Shutdown the socket in the cleanup function to enable more than + // one agent attach in a sequence (see comments to listener_cleanup()). + AixAttachListener::write_fully(this->socket(), (char*) st->base(), st->size()); + } + + // done + RESTARTABLE(::close(this->socket()), rc); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + delete this; +} + + +// AttachListener functions + +AttachOperation* AttachListener::dequeue() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + AttachOperation* op = AixAttachListener::dequeue(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return op; +} + +// Performs initialization at vm startup. +// For AIX we remove any stale .java_pid file which could cause +// an attaching process to think we are ready to receive on the +// domain socket before we are properly initialized. + +void AttachListener::vm_start() { + char fn[UNIX_PATH_MAX]; + struct stat64 st; + int ret; + + int n = snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", + os::get_temp_directory(), os::current_process_id()); + assert(n < (int)UNIX_PATH_MAX, "java_pid file name buffer overflow"); + + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == 0) { + ret = ::unlink(fn); + if (ret == -1) { + debug_only(warning("failed to remove stale attach pid file at %s", fn)); + } + } +} + +int AttachListener::pd_init() { + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or + // java_suspend_self() via check_and_wait_while_suspended() + + int ret_code = AixAttachListener::init(); + + // were we externally suspended while we were waiting? + thread->check_and_wait_while_suspended(); + + return ret_code; +} + +// Attach Listener is started lazily except in the case when +// +ReduseSignalUsage is used +bool AttachListener::init_at_startup() { + if (ReduceSignalUsage) { + return true; + } else { + return false; + } +} + +// If the file .attach_pid exists in the working directory +// or /tmp then this is the trigger to start the attach mechanism +bool AttachListener::is_init_trigger() { + if (init_at_startup() || is_initialized()) { + return false; // initialized at startup or already initialized + } + char fn[PATH_MAX+1]; + sprintf(fn, ".attach_pid%d", os::current_process_id()); + int ret; + struct stat64 st; + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == -1) { + snprintf(fn, sizeof(fn), "%s/.attach_pid%d", + os::get_temp_directory(), os::current_process_id()); + RESTARTABLE(::stat64(fn, &st), ret); + } + if (ret == 0) { + // simple check to avoid starting the attach mechanism when + // a bogus user creates the file + if (st.st_uid == geteuid()) { + init(); + return true; + } + } + return false; +} + +// if VM aborts then remove listener +void AttachListener::abort() { + listener_cleanup(); +} + +void AttachListener::pd_data_dump() { + os::signal_notify(SIGQUIT); +} + +AttachOperationFunctionInfo* AttachListener::pd_find_operation(const char* n) { + return NULL; +} + +jint AttachListener::pd_set_flag(AttachOperation* op, outputStream* out) { + out->print_cr("flag '%s' cannot be changed", op->arg(0)); + return JNI_ERR; +} + +void AttachListener::pd_detachall() { + // Cleanup server socket to detach clients. + listener_cleanup(); +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/c2_globals_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/c2_globals_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_AIX_VM_C2_GLOBALS_AIX_HPP +#define OS_AIX_VM_C2_GLOBALS_AIX_HPP + +#include "utilities/globalDefinitions.hpp" +#include "utilities/macros.hpp" + +// +// Sets the default values for operating system dependent flags used by the +// server compiler. (see c2_globals.hpp) +// + +#endif // OS_AIX_VM_C2_GLOBALS_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/chaitin_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/chaitin_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "opto/chaitin.hpp" +#include "opto/machnode.hpp" + +void PhaseRegAlloc::pd_preallocate_hook() { + // no action +} + +#ifdef ASSERT +void PhaseRegAlloc::pd_postallocate_verify_hook() { + // no action +} +#endif diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/decoder_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/decoder_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 SAP AG. 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. + * + */ + +#include "utilities/decoder.hpp" +#include "porting_aix.hpp" + +// Provide simple AIXDecoder which enables decoding of C frames in VM. +class AIXDecoder: public AbstractDecoder { + public: + AIXDecoder() { + _decoder_status = no_error; + } + ~AIXDecoder() {} + + virtual bool can_decode_C_frame_in_vm() const { return true; } + + virtual bool demangle(const char* symbol, char* buf, int buflen) { return false; } // demangled by getFuncName + + virtual bool decode(address addr, char* buf, int buflen, int* offset, const char* modulepath) { + return (::getFuncName((codeptr_t)addr, buf, buflen, offset, 0, 0, 0) == 0); + } + virtual bool decode(address addr, char *buf, int buflen, int* offset, const void *base) { + ShouldNotReachHere(); + return false; + } +}; diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/globals_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/globals_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_AIX_VM_GLOBALS_AIX_HPP +#define OS_AIX_VM_GLOBALS_AIX_HPP + +// +// Defines Aix specific flags. They are not available on other platforms. +// +#define RUNTIME_OS_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ + \ + /* If UseLargePages == true allow or deny usage of 16M pages. 16M pages are */ \ + /* a scarce resource and there may be situations where we do not want the VM */ \ + /* to run with 16M pages. (Will fall back to 64K pages). */ \ + product_pd(bool, Use16MPages, \ + "Use 16M pages if available.") \ + \ + /* use optimized addresses for the polling page, */ \ + /* e.g. map it to a special 32-bit address. */ \ + product_pd(bool, OptimizePollingPageLocation, \ + "Optimize the location of the polling page used for Safepoints") \ + \ + product_pd(intx, AttachListenerTimeout, \ + "Timeout in ms the attach listener waits for a request") \ + \ + +// Per default, do not allow 16M pages. 16M pages have to be switched on specifically. +define_pd_global(bool, Use16MPages, false); +define_pd_global(bool, OptimizePollingPageLocation, true); +define_pd_global(intx, AttachListenerTimeout, 1000); + +// +// Defines Aix-specific default values. The flags are available on all +// platforms, but they may have different default values on other platforms. +// +define_pd_global(bool, UseLargePages, true); +define_pd_global(bool, UseLargePagesIndividualAllocation, false); +define_pd_global(bool, UseOSErrorReporting, false); +define_pd_global(bool, UseThreadPriorities, true) ; + +#endif // OS_AIX_VM_GLOBALS_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/interfaceSupport_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/interfaceSupport_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_LINUX_VM_INTERFACESUPPORT_LINUX_HPP +#define OS_LINUX_VM_INTERFACESUPPORT_LINUX_HPP + +// Contains inlined functions for class InterfaceSupport + +static inline void serialize_memory(JavaThread *thread) { + os::write_memory_serialize_page(thread); +} + +#endif // OS_LINUX_VM_INTERFACESUPPORT_LINUX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/jsig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/jsig.c Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +/* CopyrightVersion 1.2 */ + +/* This is a special library that should be loaded before libc & + * libthread to interpose the signal handler installation functions: + * sigaction(), signal(), sigset(). + * Used for signal-chaining. See RFE 4381843. + */ + +#include +#include +#include +#include +#include + +#define bool int +#define true 1 +#define false 0 + +// Highest so far on AIX 5.2 is SIGSAK (63) +#define MAXSIGNUM 63 +#define MASK(sig) ((unsigned int)1 << sig) + +static struct sigaction sact[MAXSIGNUM]; /* saved signal handlers */ +static unsigned int jvmsigs = 0; /* signals used by jvm */ + +/* used to synchronize the installation of signal handlers */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_t tid = 0; + +typedef void (*sa_handler_t)(int); +typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +// signal_t is already defined on AIX +typedef sa_handler_t (*signal_like_function_t)(int, sa_handler_t); +typedef int (*sigaction_t)(int, const struct sigaction *, struct sigaction *); + +static signal_like_function_t os_signal = 0; /* os's version of signal()/sigset() */ +static sigaction_t os_sigaction = 0; /* os's version of sigaction() */ + +static bool jvm_signal_installing = false; +static bool jvm_signal_installed = false; + +static void signal_lock() { + pthread_mutex_lock(&mutex); + /* When the jvm is installing its set of signal handlers, threads + * other than the jvm thread should wait */ + if (jvm_signal_installing) { + if (tid != pthread_self()) { + pthread_cond_wait(&cond, &mutex); + } + } +} + +static void signal_unlock() { + pthread_mutex_unlock(&mutex); +} + +static sa_handler_t call_os_signal(int sig, sa_handler_t disp, + bool is_sigset) { + if (os_signal == NULL) { + if (!is_sigset) { + // Aix: call functions directly instead of dlsym'ing them + os_signal = signal; + } else { + // Aix: call functions directly instead of dlsym'ing them + os_signal = sigset; + } + if (os_signal == NULL) { + printf("%s\n", dlerror()); + exit(0); + } + } + return (*os_signal)(sig, disp); +} + +static void save_signal_handler(int sig, sa_handler_t disp) { + sigset_t set; + sact[sig].sa_handler = disp; + sigemptyset(&set); + sact[sig].sa_mask = set; + sact[sig].sa_flags = 0; +} + +static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) { + sa_handler_t oldhandler; + bool sigused; + + signal_lock(); + + sigused = (MASK(sig) & jvmsigs) != 0; + if (jvm_signal_installed && sigused) { + /* jvm has installed its signal handler for this signal. */ + /* Save the handler. Don't really install it. */ + oldhandler = sact[sig].sa_handler; + save_signal_handler(sig, disp); + + signal_unlock(); + return oldhandler; + } else if (jvm_signal_installing) { + /* jvm is installing its signal handlers. Install the new + * handlers and save the old ones. jvm uses sigaction(). + * Leave the piece here just in case. */ + oldhandler = call_os_signal(sig, disp, is_sigset); + save_signal_handler(sig, oldhandler); + + /* Record the signals used by jvm */ + jvmsigs |= MASK(sig); + + signal_unlock(); + return oldhandler; + } else { + /* jvm has no relation with this signal (yet). Install the + * the handler. */ + oldhandler = call_os_signal(sig, disp, is_sigset); + + signal_unlock(); + return oldhandler; + } +} + +sa_handler_t signal(int sig, sa_handler_t disp) { + return set_signal(sig, disp, false); +} + +sa_handler_t sigset(int sig, sa_handler_t disp) { + return set_signal(sig, disp, true); + } + +static int call_os_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact) { + if (os_sigaction == NULL) { + // Aix: call functions directly instead of dlsym'ing them + os_sigaction = sigaction; + if (os_sigaction == NULL) { + printf("%s\n", dlerror()); + exit(0); + } + } + return (*os_sigaction)(sig, act, oact); +} + +int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { + int res; + bool sigused; + struct sigaction oldAct; + + signal_lock(); + + sigused = (MASK(sig) & jvmsigs) != 0; + if (jvm_signal_installed && sigused) { + /* jvm has installed its signal handler for this signal. */ + /* Save the handler. Don't really install it. */ + if (oact != NULL) { + *oact = sact[sig]; + } + if (act != NULL) { + sact[sig] = *act; + } + + signal_unlock(); + return 0; + } else if (jvm_signal_installing) { + /* jvm is installing its signal handlers. Install the new + * handlers and save the old ones. */ + res = call_os_sigaction(sig, act, &oldAct); + sact[sig] = oldAct; + if (oact != NULL) { + *oact = oldAct; + } + + /* Record the signals used by jvm */ + jvmsigs |= MASK(sig); + + signal_unlock(); + return res; + } else { + /* jvm has no relation with this signal (yet). Install the + * the handler. */ + res = call_os_sigaction(sig, act, oact); + + signal_unlock(); + return res; + } +} + +/* The three functions for the jvm to call into */ +void JVM_begin_signal_setting() { + signal_lock(); + jvm_signal_installing = true; + tid = pthread_self(); + signal_unlock(); +} + +void JVM_end_signal_setting() { + signal_lock(); + jvm_signal_installed = true; + jvm_signal_installing = false; + pthread_cond_broadcast(&cond); + signal_unlock(); +} + +struct sigaction *JVM_get_signal_action(int sig) { + /* Does race condition make sense here? */ + if ((MASK(sig) & jvmsigs) != 0) { + return &sact[sig]; + } + return NULL; +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/jvm_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/jvm_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "prims/jvm.h" +#include "runtime/interfaceSupport.hpp" +#include "runtime/osThread.hpp" + +#include + + +// sun.misc.Signal /////////////////////////////////////////////////////////// +// Signal code is mostly copied from classic vm, signals_md.c 1.4 98/08/23 +/* + * This function is included primarily as a debugging aid. If Java is + * running in a console window, then pressing will cause + * the current state of all active threads and monitors to be written + * to the console window. + */ + +JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler)) + // Copied from classic vm + // signals_md.c 1.4 98/08/23 + void* newHandler = handler == (void *)2 + ? os::user_handler() + : handler; + switch (sig) { + /* The following are already used by the VM. */ + case INTERRUPT_SIGNAL: + case SIGFPE: + case SIGILL: + case SIGSEGV: + + /* The following signal is used by the VM to dump thread stacks unless + ReduceSignalUsage is set, in which case the user is allowed to set + his own _native_ handler for this signal; thus, in either case, + we do not allow JVM_RegisterSignal to change the handler. */ + case BREAK_SIGNAL: + return (void *)-1; + + /* The following signals are used for Shutdown Hooks support. However, if + ReduceSignalUsage (-Xrs) is set, Shutdown Hooks must be invoked via + System.exit(), Java is not allowed to use these signals, and the the + user is allowed to set his own _native_ handler for these signals and + invoke System.exit() as needed. Terminator.setup() is avoiding + registration of these signals when -Xrs is present. + - If the HUP signal is ignored (from the nohup) command, then Java + is not allowed to use this signal. + */ + + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + case SHUTDOWN3_SIGNAL: + if (ReduceSignalUsage) return (void*)-1; + if (os::Aix::is_sig_ignored(sig)) return (void*)1; + } + + void* oldHandler = os::signal(sig, newHandler); + if (oldHandler == os::user_handler()) { + return (void *)2; + } else { + return oldHandler; + } +JVM_END + + +JVM_ENTRY_NO_ENV(jboolean, JVM_RaiseSignal(jint sig)) + if (ReduceSignalUsage) { + // do not allow SHUTDOWN1_SIGNAL,SHUTDOWN2_SIGNAL,SHUTDOWN3_SIGNAL, + // BREAK_SIGNAL to be raised when ReduceSignalUsage is set, since + // no handler for them is actually registered in JVM or via + // JVM_RegisterSignal. + if (sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SHUTDOWN3_SIGNAL || sig == BREAK_SIGNAL) { + return JNI_FALSE; + } + } + else if ((sig == SHUTDOWN1_SIGNAL || sig == SHUTDOWN2_SIGNAL || + sig == SHUTDOWN3_SIGNAL) && os::Aix::is_sig_ignored(sig)) { + // do not allow SHUTDOWN1_SIGNAL to be raised when SHUTDOWN1_SIGNAL + // is ignored, since no handler for them is actually registered in JVM + // or via JVM_RegisterSignal. + // This also applies for SHUTDOWN2_SIGNAL and SHUTDOWN3_SIGNAL + return JNI_FALSE; + } + + os::signal_raise(sig); + return JNI_TRUE; +JVM_END + +/* + All the defined signal names for Linux. + + NOTE that not all of these names are accepted by our Java implementation + + Via an existing claim by the VM, sigaction restrictions, or + the "rules of Unix" some of these names will be rejected at runtime. + For example the VM sets up to handle USR1, sigaction returns EINVAL for + STOP, and Linux simply doesn't allow catching of KILL. + + Here are the names currently accepted by a user of sun.misc.Signal with + 1.4.1 (ignoring potential interaction with use of chaining, etc): + + HUP, INT, TRAP, ABRT, IOT, BUS, USR2, PIPE, ALRM, TERM, STKFLT, + CLD, CHLD, CONT, TSTP, TTIN, TTOU, URG, XCPU, XFSZ, VTALRM, PROF, + WINCH, POLL, IO, PWR, SYS + +*/ + +struct siglabel { + const char *name; + int number; +}; + +struct siglabel siglabels[] = { + /* derived from /usr/include/bits/signum.h on RH7.2 */ + "HUP", SIGHUP, /* Hangup (POSIX). */ + "INT", SIGINT, /* Interrupt (ANSI). */ + "QUIT", SIGQUIT, /* Quit (POSIX). */ + "ILL", SIGILL, /* Illegal instruction (ANSI). */ + "TRAP", SIGTRAP, /* Trace trap (POSIX). */ + "ABRT", SIGABRT, /* Abort (ANSI). */ + "IOT", SIGIOT, /* IOT trap (4.2 BSD). */ + "BUS", SIGBUS, /* BUS error (4.2 BSD). */ + "FPE", SIGFPE, /* Floating-point exception (ANSI). */ + "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ + "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ + "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ + "USR2", SIGUSR2, /* User-defined signal 2 (POSIX). */ + "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ + "ALRM", SIGALRM, /* Alarm clock (POSIX). */ + "TERM", SIGTERM, /* Termination (ANSI). */ +#ifdef SIGSTKFLT + "STKFLT", SIGSTKFLT, /* Stack fault. */ +#endif + "CLD", SIGCLD, /* Same as SIGCHLD (System V). */ + "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ + "CONT", SIGCONT, /* Continue (POSIX). */ + "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ + "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ + "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ + "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ + "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ + "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ + "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ + "DANGER", SIGDANGER, /* System crash imminent; free up some page space (AIX). */ + "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ + "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ + "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ + "POLL", SIGPOLL, /* Pollable event occurred (System V). */ + "IO", SIGIO, /* I/O now possible (4.2 BSD). */ + "PWR", SIGPWR, /* Power failure restart (System V). */ +#ifdef SIGSYS + "SYS", SIGSYS /* Bad system call. Only on some Linuxen! */ +#endif + }; + +JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name)) + + /* find and return the named signal's number */ + + for(uint i=0; i /* For DIR */ + +// Must redefine NULL because the macro gets redefined to int 0 +// by dirent.h. This redefinition is included later then the standard definition in +// globalDefinitions_.hpp and leads to assertions in the VM initialization. +// We definitely need NULL to have the same lengh as an address pointer. +#ifdef _LP64 +#undef NULL +#define NULL 0L +#else +#ifndef NULL +#define NULL 0 +#endif +#endif + +#include /* For MAXPATHLEN */ +#include /* For socklen_t */ +#include /* For F_OK, R_OK, W_OK */ + +#define JNI_ONLOAD_SYMBOLS {"JNI_OnLoad"} +#define JNI_ONUNLOAD_SYMBOLS {"JNI_OnUnload"} +#define JVM_ONLOAD_SYMBOLS {"JVM_OnLoad"} +#define AGENT_ONLOAD_SYMBOLS {"Agent_OnLoad"} +#define AGENT_ONUNLOAD_SYMBOLS {"Agent_OnUnload"} +#define AGENT_ONATTACH_SYMBOLS {"Agent_OnAttach"} + +#define JNI_LIB_PREFIX "lib" +#define JNI_LIB_SUFFIX ".so" + +// Hack: MAXPATHLEN is 4095 on some Linux and 4096 on others. This may +// cause problems if JVM and the rest of JDK are built on different +// Linux releases. Here we define JVM_MAXPATHLEN to be MAXPATHLEN + 1, +// so buffers declared in VM are always >= 4096. +#define JVM_MAXPATHLEN MAXPATHLEN + 1 + +#define JVM_R_OK R_OK +#define JVM_W_OK W_OK +#define JVM_X_OK X_OK +#define JVM_F_OK F_OK + +/* + * File I/O + */ + +#include +#include +#include +#include + +/* O Flags */ + +#define JVM_O_RDONLY O_RDONLY +#define JVM_O_WRONLY O_WRONLY +#define JVM_O_RDWR O_RDWR +#define JVM_O_O_APPEND O_APPEND +#define JVM_O_EXCL O_EXCL +#define JVM_O_CREAT O_CREAT + +/* Signal definitions */ + +#define BREAK_SIGNAL SIGQUIT /* Thread dumping support. */ +#define INTERRUPT_SIGNAL SIGUSR1 /* Interruptible I/O support. */ +#define SHUTDOWN1_SIGNAL SIGHUP /* Shutdown Hooks support. */ +#define SHUTDOWN2_SIGNAL SIGINT +#define SHUTDOWN3_SIGNAL SIGTERM + +#endif /* JVM_MD_H */ + +#endif // OS_AIX_VM_JVM_AIX_H diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/libperfstat_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/libperfstat_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,124 @@ +/* + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "runtime/arguments.hpp" +#include "libperfstat_aix.hpp" + +// For dlopen and friends +#include + +// handle to the libperfstat +static void* g_libhandle = NULL; + +// whether initialization worked +static bool g_initialized = false; + + +typedef int (*fun_perfstat_cpu_total_t) (perfstat_id_t *name, perfstat_cpu_total_t* userbuff, + int sizeof_userbuff, int desired_number); + +typedef int (*fun_perfstat_memory_total_t) (perfstat_id_t *name, perfstat_memory_total_t* userbuff, + int sizeof_userbuff, int desired_number); + +typedef void (*fun_perfstat_reset_t) (); + +static fun_perfstat_cpu_total_t g_fun_perfstat_cpu_total = NULL; +static fun_perfstat_memory_total_t g_fun_perfstat_memory_total = NULL; +static fun_perfstat_reset_t g_fun_perfstat_reset = NULL; + +bool libperfstat::init() { + + if (g_initialized) { + return true; + } + + g_initialized = false; + + // dynamically load the libperfstat porting library. + g_libhandle = dlopen("/usr/lib/libperfstat.a(shr_64.o)", RTLD_MEMBER | RTLD_NOW); + if (!g_libhandle) { + if (Verbose) { + fprintf(stderr, "Cannot load libperfstat.a (dlerror: %s)", dlerror()); + } + return false; + } + + // resolve function pointers + +#define RESOLVE_FUN_NO_ERROR(name) \ + g_fun_##name = (fun_##name##_t) dlsym(g_libhandle, #name); + +#define RESOLVE_FUN(name) \ + RESOLVE_FUN_NO_ERROR(name) \ + if (!g_fun_##name) { \ + if (Verbose) { \ + fprintf(stderr, "Cannot resolve " #name "() from libperfstat.a\n" \ + " (dlerror: %s)", dlerror()); \ + } \ + return false; \ + } + + RESOLVE_FUN(perfstat_cpu_total); + RESOLVE_FUN(perfstat_memory_total); + RESOLVE_FUN(perfstat_reset); + + g_initialized = true; + + return true; +} + +void libperfstat::cleanup() { + + g_initialized = false; + + if (g_libhandle) { + dlclose(g_libhandle); + g_libhandle = NULL; + } + + g_fun_perfstat_cpu_total = NULL; + g_fun_perfstat_memory_total = NULL; + g_fun_perfstat_reset = NULL; +} + +int libperfstat::perfstat_memory_total(perfstat_id_t *name, + perfstat_memory_total_t* userbuff, + int sizeof_userbuff, int desired_number) { + assert(g_initialized, "libperfstat not initialized"); + assert(g_fun_perfstat_memory_total, ""); + return g_fun_perfstat_memory_total(name, userbuff, sizeof_userbuff, desired_number); +} + +int libperfstat::perfstat_cpu_total(perfstat_id_t *name, perfstat_cpu_total_t* userbuff, + int sizeof_userbuff, int desired_number) { + assert(g_initialized, "libperfstat not initialized"); + assert(g_fun_perfstat_cpu_total, ""); + return g_fun_perfstat_cpu_total(name, userbuff, sizeof_userbuff, desired_number); +} + +void libperfstat::perfstat_reset() { + assert(g_initialized, "libperfstat not initialized"); + assert(g_fun_perfstat_reset, ""); + g_fun_perfstat_reset(); +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/libperfstat_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/libperfstat_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// encapsulates the libperfstat library. +// +// The purpose of this code is to dynamically load the libperfstat library +// instead of statically linking against it. The libperfstat library is an +// AIX-specific library which only exists on AIX, not on PASE. If I want to +// share binaries between AIX and PASE, I cannot directly link against libperfstat.so. + +#ifndef OS_AIX_VM_LIBPERFSTAT_AIX_HPP +#define OS_AIX_VM_LIBPERFSTAT_AIX_HPP + +#include + +class libperfstat { + +public: + + // Load the libperfstat library (must be in LIBPATH). + // Returns true if succeeded, false if error. + static bool init(); + + // cleanup of the libo4 porting library. + static void cleanup(); + + // direct wrappers for the libperfstat functionality. All they do is + // to call the functions with the same name via function pointers. + static int perfstat_cpu_total(perfstat_id_t *name, perfstat_cpu_total_t* userbuff, + int sizeof_userbuff, int desired_number); + + static int perfstat_memory_total(perfstat_id_t *name, perfstat_memory_total_t* userbuff, + int sizeof_userbuff, int desired_number); + + static void perfstat_reset(); +}; + +#endif // OS_AIX_VM_LIBPERFSTAT_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/loadlib_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/loadlib_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,185 @@ +/* + * Copyright 2012, 2013 SAP AG. 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. + * + */ + + +// Implementation of LoadedLibraries and friends + +// Ultimately this just uses loadquery() +// See: +// http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp +// ?topic=/com.ibm.aix.basetechref/doc/basetrf1/loadquery.htm + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +// 'allocation.inline.hpp' triggers the inclusion of 'inttypes.h' which defines macros +// required by the definitions in 'globalDefinitions.hpp'. But these macros in 'inttypes.h' +// are only defined if '__STDC_FORMAT_MACROS' is defined! +#include "memory/allocation.inline.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/threadCritical.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" +#include "loadlib_aix.hpp" +#include "porting_aix.hpp" + +// For loadquery() +#include + +/////////////////////////////////////////////////////////////////////////////// +// Implementation for LoadedLibraryModule + +// output debug info +void LoadedLibraryModule::print(outputStream* os) const { + os->print("%15.15s: text: " INTPTR_FORMAT " - " INTPTR_FORMAT + ", data: " INTPTR_FORMAT " - " INTPTR_FORMAT " ", + shortname, text_from, text_to, data_from, data_to); + os->print(" %s", fullpath); + if (strlen(membername) > 0) { + os->print("(%s)", membername); + } + os->cr(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Implementation for LoadedLibraries + +// class variables +LoadedLibraryModule LoadedLibraries::tab[MAX_MODULES]; +int LoadedLibraries::num_loaded = 0; + +// Checks whether the address p points to any of the loaded code segments. +// If it does, returns the LoadedLibraryModule entry. If not, returns NULL. +// static +const LoadedLibraryModule* LoadedLibraries::find_for_text_address(const unsigned char* p) { + + if (num_loaded == 0) { + reload(); + } + for (int i = 0; i < num_loaded; i++) { + if (tab[i].is_in_text(p)) { + return &tab[i]; + } + } + return NULL; +} + +// Checks whether the address p points to any of the loaded data segments. +// If it does, returns the LoadedLibraryModule entry. If not, returns NULL. +// static +const LoadedLibraryModule* LoadedLibraries::find_for_data_address(const unsigned char* p) { + if (num_loaded == 0) { + reload(); + } + for (int i = 0; i < num_loaded; i++) { + if (tab[i].is_in_data(p)) { + return &tab[i]; + } + } + return NULL; +} + +// Rebuild the internal table of LoadedLibraryModule objects +// static +void LoadedLibraries::reload() { + + ThreadCritical cs; + + // discard old content + num_loaded = 0; + + // Call loadquery(L_GETINFO..) to get a list of all loaded Dlls from AIX. + size_t buf_size = 4096; + char* loadquery_buf = AllocateHeap(buf_size, mtInternal); + + while(loadquery(L_GETINFO, loadquery_buf, buf_size) == -1) { + if (errno == ENOMEM) { + buf_size *= 2; + loadquery_buf = ReallocateHeap(loadquery_buf, buf_size, mtInternal); + } else { + FreeHeap(loadquery_buf); + // Ensure that the uintptr_t pointer is valid + assert(errno != EFAULT, "loadquery: Invalid uintptr_t in info buffer."); + fprintf(stderr, "loadquery failed (%d %s)", errno, strerror(errno)); + return; + } + } + + // Iterate over the loadquery result. For details see sys/ldr.h on AIX. + const struct ld_info* p = (struct ld_info*) loadquery_buf; + + // Ensure we have all loaded libs. + bool all_loaded = false; + while(num_loaded < MAX_MODULES) { + LoadedLibraryModule& mod = tab[num_loaded]; + mod.text_from = (const unsigned char*) p->ldinfo_textorg; + mod.text_to = (const unsigned char*) (((char*)p->ldinfo_textorg) + p->ldinfo_textsize); + mod.data_from = (const unsigned char*) p->ldinfo_dataorg; + mod.data_to = (const unsigned char*) (((char*)p->ldinfo_dataorg) + p->ldinfo_datasize); + sprintf(mod.fullpath, "%.*s", sizeof(mod.fullpath), p->ldinfo_filename); + // do we have a member name as well (see ldr.h)? + const char* p_mbr_name = p->ldinfo_filename + strlen(p->ldinfo_filename) + 1; + if (*p_mbr_name) { + sprintf(mod.membername, "%.*s", sizeof(mod.membername), p_mbr_name); + } else { + mod.membername[0] = '\0'; + } + + // fill in the short name + const char* p_slash = strrchr(mod.fullpath, '/'); + if (p_slash) { + sprintf(mod.shortname, "%.*s", sizeof(mod.shortname), p_slash + 1); + } else { + sprintf(mod.shortname, "%.*s", sizeof(mod.shortname), mod.fullpath); + } + num_loaded ++; + + // next entry... + if (p->ldinfo_next) { + p = (struct ld_info*)(((char*)p) + p->ldinfo_next); + } else { + all_loaded = true; + break; + } + } + + FreeHeap(loadquery_buf); + + // Ensure we have all loaded libs + assert(all_loaded, "loadquery returned more entries then expected. Please increase MAX_MODULES"); + +} // end LoadedLibraries::reload() + + +// output loaded libraries table +//static +void LoadedLibraries::print(outputStream* os) { + + for (int i = 0; i < num_loaded; i++) { + tab[i].print(os); + } + +} + diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/loadlib_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/loadlib_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,128 @@ +/* + * Copyright 2012, 2013 SAP AG. 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. + * + */ + + +// Loadlib_aix.cpp contains support code for analysing the memory +// layout of loaded binaries in ones own process space. +// +// It is needed, among other things, to provide a dladdr() emulation, because +// that one is not provided by AIX + +#ifndef OS_AIX_VM_LOADLIB_AIX_HPP +#define OS_AIX_VM_LOADLIB_AIX_HPP + +class outputStream; + +// This class holds information about a single loaded library module. +// Note that on AIX, a single library can be spread over multiple +// uintptr_t range on a module base, eg. +// libC.a(shr3_64.o) or libC.a(shrcore_64.o). +class LoadedLibraryModule { + + friend class LoadedLibraries; + + char fullpath[512]; // eg /usr/lib/libC.a + char shortname[30]; // eg libC.a + char membername[30]; // eg shrcore_64.o + const unsigned char* text_from; + const unsigned char* text_to; + const unsigned char* data_from; + const unsigned char* data_to; + + public: + + const char* get_fullpath() const { + return fullpath; + } + const char* get_shortname() const { + return shortname; + } + const char* get_membername() const { + return membername; + } + + // text_from, text_to: returns the range of the text (code) + // segment for that module + const unsigned char* get_text_from() const { + return text_from; + } + const unsigned char* get_text_to() const { + return text_to; + } + + // data_from/data_to: returns the range of the data + // segment for that module + const unsigned char* get_data_from() const { + return data_from; + } + const unsigned char* get_data_to() const { + return data_to; + } + + // returns true if the + bool is_in_text(const unsigned char* p) const { + return p >= text_from && p < text_to ? true : false; + } + + bool is_in_data(const unsigned char* p) const { + return p >= data_from && p < data_to ? true : false; + } + + // output debug info + void print(outputStream* os) const; + +}; // end LoadedLibraryModule + +// This class is a singleton holding a map of all loaded binaries +// in the AIX process space. +class LoadedLibraries +// : AllStatic (including allocation.hpp just for AllStatic is overkill.) +{ + + private: + + enum {MAX_MODULES = 100}; + static LoadedLibraryModule tab[MAX_MODULES]; + static int num_loaded; + + public: + + // rebuild the internal table of LoadedLibraryModule objects + static void reload(); + + // checks whether the address p points to any of the loaded code segments. + // If it does, returns the LoadedLibraryModule entry. If not, returns NULL. + static const LoadedLibraryModule* find_for_text_address(const unsigned char* p); + + // checks whether the address p points to any of the loaded data segments. + // If it does, returns the LoadedLibraryModule entry. If not, returns NULL. + static const LoadedLibraryModule* find_for_data_address(const unsigned char* p); + + // output debug info + static void print(outputStream* os); + +}; // end LoadedLibraries + + +#endif // OS_AIX_VM_LOADLIB_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/mutex_aix.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/mutex_aix.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1999, 2010, 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. + * + */ + +#ifndef OS_LINUX_VM_MUTEX_LINUX_INLINE_HPP +#define OS_LINUX_VM_MUTEX_LINUX_INLINE_HPP + +#include "os_aix.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "thread_aix.inline.hpp" + + +// Reconciliation History +// mutex_solaris.inline.hpp 1.5 99/06/22 16:38:49 +// End + +#endif // OS_LINUX_VM_MUTEX_LINUX_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/osThread_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/osThread_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// no precompiled headers +#include "runtime/atomic.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/os.hpp" +#include "runtime/osThread.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/vmThread.hpp" +#ifdef TARGET_ARCH_ppc +# include "assembler_ppc.inline.hpp" +#endif + + +void OSThread::pd_initialize() { + assert(this != NULL, "check"); + _thread_id = 0; + _pthread_id = 0; + _siginfo = NULL; + _ucontext = NULL; + _expanding_stack = 0; + _alt_sig_stack = NULL; + + _last_cpu_times.sys = _last_cpu_times.user = 0L; + + sigemptyset(&_caller_sigmask); + + _startThread_lock = new Monitor(Mutex::event, "startThread_lock", true); + assert(_startThread_lock !=NULL, "check"); +} + +void OSThread::pd_destroy() { + delete _startThread_lock; +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/osThread_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/osThread_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_AIX_VM_OSTHREAD_AIX_HPP +#define OS_AIX_VM_OSTHREAD_AIX_HPP + + public: + typedef pid_t thread_id_t; + + private: + int _thread_type; + + public: + + int thread_type() const { + return _thread_type; + } + void set_thread_type(int type) { + _thread_type = type; + } + + private: + + // _pthread_id is the pthread id, which is used by library calls + // (e.g. pthread_kill). + pthread_t _pthread_id; + + sigset_t _caller_sigmask; // Caller's signal mask + + public: + + // Methods to save/restore caller's signal mask + sigset_t caller_sigmask() const { return _caller_sigmask; } + void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } + +#ifndef PRODUCT + // Used for debugging, return a unique integer for each thread. + int thread_identifier() const { return _thread_id; } +#endif +#ifdef ASSERT + // We expect no reposition failures so kill vm if we get one. + // + bool valid_reposition_failure() { + return false; + } +#endif // ASSERT + pthread_t pthread_id() const { + return _pthread_id; + } + void set_pthread_id(pthread_t tid) { + _pthread_id = tid; + } + + // *************************************************************** + // suspension support. + // *************************************************************** + + public: + // flags that support signal based suspend/resume on Linux are in a + // separate class to avoid confusion with many flags in OSThread that + // are used by VM level suspend/resume. + os::SuspendResume sr; + + // _ucontext and _siginfo are used by SR_handler() to save thread context, + // and they will later be used to walk the stack or reposition thread PC. + // If the thread is not suspended in SR_handler() (e.g. self suspend), + // the value in _ucontext is meaningless, so we must use the last Java + // frame information as the frame. This will mean that for threads + // that are parked on a mutex the profiler (and safepoint mechanism) + // will see the thread as if it were still in the Java frame. This + // not a problem for the profiler since the Java frame is a close + // enough result. For the safepoint mechanism when the give it the + // Java frame we are not at a point where the safepoint needs the + // frame to that accurate (like for a compiled safepoint) since we + // should be in a place where we are native and will block ourselves + // if we transition. + private: + void* _siginfo; + ucontext_t* _ucontext; + int _expanding_stack; // non zero if manually expanding stack + address _alt_sig_stack; // address of base of alternate signal stack + + public: + void* siginfo() const { return _siginfo; } + void set_siginfo(void* ptr) { _siginfo = ptr; } + ucontext_t* ucontext() const { return _ucontext; } + void set_ucontext(ucontext_t* ptr) { _ucontext = ptr; } + void set_expanding_stack(void) { _expanding_stack = 1; } + void clear_expanding_stack(void) { _expanding_stack = 0; } + int expanding_stack(void) { return _expanding_stack; } + + void set_alt_sig_stack(address val) { _alt_sig_stack = val; } + address alt_sig_stack(void) { return _alt_sig_stack; } + + private: + Monitor* _startThread_lock; // sync parent and child in thread creation + + public: + + Monitor* startThread_lock() const { + return _startThread_lock; + } + + // *************************************************************** + // Platform dependent initialization and cleanup + // *************************************************************** + + private: + + void pd_initialize(); + void pd_destroy(); + + public: + + // The last measured values of cpu timing to prevent the "stale + // value return" bug in thread_cpu_time. + volatile struct { + jlong sys; + jlong user; + } _last_cpu_times; + +#endif // OS_AIX_VM_OSTHREAD_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/os_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/os_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,5145 @@ +/* + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// According to the AIX OS doc #pragma alloca must be used +// with C++ compiler before referencing the function alloca() +#pragma alloca + +// no precompiled headers +#include "classfile/classLoader.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "code/icBuffer.hpp" +#include "code/vtableStubs.hpp" +#include "compiler/compileBroker.hpp" +#include "interpreter/interpreter.hpp" +#include "jvm_aix.h" +#include "libperfstat_aix.hpp" +#include "loadlib_aix.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/filemap.hpp" +#include "mutex_aix.inline.hpp" +#include "oops/oop.inline.hpp" +#include "os_share_aix.hpp" +#include "porting_aix.hpp" +#include "prims/jniFastGetField.hpp" +#include "prims/jvm.h" +#include "prims/jvm_misc.hpp" +#include "runtime/arguments.hpp" +#include "runtime/extendedPC.hpp" +#include "runtime/globals.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/objectMonitor.hpp" +#include "runtime/osThread.hpp" +#include "runtime/perfMemory.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/statSampler.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/threadCritical.hpp" +#include "runtime/timer.hpp" +#include "services/attachListener.hpp" +#include "services/runtimeService.hpp" +#include "thread_aix.inline.hpp" +#include "utilities/decoder.hpp" +#include "utilities/defaultStream.hpp" +#include "utilities/events.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/vmError.hpp" +#ifdef TARGET_ARCH_ppc +# include "assembler_ppc.inline.hpp" +# include "nativeInst_ppc.hpp" +#endif +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#endif +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif + +// put OS-includes here (sorted alphabetically) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Add missing declarations (should be in procinfo.h but isn't until AIX 6.1). +#if !defined(_AIXVERSION_610) +extern "C" { + int getthrds64(pid_t ProcessIdentifier, + struct thrdentry64* ThreadBuffer, + int ThreadSize, + tid64_t* IndexPointer, + int Count); +} +#endif + +// Excerpts from systemcfg.h definitions newer than AIX 5.3 +#ifndef PV_7 +# define PV_7 0x200000 // Power PC 7 +# define PV_7_Compat 0x208000 // Power PC 7 +#endif + +#define MAX_PATH (2 * K) + +// for timer info max values which include all bits +#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) +// for multipage initialization error analysis (in 'g_multipage_error') +#define ERROR_MP_OS_TOO_OLD 100 +#define ERROR_MP_EXTSHM_ACTIVE 101 +#define ERROR_MP_VMGETINFO_FAILED 102 +#define ERROR_MP_VMGETINFO_CLAIMS_NO_SUPPORT_FOR_64K 103 + +// the semantics in this file are thus that codeptr_t is a *real code ptr* +// This means that any function taking codeptr_t as arguments will assume +// a real codeptr and won't handle function descriptors (eg getFuncName), +// whereas functions taking address as args will deal with function +// descriptors (eg os::dll_address_to_library_name) +typedef unsigned int* codeptr_t; + +// typedefs for stackslots, stack pointers, pointers to op codes +typedef unsigned long stackslot_t; +typedef stackslot_t* stackptr_t; + +// Query dimensions of the stack of the calling thread. +static void query_stack_dimensions(address* p_stack_base, size_t* p_stack_size); + +// Function to check a given stack pointer against given stack limits. +inline bool is_valid_stackpointer(stackptr_t sp, stackptr_t stack_base, size_t stack_size) { + if (((uintptr_t)sp) & 0x7) { + return false; + } + if (sp > stack_base) { + return false; + } + if (sp < (stackptr_t) ((address)stack_base - stack_size)) { + return false; + } + return true; +} + +// Returns true if function is a valid codepointer. +inline bool is_valid_codepointer(codeptr_t p) { + if (!p) { + return false; + } + if (((uintptr_t)p) & 0x3) { + return false; + } + if (LoadedLibraries::find_for_text_address((address)p) == NULL) { + return false; + } + return true; +} + +// macro to check a given stack pointer against given stack limits and to die if test fails +#define CHECK_STACK_PTR(sp, stack_base, stack_size) { \ + guarantee(is_valid_stackpointer((stackptr_t)(sp), (stackptr_t)(stack_base), stack_size), "Stack Pointer Invalid"); \ +} + +// macro to check the current stack pointer against given stacklimits +#define CHECK_CURRENT_STACK_PTR(stack_base, stack_size) { \ + address sp; \ + sp = os::current_stack_pointer(); \ + CHECK_STACK_PTR(sp, stack_base, stack_size); \ +} + +//////////////////////////////////////////////////////////////////////////////// +// global variables (for a description see os_aix.hpp) + +julong os::Aix::_physical_memory = 0; +pthread_t os::Aix::_main_thread = ((pthread_t)0); +int os::Aix::_page_size = -1; +int os::Aix::_on_pase = -1; +int os::Aix::_os_version = -1; +int os::Aix::_stack_page_size = -1; +size_t os::Aix::_shm_default_page_size = -1; +int os::Aix::_can_use_64K_pages = -1; +int os::Aix::_can_use_16M_pages = -1; +int os::Aix::_xpg_sus_mode = -1; +int os::Aix::_extshm = -1; +int os::Aix::_logical_cpus = -1; + +//////////////////////////////////////////////////////////////////////////////// +// local variables + +static int g_multipage_error = -1; // error analysis for multipage initialization +static jlong initial_time_count = 0; +static int clock_tics_per_sec = 100; +static sigset_t check_signal_done; // For diagnostics to print a message once (see run_periodic_checks) +static bool check_signals = true; +static pid_t _initial_pid = 0; +static int SR_signum = SIGUSR2; // Signal used to suspend/resume a thread (must be > SIGSEGV, see 4355769) +static sigset_t SR_sigset; +static pthread_mutex_t dl_mutex; // Used to protect dlsym() calls */ + +julong os::available_memory() { + return Aix::available_memory(); +} + +julong os::Aix::available_memory() { + Unimplemented(); + return 0; +} + +julong os::physical_memory() { + return Aix::physical_memory(); +} + +julong os::allocatable_physical_memory(julong size) { +#ifdef _LP64 + return size; +#else + Unimplemented(); +#endif // _LP64 +} + +//////////////////////////////////////////////////////////////////////////////// +// environment support + +bool os::getenv(const char* name, char* buf, int len) { + const char* val = ::getenv(name); + if (val != NULL && strlen(val) < (size_t)len) { + strcpy(buf, val); + return true; + } + if (len > 0) buf[0] = 0; // return a null string + return false; +} + + +// Return true if user is running as root. + +bool os::have_special_privileges() { + static bool init = false; + static bool privileges = false; + if (!init) { + privileges = (getuid() != geteuid()) || (getgid() != getegid()); + init = true; + } + return privileges; +} + +// Helper function, emulates disclaim64 using multiple 32bit disclaims +// because we cannot use disclaim64() on AS/400 and old AIX releases. +static bool my_disclaim64(char* addr, size_t size) { + + if (size == 0) { + return true; + } + + // Maximum size 32bit disclaim() accepts. (Theoretically 4GB, but I just do not trust that.) + const unsigned int maxDisclaimSize = 0x80000000; + + const unsigned int numFullDisclaimsNeeded = (size / maxDisclaimSize); + const unsigned int lastDisclaimSize = (size % maxDisclaimSize); + + char* p = addr; + + for (int i = 0; i < numFullDisclaimsNeeded; i ++) { + if (::disclaim(p, maxDisclaimSize, DISCLAIM_ZEROMEM) != 0) { + //if (Verbose) + fprintf(stderr, "Cannot disclaim %p - %p (errno %d)\n", p, p + maxDisclaimSize, errno); + return false; + } + p += maxDisclaimSize; + } + + if (lastDisclaimSize > 0) { + if (::disclaim(p, lastDisclaimSize, DISCLAIM_ZEROMEM) != 0) { + //if (Verbose) + fprintf(stderr, "Cannot disclaim %p - %p (errno %d)\n", p, p + lastDisclaimSize, errno); + return false; + } + } + + return true; +} + +// Cpu architecture string +#if defined(PPC32) +static char cpu_arch[] = "ppc"; +#elif defined(PPC64) +static char cpu_arch[] = "ppc64"; +#else +#error Add appropriate cpu_arch setting +#endif + + +// Given an address, returns the size of the page backing that address. +size_t os::Aix::query_pagesize(void* addr) { + + vm_page_info pi; + pi.addr = (uint64_t)addr; + if (::vmgetinfo(&pi, VM_PAGE_INFO, sizeof(pi)) == 0) { + return pi.pagesize; + } else { + fprintf(stderr, "vmgetinfo failed to retrieve page size for address %p (errno %d).\n", addr, errno); + assert(false, "vmgetinfo failed to retrieve page size"); + return SIZE_4K; + } + +} + +// Returns the kernel thread id of the currently running thread. +pid_t os::Aix::gettid() { + return (pid_t) thread_self(); +} + +void os::Aix::initialize_system_info() { + + // get the number of online(logical) cpus instead of configured + os::_processor_count = sysconf(_SC_NPROCESSORS_ONLN); + assert(_processor_count > 0, "_processor_count must be > 0"); + + // retrieve total physical storage + os::Aix::meminfo_t mi; + if (!os::Aix::get_meminfo(&mi)) { + fprintf(stderr, "os::Aix::get_meminfo failed.\n"); fflush(stderr); + assert(false, "os::Aix::get_meminfo failed."); + } + _physical_memory = (julong) mi.real_total; +} + +// Helper function for tracing page sizes. +static const char* describe_pagesize(size_t pagesize) { + switch (pagesize) { + case SIZE_4K : return "4K"; + case SIZE_64K: return "64K"; + case SIZE_16M: return "16M"; + case SIZE_16G: return "16G"; + default: + assert(false, "surprise"); + return "??"; + } +} + +// Retrieve information about multipage size support. Will initialize +// Aix::_page_size, Aix::_stack_page_size, Aix::_can_use_64K_pages, +// Aix::_can_use_16M_pages. +// Must be called before calling os::large_page_init(). +void os::Aix::query_multipage_support() { + + guarantee(_page_size == -1 && + _stack_page_size == -1 && + _can_use_64K_pages == -1 && + _can_use_16M_pages == -1 && + g_multipage_error == -1, + "do not call twice"); + + _page_size = ::sysconf(_SC_PAGESIZE); + + // This really would surprise me. + assert(_page_size == SIZE_4K, "surprise!"); + + + // query default data page size (default page size for C-Heap, pthread stacks and .bss). + // Default data page size is influenced either by linker options (-bdatapsize) + // or by environment variable LDR_CNTRL (suboption DATAPSIZE). If none is given, + // default should be 4K. + size_t data_page_size = SIZE_4K; + { + void* p = ::malloc(SIZE_16M); + data_page_size = os::Aix::query_pagesize(p); + ::free(p); + } + + // query default shm page size (LDR_CNTRL SHMPSIZE) + { + const int shmid = ::shmget(IPC_PRIVATE, 1, IPC_CREAT | S_IRUSR | S_IWUSR); + guarantee(shmid != -1, "shmget failed"); + void* p = ::shmat(shmid, NULL, 0); + ::shmctl(shmid, IPC_RMID, NULL); + guarantee(p != (void*) -1, "shmat failed"); + _shm_default_page_size = os::Aix::query_pagesize(p); + ::shmdt(p); + } + + // before querying the stack page size, make sure we are not running as primordial + // thread (because primordial thread's stack may have different page size than + // pthread thread stacks). Running a VM on the primordial thread won't work for a + // number of reasons so we may just as well guarantee it here + guarantee(!os::Aix::is_primordial_thread(), "Must not be called for primordial thread"); + + // query stack page size + { + int dummy = 0; + _stack_page_size = os::Aix::query_pagesize(&dummy); + // everything else would surprise me and should be looked into + guarantee(_stack_page_size == SIZE_4K || _stack_page_size == SIZE_64K, "Wrong page size"); + // also, just for completeness: pthread stacks are allocated from C heap, so + // stack page size should be the same as data page size + guarantee(_stack_page_size == data_page_size, "stack page size should be the same as data page size"); + } + + // EXTSHM is bad: among other things, it prevents setting pagesize dynamically + // for system V shm. + if (Aix::extshm()) { + if (Verbose) { + fprintf(stderr, "EXTSHM is active - will disable large page support.\n" + "Please make sure EXTSHM is OFF for large page support.\n"); + } + g_multipage_error = ERROR_MP_EXTSHM_ACTIVE; + _can_use_64K_pages = _can_use_16M_pages = 0; + goto query_multipage_support_end; + } + + // now check which page sizes the OS claims it supports, and of those, which actually can be used. + { + const int MAX_PAGE_SIZES = 4; + psize_t sizes[MAX_PAGE_SIZES]; + const int num_psizes = ::vmgetinfo(sizes, VMINFO_GETPSIZES, MAX_PAGE_SIZES); + if (num_psizes == -1) { + if (Verbose) { + fprintf(stderr, "vmgetinfo(VMINFO_GETPSIZES) failed (errno: %d)\n", errno); + fprintf(stderr, "disabling multipage support.\n"); + } + g_multipage_error = ERROR_MP_VMGETINFO_FAILED; + _can_use_64K_pages = _can_use_16M_pages = 0; + goto query_multipage_support_end; + } + guarantee(num_psizes > 0, "vmgetinfo(.., VMINFO_GETPSIZES, ...) failed."); + assert(num_psizes <= MAX_PAGE_SIZES, "Surprise! more than 4 page sizes?"); + if (Verbose) { + fprintf(stderr, "vmgetinfo(.., VMINFO_GETPSIZES, ...) returns %d supported page sizes: ", num_psizes); + for (int i = 0; i < num_psizes; i ++) { + fprintf(stderr, " %s ", describe_pagesize(sizes[i])); + } + fprintf(stderr, " .\n"); + } + + // Can we use 64K, 16M pages? + _can_use_64K_pages = 0; + _can_use_16M_pages = 0; + for (int i = 0; i < num_psizes; i ++) { + if (sizes[i] == SIZE_64K) { + _can_use_64K_pages = 1; + } else if (sizes[i] == SIZE_16M) { + _can_use_16M_pages = 1; + } + } + + if (!_can_use_64K_pages) { + g_multipage_error = ERROR_MP_VMGETINFO_CLAIMS_NO_SUPPORT_FOR_64K; + } + + // Double-check for 16M pages: Even if AIX claims to be able to use 16M pages, + // there must be an actual 16M page pool, and we must run with enough rights. + if (_can_use_16M_pages) { + const int shmid = ::shmget(IPC_PRIVATE, SIZE_16M, IPC_CREAT | S_IRUSR | S_IWUSR); + guarantee(shmid != -1, "shmget failed"); + struct shmid_ds shm_buf = { 0 }; + shm_buf.shm_pagesize = SIZE_16M; + const bool can_set_pagesize = ::shmctl(shmid, SHM_PAGESIZE, &shm_buf) == 0 ? true : false; + const int en = errno; + ::shmctl(shmid, IPC_RMID, NULL); + if (!can_set_pagesize) { + if (Verbose) { + fprintf(stderr, "Failed to allocate even one misely 16M page. shmctl failed with %d (%s).\n" + "Will deactivate 16M support.\n", en, strerror(en)); + } + _can_use_16M_pages = 0; + } + } + + } // end: check which pages can be used for shared memory + +query_multipage_support_end: + + guarantee(_page_size != -1 && + _stack_page_size != -1 && + _can_use_64K_pages != -1 && + _can_use_16M_pages != -1, "Page sizes not properly initialized"); + + if (_can_use_64K_pages) { + g_multipage_error = 0; + } + + if (Verbose) { + fprintf(stderr, "Data page size (C-Heap, bss, etc): %s\n", describe_pagesize(data_page_size)); + fprintf(stderr, "Thread stack page size (pthread): %s\n", describe_pagesize(_stack_page_size)); + fprintf(stderr, "Default shared memory page size: %s\n", describe_pagesize(_shm_default_page_size)); + fprintf(stderr, "Can use 64K pages dynamically with shared meory: %s\n", (_can_use_64K_pages ? "yes" :"no")); + fprintf(stderr, "Can use 16M pages dynamically with shared memory: %s\n", (_can_use_16M_pages ? "yes" :"no")); + fprintf(stderr, "Multipage error details: %d\n", g_multipage_error); + } + +} // end os::Aix::query_multipage_support() + + +// The code for this method was initially derived from the version in os_linux.cpp +void os::init_system_properties_values() { + // The next few definitions allow the code to be verbatim: +#define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n), mtInternal) +#define DEFAULT_LIBPATH "/usr/lib:/lib" +#define EXTENSIONS_DIR "/lib/ext" +#define ENDORSED_DIR "/lib/endorsed" + + // sysclasspath, java_home, dll_dir + char *home_path; + char *dll_path; + char *pslash; + char buf[MAXPATHLEN]; + os::jvm_path(buf, sizeof(buf)); + + // Found the full path to libjvm.so. + // Now cut the path to /jre if we can. + *(strrchr(buf, '/')) = '\0'; // get rid of /libjvm.so + pslash = strrchr(buf, '/'); + if (pslash != NULL) { + *pslash = '\0'; // get rid of /{client|server|hotspot} + } + + dll_path = malloc(strlen(buf) + 1); + strcpy(dll_path, buf); + Arguments::set_dll_dir(dll_path); + + if (pslash != NULL) { + pslash = strrchr(buf, '/'); + if (pslash != NULL) { + *pslash = '\0'; // get rid of / + pslash = strrchr(buf, '/'); + if (pslash != NULL) { + *pslash = '\0'; // get rid of /lib + } + } + } + + home_path = malloc(strlen(buf) + 1); + strcpy(home_path, buf); + Arguments::set_java_home(home_path); + + if (!set_boot_path('/', ':')) return; + + // Where to look for native libraries + + // On Aix we get the user setting of LIBPATH + // Eventually, all the library path setting will be done here. + char *ld_library_path; + + // Construct the invariant part of ld_library_path. + ld_library_path = (char *) malloc(sizeof(DEFAULT_LIBPATH)); + sprintf(ld_library_path, DEFAULT_LIBPATH); + + // Get the user setting of LIBPATH, and prepended it. + char *v = ::getenv("LIBPATH"); + if (v == NULL) { + v = ""; + } + + char *t = ld_library_path; + // That's +1 for the colon and +1 for the trailing '\0' + ld_library_path = (char *) malloc(strlen(v) + 1 + strlen(t) + 1); + sprintf(ld_library_path, "%s:%s", v, t); + + Arguments::set_library_path(ld_library_path); + + // Extensions directories + char* cbuf = malloc(strlen(Arguments::get_java_home()) + sizeof(EXTENSIONS_DIR)); + sprintf(cbuf, "%s" EXTENSIONS_DIR, Arguments::get_java_home()); + Arguments::set_ext_dirs(cbuf); + + // Endorsed standards default directory. + cbuf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); + sprintf(cbuf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(cbuf); + +#undef malloc +#undef DEFAULT_LIBPATH +#undef EXTENSIONS_DIR +#undef ENDORSED_DIR +} + +//////////////////////////////////////////////////////////////////////////////// +// breakpoint support + +void os::breakpoint() { + BREAKPOINT; +} + +extern "C" void breakpoint() { + // use debugger to set breakpoint here +} + +//////////////////////////////////////////////////////////////////////////////// +// signal support + +debug_only(static bool signal_sets_initialized = false); +static sigset_t unblocked_sigs, vm_sigs, allowdebug_blocked_sigs; + +bool os::Aix::is_sig_ignored(int sig) { + struct sigaction oact; + sigaction(sig, (struct sigaction*)NULL, &oact); + void* ohlr = oact.sa_sigaction ? CAST_FROM_FN_PTR(void*, oact.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oact.sa_handler); + if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN)) + return true; + else + return false; +} + +void os::Aix::signal_sets_init() { + // Should also have an assertion stating we are still single-threaded. + assert(!signal_sets_initialized, "Already initialized"); + // Fill in signals that are necessarily unblocked for all threads in + // the VM. Currently, we unblock the following signals: + // SHUTDOWN{1,2,3}_SIGNAL: for shutdown hooks support (unless over-ridden + // by -Xrs (=ReduceSignalUsage)); + // BREAK_SIGNAL which is unblocked only by the VM thread and blocked by all + // other threads. The "ReduceSignalUsage" boolean tells us not to alter + // the dispositions or masks wrt these signals. + // Programs embedding the VM that want to use the above signals for their + // own purposes must, at this time, use the "-Xrs" option to prevent + // interference with shutdown hooks and BREAK_SIGNAL thread dumping. + // (See bug 4345157, and other related bugs). + // In reality, though, unblocking these signals is really a nop, since + // these signals are not blocked by default. + sigemptyset(&unblocked_sigs); + sigemptyset(&allowdebug_blocked_sigs); + sigaddset(&unblocked_sigs, SIGILL); + sigaddset(&unblocked_sigs, SIGSEGV); + sigaddset(&unblocked_sigs, SIGBUS); + sigaddset(&unblocked_sigs, SIGFPE); + sigaddset(&unblocked_sigs, SIGTRAP); + sigaddset(&unblocked_sigs, SIGDANGER); + sigaddset(&unblocked_sigs, SR_signum); + + if (!ReduceSignalUsage) { + if (!os::Aix::is_sig_ignored(SHUTDOWN1_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN1_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN1_SIGNAL); + } + if (!os::Aix::is_sig_ignored(SHUTDOWN2_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN2_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN2_SIGNAL); + } + if (!os::Aix::is_sig_ignored(SHUTDOWN3_SIGNAL)) { + sigaddset(&unblocked_sigs, SHUTDOWN3_SIGNAL); + sigaddset(&allowdebug_blocked_sigs, SHUTDOWN3_SIGNAL); + } + } + // Fill in signals that are blocked by all but the VM thread. + sigemptyset(&vm_sigs); + if (!ReduceSignalUsage) + sigaddset(&vm_sigs, BREAK_SIGNAL); + debug_only(signal_sets_initialized = true); +} + +// These are signals that are unblocked while a thread is running Java. +// (For some reason, they get blocked by default.) +sigset_t* os::Aix::unblocked_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &unblocked_sigs; +} + +// These are the signals that are blocked while a (non-VM) thread is +// running Java. Only the VM thread handles these signals. +sigset_t* os::Aix::vm_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &vm_sigs; +} + +// These are signals that are blocked during cond_wait to allow debugger in +sigset_t* os::Aix::allowdebug_blocked_signals() { + assert(signal_sets_initialized, "Not initialized"); + return &allowdebug_blocked_sigs; +} + +void os::Aix::hotspot_sigmask(Thread* thread) { + + //Save caller's signal mask before setting VM signal mask + sigset_t caller_sigmask; + pthread_sigmask(SIG_BLOCK, NULL, &caller_sigmask); + + OSThread* osthread = thread->osthread(); + osthread->set_caller_sigmask(caller_sigmask); + + pthread_sigmask(SIG_UNBLOCK, os::Aix::unblocked_signals(), NULL); + + if (!ReduceSignalUsage) { + if (thread->is_VM_thread()) { + // Only the VM thread handles BREAK_SIGNAL ... + pthread_sigmask(SIG_UNBLOCK, vm_signals(), NULL); + } else { + // ... all other threads block BREAK_SIGNAL + pthread_sigmask(SIG_BLOCK, vm_signals(), NULL); + } + } +} + +// retrieve memory information. +// Returns false if something went wrong; +// content of pmi undefined in this case. +bool os::Aix::get_meminfo(meminfo_t* pmi) { + + assert(pmi, "get_meminfo: invalid parameter"); + + memset(pmi, 0, sizeof(meminfo_t)); + + if (os::Aix::on_pase()) { + + Unimplemented(); + return false; + + } else { + + // On AIX, I use the (dynamically loaded) perfstat library to retrieve memory statistics + // See: + // http://publib.boulder.ibm.com/infocenter/systems/index.jsp + // ?topic=/com.ibm.aix.basetechref/doc/basetrf1/perfstat_memtot.htm + // http://publib.boulder.ibm.com/infocenter/systems/index.jsp + // ?topic=/com.ibm.aix.files/doc/aixfiles/libperfstat.h.htm + + perfstat_memory_total_t psmt; + memset (&psmt, '\0', sizeof(psmt)); + const int rc = libperfstat::perfstat_memory_total(NULL, &psmt, sizeof(psmt), 1); + if (rc == -1) { + fprintf(stderr, "perfstat_memory_total() failed (errno=%d)\n", errno); + assert(0, "perfstat_memory_total() failed"); + return false; + } + + assert(rc == 1, "perfstat_memory_total() - weird return code"); + + // excerpt from + // http://publib.boulder.ibm.com/infocenter/systems/index.jsp + // ?topic=/com.ibm.aix.files/doc/aixfiles/libperfstat.h.htm + // The fields of perfstat_memory_total_t: + // u_longlong_t virt_total Total virtual memory (in 4 KB pages). + // u_longlong_t real_total Total real memory (in 4 KB pages). + // u_longlong_t real_free Free real memory (in 4 KB pages). + // u_longlong_t pgsp_total Total paging space (in 4 KB pages). + // u_longlong_t pgsp_free Free paging space (in 4 KB pages). + + pmi->virt_total = psmt.virt_total * 4096; + pmi->real_total = psmt.real_total * 4096; + pmi->real_free = psmt.real_free * 4096; + pmi->pgsp_total = psmt.pgsp_total * 4096; + pmi->pgsp_free = psmt.pgsp_free * 4096; + + return true; + + } +} // end os::Aix::get_meminfo + +// Retrieve global cpu information. +// Returns false if something went wrong; +// the content of pci is undefined in this case. +bool os::Aix::get_cpuinfo(cpuinfo_t* pci) { + assert(pci, "get_cpuinfo: invalid parameter"); + memset(pci, 0, sizeof(cpuinfo_t)); + + perfstat_cpu_total_t psct; + memset (&psct, '\0', sizeof(psct)); + + if (-1 == libperfstat::perfstat_cpu_total(NULL, &psct, sizeof(perfstat_cpu_total_t), 1)) { + fprintf(stderr, "perfstat_cpu_total() failed (errno=%d)\n", errno); + assert(0, "perfstat_cpu_total() failed"); + return false; + } + + // global cpu information + strcpy (pci->description, psct.description); + pci->processorHZ = psct.processorHZ; + pci->ncpus = psct.ncpus; + os::Aix::_logical_cpus = psct.ncpus; + for (int i = 0; i < 3; i++) { + pci->loadavg[i] = (double) psct.loadavg[i] / (1 << SBITS); + } + + // get the processor version from _system_configuration + switch (_system_configuration.version) { + case PV_7: + strcpy(pci->version, "Power PC 7"); + break; + case PV_6_1: + strcpy(pci->version, "Power PC 6 DD1.x"); + break; + case PV_6: + strcpy(pci->version, "Power PC 6"); + break; + case PV_5: + strcpy(pci->version, "Power PC 5"); + break; + case PV_5_2: + strcpy(pci->version, "Power PC 5_2"); + break; + case PV_5_3: + strcpy(pci->version, "Power PC 5_3"); + break; + case PV_5_Compat: + strcpy(pci->version, "PV_5_Compat"); + break; + case PV_6_Compat: + strcpy(pci->version, "PV_6_Compat"); + break; + case PV_7_Compat: + strcpy(pci->version, "PV_7_Compat"); + break; + default: + strcpy(pci->version, "unknown"); + } + + return true; + +} //end os::Aix::get_cpuinfo + +////////////////////////////////////////////////////////////////////////////// +// detecting pthread library + +void os::Aix::libpthread_init() { + return; +} + +////////////////////////////////////////////////////////////////////////////// +// create new thread + +// Thread start routine for all newly created threads +static void *java_start(Thread *thread) { + + // find out my own stack dimensions + { + // actually, this should do exactly the same as thread->record_stack_base_and_size... + address base = 0; + size_t size = 0; + query_stack_dimensions(&base, &size); + thread->set_stack_base(base); + thread->set_stack_size(size); + } + + // Do some sanity checks. + CHECK_CURRENT_STACK_PTR(thread->stack_base(), thread->stack_size()); + + // Try to randomize the cache line index of hot stack frames. + // This helps when threads of the same stack traces evict each other's + // cache lines. The threads can be either from the same JVM instance, or + // from different JVM instances. The benefit is especially true for + // processors with hyperthreading technology. + + static int counter = 0; + int pid = os::current_process_id(); + alloca(((pid ^ counter++) & 7) * 128); + + ThreadLocalStorage::set_thread(thread); + + OSThread* osthread = thread->osthread(); + + // thread_id is kernel thread id (similar to Solaris LWP id) + osthread->set_thread_id(os::Aix::gettid()); + + // initialize signal mask for this thread + os::Aix::hotspot_sigmask(thread); + + // initialize floating point control register + os::Aix::init_thread_fpu_state(); + + assert(osthread->get_state() == RUNNABLE, "invalid os thread state"); + + // call one more level start routine + thread->run(); + + return 0; +} + +bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { + + // We want the whole function to be synchronized. + ThreadCritical cs; + + assert(thread->osthread() == NULL, "caller responsible"); + + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + if (osthread == NULL) { + return false; + } + + // set the correct thread state + osthread->set_thread_type(thr_type); + + // Initial state is ALLOCATED but not INITIALIZED + osthread->set_state(ALLOCATED); + + thread->set_osthread(osthread); + + // init thread attributes + pthread_attr_t attr; + pthread_attr_init(&attr); + guarantee(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0, "???"); + + // Make sure we run in 1:1 kernel-user-thread mode. + if (os::Aix::on_aix()) { + guarantee(pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0, "???"); + guarantee(pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0, "???"); + } // end: aix + + // Start in suspended state, and in os::thread_start, wake the thread up. + guarantee(pthread_attr_setsuspendstate_np(&attr, PTHREAD_CREATE_SUSPENDED_NP) == 0, "???"); + + // calculate stack size if it's not specified by caller + if (os::Aix::supports_variable_stack_size()) { + if (stack_size == 0) { + stack_size = os::Aix::default_stack_size(thr_type); + + switch (thr_type) { + case os::java_thread: + // Java threads use ThreadStackSize whose default value can be changed with the flag -Xss. + assert(JavaThread::stack_size_at_create() > 0, "this should be set"); + stack_size = JavaThread::stack_size_at_create(); + break; + case os::compiler_thread: + if (CompilerThreadStackSize > 0) { + stack_size = (size_t)(CompilerThreadStackSize * K); + break; + } // else fall through: + // use VMThreadStackSize if CompilerThreadStackSize is not defined + case os::vm_thread: + case os::pgc_thread: + case os::cgc_thread: + case os::watcher_thread: + if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); + break; + } + } + + stack_size = MAX2(stack_size, os::Aix::min_stack_allowed); + pthread_attr_setstacksize(&attr, stack_size); + } //else let thread_create() pick the default value (96 K on AIX) + + pthread_t tid; + int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); + + pthread_attr_destroy(&attr); + + if (ret != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + perror("pthread_create()"); + } + // Need to clean up stuff we've allocated so far + thread->set_osthread(NULL); + delete osthread; + return false; + } + + // Store pthread info into the OSThread + osthread->set_pthread_id(tid); + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// attach existing thread + +// bootstrap the main thread +bool os::create_main_thread(JavaThread* thread) { + assert(os::Aix::_main_thread == pthread_self(), "should be called inside main thread"); + return create_attached_thread(thread); +} + +bool os::create_attached_thread(JavaThread* thread) { +#ifdef ASSERT + thread->verify_not_published(); +#endif + + // Allocate the OSThread object + OSThread* osthread = new OSThread(NULL, NULL); + + if (osthread == NULL) { + return false; + } + + // Store pthread info into the OSThread + osthread->set_thread_id(os::Aix::gettid()); + osthread->set_pthread_id(::pthread_self()); + + // initialize floating point control register + os::Aix::init_thread_fpu_state(); + + // some sanity checks + CHECK_CURRENT_STACK_PTR(thread->stack_base(), thread->stack_size()); + + // Initial thread state is RUNNABLE + osthread->set_state(RUNNABLE); + + thread->set_osthread(osthread); + + if (UseNUMA) { + int lgrp_id = os::numa_get_group_id(); + if (lgrp_id != -1) { + thread->set_lgrp_id(lgrp_id); + } + } + + // initialize signal mask for this thread + // and save the caller's signal mask + os::Aix::hotspot_sigmask(thread); + + return true; +} + +void os::pd_start_thread(Thread* thread) { + int status = pthread_continue_np(thread->osthread()->pthread_id()); + assert(status == 0, "thr_continue failed"); +} + +// Free OS resources related to the OSThread +void os::free_thread(OSThread* osthread) { + assert(osthread != NULL, "osthread not set"); + + if (Thread::current()->osthread() == osthread) { + // Restore caller's signal mask + sigset_t sigmask = osthread->caller_sigmask(); + pthread_sigmask(SIG_SETMASK, &sigmask, NULL); + } + + delete osthread; +} + +////////////////////////////////////////////////////////////////////////////// +// thread local storage + +int os::allocate_thread_local_storage() { + pthread_key_t key; + int rslt = pthread_key_create(&key, NULL); + assert(rslt == 0, "cannot allocate thread local storage"); + return (int)key; +} + +// Note: This is currently not used by VM, as we don't destroy TLS key +// on VM exit. +void os::free_thread_local_storage(int index) { + int rslt = pthread_key_delete((pthread_key_t)index); + assert(rslt == 0, "invalid index"); +} + +void os::thread_local_storage_at_put(int index, void* value) { + int rslt = pthread_setspecific((pthread_key_t)index, value); + assert(rslt == 0, "pthread_setspecific failed"); +} + +extern "C" Thread* get_thread() { + return ThreadLocalStorage::thread(); +} + +//////////////////////////////////////////////////////////////////////////////// +// time support + +// Time since start-up in seconds to a fine granularity. +// Used by VMSelfDestructTimer and the MemProfiler. +double os::elapsedTime() { + return (double)(os::elapsed_counter()) * 0.000001; +} + +jlong os::elapsed_counter() { + timeval time; + int status = gettimeofday(&time, NULL); + return jlong(time.tv_sec) * 1000 * 1000 + jlong(time.tv_usec) - initial_time_count; +} + +jlong os::elapsed_frequency() { + return (1000 * 1000); +} + +// For now, we say that linux does not support vtime. I have no idea +// whether it can actually be made to (DLD, 9/13/05). + +bool os::supports_vtime() { return false; } +bool os::enable_vtime() { return false; } +bool os::vtime_enabled() { return false; } +double os::elapsedVTime() { + // better than nothing, but not much + return elapsedTime(); +} + +jlong os::javaTimeMillis() { + timeval time; + int status = gettimeofday(&time, NULL); + assert(status != -1, "aix error at gettimeofday()"); + return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); +} + +// We need to manually declare mread_real_time, +// because IBM didn't provide a prototype in time.h. +// (they probably only ever tested in C, not C++) +extern "C" +int mread_real_time(timebasestruct_t *t, size_t size_of_timebasestruct_t); + +jlong os::javaTimeNanos() { + if (os::Aix::on_pase()) { + Unimplemented(); + return 0; + } + else { + // On AIX use the precision of processors real time clock + // or time base registers. + timebasestruct_t time; + int rc; + + // If the CPU has a time register, it will be used and + // we have to convert to real time first. After convertion we have following data: + // time.tb_high [seconds since 00:00:00 UTC on 1.1.1970] + // time.tb_low [nanoseconds after the last full second above] + // We better use mread_real_time here instead of read_real_time + // to ensure that we will get a monotonic increasing time. + if (mread_real_time(&time, TIMEBASE_SZ) != RTC_POWER) { + rc = time_base_to_time(&time, TIMEBASE_SZ); + assert(rc != -1, "aix error at time_base_to_time()"); + } + return jlong(time.tb_high) * (1000 * 1000 * 1000) + jlong(time.tb_low); + } +} + +void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { + { + // gettimeofday - based on time in seconds since the Epoch thus does not wrap + info_ptr->max_value = ALL_64_BITS; + + // gettimeofday is a real time clock so it skips + info_ptr->may_skip_backward = true; + info_ptr->may_skip_forward = true; + } + + info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time +} + +// Return the real, user, and system times in seconds from an +// arbitrary fixed point in the past. +bool os::getTimesSecs(double* process_real_time, + double* process_user_time, + double* process_system_time) { + Unimplemented(); + return false; +} + + +char * os::local_time_string(char *buf, size_t buflen) { + struct tm t; + time_t long_time; + time(&long_time); + localtime_r(&long_time, &t); + jio_snprintf(buf, buflen, "%d-%02d-%02d %02d:%02d:%02d", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec); + return buf; +} + +struct tm* os::localtime_pd(const time_t* clock, struct tm* res) { + return localtime_r(clock, res); +} + +//////////////////////////////////////////////////////////////////////////////// +// runtime exit support + +// Note: os::shutdown() might be called very early during initialization, or +// called from signal handler. Before adding something to os::shutdown(), make +// sure it is async-safe and can handle partially initialized VM. +void os::shutdown() { + + // allow PerfMemory to attempt cleanup of any persistent resources + perfMemory_exit(); + + // needs to remove object in file system + AttachListener::abort(); + + // flush buffered output, finish log files + ostream_abort(); + + // Check for abort hook + abort_hook_t abort_hook = Arguments::abort_hook(); + if (abort_hook != NULL) { + abort_hook(); + } + +} + +// Note: os::abort() might be called very early during initialization, or +// called from signal handler. Before adding something to os::abort(), make +// sure it is async-safe and can handle partially initialized VM. +void os::abort(bool dump_core) { + os::shutdown(); + if (dump_core) { +#ifndef PRODUCT + fdStream out(defaultStream::output_fd()); + out.print_raw("Current thread is "); + char buf[16]; + jio_snprintf(buf, sizeof(buf), UINTX_FORMAT, os::current_thread_id()); + out.print_raw_cr(buf); + out.print_raw_cr("Dumping core ..."); +#endif + ::abort(); // dump core + } + + ::exit(1); +} + +// Die immediately, no exit hook, no abort hook, no cleanup. +void os::die() { + ::abort(); +} + +// This method is a copy of JDK's sysGetLastErrorString +// from src/solaris/hpi/src/system_md.c + +size_t os::lasterror(char *buf, size_t len) { + + if (errno == 0) return 0; + + const char *s = ::strerror(errno); + size_t n = ::strlen(s); + if (n >= len) { + n = len - 1; + } + ::strncpy(buf, s, n); + buf[n] = '\0'; + return n; +} + +intx os::current_thread_id() { return (intx)pthread_self(); } +int os::current_process_id() { + + // This implementation returns a unique pid, the pid of the + // launcher thread that starts the vm 'process'. + + // Under POSIX, getpid() returns the same pid as the + // launcher thread rather than a unique pid per thread. + // Use gettid() if you want the old pre NPTL behaviour. + + // if you are looking for the result of a call to getpid() that + // returns a unique pid for the calling thread, then look at the + // OSThread::thread_id() method in osThread_linux.hpp file + + return (int)(_initial_pid ? _initial_pid : getpid()); +} + +// DLL functions + +const char* os::dll_file_extension() { return ".so"; } + +// This must be hard coded because it's the system's temporary +// directory not the java application's temp directory, ala java.io.tmpdir. +const char* os::get_temp_directory() { return "/tmp"; } + +static bool file_exists(const char* filename) { + struct stat statbuf; + if (filename == NULL || strlen(filename) == 0) { + return false; + } + return os::stat(filename, &statbuf) == 0; +} + +void os::dll_build_name(char* buffer, size_t buflen, + const char* pname, const char* fname) { + // Copied from libhpi + const size_t pnamelen = pname ? strlen(pname) : 0; + + // Quietly truncate on buffer overflow. Should be an error. + if (pnamelen + strlen(fname) + 10 > (size_t) buflen) { + *buffer = '\0'; + return; + } + + if (pnamelen == 0) { + snprintf(buffer, buflen, "lib%s.so", fname); + } else if (strchr(pname, *os::path_separator()) != NULL) { + int n; + char** pelements = split_path(pname, &n); + for (int i = 0; i < n; i++) { + // Really shouldn't be NULL, but check can't hurt + if (pelements[i] == NULL || strlen(pelements[i]) == 0) { + continue; // skip the empty path values + } + snprintf(buffer, buflen, "%s/lib%s.so", pelements[i], fname); + if (file_exists(buffer)) { + break; + } + } + // release the storage + for (int i = 0; i < n; i++) { + if (pelements[i] != NULL) { + FREE_C_HEAP_ARRAY(char, pelements[i], mtInternal); + } + } + if (pelements != NULL) { + FREE_C_HEAP_ARRAY(char*, pelements, mtInternal); + } + } else { + snprintf(buffer, buflen, "%s/lib%s.so", pname, fname); + } +} + +const char* os::get_current_directory(char *buf, int buflen) { + return getcwd(buf, buflen); +} + +// Check if addr is inside libjvm[_g].so. +bool os::address_is_in_vm(address addr) { + + // Input could be a real pc or a function pointer literal. The latter + // would be a function descriptor residing in the data segment of a module. + + const LoadedLibraryModule* lib = LoadedLibraries::find_for_text_address(addr); + if (lib) { + if (strcmp(lib->get_shortname(), "libjvm.so") == 0) { + return true; + } else { + return false; + } + } else { + lib = LoadedLibraries::find_for_data_address(addr); + if (lib) { + if (strcmp(lib->get_shortname(), "libjvm.so") == 0) { + return true; + } else { + return false; + } + } else { + return false; + } + } +} + +// Resolve an AIX function descriptor literal to a code pointer. +// If the input is a valid code pointer to a text segment of a loaded module, +// it is returned unchanged. +// If the input is a valid AIX function descriptor, it is resolved to the +// code entry point. +// If the input is neither a valid function descriptor nor a valid code pointer, +// NULL is returned. +static address resolve_function_descriptor_to_code_pointer(address p) { + + const LoadedLibraryModule* lib = LoadedLibraries::find_for_text_address(p); + if (lib) { + // its a real code pointer + return p; + } else { + lib = LoadedLibraries::find_for_data_address(p); + if (lib) { + // pointer to data segment, potential function descriptor + address code_entry = (address)(((FunctionDescriptor*)p)->entry()); + if (LoadedLibraries::find_for_text_address(code_entry)) { + // Its a function descriptor + return code_entry; + } + } + } + return NULL; +} + +bool os::dll_address_to_function_name(address addr, char *buf, + int buflen, int *offset) { + if (offset) { + *offset = -1; + } + if (buf) { + buf[0] = '\0'; + } + + // Resolve function ptr literals first. + addr = resolve_function_descriptor_to_code_pointer(addr); + if (!addr) { + return false; + } + + // Go through Decoder::decode to call getFuncName which reads the name from the traceback table. + return Decoder::decode(addr, buf, buflen, offset); +} + +static int getModuleName(codeptr_t pc, // [in] program counter + char* p_name, size_t namelen, // [out] optional: function name + char* p_errmsg, size_t errmsglen // [out] optional: user provided buffer for error messages + ) { + + // initialize output parameters + if (p_name && namelen > 0) { + *p_name = '\0'; + } + if (p_errmsg && errmsglen > 0) { + *p_errmsg = '\0'; + } + + const LoadedLibraryModule* const lib = LoadedLibraries::find_for_text_address((address)pc); + if (lib) { + if (p_name && namelen > 0) { + sprintf(p_name, "%.*s", namelen, lib->get_shortname()); + } + return 0; + } + + if (Verbose) { + fprintf(stderr, "pc outside any module"); + } + + return -1; + +} + +bool os::dll_address_to_library_name(address addr, char* buf, + int buflen, int* offset) { + if (offset) { + *offset = -1; + } + if (buf) { + buf[0] = '\0'; + } + + // Resolve function ptr literals first. + addr = resolve_function_descriptor_to_code_pointer(addr); + if (!addr) { + return false; + } + + if (::getModuleName((codeptr_t) addr, buf, buflen, 0, 0) == 0) { + return true; + } + return false; +} + +// Loads .dll/.so and in case of error it checks if .dll/.so was built +// for the same architecture as Hotspot is running on +void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { + + if (ebuf && ebuflen > 0) { + ebuf[0] = '\0'; + ebuf[ebuflen - 1] = '\0'; + } + + if (!filename || strlen(filename) == 0) { + ::strncpy(ebuf, "dll_load: empty filename specified", ebuflen - 1); + return NULL; + } + + // RTLD_LAZY is currently not implemented. The dl is loaded immediately with all its dependants. + void * result= ::dlopen(filename, RTLD_LAZY); + if (result != NULL) { + // Reload dll cache. Don't do this in signal handling. + LoadedLibraries::reload(); + return result; + } else { + // error analysis when dlopen fails + const char* const error_report = ::dlerror(); + if (error_report && ebuf && ebuflen > 0) { + snprintf(ebuf, ebuflen - 1, "%s, LIBPATH=%s, LD_LIBRARY_PATH=%s : %s", + filename, ::getenv("LIBPATH"), ::getenv("LD_LIBRARY_PATH"), error_report); + } + } + return NULL; +} + +// Glibc-2.0 libdl is not MT safe. If you are building with any glibc, +// chances are you might want to run the generated bits against glibc-2.0 +// libdl.so, so always use locking for any version of glibc. +void* os::dll_lookup(void* handle, const char* name) { + pthread_mutex_lock(&dl_mutex); + void* res = dlsym(handle, name); + pthread_mutex_unlock(&dl_mutex); + return res; +} + +void os::print_dll_info(outputStream *st) { + st->print_cr("Dynamic libraries:"); + LoadedLibraries::print(st); +} + +void os::print_os_info(outputStream* st) { + st->print("OS:"); + + st->print("uname:"); + struct utsname name; + uname(&name); + st->print(name.sysname); st->print(" "); + st->print(name.nodename); st->print(" "); + st->print(name.release); st->print(" "); + st->print(name.version); st->print(" "); + st->print(name.machine); + st->cr(); + + // rlimit + st->print("rlimit:"); + struct rlimit rlim; + + st->print(" STACK "); + getrlimit(RLIMIT_STACK, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + st->print(", CORE "); + getrlimit(RLIMIT_CORE, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + st->print(", NPROC "); + st->print("%d", sysconf(_SC_CHILD_MAX)); + + st->print(", NOFILE "); + getrlimit(RLIMIT_NOFILE, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%d", rlim.rlim_cur); + + st->print(", AS "); + getrlimit(RLIMIT_AS, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + + // Print limits on DATA, because it limits the C-heap. + st->print(", DATA "); + getrlimit(RLIMIT_DATA, &rlim); + if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); + else st->print("%uk", rlim.rlim_cur >> 10); + st->cr(); + + // load average + st->print("load average:"); + double loadavg[3] = {-1.L, -1.L, -1.L}; + os::loadavg(loadavg, 3); + st->print("%0.02f %0.02f %0.02f", loadavg[0], loadavg[1], loadavg[2]); + st->cr(); +} + +void os::print_memory_info(outputStream* st) { + + st->print_cr("Memory:"); + + st->print_cr(" default page size: %s", describe_pagesize(os::vm_page_size())); + st->print_cr(" default stack page size: %s", describe_pagesize(os::vm_page_size())); + st->print_cr(" default shm page size: %s", describe_pagesize(os::Aix::shm_default_page_size())); + st->print_cr(" can use 64K pages dynamically: %s", (os::Aix::can_use_64K_pages() ? "yes" :"no")); + st->print_cr(" can use 16M pages dynamically: %s", (os::Aix::can_use_16M_pages() ? "yes" :"no")); + if (g_multipage_error != 0) { + st->print_cr(" multipage error: %d", g_multipage_error); + } + + // print out LDR_CNTRL because it affects the default page sizes + const char* const ldr_cntrl = ::getenv("LDR_CNTRL"); + st->print_cr(" LDR_CNTRL=%s.", ldr_cntrl ? ldr_cntrl : ""); + + const char* const extshm = ::getenv("EXTSHM"); + st->print_cr(" EXTSHM=%s.", extshm ? extshm : ""); + + // Call os::Aix::get_meminfo() to retrieve memory statistics. + os::Aix::meminfo_t mi; + if (os::Aix::get_meminfo(&mi)) { + char buffer[256]; + if (os::Aix::on_aix()) { + jio_snprintf(buffer, sizeof(buffer), + " physical total : %llu\n" + " physical free : %llu\n" + " swap total : %llu\n" + " swap free : %llu\n", + mi.real_total, + mi.real_free, + mi.pgsp_total, + mi.pgsp_free); + } else { + Unimplemented(); + } + st->print_raw(buffer); + } else { + st->print_cr(" (no more information available)"); + } +} + +void os::pd_print_cpu_info(outputStream* st) { + // cpu + st->print("CPU:"); + st->print("total %d", os::processor_count()); + // It's not safe to query number of active processors after crash + // st->print("(active %d)", os::active_processor_count()); + st->print(" %s", VM_Version::cpu_features()); + st->cr(); +} + +void os::print_siginfo(outputStream* st, void* siginfo) { + // Use common posix version. + os::Posix::print_siginfo_brief(st, (const siginfo_t*) siginfo); + st->cr(); +} + + +static void print_signal_handler(outputStream* st, int sig, + char* buf, size_t buflen); + +void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { + st->print_cr("Signal Handlers:"); + print_signal_handler(st, SIGSEGV, buf, buflen); + print_signal_handler(st, SIGBUS , buf, buflen); + print_signal_handler(st, SIGFPE , buf, buflen); + print_signal_handler(st, SIGPIPE, buf, buflen); + print_signal_handler(st, SIGXFSZ, buf, buflen); + print_signal_handler(st, SIGILL , buf, buflen); + print_signal_handler(st, INTERRUPT_SIGNAL, buf, buflen); + print_signal_handler(st, SR_signum, buf, buflen); + print_signal_handler(st, SHUTDOWN1_SIGNAL, buf, buflen); + print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen); + print_signal_handler(st, SHUTDOWN3_SIGNAL , buf, buflen); + print_signal_handler(st, BREAK_SIGNAL, buf, buflen); + print_signal_handler(st, SIGTRAP, buf, buflen); + print_signal_handler(st, SIGDANGER, buf, buflen); +} + +static char saved_jvm_path[MAXPATHLEN] = {0}; + +// Find the full path to the current module, libjvm.so or libjvm_g.so +void os::jvm_path(char *buf, jint buflen) { + // Error checking. + if (buflen < MAXPATHLEN) { + assert(false, "must use a large-enough buffer"); + buf[0] = '\0'; + return; + } + // Lazy resolve the path to current module. + if (saved_jvm_path[0] != 0) { + strcpy(buf, saved_jvm_path); + return; + } + + Dl_info dlinfo; + int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo); + assert(ret != 0, "cannot locate libjvm"); + char* rp = realpath((char *)dlinfo.dli_fname, buf); + assert(rp != NULL, "error in realpath(): maybe the 'path' argument is too long?"); + + strcpy(saved_jvm_path, buf); +} + +void os::print_jni_name_prefix_on(outputStream* st, int args_size) { + // no prefix required, not even "_" +} + +void os::print_jni_name_suffix_on(outputStream* st, int args_size) { + // no suffix required +} + +//////////////////////////////////////////////////////////////////////////////// +// sun.misc.Signal support + +static volatile jint sigint_count = 0; + +static void +UserHandler(int sig, void *siginfo, void *context) { + // 4511530 - sem_post is serialized and handled by the manager thread. When + // the program is interrupted by Ctrl-C, SIGINT is sent to every thread. We + // don't want to flood the manager thread with sem_post requests. + if (sig == SIGINT && Atomic::add(1, &sigint_count) > 1) + return; + + // Ctrl-C is pressed during error reporting, likely because the error + // handler fails to abort. Let VM die immediately. + if (sig == SIGINT && is_error_reported()) { + os::die(); + } + + os::signal_notify(sig); +} + +void* os::user_handler() { + return CAST_FROM_FN_PTR(void*, UserHandler); +} + +extern "C" { + typedef void (*sa_handler_t)(int); + typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +} + +void* os::signal(int signal_number, void* handler) { + struct sigaction sigAct, oldSigAct; + + sigfillset(&(sigAct.sa_mask)); + + // Do not block out synchronous signals in the signal handler. + // Blocking synchronous signals only makes sense if you can really + // be sure that those signals won't happen during signal handling, + // when the blocking applies. Normal signal handlers are lean and + // do not cause signals. But our signal handlers tend to be "risky" + // - secondary SIGSEGV, SIGILL, SIGBUS' may and do happen. + // On AIX, PASE there was a case where a SIGSEGV happened, followed + // by a SIGILL, which was blocked due to the signal mask. The process + // just hung forever. Better to crash from a secondary signal than to hang. + sigdelset(&(sigAct.sa_mask), SIGSEGV); + sigdelset(&(sigAct.sa_mask), SIGBUS); + sigdelset(&(sigAct.sa_mask), SIGILL); + sigdelset(&(sigAct.sa_mask), SIGFPE); + sigdelset(&(sigAct.sa_mask), SIGTRAP); + + sigAct.sa_flags = SA_RESTART|SA_SIGINFO; + + sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler); + + if (sigaction(signal_number, &sigAct, &oldSigAct)) { + // -1 means registration failed + return (void *)-1; + } + + return CAST_FROM_FN_PTR(void*, oldSigAct.sa_handler); +} + +void os::signal_raise(int signal_number) { + ::raise(signal_number); +} + +// +// The following code is moved from os.cpp for making this +// code platform specific, which it is by its very nature. +// + +// Will be modified when max signal is changed to be dynamic +int os::sigexitnum_pd() { + return NSIG; +} + +// a counter for each possible signal value +static volatile jint pending_signals[NSIG+1] = { 0 }; + +// Linux(POSIX) specific hand shaking semaphore. +static sem_t sig_sem; + +void os::signal_init_pd() { + // Initialize signal structures + ::memset((void*)pending_signals, 0, sizeof(pending_signals)); + + // Initialize signal semaphore + int rc = ::sem_init(&sig_sem, 0, 0); + guarantee(rc != -1, "sem_init failed"); +} + +void os::signal_notify(int sig) { + Atomic::inc(&pending_signals[sig]); + ::sem_post(&sig_sem); +} + +static int check_pending_signals(bool wait) { + Atomic::store(0, &sigint_count); + for (;;) { + for (int i = 0; i < NSIG + 1; i++) { + jint n = pending_signals[i]; + if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) { + return i; + } + } + if (!wait) { + return -1; + } + JavaThread *thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + + bool threadIsSuspended; + do { + thread->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + + ::sem_wait(&sig_sem); + + // were we externally suspended while we were waiting? + threadIsSuspended = thread->handle_special_suspend_equivalent_condition(); + if (threadIsSuspended) { + // + // The semaphore has been incremented, but while we were waiting + // another thread suspended us. We don't want to continue running + // while suspended because that would surprise the thread that + // suspended us. + // + ::sem_post(&sig_sem); + + thread->java_suspend_self(); + } + } while (threadIsSuspended); + } +} + +int os::signal_lookup() { + return check_pending_signals(false); +} + +int os::signal_wait() { + return check_pending_signals(true); +} + +//////////////////////////////////////////////////////////////////////////////// +// Virtual Memory + +// AddrRange describes an immutable address range +// +// This is a helper class for the 'shared memory bookkeeping' below. +class AddrRange { + friend class ShmBkBlock; + + char* _start; + size_t _size; + +public: + + AddrRange(char* start, size_t size) + : _start(start), _size(size) + {} + + AddrRange(const AddrRange& r) + : _start(r.start()), _size(r.size()) + {} + + char* start() const { return _start; } + size_t size() const { return _size; } + char* end() const { return _start + _size; } + bool is_empty() const { return _size == 0 ? true : false; } + + static AddrRange empty_range() { return AddrRange(NULL, 0); } + + bool contains(const char* p) const { + return start() <= p && end() > p; + } + + bool contains(const AddrRange& range) const { + return start() <= range.start() && end() >= range.end(); + } + + bool intersects(const AddrRange& range) const { + return (range.start() <= start() && range.end() > start()) || + (range.start() < end() && range.end() >= end()) || + contains(range); + } + + bool is_same_range(const AddrRange& range) const { + return start() == range.start() && size() == range.size(); + } + + // return the closest inside range consisting of whole pages + AddrRange find_closest_aligned_range(size_t pagesize) const { + if (pagesize == 0 || is_empty()) { + return empty_range(); + } + char* const from = (char*)align_size_up((intptr_t)_start, pagesize); + char* const to = (char*)align_size_down((intptr_t)end(), pagesize); + if (from > to) { + return empty_range(); + } + return AddrRange(from, to - from); + } +}; + +//////////////////////////////////////////////////////////////////////////// +// shared memory bookkeeping +// +// the os::reserve_memory() API and friends hand out different kind of memory, depending +// on need and circumstances. Memory may be allocated with mmap() or with shmget/shmat. +// +// But these memory types have to be treated differently. For example, to uncommit +// mmap-based memory, msync(MS_INVALIDATE) is needed, to uncommit shmat-based memory, +// disclaim64() is needed. +// +// Therefore we need to keep track of the allocated memory segments and their +// properties. + +// ShmBkBlock: base class for all blocks in the shared memory bookkeeping +class ShmBkBlock { + + ShmBkBlock* _next; + +protected: + + AddrRange _range; + const size_t _pagesize; + const bool _pinned; + +public: + + ShmBkBlock(AddrRange range, size_t pagesize, bool pinned) + : _range(range), _pagesize(pagesize), _pinned(pinned) , _next(NULL) { + + assert(_pagesize == SIZE_4K || _pagesize == SIZE_64K || _pagesize == SIZE_16M, "invalid page size"); + assert(!_range.is_empty(), "invalid range"); + } + + virtual void print(outputStream* st) const { + st->print("0x%p ... 0x%p (%llu) - %d %s pages - %s", + _range.start(), _range.end(), _range.size(), + _range.size() / _pagesize, describe_pagesize(_pagesize), + _pinned ? "pinned" : ""); + } + + enum Type { MMAP, SHMAT }; + virtual Type getType() = 0; + + char* base() const { return _range.start(); } + size_t size() const { return _range.size(); } + + void setAddrRange(AddrRange range) { + _range = range; + } + + bool containsAddress(const char* p) const { + return _range.contains(p); + } + + bool containsRange(const char* p, size_t size) const { + return _range.contains(AddrRange((char*)p, size)); + } + + bool isSameRange(const char* p, size_t size) const { + return _range.is_same_range(AddrRange((char*)p, size)); + } + + virtual bool disclaim(char* p, size_t size) = 0; + virtual bool release() = 0; + + // blocks live in a list. + ShmBkBlock* next() const { return _next; } + void set_next(ShmBkBlock* blk) { _next = blk; } + +}; // end: ShmBkBlock + + +// ShmBkMappedBlock: describes an block allocated with mmap() +class ShmBkMappedBlock : public ShmBkBlock { +public: + + ShmBkMappedBlock(AddrRange range) + : ShmBkBlock(range, SIZE_4K, false) {} // mmap: always 4K, never pinned + + void print(outputStream* st) const { + ShmBkBlock::print(st); + st->print_cr(" - mmap'ed"); + } + + Type getType() { + return MMAP; + } + + bool disclaim(char* p, size_t size) { + + AddrRange r(p, size); + + guarantee(_range.contains(r), "invalid disclaim"); + + // only disclaim whole ranges. + const AddrRange r2 = r.find_closest_aligned_range(_pagesize); + if (r2.is_empty()) { + return true; + } + + const int rc = ::msync(r2.start(), r2.size(), MS_INVALIDATE); + + if (rc != 0) { + warning("msync(0x%p, %llu, MS_INVALIDATE) failed (%d)\n", r2.start(), r2.size(), errno); + } + + return rc == 0 ? true : false; + } + + bool release() { + // mmap'ed blocks are released using munmap + if (::munmap(_range.start(), _range.size()) != 0) { + warning("munmap(0x%p, %llu) failed (%d)\n", _range.start(), _range.size(), errno); + return false; + } + return true; + } +}; // end: ShmBkMappedBlock + +// ShmBkShmatedBlock: describes an block allocated with shmget/shmat() +class ShmBkShmatedBlock : public ShmBkBlock { +public: + + ShmBkShmatedBlock(AddrRange range, size_t pagesize, bool pinned) + : ShmBkBlock(range, pagesize, pinned) {} + + void print(outputStream* st) const { + ShmBkBlock::print(st); + st->print_cr(" - shmat'ed"); + } + + Type getType() { + return SHMAT; + } + + bool disclaim(char* p, size_t size) { + + AddrRange r(p, size); + + if (_pinned) { + return true; + } + + // shmat'ed blocks are disclaimed using disclaim64 + guarantee(_range.contains(r), "invalid disclaim"); + + // only disclaim whole ranges. + const AddrRange r2 = r.find_closest_aligned_range(_pagesize); + if (r2.is_empty()) { + return true; + } + + const bool rc = my_disclaim64(r2.start(), r2.size()); + + if (Verbose && !rc) { + warning("failed to disclaim shm %p-%p\n", r2.start(), r2.end()); + } + + return rc; + } + + bool release() { + bool rc = false; + if (::shmdt(_range.start()) != 0) { + warning("shmdt(0x%p) failed (%d)\n", _range.start(), errno); + } else { + rc = true; + } + return rc; + } + +}; // end: ShmBkShmatedBlock + +static ShmBkBlock* g_shmbk_list = NULL; +static volatile jint g_shmbk_table_lock = 0; + +// keep some usage statistics +static struct { + int nodes; // number of nodes in list + size_t bytes; // reserved - not committed - bytes. + int reserves; // how often reserve was called + int lookups; // how often a lookup was made +} g_shmbk_stats = { 0, 0, 0, 0 }; + +// add information about a shared memory segment to the bookkeeping +static void shmbk_register(ShmBkBlock* p_block) { + guarantee(p_block, "logic error"); + p_block->set_next(g_shmbk_list); + g_shmbk_list = p_block; + g_shmbk_stats.reserves ++; + g_shmbk_stats.bytes += p_block->size(); + g_shmbk_stats.nodes ++; +} + +// remove information about a shared memory segment by its starting address +static void shmbk_unregister(ShmBkBlock* p_block) { + ShmBkBlock* p = g_shmbk_list; + ShmBkBlock* prev = NULL; + while (p) { + if (p == p_block) { + if (prev) { + prev->set_next(p->next()); + } else { + g_shmbk_list = p->next(); + } + g_shmbk_stats.nodes --; + g_shmbk_stats.bytes -= p->size(); + return; + } + prev = p; + p = p->next(); + } + assert(false, "should not happen"); +} + +// given a pointer, return shared memory bookkeeping record for the segment it points into +// using the returned block info must happen under lock protection +static ShmBkBlock* shmbk_find_by_containing_address(const char* addr) { + g_shmbk_stats.lookups ++; + ShmBkBlock* p = g_shmbk_list; + while (p) { + if (p->containsAddress(addr)) { + return p; + } + p = p->next(); + } + return NULL; +} + +// dump all information about all memory segments allocated with os::reserve_memory() +void shmbk_dump_info() { + tty->print_cr("-- shared mem bookkeeping (alive: %d segments, %llu bytes, " + "total reserves: %d total lookups: %d)", + g_shmbk_stats.nodes, g_shmbk_stats.bytes, g_shmbk_stats.reserves, g_shmbk_stats.lookups); + const ShmBkBlock* p = g_shmbk_list; + int i = 0; + while (p) { + p->print(tty); + p = p->next(); + i ++; + } +} + +#define LOCK_SHMBK { ThreadCritical _LOCK_SHMBK; +#define UNLOCK_SHMBK } + +// End: shared memory bookkeeping +//////////////////////////////////////////////////////////////////////////////////////////////////// + +int os::vm_page_size() { + // Seems redundant as all get out + assert(os::Aix::page_size() != -1, "must call os::init"); + return os::Aix::page_size(); +} + +// Aix allocates memory by pages. +int os::vm_allocation_granularity() { + assert(os::Aix::page_size() != -1, "must call os::init"); + return os::Aix::page_size(); +} + +#ifdef PRODUCT +static void warn_fail_commit_memory(char* addr, size_t size, bool exec, + int err) { + warning("INFO: os::commit_memory(" PTR_FORMAT ", " SIZE_FORMAT + ", %d) failed; error='%s' (errno=%d)", addr, size, exec, + strerror(err), err); +} +#endif + +void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, + const char* mesg) { + assert(mesg != NULL, "mesg must be specified"); + if (!pd_commit_memory(addr, size, exec)) { + // add extra info in product mode for vm_exit_out_of_memory(): + PRODUCT_ONLY(warn_fail_commit_memory(addr, size, exec, errno);) + vm_exit_out_of_memory(size, mesg); + } +} + +bool os::pd_commit_memory(char* addr, size_t size, bool exec) { + + // Commit is a noop. There is no explicit commit + // needed on AIX. Memory is committed when touched. + // + // Debug : check address range for validity +#ifdef ASSERT + LOCK_SHMBK + ShmBkBlock* const block = shmbk_find_by_containing_address(addr); + if (!block) { + fprintf(stderr, "invalid pointer: " INTPTR_FORMAT "\n", addr); + shmbk_dump_info(); + assert(false, "invalid pointer"); + return false; + } else if (!block->containsRange(addr, size)) { + fprintf(stderr, "invalid range: " INTPTR_FORMAT " .. " INTPTR_FORMAT "\n", addr, addr + size); + shmbk_dump_info(); + assert(false, "invalid range"); + return false; + } + UNLOCK_SHMBK +#endif // ASSERT + + return true; +} + +bool os::pd_commit_memory(char* addr, size_t size, size_t alignment_hint, bool exec) { + return pd_commit_memory(addr, size, exec); +} + +void os::pd_commit_memory_or_exit(char* addr, size_t size, + size_t alignment_hint, bool exec, + const char* mesg) { + // alignment_hint is ignored on this OS + pd_commit_memory_or_exit(addr, size, exec, mesg); +} + +bool os::pd_uncommit_memory(char* addr, size_t size) { + + // Delegate to ShmBkBlock class which knows how to uncommit its memory. + + bool rc = false; + LOCK_SHMBK + ShmBkBlock* const block = shmbk_find_by_containing_address(addr); + if (!block) { + fprintf(stderr, "invalid pointer: 0x%p.\n", addr); + shmbk_dump_info(); + assert(false, "invalid pointer"); + return false; + } else if (!block->containsRange(addr, size)) { + fprintf(stderr, "invalid range: 0x%p .. 0x%p.\n", addr, addr + size); + shmbk_dump_info(); + assert(false, "invalid range"); + return false; + } + rc = block->disclaim(addr, size); + UNLOCK_SHMBK + + if (Verbose && !rc) { + warning("failed to disclaim 0x%p .. 0x%p (0x%llX bytes).", addr, addr + size, size); + } + return rc; +} + +bool os::pd_create_stack_guard_pages(char* addr, size_t size) { + return os::guard_memory(addr, size); +} + +bool os::remove_stack_guard_pages(char* addr, size_t size) { + return os::unguard_memory(addr, size); +} + +void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) { +} + +void os::pd_free_memory(char *addr, size_t bytes, size_t alignment_hint) { +} + +void os::numa_make_global(char *addr, size_t bytes) { +} + +void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) { +} + +bool os::numa_topology_changed() { + return false; +} + +size_t os::numa_get_groups_num() { + return 1; +} + +int os::numa_get_group_id() { + return 0; +} + +size_t os::numa_get_leaf_groups(int *ids, size_t size) { + if (size > 0) { + ids[0] = 0; + return 1; + } + return 0; +} + +bool os::get_page_info(char *start, page_info* info) { + return false; +} + +char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { + return end; +} + +// Flags for reserve_shmatted_memory: +#define RESSHM_WISHADDR_OR_FAIL 1 +#define RESSHM_TRY_16M_PAGES 2 +#define RESSHM_16M_PAGES_OR_FAIL 4 + +// Result of reserve_shmatted_memory: +struct shmatted_memory_info_t { + char* addr; + size_t pagesize; + bool pinned; +}; + +// Reserve a section of shmatted memory. +// params: +// bytes [in]: size of memory, in bytes +// requested_addr [in]: wish address. +// NULL = no wish. +// If RESSHM_WISHADDR_OR_FAIL is set in flags and wish address cannot +// be obtained, function will fail. Otherwise wish address is treated as hint and +// another pointer is returned. +// flags [in]: some flags. Valid flags are: +// RESSHM_WISHADDR_OR_FAIL - fail if wish address is given and cannot be obtained. +// RESSHM_TRY_16M_PAGES - try to allocate from 16M page pool +// (requires UseLargePages and Use16MPages) +// RESSHM_16M_PAGES_OR_FAIL - if you cannot allocate from 16M page pool, fail. +// Otherwise any other page size will do. +// p_info [out] : holds information about the created shared memory segment. +static bool reserve_shmatted_memory(size_t bytes, char* requested_addr, int flags, shmatted_memory_info_t* p_info) { + + assert(p_info, "parameter error"); + + // init output struct. + p_info->addr = NULL; + + // neither should we be here for EXTSHM=ON. + if (os::Aix::extshm()) { + ShouldNotReachHere(); + } + + // extract flags. sanity checks. + const bool wishaddr_or_fail = + flags & RESSHM_WISHADDR_OR_FAIL; + const bool try_16M_pages = + flags & RESSHM_TRY_16M_PAGES; + const bool f16M_pages_or_fail = + flags & RESSHM_16M_PAGES_OR_FAIL; + + // first check: if a wish address is given and it is mandatory, but not aligned to segment boundary, + // shmat will fail anyway, so save some cycles by failing right away + if (requested_addr && ((uintptr_t)requested_addr % SIZE_256M == 0)) { + if (wishaddr_or_fail) { + return false; + } else { + requested_addr = NULL; + } + } + + char* addr = NULL; + + // Align size of shm up to the largest possible page size, to avoid errors later on when we try to change + // pagesize dynamically. + const size_t size = align_size_up(bytes, SIZE_16M); + + // reserve the shared segment + int shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | S_IRUSR | S_IWUSR); + if (shmid == -1) { + warning("shmget(.., %lld, ..) failed (errno: %d).", size, errno); + return false; + } + + // Important note: + // It is very important that we, upon leaving this function, do not leave a shm segment alive. + // We must right after attaching it remove it from the system. System V shm segments are global and + // survive the process. + // So, from here on: Do not assert. Do not return. Always do a "goto cleanup_shm". + + // try forcing the page size + size_t pagesize = -1; // unknown so far + + if (UseLargePages) { + + struct shmid_ds shmbuf; + memset(&shmbuf, 0, sizeof(shmbuf)); + + // First, try to take from 16M page pool if... + if (os::Aix::can_use_16M_pages() // we can ... + && Use16MPages // we are not explicitly forbidden to do so (-XX:-Use16MPages).. + && try_16M_pages) { // caller wants us to. + shmbuf.shm_pagesize = SIZE_16M; + if (shmctl(shmid, SHM_PAGESIZE, &shmbuf) == 0) { + pagesize = SIZE_16M; + } else { + warning("Failed to allocate %d 16M pages. 16M page pool might be exhausted. (shmctl failed with %d)", + size / SIZE_16M, errno); + if (f16M_pages_or_fail) { + goto cleanup_shm; + } + } + } + + // Nothing yet? Try setting 64K pages. Note that I never saw this fail, but in theory it might, + // because the 64K page pool may also be exhausted. + if (pagesize == -1) { + shmbuf.shm_pagesize = SIZE_64K; + if (shmctl(shmid, SHM_PAGESIZE, &shmbuf) == 0) { + pagesize = SIZE_64K; + } else { + warning("Failed to allocate %d 64K pages. (shmctl failed with %d)", + size / SIZE_64K, errno); + // here I give up. leave page_size -1 - later, after attaching, we will query the + // real page size of the attached memory. (in theory, it may be something different + // from 4K if LDR_CNTRL SHM_PSIZE is set) + } + } + } + + // sanity point + assert(pagesize == -1 || pagesize == SIZE_16M || pagesize == SIZE_64K, "wrong page size"); + + // Now attach the shared segment. + addr = (char*) shmat(shmid, requested_addr, 0); + if (addr == (char*)-1) { + // How to handle attach failure: + // If it failed for a specific wish address, tolerate this: in that case, if wish address was + // mandatory, fail, if not, retry anywhere. + // If it failed for any other reason, treat that as fatal error. + addr = NULL; + if (requested_addr) { + if (wishaddr_or_fail) { + goto cleanup_shm; + } else { + addr = (char*) shmat(shmid, NULL, 0); + if (addr == (char*)-1) { // fatal + addr = NULL; + warning("shmat failed (errno: %d)", errno); + goto cleanup_shm; + } + } + } else { // fatal + addr = NULL; + warning("shmat failed (errno: %d)", errno); + goto cleanup_shm; + } + } + + // sanity point + assert(addr && addr != (char*) -1, "wrong address"); + + // after successful Attach remove the segment - right away. + if (::shmctl(shmid, IPC_RMID, NULL) == -1) { + warning("shmctl(%u, IPC_RMID) failed (%d)\n", shmid, errno); + guarantee(false, "failed to remove shared memory segment!"); + } + shmid = -1; + + // query the real page size. In case setting the page size did not work (see above), the system + // may have given us something other then 4K (LDR_CNTRL) + { + const size_t real_pagesize = os::Aix::query_pagesize(addr); + if (pagesize != -1) { + assert(pagesize == real_pagesize, "unexpected pagesize after shmat"); + } else { + pagesize = real_pagesize; + } + } + + // Now register the reserved block with internal book keeping. + LOCK_SHMBK + const bool pinned = pagesize >= SIZE_16M ? true : false; + ShmBkShmatedBlock* const p_block = new ShmBkShmatedBlock(AddrRange(addr, size), pagesize, pinned); + assert(p_block, ""); + shmbk_register(p_block); + UNLOCK_SHMBK + +cleanup_shm: + + // if we have not done so yet, remove the shared memory segment. This is very important. + if (shmid != -1) { + if (::shmctl(shmid, IPC_RMID, NULL) == -1) { + warning("shmctl(%u, IPC_RMID) failed (%d)\n", shmid, errno); + guarantee(false, "failed to remove shared memory segment!"); + } + shmid = -1; + } + + // trace + if (Verbose && !addr) { + if (requested_addr != NULL) { + warning("failed to shm-allocate 0x%llX bytes at with address 0x%p.", size, requested_addr); + } else { + warning("failed to shm-allocate 0x%llX bytes at any address.", size); + } + } + + // hand info to caller + if (addr) { + p_info->addr = addr; + p_info->pagesize = pagesize; + p_info->pinned = pagesize == SIZE_16M ? true : false; + } + + // sanity test: + if (requested_addr && addr && wishaddr_or_fail) { + guarantee(addr == requested_addr, "shmat error"); + } + + // just one more test to really make sure we have no dangling shm segments. + guarantee(shmid == -1, "dangling shm segments"); + + return addr ? true : false; + +} // end: reserve_shmatted_memory + +// Reserve memory using mmap. Behaves the same as reserve_shmatted_memory(): +// will return NULL in case of an error. +static char* reserve_mmaped_memory(size_t bytes, char* requested_addr) { + + // if a wish address is given, but not aligned to 4K page boundary, mmap will fail. + if (requested_addr && ((uintptr_t)requested_addr % os::vm_page_size() != 0)) { + warning("Wish address 0x%p not aligned to page boundary.", requested_addr); + return NULL; + } + + const size_t size = align_size_up(bytes, SIZE_4K); + + // Note: MAP_SHARED (instead of MAP_PRIVATE) needed to be able to + // msync(MS_INVALIDATE) (see os::uncommit_memory) + int flags = MAP_ANONYMOUS | MAP_SHARED; + + // MAP_FIXED is needed to enforce requested_addr - manpage is vague about what + // it means if wishaddress is given but MAP_FIXED is not set. + // + // Note however that this changes semantics in SPEC1170 mode insofar as MAP_FIXED + // clobbers the address range, which is probably not what the caller wants. That's + // why I assert here (again) that the SPEC1170 compat mode is off. + // If we want to be able to run under SPEC1170, we have to do some porting and + // testing. + if (requested_addr != NULL) { + assert(!os::Aix::xpg_sus_mode(), "SPEC1170 mode not allowed."); + flags |= MAP_FIXED; + } + + char* addr = (char*)::mmap(requested_addr, size, PROT_READ|PROT_WRITE|PROT_EXEC, flags, -1, 0); + + if (addr == MAP_FAILED) { + // attach failed: tolerate for specific wish addresses. Not being able to attach + // anywhere is a fatal error. + if (requested_addr == NULL) { + // It's ok to fail here if the machine has not enough memory. + warning("mmap(NULL, 0x%llX, ..) failed (%d)", size, errno); + } + addr = NULL; + goto cleanup_mmap; + } + + // If we did request a specific address and that address was not available, fail. + if (addr && requested_addr) { + guarantee(addr == requested_addr, "unexpected"); + } + + // register this mmap'ed segment with book keeping + LOCK_SHMBK + ShmBkMappedBlock* const p_block = new ShmBkMappedBlock(AddrRange(addr, size)); + assert(p_block, ""); + shmbk_register(p_block); + UNLOCK_SHMBK + +cleanup_mmap: + + if (addr) { + if (Verbose) { + fprintf(stderr, "mmap-allocated 0x%p .. 0x%p (0x%llX bytes)\n", addr, addr + bytes, bytes); + } + } + else { + if (requested_addr != NULL) { + warning("failed to mmap-allocate 0x%llX bytes at wish address 0x%p.", bytes, requested_addr); + } else { + warning("failed to mmap-allocate 0x%llX bytes at any address.", bytes); + } + } + + return addr; + +} // end: reserve_mmaped_memory + +// Reserves and attaches a shared memory segment. +// Will assert if a wish address is given and could not be obtained. +char* os::pd_reserve_memory(size_t bytes, char* requested_addr, size_t alignment_hint) { + return os::attempt_reserve_memory_at(bytes, requested_addr); +} + +bool os::pd_release_memory(char* addr, size_t size) { + + // delegate to ShmBkBlock class which knows how to uncommit its memory. + + bool rc = false; + LOCK_SHMBK + ShmBkBlock* const block = shmbk_find_by_containing_address(addr); + if (!block) { + fprintf(stderr, "invalid pointer: 0x%p.\n", addr); + shmbk_dump_info(); + assert(false, "invalid pointer"); + return false; + } + else if (!block->isSameRange(addr, size)) { + if (block->getType() == ShmBkBlock::MMAP) { + // Release only the same range or a the beginning or the end of a range. + if (block->base() == addr && size < block->size()) { + ShmBkMappedBlock* const b = new ShmBkMappedBlock(AddrRange(block->base() + size, block->size() - size)); + assert(b, ""); + shmbk_register(b); + block->setAddrRange(AddrRange(addr, size)); + } + else if (addr > block->base() && addr + size == block->base() + block->size()) { + ShmBkMappedBlock* const b = new ShmBkMappedBlock(AddrRange(block->base(), block->size() - size)); + assert(b, ""); + shmbk_register(b); + block->setAddrRange(AddrRange(addr, size)); + } + else { + fprintf(stderr, "invalid mmap range: 0x%p .. 0x%p.\n", addr, addr + size); + shmbk_dump_info(); + assert(false, "invalid mmap range"); + return false; + } + } + else { + // Release only the same range. No partial release allowed. + // Soften the requirement a bit, because the user may think he owns a smaller size + // than the block is due to alignment etc. + if (block->base() != addr || block->size() < size) { + fprintf(stderr, "invalid shmget range: 0x%p .. 0x%p.\n", addr, addr + size); + shmbk_dump_info(); + assert(false, "invalid shmget range"); + return false; + } + } + } + rc = block->release(); + assert(rc, "release failed"); + // remove block from bookkeeping + shmbk_unregister(block); + delete block; + UNLOCK_SHMBK + + if (!rc) { + warning("failed to released %lu bytes at 0x%p", size, addr); + } + + return rc; +} + +static bool checked_mprotect(char* addr, size_t size, int prot) { + + // Little problem here: if SPEC1170 behaviour is off, mprotect() on AIX will + // not tell me if protection failed when trying to protect an un-protectable range. + // + // This means if the memory was allocated using shmget/shmat, protection wont work + // but mprotect will still return 0: + // + // See http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/mprotect.htm + + bool rc = ::mprotect(addr, size, prot) == 0 ? true : false; + + if (!rc) { + const char* const s_errno = strerror(errno); + warning("mprotect(" PTR_FORMAT "-" PTR_FORMAT ", 0x%X) failed (%s).", addr, addr + size, prot, s_errno); + return false; + } + + // mprotect success check + // + // Mprotect said it changed the protection but can I believe it? + // + // To be sure I need to check the protection afterwards. Try to + // read from protected memory and check whether that causes a segfault. + // + if (!os::Aix::xpg_sus_mode()) { + + if (CanUseSafeFetch32()) { + + const bool read_protected = + (SafeFetch32((int*)addr, 0x12345678) == 0x12345678 && + SafeFetch32((int*)addr, 0x76543210) == 0x76543210) ? true : false; + + if (prot & PROT_READ) { + rc = !read_protected; + } else { + rc = read_protected; + } + } + } + if (!rc) { + assert(false, "mprotect failed."); + } + return rc; +} + +// Set protections specified +bool os::protect_memory(char* addr, size_t size, ProtType prot, bool is_committed) { + unsigned int p = 0; + switch (prot) { + case MEM_PROT_NONE: p = PROT_NONE; break; + case MEM_PROT_READ: p = PROT_READ; break; + case MEM_PROT_RW: p = PROT_READ|PROT_WRITE; break; + case MEM_PROT_RWX: p = PROT_READ|PROT_WRITE|PROT_EXEC; break; + default: + ShouldNotReachHere(); + } + // is_committed is unused. + return checked_mprotect(addr, size, p); +} + +bool os::guard_memory(char* addr, size_t size) { + return checked_mprotect(addr, size, PROT_NONE); +} + +bool os::unguard_memory(char* addr, size_t size) { + return checked_mprotect(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC); +} + +// Large page support + +static size_t _large_page_size = 0; + +// Enable large page support if OS allows that. +void os::large_page_init() { + + // Note: os::Aix::query_multipage_support must run first. + + if (!UseLargePages) { + return; + } + + if (!Aix::can_use_64K_pages()) { + assert(!Aix::can_use_16M_pages(), "64K is a precondition for 16M."); + UseLargePages = false; + return; + } + + if (!Aix::can_use_16M_pages() && Use16MPages) { + fprintf(stderr, "Cannot use 16M pages. Please ensure that there is a 16M page pool " + " and that the VM runs with CAP_BYPASS_RAC_VMM and CAP_PROPAGATE capabilities.\n"); + } + + // Do not report 16M page alignment as part of os::_page_sizes if we are + // explicitly forbidden from using 16M pages. Doing so would increase the + // alignment the garbage collector calculates with, slightly increasing + // heap usage. We should only pay for 16M alignment if we really want to + // use 16M pages. + if (Use16MPages && Aix::can_use_16M_pages()) { + _large_page_size = SIZE_16M; + _page_sizes[0] = SIZE_16M; + _page_sizes[1] = SIZE_64K; + _page_sizes[2] = SIZE_4K; + _page_sizes[3] = 0; + } else if (Aix::can_use_64K_pages()) { + _large_page_size = SIZE_64K; + _page_sizes[0] = SIZE_64K; + _page_sizes[1] = SIZE_4K; + _page_sizes[2] = 0; + } + + if (Verbose) { + ("Default large page size is 0x%llX.", _large_page_size); + } +} // end: os::large_page_init() + +char* os::reserve_memory_special(size_t bytes, size_t alignment, char* req_addr, bool exec) { + // "exec" is passed in but not used. Creating the shared image for + // the code cache doesn't have an SHM_X executable permission to check. + Unimplemented(); + return 0; +} + +bool os::release_memory_special(char* base, size_t bytes) { + // detaching the SHM segment will also delete it, see reserve_memory_special() + Unimplemented(); + return false; +} + +size_t os::large_page_size() { + return _large_page_size; +} + +bool os::can_commit_large_page_memory() { + // Well, sadly we cannot commit anything at all (see comment in + // os::commit_memory) but we claim to so we can make use of large pages + return true; +} + +bool os::can_execute_large_page_memory() { + // We can do that + return true; +} + +// Reserve memory at an arbitrary address, only if that area is +// available (and not reserved for something else). +char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) { + + bool use_mmap = false; + + // mmap: smaller graining, no large page support + // shm: large graining (256M), large page support, limited number of shm segments + // + // Prefer mmap wherever we either do not need large page support or have OS limits + + if (!UseLargePages || bytes < SIZE_16M) { + use_mmap = true; + } + + char* addr = NULL; + if (use_mmap) { + addr = reserve_mmaped_memory(bytes, requested_addr); + } else { + // shmat: wish address is mandatory, and do not try 16M pages here. + shmatted_memory_info_t info; + const int flags = RESSHM_WISHADDR_OR_FAIL; + if (reserve_shmatted_memory(bytes, requested_addr, flags, &info)) { + addr = info.addr; + } + } + + return addr; +} + +size_t os::read(int fd, void *buf, unsigned int nBytes) { + return ::read(fd, buf, nBytes); +} + +#define NANOSECS_PER_MILLISEC 1000000 + +int os::sleep(Thread* thread, jlong millis, bool interruptible) { + assert(thread == Thread::current(), "thread consistency check"); + + // Prevent nasty overflow in deadline calculation + // by handling long sleeps similar to solaris or windows. + const jlong limit = INT_MAX; + int result; + while (millis > limit) { + if ((result = os::sleep(thread, limit, interruptible)) != OS_OK) { + return result; + } + millis -= limit; + } + + ParkEvent * const slp = thread->_SleepEvent; + slp->reset(); + OrderAccess::fence(); + + if (interruptible) { + jlong prevtime = javaTimeNanos(); + + // Prevent precision loss and too long sleeps + jlong deadline = prevtime + millis * NANOSECS_PER_MILLISEC; + + for (;;) { + if (os::is_interrupted(thread, true)) { + return OS_INTRPT; + } + + jlong newtime = javaTimeNanos(); + + assert(newtime >= prevtime, "time moving backwards"); + // Doing prevtime and newtime in microseconds doesn't help precision, + // and trying to round up to avoid lost milliseconds can result in a + // too-short delay. + millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC; + + if (millis <= 0) { + return OS_OK; + } + + // Stop sleeping if we passed the deadline + if (newtime >= deadline) { + return OS_OK; + } + + prevtime = newtime; + + { + assert(thread->is_Java_thread(), "sanity check"); + JavaThread *jt = (JavaThread *) thread; + ThreadBlockInVM tbivm(jt); + OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */); + + jt->set_suspend_equivalent(); + + slp->park(millis); + + // were we externally suspended while we were waiting? + jt->check_and_wait_while_suspended(); + } + } + } else { + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + jlong prevtime = javaTimeNanos(); + + // Prevent precision loss and too long sleeps + jlong deadline = prevtime + millis * NANOSECS_PER_MILLISEC; + + for (;;) { + // It'd be nice to avoid the back-to-back javaTimeNanos() calls on + // the 1st iteration ... + jlong newtime = javaTimeNanos(); + + if (newtime - prevtime < 0) { + // time moving backwards, should only happen if no monotonic clock + // not a guarantee() because JVM should not abort on kernel/glibc bugs + // - HS14 Commented out as not implemented. + // - TODO Maybe we should implement it? + //assert(!Aix::supports_monotonic_clock(), "time moving backwards"); + } else { + millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC; + } + + if (millis <= 0) break; + + if (newtime >= deadline) { + break; + } + + prevtime = newtime; + slp->park(millis); + } + return OS_OK; + } +} + +void os::naked_short_sleep(jlong ms) { + struct timespec req; + + assert(ms < 1000, "Un-interruptable sleep, short time use only"); + req.tv_sec = 0; + if (ms > 0) { + req.tv_nsec = (ms % 1000) * 1000000; + } + else { + req.tv_nsec = 1; + } + + nanosleep(&req, NULL); + + return; +} + +// Sleep forever; naked call to OS-specific sleep; use with CAUTION +void os::infinite_sleep() { + while (true) { // sleep forever ... + ::sleep(100); // ... 100 seconds at a time + } +} + +// Used to convert frequent JVM_Yield() to nops +bool os::dont_yield() { + return DontYieldALot; +} + +void os::yield() { + sched_yield(); +} + +os::YieldResult os::NakedYield() { sched_yield(); return os::YIELD_UNKNOWN; } + +void os::yield_all(int attempts) { + // Yields to all threads, including threads with lower priorities + // Threads on Linux are all with same priority. The Solaris style + // os::yield_all() with nanosleep(1ms) is not necessary. + sched_yield(); +} + +// Called from the tight loops to possibly influence time-sharing heuristics +void os::loop_breaker(int attempts) { + os::yield_all(attempts); +} + +//////////////////////////////////////////////////////////////////////////////// +// thread priority support + +// From AIX manpage to pthread_setschedparam +// (see: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp? +// topic=/com.ibm.aix.basetechref/doc/basetrf1/pthread_setschedparam.htm): +// +// "If schedpolicy is SCHED_OTHER, then sched_priority must be in the +// range from 40 to 80, where 40 is the least favored priority and 80 +// is the most favored." +// +// (Actually, I doubt this even has an impact on AIX, as we do kernel +// scheduling there; however, this still leaves iSeries.) +// +// We use the same values for AIX and PASE. +int os::java_to_os_priority[CriticalPriority + 1] = { + 54, // 0 Entry should never be used + + 55, // 1 MinPriority + 55, // 2 + 56, // 3 + + 56, // 4 + 57, // 5 NormPriority + 57, // 6 + + 58, // 7 + 58, // 8 + 59, // 9 NearMaxPriority + + 60, // 10 MaxPriority + + 60 // 11 CriticalPriority +}; + +OSReturn os::set_native_priority(Thread* thread, int newpri) { + if (!UseThreadPriorities) return OS_OK; + pthread_t thr = thread->osthread()->pthread_id(); + int policy = SCHED_OTHER; + struct sched_param param; + param.sched_priority = newpri; + int ret = pthread_setschedparam(thr, policy, ¶m); + + if (Verbose) { + if (ret == 0) { + fprintf(stderr, "changed priority of thread %d to %d\n", (int)thr, newpri); + } else { + fprintf(stderr, "Could not changed priority for thread %d to %d (error %d, %s)\n", + (int)thr, newpri, ret, strerror(ret)); + } + } + return (ret == 0) ? OS_OK : OS_ERR; +} + +OSReturn os::get_native_priority(const Thread* const thread, int *priority_ptr) { + if (!UseThreadPriorities) { + *priority_ptr = java_to_os_priority[NormPriority]; + return OS_OK; + } + pthread_t thr = thread->osthread()->pthread_id(); + int policy = SCHED_OTHER; + struct sched_param param; + int ret = pthread_getschedparam(thr, &policy, ¶m); + *priority_ptr = param.sched_priority; + + return (ret == 0) ? OS_OK : OS_ERR; +} + +// Hint to the underlying OS that a task switch would not be good. +// Void return because it's a hint and can fail. +void os::hint_no_preempt() {} + +//////////////////////////////////////////////////////////////////////////////// +// suspend/resume support + +// the low-level signal-based suspend/resume support is a remnant from the +// old VM-suspension that used to be for java-suspension, safepoints etc, +// within hotspot. Now there is a single use-case for this: +// - calling get_thread_pc() on the VMThread by the flat-profiler task +// that runs in the watcher thread. +// The remaining code is greatly simplified from the more general suspension +// code that used to be used. +// +// The protocol is quite simple: +// - suspend: +// - sends a signal to the target thread +// - polls the suspend state of the osthread using a yield loop +// - target thread signal handler (SR_handler) sets suspend state +// and blocks in sigsuspend until continued +// - resume: +// - sets target osthread state to continue +// - sends signal to end the sigsuspend loop in the SR_handler +// +// Note that the SR_lock plays no role in this suspend/resume protocol. +// + +static void resume_clear_context(OSThread *osthread) { + osthread->set_ucontext(NULL); + osthread->set_siginfo(NULL); +} + +static void suspend_save_context(OSThread *osthread, siginfo_t* siginfo, ucontext_t* context) { + osthread->set_ucontext(context); + osthread->set_siginfo(siginfo); +} + +// +// Handler function invoked when a thread's execution is suspended or +// resumed. We have to be careful that only async-safe functions are +// called here (Note: most pthread functions are not async safe and +// should be avoided.) +// +// Note: sigwait() is a more natural fit than sigsuspend() from an +// interface point of view, but sigwait() prevents the signal hander +// from being run. libpthread would get very confused by not having +// its signal handlers run and prevents sigwait()'s use with the +// mutex granting granting signal. +// +// Currently only ever called on the VMThread and JavaThreads (PC sampling). +// +static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { + // Save and restore errno to avoid confusing native code with EINTR + // after sigsuspend. + int old_errno = errno; + + Thread* thread = Thread::current(); + OSThread* osthread = thread->osthread(); + assert(thread->is_VM_thread() || thread->is_Java_thread(), "Must be VMThread or JavaThread"); + + os::SuspendResume::State current = osthread->sr.state(); + if (current == os::SuspendResume::SR_SUSPEND_REQUEST) { + suspend_save_context(osthread, siginfo, context); + + // attempt to switch the state, we assume we had a SUSPEND_REQUEST + os::SuspendResume::State state = osthread->sr.suspended(); + if (state == os::SuspendResume::SR_SUSPENDED) { + sigset_t suspend_set; // signals for sigsuspend() + + // get current set of blocked signals and unblock resume signal + pthread_sigmask(SIG_BLOCK, NULL, &suspend_set); + sigdelset(&suspend_set, SR_signum); + + // wait here until we are resumed + while (1) { + sigsuspend(&suspend_set); + + os::SuspendResume::State result = osthread->sr.running(); + if (result == os::SuspendResume::SR_RUNNING) { + break; + } + } + + } else if (state == os::SuspendResume::SR_RUNNING) { + // request was cancelled, continue + } else { + ShouldNotReachHere(); + } + + resume_clear_context(osthread); + } else if (current == os::SuspendResume::SR_RUNNING) { + // request was cancelled, continue + } else if (current == os::SuspendResume::SR_WAKEUP_REQUEST) { + // ignore + } else { + ShouldNotReachHere(); + } + + errno = old_errno; +} + + +static int SR_initialize() { + struct sigaction act; + char *s; + // Get signal number to use for suspend/resume + if ((s = ::getenv("_JAVA_SR_SIGNUM")) != 0) { + int sig = ::strtol(s, 0, 10); + if (sig > 0 || sig < NSIG) { + SR_signum = sig; + } + } + + assert(SR_signum > SIGSEGV && SR_signum > SIGBUS, + "SR_signum must be greater than max(SIGSEGV, SIGBUS), see 4355769"); + + sigemptyset(&SR_sigset); + sigaddset(&SR_sigset, SR_signum); + + // Set up signal handler for suspend/resume. + act.sa_flags = SA_RESTART|SA_SIGINFO; + act.sa_handler = (void (*)(int)) SR_handler; + + // SR_signum is blocked by default. + // 4528190 - We also need to block pthread restart signal (32 on all + // supported Linux platforms). Note that LinuxThreads need to block + // this signal for all threads to work properly. So we don't have + // to use hard-coded signal number when setting up the mask. + pthread_sigmask(SIG_BLOCK, NULL, &act.sa_mask); + + if (sigaction(SR_signum, &act, 0) == -1) { + return -1; + } + + // Save signal flag + os::Aix::set_our_sigflags(SR_signum, act.sa_flags); + return 0; +} + +static int SR_finalize() { + return 0; +} + +static int sr_notify(OSThread* osthread) { + int status = pthread_kill(osthread->pthread_id(), SR_signum); + assert_status(status == 0, status, "pthread_kill"); + return status; +} + +// "Randomly" selected value for how long we want to spin +// before bailing out on suspending a thread, also how often +// we send a signal to a thread we want to resume +static const int RANDOMLY_LARGE_INTEGER = 1000000; +static const int RANDOMLY_LARGE_INTEGER2 = 100; + +// returns true on success and false on error - really an error is fatal +// but this seems the normal response to library errors +static bool do_suspend(OSThread* osthread) { + assert(osthread->sr.is_running(), "thread should be running"); + // mark as suspended and send signal + + if (osthread->sr.request_suspend() != os::SuspendResume::SR_SUSPEND_REQUEST) { + // failed to switch, state wasn't running? + ShouldNotReachHere(); + return false; + } + + if (sr_notify(osthread) != 0) { + // try to cancel, switch to running + + os::SuspendResume::State result = osthread->sr.cancel_suspend(); + if (result == os::SuspendResume::SR_RUNNING) { + // cancelled + return false; + } else if (result == os::SuspendResume::SR_SUSPENDED) { + // somehow managed to suspend + return true; + } else { + ShouldNotReachHere(); + return false; + } + } + + // managed to send the signal and switch to SUSPEND_REQUEST, now wait for SUSPENDED + + for (int n = 0; !osthread->sr.is_suspended(); n++) { + for (int i = 0; i < RANDOMLY_LARGE_INTEGER2 && !osthread->sr.is_suspended(); i++) { + os::yield_all(i); + } + + // timeout, try to cancel the request + if (n >= RANDOMLY_LARGE_INTEGER) { + os::SuspendResume::State cancelled = osthread->sr.cancel_suspend(); + if (cancelled == os::SuspendResume::SR_RUNNING) { + return false; + } else if (cancelled == os::SuspendResume::SR_SUSPENDED) { + return true; + } else { + ShouldNotReachHere(); + return false; + } + } + } + + guarantee(osthread->sr.is_suspended(), "Must be suspended"); + return true; +} + +static void do_resume(OSThread* osthread) { + //assert(osthread->sr.is_suspended(), "thread should be suspended"); + + if (osthread->sr.request_wakeup() != os::SuspendResume::SR_WAKEUP_REQUEST) { + // failed to switch to WAKEUP_REQUEST + ShouldNotReachHere(); + return; + } + + while (!osthread->sr.is_running()) { + if (sr_notify(osthread) == 0) { + for (int n = 0; n < RANDOMLY_LARGE_INTEGER && !osthread->sr.is_running(); n++) { + for (int i = 0; i < 100 && !osthread->sr.is_running(); i++) { + os::yield_all(i); + } + } + } else { + ShouldNotReachHere(); + } + } + + guarantee(osthread->sr.is_running(), "Must be running!"); +} + +//////////////////////////////////////////////////////////////////////////////// +// interrupt support + +void os::interrupt(Thread* thread) { + assert(Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + + if (!osthread->interrupted()) { + osthread->set_interrupted(true); + // More than one thread can get here with the same value of osthread, + // resulting in multiple notifications. We do, however, want the store + // to interrupted() to be visible to other threads before we execute unpark(). + OrderAccess::fence(); + ParkEvent * const slp = thread->_SleepEvent; + if (slp != NULL) slp->unpark(); + } + + // For JSR166. Unpark even if interrupt status already was set + if (thread->is_Java_thread()) + ((JavaThread*)thread)->parker()->unpark(); + + ParkEvent * ev = thread->_ParkEvent; + if (ev != NULL) ev->unpark(); + +} + +bool os::is_interrupted(Thread* thread, bool clear_interrupted) { + assert(Thread::current() == thread || Threads_lock->owned_by_self(), + "possibility of dangling Thread pointer"); + + OSThread* osthread = thread->osthread(); + + bool interrupted = osthread->interrupted(); + + if (interrupted && clear_interrupted) { + osthread->set_interrupted(false); + // consider thread->_SleepEvent->reset() ... optional optimization + } + + return interrupted; +} + +/////////////////////////////////////////////////////////////////////////////////// +// signal handling (except suspend/resume) + +// This routine may be used by user applications as a "hook" to catch signals. +// The user-defined signal handler must pass unrecognized signals to this +// routine, and if it returns true (non-zero), then the signal handler must +// return immediately. If the flag "abort_if_unrecognized" is true, then this +// routine will never retun false (zero), but instead will execute a VM panic +// routine kill the process. +// +// If this routine returns false, it is OK to call it again. This allows +// the user-defined signal handler to perform checks either before or after +// the VM performs its own checks. Naturally, the user code would be making +// a serious error if it tried to handle an exception (such as a null check +// or breakpoint) that the VM was generating for its own correct operation. +// +// This routine may recognize any of the following kinds of signals: +// SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGQUIT, SIGPIPE, SIGXFSZ, SIGUSR1. +// It should be consulted by handlers for any of those signals. +// +// The caller of this routine must pass in the three arguments supplied +// to the function referred to in the "sa_sigaction" (not the "sa_handler") +// field of the structure passed to sigaction(). This routine assumes that +// the sa_flags field passed to sigaction() includes SA_SIGINFO and SA_RESTART. +// +// Note that the VM will print warnings if it detects conflicting signal +// handlers, unless invoked with the option "-XX:+AllowUserSignalHandlers". +// +extern "C" JNIEXPORT int +JVM_handle_aix_signal(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized); + +// Set thread signal mask (for some reason on AIX sigthreadmask() seems +// to be the thing to call; documentation is not terribly clear about whether +// pthread_sigmask also works, and if it does, whether it does the same. +bool set_thread_signal_mask(int how, const sigset_t* set, sigset_t* oset) { + const int rc = ::pthread_sigmask(how, set, oset); + // return value semantics differ slightly for error case: + // pthread_sigmask returns error number, sigthreadmask -1 and sets global errno + // (so, pthread_sigmask is more theadsafe for error handling) + // But success is always 0. + return rc == 0 ? true : false; +} + +// Function to unblock all signals which are, according +// to POSIX, typical program error signals. If they happen while being blocked, +// they typically will bring down the process immediately. +bool unblock_program_error_signals() { + sigset_t set; + ::sigemptyset(&set); + ::sigaddset(&set, SIGILL); + ::sigaddset(&set, SIGBUS); + ::sigaddset(&set, SIGFPE); + ::sigaddset(&set, SIGSEGV); + return set_thread_signal_mask(SIG_UNBLOCK, &set, NULL); +} + +// Renamed from 'signalHandler' to avoid collision with other shared libs. +void javaSignalHandler(int sig, siginfo_t* info, void* uc) { + assert(info != NULL && uc != NULL, "it must be old kernel"); + + // Never leave program error signals blocked; + // on all our platforms they would bring down the process immediately when + // getting raised while being blocked. + unblock_program_error_signals(); + + JVM_handle_aix_signal(sig, info, uc, true); +} + + +// This boolean allows users to forward their own non-matching signals +// to JVM_handle_aix_signal, harmlessly. +bool os::Aix::signal_handlers_are_installed = false; + +// For signal-chaining +struct sigaction os::Aix::sigact[MAXSIGNUM]; +unsigned int os::Aix::sigs = 0; +bool os::Aix::libjsig_is_loaded = false; +typedef struct sigaction *(*get_signal_t)(int); +get_signal_t os::Aix::get_signal_action = NULL; + +struct sigaction* os::Aix::get_chained_signal_action(int sig) { + struct sigaction *actp = NULL; + + if (libjsig_is_loaded) { + // Retrieve the old signal handler from libjsig + actp = (*get_signal_action)(sig); + } + if (actp == NULL) { + // Retrieve the preinstalled signal handler from jvm + actp = get_preinstalled_handler(sig); + } + + return actp; +} + +static bool call_chained_handler(struct sigaction *actp, int sig, + siginfo_t *siginfo, void *context) { + Unimplemented(); + return true; +} + +bool os::Aix::chained_handler(int sig, siginfo_t* siginfo, void* context) { + bool chained = false; + // signal-chaining + if (UseSignalChaining) { + struct sigaction *actp = get_chained_signal_action(sig); + if (actp != NULL) { + chained = call_chained_handler(actp, sig, siginfo, context); + } + } + return chained; +} + +struct sigaction* os::Aix::get_preinstalled_handler(int sig) { + if ((((unsigned int)1 << sig) & sigs) != 0) { + return &sigact[sig]; + } + return NULL; +} + +void os::Aix::save_preinstalled_handler(int sig, struct sigaction& oldAct) { + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + sigact[sig] = oldAct; + sigs |= (unsigned int)1 << sig; +} + +// for diagnostic +int os::Aix::sigflags[MAXSIGNUM]; + +int os::Aix::get_our_sigflags(int sig) { + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + return sigflags[sig]; +} + +void os::Aix::set_our_sigflags(int sig, int flags) { + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + sigflags[sig] = flags; +} + +void os::Aix::set_signal_handler(int sig, bool set_installed) { + // Check for overwrite. + struct sigaction oldAct; + sigaction(sig, (struct sigaction*)NULL, &oldAct); + + void* oldhand = oldAct.sa_sigaction + ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + // Renamed 'signalHandler' to avoid collision with other shared libs. + if (oldhand != CAST_FROM_FN_PTR(void*, SIG_DFL) && + oldhand != CAST_FROM_FN_PTR(void*, SIG_IGN) && + oldhand != CAST_FROM_FN_PTR(void*, (sa_sigaction_t)javaSignalHandler)) { + if (AllowUserSignalHandlers || !set_installed) { + // Do not overwrite; user takes responsibility to forward to us. + return; + } else if (UseSignalChaining) { + // save the old handler in jvm + save_preinstalled_handler(sig, oldAct); + // libjsig also interposes the sigaction() call below and saves the + // old sigaction on it own. + } else { + fatal(err_msg("Encountered unexpected pre-existing sigaction handler " + "%#lx for signal %d.", (long)oldhand, sig)); + } + } + + struct sigaction sigAct; + sigfillset(&(sigAct.sa_mask)); + if (!set_installed) { + sigAct.sa_handler = SIG_DFL; + sigAct.sa_flags = SA_RESTART; + } else { + // Renamed 'signalHandler' to avoid collision with other shared libs. + sigAct.sa_sigaction = javaSignalHandler; + sigAct.sa_flags = SA_SIGINFO|SA_RESTART; + } + // Save flags, which are set by ours + assert(sig > 0 && sig < MAXSIGNUM, "vm signal out of expected range"); + sigflags[sig] = sigAct.sa_flags; + + int ret = sigaction(sig, &sigAct, &oldAct); + assert(ret == 0, "check"); + + void* oldhand2 = oldAct.sa_sigaction + ? CAST_FROM_FN_PTR(void*, oldAct.sa_sigaction) + : CAST_FROM_FN_PTR(void*, oldAct.sa_handler); + assert(oldhand2 == oldhand, "no concurrent signal handler installation"); +} + +// install signal handlers for signals that HotSpot needs to +// handle in order to support Java-level exception handling. +void os::Aix::install_signal_handlers() { + if (!signal_handlers_are_installed) { + signal_handlers_are_installed = true; + + // signal-chaining + typedef void (*signal_setting_t)(); + signal_setting_t begin_signal_setting = NULL; + signal_setting_t end_signal_setting = NULL; + begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); + if (begin_signal_setting != NULL) { + end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); + get_signal_action = CAST_TO_FN_PTR(get_signal_t, + dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); + libjsig_is_loaded = true; + assert(UseSignalChaining, "should enable signal-chaining"); + } + if (libjsig_is_loaded) { + // Tell libjsig jvm is setting signal handlers + (*begin_signal_setting)(); + } + + set_signal_handler(SIGSEGV, true); + set_signal_handler(SIGPIPE, true); + set_signal_handler(SIGBUS, true); + set_signal_handler(SIGILL, true); + set_signal_handler(SIGFPE, true); + set_signal_handler(SIGTRAP, true); + set_signal_handler(SIGXFSZ, true); + set_signal_handler(SIGDANGER, true); + + if (libjsig_is_loaded) { + // Tell libjsig jvm finishes setting signal handlers + (*end_signal_setting)(); + } + + // We don't activate signal checker if libjsig is in place, we trust ourselves + // and if UserSignalHandler is installed all bets are off. + // Log that signal checking is off only if -verbose:jni is specified. + if (CheckJNICalls) { + if (libjsig_is_loaded) { + tty->print_cr("Info: libjsig is activated, all active signal checking is disabled"); + check_signals = false; + } + if (AllowUserSignalHandlers) { + tty->print_cr("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); + check_signals = false; + } + // need to initialize check_signal_done + ::sigemptyset(&check_signal_done); + } + } +} + +static const char* get_signal_handler_name(address handler, + char* buf, int buflen) { + int offset; + bool found = os::dll_address_to_library_name(handler, buf, buflen, &offset); + if (found) { + // skip directory names + const char *p1, *p2; + p1 = buf; + size_t len = strlen(os::file_separator()); + while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len; + // The way os::dll_address_to_library_name is implemented on Aix + // right now, it always returns -1 for the offset which is not + // terribly informative. + // Will fix that. For now, omit the offset. + jio_snprintf(buf, buflen, "%s", p1); + } else { + jio_snprintf(buf, buflen, PTR_FORMAT, handler); + } + return buf; +} + +static void print_signal_handler(outputStream* st, int sig, + char* buf, size_t buflen) { + struct sigaction sa; + sigaction(sig, NULL, &sa); + + st->print("%s: ", os::exception_name(sig, buf, buflen)); + + address handler = (sa.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) + : CAST_FROM_FN_PTR(address, sa.sa_handler); + + if (handler == CAST_FROM_FN_PTR(address, SIG_DFL)) { + st->print("SIG_DFL"); + } else if (handler == CAST_FROM_FN_PTR(address, SIG_IGN)) { + st->print("SIG_IGN"); + } else { + st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); + } + + // Print readable mask. + st->print(", sa_mask[0]="); + os::Posix::print_signal_set_short(st, &sa.sa_mask); + + address rh = VMError::get_resetted_sighandler(sig); + // May be, handler was resetted by VMError? + if (rh != NULL) { + handler = rh; + sa.sa_flags = VMError::get_resetted_sigflags(sig); + } + + // Print textual representation of sa_flags. + st->print(", sa_flags="); + os::Posix::print_sa_flags(st, sa.sa_flags); + + // Check: is it our handler? + if (handler == CAST_FROM_FN_PTR(address, (sa_sigaction_t)javaSignalHandler) || + handler == CAST_FROM_FN_PTR(address, (sa_sigaction_t)SR_handler)) { + // It is our signal handler. + // Check for flags, reset system-used one! + if ((int)sa.sa_flags != os::Aix::get_our_sigflags(sig)) { + st->print(", flags was changed from " PTR32_FORMAT ", consider using jsig library", + os::Aix::get_our_sigflags(sig)); + } + } + st->cr(); +} + + +#define DO_SIGNAL_CHECK(sig) \ + if (!sigismember(&check_signal_done, sig)) \ + os::Aix::check_signal_handler(sig) + +// This method is a periodic task to check for misbehaving JNI applications +// under CheckJNI, we can add any periodic checks here + +void os::run_periodic_checks() { + + if (check_signals == false) return; + + // SEGV and BUS if overridden could potentially prevent + // generation of hs*.log in the event of a crash, debugging + // such a case can be very challenging, so we absolutely + // check the following for a good measure: + DO_SIGNAL_CHECK(SIGSEGV); + DO_SIGNAL_CHECK(SIGILL); + DO_SIGNAL_CHECK(SIGFPE); + DO_SIGNAL_CHECK(SIGBUS); + DO_SIGNAL_CHECK(SIGPIPE); + DO_SIGNAL_CHECK(SIGXFSZ); + if (UseSIGTRAP) { + DO_SIGNAL_CHECK(SIGTRAP); + } + DO_SIGNAL_CHECK(SIGDANGER); + + // ReduceSignalUsage allows the user to override these handlers + // see comments at the very top and jvm_solaris.h + if (!ReduceSignalUsage) { + DO_SIGNAL_CHECK(SHUTDOWN1_SIGNAL); + DO_SIGNAL_CHECK(SHUTDOWN2_SIGNAL); + DO_SIGNAL_CHECK(SHUTDOWN3_SIGNAL); + DO_SIGNAL_CHECK(BREAK_SIGNAL); + } + + DO_SIGNAL_CHECK(SR_signum); + DO_SIGNAL_CHECK(INTERRUPT_SIGNAL); +} + +typedef int (*os_sigaction_t)(int, const struct sigaction *, struct sigaction *); + +static os_sigaction_t os_sigaction = NULL; + +void os::Aix::check_signal_handler(int sig) { + char buf[O_BUFLEN]; + address jvmHandler = NULL; + + struct sigaction act; + if (os_sigaction == NULL) { + // only trust the default sigaction, in case it has been interposed + os_sigaction = (os_sigaction_t)dlsym(RTLD_DEFAULT, "sigaction"); + if (os_sigaction == NULL) return; + } + + os_sigaction(sig, (struct sigaction*)NULL, &act); + + address thisHandler = (act.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, act.sa_sigaction) + : CAST_FROM_FN_PTR(address, act.sa_handler); + + + switch(sig) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGPIPE: + case SIGILL: + case SIGXFSZ: + // Renamed 'signalHandler' to avoid collision with other shared libs. + jvmHandler = CAST_FROM_FN_PTR(address, (sa_sigaction_t)javaSignalHandler); + break; + + case SHUTDOWN1_SIGNAL: + case SHUTDOWN2_SIGNAL: + case SHUTDOWN3_SIGNAL: + case BREAK_SIGNAL: + jvmHandler = (address)user_handler(); + break; + + case INTERRUPT_SIGNAL: + jvmHandler = CAST_FROM_FN_PTR(address, SIG_DFL); + break; + + default: + if (sig == SR_signum) { + jvmHandler = CAST_FROM_FN_PTR(address, (sa_sigaction_t)SR_handler); + } else { + return; + } + break; + } + + if (thisHandler != jvmHandler) { + tty->print("Warning: %s handler ", exception_name(sig, buf, O_BUFLEN)); + tty->print("expected:%s", get_signal_handler_name(jvmHandler, buf, O_BUFLEN)); + tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN)); + // No need to check this sig any longer + sigaddset(&check_signal_done, sig); + } else if (os::Aix::get_our_sigflags(sig) != 0 && (int)act.sa_flags != os::Aix::get_our_sigflags(sig)) { + tty->print("Warning: %s handler flags ", exception_name(sig, buf, O_BUFLEN)); + tty->print("expected:" PTR32_FORMAT, os::Aix::get_our_sigflags(sig)); + tty->print_cr(" found:" PTR32_FORMAT, act.sa_flags); + // No need to check this sig any longer + sigaddset(&check_signal_done, sig); + } + + // Dump all the signal + if (sigismember(&check_signal_done, sig)) { + print_signal_handlers(tty, buf, O_BUFLEN); + } +} + +extern bool signal_name(int signo, char* buf, size_t len); + +const char* os::exception_name(int exception_code, char* buf, size_t size) { + if (0 < exception_code && exception_code <= SIGRTMAX) { + // signal + if (!signal_name(exception_code, buf, size)) { + jio_snprintf(buf, size, "SIG%d", exception_code); + } + return buf; + } else { + return NULL; + } +} + +// To install functions for atexit system call +extern "C" { + static void perfMemory_exit_helper() { + perfMemory_exit(); + } +} + +// This is called _before_ the most of global arguments have been parsed. +void os::init(void) { + // This is basic, we want to know if that ever changes. + // (shared memory boundary is supposed to be a 256M aligned) + assert(SHMLBA == ((uint64_t)0x10000000ULL)/*256M*/, "unexpected"); + + // First off, we need to know whether we run on AIX or PASE, and + // the OS level we run on. + os::Aix::initialize_os_info(); + + // Scan environment (SPEC1170 behaviour, etc) + os::Aix::scan_environment(); + + // Check which pages are supported by AIX. + os::Aix::query_multipage_support(); + + // Next, we need to initialize libo4 and libperfstat libraries. + if (os::Aix::on_pase()) { + os::Aix::initialize_libo4(); + } else { + os::Aix::initialize_libperfstat(); + } + + // Reset the perfstat information provided by ODM. + if (os::Aix::on_aix()) { + libperfstat::perfstat_reset(); + } + + // Now initialze basic system properties. Note that for some of the values we + // need libperfstat etc. + os::Aix::initialize_system_info(); + + // Initialize large page support. + if (UseLargePages) { + os::large_page_init(); + if (!UseLargePages) { + // initialize os::_page_sizes + _page_sizes[0] = Aix::page_size(); + _page_sizes[1] = 0; + if (Verbose) { + fprintf(stderr, "Large Page initialization failed: setting UseLargePages=0.\n"); + } + } + } else { + // initialize os::_page_sizes + _page_sizes[0] = Aix::page_size(); + _page_sizes[1] = 0; + } + + // debug trace + if (Verbose) { + fprintf(stderr, "os::vm_page_size 0x%llX\n", os::vm_page_size()); + fprintf(stderr, "os::large_page_size 0x%llX\n", os::large_page_size()); + fprintf(stderr, "os::_page_sizes = ( "); + for (int i = 0; _page_sizes[i]; i ++) { + fprintf(stderr, " %s ", describe_pagesize(_page_sizes[i])); + } + fprintf(stderr, ")\n"); + } + + _initial_pid = getpid(); + + clock_tics_per_sec = sysconf(_SC_CLK_TCK); + + init_random(1234567); + + ThreadCritical::initialize(); + + // Main_thread points to the aboriginal thread. + Aix::_main_thread = pthread_self(); + + initial_time_count = os::elapsed_counter(); + pthread_mutex_init(&dl_mutex, NULL); +} + +// this is called _after_ the global arguments have been parsed +jint os::init_2(void) { + + if (Verbose) { + fprintf(stderr, "processor count: %d\n", os::_processor_count); + fprintf(stderr, "physical memory: %lu\n", Aix::_physical_memory); + } + + // initially build up the loaded dll map + LoadedLibraries::reload(); + + const int page_size = Aix::page_size(); + const int map_size = page_size; + + address map_address = (address) MAP_FAILED; + const int prot = PROT_READ; + const int flags = MAP_PRIVATE|MAP_ANONYMOUS; + + // use optimized addresses for the polling page, + // e.g. map it to a special 32-bit address. + if (OptimizePollingPageLocation) { + // architecture-specific list of address wishes: + address address_wishes[] = { + // AIX: addresses lower than 0x30000000 don't seem to work on AIX. + // PPC64: all address wishes are non-negative 32 bit values where + // the lower 16 bits are all zero. we can load these addresses + // with a single ppc_lis instruction. + (address) 0x30000000, (address) 0x31000000, + (address) 0x32000000, (address) 0x33000000, + (address) 0x40000000, (address) 0x41000000, + (address) 0x42000000, (address) 0x43000000, + (address) 0x50000000, (address) 0x51000000, + (address) 0x52000000, (address) 0x53000000, + (address) 0x60000000, (address) 0x61000000, + (address) 0x62000000, (address) 0x63000000 + }; + int address_wishes_length = sizeof(address_wishes)/sizeof(address); + + // iterate over the list of address wishes: + for (int i=0; i %p\n", + address_wishes[i], map_address + (ssize_t)page_size); + } + + if (map_address + (ssize_t)page_size == address_wishes[i]) { + // map succeeded and map_address is at wished address, exit loop. + break; + } + + if (map_address != (address) MAP_FAILED) { + // map succeeded, but polling_page is not at wished address, unmap and continue. + ::munmap(map_address, map_size); + map_address = (address) MAP_FAILED; + } + // map failed, continue loop. + } + } // end OptimizePollingPageLocation + + if (map_address == (address) MAP_FAILED) { + map_address = (address) ::mmap(NULL, map_size, prot, flags, -1, 0); + } + guarantee(map_address != MAP_FAILED, "os::init_2: failed to allocate polling page"); + os::set_polling_page(map_address); + + if (!UseMembar) { + address mem_serialize_page = (address) ::mmap(NULL, Aix::page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + guarantee(mem_serialize_page != NULL, "mmap Failed for memory serialize page"); + os::set_memory_serialize_page(mem_serialize_page); + +#ifndef PRODUCT + if (Verbose && PrintMiscellaneous) + tty->print("[Memory Serialize Page address: " INTPTR_FORMAT "]\n", (intptr_t)mem_serialize_page); +#endif + } + + // initialize suspend/resume support - must do this before signal_sets_init() + if (SR_initialize() != 0) { + perror("SR_initialize failed"); + return JNI_ERR; + } + + Aix::signal_sets_init(); + Aix::install_signal_handlers(); + + // Check minimum allowable stack size for thread creation and to initialize + // the java system classes, including StackOverflowError - depends on page + // size. Add a page for compiler2 recursion in main thread. + // Add in 2*BytesPerWord times page size to account for VM stack during + // class initialization depending on 32 or 64 bit VM. + os::Aix::min_stack_allowed = MAX2(os::Aix::min_stack_allowed, + (size_t)(StackYellowPages+StackRedPages+StackShadowPages + + 2*BytesPerWord COMPILER2_PRESENT(+1)) * Aix::page_size()); + + size_t threadStackSizeInBytes = ThreadStackSize * K; + if (threadStackSizeInBytes != 0 && + threadStackSizeInBytes < os::Aix::min_stack_allowed) { + tty->print_cr("\nThe stack size specified is too small, " + "Specify at least %dk", + os::Aix::min_stack_allowed / K); + return JNI_ERR; + } + + // Make the stack size a multiple of the page size so that + // the yellow/red zones can be guarded. + // note that this can be 0, if no default stacksize was set + JavaThread::set_stack_size_at_create(round_to(threadStackSizeInBytes, vm_page_size())); + + Aix::libpthread_init(); + + if (MaxFDLimit) { + // set the number of file descriptors to max. print out error + // if getrlimit/setrlimit fails but continue regardless. + struct rlimit nbr_files; + int status = getrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) + perror("os::init_2 getrlimit failed"); + } else { + nbr_files.rlim_cur = nbr_files.rlim_max; + status = setrlimit(RLIMIT_NOFILE, &nbr_files); + if (status != 0) { + if (PrintMiscellaneous && (Verbose || WizardMode)) + perror("os::init_2 setrlimit failed"); + } + } + } + + if (PerfAllowAtExitRegistration) { + // only register atexit functions if PerfAllowAtExitRegistration is set. + // atexit functions can be delayed until process exit time, which + // can be problematic for embedded VM situations. Embedded VMs should + // call DestroyJavaVM() to assure that VM resources are released. + + // note: perfMemory_exit_helper atexit function may be removed in + // the future if the appropriate cleanup code can be added to the + // VM_Exit VMOperation's doit method. + if (atexit(perfMemory_exit_helper) != 0) { + warning("os::init_2 atexit(perfMemory_exit_helper) failed"); + } + } + + return JNI_OK; +} + +// this is called at the end of vm_initialization +void os::init_3(void) { + return; +} + +// Mark the polling page as unreadable +void os::make_polling_page_unreadable(void) { + if (!guard_memory((char*)_polling_page, Aix::page_size())) { + fatal("Could not disable polling page"); + } +}; + +// Mark the polling page as readable +void os::make_polling_page_readable(void) { + // Changed according to os_linux.cpp. + if (!checked_mprotect((char *)_polling_page, Aix::page_size(), PROT_READ)) { + fatal(err_msg("Could not enable polling page at " PTR_FORMAT, _polling_page)); + } +}; + +int os::active_processor_count() { + int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); + assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); + return online_cpus; +} + +void os::set_native_thread_name(const char *name) { + // Not yet implemented. + return; +} + +bool os::distribute_processes(uint length, uint* distribution) { + // Not yet implemented. + return false; +} + +bool os::bind_to_processor(uint processor_id) { + // Not yet implemented. + return false; +} + +void os::SuspendedThreadTask::internal_do_task() { + if (do_suspend(_thread->osthread())) { + SuspendedThreadTaskContext context(_thread, _thread->osthread()->ucontext()); + do_task(context); + do_resume(_thread->osthread()); + } +} + +class PcFetcher : public os::SuspendedThreadTask { +public: + PcFetcher(Thread* thread) : os::SuspendedThreadTask(thread) {} + ExtendedPC result(); +protected: + void do_task(const os::SuspendedThreadTaskContext& context); +private: + ExtendedPC _epc; +}; + +ExtendedPC PcFetcher::result() { + guarantee(is_done(), "task is not done yet."); + return _epc; +} + +void PcFetcher::do_task(const os::SuspendedThreadTaskContext& context) { + Thread* thread = context.thread(); + OSThread* osthread = thread->osthread(); + if (osthread->ucontext() != NULL) { + _epc = os::Aix::ucontext_get_pc((ucontext_t *) context.ucontext()); + } else { + // NULL context is unexpected, double-check this is the VMThread. + guarantee(thread->is_VM_thread(), "can only be called for VMThread"); + } +} + +// Suspends the target using the signal mechanism and then grabs the PC before +// resuming the target. Used by the flat-profiler only +ExtendedPC os::get_thread_pc(Thread* thread) { + // Make sure that it is called by the watcher for the VMThread. + assert(Thread::current()->is_Watcher_thread(), "Must be watcher"); + assert(thread->is_VM_thread(), "Can only be called for VMThread"); + + PcFetcher fetcher(thread); + fetcher.run(); + return fetcher.result(); +} + +// Not neede on Aix. +// int os::Aix::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime) { +// } + +//////////////////////////////////////////////////////////////////////////////// +// debug support + +static address same_page(address x, address y) { + intptr_t page_bits = -os::vm_page_size(); + if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) + return x; + else if (x > y) + return (address)(intptr_t(y) | ~page_bits) + 1; + else + return (address)(intptr_t(y) & page_bits); +} + +bool os::find(address addr, outputStream* st) { + Unimplemented(); + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// misc + +// This does not do anything on Aix. This is basically a hook for being +// able to use structured exception handling (thread-local exception filters) +// on, e.g., Win32. +void +os::os_exception_wrapper(java_call_t f, JavaValue* value, methodHandle* method, + JavaCallArguments* args, Thread* thread) { + f(value, method, args, thread); +} + +void os::print_statistics() { +} + +int os::message_box(const char* title, const char* message) { + int i; + fdStream err(defaultStream::error_fd()); + for (i = 0; i < 78; i++) err.print_raw("="); + err.cr(); + err.print_raw_cr(title); + for (i = 0; i < 78; i++) err.print_raw("-"); + err.cr(); + err.print_raw_cr(message); + for (i = 0; i < 78; i++) err.print_raw("="); + err.cr(); + + char buf[16]; + // Prevent process from exiting upon "read error" without consuming all CPU + while (::read(0, buf, sizeof(buf)) <= 0) { ::sleep(100); } + + return buf[0] == 'y' || buf[0] == 'Y'; +} + +int os::stat(const char *path, struct stat *sbuf) { + char pathbuf[MAX_PATH]; + if (strlen(path) > MAX_PATH - 1) { + errno = ENAMETOOLONG; + return -1; + } + os::native_path(strcpy(pathbuf, path)); + return ::stat(pathbuf, sbuf); +} + +bool os::check_heap(bool force) { + return true; +} + +// int local_vsnprintf(char* buf, size_t count, const char* format, va_list args) { +// return ::vsnprintf(buf, count, format, args); +// } + +// Is a (classpath) directory empty? +bool os::dir_is_empty(const char* path) { + Unimplemented(); + return false; +} + +// This code originates from JDK's sysOpen and open64_w +// from src/solaris/hpi/src/system_md.c + +#ifndef O_DELETE +#define O_DELETE 0x10000 +#endif + +// Open a file. Unlink the file immediately after open returns +// if the specified oflag has the O_DELETE flag set. +// O_DELETE is used only in j2se/src/share/native/java/util/zip/ZipFile.c + +int os::open(const char *path, int oflag, int mode) { + + if (strlen(path) > MAX_PATH - 1) { + errno = ENAMETOOLONG; + return -1; + } + int fd; + int o_delete = (oflag & O_DELETE); + oflag = oflag & ~O_DELETE; + + fd = ::open64(path, oflag, mode); + if (fd == -1) return -1; + + //If the open succeeded, the file might still be a directory + { + struct stat64 buf64; + int ret = ::fstat64(fd, &buf64); + int st_mode = buf64.st_mode; + + if (ret != -1) { + if ((st_mode & S_IFMT) == S_IFDIR) { + errno = EISDIR; + ::close(fd); + return -1; + } + } else { + ::close(fd); + return -1; + } + } + + // All file descriptors that are opened in the JVM and not + // specifically destined for a subprocess should have the + // close-on-exec flag set. If we don't set it, then careless 3rd + // party native code might fork and exec without closing all + // appropriate file descriptors (e.g. as we do in closeDescriptors in + // UNIXProcess.c), and this in turn might: + // + // - cause end-of-file to fail to be detected on some file + // descriptors, resulting in mysterious hangs, or + // + // - might cause an fopen in the subprocess to fail on a system + // suffering from bug 1085341. + // + // (Yes, the default setting of the close-on-exec flag is a Unix + // design flaw.) + // + // See: + // 1085341: 32-bit stdio routines should support file descriptors >255 + // 4843136: (process) pipe file descriptor from Runtime.exec not being closed + // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9 +#ifdef FD_CLOEXEC + { + int flags = ::fcntl(fd, F_GETFD); + if (flags != -1) + ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } +#endif + + if (o_delete != 0) { + ::unlink(path); + } + return fd; +} + + +// create binary file, rewriting existing file if required +int os::create_binary_file(const char* path, bool rewrite_existing) { + Unimplemented(); + return 0; +} + +// return current position of file pointer +jlong os::current_file_offset(int fd) { + return (jlong)::lseek64(fd, (off64_t)0, SEEK_CUR); +} + +// move file pointer to the specified offset +jlong os::seek_to_file_offset(int fd, jlong offset) { + return (jlong)::lseek64(fd, (off64_t)offset, SEEK_SET); +} + +// This code originates from JDK's sysAvailable +// from src/solaris/hpi/src/native_threads/src/sys_api_td.c + +int os::available(int fd, jlong *bytes) { + jlong cur, end; + int mode; + struct stat64 buf64; + + if (::fstat64(fd, &buf64) >= 0) { + mode = buf64.st_mode; + if (S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { + // XXX: is the following call interruptible? If so, this might + // need to go through the INTERRUPT_IO() wrapper as for other + // blocking, interruptible calls in this file. + int n; + if (::ioctl(fd, FIONREAD, &n) >= 0) { + *bytes = n; + return 1; + } + } + } + if ((cur = ::lseek64(fd, 0L, SEEK_CUR)) == -1) { + return 0; + } else if ((end = ::lseek64(fd, 0L, SEEK_END)) == -1) { + return 0; + } else if (::lseek64(fd, cur, SEEK_SET) == -1) { + return 0; + } + *bytes = end - cur; + return 1; +} + +int os::socket_available(int fd, jint *pbytes) { + // Linux doc says EINTR not returned, unlike Solaris + int ret = ::ioctl(fd, FIONREAD, pbytes); + + //%% note ioctl can return 0 when successful, JVM_SocketAvailable + // is expected to return 0 on failure and 1 on success to the jdk. + return (ret < 0) ? 0 : 1; +} + +// Map a block of memory. +char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + Unimplemented(); + return NULL; +} + + +// Remap a block of memory. +char* os::pd_remap_memory(int fd, const char* file_name, size_t file_offset, + char *addr, size_t bytes, bool read_only, + bool allow_exec) { + // same as map_memory() on this OS + return os::map_memory(fd, file_name, file_offset, addr, bytes, read_only, + allow_exec); +} + +// Unmap a block of memory. +bool os::pd_unmap_memory(char* addr, size_t bytes) { + return munmap(addr, bytes) == 0; +} + +// current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) +// are used by JVM M&M and JVMTI to get user+sys or user CPU time +// of a thread. +// +// current_thread_cpu_time() and thread_cpu_time(Thread*) returns +// the fast estimate available on the platform. + +jlong os::current_thread_cpu_time() { + // return user + sys since the cost is the same + const jlong n = os::thread_cpu_time(Thread::current(), true /* user + sys */); + assert(n >= 0, "negative CPU time"); + return n; +} + +jlong os::thread_cpu_time(Thread* thread) { + // consistent with what current_thread_cpu_time() returns + const jlong n = os::thread_cpu_time(thread, true /* user + sys */); + assert(n >= 0, "negative CPU time"); + return n; +} + +jlong os::current_thread_cpu_time(bool user_sys_cpu_time) { + const jlong n = os::thread_cpu_time(Thread::current(), user_sys_cpu_time); + assert(n >= 0, "negative CPU time"); + return n; +} + +static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong* p_user_time) { + bool error = false; + + jlong sys_time = 0; + jlong user_time = 0; + + // reimplemented using getthrds64(). + // + // goes like this: + // For the thread in question, get the kernel thread id. Then get the + // kernel thread statistics using that id. + // + // This only works of course when no pthread scheduling is used, + // ie there is a 1:1 relationship to kernel threads. + // On AIX, see AIXTHREAD_SCOPE variable. + + pthread_t pthtid = thread->osthread()->pthread_id(); + + // retrieve kernel thread id for the pthread: + tid64_t tid = 0; + struct __pthrdsinfo pinfo; + // I just love those otherworldly IBM APIs which force me to hand down + // dummy buffers for stuff I dont care for... + char dummy[1]; + int dummy_size = sizeof(dummy); + if (pthread_getthrds_np(&pthtid, PTHRDSINFO_QUERY_TID, &pinfo, sizeof(pinfo), + dummy, &dummy_size) == 0) { + tid = pinfo.__pi_tid; + } else { + tty->print_cr("pthread_getthrds_np failed."); + error = true; + } + + // retrieve kernel timing info for that kernel thread + if (!error) { + struct thrdentry64 thrdentry; + if (getthrds64(getpid(), &thrdentry, sizeof(thrdentry), &tid, 1) == 1) { + sys_time = thrdentry.ti_ru.ru_stime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_stime.tv_usec * 1000LL; + user_time = thrdentry.ti_ru.ru_utime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_utime.tv_usec * 1000LL; + } else { + tty->print_cr("pthread_getthrds_np failed."); + error = true; + } + } + + if (p_sys_time) { + *p_sys_time = sys_time; + } + + if (p_user_time) { + *p_user_time = user_time; + } + + if (error) { + return false; + } + + return true; +} + +jlong os::thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { + jlong sys_time; + jlong user_time; + + if (!thread_cpu_time_unchecked(thread, &sys_time, &user_time)) { + return -1; + } + + return user_sys_cpu_time ? sys_time + user_time : user_time; +} + +void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->may_skip_backward = false; // elapsed time not wall time + info_ptr->may_skip_forward = false; // elapsed time not wall time + info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned +} + +void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { + info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->may_skip_backward = false; // elapsed time not wall time + info_ptr->may_skip_forward = false; // elapsed time not wall time + info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned +} + +bool os::is_thread_cpu_time_supported() { + return true; +} + +// System loadavg support. Returns -1 if load average cannot be obtained. +// For now just return the system wide load average (no processor sets). +int os::loadavg(double values[], int nelem) { + + // Implemented using libperfstat on AIX. + + guarantee(nelem >= 0 && nelem <= 3, "argument error"); + guarantee(values, "argument error"); + + if (os::Aix::on_pase()) { + Unimplemented(); + return -1; + } else { + // AIX: use libperfstat + // + // See also: + // http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/perfstat_cputot.htm + // /usr/include/libperfstat.h: + + // Use the already AIX version independent get_cpuinfo. + os::Aix::cpuinfo_t ci; + if (os::Aix::get_cpuinfo(&ci)) { + for (int i = 0; i < nelem; i++) { + values[i] = ci.loadavg[i]; + } + } else { + return -1; + } + return nelem; + } +} + +void os::pause() { + char filename[MAX_PATH]; + if (PauseAtStartupFile && PauseAtStartupFile[0]) { + jio_snprintf(filename, MAX_PATH, PauseAtStartupFile); + } else { + jio_snprintf(filename, MAX_PATH, "./vm.paused.%d", current_process_id()); + } + + int fd = ::open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + struct stat buf; + ::close(fd); + while (::stat(filename, &buf) == 0) { + (void)::poll(NULL, 0, 100); + } + } else { + jio_fprintf(stderr, + "Could not open pause file '%s', continuing immediately.\n", filename); + } +} + +bool os::Aix::is_primordial_thread() { + if (pthread_self() == (pthread_t)1) { + return true; + } else { + return false; + } +} + +// OS recognitions (PASE/AIX, OS level) call this before calling any +// one of Aix::on_pase(), Aix::os_version() static +void os::Aix::initialize_os_info() { + + assert(_on_pase == -1 && _os_version == -1, "already called."); + + struct utsname uts; + memset(&uts, 0, sizeof(uts)); + strcpy(uts.sysname, "?"); + if (::uname(&uts) == -1) { + fprintf(stderr, "uname failed (%d)\n", errno); + guarantee(0, "Could not determine whether we run on AIX or PASE"); + } else { + if (Verbose) { + fprintf(stderr,"uname says: sysname \"%s\" version \"%s\" release \"%s\" " + "node \"%s\" machine \"%s\"\n", + uts.sysname, uts.version, uts.release, uts.nodename, uts.machine); + } + const int major = atoi(uts.version); + assert(major > 0, "invalid OS version"); + const int minor = atoi(uts.release); + assert(minor > 0, "invalid OS release"); + _os_version = (major << 8) | minor; + if (strcmp(uts.sysname, "OS400") == 0) { + Unimplemented(); + } else if (strcmp(uts.sysname, "AIX") == 0) { + // We run on AIX. We do not support versions older than AIX 5.3. + _on_pase = 0; + if (_os_version < 0x0503) { + fprintf(stderr, "AIX release older than AIX 5.3 not supported.\n"); + assert(false, "AIX release too old."); + } else { + if (Verbose) { + fprintf(stderr, "We run on AIX %d.%d\n", major, minor); + } + } + } else { + assert(false, "unknown OS"); + } + } + + guarantee(_on_pase != -1 && _os_version, "Could not determine AIX/OS400 release"); + +} // end: os::Aix::initialize_os_info() + +// Scan environment for important settings which might effect the VM. +// Trace out settings. Warn about invalid settings and/or correct them. +// +// Must run after os::Aix::initialue_os_info(). +void os::Aix::scan_environment() { + + char* p; + int rc; + + // Warn explicity if EXTSHM=ON is used. That switch changes how + // System V shared memory behaves. One effect is that page size of + // shared memory cannot be change dynamically, effectivly preventing + // large pages from working. + // This switch was needed on AIX 32bit, but on AIX 64bit the general + // recommendation is (in OSS notes) to switch it off. + p = ::getenv("EXTSHM"); + if (Verbose) { + fprintf(stderr, "EXTSHM=%s.\n", p ? p : ""); + } + if (p && strcmp(p, "ON") == 0) { + fprintf(stderr, "Unsupported setting: EXTSHM=ON. Large Page support will be disabled.\n"); + _extshm = 1; + } else { + _extshm = 0; + } + + // SPEC1170 behaviour: will change the behaviour of a number of POSIX APIs. + // Not tested, not supported. + // + // Note that it might be worth the trouble to test and to require it, if only to + // get useful return codes for mprotect. + // + // Note: Setting XPG_SUS_ENV in the process is too late. Must be set earlier (before + // exec() ? before loading the libjvm ? ....) + p = ::getenv("XPG_SUS_ENV"); + if (Verbose) { + fprintf(stderr, "XPG_SUS_ENV=%s.\n", p ? p : ""); + } + if (p && strcmp(p, "ON") == 0) { + _xpg_sus_mode = 1; + fprintf(stderr, "Unsupported setting: XPG_SUS_ENV=ON\n"); + // This is not supported. Worst of all, it changes behaviour of mmap MAP_FIXED to + // clobber address ranges. If we ever want to support that, we have to do some + // testing first. + guarantee(false, "XPG_SUS_ENV=ON not supported"); + } else { + _xpg_sus_mode = 0; + } + + // Switch off AIX internal (pthread) guard pages. This has + // immediate effect for any pthread_create calls which follow. + p = ::getenv("AIXTHREAD_GUARDPAGES"); + if (Verbose) { + fprintf(stderr, "AIXTHREAD_GUARDPAGES=%s.\n", p ? p : ""); + fprintf(stderr, "setting AIXTHREAD_GUARDPAGES=0.\n"); + } + rc = ::putenv("AIXTHREAD_GUARDPAGES=0"); + guarantee(rc == 0, ""); + +} // end: os::Aix::scan_environment() + +// PASE: initialize the libo4 library (AS400 PASE porting library). +void os::Aix::initialize_libo4() { + Unimplemented(); +} + +// AIX: initialize the libperfstat library (we load this dynamically +// because it is only available on AIX. +void os::Aix::initialize_libperfstat() { + + assert(os::Aix::on_aix(), "AIX only"); + + if (!libperfstat::init()) { + fprintf(stderr, "libperfstat initialization failed.\n"); + assert(false, "libperfstat initialization failed"); + } else { + if (Verbose) { + fprintf(stderr, "libperfstat initialized.\n"); + } + } +} // end: os::Aix::initialize_libperfstat + +///////////////////////////////////////////////////////////////////////////// +// thread stack + +// function to query the current stack size using pthread_getthrds_np +// +// ! do not change anything here unless you know what you are doing ! +static void query_stack_dimensions(address* p_stack_base, size_t* p_stack_size) { + + // This only works when invoked on a pthread. As we agreed not to use + // primordial threads anyway, I assert here + guarantee(!os::Aix::is_primordial_thread(), "not allowed on the primordial thread"); + + // information about this api can be found (a) in the pthread.h header and + // (b) in http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.basetechref/doc/basetrf1/pthread_getthrds_np.htm + // + // The use of this API to find out the current stack is kind of undefined. + // But after a lot of tries and asking IBM about it, I concluded that it is safe + // enough for cases where I let the pthread library create its stacks. For cases + // where I create an own stack and pass this to pthread_create, it seems not to + // work (the returned stack size in that case is 0). + + pthread_t tid = pthread_self(); + struct __pthrdsinfo pinfo; + char dummy[1]; // we only need this to satisfy the api and to not get E + int dummy_size = sizeof(dummy); + + memset(&pinfo, 0, sizeof(pinfo)); + + const int rc = pthread_getthrds_np (&tid, PTHRDSINFO_QUERY_ALL, &pinfo, + sizeof(pinfo), dummy, &dummy_size); + + if (rc != 0) { + fprintf(stderr, "pthread_getthrds_np failed (%d)\n", rc); + guarantee(0, "pthread_getthrds_np failed"); + } + + guarantee(pinfo.__pi_stackend, "returned stack base invalid"); + + // the following can happen when invoking pthread_getthrds_np on a pthread running on a user provided stack + // (when handing down a stack to pthread create, see pthread_attr_setstackaddr). + // Not sure what to do here - I feel inclined to forbid this use case completely. + guarantee(pinfo.__pi_stacksize, "returned stack size invalid"); + + // On AIX, stacks are not necessarily page aligned so round the base and size accordingly + if (p_stack_base) { + (*p_stack_base) = (address) align_size_up((intptr_t)pinfo.__pi_stackend, os::Aix::stack_page_size()); + } + + if (p_stack_size) { + (*p_stack_size) = pinfo.__pi_stacksize - os::Aix::stack_page_size(); + } + +#ifndef PRODUCT + if (Verbose) { + fprintf(stderr, + "query_stack_dimensions() -> real stack_base=" INTPTR_FORMAT ", real stack_addr=" INTPTR_FORMAT + ", real stack_size=" INTPTR_FORMAT + ", stack_base=" INTPTR_FORMAT ", stack_size=" INTPTR_FORMAT "\n", + (intptr_t)pinfo.__pi_stackend, (intptr_t)pinfo.__pi_stackaddr, pinfo.__pi_stacksize, + (intptr_t)align_size_up((intptr_t)pinfo.__pi_stackend, os::Aix::stack_page_size()), + pinfo.__pi_stacksize - os::Aix::stack_page_size()); + } +#endif + +} // end query_stack_dimensions + +// get the current stack base from the OS (actually, the pthread library) +address os::current_stack_base() { + address p; + query_stack_dimensions(&p, 0); + return p; +} + +// get the current stack size from the OS (actually, the pthread library) +size_t os::current_stack_size() { + size_t s; + query_stack_dimensions(0, &s); + return s; +} + +// Refer to the comments in os_solaris.cpp park-unpark. +// +// Beware -- Some versions of NPTL embody a flaw where pthread_cond_timedwait() can +// hang indefinitely. For instance NPTL 0.60 on 2.4.21-4ELsmp is vulnerable. +// For specifics regarding the bug see GLIBC BUGID 261237 : +// http://www.mail-archive.com/debian-glibc@lists.debian.org/msg10837.html. +// Briefly, pthread_cond_timedwait() calls with an expiry time that's not in the future +// will either hang or corrupt the condvar, resulting in subsequent hangs if the condvar +// is used. (The simple C test-case provided in the GLIBC bug report manifests the +// hang). The JVM is vulernable via sleep(), Object.wait(timo), LockSupport.parkNanos() +// and monitorenter when we're using 1-0 locking. All those operations may result in +// calls to pthread_cond_timedwait(). Using LD_ASSUME_KERNEL to use an older version +// of libpthread avoids the problem, but isn't practical. +// +// Possible remedies: +// +// 1. Establish a minimum relative wait time. 50 to 100 msecs seems to work. +// This is palliative and probabilistic, however. If the thread is preempted +// between the call to compute_abstime() and pthread_cond_timedwait(), more +// than the minimum period may have passed, and the abstime may be stale (in the +// past) resultin in a hang. Using this technique reduces the odds of a hang +// but the JVM is still vulnerable, particularly on heavily loaded systems. +// +// 2. Modify park-unpark to use per-thread (per ParkEvent) pipe-pairs instead +// of the usual flag-condvar-mutex idiom. The write side of the pipe is set +// NDELAY. unpark() reduces to write(), park() reduces to read() and park(timo) +// reduces to poll()+read(). This works well, but consumes 2 FDs per extant +// thread. +// +// 3. Embargo pthread_cond_timedwait() and implement a native "chron" thread +// that manages timeouts. We'd emulate pthread_cond_timedwait() by enqueuing +// a timeout request to the chron thread and then blocking via pthread_cond_wait(). +// This also works well. In fact it avoids kernel-level scalability impediments +// on certain platforms that don't handle lots of active pthread_cond_timedwait() +// timers in a graceful fashion. +// +// 4. When the abstime value is in the past it appears that control returns +// correctly from pthread_cond_timedwait(), but the condvar is left corrupt. +// Subsequent timedwait/wait calls may hang indefinitely. Given that, we +// can avoid the problem by reinitializing the condvar -- by cond_destroy() +// followed by cond_init() -- after all calls to pthread_cond_timedwait(). +// It may be possible to avoid reinitialization by checking the return +// value from pthread_cond_timedwait(). In addition to reinitializing the +// condvar we must establish the invariant that cond_signal() is only called +// within critical sections protected by the adjunct mutex. This prevents +// cond_signal() from "seeing" a condvar that's in the midst of being +// reinitialized or that is corrupt. Sadly, this invariant obviates the +// desirable signal-after-unlock optimization that avoids futile context switching. +// +// I'm also concerned that some versions of NTPL might allocate an auxilliary +// structure when a condvar is used or initialized. cond_destroy() would +// release the helper structure. Our reinitialize-after-timedwait fix +// put excessive stress on malloc/free and locks protecting the c-heap. +// +// We currently use (4). See the WorkAroundNTPLTimedWaitHang flag. +// It may be possible to refine (4) by checking the kernel and NTPL verisons +// and only enabling the work-around for vulnerable environments. + +// utility to compute the abstime argument to timedwait: +// millis is the relative timeout time +// abstime will be the absolute timeout time +// TODO: replace compute_abstime() with unpackTime() + +static struct timespec* compute_abstime(timespec* abstime, jlong millis) { + if (millis < 0) millis = 0; + struct timeval now; + int status = gettimeofday(&now, NULL); + assert(status == 0, "gettimeofday"); + jlong seconds = millis / 1000; + millis %= 1000; + if (seconds > 50000000) { // see man cond_timedwait(3T) + seconds = 50000000; + } + abstime->tv_sec = now.tv_sec + seconds; + long usec = now.tv_usec + millis * 1000; + if (usec >= 1000000) { + abstime->tv_sec += 1; + usec -= 1000000; + } + abstime->tv_nsec = usec * 1000; + return abstime; +} + + +// Test-and-clear _Event, always leaves _Event set to 0, returns immediately. +// Conceptually TryPark() should be equivalent to park(0). + +int os::PlatformEvent::TryPark() { + for (;;) { + const int v = _Event; + guarantee ((v == 0) || (v == 1), "invariant"); + if (Atomic::cmpxchg (0, &_Event, v) == v) return v; + } +} + +void os::PlatformEvent::park() { // AKA "down()" + // Invariant: Only the thread associated with the Event/PlatformEvent + // may call park(). + // TODO: assert that _Assoc != NULL or _Assoc == Self + int v; + for (;;) { + v = _Event; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break; + } + guarantee (v >= 0, "invariant"); + if (v == 0) { + // Do this the hard way by blocking ... + int status = pthread_mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + guarantee (_nParked == 0, "invariant"); + ++ _nParked; + while (_Event < 0) { + status = pthread_cond_wait(_cond, _mutex); + assert_status(status == 0 || status == ETIMEDOUT, status, "cond_timedwait"); + } + -- _nParked; + + // In theory we could move the ST of 0 into _Event past the unlock(), + // but then we'd need a MEMBAR after the ST. + _Event = 0; + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + } + guarantee (_Event >= 0, "invariant"); +} + +int os::PlatformEvent::park(jlong millis) { + guarantee (_nParked == 0, "invariant"); + + int v; + for (;;) { + v = _Event; + if (Atomic::cmpxchg (v-1, &_Event, v) == v) break; + } + guarantee (v >= 0, "invariant"); + if (v != 0) return OS_OK; + + // We do this the hard way, by blocking the thread. + // Consider enforcing a minimum timeout value. + struct timespec abst; + compute_abstime(&abst, millis); + + int ret = OS_TIMEOUT; + int status = pthread_mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + guarantee (_nParked == 0, "invariant"); + ++_nParked; + + // Object.wait(timo) will return because of + // (a) notification + // (b) timeout + // (c) thread.interrupt + // + // Thread.interrupt and object.notify{All} both call Event::set. + // That is, we treat thread.interrupt as a special case of notification. + // The underlying Solaris implementation, cond_timedwait, admits + // spurious/premature wakeups, but the JLS/JVM spec prevents the + // JVM from making those visible to Java code. As such, we must + // filter out spurious wakeups. We assume all ETIME returns are valid. + // + // TODO: properly differentiate simultaneous notify+interrupt. + // In that case, we should propagate the notify to another waiter. + + while (_Event < 0) { + status = pthread_cond_timedwait(_cond, _mutex, &abst); + assert_status(status == 0 || status == ETIMEDOUT, + status, "cond_timedwait"); + if (!FilterSpuriousWakeups) break; // previous semantics + if (status == ETIMEDOUT) break; + // We consume and ignore EINTR and spurious wakeups. + } + --_nParked; + if (_Event >= 0) { + ret = OS_OK; + } + _Event = 0; + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + assert (_nParked == 0, "invariant"); + return ret; +} + +void os::PlatformEvent::unpark() { + int v, AnyWaiters; + for (;;) { + v = _Event; + if (v > 0) { + // The LD of _Event could have reordered or be satisfied + // by a read-aside from this processor's write buffer. + // To avoid problems execute a barrier and then + // ratify the value. + OrderAccess::fence(); + if (_Event == v) return; + continue; + } + if (Atomic::cmpxchg (v+1, &_Event, v) == v) break; + } + if (v < 0) { + // Wait for the thread associated with the event to vacate + int status = pthread_mutex_lock(_mutex); + assert_status(status == 0, status, "mutex_lock"); + AnyWaiters = _nParked; + + if (AnyWaiters != 0) { + // We intentional signal *after* dropping the lock + // to avoid a common class of futile wakeups. + status = pthread_cond_signal(_cond); + assert_status(status == 0, status, "cond_signal"); + } + // Mutex should be locked for pthread_cond_signal(_cond). + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "mutex_unlock"); + } + + // Note that we signal() _after dropping the lock for "immortal" Events. + // This is safe and avoids a common class of futile wakeups. In rare + // circumstances this can cause a thread to return prematurely from + // cond_{timed}wait() but the spurious wakeup is benign and the victim will + // simply re-test the condition and re-park itself. +} + + +// JSR166 +// ------------------------------------------------------- + +// +// The solaris and linux implementations of park/unpark are fairly +// conservative for now, but can be improved. They currently use a +// mutex/condvar pair, plus a a count. +// Park decrements count if > 0, else does a condvar wait. Unpark +// sets count to 1 and signals condvar. Only one thread ever waits +// on the condvar. Contention seen when trying to park implies that someone +// is unparking you, so don't wait. And spurious returns are fine, so there +// is no need to track notifications. +// + +#define MAX_SECS 100000000 +// +// This code is common to linux and solaris and will be moved to a +// common place in dolphin. +// +// The passed in time value is either a relative time in nanoseconds +// or an absolute time in milliseconds. Either way it has to be unpacked +// into suitable seconds and nanoseconds components and stored in the +// given timespec structure. +// Given time is a 64-bit value and the time_t used in the timespec is only +// a signed-32-bit value (except on 64-bit Linux) we have to watch for +// overflow if times way in the future are given. Further on Solaris versions +// prior to 10 there is a restriction (see cond_timedwait) that the specified +// number of seconds, in abstime, is less than current_time + 100,000,000. +// As it will be 28 years before "now + 100000000" will overflow we can +// ignore overflow and just impose a hard-limit on seconds using the value +// of "now + 100,000,000". This places a limit on the timeout of about 3.17 +// years from "now". +// + +static void unpackTime(timespec* absTime, bool isAbsolute, jlong time) { + assert (time > 0, "convertTime"); + + struct timeval now; + int status = gettimeofday(&now, NULL); + assert(status == 0, "gettimeofday"); + + time_t max_secs = now.tv_sec + MAX_SECS; + + if (isAbsolute) { + jlong secs = time / 1000; + if (secs > max_secs) { + absTime->tv_sec = max_secs; + } + else { + absTime->tv_sec = secs; + } + absTime->tv_nsec = (time % 1000) * NANOSECS_PER_MILLISEC; + } + else { + jlong secs = time / NANOSECS_PER_SEC; + if (secs >= MAX_SECS) { + absTime->tv_sec = max_secs; + absTime->tv_nsec = 0; + } + else { + absTime->tv_sec = now.tv_sec + secs; + absTime->tv_nsec = (time % NANOSECS_PER_SEC) + now.tv_usec*1000; + if (absTime->tv_nsec >= NANOSECS_PER_SEC) { + absTime->tv_nsec -= NANOSECS_PER_SEC; + ++absTime->tv_sec; // note: this must be <= max_secs + } + } + } + assert(absTime->tv_sec >= 0, "tv_sec < 0"); + assert(absTime->tv_sec <= max_secs, "tv_sec > max_secs"); + assert(absTime->tv_nsec >= 0, "tv_nsec < 0"); + assert(absTime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec"); +} + +void Parker::park(bool isAbsolute, jlong time) { + // Optional fast-path check: + // Return immediately if a permit is available. + if (_counter > 0) { + _counter = 0; + OrderAccess::fence(); + return; + } + + Thread* thread = Thread::current(); + assert(thread->is_Java_thread(), "Must be JavaThread"); + JavaThread *jt = (JavaThread *)thread; + + // Optional optimization -- avoid state transitions if there's an interrupt pending. + // Check interrupt before trying to wait + if (Thread::is_interrupted(thread, false)) { + return; + } + + // Next, demultiplex/decode time arguments + timespec absTime; + if (time < 0 || (isAbsolute && time == 0)) { // don't wait at all + return; + } + if (time > 0) { + unpackTime(&absTime, isAbsolute, time); + } + + + // Enter safepoint region + // Beware of deadlocks such as 6317397. + // The per-thread Parker:: mutex is a classic leaf-lock. + // In particular a thread must never block on the Threads_lock while + // holding the Parker:: mutex. If safepoints are pending both the + // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock. + ThreadBlockInVM tbivm(jt); + + // Don't wait if cannot get lock since interference arises from + // unblocking. Also. check interrupt before trying wait + if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) { + return; + } + + int status; + if (_counter > 0) { // no wait needed + _counter = 0; + status = pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant"); + OrderAccess::fence(); + return; + } + +#ifdef ASSERT + // Don't catch signals while blocked; let the running threads have the signals. + // (This allows a debugger to break into the running thread.) + sigset_t oldsigs; + sigset_t* allowdebug_blocked = os::Aix::allowdebug_blocked_signals(); + pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs); +#endif + + OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */); + jt->set_suspend_equivalent(); + // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self() + + if (time == 0) { + status = pthread_cond_wait (_cond, _mutex); + } else { + status = pthread_cond_timedwait (_cond, _mutex, &absTime); + if (status != 0 && WorkAroundNPTLTimedWaitHang) { + pthread_cond_destroy (_cond); + pthread_cond_init (_cond, NULL); + } + } + assert_status(status == 0 || status == EINTR || + status == ETIME || status == ETIMEDOUT, + status, "cond_timedwait"); + +#ifdef ASSERT + pthread_sigmask(SIG_SETMASK, &oldsigs, NULL); +#endif + + _counter = 0; + status = pthread_mutex_unlock(_mutex); + assert_status(status == 0, status, "invariant"); + // If externally suspended while waiting, re-suspend + if (jt->handle_special_suspend_equivalent_condition()) { + jt->java_suspend_self(); + } + + OrderAccess::fence(); +} + +void Parker::unpark() { + int s, status; + status = pthread_mutex_lock(_mutex); + assert (status == 0, "invariant"); + s = _counter; + _counter = 1; + if (s < 1) { + if (WorkAroundNPTLTimedWaitHang) { + status = pthread_cond_signal (_cond); + assert (status == 0, "invariant"); + status = pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant"); + } else { + status = pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant"); + status = pthread_cond_signal (_cond); + assert (status == 0, "invariant"); + } + } else { + pthread_mutex_unlock(_mutex); + assert (status == 0, "invariant"); + } +} + + +extern char** environ; + +// Run the specified command in a separate process. Return its exit value, +// or -1 on failure (e.g. can't fork a new process). +// Unlike system(), this function can be called from signal handler. It +// doesn't block SIGINT et al. +int os::fork_and_exec(char* cmd) { + Unimplemented(); + return 0; +} + +// is_headless_jre() +// +// Test for the existence of xawt/libmawt.so or libawt_xawt.so +// in order to report if we are running in a headless jre. +// +// Since JDK8 xawt/libmawt.so is moved into the same directory +// as libawt.so, and renamed libawt_xawt.so +bool os::is_headless_jre() { + struct stat statbuf; + char buf[MAXPATHLEN]; + char libmawtpath[MAXPATHLEN]; + const char *xawtstr = "/xawt/libmawt.so"; + const char *new_xawtstr = "/libawt_xawt.so"; + + char *p; + + // Get path to libjvm.so + os::jvm_path(buf, sizeof(buf)); + + // Get rid of libjvm.so + p = strrchr(buf, '/'); + if (p == NULL) return false; + else *p = '\0'; + + // Get rid of client or server + p = strrchr(buf, '/'); + if (p == NULL) return false; + else *p = '\0'; + + // check xawt/libmawt.so + strcpy(libmawtpath, buf); + strcat(libmawtpath, xawtstr); + if (::stat(libmawtpath, &statbuf) == 0) return false; + + // check libawt_xawt.so + strcpy(libmawtpath, buf); + strcat(libmawtpath, new_xawtstr); + if (::stat(libmawtpath, &statbuf) == 0) return false; + + return true; +} + +// Get the default path to the core file +// Returns the length of the string +int os::get_core_path(char* buffer, size_t bufferSize) { + const char* p = get_current_directory(buffer, bufferSize); + + if (p == NULL) { + assert(p != NULL, "failed to get current directory"); + return 0; + } + + return strlen(buffer); +} + +#ifndef PRODUCT +void TestReserveMemorySpecial_test() { + // No tests available for this platform +} +#endif diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/os_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/os_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,381 @@ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 SAP AG. 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. + * + */ + +#ifndef OS_AIX_VM_OS_AIX_HPP +#define OS_AIX_VM_OS_AIX_HPP + +// Information about the protection of the page at address '0' on this os. +static bool zero_page_read_protected() { return false; } + +// Class Aix defines the interface to the Aix operating systems. + +class Aix { + friend class os; + + // For signal-chaining + // highest so far (AIX 5.2) is SIGSAK (63) +#define MAXSIGNUM 63 + // length of strings included in the libperfstat structures +#define IDENTIFIER_LENGTH 64 + + static struct sigaction sigact[MAXSIGNUM]; // saved preinstalled sigactions + static unsigned int sigs; // mask of signals that have + // preinstalled signal handlers + static bool libjsig_is_loaded; // libjsig that interposes sigaction(), + // __sigaction(), signal() is loaded + static struct sigaction *(*get_signal_action)(int); + static struct sigaction *get_preinstalled_handler(int); + static void save_preinstalled_handler(int, struct sigaction&); + + static void check_signal_handler(int sig); + + // For signal flags diagnostics + static int sigflags[MAXSIGNUM]; + + protected: + + static julong _physical_memory; + static pthread_t _main_thread; + static Mutex* _createThread_lock; + static int _page_size; + static int _logical_cpus; + + // -1 = uninitialized, 0 = AIX, 1 = OS/400 (PASE) + static int _on_pase; + + // -1 = uninitialized, otherwise 16 bit number: + // lower 8 bit - minor version + // higher 8 bit - major version + // For AIX, e.g. 0x0601 for AIX 6.1 + // for OS/400 e.g. 0x0504 for OS/400 V5R4 + static int _os_version; + + // -1 = uninitialized, + // 0 - SPEC1170 not requested (XPG_SUS_ENV is OFF or not set) + // 1 - SPEC1170 requested (XPG_SUS_ENV is ON) + static int _xpg_sus_mode; + + // -1 = uninitialized, + // 0 - EXTSHM=OFF or not set + // 1 - EXTSHM=ON + static int _extshm; + + // page sizes on AIX. + // + // AIX supports four different page sizes - 4K, 64K, 16MB, 16GB. The latter two + // (16M "large" resp. 16G "huge" pages) require special setup and are normally + // not available. + // + // AIX supports multiple page sizes per process, for: + // - Stack (of the primordial thread, so not relevant for us) + // - Data - data, bss, heap, for us also pthread stacks + // - Text - text code + // - shared memory + // + // Default page sizes can be set via linker options (-bdatapsize, -bstacksize, ...) + // and via environment variable LDR_CNTRL (DATAPSIZE, STACKPSIZE, ...) + // + // For shared memory, page size can be set dynamically via shmctl(). Different shared memory + // regions can have different page sizes. + // + // More information can be found at AIBM info center: + // http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=/com.ibm.aix.prftungd/doc/prftungd/multiple_page_size_app_support.htm + // + // ----- + // We want to support 4K and 64K and, if the machine is set up correctly, 16MB pages. + // + + // page size of the stack of newly created pthreads + // (should be LDR_CNTRL DATAPSIZE because stack is allocated on heap by pthread lib) + static int _stack_page_size; + + // Default shm page size. Read: what page size shared memory will be backed + // with if no page size was set explicitly using shmctl(SHM_PAGESIZE). + // Should be LDR_CNTRL SHMPSIZE. + static size_t _shm_default_page_size; + + // True if sys V shm can be used with 64K pages dynamically. + // (via shmctl(.. SHM_PAGESIZE..). Should be true for AIX 53 and + // newer / PASE V6R1 and newer. (0 or 1, -1 if not initialized) + static int _can_use_64K_pages; + + // True if sys V shm can be used with 16M pages dynamically. + // (via shmctl(.. SHM_PAGESIZE..). Only true on AIX 5.3 and + // newer, if the system was set up to use 16M pages and the + // jvm has enough user rights. (0 or 1, -1 if not initialized) + static int _can_use_16M_pages; + + static julong available_memory(); + static julong physical_memory() { return _physical_memory; } + static void initialize_system_info(); + + // OS recognitions (PASE/AIX, OS level) call this before calling any + // one of Aix::on_pase(), Aix::os_version(). + static void initialize_os_info(); + + // Scan environment for important settings which might effect the + // VM. Trace out settings. Warn about invalid settings and/or + // correct them. + // + // Must run after os::Aix::initialue_os_info(). + static void scan_environment(); + + // Retrieve information about multipage size support. Will initialize + // _page_size, _stack_page_size, _can_use_64K_pages/_can_use_16M_pages + static void query_multipage_support(); + + // Initialize libo4 (on PASE) and libperfstat (on AIX). Call this + // before relying on functions from either lib, e.g. Aix::get_meminfo(). + static void initialize_libo4(); + static void initialize_libperfstat(); + + static bool supports_variable_stack_size(); + + public: + static void init_thread_fpu_state(); + static pthread_t main_thread(void) { return _main_thread; } + // returns kernel thread id (similar to LWP id on Solaris), which can be + // used to access /proc + static pid_t gettid(); + static void set_createThread_lock(Mutex* lk) { _createThread_lock = lk; } + static Mutex* createThread_lock(void) { return _createThread_lock; } + static void hotspot_sigmask(Thread* thread); + + // Given an address, returns the size of the page backing that address + static size_t query_pagesize(void* p); + + // Return `true' if the calling thread is the primordial thread. The + // primordial thread is the thread which contains the main function, + // *not* necessarily the thread which initialized the VM by calling + // JNI_CreateJavaVM. + static bool is_primordial_thread(void); + + static int page_size(void) { + assert(_page_size != -1, "not initialized"); + return _page_size; + } + + // Accessor methods for stack page size which may be different from usual page size. + static int stack_page_size(void) { + assert(_stack_page_size != -1, "not initialized"); + return _stack_page_size; + } + + // default shm page size. Read: what page size shared memory + // will be backed with if no page size was set explicitly using shmctl(SHM_PAGESIZE). + // Should be LDR_CNTRL SHMPSIZE. + static int shm_default_page_size(void) { + assert(_shm_default_page_size != -1, "not initialized"); + return _shm_default_page_size; + } + + // Return true if sys V shm can be used with 64K pages dynamically + // (via shmctl(.. SHM_PAGESIZE..). + static bool can_use_64K_pages () { + assert(_can_use_64K_pages != -1, "not initialized"); + return _can_use_64K_pages == 1 ? true : false; + } + + // Return true if sys V shm can be used with 16M pages dynamically. + // (via shmctl(.. SHM_PAGESIZE..). + static bool can_use_16M_pages () { + assert(_can_use_16M_pages != -1, "not initialized"); + return _can_use_16M_pages == 1 ? true : false; + } + + static address ucontext_get_pc(ucontext_t* uc); + static intptr_t* ucontext_get_sp(ucontext_t* uc); + static intptr_t* ucontext_get_fp(ucontext_t* uc); + // Set PC into context. Needed for continuation after signal. + static void ucontext_set_pc(ucontext_t* uc, address pc); + + // This boolean allows users to forward their own non-matching signals + // to JVM_handle_aix_signal, harmlessly. + static bool signal_handlers_are_installed; + + static int get_our_sigflags(int); + static void set_our_sigflags(int, int); + static void signal_sets_init(); + static void install_signal_handlers(); + static void set_signal_handler(int, bool); + static bool is_sig_ignored(int sig); + + static sigset_t* unblocked_signals(); + static sigset_t* vm_signals(); + static sigset_t* allowdebug_blocked_signals(); + + // For signal-chaining + static struct sigaction *get_chained_signal_action(int sig); + static bool chained_handler(int sig, siginfo_t* siginfo, void* context); + + // libpthread version string + static void libpthread_init(); + + // Minimum stack size a thread can be created with (allowing + // the VM to completely create the thread and enter user code) + static size_t min_stack_allowed; + + // Return default stack size or guard size for the specified thread type + static size_t default_stack_size(os::ThreadType thr_type); + static size_t default_guard_size(os::ThreadType thr_type); + + // Function returns true if we run on OS/400 (pase), false if we run + // on AIX. + static bool on_pase() { + assert(_on_pase != -1, "not initialized"); + return _on_pase ? true : false; + } + + // Function returns true if we run on AIX, false if we run on OS/400 + // (pase). + static bool on_aix() { + assert(_on_pase != -1, "not initialized"); + return _on_pase ? false : true; + } + + // -1 = uninitialized, otherwise 16 bit number: + // lower 8 bit - minor version + // higher 8 bit - major version + // For AIX, e.g. 0x0601 for AIX 6.1 + // for OS/400 e.g. 0x0504 for OS/400 V5R4 + static int os_version () { + assert(_os_version != -1, "not initialized"); + return _os_version; + } + + // Convenience method: returns true if running on AIX 5.3 or older. + static bool on_aix_53_or_older() { + return on_aix() && os_version() <= 0x0503; + } + + // Returns true if we run in SPEC1170 compliant mode (XPG_SUS_ENV=ON). + static bool xpg_sus_mode() { + assert(_xpg_sus_mode != -1, "not initialized"); + return _xpg_sus_mode; + } + + // Returns true if EXTSHM=ON. + static bool extshm() { + assert(_extshm != -1, "not initialized"); + return _extshm; + } + + // result struct for get_meminfo() + struct meminfo_t { + + // Amount of virtual memory (in units of 4 KB pages) + unsigned long long virt_total; + + // Amount of real memory, in bytes + unsigned long long real_total; + + // Amount of free real memory, in bytes + unsigned long long real_free; + + // Total amount of paging space, in bytes + unsigned long long pgsp_total; + + // Amount of free paging space, in bytes + unsigned long long pgsp_free; + + }; + + // Result struct for get_cpuinfo(). + struct cpuinfo_t { + char description[IDENTIFIER_LENGTH]; // processor description (type/official name) + u_longlong_t processorHZ; // processor speed in Hz + int ncpus; // number of active logical processors + double loadavg[3]; // (1<. + char version[20]; // processor version from _system_configuration (sys/systemcfg.h) + }; + + // Functions to retrieve memory information on AIX, PASE. + // (on AIX, using libperfstat, on PASE with libo4.so). + // Returns true if ok, false if error. + static bool get_meminfo(meminfo_t* pmi); + + // Function to retrieve cpu information on AIX + // (on AIX, using libperfstat) + // Returns true if ok, false if error. + static bool get_cpuinfo(cpuinfo_t* pci); + +}; // os::Aix class + + +class PlatformEvent : public CHeapObj { + private: + double CachePad [4]; // increase odds that _mutex is sole occupant of cache line + volatile int _Event; + volatile int _nParked; + pthread_mutex_t _mutex [1]; + pthread_cond_t _cond [1]; + double PostPad [2]; + Thread * _Assoc; + + public: // TODO-FIXME: make dtor private + ~PlatformEvent() { guarantee (0, "invariant"); } + + public: + PlatformEvent() { + int status; + status = pthread_cond_init (_cond, NULL); + assert_status(status == 0, status, "cond_init"); + status = pthread_mutex_init (_mutex, NULL); + assert_status(status == 0, status, "mutex_init"); + _Event = 0; + _nParked = 0; + _Assoc = NULL; + } + + // Use caution with reset() and fired() -- they may require MEMBARs + void reset() { _Event = 0; } + int fired() { return _Event; } + void park (); + void unpark (); + int TryPark (); + int park (jlong millis); + void SetAssociation (Thread * a) { _Assoc = a; } +}; + +class PlatformParker : public CHeapObj { + protected: + pthread_mutex_t _mutex [1]; + pthread_cond_t _cond [1]; + + public: // TODO-FIXME: make dtor private + ~PlatformParker() { guarantee (0, "invariant"); } + + public: + PlatformParker() { + int status; + status = pthread_cond_init (_cond, NULL); + assert_status(status == 0, status, "cond_init"); + status = pthread_mutex_init (_mutex, NULL); + assert_status(status == 0, status, "mutex_init"); + } +}; + +#endif // OS_AIX_VM_OS_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/os_aix.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/os_aix.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_AIX_VM_OS_AIX_INLINE_HPP +#define OS_AIX_VM_OS_AIX_INLINE_HPP + +#include "runtime/atomic.hpp" +#include "runtime/os.hpp" +#ifdef TARGET_OS_ARCH_aix_ppc +# include "atomic_aix_ppc.inline.hpp" +# include "orderAccess_aix_ppc.inline.hpp" +#endif + +// System includes + +#include +#include +#include +#include +#include + +// Defined in the system headers included above. +#undef rem_size + +inline void* os::thread_local_storage_at(int index) { + return pthread_getspecific((pthread_key_t)index); +} + +inline const char* os::file_separator() { + return "/"; +} + +inline const char* os::line_separator() { + return "\n"; +} + +inline const char* os::path_separator() { + return ":"; +} + +inline const char* os::jlong_format_specifier() { + return "%lld"; +} + +inline const char* os::julong_format_specifier() { + return "%llu"; +} + +// File names are case-sensitive on windows only +inline int os::file_name_strcmp(const char* s1, const char* s2) { + return strcmp(s1, s2); +} + +inline bool os::obsolete_option(const JavaVMOption *option) { + return false; +} + +inline bool os::uses_stack_guard_pages() { + return true; +} + +inline bool os::allocate_stack_guard_pages() { + assert(uses_stack_guard_pages(), "sanity check"); + return true; +} + + +// On Aix, reservations are made on a page by page basis, nothing to do. +inline void os::pd_split_reserved_memory(char *base, size_t size, + size_t split, bool realloc) { +} + + +// Bang the shadow pages if they need to be touched to be mapped. +inline void os::bang_stack_shadow_pages() { +} + +inline void os::dll_unload(void *lib) { + ::dlclose(lib); +} + +inline const int os::default_file_open_flags() { return 0;} + +inline DIR* os::opendir(const char* dirname) +{ + assert(dirname != NULL, "just checking"); + return ::opendir(dirname); +} + +inline int os::readdir_buf_size(const char *path) +{ + // according to aix sys/limits, NAME_MAX must be retrieved at runtime. */ + const long my_NAME_MAX = pathconf(path, _PC_NAME_MAX); + return my_NAME_MAX + sizeof(dirent) + 1; +} + +inline jlong os::lseek(int fd, jlong offset, int whence) { + return (jlong) ::lseek64(fd, offset, whence); +} + +inline int os::fsync(int fd) { + return ::fsync(fd); +} + +inline char* os::native_path(char *path) { + return path; +} + +inline int os::ftruncate(int fd, jlong length) { + return ::ftruncate64(fd, length); +} + +inline struct dirent* os::readdir(DIR* dirp, dirent *dbuf) +{ + dirent* p; + int status; + assert(dirp != NULL, "just checking"); + + // NOTE: Linux readdir_r (on RH 6.2 and 7.2 at least) is NOT like the POSIX + // version. Here is the doc for this function: + // http://www.gnu.org/manual/glibc-2.2.3/html_node/libc_262.html + + if((status = ::readdir_r(dirp, dbuf, &p)) != 0) { + errno = status; + return NULL; + } else + return p; +} + +inline int os::closedir(DIR *dirp) { + assert(dirp != NULL, "argument is NULL"); + return ::closedir(dirp); +} + +// macros for restartable system calls + +#define RESTARTABLE(_cmd, _result) do { \ + _result = _cmd; \ + } while(((int)_result == OS_ERR) && (errno == EINTR)) + +#define RESTARTABLE_RETURN_INT(_cmd) do { \ + int _result; \ + RESTARTABLE(_cmd, _result); \ + return _result; \ +} while(false) + +// We don't have NUMA support on Aix, but we need this for compilation. +inline bool os::numa_has_static_binding() { ShouldNotReachHere(); return true; } +inline bool os::numa_has_group_homing() { ShouldNotReachHere(); return false; } + +inline size_t os::restartable_read(int fd, void *buf, unsigned int nBytes) { + size_t res; + RESTARTABLE( (size_t) ::read(fd, buf, (size_t) nBytes), res); + return res; +} + +inline size_t os::write(int fd, const void *buf, unsigned int nBytes) { + size_t res; + RESTARTABLE((size_t) ::write(fd, buf, (size_t) nBytes), res); + return res; +} + +inline int os::close(int fd) { + return ::close(fd); +} + +inline int os::socket_close(int fd) { + return ::close(fd); +} + +inline int os::socket(int domain, int type, int protocol) { + return ::socket(domain, type, protocol); +} + +inline int os::recv(int fd, char* buf, size_t nBytes, uint flags) { + RESTARTABLE_RETURN_INT(::recv(fd, buf, nBytes, flags)); +} + +inline int os::send(int fd, char* buf, size_t nBytes, uint flags) { + RESTARTABLE_RETURN_INT(::send(fd, buf, nBytes, flags)); +} + +inline int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) { + return os::send(fd, buf, nBytes, flags); +} + +inline int os::timeout(int fd, long timeout) { + julong prevtime,newtime; + struct timeval t; + + gettimeofday(&t, NULL); + prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; + + for(;;) { + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLIN | POLLERR; + + int res = ::poll(&pfd, 1, timeout); + + if (res == OS_ERR && errno == EINTR) { + + // On Linux any value < 0 means "forever" + + if(timeout >= 0) { + gettimeofday(&t, NULL); + newtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; + timeout -= newtime - prevtime; + if(timeout <= 0) + return OS_OK; + prevtime = newtime; + } + } else + return res; + } +} + +inline int os::listen(int fd, int count) { + return ::listen(fd, count); +} + +inline int os::connect(int fd, struct sockaddr* him, socklen_t len) { + RESTARTABLE_RETURN_INT(::connect(fd, him, len)); +} + +inline int os::accept(int fd, struct sockaddr* him, socklen_t* len) { + // Linux doc says this can't return EINTR, unlike accept() on Solaris. + // But see attachListener_linux.cpp, LinuxAttachListener::dequeue(). + return (int)::accept(fd, him, len); +} + +inline int os::recvfrom(int fd, char* buf, size_t nBytes, uint flags, + sockaddr* from, socklen_t* fromlen) { + RESTARTABLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen)); +} + +inline int os::sendto(int fd, char* buf, size_t len, uint flags, + struct sockaddr* to, socklen_t tolen) { + RESTARTABLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen)); +} + +inline int os::socket_shutdown(int fd, int howto) { + return ::shutdown(fd, howto); +} + +inline int os::bind(int fd, struct sockaddr* him, socklen_t len) { + return ::bind(fd, him, len); +} + +inline int os::get_sock_name(int fd, struct sockaddr* him, socklen_t* len) { + return ::getsockname(fd, him, len); +} + +inline int os::get_host_name(char* name, int namelen) { + return ::gethostname(name, namelen); +} + +inline struct hostent* os::get_host_by_name(char* name) { + return ::gethostbyname(name); +} + +inline int os::get_sock_opt(int fd, int level, int optname, + char* optval, socklen_t* optlen) { + return ::getsockopt(fd, level, optname, optval, optlen); +} + +inline int os::set_sock_opt(int fd, int level, int optname, + const char* optval, socklen_t optlen) { + return ::setsockopt(fd, level, optname, optval, optlen); +} +#endif // OS_AIX_VM_OS_AIX_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/os_share_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/os_share_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1999, 2010, 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. + * + */ + +#ifndef OS_AIX_VM_OS_SHARE_AIX_HPP +#define OS_AIX_VM_OS_SHARE_AIX_HPP + +// misc +void signalHandler(int, siginfo_t*, ucontext_t*); +void handle_unexpected_exception(Thread* thread, int sig, siginfo_t* info, address pc, address adjusted_pc); +#ifndef PRODUCT +void continue_with_dump(void); +#endif + +#define PROCFILE_LENGTH 128 + +#endif // OS_AIX_VM_OS_SHARE_AIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/perfMemory_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/perfMemory_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,1344 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "classfile/vmSymbols.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "oops/oop.inline.hpp" +#include "os_aix.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/perfMemory.hpp" +#include "services/memTracker.hpp" +#include "utilities/exceptions.hpp" + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include + +static char* backing_store_file_name = NULL; // name of the backing store + // file, if successfully created. + +// Standard Memory Implementation Details + +// create the PerfData memory region in standard memory. +// +static char* create_standard_memory(size_t size) { + + // allocate an aligned chuck of memory + char* mapAddress = os::reserve_memory(size); + + if (mapAddress == NULL) { + return NULL; + } + + // commit memory + if (!os::commit_memory(mapAddress, size, !ExecMem)) { + if (PrintMiscellaneous && Verbose) { + warning("Could not commit PerfData memory\n"); + } + os::release_memory(mapAddress, size); + return NULL; + } + + return mapAddress; +} + +// delete the PerfData memory region +// +static void delete_standard_memory(char* addr, size_t size) { + + // there are no persistent external resources to cleanup for standard + // memory. since DestroyJavaVM does not support unloading of the JVM, + // cleanup of the memory resource is not performed. The memory will be + // reclaimed by the OS upon termination of the process. + // + return; +} + +// save the specified memory region to the given file +// +// Note: this function might be called from signal handler (by os::abort()), +// don't allocate heap memory. +// +static void save_memory_to_file(char* addr, size_t size) { + + const char* destfile = PerfMemory::get_perfdata_file_path(); + assert(destfile[0] != '\0', "invalid PerfData file path"); + + int result; + + RESTARTABLE(::open(destfile, O_CREAT|O_WRONLY|O_TRUNC, S_IREAD|S_IWRITE), + result);; + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not create Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + } else { + int fd = result; + + for (size_t remaining = size; remaining > 0;) { + + RESTARTABLE(::write(fd, addr, remaining), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("Could not write Perfdata save file: %s: %s\n", + destfile, strerror(errno)); + } + break; + } + + remaining -= (size_t)result; + addr += result; + } + + RESTARTABLE(::close(fd), result); + if (PrintMiscellaneous && Verbose) { + if (result == OS_ERR) { + warning("Could not close %s: %s\n", destfile, strerror(errno)); + } + } + } + FREE_C_HEAP_ARRAY(char, destfile, mtInternal); +} + + +// Shared Memory Implementation Details + +// Note: the solaris and linux shared memory implementation uses the mmap +// interface with a backing store file to implement named shared memory. +// Using the file system as the name space for shared memory allows a +// common name space to be supported across a variety of platforms. It +// also provides a name space that Java applications can deal with through +// simple file apis. +// +// The solaris and linux implementations store the backing store file in +// a user specific temporary directory located in the /tmp file system, +// which is always a local file system and is sometimes a RAM based file +// system. + +// return the user specific temporary directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_tmp_dir(const char* user) { + + const char* tmpdir = os::get_temp_directory(); + const char* perfdir = PERFDATA_NAME; + size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 3; + char* dirname = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); + + // construct the path name to user specific tmp directory + snprintf(dirname, nbytes, "%s/%s_%s", tmpdir, perfdir, user); + + return dirname; +} + +// convert the given file name into a process id. if the file +// does not meet the file naming constraints, return 0. +// +static pid_t filename_to_pid(const char* filename) { + + // a filename that doesn't begin with a digit is not a + // candidate for conversion. + // + if (!isdigit(*filename)) { + return 0; + } + + // check if file name can be converted to an integer without + // any leftover characters. + // + char* remainder = NULL; + errno = 0; + pid_t pid = (pid_t)strtol(filename, &remainder, 10); + + if (errno != 0) { + return 0; + } + + // check for left over characters. If any, then the filename is + // not a candidate for conversion. + // + if (remainder != NULL && *remainder != '\0') { + return 0; + } + + // successful conversion, return the pid + return pid; +} + +// Check if the given statbuf is considered a secure directory for +// the backing store files. Returns true if the directory is considered +// a secure location. Returns false if the statbuf is a symbolic link or +// if an error occurred. +static bool is_statbuf_secure(struct stat *statp) { + if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) { + // The path represents a link or some non-directory file type, + // which is not what we expected. Declare it insecure. + // + return false; + } + // We have an existing directory, check if the permissions are safe. + if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) { + // The directory is open for writing and could be subjected + // to a symlink or a hard link attack. Declare it insecure. + return false; + } + // See if the uid of the directory matches the effective uid of the process. + // + if (statp->st_uid != geteuid()) { + // The directory was not created by this user, declare it insecure. + return false; + } + return true; +} + + +// Check if the given path is considered a secure directory for +// the backing store files. Returns true if the directory exists +// and is considered a secure location. Returns false if the path +// is a symbolic link or if an error occurred. +static bool is_directory_secure(const char* path) { + struct stat statbuf; + int result = 0; + + RESTARTABLE(::lstat(path, &statbuf), result); + if (result == OS_ERR) { + return false; + } + + // The path exists, see if it is secure. + return is_statbuf_secure(&statbuf); +} + +// (Taken over from Solaris to support the O_NOFOLLOW case on AIX.) +// Check if the given directory file descriptor is considered a secure +// directory for the backing store files. Returns true if the directory +// exists and is considered a secure location. Returns false if the path +// is a symbolic link or if an error occurred. +static bool is_dirfd_secure(int dir_fd) { + struct stat statbuf; + int result = 0; + + RESTARTABLE(::fstat(dir_fd, &statbuf), result); + if (result == OS_ERR) { + return false; + } + + // The path exists, now check its mode. + return is_statbuf_secure(&statbuf); +} + + +// Check to make sure fd1 and fd2 are referencing the same file system object. +static bool is_same_fsobject(int fd1, int fd2) { + struct stat statbuf1; + struct stat statbuf2; + int result = 0; + + RESTARTABLE(::fstat(fd1, &statbuf1), result); + if (result == OS_ERR) { + return false; + } + RESTARTABLE(::fstat(fd2, &statbuf2), result); + if (result == OS_ERR) { + return false; + } + + if ((statbuf1.st_ino == statbuf2.st_ino) && + (statbuf1.st_dev == statbuf2.st_dev)) { + return true; + } else { + return false; + } +} + +// Helper functions for open without O_NOFOLLOW which is not present on AIX 5.3/6.1. +// We use the jdk6 implementation here. +#ifndef O_NOFOLLOW +// The O_NOFOLLOW oflag doesn't exist before solaris 5.10, this is to simulate that behaviour +// was done in jdk 5/6 hotspot by Oracle this way +static int open_o_nofollow_impl(const char* path, int oflag, mode_t mode, bool use_mode) { + struct stat orig_st; + struct stat new_st; + bool create; + int error; + int fd; + + create = false; + + if (lstat(path, &orig_st) != 0) { + if (errno == ENOENT && (oflag & O_CREAT) != 0) { + // File doesn't exist, but_we want to create it, add O_EXCL flag + // to make sure no-one creates it (or a symlink) before us + // This works as we expect with symlinks, from posix man page: + // 'If O_EXCL and O_CREAT are set, and path names a symbolic + // link, open() shall fail and set errno to [EEXIST]'. + oflag |= O_EXCL; + create = true; + } else { + // File doesn't exist, and we are not creating it. + return OS_ERR; + } + } else { + // Lstat success, check if existing file is a link. + if ((orig_st.st_mode & S_IFMT) == S_IFLNK) { + // File is a symlink. + errno = ELOOP; + return OS_ERR; + } + } + + if (use_mode == true) { + fd = open(path, oflag, mode); + } else { + fd = open(path, oflag); + } + + if (fd == OS_ERR) { + return fd; + } + + // Can't do inode checks on before/after if we created the file. + if (create == false) { + if (fstat(fd, &new_st) != 0) { + // Keep errno from fstat, in case close also fails. + error = errno; + ::close(fd); + errno = error; + return OS_ERR; + } + + if (orig_st.st_dev != new_st.st_dev || orig_st.st_ino != new_st.st_ino) { + // File was tampered with during race window. + ::close(fd); + errno = EEXIST; + if (PrintMiscellaneous && Verbose) { + warning("possible file tampering attempt detected when opening %s", path); + } + return OS_ERR; + } + } + + return fd; +} + +static int open_o_nofollow(const char* path, int oflag, mode_t mode) { + return open_o_nofollow_impl(path, oflag, mode, true); +} + +static int open_o_nofollow(const char* path, int oflag) { + return open_o_nofollow_impl(path, oflag, 0, false); +} +#endif + +// Open the directory of the given path and validate it. +// Return a DIR * of the open directory. +static DIR *open_directory_secure(const char* dirname) { + // Open the directory using open() so that it can be verified + // to be secure by calling is_dirfd_secure(), opendir() and then check + // to see if they are the same file system object. This method does not + // introduce a window of opportunity for the directory to be attacked that + // calling opendir() and is_directory_secure() does. + int result; + DIR *dirp = NULL; + + // No O_NOFOLLOW defined at buildtime, and it is not documented for open; + // so provide a workaround in this case. +#ifdef O_NOFOLLOW + RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result); +#else + // workaround (jdk6 coding) + RESTARTABLE(::open_o_nofollow(dirname, O_RDONLY), result); +#endif + + if (result == OS_ERR) { + // Directory doesn't exist or is a symlink, so there is nothing to cleanup. + if (PrintMiscellaneous && Verbose) { + if (errno == ELOOP) { + warning("directory %s is a symlink and is not secure\n", dirname); + } else { + warning("could not open directory %s: %s\n", dirname, strerror(errno)); + } + } + return dirp; + } + int fd = result; + + // Determine if the open directory is secure. + if (!is_dirfd_secure(fd)) { + // The directory is not a secure directory. + os::close(fd); + return dirp; + } + + // Open the directory. + dirp = ::opendir(dirname); + if (dirp == NULL) { + // The directory doesn't exist, close fd and return. + os::close(fd); + return dirp; + } + + // Check to make sure fd and dirp are referencing the same file system object. + if (!is_same_fsobject(fd, dirp->dd_fd)) { + // The directory is not secure. + os::close(fd); + os::closedir(dirp); + dirp = NULL; + return dirp; + } + + // Close initial open now that we know directory is secure + os::close(fd); + + return dirp; +} + +// NOTE: The code below uses fchdir(), open() and unlink() because +// fdopendir(), openat() and unlinkat() are not supported on all +// versions. Once the support for fdopendir(), openat() and unlinkat() +// is available on all supported versions the code can be changed +// to use these functions. + +// Open the directory of the given path, validate it and set the +// current working directory to it. +// Return a DIR * of the open directory and the saved cwd fd. +// +static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) { + + // Open the directory. + DIR* dirp = open_directory_secure(dirname); + if (dirp == NULL) { + // Directory doesn't exist or is insecure, so there is nothing to cleanup. + return dirp; + } + int fd = dirp->dd_fd; + + // Open a fd to the cwd and save it off. + int result; + RESTARTABLE(::open(".", O_RDONLY), result); + if (result == OS_ERR) { + *saved_cwd_fd = -1; + } else { + *saved_cwd_fd = result; + } + + // Set the current directory to dirname by using the fd of the directory. + result = fchdir(fd); + + return dirp; +} + +// Close the directory and restore the current working directory. +static void close_directory_secure_cwd(DIR* dirp, int saved_cwd_fd) { + + int result; + // If we have a saved cwd change back to it and close the fd. + if (saved_cwd_fd != -1) { + result = fchdir(saved_cwd_fd); + ::close(saved_cwd_fd); + } + + // Close the directory. + os::closedir(dirp); +} + +// Check if the given file descriptor is considered a secure. +static bool is_file_secure(int fd, const char *filename) { + + int result; + struct stat statbuf; + + // Determine if the file is secure. + RESTARTABLE(::fstat(fd, &statbuf), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("fstat failed on %s: %s\n", filename, strerror(errno)); + } + return false; + } + if (statbuf.st_nlink > 1) { + // A file with multiple links is not expected. + if (PrintMiscellaneous && Verbose) { + warning("file %s has multiple links\n", filename); + } + return false; + } + return true; +} + +// Return the user name for the given user id. +// +// The caller is expected to free the allocated memory. +static char* get_user_name(uid_t uid) { + + struct passwd pwent; + + // Determine the max pwbuf size from sysconf, and hardcode + // a default if this not available through sysconf. + long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) + bufsize = 1024; + + char* pwbuf = NEW_C_HEAP_ARRAY(char, bufsize, mtInternal); + + // POSIX interface to getpwuid_r is used on LINUX + struct passwd* p; + int result = getpwuid_r(uid, &pwent, pwbuf, (size_t)bufsize, &p); + + if (result != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { + if (PrintMiscellaneous && Verbose) { + if (result != 0) { + warning("Could not retrieve passwd entry: %s\n", + strerror(result)); + } + else if (p == NULL) { + // this check is added to protect against an observed problem + // with getpwuid_r() on RedHat 9 where getpwuid_r returns 0, + // indicating success, but has p == NULL. This was observed when + // inserting a file descriptor exhaustion fault prior to the call + // getpwuid_r() call. In this case, error is set to the appropriate + // error condition, but this is undocumented behavior. This check + // is safe under any condition, but the use of errno in the output + // message may result in an erroneous message. + // Bug Id 89052 was opened with RedHat. + // + warning("Could not retrieve passwd entry: %s\n", + strerror(errno)); + } + else { + warning("Could not determine user name: %s\n", + p->pw_name == NULL ? "pw_name = NULL" : + "pw_name zero length"); + } + } + FREE_C_HEAP_ARRAY(char, pwbuf, mtInternal); + return NULL; + } + + char* user_name = NEW_C_HEAP_ARRAY(char, strlen(p->pw_name) + 1, mtInternal); + strcpy(user_name, p->pw_name); + + FREE_C_HEAP_ARRAY(char, pwbuf, mtInternal); + return user_name; +} + +// return the name of the user that owns the process identified by vmid. +// +// This method uses a slow directory search algorithm to find the backing +// store file for the specified vmid and returns the user name, as determined +// by the user name suffix of the hsperfdata_ directory name. +// +// the caller is expected to free the allocated memory. +// +static char* get_user_name_slow(int vmid, TRAPS) { + + // short circuit the directory search if the process doesn't even exist. + if (kill(vmid, 0) == OS_ERR) { + if (errno == ESRCH) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + else /* EPERM */ { + THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); + } + } + + // directory search + char* oldest_user = NULL; + time_t oldest_ctime = 0; + + const char* tmpdirname = os::get_temp_directory(); + + DIR* tmpdirp = os::opendir(tmpdirname); + + if (tmpdirp == NULL) { + return NULL; + } + + // for each entry in the directory that matches the pattern hsperfdata_*, + // open the directory and check if the file for the given vmid exists. + // The file with the expected name and the latest creation date is used + // to determine the user name for the process id. + // + struct dirent* dentry; + char* tdbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(tmpdirname), mtInternal); + errno = 0; + while ((dentry = os::readdir(tmpdirp, (struct dirent *)tdbuf)) != NULL) { + + // check if the directory entry is a hsperfdata file + if (strncmp(dentry->d_name, PERFDATA_NAME, strlen(PERFDATA_NAME)) != 0) { + continue; + } + + char* usrdir_name = NEW_C_HEAP_ARRAY(char, + strlen(tmpdirname) + strlen(dentry->d_name) + 2, mtInternal); + strcpy(usrdir_name, tmpdirname); + strcat(usrdir_name, "/"); + strcat(usrdir_name, dentry->d_name); + + // Open the user directory. + DIR* subdirp = open_directory_secure(usrdir_name); + + if (subdirp == NULL) { + FREE_C_HEAP_ARRAY(char, usrdir_name, mtInternal); + continue; + } + + // Since we don't create the backing store files in directories + // pointed to by symbolic links, we also don't follow them when + // looking for the files. We check for a symbolic link after the + // call to opendir in order to eliminate a small window where the + // symlink can be exploited. + // + if (!is_directory_secure(usrdir_name)) { + FREE_C_HEAP_ARRAY(char, usrdir_name, mtInternal); + os::closedir(subdirp); + continue; + } + + struct dirent* udentry; + char* udbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(usrdir_name), mtInternal); + errno = 0; + while ((udentry = os::readdir(subdirp, (struct dirent *)udbuf)) != NULL) { + + if (filename_to_pid(udentry->d_name) == vmid) { + struct stat statbuf; + int result; + + char* filename = NEW_C_HEAP_ARRAY(char, + strlen(usrdir_name) + strlen(udentry->d_name) + 2, mtInternal); + + strcpy(filename, usrdir_name); + strcat(filename, "/"); + strcat(filename, udentry->d_name); + + // don't follow symbolic links for the file + RESTARTABLE(::lstat(filename, &statbuf), result); + if (result == OS_ERR) { + FREE_C_HEAP_ARRAY(char, filename, mtInternal); + continue; + } + + // skip over files that are not regular files. + if (!S_ISREG(statbuf.st_mode)) { + FREE_C_HEAP_ARRAY(char, filename, mtInternal); + continue; + } + + // compare and save filename with latest creation time + if (statbuf.st_size > 0 && statbuf.st_ctime > oldest_ctime) { + + if (statbuf.st_ctime > oldest_ctime) { + char* user = strchr(dentry->d_name, '_') + 1; + + if (oldest_user != NULL) FREE_C_HEAP_ARRAY(char, oldest_user, mtInternal); + oldest_user = NEW_C_HEAP_ARRAY(char, strlen(user)+1, mtInternal); + + strcpy(oldest_user, user); + oldest_ctime = statbuf.st_ctime; + } + } + + FREE_C_HEAP_ARRAY(char, filename, mtInternal); + } + } + os::closedir(subdirp); + FREE_C_HEAP_ARRAY(char, udbuf, mtInternal); + FREE_C_HEAP_ARRAY(char, usrdir_name, mtInternal); + } + os::closedir(tmpdirp); + FREE_C_HEAP_ARRAY(char, tdbuf, mtInternal); + + return(oldest_user); +} + +// return the name of the user that owns the JVM indicated by the given vmid. +// +static char* get_user_name(int vmid, TRAPS) { + return get_user_name_slow(vmid, CHECK_NULL); +} + +// return the file name of the backing store file for the named +// shared memory region for the given user name and vmid. +// +// the caller is expected to free the allocated memory. +// +static char* get_sharedmem_filename(const char* dirname, int vmid) { + + // add 2 for the file separator and a null terminator. + size_t nbytes = strlen(dirname) + UINT_CHARS + 2; + + char* name = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); + snprintf(name, nbytes, "%s/%d", dirname, vmid); + + return name; +} + + +// remove file +// +// this method removes the file specified by the given path +// +static void remove_file(const char* path) { + + int result; + + // if the file is a directory, the following unlink will fail. since + // we don't expect to find directories in the user temp directory, we + // won't try to handle this situation. even if accidentially or + // maliciously planted, the directory's presence won't hurt anything. + // + RESTARTABLE(::unlink(path), result); + if (PrintMiscellaneous && Verbose && result == OS_ERR) { + if (errno != ENOENT) { + warning("Could not unlink shared memory backing" + " store file %s : %s\n", path, strerror(errno)); + } + } +} + +// Cleanup stale shared memory resources +// +// This method attempts to remove all stale shared memory files in +// the named user temporary directory. It scans the named directory +// for files matching the pattern ^$[0-9]*$. For each file found, the +// process id is extracted from the file name and a test is run to +// determine if the process is alive. If the process is not alive, +// any stale file resources are removed. +static void cleanup_sharedmem_resources(const char* dirname) { + + int saved_cwd_fd; + // Open the directory. + DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd); + if (dirp == NULL) { + // Directory doesn't exist or is insecure, so there is nothing to cleanup. + return; + } + + // For each entry in the directory that matches the expected file + // name pattern, determine if the file resources are stale and if + // so, remove the file resources. Note, instrumented HotSpot processes + // for this user may start and/or terminate during this search and + // remove or create new files in this directory. The behavior of this + // loop under these conditions is dependent upon the implementation of + // opendir/readdir. + struct dirent* entry; + char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname), mtInternal); + + errno = 0; + while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) { + + pid_t pid = filename_to_pid(entry->d_name); + + if (pid == 0) { + + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + + // Attempt to remove all unexpected files, except "." and "..". + unlink(entry->d_name); + } + + errno = 0; + continue; + } + + // We now have a file name that converts to a valid integer + // that could represent a process id . if this process id + // matches the current process id or the process is not running, + // then remove the stale file resources. + // + // Process liveness is detected by sending signal number 0 to + // the process id (see kill(2)). if kill determines that the + // process does not exist, then the file resources are removed. + // if kill determines that that we don't have permission to + // signal the process, then the file resources are assumed to + // be stale and are removed because the resources for such a + // process should be in a different user specific directory. + if ((pid == os::current_process_id()) || + (kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) { + + unlink(entry->d_name); + } + errno = 0; + } + + // Close the directory and reset the current working directory. + close_directory_secure_cwd(dirp, saved_cwd_fd); + + FREE_C_HEAP_ARRAY(char, dbuf, mtInternal); +} + +// Make the user specific temporary directory. Returns true if +// the directory exists and is secure upon return. Returns false +// if the directory exists but is either a symlink, is otherwise +// insecure, or if an error occurred. +static bool make_user_tmp_dir(const char* dirname) { + + // Create the directory with 0755 permissions. note that the directory + // will be owned by euid::egid, which may not be the same as uid::gid. + if (mkdir(dirname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == OS_ERR) { + if (errno == EEXIST) { + // The directory already exists and was probably created by another + // JVM instance. However, this could also be the result of a + // deliberate symlink. Verify that the existing directory is safe. + if (!is_directory_secure(dirname)) { + // Directory is not secure. + if (PrintMiscellaneous && Verbose) { + warning("%s directory is insecure\n", dirname); + } + return false; + } + } + else { + // we encountered some other failure while attempting + // to create the directory + // + if (PrintMiscellaneous && Verbose) { + warning("could not create directory %s: %s\n", + dirname, strerror(errno)); + } + return false; + } + } + return true; +} + +// create the shared memory file resources +// +// This method creates the shared memory file with the given size +// This method also creates the user specific temporary directory, if +// it does not yet exist. +// +static int create_sharedmem_resources(const char* dirname, const char* filename, size_t size) { + + // make the user temporary directory + if (!make_user_tmp_dir(dirname)) { + // could not make/find the directory or the found directory + // was not secure + return -1; + } + + int saved_cwd_fd; + // Open the directory and set the current working directory to it. + DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd); + if (dirp == NULL) { + // Directory doesn't exist or is insecure, so cannot create shared + // memory file. + return -1; + } + + // Open the filename in the current directory. + // Cannot use O_TRUNC here; truncation of an existing file has to happen + // after the is_file_secure() check below. + int result; + + // No O_NOFOLLOW defined at buildtime, and it is not documented for open; + // so provide a workaround in this case. +#ifdef O_NOFOLLOW + RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IREAD|S_IWRITE), result); +#else + // workaround function (jdk6 code) + RESTARTABLE(::open_o_nofollow(filename, O_RDWR|O_CREAT, S_IREAD|S_IWRITE), result); +#endif + + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + if (errno == ELOOP) { + warning("file %s is a symlink and is not secure\n", filename); + } else { + warning("could not create file %s: %s\n", filename, strerror(errno)); + } + } + // Close the directory and reset the current working directory. + close_directory_secure_cwd(dirp, saved_cwd_fd); + + return -1; + } + // Close the directory and reset the current working directory. + close_directory_secure_cwd(dirp, saved_cwd_fd); + + // save the file descriptor + int fd = result; + + // Check to see if the file is secure. + if (!is_file_secure(fd, filename)) { + ::close(fd); + return -1; + } + + // Truncate the file to get rid of any existing data. + RESTARTABLE(::ftruncate(fd, (off_t)0), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not truncate shared memory file: %s\n", strerror(errno)); + } + ::close(fd); + return -1; + } + // set the file size + RESTARTABLE(::ftruncate(fd, (off_t)size), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not set shared memory file size: %s\n", strerror(errno)); + } + RESTARTABLE(::close(fd), result); + return -1; + } + + return fd; +} + +// open the shared memory file for the given user and vmid. returns +// the file descriptor for the open file or -1 if the file could not +// be opened. +// +static int open_sharedmem_file(const char* filename, int oflags, TRAPS) { + + // open the file + int result; + // No O_NOFOLLOW defined at buildtime, and it is not documented for open; + // so provide a workaround in this case +#ifdef O_NOFOLLOW + RESTARTABLE(::open(filename, oflags), result); +#else + RESTARTABLE(::open_o_nofollow(filename, oflags), result); +#endif + + if (result == OS_ERR) { + if (errno == ENOENT) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + else if (errno == EACCES) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Permission denied"); + } + else { + THROW_MSG_0(vmSymbols::java_io_IOException(), strerror(errno)); + } + } + int fd = result; + + // Check to see if the file is secure. + if (!is_file_secure(fd, filename)) { + ::close(fd); + return -1; + } + + return fd; +} + +// create a named shared memory region. returns the address of the +// memory region on success or NULL on failure. A return value of +// NULL will ultimately disable the shared memory feature. +// +// On Solaris and Linux, the name space for shared memory objects +// is the file system name space. +// +// A monitoring application attaching to a JVM does not need to know +// the file system name of the shared memory object. However, it may +// be convenient for applications to discover the existence of newly +// created and terminating JVMs by watching the file system name space +// for files being created or removed. +// +static char* mmap_create_shared(size_t size) { + + int result; + int fd; + char* mapAddress; + + int vmid = os::current_process_id(); + + char* user_name = get_user_name(geteuid()); + + if (user_name == NULL) + return NULL; + + char* dirname = get_user_tmp_dir(user_name); + char* filename = get_sharedmem_filename(dirname, vmid); + + // Get the short filename. + char* short_filename = strrchr(filename, '/'); + if (short_filename == NULL) { + short_filename = filename; + } else { + short_filename++; + } + + // cleanup any stale shared memory files + cleanup_sharedmem_resources(dirname); + + assert(((size > 0) && (size % os::vm_page_size() == 0)), + "unexpected PerfMemory region size"); + + fd = create_sharedmem_resources(dirname, short_filename, size); + + FREE_C_HEAP_ARRAY(char, user_name, mtInternal); + FREE_C_HEAP_ARRAY(char, dirname, mtInternal); + + if (fd == -1) { + FREE_C_HEAP_ARRAY(char, filename, mtInternal); + return NULL; + } + + mapAddress = (char*)::mmap((char*)0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + // attempt to close the file - restart it if it was interrupted, + // but ignore other failures + RESTARTABLE(::close(fd), result); + assert(result != OS_ERR, "could not close file"); + + if (mapAddress == MAP_FAILED) { + if (PrintMiscellaneous && Verbose) { + warning("mmap failed - %s\n", strerror(errno)); + } + remove_file(filename); + FREE_C_HEAP_ARRAY(char, filename, mtInternal); + return NULL; + } + + // save the file name for use in delete_shared_memory() + backing_store_file_name = filename; + + // clear the shared memory region + (void)::memset((void*) mapAddress, 0, size); + + // It does not go through os api, the operation has to record from here. + MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + + return mapAddress; +} + +// release a named shared memory region +// +static void unmap_shared(char* addr, size_t bytes) { + // Do not rely on os::reserve_memory/os::release_memory to use mmap. + // Use os::reserve_memory/os::release_memory for PerfDisableSharedMem=1, mmap/munmap for PerfDisableSharedMem=0 + if (::munmap(addr, bytes) == -1) { + warning("perfmemory: munmap failed (%d)\n", errno); + } +} + +// create the PerfData memory region in shared memory. +// +static char* create_shared_memory(size_t size) { + + // create the shared memory region. + return mmap_create_shared(size); +} + +// delete the shared PerfData memory region +// +static void delete_shared_memory(char* addr, size_t size) { + + // cleanup the persistent shared memory resources. since DestroyJavaVM does + // not support unloading of the JVM, unmapping of the memory resource is + // not performed. The memory will be reclaimed by the OS upon termination of + // the process. The backing store file is deleted from the file system. + + assert(!PerfDisableSharedMem, "shouldn't be here"); + + if (backing_store_file_name != NULL) { + remove_file(backing_store_file_name); + // Don't.. Free heap memory could deadlock os::abort() if it is called + // from signal handler. OS will reclaim the heap memory. + // FREE_C_HEAP_ARRAY(char, backing_store_file_name, mtInternal); + backing_store_file_name = NULL; + } +} + +// return the size of the file for the given file descriptor +// or 0 if it is not a valid size for a shared memory file +// +static size_t sharedmem_filesize(int fd, TRAPS) { + + struct stat statbuf; + int result; + + RESTARTABLE(::fstat(fd, &statbuf), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("fstat failed: %s\n", strerror(errno)); + } + THROW_MSG_0(vmSymbols::java_io_IOException(), + "Could not determine PerfMemory size"); + } + + if ((statbuf.st_size == 0) || + ((size_t)statbuf.st_size % os::vm_page_size() != 0)) { + THROW_MSG_0(vmSymbols::java_lang_Exception(), + "Invalid PerfMemory size"); + } + + return (size_t)statbuf.st_size; +} + +// attach to a named shared memory region. +// +static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemoryMode mode, char** addr, size_t* sizep, TRAPS) { + + char* mapAddress; + int result; + int fd; + size_t size = 0; + const char* luser = NULL; + + int mmap_prot; + int file_flags; + + ResourceMark rm; + + // map the high level access mode to the appropriate permission + // constructs for the file and the shared memory mapping. + if (mode == PerfMemory::PERF_MODE_RO) { + mmap_prot = PROT_READ; + + // No O_NOFOLLOW defined at buildtime, and it is not documented for open. +#ifdef O_NOFOLLOW + file_flags = O_RDONLY | O_NOFOLLOW; +#else + file_flags = O_RDONLY; +#endif + } + else if (mode == PerfMemory::PERF_MODE_RW) { +#ifdef LATER + mmap_prot = PROT_READ | PROT_WRITE; + file_flags = O_RDWR | O_NOFOLLOW; +#else + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Unsupported access mode"); +#endif + } + else { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Illegal access mode"); + } + + if (user == NULL || strlen(user) == 0) { + luser = get_user_name(vmid, CHECK); + } + else { + luser = user; + } + + if (luser == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Could not map vmid to user Name"); + } + + char* dirname = get_user_tmp_dir(luser); + + // since we don't follow symbolic links when creating the backing + // store file, we don't follow them when attaching either. + // + if (!is_directory_secure(dirname)) { + FREE_C_HEAP_ARRAY(char, dirname, mtInternal); + if (luser != user) { + FREE_C_HEAP_ARRAY(char, luser, mtInternal); + } + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + "Process not found"); + } + + char* filename = get_sharedmem_filename(dirname, vmid); + + // copy heap memory to resource memory. the open_sharedmem_file + // method below need to use the filename, but could throw an + // exception. using a resource array prevents the leak that + // would otherwise occur. + char* rfilename = NEW_RESOURCE_ARRAY(char, strlen(filename) + 1); + strcpy(rfilename, filename); + + // free the c heap resources that are no longer needed + if (luser != user) FREE_C_HEAP_ARRAY(char, luser, mtInternal); + FREE_C_HEAP_ARRAY(char, dirname, mtInternal); + FREE_C_HEAP_ARRAY(char, filename, mtInternal); + + // open the shared memory file for the give vmid + fd = open_sharedmem_file(rfilename, file_flags, CHECK); + assert(fd != OS_ERR, "unexpected value"); + + if (*sizep == 0) { + size = sharedmem_filesize(fd, CHECK); + assert(size != 0, "unexpected size"); + } else { + size = *sizep; + } + + mapAddress = (char*)::mmap((char*)0, size, mmap_prot, MAP_SHARED, fd, 0); + + // attempt to close the file - restart if it gets interrupted, + // but ignore other failures + RESTARTABLE(::close(fd), result); + assert(result != OS_ERR, "could not close file"); + + if (mapAddress == MAP_FAILED) { + if (PrintMiscellaneous && Verbose) { + warning("mmap failed: %s\n", strerror(errno)); + } + THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), + "Could not map PerfMemory"); + } + + // It does not go through os api, the operation has to record from here. + MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + + *addr = mapAddress; + *sizep = size; + + if (PerfTraceMemOps) { + tty->print("mapped " SIZE_FORMAT " bytes for vmid %d at " + INTPTR_FORMAT "\n", size, vmid, (void*)mapAddress); + } +} + + + + +// create the PerfData memory region +// +// This method creates the memory region used to store performance +// data for the JVM. The memory may be created in standard or +// shared memory. +// +void PerfMemory::create_memory_region(size_t size) { + + if (PerfDisableSharedMem) { + // do not share the memory for the performance data. + _start = create_standard_memory(size); + } + else { + _start = create_shared_memory(size); + if (_start == NULL) { + + // creation of the shared memory region failed, attempt + // to create a contiguous, non-shared memory region instead. + // + if (PrintMiscellaneous && Verbose) { + warning("Reverting to non-shared PerfMemory region.\n"); + } + PerfDisableSharedMem = true; + _start = create_standard_memory(size); + } + } + + if (_start != NULL) _capacity = size; + +} + +// delete the PerfData memory region +// +// This method deletes the memory region used to store performance +// data for the JVM. The memory region indicated by the +// tuple will be inaccessible after a call to this method. +// +void PerfMemory::delete_memory_region() { + + assert((start() != NULL && capacity() > 0), "verify proper state"); + + // If user specifies PerfDataSaveFile, it will save the performance data + // to the specified file name no matter whether PerfDataSaveToFile is specified + // or not. In other word, -XX:PerfDataSaveFile=.. overrides flag + // -XX:+PerfDataSaveToFile. + if (PerfDataSaveToFile || PerfDataSaveFile != NULL) { + save_memory_to_file(start(), capacity()); + } + + if (PerfDisableSharedMem) { + delete_standard_memory(start(), capacity()); + } + else { + delete_shared_memory(start(), capacity()); + } +} + +// attach to the PerfData memory region for another JVM +// +// This method returns an tuple that points to +// a memory buffer that is kept reasonably synchronized with +// the PerfData memory region for the indicated JVM. This +// buffer may be kept in synchronization via shared memory +// or some other mechanism that keeps the buffer updated. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to map +// the indicated process's PerfData memory region into this JVMs +// address space. +// +void PerfMemory::attach(const char* user, int vmid, PerfMemoryMode mode, char** addrp, size_t* sizep, TRAPS) { + + if (vmid == 0 || vmid == os::current_process_id()) { + *addrp = start(); + *sizep = capacity(); + return; + } + + mmap_attach_shared(user, vmid, mode, addrp, sizep, CHECK); +} + +// detach from the PerfData memory region of another JVM +// +// This method detaches the PerfData memory region of another +// JVM, specified as an tuple of a buffer +// in this process's address space. This method may perform +// arbitrary actions to accomplish the detachment. The memory +// region specified by will be inaccessible after +// a call to this method. +// +// If the JVM chooses not to support the attachability feature, +// this method should throw an UnsupportedOperation exception. +// +// This implementation utilizes named shared memory to detach +// the indicated process's PerfData memory region from this +// process's address space. +// +void PerfMemory::detach(char* addr, size_t bytes, TRAPS) { + + assert(addr != 0, "address sanity check"); + assert(bytes > 0, "capacity sanity check"); + + if (PerfMemory::contains(addr) || PerfMemory::contains(addr + bytes - 1)) { + // prevent accidental detachment of this process's PerfMemory region + return; + } + + unmap_shared(addr, bytes); +} + +char* PerfMemory::backing_store_filename() { + return backing_store_file_name; +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/porting_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/porting_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,369 @@ +/* + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "loadlib_aix.hpp" +#include "porting_aix.hpp" +#include "utilities/debug.hpp" +#ifdef TARGET_ARCH_ppc +# include "assembler_ppc.hpp" +#endif + +#include +#include + +////////////////////////////////// +// Provide implementation for dladdr based on LoadedLibraries pool and +// traceback table scan (see getFuncName). + +// Search traceback table in stack, +// return procedure name from trace back table. +#define MAX_FUNC_SEARCH_LEN 0x10000 +// Any PC below this value is considered toast. +#define MINIMUM_VALUE_FOR_PC ((unsigned int*)0x1024) + +#define PTRDIFF_BYTES(p1,p2) (((ptrdiff_t)p1) - ((ptrdiff_t)p2)) + +// Align a pointer without having to cast. +inline char* align_ptr_up(char* ptr, intptr_t alignment) { + return (char*) align_size_up((intptr_t)ptr, alignment); +} + +// Trace if verbose to tty. +// I use these now instead of the Xtrace system because the latter is +// not available at init time, hence worthless. Until we fix this, all +// tracing here is done with -XX:+Verbose. +#define trcVerbose(fmt, ...) { \ + if (Verbose) { \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + fputc('\n', stderr); fflush(stderr); \ + } \ +} +#define ERRBYE(s) { trcVerbose(s); return -1; } + +// Unfortunately, the interface of dladdr makes the implementator +// responsible for maintaining memory for function name/library +// name. I guess this is because most OS's keep those values as part +// of the mapped executable image ready to use. On AIX, this doesn't +// work, so I have to keep the returned strings. For now, I do this in +// a primitive string map. Should this turn out to be a performance +// problem, a better hashmap has to be used. +class fixed_strings { + struct node { + char* v; + node* next; + }; + + node* first; + + public: + + fixed_strings() : first(0) {} + ~fixed_strings() { + node* n = first; + while (n) { + node* p = n; + n = n->next; + free(p->v); + delete p; + } + } + + char* intern(const char* s) { + for (node* n = first; n; n = n->next) { + if (strcmp(n->v, s) == 0) { + return n->v; + } + } + node* p = new node; + p->v = strdup(s); + p->next = first; + first = p; + return p->v; + } +}; + +static fixed_strings dladdr_fixed_strings; + +// Given a code pointer, returns the function name and the displacement. +// Function looks for the traceback table at the end of the function. +extern "C" int getFuncName( + codeptr_t pc, // [in] program counter + char* p_name, size_t namelen, // [out] optional: function name ("" if not available) + int* p_displacement, // [out] optional: displacement (-1 if not available) + const struct tbtable** p_tb, // [out] optional: ptr to traceback table to get further + // information (NULL if not available) + char* p_errmsg, size_t errmsglen // [out] optional: user provided buffer for error messages + ) { + struct tbtable* tb = 0; + unsigned int searchcount = 0; + + // initialize output parameters + if (p_name && namelen > 0) { + *p_name = '\0'; + } + if (p_errmsg && errmsglen > 0) { + *p_errmsg = '\0'; + } + if (p_displacement) { + *p_displacement = -1; + } + if (p_tb) { + *p_tb = NULL; + } + + // weed out obvious bogus states + if (pc < MINIMUM_VALUE_FOR_PC) { + ERRBYE("invalid program counter"); + } + + codeptr_t pc2 = pc; + + // make sure the pointer is word aligned. + pc2 = (codeptr_t) align_ptr_up((char*)pc2, 4); + + // Find start of traceback table. + // (starts after code, is marked by word-aligned (32bit) zeros) + while ((*pc2 != NULL) && (searchcount++ < MAX_FUNC_SEARCH_LEN)) { + pc2++; + } + if (*pc2 != 0) { + ERRBYE("could not find traceback table within 5000 bytes of program counter"); + } + // + // Set up addressability to the traceback table + // + tb = (struct tbtable*) (pc2 + 1); + + // Is this really a traceback table? No way to be sure but + // some indicators we can check. + if (tb->tb.lang >= 0xf && tb->tb.lang <= 0xfb) { + // Language specifiers, go from 0 (C) to 14 (Objective C). + // According to spec, 0xf-0xfa reserved, 0xfb-0xff reserved for ibm. + ERRBYE("not a traceback table"); + } + + // Existence of fields in the tbtable extension are contingent upon + // specific fields in the base table. Check for their existence so + // that we can address the function name if it exists. + pc2 = (codeptr_t) tb + + sizeof(struct tbtable_short)/sizeof(int); + if (tb->tb.fixedparms != 0 || tb->tb.floatparms != 0) + pc2++; + + if (tb->tb.has_tboff == TRUE) { + + // I want to know the displacement + const unsigned int tb_offset = *pc2; + codeptr_t start_of_procedure = + (codeptr_t)(((char*)tb) - 4 - tb_offset); // (-4 to omit leading 0000) + + // Weed out the cases where we did find the wrong traceback table. + if (pc < start_of_procedure) { + ERRBYE("could not find (the real) traceback table within 5000 bytes of program counter"); + } + + // return the displacement + if (p_displacement) { + (*p_displacement) = (int) PTRDIFF_BYTES(pc, start_of_procedure); + } + + pc2++; + } else { + // return -1 for displacement + if (p_displacement) { + (*p_displacement) = -1; + } + } + + if (tb->tb.int_hndl == TRUE) + pc2++; + + if (tb->tb.has_ctl == TRUE) + pc2 += (*pc2) + 1; // don't care + + // + // return function name if it exists. + // + if (p_name && namelen > 0) { + if (tb->tb.name_present) { + char buf[256]; + const short l = MIN2(*((short*)pc2), sizeof(buf) - 1); + memcpy(buf, (char*)pc2 + sizeof(short), l); + buf[l] = '\0'; + + p_name[0] = '\0'; + + // If it is a C++ name, try and demangle it using the Demangle interface (see demangle.h). + char* rest; + Name* const name = Demangle(buf, rest); + if (name) { + const char* const demangled_name = name->Text(); + if (demangled_name) { + strncpy(p_name, demangled_name, namelen-1); + p_name[namelen-1] = '\0'; + } + delete name; + } + + // Fallback: if demangling did not work, just provide the unmangled name. + if (p_name[0] == '\0') { + strncpy(p_name, buf, namelen-1); + p_name[namelen-1] = '\0'; + } + + } else { + strncpy(p_name, "", namelen-1); + p_name[namelen-1] = '\0'; + } + } + // Return traceback table, if user wants it. + if (p_tb) { + (*p_tb) = tb; + } + + return 0; +} + +// Special implementation of dladdr for Aix based on LoadedLibraries +// Note: dladdr returns non-zero for ok, 0 for error! +// Note: dladdr is not posix, but a non-standard GNU extension. So this tries to +// fulfill the contract of dladdr on Linux (see http://linux.die.net/man/3/dladdr) +// Note: addr may be both an AIX function descriptor or a real code pointer +// to the entry of a function. +extern "C" +int dladdr(void* addr, Dl_info* info) { + + if (!addr) { + return 0; + } + + assert(info, ""); + + int rc = 0; + + const char* const ZEROSTRING = ""; + + // Always return a string, even if a "" one. Linux dladdr manpage + // does not say anything about returning NULL + info->dli_fname = ZEROSTRING; + info->dli_sname = ZEROSTRING; + info->dli_saddr = NULL; + + address p = (address) addr; + const LoadedLibraryModule* lib = NULL; + + enum { noclue, code, data } type = noclue; + + trcVerbose("dladdr(%p)...", p); + + // Note: input address may be a function. I accept both a pointer to + // the entry of a function and a pointer to the function decriptor. + // (see ppc64 ABI) + lib = LoadedLibraries::find_for_text_address(p); + if (lib) { + type = code; + } + + if (!lib) { + // Not a pointer into any text segment. Is it a function descriptor? + const FunctionDescriptor* const pfd = (const FunctionDescriptor*) p; + p = pfd->entry(); + if (p) { + lib = LoadedLibraries::find_for_text_address(p); + if (lib) { + type = code; + } + } + } + + if (!lib) { + // Neither direct code pointer nor function descriptor. A data ptr? + p = (address)addr; + lib = LoadedLibraries::find_for_data_address(p); + if (lib) { + type = data; + } + } + + // If we did find the shared library this address belongs to (either + // code or data segment) resolve library path and, if possible, the + // symbol name. + if (lib) { + const char* const interned_libpath = + dladdr_fixed_strings.intern(lib->get_fullpath()); + if (interned_libpath) { + info->dli_fname = interned_libpath; + } + + if (type == code) { + + // For code symbols resolve function name and displacement. Use + // displacement to calc start of function. + char funcname[256] = ""; + int displacement = 0; + + if (getFuncName((codeptr_t) p, funcname, sizeof(funcname), &displacement, + NULL, NULL, 0) == 0) { + if (funcname[0] != '\0') { + const char* const interned = dladdr_fixed_strings.intern(funcname); + info->dli_sname = interned; + trcVerbose("... function name: %s ...", interned); + } + + // From the displacement calculate the start of the function. + if (displacement != -1) { + info->dli_saddr = p - displacement; + } else { + info->dli_saddr = p; + } + } else { + + // No traceback table found. Just assume the pointer is it. + info->dli_saddr = p; + + } + + } else if (type == data) { + + // For data symbols. + info->dli_saddr = p; + + } else { + ShouldNotReachHere(); + } + + rc = 1; // success: return 1 [sic] + + } + + // sanity checks. + if (rc) { + assert(info->dli_fname, ""); + assert(info->dli_sname, ""); + assert(info->dli_saddr, ""); + } + + return rc; // error: return 0 [sic] + +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/porting_aix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/porting_aix.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include + +// Header file to contain porting-relevant code which does not have a +// home anywhere else and which can not go into os_.h because +// that header is included inside the os class definition, hence all +// its content is part of the os class. + +// Aix' own version of dladdr(). +// This function tries to mimick dladdr(3) on Linux +// (see http://linux.die.net/man/3/dladdr) +// dladdr(3) is not POSIX but a GNU extension, and is not available on AIX. +// +// Differences between AIX dladdr and Linux dladdr: +// +// 1) Dl_info.dli_fbase: can never work, is disabled. +// A loaded image on AIX is divided in multiple segments, at least two +// (text and data) but potentially also far more. This is because the loader may +// load each member into an own segment, as for instance happens with the libC.a +// 2) Dl_info.dli_sname: This only works for code symbols (functions); for data, a +// zero-length string is returned (""). +// 3) Dl_info.dli_saddr: For code, this will return the entry point of the function, +// not the function descriptor. + +typedef struct { + const char *dli_fname; // file path of loaded library + // void *dli_fbase; + const char *dli_sname; // symbol name; "" if not known + void *dli_saddr; // address of *entry* of function; not function descriptor; +} Dl_info; + +// Note: we export this to use it inside J2se too +#ifdef __cplusplus +extern "C" +#endif +int dladdr(void *addr, Dl_info *info); + + +// The semantics in this file are thus that codeptr_t is a *real code ptr*. +// This means that any function taking codeptr_t as arguments will assume +// a real codeptr and won't handle function descriptors (eg getFuncName), +// whereas functions taking address as args will deal with function +// descriptors (eg os::dll_address_to_library_name). +typedef unsigned int* codeptr_t; + +// helper function - given a program counter, tries to locate the traceback table and +// returns info from it (like, most importantly, function name, displacement of the +// pc inside the function, and the traceback table itself. +#ifdef __cplusplus +extern "C" +#endif +int getFuncName( + codeptr_t pc, // [in] program counter + char* p_name, size_t namelen, // [out] optional: user provided buffer for the function name + int* p_displacement, // [out] optional: displacement + const struct tbtable** p_tb, // [out] optional: ptr to traceback table to get further information + char* p_errmsg, size_t errmsglen // [out] optional: user provided buffer for error messages + ); diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/threadCritical_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/threadCritical_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/threadCritical.hpp" +#include "thread_aix.inline.hpp" + +// put OS-includes here +# include + +// +// See threadCritical.hpp for details of this class. +// + +static pthread_t tc_owner = 0; +static pthread_mutex_t tc_mutex = PTHREAD_MUTEX_INITIALIZER; +static int tc_count = 0; + +void ThreadCritical::initialize() { +} + +void ThreadCritical::release() { +} + +ThreadCritical::ThreadCritical() { + pthread_t self = pthread_self(); + if (self != tc_owner) { + int ret = pthread_mutex_lock(&tc_mutex); + guarantee(ret == 0, "fatal error with pthread_mutex_lock()"); + assert(tc_count == 0, "Lock acquired with illegal reentry count."); + tc_owner = self; + } + tc_count++; +} + +ThreadCritical::~ThreadCritical() { + assert(tc_owner == pthread_self(), "must have correct owner"); + assert(tc_count > 0, "must have correct count"); + + tc_count--; + if (tc_count == 0) { + tc_owner = 0; + int ret = pthread_mutex_unlock(&tc_mutex); + guarantee(ret == 0, "fatal error with pthread_mutex_unlock()"); + } +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/thread_aix.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/thread_aix.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_AIX_VM_THREAD_AIX_INLINE_HPP +#define OS_AIX_VM_THREAD_AIX_INLINE_HPP + +#include "runtime/atomic.hpp" +#include "runtime/prefetch.hpp" +#include "runtime/thread.hpp" +#include "runtime/threadLocalStorage.hpp" + +#include "atomic_aix_ppc.inline.hpp" +#include "orderAccess_aix_ppc.inline.hpp" +#include "prefetch_aix_ppc.inline.hpp" + +// Contains inlined functions for class Thread and ThreadLocalStorage + +inline void ThreadLocalStorage::pd_invalidate_all() {} // nothing to do + +#endif // OS_AIX_VM_THREAD_AIX_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/aix/vm/vmError_aix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/vmError_aix.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2003, 2010, 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/arguments.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.hpp" +#include "utilities/vmError.hpp" + +#include +#include +#include +#include + +void VMError::show_message_box(char *buf, int buflen) { + bool yes; + do { + error_string(buf, buflen); + int len = (int)strlen(buf); + char *p = &buf[len]; + + jio_snprintf(p, buflen - len, + "\n\n" + "Do you want to debug the problem?\n\n" + "To debug, run 'dbx -a %d'; then switch to thread tid " INTX_FORMAT ", k-tid " INTX_FORMAT "\n" + "Enter 'yes' to launch dbx automatically (PATH must include dbx)\n" + "Otherwise, press RETURN to abort...", + os::current_process_id(), + os::current_thread_id(), thread_self()); + + yes = os::message_box("Unexpected Error", buf); + + if (yes) { + // yes, user asked VM to launch debugger + jio_snprintf(buf, buflen, "dbx -a %d", os::current_process_id()); + + os::fork_and_exec(buf); + yes = false; + } + } while (yes); +} + +// Handle all synchronous signals which may happen during signal handling, +// not just SIGSEGV and SIGBUS. +static const int SIGNALS[] = { SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP }; // add more if needed +static const int NUM_SIGNALS = sizeof(SIGNALS) / sizeof(int); + +// Space for our "saved" signal flags and handlers +static int resettedSigflags[NUM_SIGNALS]; +static address resettedSighandler[NUM_SIGNALS]; + +static void save_signal(int idx, int sig) { + struct sigaction sa; + sigaction(sig, NULL, &sa); + resettedSigflags[idx] = sa.sa_flags; + resettedSighandler[idx] = (sa.sa_flags & SA_SIGINFO) + ? CAST_FROM_FN_PTR(address, sa.sa_sigaction) + : CAST_FROM_FN_PTR(address, sa.sa_handler); +} + +int VMError::get_resetted_sigflags(int sig) { + // Handle all program errors. + for (int i = 0; i < NUM_SIGNALS; i++) { + if (SIGNALS[i] == sig) { + return resettedSigflags[i]; + } + } + return -1; +} + +address VMError::get_resetted_sighandler(int sig) { + // Handle all program errors. + for (int i = 0; i < NUM_SIGNALS; i++) { + if (SIGNALS[i] == sig) { + return resettedSighandler[i]; + } + } + return NULL; +} + +static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { + // Unmask current signal. + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + + Unimplemented(); +} + +void VMError::reset_signal_handlers() { + sigset_t newset; + sigemptyset(&newset); + + for (int i = 0; i < NUM_SIGNALS; i++) { + save_signal(i, SIGNALS[i]); + os::signal(SIGNALS[i], CAST_FROM_FN_PTR(void *, crash_handler)); + sigaddset(&newset, SIGNALS[i]); + } + + sigthreadmask(SIG_UNBLOCK, &newset, NULL); +} diff -r 368df6a4b403 -r d982b0f5b59a src/os/bsd/vm/os_bsd.cpp --- a/src/os/bsd/vm/os_bsd.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/bsd/vm/os_bsd.cpp Sun May 21 11:12:48 2017 -0700 @@ -287,7 +287,7 @@ static char cpu_arch[] = "amd64"; #elif defined(ARM) static char cpu_arch[] = "arm"; -#elif defined(PPC) +#elif defined(PPC32) static char cpu_arch[] = "ppc"; #elif defined(SPARC) # ifdef _LP64 diff -r 368df6a4b403 -r d982b0f5b59a src/os/bsd/vm/os_bsd.hpp --- a/src/os/bsd/vm/os_bsd.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/bsd/vm/os_bsd.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, 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 @@ -27,6 +27,9 @@ // Bsd_OS defines the interface to Bsd operating systems +// Information about the protection of the page at address '0' on this os. +static bool zero_page_read_protected() { return true; } + /* pthread_getattr_np comes with BsdThreads-0.9-7 on RedHat 7.1 */ typedef int (*pthread_getattr_func_type) (pthread_t, pthread_attr_t *); diff -r 368df6a4b403 -r d982b0f5b59a src/os/linux/vm/decoder_linux.cpp --- a/src/os/linux/vm/decoder_linux.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/linux/vm/decoder_linux.cpp Sun May 21 11:12:48 2017 -0700 @@ -32,6 +32,12 @@ char* result; size_t size = (size_t)buflen; +#ifdef PPC64 + // On PPC64 ElfDecoder::decode() may return a dot (.) prefixed name + // (see elfFuncDescTable.hpp for details) + if (symbol && *symbol == '.') symbol += 1; +#endif + // Don't pass buf to __cxa_demangle. In case of the 'buf' is too small, // __cxa_demangle will call system "realloc" for additional memory, which // may use different malloc/realloc mechanism that allocates 'buf'. diff -r 368df6a4b403 -r d982b0f5b59a src/os/linux/vm/os_linux.cpp --- a/src/os/linux/vm/os_linux.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/linux/vm/os_linux.cpp Sun May 21 11:12:48 2017 -0700 @@ -160,7 +160,7 @@ // For diagnostics to print a message once. see run_periodic_checks static sigset_t check_signal_done; -static bool check_signals = true;; +static bool check_signals = true; static pid_t _initial_pid = 0; @@ -293,8 +293,10 @@ static char cpu_arch[] = "amd64"; #elif defined(ARM) static char cpu_arch[] = "arm"; -#elif defined(PPC) +#elif defined(PPC32) static char cpu_arch[] = "ppc"; +#elif defined(PPC64) +static char cpu_arch[] = "ppc64"; #elif defined(SPARC) # ifdef _LP64 static char cpu_arch[] = "sparcv9"; @@ -566,6 +568,9 @@ sigaddset(&unblocked_sigs, SIGSEGV); sigaddset(&unblocked_sigs, SIGBUS); sigaddset(&unblocked_sigs, SIGFPE); +#if defined(PPC64) + sigaddset(&unblocked_sigs, SIGTRAP); +#endif sigaddset(&unblocked_sigs, SR_signum); if (!ReduceSignalUsage) { @@ -2003,7 +2008,11 @@ {EM_SPARC32PLUS, EM_SPARC, ELFCLASS32, ELFDATA2MSB, (char*)"Sparc 32"}, {EM_SPARCV9, EM_SPARCV9, ELFCLASS64, ELFDATA2MSB, (char*)"Sparc v9 64"}, {EM_PPC, EM_PPC, ELFCLASS32, ELFDATA2MSB, (char*)"Power PC 32"}, +#if defined(VM_LITTLE_ENDIAN) + {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2LSB, (char*)"Power PC 64"}, +#else {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"}, +#endif {EM_ARM, EM_ARM, ELFCLASS32, ELFDATA2LSB, (char*)"ARM"}, {EM_S390, EM_S390, ELFCLASSNONE, ELFDATA2MSB, (char*)"IBM System/390"}, {EM_ALPHA, EM_ALPHA, ELFCLASS64, ELFDATA2LSB, (char*)"Alpha"}, @@ -2359,6 +2368,7 @@ print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen); print_signal_handler(st, SHUTDOWN3_SIGNAL , buf, buflen); print_signal_handler(st, BREAK_SIGNAL, buf, buflen); + print_signal_handler(st, SIGTRAP, buf, buflen); } static char saved_jvm_path[MAXPATHLEN] = {0}; @@ -4491,6 +4501,9 @@ set_signal_handler(SIGBUS, true); set_signal_handler(SIGILL, true); set_signal_handler(SIGFPE, true); +#if defined(PPC64) + set_signal_handler(SIGTRAP, true); +#endif set_signal_handler(SIGXFSZ, true); if (libjsig_is_loaded) { @@ -4631,7 +4644,9 @@ DO_SIGNAL_CHECK(SIGBUS); DO_SIGNAL_CHECK(SIGPIPE); DO_SIGNAL_CHECK(SIGXFSZ); - +#if defined(PPC64) + DO_SIGNAL_CHECK(SIGTRAP); +#endif // ReduceSignalUsage allows the user to override these handlers // see comments at the very top and jvm_solaris.h @@ -4955,7 +4970,7 @@ // the future if the appropriate cleanup code can be added to the // VM_Exit VMOperation's doit method. if (atexit(perfMemory_exit_helper) != 0) { - warning("os::init2 atexit(perfMemory_exit_helper) failed"); + warning("os::init_2 atexit(perfMemory_exit_helper) failed"); } } @@ -4966,8 +4981,7 @@ } // this is called at the end of vm_initialization -void os::init_3(void) -{ +void os::init_3(void) { #ifdef JAVASE_EMBEDDED // Start the MemNotifyThread if (LowMemoryProtection) { @@ -5084,7 +5098,7 @@ // debug support static address same_page(address x, address y) { - int page_bits = -os::vm_page_size(); + intptr_t page_bits = -os::vm_page_size(); if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) return x; else if (x > y) diff -r 368df6a4b403 -r d982b0f5b59a src/os/linux/vm/os_linux.hpp --- a/src/os/linux/vm/os_linux.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/linux/vm/os_linux.hpp Sun May 21 11:12:48 2017 -0700 @@ -30,6 +30,9 @@ /* pthread_getattr_np comes with LinuxThreads-0.9-7 on RedHat 7.1 */ typedef int (*pthread_getattr_func_type) (pthread_t, pthread_attr_t *); +// Information about the protection of the page at address '0' on this os. +static bool zero_page_read_protected() { return true; } + class Linux { friend class os; friend class TestReserveMemorySpecial; diff -r 368df6a4b403 -r d982b0f5b59a src/os/posix/launcher/java_md.c --- a/src/os/posix/launcher/java_md.c Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/posix/launcher/java_md.c Sun May 21 11:12:48 2017 -0700 @@ -47,6 +47,11 @@ #include #endif +// We use our own dladdr() implementation on AIX since there is no dladdr. +#ifdef AIX +#include "porting_aix.hpp" +#endif + #ifdef __APPLE__ #define JVM_DLL "libjvm.dylib" #define JAVA_DLL "libjava.dylib" @@ -58,6 +63,12 @@ #endif #ifndef GAMMA /* launcher.make defines ARCH */ + +// We must use LIBPATH on AIX. +#ifdef AIX +#define LD_LIBRARY_PATH "LIBPATH" +#endif + /* * If a processor / os combination has the ability to run binaries of * two data models and cohabitation of jre/jdk bits with both data @@ -1887,7 +1898,7 @@ int ContinueInNewThread(int (JNICALL *continuation)(void *), jlong stack_size, void * args) { int rslt; -#if defined(__linux__) || defined(_ALLBSD_SOURCE) +#if defined(__linux__) || defined(AIX) || defined(_ALLBSD_SOURCE) pthread_t tid; pthread_attr_t attr; pthread_attr_init(&attr); diff -r 368df6a4b403 -r d982b0f5b59a src/os/posix/vm/os_posix.cpp --- a/src/os/posix/vm/os_posix.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/posix/vm/os_posix.cpp Sun May 21 11:12:48 2017 -0700 @@ -22,17 +22,25 @@ * */ +#include "utilities/globalDefinitions.hpp" #include "prims/jvm.h" #include "runtime/frame.inline.hpp" #include "runtime/os.hpp" #include "utilities/vmError.hpp" +#include #include #include #include #include #include +// Todo: provide a os::get_max_process_id() or similar. Number of processes +// may have been configured, can be read more accurately from proc fs etc. +#ifndef MAX_PID +#define MAX_PID INT_MAX +#endif +#define IS_VALID_PID(p) (p > 0 && p < MAX_PID) // Check core dump limit and report possible place where core can be found void os::check_or_create_dump(void* exceptionRecord, void* contextRecord, char* buffer, size_t bufferSize) { @@ -162,8 +170,8 @@ if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); else st->print("%uk", rlim.rlim_cur >> 10); - //Isn't there on solaris -#ifndef TARGET_OS_FAMILY_solaris + // Isn't there on solaris and aix. +#if !defined(TARGET_OS_FAMILY_solaris) && !defined(TARGET_OS_FAMILY_aix) st->print(", NPROC "); getrlimit(RLIMIT_NPROC, &rlim); if (rlim.rlim_cur == RLIM_INFINITY) st->print("infinity"); @@ -202,6 +210,483 @@ st->cr(); } +// Returned string is a constant. For unknown signals "UNKNOWN" is returned. +const char* os::Posix::get_signal_name(int sig, char* out, size_t outlen) { + + static const struct { + int sig; const char* name; + } + info[] = + { + { SIGABRT, "SIGABRT" }, +#ifdef SIGAIO + { SIGAIO, "SIGAIO" }, +#endif + { SIGALRM, "SIGALRM" }, +#ifdef SIGALRM1 + { SIGALRM1, "SIGALRM1" }, +#endif + { SIGBUS, "SIGBUS" }, +#ifdef SIGCANCEL + { SIGCANCEL, "SIGCANCEL" }, +#endif + { SIGCHLD, "SIGCHLD" }, +#ifdef SIGCLD + { SIGCLD, "SIGCLD" }, +#endif + { SIGCONT, "SIGCONT" }, +#ifdef SIGCPUFAIL + { SIGCPUFAIL, "SIGCPUFAIL" }, +#endif +#ifdef SIGDANGER + { SIGDANGER, "SIGDANGER" }, +#endif +#ifdef SIGDIL + { SIGDIL, "SIGDIL" }, +#endif +#ifdef SIGEMT + { SIGEMT, "SIGEMT" }, +#endif + { SIGFPE, "SIGFPE" }, +#ifdef SIGFREEZE + { SIGFREEZE, "SIGFREEZE" }, +#endif +#ifdef SIGGFAULT + { SIGGFAULT, "SIGGFAULT" }, +#endif +#ifdef SIGGRANT + { SIGGRANT, "SIGGRANT" }, +#endif + { SIGHUP, "SIGHUP" }, + { SIGILL, "SIGILL" }, + { SIGINT, "SIGINT" }, +#ifdef SIGIO + { SIGIO, "SIGIO" }, +#endif +#ifdef SIGIOINT + { SIGIOINT, "SIGIOINT" }, +#endif +#ifdef SIGIOT + // SIGIOT is there for BSD compatibility, but on most Unices just a + // synonym for SIGABRT. The result should be "SIGABRT", not + // "SIGIOT". + #if (SIGIOT != SIGABRT ) + { SIGIOT, "SIGIOT" }, + #endif +#endif +#ifdef SIGKAP + { SIGKAP, "SIGKAP" }, +#endif + { SIGKILL, "SIGKILL" }, +#ifdef SIGLOST + { SIGLOST, "SIGLOST" }, +#endif +#ifdef SIGLWP + { SIGLWP, "SIGLWP" }, +#endif +#ifdef SIGLWPTIMER + { SIGLWPTIMER, "SIGLWPTIMER" }, +#endif +#ifdef SIGMIGRATE + { SIGMIGRATE, "SIGMIGRATE" }, +#endif +#ifdef SIGMSG + { SIGMSG, "SIGMSG" }, +#endif + { SIGPIPE, "SIGPIPE" }, +#ifdef SIGPOLL + { SIGPOLL, "SIGPOLL" }, +#endif +#ifdef SIGPRE + { SIGPRE, "SIGPRE" }, +#endif + { SIGPROF, "SIGPROF" }, +#ifdef SIGPTY + { SIGPTY, "SIGPTY" }, +#endif +#ifdef SIGPWR + { SIGPWR, "SIGPWR" }, +#endif + { SIGQUIT, "SIGQUIT" }, +#ifdef SIGRECONFIG + { SIGRECONFIG, "SIGRECONFIG" }, +#endif +#ifdef SIGRECOVERY + { SIGRECOVERY, "SIGRECOVERY" }, +#endif +#ifdef SIGRESERVE + { SIGRESERVE, "SIGRESERVE" }, +#endif +#ifdef SIGRETRACT + { SIGRETRACT, "SIGRETRACT" }, +#endif +#ifdef SIGSAK + { SIGSAK, "SIGSAK" }, +#endif + { SIGSEGV, "SIGSEGV" }, +#ifdef SIGSOUND + { SIGSOUND, "SIGSOUND" }, +#endif + { SIGSTOP, "SIGSTOP" }, + { SIGSYS, "SIGSYS" }, +#ifdef SIGSYSERROR + { SIGSYSERROR, "SIGSYSERROR" }, +#endif +#ifdef SIGTALRM + { SIGTALRM, "SIGTALRM" }, +#endif + { SIGTERM, "SIGTERM" }, +#ifdef SIGTHAW + { SIGTHAW, "SIGTHAW" }, +#endif + { SIGTRAP, "SIGTRAP" }, +#ifdef SIGTSTP + { SIGTSTP, "SIGTSTP" }, +#endif + { SIGTTIN, "SIGTTIN" }, + { SIGTTOU, "SIGTTOU" }, +#ifdef SIGURG + { SIGURG, "SIGURG" }, +#endif + { SIGUSR1, "SIGUSR1" }, + { SIGUSR2, "SIGUSR2" }, +#ifdef SIGVIRT + { SIGVIRT, "SIGVIRT" }, +#endif + { SIGVTALRM, "SIGVTALRM" }, +#ifdef SIGWAITING + { SIGWAITING, "SIGWAITING" }, +#endif +#ifdef SIGWINCH + { SIGWINCH, "SIGWINCH" }, +#endif +#ifdef SIGWINDOW + { SIGWINDOW, "SIGWINDOW" }, +#endif + { SIGXCPU, "SIGXCPU" }, + { SIGXFSZ, "SIGXFSZ" }, +#ifdef SIGXRES + { SIGXRES, "SIGXRES" }, +#endif + { -1, NULL } + }; + + const char* ret = NULL; + +#ifdef SIGRTMIN + if (sig >= SIGRTMIN && sig <= SIGRTMAX) { + if (sig == SIGRTMIN) { + ret = "SIGRTMIN"; + } else if (sig == SIGRTMAX) { + ret = "SIGRTMAX"; + } else { + jio_snprintf(out, outlen, "SIGRTMIN+%d", sig - SIGRTMIN); + return out; + } + } +#endif + + if (sig > 0) { + for (int idx = 0; info[idx].sig != -1; idx ++) { + if (info[idx].sig == sig) { + ret = info[idx].name; + break; + } + } + } + + if (!ret) { + if (!is_valid_signal(sig)) { + ret = "INVALID"; + } else { + ret = "UNKNOWN"; + } + } + + jio_snprintf(out, outlen, ret); + return out; +} + +// Returns true if signal number is valid. +bool os::Posix::is_valid_signal(int sig) { + // MacOS not really POSIX compliant: sigaddset does not return + // an error for invalid signal numbers. However, MacOS does not + // support real time signals and simply seems to have just 33 + // signals with no holes in the signal range. +#ifdef __APPLE__ + return sig >= 1 && sig < NSIG; +#else + // Use sigaddset to check for signal validity. + sigset_t set; + if (sigaddset(&set, sig) == -1 && errno == EINVAL) { + return false; + } + return true; +#endif +} + +#define NUM_IMPORTANT_SIGS 32 +// Returns one-line short description of a signal set in a user provided buffer. +const char* os::Posix::describe_signal_set_short(const sigset_t* set, char* buffer, size_t buf_size) { + assert(buf_size == (NUM_IMPORTANT_SIGS + 1), "wrong buffer size"); + // Note: for shortness, just print out the first 32. That should + // cover most of the useful ones, apart from realtime signals. + for (int sig = 1; sig <= NUM_IMPORTANT_SIGS; sig++) { + const int rc = sigismember(set, sig); + if (rc == -1 && errno == EINVAL) { + buffer[sig-1] = '?'; + } else { + buffer[sig-1] = rc == 0 ? '0' : '1'; + } + } + buffer[NUM_IMPORTANT_SIGS] = 0; + return buffer; +} + +// Prints one-line description of a signal set. +void os::Posix::print_signal_set_short(outputStream* st, const sigset_t* set) { + char buf[NUM_IMPORTANT_SIGS + 1]; + os::Posix::describe_signal_set_short(set, buf, sizeof(buf)); + st->print(buf); +} + +// Writes one-line description of a combination of sigaction.sa_flags into a user +// provided buffer. Returns that buffer. +const char* os::Posix::describe_sa_flags(int flags, char* buffer, size_t size) { + char* p = buffer; + size_t remaining = size; + bool first = true; + int idx = 0; + + assert(buffer, "invalid argument"); + + if (size == 0) { + return buffer; + } + + strncpy(buffer, "none", size); + + const struct { + int i; + const char* s; + } flaginfo [] = { + { SA_NOCLDSTOP, "SA_NOCLDSTOP" }, + { SA_ONSTACK, "SA_ONSTACK" }, + { SA_RESETHAND, "SA_RESETHAND" }, + { SA_RESTART, "SA_RESTART" }, + { SA_SIGINFO, "SA_SIGINFO" }, + { SA_NOCLDWAIT, "SA_NOCLDWAIT" }, + { SA_NODEFER, "SA_NODEFER" }, +#ifdef AIX + { SA_ONSTACK, "SA_ONSTACK" }, + { SA_OLDSTYLE, "SA_OLDSTYLE" }, +#endif + { 0, NULL } + }; + + for (idx = 0; flaginfo[idx].s && remaining > 1; idx++) { + if (flags & flaginfo[idx].i) { + if (first) { + jio_snprintf(p, remaining, "%s", flaginfo[idx].s); + first = false; + } else { + jio_snprintf(p, remaining, "|%s", flaginfo[idx].s); + } + const size_t len = strlen(p); + p += len; + remaining -= len; + } + } + + buffer[size - 1] = '\0'; + + return buffer; +} + +// Prints one-line description of a combination of sigaction.sa_flags. +void os::Posix::print_sa_flags(outputStream* st, int flags) { + char buffer[0x100]; + os::Posix::describe_sa_flags(flags, buffer, sizeof(buffer)); + st->print(buffer); +} + +// Helper function for os::Posix::print_siginfo_...(): +// return a textual description for signal code. +struct enum_sigcode_desc_t { + const char* s_name; + const char* s_desc; +}; + +static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t* out) { + + const struct { + int sig; int code; const char* s_code; const char* s_desc; + } t1 [] = { + { SIGILL, ILL_ILLOPC, "ILL_ILLOPC", "Illegal opcode." }, + { SIGILL, ILL_ILLOPN, "ILL_ILLOPN", "Illegal operand." }, + { SIGILL, ILL_ILLADR, "ILL_ILLADR", "Illegal addressing mode." }, + { SIGILL, ILL_ILLTRP, "ILL_ILLTRP", "Illegal trap." }, + { SIGILL, ILL_PRVOPC, "ILL_PRVOPC", "Privileged opcode." }, + { SIGILL, ILL_PRVREG, "ILL_PRVREG", "Privileged register." }, + { SIGILL, ILL_COPROC, "ILL_COPROC", "Coprocessor error." }, + { SIGILL, ILL_BADSTK, "ILL_BADSTK", "Internal stack error." }, +#if defined(IA64) && defined(LINUX) + { SIGILL, ILL_BADIADDR, "ILL_BADIADDR", "Unimplemented instruction address" }, + { SIGILL, ILL_BREAK, "ILL_BREAK", "Application Break instruction" }, +#endif + { SIGFPE, FPE_INTDIV, "FPE_INTDIV", "Integer divide by zero." }, + { SIGFPE, FPE_INTOVF, "FPE_INTOVF", "Integer overflow." }, + { SIGFPE, FPE_FLTDIV, "FPE_FLTDIV", "Floating-point divide by zero." }, + { SIGFPE, FPE_FLTOVF, "FPE_FLTOVF", "Floating-point overflow." }, + { SIGFPE, FPE_FLTUND, "FPE_FLTUND", "Floating-point underflow." }, + { SIGFPE, FPE_FLTRES, "FPE_FLTRES", "Floating-point inexact result." }, + { SIGFPE, FPE_FLTINV, "FPE_FLTINV", "Invalid floating-point operation." }, + { SIGFPE, FPE_FLTSUB, "FPE_FLTSUB", "Subscript out of range." }, + { SIGSEGV, SEGV_MAPERR, "SEGV_MAPERR", "Address not mapped to object." }, + { SIGSEGV, SEGV_ACCERR, "SEGV_ACCERR", "Invalid permissions for mapped object." }, +#ifdef AIX + // no explanation found what keyerr would be + { SIGSEGV, SEGV_KEYERR, "SEGV_KEYERR", "key error" }, +#endif +#if defined(IA64) && !defined(AIX) + { SIGSEGV, SEGV_PSTKOVF, "SEGV_PSTKOVF", "Paragraph stack overflow" }, +#endif + { SIGBUS, BUS_ADRALN, "BUS_ADRALN", "Invalid address alignment." }, + { SIGBUS, BUS_ADRERR, "BUS_ADRERR", "Nonexistent physical address." }, + { SIGBUS, BUS_OBJERR, "BUS_OBJERR", "Object-specific hardware error." }, + { SIGTRAP, TRAP_BRKPT, "TRAP_BRKPT", "Process breakpoint." }, + { SIGTRAP, TRAP_TRACE, "TRAP_TRACE", "Process trace trap." }, + { SIGCHLD, CLD_EXITED, "CLD_EXITED", "Child has exited." }, + { SIGCHLD, CLD_KILLED, "CLD_KILLED", "Child has terminated abnormally and did not create a core file." }, + { SIGCHLD, CLD_DUMPED, "CLD_DUMPED", "Child has terminated abnormally and created a core file." }, + { SIGCHLD, CLD_TRAPPED, "CLD_TRAPPED", "Traced child has trapped." }, + { SIGCHLD, CLD_STOPPED, "CLD_STOPPED", "Child has stopped." }, + { SIGCHLD, CLD_CONTINUED,"CLD_CONTINUED","Stopped child has continued." }, +#ifdef SIGPOLL + { SIGPOLL, POLL_OUT, "POLL_OUT", "Output buffers available." }, + { SIGPOLL, POLL_MSG, "POLL_MSG", "Input message available." }, + { SIGPOLL, POLL_ERR, "POLL_ERR", "I/O error." }, + { SIGPOLL, POLL_PRI, "POLL_PRI", "High priority input available." }, + { SIGPOLL, POLL_HUP, "POLL_HUP", "Device disconnected. [Option End]" }, +#endif + { -1, -1, NULL, NULL } + }; + + // Codes valid in any signal context. + const struct { + int code; const char* s_code; const char* s_desc; + } t2 [] = { + { SI_USER, "SI_USER", "Signal sent by kill()." }, + { SI_QUEUE, "SI_QUEUE", "Signal sent by the sigqueue()." }, + { SI_TIMER, "SI_TIMER", "Signal generated by expiration of a timer set by timer_settime()." }, + { SI_ASYNCIO, "SI_ASYNCIO", "Signal generated by completion of an asynchronous I/O request." }, + { SI_MESGQ, "SI_MESGQ", "Signal generated by arrival of a message on an empty message queue." }, + // Linux specific +#ifdef SI_TKILL + { SI_TKILL, "SI_TKILL", "Signal sent by tkill (pthread_kill)" }, +#endif +#ifdef SI_DETHREAD + { SI_DETHREAD, "SI_DETHREAD", "Signal sent by execve() killing subsidiary threads" }, +#endif +#ifdef SI_KERNEL + { SI_KERNEL, "SI_KERNEL", "Signal sent by kernel." }, +#endif +#ifdef SI_SIGIO + { SI_SIGIO, "SI_SIGIO", "Signal sent by queued SIGIO" }, +#endif + +#ifdef AIX + { SI_UNDEFINED, "SI_UNDEFINED","siginfo contains partial information" }, + { SI_EMPTY, "SI_EMPTY", "siginfo contains no useful information" }, +#endif + +#ifdef __sun + { SI_NOINFO, "SI_NOINFO", "No signal information" }, + { SI_RCTL, "SI_RCTL", "kernel generated signal via rctl action" }, + { SI_LWP, "SI_LWP", "Signal sent via lwp_kill" }, +#endif + + { -1, NULL, NULL } + }; + + const char* s_code = NULL; + const char* s_desc = NULL; + + for (int i = 0; t1[i].sig != -1; i ++) { + if (t1[i].sig == si->si_signo && t1[i].code == si->si_code) { + s_code = t1[i].s_code; + s_desc = t1[i].s_desc; + break; + } + } + + if (s_code == NULL) { + for (int i = 0; t2[i].s_code != NULL; i ++) { + if (t2[i].code == si->si_code) { + s_code = t2[i].s_code; + s_desc = t2[i].s_desc; + } + } + } + + if (s_code == NULL) { + out->s_name = "unknown"; + out->s_desc = "unknown"; + return false; + } + + out->s_name = s_code; + out->s_desc = s_desc; + + return true; +} + +// A POSIX conform, platform-independend siginfo print routine. +// Short print out on one line. +void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) { + char buf[20]; + os->print("siginfo: "); + + if (!si) { + os->print(""); + return; + } + + // See print_siginfo_full() for details. + const int sig = si->si_signo; + + os->print("si_signo: %d (%s)", sig, os::Posix::get_signal_name(sig, buf, sizeof(buf))); + + enum_sigcode_desc_t ed; + if (get_signal_code_description(si, &ed)) { + os->print(", si_code: %d (%s)", si->si_code, ed.s_name); + } else { + os->print(", si_code: %d (unknown)", si->si_code); + } + + if (si->si_errno) { + os->print(", si_errno: %d", si->si_errno); + } + + const int me = (int) ::getpid(); + const int pid = (int) si->si_pid; + + if (si->si_code == SI_USER || si->si_code == SI_QUEUE) { + if (IS_VALID_PID(pid) && pid != me) { + os->print(", sent from pid: %d (uid: %d)", pid, (int) si->si_uid); + } + } else if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || + sig == SIGTRAP || sig == SIGFPE) { + os->print(", si_addr: " PTR_FORMAT, si->si_addr); +#ifdef SIGPOLL + } else if (sig == SIGPOLL) { + os->print(", si_band: " PTR64_FORMAT, (uint64_t)si->si_band); +#endif + } else if (sig == SIGCHLD) { + os->print_cr(", si_pid: %d, si_uid: %d, si_status: %d", (int) si->si_pid, si->si_uid, si->si_status); + } +} + os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); } @@ -257,5 +742,3 @@ } } } - - diff -r 368df6a4b403 -r d982b0f5b59a src/os/posix/vm/os_posix.hpp --- a/src/os/posix/vm/os_posix.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/posix/vm/os_posix.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, 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 @@ -34,6 +34,30 @@ static void print_libversion_info(outputStream* st); static void print_load_average(outputStream* st); +public: + + // Returns true if signal is valid. + static bool is_valid_signal(int sig); + + // Helper function, returns a string (e.g. "SIGILL") for a signal. + // Returned string is a constant. For unknown signals "UNKNOWN" is returned. + static const char* get_signal_name(int sig, char* out, size_t outlen); + + // Returns one-line short description of a signal set in a user provided buffer. + static const char* describe_signal_set_short(const sigset_t* set, char* buffer, size_t size); + + // Prints a short one-line description of a signal set. + static void print_signal_set_short(outputStream* st, const sigset_t* set); + + // Writes a one-line description of a combination of sigaction.sa_flags + // into a user provided buffer. Returns that buffer. + static const char* describe_sa_flags(int flags, char* buffer, size_t size); + + // Prints a one-line description of a combination of sigaction.sa_flags. + static void print_sa_flags(outputStream* st, int flags); + + // A POSIX conform, platform-independend siginfo print routine. + static void print_siginfo_brief(outputStream* os, const siginfo_t* si); }; @@ -57,4 +81,4 @@ sigjmp_buf _jmpbuf; }; -#endif +#endif // OS_POSIX_VM_OS_POSIX_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os/solaris/vm/os_solaris.hpp --- a/src/os/solaris/vm/os_solaris.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/solaris/vm/os_solaris.hpp Sun May 21 11:12:48 2017 -0700 @@ -27,6 +27,9 @@ // Solaris_OS defines the interface to Solaris operating systems +// Information about the protection of the page at address '0' on this os. +static bool zero_page_read_protected() { return true; } + class Solaris { friend class os; diff -r 368df6a4b403 -r d982b0f5b59a src/os/windows/vm/os_windows.hpp --- a/src/os/windows/vm/os_windows.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os/windows/vm/os_windows.hpp Sun May 21 11:12:48 2017 -0700 @@ -26,6 +26,9 @@ #define OS_WINDOWS_VM_OS_WINDOWS_HPP // Win32_OS defines the interface to windows operating systems +// Information about the protection of the page at address '0' on this os. +static bool zero_page_read_protected() { return true; } + class win32 { friend class os; diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/aix_ppc_64.ad --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/aix_ppc_64.ad Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,24 @@ +// +// Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. +// Copyright 2012 SAP AG. 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. +// +// diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,401 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_PPC_OJDKPPC_VM_ATOMIC_AIX_PPC_INLINE_HPP +#define OS_CPU_AIX_PPC_OJDKPPC_VM_ATOMIC_AIX_PPC_INLINE_HPP + +#include "orderAccess_aix_ppc.inline.hpp" +#include "runtime/atomic.hpp" +#include "runtime/os.hpp" +#include "vm_version_ppc.hpp" + +#ifndef _LP64 +#error "Atomic currently only impleneted for PPC64" +#endif + +// Implementation of class atomic + +inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } + +inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void* volatile *)dest = store_value; } + +inline jlong Atomic::load(volatile jlong* src) { return *src; } + +// +// machine barrier instructions: +// +// - ppc_sync two-way memory barrier, aka fence +// - ppc_lwsync orders Store|Store, +// Load|Store, +// Load|Load, +// but not Store|Load +// - ppc_eieio orders memory accesses for device memory (only) +// - ppc_isync invalidates speculatively executed instructions +// From the POWER ISA 2.06 documentation: +// "[...] an isync instruction prevents the execution of +// instructions following the isync until instructions +// preceding the isync have completed, [...]" +// From IBM's AIX assembler reference: +// "The isync [...] instructions causes the processor to +// refetch any instructions that might have been fetched +// prior to the isync instruction. The instruction isync +// causes the processor to wait for all previous instructions +// to complete. Then any instructions already fetched are +// discarded and instruction processing continues in the +// environment established by the previous instructions." +// +// semantic barrier instructions: +// (as defined in orderAccess.hpp) +// +// - ppc_release orders Store|Store, (maps to ppc_lwsync) +// Load|Store +// - ppc_acquire orders Load|Store, (maps to ppc_lwsync) +// Load|Load +// - ppc_fence orders Store|Store, (maps to ppc_sync) +// Load|Store, +// Load|Load, +// Store|Load +// + +#define strasm_sync "\n sync \n" +#define strasm_lwsync "\n lwsync \n" +#define strasm_isync "\n isync \n" +#define strasm_release strasm_lwsync +#define strasm_acquire strasm_lwsync +#define strasm_fence strasm_sync +#define strasm_nobarrier "" +#define strasm_nobarrier_clobber_memory "" + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + + unsigned int result; + + __asm__ __volatile__ ( + strasm_lwsync + "1: lwarx %0, 0, %2 \n" + " add %0, %0, %1 \n" + " stwcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_isync + : /*%0*/"=&r" (result) + : /*%1*/"r" (add_value), /*%2*/"r" (dest) + : "cc", "memory" ); + + return (jint) result; +} + + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + + long result; + + __asm__ __volatile__ ( + strasm_lwsync + "1: ldarx %0, 0, %2 \n" + " add %0, %0, %1 \n" + " stdcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_isync + : /*%0*/"=&r" (result) + : /*%1*/"r" (add_value), /*%2*/"r" (dest) + : "cc", "memory" ); + + return (intptr_t) result; +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add_ptr(add_value, (volatile intptr_t*)dest); +} + + +inline void Atomic::inc (volatile jint* dest) { + + unsigned int temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: lwarx %0, 0, %2 \n" + " addic %0, %0, 1 \n" + " stwcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + + long temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: ldarx %0, 0, %2 \n" + " addic %0, %0, 1 \n" + " stdcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::inc_ptr(volatile void* dest) { + inc_ptr((volatile intptr_t*)dest); +} + + +inline void Atomic::dec (volatile jint* dest) { + + unsigned int temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: lwarx %0, 0, %2 \n" + " addic %0, %0, -1 \n" + " stwcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + + long temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: ldarx %0, 0, %2 \n" + " addic %0, %0, -1 \n" + " stdcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::dec_ptr(volatile void* dest) { + dec_ptr((volatile intptr_t*)dest); +} + +inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) { + + // Note that xchg_ptr doesn't necessarily do an acquire + // (see synchronizer.cpp). + + unsigned int old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* lwsync */ + strasm_lwsync + /* atomic loop */ + "1: \n" + " lwarx %[old_value], %[dest], %[zero] \n" + " stwcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* isync */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (jint) old_value; +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + + // Note that xchg_ptr doesn't necessarily do an acquire + // (see synchronizer.cpp). + + long old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* lwsync */ + strasm_lwsync + /* atomic loop */ + "1: \n" + " ldarx %[old_value], %[dest], %[zero] \n" + " stdcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* isync */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (intptr_t) old_value; +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); +} + +inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) { + + // Note that cmpxchg guarantees a two-way memory barrier across + // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire' + // (see atomic.hpp). + + unsigned int old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* fence */ + strasm_sync + /* simple guard */ + " lwz %[old_value], 0(%[dest]) \n" + " cmpw %[compare_value], %[old_value] \n" + " bne- 2f \n" + /* atomic loop */ + "1: \n" + " lwarx %[old_value], %[dest], %[zero] \n" + " cmpw %[compare_value], %[old_value] \n" + " bne- 2f \n" + " stwcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* acquire */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [compare_value] "r" (compare_value), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (jint) old_value; +} + +inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong compare_value) { + + // Note that cmpxchg guarantees a two-way memory barrier across + // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire' + // (see atomic.hpp). + + long old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* fence */ + strasm_sync + /* simple guard */ + " ld %[old_value], 0(%[dest]) \n" + " cmpd %[compare_value], %[old_value] \n" + " bne- 2f \n" + /* atomic loop */ + "1: \n" + " ldarx %[old_value], %[dest], %[zero] \n" + " cmpd %[compare_value], %[old_value] \n" + " bne- 2f \n" + " stdcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* acquire */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [compare_value] "r" (compare_value), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (jlong) old_value; +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +#undef strasm_sync +#undef strasm_lwsync +#undef strasm_isync +#undef strasm_release +#undef strasm_acquire +#undef strasm_fence +#undef strasm_nobarrier +#undef strasm_nobarrier_clobber_memory + +#endif // OS_CPU_AIX_OJDKPPC_VM_ATOMIC_AIX_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/globals_aix_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/globals_aix_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_OJDKPPC_VM_GLOBALS_AIX_PPC_HPP +#define OS_CPU_AIX_OJDKPPC_VM_GLOBALS_AIX_PPC_HPP + +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) + +define_pd_global(bool, DontYieldALot, false); +define_pd_global(intx, ThreadStackSize, 2048); // 0 => use system default +define_pd_global(intx, VMThreadStackSize, 2048); + +// if we set CompilerThreadStackSize to a value different than 0, it will +// be used in os::create_thread(). Otherwise, due the strange logic in os::create_thread(), +// the stack size for compiler threads will default to VMThreadStackSize, although it +// is defined to 4M in os::Aix::default_stack_size()! +define_pd_global(intx, CompilerThreadStackSize, 4096); + +// Allow extra space in DEBUG builds for asserts. +define_pd_global(uintx,JVMInvokeMethodSlack, 8192); + +define_pd_global(intx, StackYellowPages, 6); +define_pd_global(intx, StackRedPages, 1); +define_pd_global(intx, StackShadowPages, 6 DEBUG_ONLY(+2)); + +// Only used on 64 bit platforms +define_pd_global(uintx,HeapBaseMinAddress, 2*G); +// Only used on 64 bit Windows platforms +define_pd_global(bool, UseVectoredExceptions, false); + +#endif // OS_CPU_AIX_OJDKPPC_VM_GLOBALS_AIX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/orderAccess_aix_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/orderAccess_aix_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_OJDKPPC_VM_ORDERACCESS_AIX_PPC_INLINE_HPP +#define OS_CPU_AIX_OJDKPPC_VM_ORDERACCESS_AIX_PPC_INLINE_HPP + +#include "runtime/orderAccess.hpp" +#include "vm_version_ppc.hpp" + +#ifndef PPC64 +#error "OrderAccess currently only implemented for PPC64" +#endif + +// Implementation of class OrderAccess. + +// +// Machine barrier instructions: +// +// - sync Two-way memory barrier, aka fence. +// - lwsync orders Store|Store, +// Load|Store, +// Load|Load, +// but not Store|Load +// - eieio orders Store|Store +// - isync Invalidates speculatively executed instructions, +// but isync may complete before storage accesses +// associated with instructions preceding isync have +// been performed. +// +// Semantic barrier instructions: +// (as defined in orderAccess.hpp) +// +// - release orders Store|Store, (maps to lwsync) +// Load|Store +// - acquire orders Load|Store, (maps to lwsync) +// Load|Load +// - fence orders Store|Store, (maps to sync) +// Load|Store, +// Load|Load, +// Store|Load +// + +#define inlasm_sync() __asm__ __volatile__ ("sync" : : : "memory"); +#define inlasm_lwsync() __asm__ __volatile__ ("lwsync" : : : "memory"); +#define inlasm_eieio() __asm__ __volatile__ ("eieio" : : : "memory"); +#define inlasm_isync() __asm__ __volatile__ ("isync" : : : "memory"); +#define inlasm_release() inlasm_lwsync(); +#define inlasm_acquire() inlasm_lwsync(); +// Use twi-isync for load_acquire (faster than lwsync). +// ATTENTION: seems like xlC 10.1 has problems with this inline assembler macro (VerifyMethodHandles found "bad vminfo in AMH.conv"): +// #define inlasm_acquire_reg(X) __asm__ __volatile__ ("twi 0,%0,0\n isync\n" : : "r" (X) : "memory"); +#define inlasm_acquire_reg(X) inlasm_lwsync(); +#define inlasm_fence() inlasm_sync(); + +inline void OrderAccess::loadload() { inlasm_lwsync(); } +inline void OrderAccess::storestore() { inlasm_lwsync(); } +inline void OrderAccess::loadstore() { inlasm_lwsync(); } +inline void OrderAccess::storeload() { inlasm_fence(); } + +inline void OrderAccess::acquire() { inlasm_acquire(); } +inline void OrderAccess::release() { inlasm_release(); } +inline void OrderAccess::fence() { inlasm_fence(); } + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { register jbyte t = *p; inlasm_acquire_reg(t); return t; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { register jshort t = *p; inlasm_acquire_reg(t); return t; } +inline jint OrderAccess::load_acquire(volatile jint* p) { register jint t = *p; inlasm_acquire_reg(t); return t; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { register jlong t = *p; inlasm_acquire_reg(t); return t; } +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { register jubyte t = *p; inlasm_acquire_reg(t); return t; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { register jushort t = *p; inlasm_acquire_reg(t); return t; } +inline juint OrderAccess::load_acquire(volatile juint* p) { register juint t = *p; inlasm_acquire_reg(t); return t; } +inline julong OrderAccess::load_acquire(volatile julong* p) { return (julong)load_acquire((volatile jlong*)p); } +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { register jfloat t = *p; inlasm_acquire(); return t; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { register jdouble t = *p; inlasm_acquire(); return t; } + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return (intptr_t)load_acquire((volatile jlong*)p); } +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return (void*) load_acquire((volatile jlong*)p); } +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return (void*) load_acquire((volatile jlong*)p); } + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { inlasm_release(); *p = v; } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { inlasm_release(); *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(julong* p, julong v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; inlasm_fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; inlasm_fence(); } + +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { inlasm_release(); *p = v; inlasm_fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { inlasm_release(); *(void* volatile *)p = v; inlasm_fence(); } + +#undef inlasm_sync +#undef inlasm_lwsync +#undef inlasm_eieio +#undef inlasm_isync +#undef inlasm_release +#undef inlasm_acquire +#undef inlasm_fence + +#endif // OS_CPU_AIX_OJDKPPC_VM_ORDERACCESS_AIX_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// no precompiled headers +#include "assembler_ppc.inline.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "code/icBuffer.hpp" +#include "code/vtableStubs.hpp" +#include "interpreter/interpreter.hpp" +#include "jvm_aix.h" +#include "memory/allocation.inline.hpp" +#include "mutex_aix.inline.hpp" +#include "nativeInst_ppc.hpp" +#include "os_share_aix.hpp" +#include "prims/jniFastGetField.hpp" +#include "prims/jvm.h" +#include "prims/jvm_misc.hpp" +#include "runtime/arguments.hpp" +#include "runtime/extendedPC.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/osThread.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/timer.hpp" +#include "thread_aix.inline.hpp" +#include "utilities/events.hpp" +#include "utilities/vmError.hpp" +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#endif +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif + +// put OS-includes here +# include + +address os::current_stack_pointer() { + address csp; + +#if !defined(USE_XLC_BUILTINS) + // inline assembly for `mr regno(csp), R1_SP': + __asm__ __volatile__ ("mr %0, 1":"=r"(csp):); +#else + csp = (address) __builtin_frame_address(0); +#endif + + return csp; +} + +char* os::non_memory_address_word() { + // Must never look like an address returned by reserve_memory, + // even in its subfields (as defined by the CPU immediate fields, + // if the CPU splits constants across multiple instructions). + + return (char*) -1; +} + +// OS specific thread initialization +// +// Calculate and store the limits of the memory stack. +void os::initialize_thread(Thread *thread) { + assert(thread != NULL, "Sanity check"); + if (!thread->is_Java_thread()) return; + + // Initialize the memory stack limit. + address mem_stk_limit = ((JavaThread *)thread)->stack_yellow_zone_base() + + (os::vm_page_size() * StackShadowPages); +} + +// Frame information (pc, sp, fp) retrieved via ucontext +// always looks like a C-frame according to the frame +// conventions in frame_ppc64.hpp. +address os::Aix::ucontext_get_pc(ucontext_t * uc) { + return (address)uc->uc_mcontext.jmp_context.iar; +} + +intptr_t* os::Aix::ucontext_get_sp(ucontext_t * uc) { + // gpr1 holds the stack pointer on aix + return (intptr_t*)uc->uc_mcontext.jmp_context.gpr[1/*REG_SP*/]; +} + +intptr_t* os::Aix::ucontext_get_fp(ucontext_t * uc) { + return NULL; +} + +void os::Aix::ucontext_set_pc(ucontext_t* uc, address new_pc) { + uc->uc_mcontext.jmp_context.iar = (uint64_t) new_pc; +} + +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, intptr_t** ret_fp) { + + ExtendedPC epc; + ucontext_t* uc = (ucontext_t*)ucVoid; + + if (uc != NULL) { + epc = ExtendedPC(os::Aix::ucontext_get_pc(uc)); + if (ret_sp) *ret_sp = os::Aix::ucontext_get_sp(uc); + if (ret_fp) *ret_fp = os::Aix::ucontext_get_fp(uc); + } else { + // construct empty ExtendedPC for return value checking + epc = ExtendedPC(NULL); + if (ret_sp) *ret_sp = (intptr_t *)NULL; + if (ret_fp) *ret_fp = (intptr_t *)NULL; + } + + return epc; +} + +frame os::fetch_frame_from_context(void* ucVoid) { + intptr_t* sp; + intptr_t* fp; + ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); + // Avoid crash during crash if pc broken. + if (epc.pc()) { + frame fr(sp, epc.pc()); + return fr; + } + frame fr(sp); + return fr; +} + +frame os::get_sender_for_C_frame(frame* fr) { + if (*fr->sp() == NULL) { + // fr is the last C frame + return frame(NULL, NULL); + } + return frame(fr->sender_sp(), fr->sender_pc()); +} + + +frame os::current_frame() { + intptr_t* csp = (intptr_t*) *((intptr_t*) os::current_stack_pointer()); + // hack. + frame topframe(csp, (address)0x8); + // return sender of current topframe which hopefully has pc != NULL. + return os::get_sender_for_C_frame(&topframe); +} + +// Utility functions + +extern "C" JNIEXPORT int +JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { + + ucontext_t* uc = (ucontext_t*) ucVoid; + + Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady + + SignalHandlerMark shm(t); + + // Note: it's not uncommon that JNI code uses signal/sigset to install + // then restore certain signal handler (e.g. to temporarily block SIGPIPE, + // or have a SIGILL handler when detecting CPU type). When that happens, + // JVM_handle_aix_signal() might be invoked with junk info/ucVoid. To + // avoid unnecessary crash when libjsig is not preloaded, try handle signals + // that do not require siginfo/ucontext first. + + if (sig == SIGPIPE) { + if (os::Aix::chained_handler(sig, info, ucVoid)) { + return 1; + } else { + if (PrintMiscellaneous && (WizardMode || Verbose)) { + warning("Ignoring SIGPIPE - see bug 4229104"); + } + return 1; + } + } + + JavaThread* thread = NULL; + VMThread* vmthread = NULL; + if (os::Aix::signal_handlers_are_installed) { + if (t != NULL) { + if(t->is_Java_thread()) { + thread = (JavaThread*)t; + } + else if(t->is_VM_thread()) { + vmthread = (VMThread *)t; + } + } + } + + // Decide if this trap can be handled by a stub. + address stub = NULL; + + // retrieve program counter + address const pc = uc ? os::Aix::ucontext_get_pc(uc) : NULL; + + // retrieve crash address + address const addr = info ? (const address) info->si_addr : NULL; + + // SafeFetch 32 handling: + // - make it work if _thread is null + // - make it use the standard os::...::ucontext_get/set_pc APIs + if (uc) { + address const pc = os::Aix::ucontext_get_pc(uc); + if (pc && StubRoutines::is_safefetch_fault(pc)) { + os::Aix::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); + return true; + } + } + + // Handle SIGDANGER right away. AIX would raise SIGDANGER whenever available swap + // space falls below 30%. This is only a chance for the process to gracefully abort. + // We can't hope to proceed after SIGDANGER since SIGKILL tailgates. + if (sig == SIGDANGER) { + goto report_and_die; + } + + if (info == NULL || uc == NULL || thread == NULL && vmthread == NULL) { + goto run_chained_handler; + } + + // If we are a java thread... + if (thread != NULL) { + + // Handle ALL stack overflow variations here + if (sig == SIGSEGV && (addr < thread->stack_base() && + addr >= thread->stack_base() - thread->stack_size())) { + // stack overflow + // + // If we are in a yellow zone and we are inside java, we disable the yellow zone and + // throw a stack overflow exception. + // If we are in native code or VM C code, we report-and-die. The original coding tried + // to continue with yellow zone disabled, but that doesn't buy us much and prevents + // hs_err_pid files. + if (thread->in_stack_yellow_zone(addr)) { + thread->disable_stack_yellow_zone(); + if (thread->thread_state() == _thread_in_Java) { + // Throw a stack overflow exception. + // Guard pages will be reenabled while unwinding the stack. + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); + goto run_stub; + } else { + // Thread was in the vm or native code. Return and try to finish. + return 1; + } + } else if (thread->in_stack_red_zone(addr)) { + // Fatal red zone violation. Disable the guard pages and fall through + // to handle_unexpected_exception way down below. + thread->disable_stack_red_zone(); + tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + goto report_and_die; + } else { + // this means a segv happened inside our stack, but not in + // the guarded zone. I'd like to know when this happens, + tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone."); + goto report_and_die; + } + + } // end handle SIGSEGV inside stack boundaries + + if (thread->thread_state() == _thread_in_Java) { + // Java thread running in Java code + + // The following signals are used for communicating VM events: + // + // SIGILL: the compiler generates illegal opcodes + // at places where it wishes to interrupt the VM: + // Safepoints, Unreachable Code, Entry points of Zombie methods, + // This results in a SIGILL with (*pc) == inserted illegal instruction. + // + // (so, SIGILLs with a pc inside the zero page are real errors) + // + // SIGTRAP: + // The ppc trap instruction raises a SIGTRAP and is very efficient if it + // does not trap. It is used for conditional branches that are expected + // to be never taken. These are: + // - zombie methods + // - IC (inline cache) misses. + // - null checks leading to UncommonTraps. + // - range checks leading to Uncommon Traps. + // On Aix, these are especially null checks, as the ImplicitNullCheck + // optimization works only in rare cases, as the page at address 0 is only + // write protected. // + // Note: !UseSIGTRAP is used to prevent SIGTRAPS altogether, to facilitate debugging. + // + // SIGSEGV: + // used for safe point polling: + // To notify all threads that they have to reach a safe point, safe point polling is used: + // All threads poll a certain mapped memory page. Normally, this page has read access. + // If the VM wants to inform the threads about impending safe points, it puts this + // page to read only ("poisens" the page), and the threads then reach a safe point. + // used for null checks: + // If the compiler finds a store it uses it for a null check. Unfortunately this + // happens rarely. In heap based and disjoint base compressd oop modes also loads + // are used for null checks. + + // A VM-related SIGILL may only occur if we are not in the zero page. + // On AIX, we get a SIGILL if we jump to 0x0 or to somewhere else + // in the zero page, because it is filled with 0x0. We ignore + // explicit SIGILLs in the zero page. + if (sig == SIGILL && (pc < (address) 0x200)) { + if (TraceTraps) + tty->print_raw_cr("SIGILL happened inside zero page."); + goto report_and_die; + } + + // Handle signal from NativeJump::patch_verified_entry(). + if (( TrapBasedNotEntrantChecks && sig == SIGTRAP && nativeInstruction_at(pc)->is_sigtrap_zombie_not_entrant()) || + (!TrapBasedNotEntrantChecks && sig == SIGILL && nativeInstruction_at(pc)->is_sigill_zombie_not_entrant())) { + if (TraceTraps) + tty->print_cr("trap: zombie_not_entrant (%s)", (sig == SIGTRAP) ? "SIGTRAP" : "SIGILL"); + stub = SharedRuntime::get_handle_wrong_method_stub(); + goto run_stub; + } + + else if (sig == SIGSEGV && os::is_poll_address(addr)) { + if (TraceTraps) + tty->print_cr("trap: safepoint_poll at " INTPTR_FORMAT " (SIGSEGV)", pc); + stub = SharedRuntime::get_poll_stub(pc); + goto run_stub; + } + + // SIGTRAP-based ic miss check in compiled code + else if (sig == SIGTRAP && TrapBasedICMissChecks && + nativeInstruction_at(pc)->is_sigtrap_ic_miss_check()) { + if (TraceTraps) + tty->print_cr("trap: ic_miss_check at " INTPTR_FORMAT " (SIGTRAP)", pc); + stub = SharedRuntime::get_ic_miss_stub(); + goto run_stub; + } + +#ifdef COMPILER2 + // SIGTRAP-based implicit null check in compiled code. + else if (sig == SIGTRAP && TrapBasedNullChecks && + nativeInstruction_at(pc)->is_sigtrap_null_check()) { + if (TraceTraps) + tty->print_cr("trap: null_check at " INTPTR_FORMAT " (SIGTRAP)", pc); + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + goto run_stub; + } +#endif + + // SIGSEGV-based implicit null check in compiled code. + else if (sig == SIGSEGV && ImplicitNullChecks && + CodeCache::contains((void*) pc) && + !MacroAssembler::needs_explicit_null_check((intptr_t) info->si_addr)) { + if (TraceTraps) + tty->print_cr("trap: null_check at " INTPTR_FORMAT " (SIGSEGV)", pc); + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } + +#ifdef COMPILER2 + // SIGTRAP-based implicit range check in compiled code. + else if (sig == SIGTRAP && TrapBasedRangeChecks && + nativeInstruction_at(pc)->is_sigtrap_range_check()) { + if (TraceTraps) + tty->print_cr("trap: range_check at " INTPTR_FORMAT " (SIGTRAP)", pc); + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + goto run_stub; + } +#endif + + else if (sig == SIGFPE /* && info->si_code == FPE_INTDIV */) { + if (TraceTraps) { + tty->print_raw_cr("Fix SIGFPE handler, trying divide by zero handler."); + } + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_DIVIDE_BY_ZERO); + goto run_stub; + } + + else if (sig == SIGBUS) { + // BugId 4454115: A read from a MappedByteBuffer can fault here if the + // underlying file has been truncated. Do not crash the VM in such a case. + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; + if (nm != NULL && nm->has_unsafe_access()) { + // We don't really need a stub here! Just set the pending exeption and + // continue at the next instruction after the faulting read. Returning + // garbage from this read is ok. + thread->set_pending_unsafe_access_error(); + uc->uc_mcontext.jmp_context.iar = ((unsigned long)pc) + 4; + return 1; + } + } + } + + else { // thread->thread_state() != _thread_in_Java + // Detect CPU features. This is only done at the very start of the VM. Later, the + // VM_Version::is_determine_features_test_running() flag should be false. + + if (sig == SIGILL && VM_Version::is_determine_features_test_running()) { + // SIGILL must be caused by VM_Version::determine_features(). + *(int *)pc = 0; // patch instruction to 0 to indicate that it causes a SIGILL, + // flushing of icache is not necessary. + stub = pc + 4; // continue with next instruction. + goto run_stub; + } + else if (thread->thread_state() == _thread_in_vm && + sig == SIGBUS && thread->doing_unsafe_access()) { + // We don't really need a stub here! Just set the pending exeption and + // continue at the next instruction after the faulting read. Returning + // garbage from this read is ok. + thread->set_pending_unsafe_access_error(); + uc->uc_mcontext.jmp_context.iar = ((unsigned long)pc) + 4; + return 1; + } + } + + // Check to see if we caught the safepoint code in the + // process of write protecting the memory serialization page. + // It write enables the page immediately after protecting it + // so we can just return to retry the write. + if ((sig == SIGSEGV) && + os::is_memory_serialize_page(thread, addr)) { + // Synchronization problem in the pseudo memory barrier code (bug id 6546278) + // Block current thread until the memory serialize page permission restored. + os::block_on_serialize_page_trap(); + return true; + } + } + +run_stub: + + // One of the above code blocks ininitalized the stub, so we want to + // delegate control to that stub. + if (stub != NULL) { + // Save all thread context in case we need to restore it. + if (thread != NULL) thread->set_saved_exception_pc(pc); + uc->uc_mcontext.jmp_context.iar = (unsigned long)stub; + return 1; + } + +run_chained_handler: + + // signal-chaining + if (os::Aix::chained_handler(sig, info, ucVoid)) { + return 1; + } + if (!abort_if_unrecognized) { + // caller wants another chance, so give it to him + return 0; + } + +report_and_die: + + // Use sigthreadmask instead of sigprocmask on AIX and unmask current signal. + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigthreadmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(t, sig, pc, info, ucVoid); + err.report_and_die(); + + ShouldNotReachHere(); + return 0; +} + +void os::Aix::init_thread_fpu_state(void) { +#if !defined(USE_XLC_BUILTINS) + // Disable FP exceptions. + __asm__ __volatile__ ("mtfsfi 6,0"); +#else + __mtfsfi(6, 0); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// thread stack + +size_t os::Aix::min_stack_allowed = 768*K; + +// Aix is always in floating stack mode. The stack size for a new +// thread can be set via pthread_attr_setstacksize(). +bool os::Aix::supports_variable_stack_size() { return true; } + +// return default stack size for thr_type +size_t os::Aix::default_stack_size(os::ThreadType thr_type) { + // default stack size (compiler thread needs larger stack) + // Notice that the setting for compiler threads here have no impact + // because of the strange 'fallback logic' in os::create_thread(). + // Better set CompilerThreadStackSize in globals_.hpp if you want to + // specify a different stack size for compiler threads! + size_t s = (thr_type == os::compiler_thread ? 4 * M : 1024 * K); + return s; +} + +size_t os::Aix::default_guard_size(os::ThreadType thr_type) { + return 2 * page_size(); +} + +///////////////////////////////////////////////////////////////////////////// +// helper functions for fatal error handler + +void os::print_context(outputStream *st, void *context) { + if (context == NULL) return; + + ucontext_t* uc = (ucontext_t*)context; + + st->print_cr("Registers:"); + st->print("pc =" INTPTR_FORMAT " ", uc->uc_mcontext.jmp_context.iar); + st->print("lr =" INTPTR_FORMAT " ", uc->uc_mcontext.jmp_context.lr); + st->print("ctr=" INTPTR_FORMAT " ", uc->uc_mcontext.jmp_context.ctr); + st->cr(); + for (int i = 0; i < 32; i++) { + st->print("r%-2d=" INTPTR_FORMAT " ", i, uc->uc_mcontext.jmp_context.gpr[i]); + if (i % 3 == 2) st->cr(); + } + st->cr(); + st->cr(); + + intptr_t *sp = (intptr_t *)os::Aix::ucontext_get_sp(uc); + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); + print_hex_dump(st, (address)sp, (address)(sp + 128), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may + // point to garbage if entry point in an nmethod is corrupted. Leave + // this at the end, and hope for the best. + address pc = os::Aix::ucontext_get_pc(uc); + st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc); + print_hex_dump(st, pc - 64, pc + 64, /*instrsize=*/4); + st->cr(); + + // Try to decode the instructions. + st->print_cr("Decoded instructions: (pc=" PTR_FORMAT ")", pc); + st->print(""); + // TODO: PPC port Disassembler::decode(pc, 16, 16, st); + st->cr(); +} + +void os::print_register_info(outputStream *st, void *context) { + if (context == NULL) return; + st->print("Not ported - print_register_info\n"); +} + +extern "C" { + int SpinPause() { + return 0; + } +} + +#ifndef PRODUCT +void os::verify_stack_alignment() { + assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment"); +} +#endif diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_OJDKPPC_VM_OS_AIX_PPC_HPP +#define OS_CPU_AIX_OJDKPPC_VM_OS_AIX_PPC_HPP + + static void setup_fpu() {} + + // Used to register dynamic code cache area with the OS + // Note: Currently only used in 64 bit Windows implementations + static bool register_code_area(char *low, char *high) { return true; } + +#endif // OS_CPU_AIX_OJDKPPC_VM_OS_AIX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_PPC_64_VM_PREFETCH_AIX_PPC_64_INLINE_HPP +#define OS_CPU_AIX_PPC_64_VM_PREFETCH_AIX_PPC_64_INLINE_HPP + +#include "runtime/prefetch.hpp" + + +inline void Prefetch::read(void *loc, intx interval) { +#if !defined(USE_XLC_BUILTINS) + __asm__ __volatile__ ( + " dcbt 0, %0 \n" + : + : /*%0*/"r" ( ((address)loc) +((long)interval) ) + //: + ); +#else + __dcbt(((address)loc) +((long)interval)); +#endif +} + +inline void Prefetch::write(void *loc, intx interval) { +#if !defined(USE_XLC_PREFETCH_WRITE_BUILTIN) + __asm__ __volatile__ ( + " dcbtst 0, %0 \n" + : + : /*%0*/"r" ( ((address)loc) +((long)interval) ) + //: + ); +#else + __dcbtst( ((address)loc) +((long)interval) ); +#endif +} + +#endif // OS_CPU_AIX_PPC_64_VM_PREFETCH_AIX_PPC_64_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/threadLocalStorage.hpp" +#include "runtime/thread.hpp" + +void ThreadLocalStorage::generate_code_for_get_thread() { + // Nothing we can do here for user-level thread. +} + +void ThreadLocalStorage::pd_init() { + // Nothing to do. +} + +void ThreadLocalStorage::pd_set_thread(Thread* thread) { + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); +} diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_OJDKPPC_VM_THREADLS_AIX_PPC_HPP +#define OS_CPU_AIX_OJDKPPC_VM_THREADLS_AIX_PPC_HPP + + // Processor dependent parts of ThreadLocalStorage + +public: + static Thread* thread() { + return (Thread *) os::thread_local_storage_at(thread_index()); + } + +#endif // OS_CPU_AIX_OJDKPPC_VM_THREADLS_AIX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/thread_aix_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/thread_aix_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/frame.hpp" +#include "runtime/thread.hpp" + +// Forte Analyzer AsyncGetCallTrace profiling support is not implemented on Aix/PPC. +bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava) { + Unimplemented(); + return false; +} + +void JavaThread::cache_global_variables() { } diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_OJDKPPC_VM_THREAD_AIX_PPC_HPP +#define OS_CPU_AIX_OJDKPPC_VM_THREAD_AIX_PPC_HPP + + private: + void pd_initialize() { + _anchor.clear(); + _last_interpreter_fp = NULL; + } + + // The `last' frame is the youngest Java frame on the thread's stack. + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + + intptr_t* sp = last_Java_sp(); + address pc = _anchor.last_Java_pc(); + + // Last_Java_pc ist not set, if we come here from compiled code. + if (pc == NULL) + pc = (address) *(sp + 2); + + return frame(sp, pc); + } + + public: + void set_base_of_stack_pointer(intptr_t* base_sp) {} + intptr_t* base_of_stack_pointer() { return NULL; } + void record_base_of_stack_pointer() {} + + // These routines are only used on cpu architectures that + // have separate register stacks (Itanium). + static bool register_stack_overflow() { return false; } + static void enable_register_stack_guard() {} + static void disable_register_stack_guard() {} + + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, + bool isInJava); + + // -Xprof support + // + // In order to find the last Java fp from an async profile + // tick, we store the current interpreter fp in the thread. + // This value is only valid while we are in the C++ interpreter + // and profiling. + protected: + intptr_t *_last_interpreter_fp; + + public: + static ByteSize last_interpreter_fp_offset() { + return byte_offset_of(JavaThread, _last_interpreter_fp); + } + + intptr_t* last_interpreter_fp() { return _last_interpreter_fp; } + +#endif // OS_CPU_AIX_OJDKPPC_VM_THREAD_AIX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/aix_ppc/vm/vmStructs_aix_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/aix_ppc/vm/vmStructs_aix_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_AIX_OJDKPPC_VM_VMSTRUCTS_AIX_PPC_HPP +#define OS_CPU_AIX_OJDKPPC_VM_VMSTRUCTS_AIX_PPC_HPP + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) \ + /* This must be the last entry, and must be present */ \ + last_entry() + + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#endif // OS_CPU_AIX_OJDKPPC_VM_VMSTRUCTS_AIX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/bsd_zero/vm/os_bsd_zero.hpp --- a/src/os_cpu/bsd_zero/vm/os_bsd_zero.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os_cpu/bsd_zero/vm/os_bsd_zero.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,7 +36,7 @@ // Atomically copy 64 bits of data static void atomic_copy64(volatile void *src, volatile void *dst) { -#if defined(PPC) && !defined(_LP64) +#if defined(PPC32) double tmp; asm volatile ("lfd %0, 0(%1)\n" "stfd %0, 0(%2)\n" diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,401 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_INLINE_HPP +#define OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_INLINE_HPP + +#include "orderAccess_linux_ppc.inline.hpp" +#include "runtime/atomic.hpp" +#include "runtime/os.hpp" +#include "vm_version_ppc.hpp" + +#ifndef PPC64 +#error "Atomic currently only implemented for PPC64" +#endif + +// Implementation of class atomic + +inline void Atomic::store (jbyte store_value, jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, jint* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, jlong* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, void* dest) { *(void**)dest = store_value; } + +inline void Atomic::store (jbyte store_value, volatile jbyte* dest) { *dest = store_value; } +inline void Atomic::store (jshort store_value, volatile jshort* dest) { *dest = store_value; } +inline void Atomic::store (jint store_value, volatile jint* dest) { *dest = store_value; } +inline void Atomic::store (jlong store_value, volatile jlong* dest) { *dest = store_value; } +inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; } +inline void Atomic::store_ptr(void* store_value, volatile void* dest) { *(void* volatile *)dest = store_value; } + +inline jlong Atomic::load(volatile jlong* src) { return *src; } + +// +// machine barrier instructions: +// +// - sync two-way memory barrier, aka fence +// - lwsync orders Store|Store, +// Load|Store, +// Load|Load, +// but not Store|Load +// - eieio orders memory accesses for device memory (only) +// - isync invalidates speculatively executed instructions +// From the POWER ISA 2.06 documentation: +// "[...] an isync instruction prevents the execution of +// instructions following the isync until instructions +// preceding the isync have completed, [...]" +// From IBM's AIX assembler reference: +// "The isync [...] instructions causes the processor to +// refetch any instructions that might have been fetched +// prior to the isync instruction. The instruction isync +// causes the processor to wait for all previous instructions +// to complete. Then any instructions already fetched are +// discarded and instruction processing continues in the +// environment established by the previous instructions." +// +// semantic barrier instructions: +// (as defined in orderAccess.hpp) +// +// - release orders Store|Store, (maps to lwsync) +// Load|Store +// - acquire orders Load|Store, (maps to lwsync) +// Load|Load +// - fence orders Store|Store, (maps to sync) +// Load|Store, +// Load|Load, +// Store|Load +// + +#define strasm_sync "\n sync \n" +#define strasm_lwsync "\n lwsync \n" +#define strasm_isync "\n isync \n" +#define strasm_release strasm_lwsync +#define strasm_acquire strasm_lwsync +#define strasm_fence strasm_sync +#define strasm_nobarrier "" +#define strasm_nobarrier_clobber_memory "" + +inline jint Atomic::add (jint add_value, volatile jint* dest) { + + unsigned int result; + + __asm__ __volatile__ ( + strasm_lwsync + "1: lwarx %0, 0, %2 \n" + " add %0, %0, %1 \n" + " stwcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_isync + : /*%0*/"=&r" (result) + : /*%1*/"r" (add_value), /*%2*/"r" (dest) + : "cc", "memory" ); + + return (jint) result; +} + + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { + + long result; + + __asm__ __volatile__ ( + strasm_lwsync + "1: ldarx %0, 0, %2 \n" + " add %0, %0, %1 \n" + " stdcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_isync + : /*%0*/"=&r" (result) + : /*%1*/"r" (add_value), /*%2*/"r" (dest) + : "cc", "memory" ); + + return (intptr_t) result; +} + +inline void* Atomic::add_ptr(intptr_t add_value, volatile void* dest) { + return (void*)add_ptr(add_value, (volatile intptr_t*)dest); +} + + +inline void Atomic::inc (volatile jint* dest) { + + unsigned int temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: lwarx %0, 0, %2 \n" + " addic %0, %0, 1 \n" + " stwcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + + long temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: ldarx %0, 0, %2 \n" + " addic %0, %0, 1 \n" + " stdcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::inc_ptr(volatile void* dest) { + inc_ptr((volatile intptr_t*)dest); +} + + +inline void Atomic::dec (volatile jint* dest) { + + unsigned int temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: lwarx %0, 0, %2 \n" + " addic %0, %0, -1 \n" + " stwcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + + long temp; + + __asm__ __volatile__ ( + strasm_nobarrier + "1: ldarx %0, 0, %2 \n" + " addic %0, %0, -1 \n" + " stdcx. %0, 0, %2 \n" + " bne- 1b \n" + strasm_nobarrier + : /*%0*/"=&r" (temp), "=m" (*dest) + : /*%2*/"r" (dest), "m" (*dest) + : "cc" strasm_nobarrier_clobber_memory); + +} + +inline void Atomic::dec_ptr(volatile void* dest) { + dec_ptr((volatile intptr_t*)dest); +} + +inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) { + + // Note that xchg_ptr doesn't necessarily do an acquire + // (see synchronizer.cpp). + + unsigned int old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* lwsync */ + strasm_lwsync + /* atomic loop */ + "1: \n" + " lwarx %[old_value], %[dest], %[zero] \n" + " stwcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* isync */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (jint) old_value; +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) { + + // Note that xchg_ptr doesn't necessarily do an acquire + // (see synchronizer.cpp). + + long old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* lwsync */ + strasm_lwsync + /* atomic loop */ + "1: \n" + " ldarx %[old_value], %[dest], %[zero] \n" + " stdcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* isync */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (intptr_t) old_value; +} + +inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) { + return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest); +} + +inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) { + + // Note that cmpxchg guarantees a two-way memory barrier across + // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire' + // (see atomic.hpp). + + unsigned int old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* fence */ + strasm_sync + /* simple guard */ + " lwz %[old_value], 0(%[dest]) \n" + " cmpw %[compare_value], %[old_value] \n" + " bne- 2f \n" + /* atomic loop */ + "1: \n" + " lwarx %[old_value], %[dest], %[zero] \n" + " cmpw %[compare_value], %[old_value] \n" + " bne- 2f \n" + " stwcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* acquire */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [compare_value] "r" (compare_value), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (jint) old_value; +} + +inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong compare_value) { + + // Note that cmpxchg guarantees a two-way memory barrier across + // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire' + // (see atomic.hpp). + + long old_value; + const uint64_t zero = 0; + + __asm__ __volatile__ ( + /* fence */ + strasm_sync + /* simple guard */ + " ld %[old_value], 0(%[dest]) \n" + " cmpd %[compare_value], %[old_value] \n" + " bne- 2f \n" + /* atomic loop */ + "1: \n" + " ldarx %[old_value], %[dest], %[zero] \n" + " cmpd %[compare_value], %[old_value] \n" + " bne- 2f \n" + " stdcx. %[exchange_value], %[dest], %[zero] \n" + " bne- 1b \n" + /* acquire */ + strasm_sync + /* exit */ + "2: \n" + /* out */ + : [old_value] "=&r" (old_value), + "=m" (*dest) + /* in */ + : [dest] "b" (dest), + [zero] "r" (zero), + [compare_value] "r" (compare_value), + [exchange_value] "r" (exchange_value), + "m" (*dest) + /* clobber */ + : "cc", + "memory" + ); + + return (jlong) old_value; +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) { + return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) { + return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value); +} + +#undef strasm_sync +#undef strasm_lwsync +#undef strasm_isync +#undef strasm_release +#undef strasm_acquire +#undef strasm_fence +#undef strasm_nobarrier +#undef strasm_nobarrier_clobber_memory + +#endif // OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/bytes_linux_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/bytes_linux_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Google Inc. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_BYTES_LINUX_PPC_INLINE_HPP +#define OS_CPU_LINUX_PPC_VM_BYTES_LINUX_PPC_INLINE_HPP + +#if defined(VM_LITTLE_ENDIAN) +#include + +// Efficient swapping of data bytes from Java byte +// ordering to native byte ordering and vice versa. +inline u2 Bytes::swap_u2(u2 x) { return bswap_16(x); } +inline u4 Bytes::swap_u4(u4 x) { return bswap_32(x); } +inline u8 Bytes::swap_u8(u8 x) { return bswap_64(x); } +#endif // VM_LITTLE_ENDIAN + +#endif // OS_CPU_LINUX_PPC_VM_BYTES_LINUX_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/globals_linux_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/globals_linux_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_GLOBALS_LINUX_PPC_HPP +#define OS_CPU_LINUX_PPC_VM_GLOBALS_LINUX_PPC_HPP + +// Sets the default values for platform dependent flags used by the runtime system. +// (see globals.hpp) + +define_pd_global(bool, DontYieldALot, false); +define_pd_global(intx, ThreadStackSize, 2048); // 0 => use system default +define_pd_global(intx, VMThreadStackSize, 2048); + +// if we set CompilerThreadStackSize to a value different than 0, it will +// be used in os::create_thread(). Otherwise, due the strange logic in os::create_thread(), +// the stack size for compiler threads will default to VMThreadStackSize, although it +// is defined to 4M in os::Linux::default_stack_size()! +define_pd_global(intx, CompilerThreadStackSize, 4096); + +// Allow extra space in DEBUG builds for asserts. +define_pd_global(uintx,JVMInvokeMethodSlack, 8192); + +define_pd_global(intx, StackYellowPages, 6); +define_pd_global(intx, StackRedPages, 1); +define_pd_global(intx, StackShadowPages, 6 DEBUG_ONLY(+2)); + +// Only used on 64 bit platforms +define_pd_global(uintx,HeapBaseMinAddress, 2*G); +// Only used on 64 bit Windows platforms +define_pd_global(bool, UseVectoredExceptions, false); + +#endif // OS_CPU_LINUX_PPC_VM_GLOBALS_LINUX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/linux_ppc_64.ad --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/linux_ppc_64.ad Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,24 @@ +// +// Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. +// Copyright 2012 SAP AG. 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. +// +// diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/orderAccess_linux_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/orderAccess_linux_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_ORDERACCESS_LINUX_PPC_INLINE_HPP +#define OS_CPU_LINUX_PPC_VM_ORDERACCESS_LINUX_PPC_INLINE_HPP + +#include "runtime/orderAccess.hpp" +#include "vm_version_ppc.hpp" + +#ifndef PPC64 +#error "OrderAccess currently only implemented for PPC64" +#endif + +// Implementation of class OrderAccess. + +// +// Machine barrier instructions: +// +// - sync Two-way memory barrier, aka fence. +// - lwsync orders Store|Store, +// Load|Store, +// Load|Load, +// but not Store|Load +// - eieio orders Store|Store +// - isync Invalidates speculatively executed instructions, +// but isync may complete before storage accesses +// associated with instructions preceding isync have +// been performed. +// +// Semantic barrier instructions: +// (as defined in orderAccess.hpp) +// +// - release orders Store|Store, (maps to lwsync) +// Load|Store +// - acquire orders Load|Store, (maps to lwsync) +// Load|Load +// - fence orders Store|Store, (maps to sync) +// Load|Store, +// Load|Load, +// Store|Load +// + +#define inlasm_sync() __asm__ __volatile__ ("sync" : : : "memory"); +#define inlasm_lwsync() __asm__ __volatile__ ("lwsync" : : : "memory"); +#define inlasm_eieio() __asm__ __volatile__ ("eieio" : : : "memory"); +#define inlasm_isync() __asm__ __volatile__ ("isync" : : : "memory"); +#define inlasm_release() inlasm_lwsync(); +#define inlasm_acquire() inlasm_lwsync(); +// Use twi-isync for load_acquire (faster than lwsync). +#define inlasm_acquire_reg(X) __asm__ __volatile__ ("twi 0,%0,0\n isync\n" : : "r" (X) : "memory"); +#define inlasm_fence() inlasm_sync(); + +inline void OrderAccess::loadload() { inlasm_lwsync(); } +inline void OrderAccess::storestore() { inlasm_lwsync(); } +inline void OrderAccess::loadstore() { inlasm_lwsync(); } +inline void OrderAccess::storeload() { inlasm_fence(); } + +inline void OrderAccess::acquire() { inlasm_acquire(); } +inline void OrderAccess::release() { inlasm_release(); } +inline void OrderAccess::fence() { inlasm_fence(); } + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { register jbyte t = *p; inlasm_acquire_reg(t); return t; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { register jshort t = *p; inlasm_acquire_reg(t); return t; } +inline jint OrderAccess::load_acquire(volatile jint* p) { register jint t = *p; inlasm_acquire_reg(t); return t; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { register jlong t = *p; inlasm_acquire_reg(t); return t; } +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { register jubyte t = *p; inlasm_acquire_reg(t); return t; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { register jushort t = *p; inlasm_acquire_reg(t); return t; } +inline juint OrderAccess::load_acquire(volatile juint* p) { register juint t = *p; inlasm_acquire_reg(t); return t; } +inline julong OrderAccess::load_acquire(volatile julong* p) { return (julong)load_acquire((volatile jlong*)p); } +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { register jfloat t = *p; inlasm_acquire(); return t; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { register jdouble t = *p; inlasm_acquire(); return t; } + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return (intptr_t)load_acquire((volatile jlong*)p); } +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return (void*) load_acquire((volatile jlong*)p); } +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { return (void*) load_acquire((volatile jlong*)p); } + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { inlasm_release(); *p = v; } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { inlasm_release(); *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { inlasm_release(); *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jlong* p, jlong v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(julong* p, julong v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; inlasm_fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; inlasm_fence(); } +inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; inlasm_fence(); } + +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { inlasm_release(); *p = v; inlasm_fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { inlasm_release(); *p = v; inlasm_fence(); } +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { inlasm_release(); *(void* volatile *)p = v; inlasm_fence(); } + +#undef inlasm_sync +#undef inlasm_lwsync +#undef inlasm_eieio +#undef inlasm_isync +#undef inlasm_release +#undef inlasm_acquire +#undef inlasm_fence + +#endif // OS_CPU_LINUX_PPC_VM_ORDERACCESS_LINUX_PPC_INLINE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,624 @@ +/* + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2015 SAP AG. 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 hat + * 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. + * + */ + +// no precompiled headers +#include "assembler_ppc.inline.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/systemDictionary.hpp" +#include "classfile/vmSymbols.hpp" +#include "code/icBuffer.hpp" +#include "code/vtableStubs.hpp" +#include "interpreter/interpreter.hpp" +#include "jvm_linux.h" +#include "memory/allocation.inline.hpp" +#include "mutex_linux.inline.hpp" +#include "nativeInst_ppc.hpp" +#include "os_share_linux.hpp" +#include "prims/jniFastGetField.hpp" +#include "prims/jvm.h" +#include "prims/jvm_misc.hpp" +#include "runtime/arguments.hpp" +#include "runtime/extendedPC.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/osThread.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/stubRoutines.hpp" +#include "runtime/timer.hpp" +#include "thread_linux.inline.hpp" +#include "utilities/events.hpp" +#include "utilities/vmError.hpp" +#ifdef COMPILER1 +#include "c1/c1_Runtime1.hpp" +#endif +#ifdef COMPILER2 +#include "opto/runtime.hpp" +#endif + +// put OS-includes here +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + + +address os::current_stack_pointer() { + intptr_t* csp; + + // inline assembly `mr regno(csp), R1_SP': + __asm__ __volatile__ ("mr %0, 1":"=r"(csp):); + + return (address) csp; +} + +char* os::non_memory_address_word() { + // Must never look like an address returned by reserve_memory, + // even in its subfields (as defined by the CPU immediate fields, + // if the CPU splits constants across multiple instructions). + + return (char*) -1; +} + +void os::initialize_thread(Thread *thread) { + assert(thread != NULL, "Sanity check"); + if (!thread->is_Java_thread()) return; + + // Initialize the memory stack limit. + address mem_stk_limit = thread->stack_base() - thread->stack_size() + + ((StackShadowPages + StackYellowPages + StackRedPages) * os::vm_page_size()); +} + +// Frame information (pc, sp, fp) retrieved via ucontext +// always looks like a C-frame according to the frame +// conventions in frame_ppc64.hpp. +address os::Linux::ucontext_get_pc(ucontext_t * uc) { + // On powerpc64, ucontext_t is not selfcontained but contains + // a pointer to an optional substructure (mcontext_t.regs) containing the volatile + // registers - NIP, among others. + // This substructure may or may not be there depending where uc came from: + // - if uc was handed over as the argument to a sigaction handler, a pointer to the + // substructure was provided by the kernel when calling the signal handler, and + // regs->nip can be accessed. + // - if uc was filled by getcontext(), it is undefined - getcontext() does not fill + // it because the volatile registers are not needed to make setcontext() work. + // Hopefully it was zero'd out beforehand. + guarantee(uc->uc_mcontext.regs != NULL, "only use ucontext_get_pc in sigaction context"); + return (address)uc->uc_mcontext.regs->nip; +} + +intptr_t* os::Linux::ucontext_get_sp(ucontext_t * uc) { + return (intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/]; +} + +intptr_t* os::Linux::ucontext_get_fp(ucontext_t * uc) { + return NULL; +} + +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, intptr_t** ret_fp) { + + ExtendedPC epc; + ucontext_t* uc = (ucontext_t*)ucVoid; + + if (uc != NULL) { + epc = ExtendedPC(os::Linux::ucontext_get_pc(uc)); + if (ret_sp) *ret_sp = os::Linux::ucontext_get_sp(uc); + if (ret_fp) *ret_fp = os::Linux::ucontext_get_fp(uc); + } else { + // construct empty ExtendedPC for return value checking + epc = ExtendedPC(NULL); + if (ret_sp) *ret_sp = (intptr_t *)NULL; + if (ret_fp) *ret_fp = (intptr_t *)NULL; + } + + return epc; +} + +frame os::fetch_frame_from_context(void* ucVoid) { + intptr_t* sp; + intptr_t* fp; + ExtendedPC epc = fetch_frame_from_context(ucVoid, &sp, &fp); + return frame(sp, epc.pc()); +} + +frame os::get_sender_for_C_frame(frame* fr) { + if (*fr->sp() == 0) { + // fr is the last C frame + return frame(NULL, NULL); + } + return frame(fr->sender_sp(), fr->sender_pc()); +} + + +frame os::current_frame() { + intptr_t* csp = (intptr_t*) *((intptr_t*) os::current_stack_pointer()); + // hack. + frame topframe(csp, (address)0x8); + // return sender of current topframe which hopefully has pc != NULL. + return os::get_sender_for_C_frame(&topframe); +} + +// Utility functions + +extern "C" JNIEXPORT int +JVM_handle_linux_signal(int sig, + siginfo_t* info, + void* ucVoid, + int abort_if_unrecognized) { + ucontext_t* uc = (ucontext_t*) ucVoid; + + Thread* t = ThreadLocalStorage::get_thread_slow(); + + SignalHandlerMark shm(t); + + // Note: it's not uncommon that JNI code uses signal/sigset to install + // then restore certain signal handler (e.g. to temporarily block SIGPIPE, + // or have a SIGILL handler when detecting CPU type). When that happens, + // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To + // avoid unnecessary crash when libjsig is not preloaded, try handle signals + // that do not require siginfo/ucontext first. + + if (sig == SIGPIPE) { + if (os::Linux::chained_handler(sig, info, ucVoid)) { + return true; + } else { + if (PrintMiscellaneous && (WizardMode || Verbose)) { + warning("Ignoring SIGPIPE - see bug 4229104"); + } + return true; + } + } + + JavaThread* thread = NULL; + VMThread* vmthread = NULL; + if (os::Linux::signal_handlers_are_installed) { + if (t != NULL) { + if(t->is_Java_thread()) { + thread = (JavaThread*)t; + } else if(t->is_VM_thread()) { + vmthread = (VMThread *)t; + } + } + } + + // Moved SafeFetch32 handling outside thread!=NULL conditional block to make + // it work if no associated JavaThread object exists. + if (uc) { + address const pc = os::Linux::ucontext_get_pc(uc); + if (pc && StubRoutines::is_safefetch_fault(pc)) { + uc->uc_mcontext.regs->nip = (unsigned long)StubRoutines::continuation_for_safefetch_fault(pc); + return true; + } + } + + // decide if this trap can be handled by a stub + address stub = NULL; + address pc = NULL; + + //%note os_trap_1 + if (info != NULL && uc != NULL && thread != NULL) { + pc = (address) os::Linux::ucontext_get_pc(uc); + + // Handle ALL stack overflow variations here + if (sig == SIGSEGV) { + // Si_addr may not be valid due to a bug in the linux-ppc64 kernel (see + // comment below). Use get_stack_bang_address instead of si_addr. + address addr = ((NativeInstruction*)pc)->get_stack_bang_address(uc); + + // Check if fault address is within thread stack. + if (addr < thread->stack_base() && + addr >= thread->stack_base() - thread->stack_size()) { + // stack overflow + if (thread->in_stack_yellow_zone(addr)) { + thread->disable_stack_yellow_zone(); + if (thread->thread_state() == _thread_in_Java) { + // Throw a stack overflow exception. + // Guard pages will be reenabled while unwinding the stack. + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); + } else { + // Thread was in the vm or native code. Return and try to finish. + return 1; + } + } else if (thread->in_stack_red_zone(addr)) { + // Fatal red zone violation. Disable the guard pages and fall through + // to handle_unexpected_exception way down below. + thread->disable_stack_red_zone(); + tty->print_raw_cr("An irrecoverable stack overflow has occurred."); + + // This is a likely cause, but hard to verify. Let's just print + // it as a hint. + tty->print_raw_cr("Please check if any of your loaded .so files has " + "enabled executable stack (see man page execstack(8))"); + } else { + // Accessing stack address below sp may cause SEGV if current + // thread has MAP_GROWSDOWN stack. This should only happen when + // current thread was created by user code with MAP_GROWSDOWN flag + // and then attached to VM. See notes in os_linux.cpp. + if (thread->osthread()->expanding_stack() == 0) { + thread->osthread()->set_expanding_stack(); + if (os::Linux::manually_expand_stack(thread, addr)) { + thread->osthread()->clear_expanding_stack(); + return 1; + } + thread->osthread()->clear_expanding_stack(); + } else { + fatal("recursive segv. expanding stack."); + } + } + } + } + + if (thread->thread_state() == _thread_in_Java) { + // Java thread running in Java code => find exception handler if any + // a fault inside compiled code, the interpreter, or a stub + + // A VM-related SIGILL may only occur if we are not in the zero page. + // On AIX, we get a SIGILL if we jump to 0x0 or to somewhere else + // in the zero page, because it is filled with 0x0. We ignore + // explicit SIGILLs in the zero page. + if (sig == SIGILL && (pc < (address) 0x200)) { + if (TraceTraps) + tty->print_raw_cr("SIGILL happened inside zero page."); + goto report_and_die; + } + + CodeBlob *cb = NULL; + // Handle signal from NativeJump::patch_verified_entry(). + if (( TrapBasedNotEntrantChecks && sig == SIGTRAP && nativeInstruction_at(pc)->is_sigtrap_zombie_not_entrant()) || + (!TrapBasedNotEntrantChecks && sig == SIGILL && nativeInstruction_at(pc)->is_sigill_zombie_not_entrant())) { + if (TraceTraps) + tty->print_cr("trap: zombie_not_entrant (%s)", (sig == SIGTRAP) ? "SIGTRAP" : "SIGILL"); + stub = SharedRuntime::get_handle_wrong_method_stub(); + } + + else if (sig == SIGSEGV && + // A linux-ppc64 kernel before 2.6.6 doesn't set si_addr on some segfaults + // in 64bit mode (cf. http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.6), + // especially when we try to read from the safepoint polling page. So the check + // (address)info->si_addr == os::get_standard_polling_page() + // doesn't work for us. We use: + ((NativeInstruction*)pc)->is_safepoint_poll() && + CodeCache::contains((void*) pc) && + ((cb = CodeCache::find_blob(pc)) != NULL) && + cb->is_nmethod()) { + if (TraceTraps) + tty->print_cr("trap: safepoint_poll at " INTPTR_FORMAT " (SIGSEGV)", pc); + stub = SharedRuntime::get_poll_stub(pc); + } + + // SIGTRAP-based ic miss check in compiled code. + else if (sig == SIGTRAP && TrapBasedICMissChecks && + nativeInstruction_at(pc)->is_sigtrap_ic_miss_check()) { + if (TraceTraps) + tty->print_cr("trap: ic_miss_check at " INTPTR_FORMAT " (SIGTRAP)", pc); + stub = SharedRuntime::get_ic_miss_stub(); + } + + // SIGTRAP-based implicit null check in compiled code. + else if (sig == SIGTRAP && TrapBasedNullChecks && + nativeInstruction_at(pc)->is_sigtrap_null_check()) { + if (TraceTraps) + tty->print_cr("trap: null_check at " INTPTR_FORMAT " (SIGTRAP)", pc); + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } + + // SIGSEGV-based implicit null check in compiled code. + else if (sig == SIGSEGV && ImplicitNullChecks && + CodeCache::contains((void*) pc) && + !MacroAssembler::needs_explicit_null_check((intptr_t) info->si_addr)) { + if (TraceTraps) + tty->print_cr("trap: null_check at " INTPTR_FORMAT " (SIGSEGV)", pc); + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } + +#ifdef COMPILER2 + // SIGTRAP-based implicit range check in compiled code. + else if (sig == SIGTRAP && TrapBasedRangeChecks && + nativeInstruction_at(pc)->is_sigtrap_range_check()) { + if (TraceTraps) + tty->print_cr("trap: range_check at " INTPTR_FORMAT " (SIGTRAP)", pc); + stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL); + } +#endif + else if (sig == SIGBUS) { + // BugId 4454115: A read from a MappedByteBuffer can fault here if the + // underlying file has been truncated. Do not crash the VM in such a case. + CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + nmethod* nm = (cb != NULL && cb->is_nmethod()) ? (nmethod*)cb : NULL; + if (nm != NULL && nm->has_unsafe_access()) { + // We don't really need a stub here! Just set the pending exeption and + // continue at the next instruction after the faulting read. Returning + // garbage from this read is ok. + thread->set_pending_unsafe_access_error(); + uc->uc_mcontext.regs->nip = ((unsigned long)pc) + 4; + return true; + } + } + } + + else { // thread->thread_state() != _thread_in_Java + if (sig == SIGILL && VM_Version::is_determine_features_test_running()) { + // SIGILL must be caused by VM_Version::determine_features(). + *(int *)pc = 0; // patch instruction to 0 to indicate that it causes a SIGILL, + // flushing of icache is not necessary. + stub = pc + 4; // continue with next instruction. + } + else if (thread->thread_state() == _thread_in_vm && + sig == SIGBUS && thread->doing_unsafe_access()) { + // We don't really need a stub here! Just set the pending exeption and + // continue at the next instruction after the faulting read. Returning + // garbage from this read is ok. + thread->set_pending_unsafe_access_error(); + uc->uc_mcontext.regs->nip = ((unsigned long)pc) + 4; + return true; + } + } + + // Check to see if we caught the safepoint code in the + // process of write protecting the memory serialization page. + // It write enables the page immediately after protecting it + // so we can just return to retry the write. + if ((sig == SIGSEGV) && + // Si_addr may not be valid due to a bug in the linux-ppc64 kernel (see comment above). + // Use is_memory_serialization instead of si_addr. + ((NativeInstruction*)pc)->is_memory_serialization(thread, ucVoid)) { + // Synchronization problem in the pseudo memory barrier code (bug id 6546278) + // Block current thread until the memory serialize page permission restored. + os::block_on_serialize_page_trap(); + return true; + } + } + + if (stub != NULL) { + // Save all thread context in case we need to restore it. + if (thread != NULL) thread->set_saved_exception_pc(pc); + uc->uc_mcontext.regs->nip = (unsigned long)stub; + return true; + } + + // signal-chaining + if (os::Linux::chained_handler(sig, info, ucVoid)) { + return true; + } + + if (!abort_if_unrecognized) { + // caller wants another chance, so give it to him + return false; + } + + if (pc == NULL && uc != NULL) { + pc = os::Linux::ucontext_get_pc(uc); + } + +report_and_die: + // unmask current signal + sigset_t newset; + sigemptyset(&newset); + sigaddset(&newset, sig); + sigprocmask(SIG_UNBLOCK, &newset, NULL); + + VMError err(t, sig, pc, info, ucVoid); + err.report_and_die(); + + ShouldNotReachHere(); + return false; +} + +void os::Linux::init_thread_fpu_state(void) { + // Disable FP exceptions. + __asm__ __volatile__ ("mtfsfi 6,0"); +} + +int os::Linux::get_fpu_control_word(void) { + // x86 has problems with FPU precision after pthread_cond_timedwait(). + // nothing to do on ppc64. + return 0; +} + +void os::Linux::set_fpu_control_word(int fpu_control) { + // x86 has problems with FPU precision after pthread_cond_timedwait(). + // nothing to do on ppc64. +} + +//////////////////////////////////////////////////////////////////////////////// +// thread stack + +size_t os::Linux::min_stack_allowed = 768*K; + +bool os::Linux::supports_variable_stack_size() { return true; } + +// return default stack size for thr_type +size_t os::Linux::default_stack_size(os::ThreadType thr_type) { + // default stack size (compiler thread needs larger stack) + // Notice that the setting for compiler threads here have no impact + // because of the strange 'fallback logic' in os::create_thread(). + // Better set CompilerThreadStackSize in globals_.hpp if you want to + // specify a different stack size for compiler threads! + size_t s = (thr_type == os::compiler_thread ? 4 * M : 1024 * K); + return s; +} + +size_t os::Linux::default_guard_size(os::ThreadType thr_type) { + return 2 * page_size(); +} + +// Java thread: +// +// Low memory addresses +// +------------------------+ +// | |\ JavaThread created by VM does not have glibc +// | glibc guard page | - guard, attached Java thread usually has +// | |/ 1 page glibc guard. +// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() +// | |\ +// | HotSpot Guard Pages | - red and yellow pages +// | |/ +// +------------------------+ JavaThread::stack_yellow_zone_base() +// | |\ +// | Normal Stack | - +// | |/ +// P2 +------------------------+ Thread::stack_base() +// +// Non-Java thread: +// +// Low memory addresses +// +------------------------+ +// | |\ +// | glibc guard page | - usually 1 page +// | |/ +// P1 +------------------------+ Thread::stack_base() - Thread::stack_size() +// | |\ +// | Normal Stack | - +// | |/ +// P2 +------------------------+ Thread::stack_base() +// +// ** P1 (aka bottom) and size ( P2 = P1 - size) are the address and stack size returned from +// pthread_attr_getstack() + +static void current_stack_region(address * bottom, size_t * size) { + if (os::Linux::is_initial_thread()) { + // initial thread needs special handling because pthread_getattr_np() + // may return bogus value. + *bottom = os::Linux::initial_thread_stack_bottom(); + *size = os::Linux::initial_thread_stack_size(); + } else { + pthread_attr_t attr; + + int rslt = pthread_getattr_np(pthread_self(), &attr); + + // JVM needs to know exact stack location, abort if it fails + if (rslt != 0) { + if (rslt == ENOMEM) { + vm_exit_out_of_memory(0, "pthread_getattr_np"); + } else { + fatal(err_msg("pthread_getattr_np failed with errno = %d", rslt)); + } + } + + if (pthread_attr_getstack(&attr, (void **)bottom, size) != 0) { + fatal("Can not locate current stack attributes!"); + } + + pthread_attr_destroy(&attr); + + } + assert(os::current_stack_pointer() >= *bottom && + os::current_stack_pointer() < *bottom + *size, "just checking"); +} + +address os::current_stack_base() { + address bottom; + size_t size; + current_stack_region(&bottom, &size); + return (bottom + size); +} + +size_t os::current_stack_size() { + // stack size includes normal stack and HotSpot guard pages + address bottom; + size_t size; + current_stack_region(&bottom, &size); + return size; +} + +///////////////////////////////////////////////////////////////////////////// +// helper functions for fatal error handler + +void os::print_context(outputStream *st, void *context) { + if (context == NULL) return; + + ucontext_t* uc = (ucontext_t*)context; + + st->print_cr("Registers:"); + st->print("pc =" INTPTR_FORMAT " ", uc->uc_mcontext.regs->nip); + st->print("lr =" INTPTR_FORMAT " ", uc->uc_mcontext.regs->link); + st->print("ctr=" INTPTR_FORMAT " ", uc->uc_mcontext.regs->ctr); + st->cr(); + for (int i = 0; i < 32; i++) { + st->print("r%-2d=" INTPTR_FORMAT " ", i, uc->uc_mcontext.regs->gpr[i]); + if (i % 3 == 2) st->cr(); + } + st->cr(); + st->cr(); + + intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc); + st->print_cr("Top of Stack: (sp=" PTR_FORMAT ")", sp); + print_hex_dump(st, (address)sp, (address)(sp + 128), sizeof(intptr_t)); + st->cr(); + + // Note: it may be unsafe to inspect memory near pc. For example, pc may + // point to garbage if entry point in an nmethod is corrupted. Leave + // this at the end, and hope for the best. + address pc = os::Linux::ucontext_get_pc(uc); + st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc); + print_hex_dump(st, pc - 64, pc + 64, /*instrsize=*/4); + st->cr(); +} + +void os::print_register_info(outputStream *st, void *context) { + if (context == NULL) return; + + ucontext_t *uc = (ucontext_t*)context; + + st->print_cr("Register to memory mapping:"); + st->cr(); + + // this is only for the "general purpose" registers + for (int i = 0; i < 32; i++) { + st->print("r%-2d=", i); + print_location(st, uc->uc_mcontext.regs->gpr[i]); + } + st->cr(); +} + +extern "C" { + int SpinPause() { + return 0; + } +} + +#ifndef PRODUCT +void os::verify_stack_alignment() { + assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment"); +} +#endif diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/os_linux_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/os_linux_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_OS_LINUX_PPC_HPP +#define OS_CPU_LINUX_PPC_VM_OS_LINUX_PPC_HPP + + static void setup_fpu() {} + + // Used to register dynamic code cache area with the OS + // Note: Currently only used in 64 bit Windows implementations + static bool register_code_area(char *low, char *high) { return true; } + +#endif // OS_CPU_LINUX_PPC_VM_OS_LINUX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/prefetch_linux_ppc.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/prefetch_linux_ppc.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_PREFETCH_LINUX_PPC_INLINE_HPP +#define OS_CPU_LINUX_PPC_VM_PREFETCH_LINUX_PPC_INLINE_HPP + +#include "runtime/prefetch.hpp" + + +inline void Prefetch::read(void *loc, intx interval) { + __asm__ __volatile__ ( + " dcbt 0, %0 \n" + : + : /*%0*/"r" ( ((address)loc) +((long)interval) ) + //: + ); +} + +inline void Prefetch::write(void *loc, intx interval) { + __asm__ __volatile__ ( + " dcbtst 0, %0 \n" + : + : /*%0*/"r" ( ((address)loc) +((long)interval) ) + //: + ); +} + +#endif // OS_CPU_LINUX_PPC_VM_PREFETCH_LINUX_OJDKPPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/threadLS_linux_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/threadLS_linux_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/threadLocalStorage.hpp" +#include "thread_linux.inline.hpp" + +void ThreadLocalStorage::generate_code_for_get_thread() { + // nothing we can do here for user-level thread +} + +void ThreadLocalStorage::pd_init() { + // Nothing to do +} + +void ThreadLocalStorage::pd_set_thread(Thread* thread) { + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); +} diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/threadLS_linux_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/threadLS_linux_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_THREADLS_LINUX_PPC_HPP +#define OS_CPU_LINUX_PPC_VM_THREADLS_LINUX_PPC_HPP + + // Processor dependent parts of ThreadLocalStorage + +public: + static Thread* thread() { + return (Thread *) os::thread_local_storage_at(thread_index()); + } + +#endif // OS_CPU_LINUX_PPC_VM_THREADLS_LINUX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/thread_linux_ppc.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2014 SAP AG. 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. + * + */ + +#include "precompiled.hpp" +#include "runtime/frame.hpp" +#include "runtime/thread.hpp" + +// Forte Analyzer AsyncGetCallTrace profiling support is not implemented on Linux/PPC. +bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava) { + Unimplemented(); + return false; +} + +void JavaThread::cache_global_variables() { } diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/thread_linux_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_THREAD_LINUX_PPC_HPP +#define OS_CPU_LINUX_PPC_VM_THREAD_LINUX_PPC_HPP + + private: + + void pd_initialize() { + _anchor.clear(); + _last_interpreter_fp = NULL; + } + + // The `last' frame is the youngest Java frame on the thread's stack. + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + + intptr_t* sp = last_Java_sp(); + address pc = _anchor.last_Java_pc(); + + // Last_Java_pc ist not set, if we come here from compiled code. + if (pc == NULL) { + pc = (address) *(sp + 2); + } + + return frame(sp, pc); + } + + public: + + void set_base_of_stack_pointer(intptr_t* base_sp) {} + intptr_t* base_of_stack_pointer() { return NULL; } + void record_base_of_stack_pointer() {} + + // These routines are only used on cpu architectures that + // have separate register stacks (Itanium). + static bool register_stack_overflow() { return false; } + static void enable_register_stack_guard() {} + static void disable_register_stack_guard() {} + + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava); + + protected: + + // -Xprof support + // + // In order to find the last Java fp from an async profile + // tick, we store the current interpreter fp in the thread. + // This value is only valid while we are in the C++ interpreter + // and profiling. + intptr_t *_last_interpreter_fp; + + public: + + static ByteSize last_interpreter_fp_offset() { + return byte_offset_of(JavaThread, _last_interpreter_fp); + } + + intptr_t* last_interpreter_fp() { return _last_interpreter_fp; } + +#endif // OS_CPU_LINUX_PPC_VM_THREAD_LINUX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_ppc/vm/vmStructs_linux_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_ppc/vm/vmStructs_linux_ppc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef OS_CPU_LINUX_PPC_VM_VMSTRUCTS_LINUX_PPC_HPP +#define OS_CPU_LINUX_PPC_VM_VMSTRUCTS_LINUX_PPC_HPP + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) \ + /* This must be the last entry, and must be present */ \ + last_entry() + + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#endif // OS_CPU_LINUX_PPC_VM_VMSTRUCTS_LINUX_PPC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/os_cpu/linux_zero/vm/os_linux_zero.hpp --- a/src/os_cpu/linux_zero/vm/os_linux_zero.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/os_cpu/linux_zero/vm/os_linux_zero.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,7 +36,7 @@ // Atomically copy 64 bits of data static void atomic_copy64(volatile void *src, volatile void *dst) { -#if defined(PPC) && !defined(_LP64) +#if defined(PPC32) double tmp; asm volatile ("lfd %0, 0(%1)\n" "stfd %0, 0(%2)\n" diff -r 368df6a4b403 -r d982b0f5b59a src/share/tools/hsdis/Makefile --- a/src/share/tools/hsdis/Makefile Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/tools/hsdis/Makefile Sun May 21 11:12:48 2017 -0700 @@ -74,6 +74,7 @@ CFLAGS/sparc += -m32 CFLAGS/sparcv9 += -m64 CFLAGS/amd64 += -m64 +CFLAGS/ppc64 += -m64 CFLAGS += $(CFLAGS/$(ARCH)) CFLAGS += -fPIC OS = linux diff -r 368df6a4b403 -r d982b0f5b59a src/share/tools/hsdis/hsdis-demo.c --- a/src/share/tools/hsdis/hsdis-demo.c Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/tools/hsdis/hsdis-demo.c Sun May 21 11:12:48 2017 -0700 @@ -62,7 +62,14 @@ if (!greeted) greet("world"); printf("...And now for something completely different:\n"); - disassemble((void*) &main, (void*) &end_of_file); + void *start = (void*) &main; + void *end = (void*) &end_of_file; +#if defined(__ia64) || defined(__powerpc__) + /* On IA64 and PPC function pointers are pointers to function descriptors */ + start = *((void**)start); + end = *((void**)end); +#endif + disassemble(start, (end > start) ? end : start + 256); printf("Cheers!\n"); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/tools/hsdis/hsdis.c --- a/src/share/tools/hsdis/hsdis.c Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/tools/hsdis/hsdis.c Sun May 21 11:12:48 2017 -0700 @@ -418,6 +418,9 @@ #ifdef LIBARCH_sparcv9 res = "sparc:v9b"; #endif +#ifdef LIBARCH_ppc64 + res = "powerpc:common64"; +#endif if (res == NULL) res = "architecture not set in Makefile!"; return res; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/adlparse.cpp --- a/src/share/vm/adlc/adlparse.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/adlparse.cpp Sun May 21 11:12:48 2017 -0700 @@ -220,6 +220,8 @@ parse_err(SYNERR, "Instructions specify ins_encode, not encode\n"); } else if (!strcmp(ident, "ins_encode")) ins_encode_parse(*instr); + // Parse late expand keyword. + else if (!strcmp(ident, "lateExpand")) lateExpand_parse(*instr); else if (!strcmp(ident, "opcode")) instr->_opcode = opcode_parse(instr); else if (!strcmp(ident, "size")) instr->_size = size_parse(instr); else if (!strcmp(ident, "effect")) effect_parse(instr); @@ -259,7 +261,8 @@ // Check for "Set" form of chain rule adjust_set_rule(instr); if (_AD._pipeline ) { - if( instr->expands() ) { + // No pipe required for late expand. + if( instr->expands() || instr->lateExpands() ) { if( instr->_ins_pipe ) parse_err(WARN, "ins_pipe and expand rule both specified for instruction \"%s\"; ins_pipe will be unused\n", instr->_ident); } else { @@ -2857,7 +2860,8 @@ inst.set_is_mach_constant(true); if (_curchar == '(') { - parse_err(SYNERR, "constanttablebase in instruct %s cannot have an argument (only constantaddress and constantoffset)", ec_name); + parse_err(SYNERR, "constanttablebase in instruct %s cannot have an argument " + "(only constantaddress and constantoffset)", ec_name); return; } } @@ -3050,6 +3054,151 @@ inst._insencode = encrule; } +//------------------------------lateExpand_parse------------------------------- +// Encode rules have the form +// lateExpand( encode_class_name(parameter_list) ); +// +// The "encode_class_name" must be defined in the encode section. +// The parameter list contains $names that are locals. +// +// This is just a copy of ins_encode_parse without the loop. +void ADLParser::lateExpand_parse(InstructForm& inst) { + inst._is_lateExpand = true; + + // Parse encode class name. + skipws(); // Skip whitespace. + if (_curchar != '(') { + + parse_err(SYNERR, "missing '(' in lateExpand definition\n"); + return; + } + next_char(); // Move past '('. + skipws(); + + InsEncode *encrule = new InsEncode(); // Encode class for instruction. + encrule->_linenum = linenum(); + char *ec_name = NULL; // String representation of encode rule. + // identifier is optional. + if (_curchar != ')') { + ec_name = get_ident(); + if (ec_name == NULL) { + parse_err(SYNERR, "Invalid lateExpand class name after 'lateExpand('.\n"); + return; + } + // Check that encoding is defined in the encode section. + EncClass *encode_class = _AD._encode->encClass(ec_name); + + // Get list for encode method's parameters + NameAndList *params = encrule->add_encode(ec_name); + + // Parse the parameters to this encode method. + skipws(); + if ( _curchar == '(' ) { + next_char(); // Move past '(' for parameters. + + // Parse the encode method's parameters. + while (_curchar != ')') { + char *param = get_ident_or_literal_constant("encoding operand"); + if ( param != NULL ) { + // Found a parameter: + + // First check for constant table support. + + // Check if this instruct is a MachConstantNode. + if (strcmp(param, "constanttablebase") == 0) { + // This instruct is a MachConstantNode. + inst.set_is_mach_constant(true); + + if (_curchar == '(') { + parse_err(SYNERR, "constanttablebase in instruct %s cannot have an argument " + "(only constantaddress and constantoffset)", ec_name); + return; + } + } + else if ((strcmp(param, "constantaddress") == 0) || + (strcmp(param, "constantoffset") == 0)) { + // This instruct is a MachConstantNode. + inst.set_is_mach_constant(true); + + // If the constant keyword has an argument, parse it. + if (_curchar == '(') constant_parse(inst); + } + + // Else check it is a local name, add it to the list, then check for more. + // New: allow hex constants as parameters to an encode method. + // New: allow parenthesized expressions as parameters. + // New: allow "primary", "secondary", "tertiary" as parameters. + // New: allow user-defined register name as parameter. + else if ( (inst._localNames[param] == NULL) && + !ADLParser::is_literal_constant(param) && + (Opcode::as_opcode_type(param) == Opcode::NOT_AN_OPCODE) && + ((_AD._register == NULL ) || (_AD._register->getRegDef(param) == NULL)) ) { + parse_err(SYNERR, "Using non-locally defined parameter %s for encoding %s.\n", param, ec_name); + return; + } + params->add_entry(param); + + skipws(); + if (_curchar == ',' ) { + // More parameters to come. + next_char(); // Move past ',' between parameters. + skipws(); // Skip to next parameter. + } + else if (_curchar == ')') { + // Done with parameter list + } + else { + // Only ',' or ')' are valid after a parameter name. + parse_err(SYNERR, "expected ',' or ')' after parameter %s.\n", + ec_name); + return; + } + + } else { + skipws(); + // Did not find a parameter. + if (_curchar == ',') { + parse_err(SYNERR, "Expected encode parameter before ',' in lateExpand %s.\n", ec_name); + return; + } + if (_curchar != ')') { + parse_err(SYNERR, "Expected ')' after lateExpand parameters.\n"); + return; + } + } + } // WHILE loop collecting parameters. + next_char(); // Move past ')' at end of parameters. + } // Done with parameter list for encoding. + + // Check for ',' or ')' after encoding. + skipws(); // Move to character after parameters. + if ( _curchar != ')' ) { + // Only a ')' is allowed. + parse_err(SYNERR, "Expected ')' after lateExpand %s.\n", ec_name); + return; + } + } // Done parsing lateExpand method and their parameters. + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' at end of lateExpand description.\n"); + return; + } + next_char(); // Move past ')'. + skipws(); // Skip leading whitespace. + + if ( _curchar != ';' ) { + parse_err(SYNERR, "Missing ';' at end of lateExpand.\n"); + return; + } + next_char(); // Move past ';'. + skipws(); // Be friendly to oper_parse(). + + // Debug Stuff. + if (_AD._adl_debug > 1) fprintf(stderr,"Instruction lateExpand: %s\n", ec_name); + + // Set encode class of this instruction. + inst._insencode = encrule; +} + //------------------------------constant_parse--------------------------------- // Parse a constant expression. @@ -3823,13 +3972,11 @@ //------------------------------expand_parse----------------------------------- ExpandRule* ADLParser::expand_parse(InstructForm *instr) { char *ident, *ident2; - OperandForm *oper; - InstructForm *ins; NameAndList *instr_and_operands = NULL; ExpandRule *exp = new ExpandRule(); - // Expand is a block containing an ordered list of instructions, each of - // which has an ordered list of operands. + // Expand is a block containing an ordered list of operands with initializers, + // or instructions, each of which has an ordered list of operands. // Check for block delimiter skipws(); // Skip leading whitespace if ((_curchar != '%') @@ -3843,12 +3990,30 @@ if (ident == NULL) { parse_err(SYNERR, "identifier expected at %c\n", _curchar); continue; - } // Check that you have a valid instruction + } + + // Check whether we should parse an instruction or operand. const Form *form = _globalNames[ident]; - ins = form ? form->is_instruction() : NULL; - if (ins == NULL) { + bool parse_oper = false; + bool parse_ins = false; + if (form == NULL) { + skipws(); + // Check whether this looks like an instruction specification. If so, + // just parse the instruction. The declaration of the instruction is + // not needed here. + if (_curchar == '(') parse_ins = true; + } else if (form->is_instruction()) { + parse_ins = true; + } else if (form->is_operand()) { + parse_oper = true; + } else { + parse_err(SYNERR, "instruction/operand name expected at %s\n", ident); + continue; + } + + if (parse_oper) { // This is a new operand - oper = form ? form->is_operand() : NULL; + OperandForm *oper = form->is_operand(); if (oper == NULL) { parse_err(SYNERR, "instruction/operand name expected at %s\n", ident); continue; @@ -3883,6 +4048,7 @@ skipws(); } else { + assert(parse_ins, "sanity"); // Add instruction to list instr_and_operands = new NameAndList(ident); // Grab operands, build nameList of them, and then put into dictionary @@ -3906,7 +4072,7 @@ parse_err(SYNERR, "operand name expected at %s\n", ident2); continue; } - oper = form2->is_operand(); + OperandForm *oper = form2->is_operand(); if (oper == NULL && !form2->is_opclass()) { parse_err(SYNERR, "operand name expected at %s\n", ident2); continue; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/adlparse.hpp --- a/src/share/vm/adlc/adlparse.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/adlparse.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -159,6 +159,8 @@ void ins_encode_parse(InstructForm &inst); void ins_encode_parse_block(InstructForm &inst); void ins_encode_parse_block_impl(InstructForm& inst, EncClass* encoding, char* ec_name); + // Parse instruction late expand rule + void lateExpand_parse(InstructForm &inst); void constant_parse(InstructForm& inst); void constant_parse_expression(EncClass* encoding, char* ec_name); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/archDesc.hpp --- a/src/share/vm/adlc/archDesc.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/archDesc.hpp Sun May 21 11:12:48 2017 -0700 @@ -311,6 +311,8 @@ void defineEvalConstant(FILE *fp, InstructForm &node); // Generator for Emit methods for instructions void defineEmit (FILE *fp, InstructForm &node); + // Generator for lateExpand methods for instructions. + void defineLateExpand(FILE *fp, InstructForm &node); // Define a MachOper encode method void define_oper_interface(FILE *fp, OperandForm &oper, FormDict &globals, diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/formssel.cpp --- a/src/share/vm/adlc/formssel.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/formssel.cpp Sun May 21 11:12:48 2017 -0700 @@ -36,20 +36,21 @@ { _ftype = Form::INS; - _matrule = NULL; - _insencode = NULL; - _constant = NULL; - _opcode = NULL; - _size = NULL; - _attribs = NULL; - _predicate = NULL; - _exprule = NULL; - _rewrule = NULL; - _format = NULL; - _peephole = NULL; - _ins_pipe = NULL; - _uniq_idx = NULL; - _num_uniq = 0; + _matrule = NULL; + _insencode = NULL; + _constant = NULL; + _is_lateExpand = false; + _opcode = NULL; + _size = NULL; + _attribs = NULL; + _predicate = NULL; + _exprule = NULL; + _rewrule = NULL; + _format = NULL; + _peephole = NULL; + _ins_pipe = NULL; + _uniq_idx = NULL; + _num_uniq = 0; _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill _cisc_spill_alternate = NULL; // possible cisc replacement _cisc_reg_mask_name = NULL; @@ -68,20 +69,21 @@ { _ftype = Form::INS; - _matrule = rule; - _insencode = instr->_insencode; - _constant = instr->_constant; - _opcode = instr->_opcode; - _size = instr->_size; - _attribs = instr->_attribs; - _predicate = instr->_predicate; - _exprule = instr->_exprule; - _rewrule = instr->_rewrule; - _format = instr->_format; - _peephole = instr->_peephole; - _ins_pipe = instr->_ins_pipe; - _uniq_idx = instr->_uniq_idx; - _num_uniq = instr->_num_uniq; + _matrule = rule; + _insencode = instr->_insencode; + _constant = instr->_constant; + _is_lateExpand = instr->_is_lateExpand; + _opcode = instr->_opcode; + _size = instr->_size; + _attribs = instr->_attribs; + _predicate = instr->_predicate; + _exprule = instr->_exprule; + _rewrule = instr->_rewrule; + _format = instr->_format; + _peephole = instr->_peephole; + _ins_pipe = instr->_ins_pipe; + _uniq_idx = instr->_uniq_idx; + _num_uniq = instr->_num_uniq; _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill _cisc_spill_alternate = NULL; // possible cisc replacement _cisc_reg_mask_name = NULL; @@ -157,6 +159,11 @@ return ( _exprule != NULL ); } +// This instruction has a late expand rule? +bool InstructForm::lateExpands() const { + return _is_lateExpand; +} + // This instruction has a peephole rule? Peephole *InstructForm::peepholes() const { return _peephole; @@ -536,6 +543,34 @@ if( data_type != Form::none ) rematerialize = true; + // Enforce/prohibit rematerializations. + // - If an instruction is attributed with 'ins_cannot_rematerialize(true)' + // then rematerialization of that instruction is prohibited and the + // instruction's value will be spilled if necessary. + // Causes that MachNode::rematerialize() returns false. + // - If an instruction is attributed with 'ins_should_rematerialize(true)' + // then rematerialization should be enforced and a copy of the instruction + // should be inserted if possible; rematerialization is not guaranteed. + // note: this may result in rematerializations in front of every use. + // (optional attribute) + // Causes that MachNode::rematerialize() returns true. + Attribute *attr = _attribs; + while (attr != NULL) { + if (!strcmp(attr->_ident,"ins_cannot_rematerialize") && !strcmp(attr->_val,"true")) { + return false; + } + if (!strcmp(attr->_ident,"ins_should_rematerialize") && !strcmp(attr->_val,"true")) { + return true; + } + attr = (Attribute *)attr->_next; + } + + // Ugly: until a better fix is implemented, disable rematerialization for + // negD nodes because they are proved to be problematic. + if (is_ideal_negD()) { + return false; + } + // Constants if( _components.count() == 1 && _components[0]->is(Component::USE_DEF) ) rematerialize = true; @@ -1265,11 +1300,11 @@ return; } if (strcmp(rep_var, "constantoffset") == 0) { - fprintf(fp, "st->print(\"#%%d\", constant_offset());\n"); + fprintf(fp, "st->print(\"#%%d\", constant_offset_unchecked());\n"); return; } if (strcmp(rep_var, "constantaddress") == 0) { - fprintf(fp, "st->print(\"constant table base + #%%d\", constant_offset());\n"); + fprintf(fp, "st->print(\"constant table base + #%%d\", constant_offset_unchecked());\n"); return; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/formssel.hpp --- a/src/share/vm/adlc/formssel.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/formssel.hpp Sun May 21 11:12:48 2017 -0700 @@ -96,6 +96,7 @@ char *_size; // Size of instruction InsEncode *_insencode; // Encoding class instruction belongs to InsEncode *_constant; // Encoding class constant value belongs to + bool _is_lateExpand; // Indicates that encoding just does a lateExpand. Attribute *_attribs; // List of Attribute rules Predicate *_predicate; // Predicate test for this instruction FormDict _effects; // Dictionary of effect rules @@ -133,6 +134,8 @@ virtual uint num_defs_or_kills(); // This instruction has an expand rule? virtual bool expands() const ; + // This instruction has a late expand rule? + virtual bool lateExpands() const; // Return this instruction's first peephole rule, or NULL virtual Peephole *peepholes() const; // Add a peephole rule to this instruction diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/main.cpp --- a/src/share/vm/adlc/main.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/main.cpp Sun May 21 11:12:48 2017 -0700 @@ -213,6 +213,7 @@ AD.addInclude(AD._CPP_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._CPP_file, "memory/allocation.inline.hpp"); AD.addInclude(AD._CPP_file, "asm/assembler.hpp"); + AD.addInclude(AD._CPP_file, "code/compiledIC.hpp"); AD.addInclude(AD._CPP_file, "code/vmreg.hpp"); AD.addInclude(AD._CPP_file, "gc_interface/collectedHeap.inline.hpp"); AD.addInclude(AD._CPP_file, "oops/compiledICHolderOop.hpp"); @@ -245,6 +246,11 @@ AD.addInclude(AD._CPP_file, "nativeInst_arm.hpp"); AD.addInclude(AD._CPP_file, "vmreg_arm.inline.hpp"); #endif +#ifdef TARGET_ARCH_ppc + AD.addInclude(AD._CPP_file, "assembler_ppc.inline.hpp"); + AD.addInclude(AD._CPP_file, "nativeInst_ppc.hpp"); + AD.addInclude(AD._CPP_file, "vmreg_ppc.inline.hpp"); +#endif AD.addInclude(AD._HPP_file, "memory/allocation.hpp"); AD.addInclude(AD._HPP_file, "opto/machnode.hpp"); AD.addInclude(AD._HPP_file, "opto/node.hpp"); @@ -269,6 +275,7 @@ AD.addInclude(AD._CPP_PIPELINE_file, "adfiles", get_basename(AD._HPP_file._name)); AD.addInclude(AD._DFA_file, "precompiled.hpp"); AD.addInclude(AD._DFA_file, "adfiles", get_basename(AD._HPP_file._name)); + AD.addInclude(AD._DFA_file, "opto/cfgnode.hpp"); // Use PROB_MAX in predicate. AD.addInclude(AD._DFA_file, "opto/matcher.hpp"); AD.addInclude(AD._DFA_file, "opto/opcodes.hpp"); // Make sure each .cpp file starts with include lines: diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/output_c.cpp --- a/src/share/vm/adlc/output_c.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/output_c.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, 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 @@ -463,8 +463,9 @@ uint resources_used_exclusively = 0; for (pipeclass->_resUsage.reset(); - (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) + (piperesource = (const PipeClassResourceForm*)pipeclass->_resUsage.iter()) != NULL; ) { element_count++; + } // Pre-compute the string length int templen; @@ -482,8 +483,8 @@ for (i = rescount; i > 0; i /= 10) maskdigit++; - static const char * pipeline_use_cycle_mask = "Pipeline_Use_Cycle_Mask"; - static const char * pipeline_use_element = "Pipeline_Use_Element"; + static const char* pipeline_use_cycle_mask = "Pipeline_Use_Cycle_Mask"; + static const char* pipeline_use_element = "Pipeline_Use_Element"; templen = 1 + (int)(strlen(pipeline_use_cycle_mask) + (int)strlen(pipeline_use_element) + @@ -496,11 +497,12 @@ templen = 0; for (pipeclass->_resUsage.reset(); - (piperesource = (const PipeClassResourceForm *)pipeclass->_resUsage.iter()) != NULL; ) { + (piperesource = (const PipeClassResourceForm*)pipeclass->_resUsage.iter()) != NULL; ) { int used_mask = pipeline->_resdict[piperesource->_resource]->is_resource()->mask(); - if (!used_mask) + if (!used_mask) { fprintf(stderr, "*** used_mask is 0 ***\n"); + } resources_used |= used_mask; @@ -509,8 +511,9 @@ for (lb = 0; (used_mask & (1 << lb)) == 0; lb++); for (ub = 31; (used_mask & (1 << ub)) == 0; ub--); - if (lb == ub) + if (lb == ub) { resources_used_exclusively |= used_mask; + } int formatlen = sprintf(&resource_mask[templen], " %s(0x%0*x, %*d, %*d, %s %s(", @@ -526,7 +529,7 @@ int cycles = piperesource->_cycles; uint stage = pipeline->_stages.index(piperesource->_stage); - if (NameList::Not_in_list == stage) { + if ((uint)NameList::Not_in_list == stage) { fprintf(stderr, "pipeline_res_mask_initializer: " "semantic error: " @@ -534,8 +537,8 @@ piperesource->_stage); exit(1); } - uint upper_limit = stage+cycles-1; - uint lower_limit = stage-1; + uint upper_limit = stage + cycles - 1; + uint lower_limit = stage - 1; uint upper_idx = upper_limit >> 5; uint lower_idx = lower_limit >> 5; uint upper_position = upper_limit & 0x1f; @@ -543,7 +546,7 @@ uint mask = (((uint)1) << upper_position) - 1; - while ( upper_idx > lower_idx ) { + while (upper_idx > lower_idx) { res_mask[upper_idx--] |= mask; mask = (uint)-1; } @@ -565,8 +568,9 @@ } resource_mask[templen] = 0; - if (last_comma) + if (last_comma) { last_comma[0] = ' '; + } // See if the same string is in the table int ndx = pipeline_res_mask.index(resource_mask); @@ -580,7 +584,7 @@ fprintf(fp_cpp, "static const Pipeline_Use_Element pipeline_res_mask_%03d[%d] = {\n%s};\n\n", ndx+1, element_count, resource_mask); - char * args = new char [9 + 2*masklen + maskdigit]; + char* args = new char [9 + 2*masklen + maskdigit]; sprintf(args, "0x%0*x, 0x%0*x, %*d", masklen, resources_used, @@ -589,8 +593,9 @@ pipeline_res_args.addName(args); } - else + else { delete [] resource_mask; + } delete [] res_mask; //delete [] res_masks; @@ -1565,6 +1570,13 @@ new_id = expand_instr->name(); InstructForm* expand_instruction = (InstructForm*)globalAD->globalNames()[new_id]; + + if (!expand_instruction) { + globalAD->syntax_err(node->_linenum, "In %s: instruction %s used in expand not declared\n", + node->_ident, new_id); + continue; + } + if (expand_instruction->has_temps()) { globalAD->syntax_err(node->_linenum, "In %s: expand rules using instructs with TEMPs aren't supported: %s", node->_ident, new_id); @@ -1623,6 +1635,13 @@ // Use 'parameter' at current position in list of new instruction's formals // instead of 'opid' when looking up info internal to new_inst const char *parameter = formal_lst->iter(); + if (!parameter) { + globalAD->syntax_err(node->_linenum, "Operand %s of expand instruction %s has" + " no equivalent in new instruction %s.", + opid, node->_ident, new_inst->_ident); + assert(0, "Wrong expand"); + } + // Check for an operand which is created in the expand rule if ((exp_pos = node->_exprule->_newopers.index(opid)) != -1) { new_pos = new_inst->operand_position(parameter,Component::USE); @@ -1787,7 +1806,7 @@ // Skip first unique operands. for( i = 1; i < cur_num_opnds; i++ ) { comp = node->_components.iter(); - if( (int)i != node->unique_opnds_idx(i) ) { + if (i != node->unique_opnds_idx(i)) { break; } new_num_opnds++; @@ -1795,7 +1814,7 @@ // Replace not unique operands with next unique operands. for( ; i < cur_num_opnds; i++ ) { comp = node->_components.iter(); - int j = node->unique_opnds_idx(i); + uint j = node->unique_opnds_idx(i); // unique_opnds_idx(i) is unique if unique_opnds_idx(j) is not unique. if( j != node->unique_opnds_idx(j) ) { fprintf(fp," set_opnd_array(%d, opnd_array(%d)->clone(C)); // %s\n", @@ -2100,16 +2119,21 @@ if (strcmp(rep_var,"$reg") == 0 || reg_conversion(rep_var) != NULL) { _reg_status = LITERAL_ACCESSED; } else { + _AD.syntax_err(_encoding._linenum, + "Invalid access to literal register parameter '%s' in %s.\n", + rep_var, _encoding._name); assert( false, "invalid access to literal register parameter"); } } // literal constant parameters must be accessed as a 'constant' field - if ( _constant_status != LITERAL_NOT_SEEN ) { - assert( _constant_status == LITERAL_SEEN, "Must have seen constant literal before now"); - if( strcmp(rep_var,"$constant") == 0 ) { - _constant_status = LITERAL_ACCESSED; + if (_constant_status != LITERAL_NOT_SEEN) { + assert(_constant_status == LITERAL_SEEN, "Must have seen constant literal before now"); + if (strcmp(rep_var,"$constant") == 0) { + _constant_status = LITERAL_ACCESSED; } else { - assert( false, "invalid access to literal constant parameter"); + _AD.syntax_err(_encoding._linenum, + "Invalid access to literal constant parameter '%s' in %s.\n", + rep_var, _encoding._name); } } } // end replacement and/or subfield @@ -2492,9 +2516,114 @@ fprintf(fp, " return (VerifyOops ? MachNode::size(ra_) : %s);\n", inst._size); // (3) and (4) - fprintf(fp,"}\n"); + fprintf(fp,"}\n\n"); } +// Emit late expand function. +void ArchDesc::defineLateExpand(FILE *fp, InstructForm &inst) { + InsEncode *ins_encode = inst._insencode; + + // Output instruction's lateExpand prototype. + fprintf(fp, "void %sNode::lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_) {\n", + inst._ident); + + assert((_encode != NULL) && (ins_encode != NULL), "You must define an encode section."); + + // Output each operand's offset into the array of registers. + inst.index_temps(fp, _globalNames); + + // Output variables "unsigned idx_", Node *n_ and "MachOpnd *op_" + // for each parameter specified in the encoding. + ins_encode->reset(); + const char *ec_name = ins_encode->encode_class_iter(); + assert(ec_name != NULL, "late expand must specify an encoding"); + + EncClass *encoding = _encode->encClass(ec_name); + if (encoding == NULL) { + fprintf(stderr, "User did not define contents of this encode_class: %s\n", ec_name); + abort(); + } + if (ins_encode->current_encoding_num_args() != encoding->num_args()) { + globalAD->syntax_err(ins_encode->_linenum, "In %s: passing %d arguments to %s but expecting %d", + inst._ident, ins_encode->current_encoding_num_args(), + ec_name, encoding->num_args()); + } + + fprintf(fp, " // Access to ins and operands for late expand.\n"); + const int buflen = 2000; + char idxbuf[buflen]; char *ib = idxbuf; idxbuf[0] = '\0'; + char nbuf [buflen]; char *nb = nbuf; nbuf[0] = '\0'; + char opbuf [buflen]; char *ob = opbuf; opbuf[0] = '\0'; + + encoding->_parameter_type.reset(); + encoding->_parameter_name.reset(); + const char *type = encoding->_parameter_type.iter(); + const char *name = encoding->_parameter_name.iter(); + int param_no = 0; + for (; (type != NULL) && (name != NULL); + (type = encoding->_parameter_type.iter()), (name = encoding->_parameter_name.iter())) { + const char* arg_name = ins_encode->rep_var_name(inst, param_no); + int idx = inst.operand_position_format(arg_name); + if (strcmp(arg_name, "constanttablebase") == 0) { + ib += sprintf(ib, " unsigned idx_%-5s = mach_constant_base_node_input(); \t// %s, \t%s\n", + name, type, arg_name); + nb += sprintf(nb, " Node *n_%-7s = lookup(idx_%s);\n", name, name); + // There is no operand for the constanttablebase. + } else if (inst.is_noninput_operand(idx)) { + globalAD->syntax_err(inst._linenum, + "In %s: you can not pass the non-input %s to a late expand encoding.\n", + inst._ident, arg_name); + } else { + ib += sprintf(ib, " unsigned idx_%-5s = idx%d; \t// %s, \t%s\n", + name, idx, type, arg_name); + nb += sprintf(nb, " Node *n_%-7s = lookup(idx_%s);\n", name, name); + ob += sprintf(ob, " %sOper *op_%s = (%sOper *)opnd_array(%d);\n", type, name, type, idx); + } + param_no++; + } + assert(ib < &idxbuf[buflen-1] && nb < &nbuf[buflen-1] && ob < &opbuf[buflen-1], "buffer overflow"); + + fprintf(fp, "%s", idxbuf); + fprintf(fp, " Node *n_region = lookup(0);\n"); + fprintf(fp, "%s%s", nbuf, opbuf); + fprintf(fp, " Compile *C = ra_->C;\n"); + + // Output this instruction's encodings. + fprintf(fp, " {"); + const char *ec_code = NULL; + const char *ec_rep_var = NULL; + assert(encoding == _encode->encClass(ec_name), ""); + + DefineEmitState pending(fp, *this, *encoding, *ins_encode, inst); + encoding->_code.reset(); + encoding->_rep_vars.reset(); + // Process list of user-defined strings, + // and occurrences of replacement variables. + // Replacement Vars are pushed into a list and then output. + while ((ec_code = encoding->_code.iter()) != NULL) { + if (! encoding->_code.is_signal(ec_code)) { + // Emit pending code. + pending.emit(); + pending.clear(); + // Emit this code section. + fprintf(fp, "%s", ec_code); + } else { + // A replacement variable or one of its subfields. + // Obtain replacement variable from list. + ec_rep_var = encoding->_rep_vars.iter(); + pending.add_rep_var(ec_rep_var); + } + } + // Emit pending code. + pending.emit(); + pending.clear(); + fprintf(fp, " }\n"); + + fprintf(fp, "}\n\n"); + + ec_name = ins_encode->encode_class_iter(); + assert(ec_name == NULL, "Late expand may only have one encoding."); +} // defineEmit ----------------------------------------------------------------- void ArchDesc::defineEmit(FILE* fp, InstructForm& inst) { InsEncode* encode = inst._insencode; @@ -2845,7 +2974,7 @@ } else if ( (strcmp(name,"disp") == 0) ) { fprintf(fp,"(PhaseRegAlloc *ra_, const Node *node, int idx) const { \n"); } else { - fprintf(fp,"() const { \n"); + fprintf(fp, "() const {\n"); } // Check for hexadecimal value OR replacement variable @@ -2895,6 +3024,8 @@ // Hex value fprintf(fp," return %s;\n", encoding); } else { + globalAD->syntax_err(oper._linenum, "In operand %s: Do not support this encode constant: '%s' for %s.", + oper._ident, encoding, name); assert( false, "Do not support octal or decimal encode constants"); } fprintf(fp," }\n"); @@ -3146,7 +3277,15 @@ // Ensure this is a machine-world instruction if ( instr->ideal_only() ) continue; - if (instr->_insencode) defineEmit (fp, *instr); + if (instr->_insencode) { + if (instr->lateExpands()) { + // Don't write this to _CPP_EXPAND_file, as the code generated calls C-code + // from code sections in ad file that is dumped to fp. + defineLateExpand(fp, *instr); + } else { + defineEmit(fp, *instr); + } + } if (instr->is_mach_constant()) defineEvalConstant(fp, *instr); if (instr->_size) defineSize (fp, *instr); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/adlc/output_h.cpp --- a/src/share/vm/adlc/output_h.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/adlc/output_h.cpp Sun May 21 11:12:48 2017 -0700 @@ -26,7 +26,11 @@ #include "adlc.hpp" // The comment delimiter used in format statements after assembler instructions. +#if defined(PPC64) +#define commentSeperator "\t//" +#else #define commentSeperator "!" +#endif // Generate the #define that describes the number of registers. static void defineRegCount(FILE *fp, RegisterForm *registers) { @@ -1511,7 +1515,20 @@ if ( instr->is_ideal_jump() ) { fprintf(fp, " GrowableArray _index2label;\n"); } - fprintf(fp,"public:\n"); + + fprintf(fp, "public:\n"); + + Attribute *att = instr->_attribs; + // Fields of the node specified in the ad file. + while (att != NULL) { + if (strncmp(att->_ident, "ins_field_", 10) == 0) { + const char *field_name = att->_ident+10; + const char *field_type = att->_val; + fprintf(fp, " %s _%s;\n", field_type, field_name); + } + att = (Attribute *)att->_next; + } + fprintf(fp," MachOper *opnd_array(uint operand_index) const {\n"); fprintf(fp," assert(operand_index < _num_opnds, \"invalid _opnd_array index\");\n"); fprintf(fp," return _opnd_array[operand_index];\n"); @@ -1558,14 +1575,23 @@ Attribute *attr = instr->_attribs; bool avoid_back_to_back = false; while (attr != NULL) { - if (strcmp(attr->_ident,"ins_cost") && - strcmp(attr->_ident,"ins_short_branch")) { - fprintf(fp," int %s() const { return %s; }\n", + if (strcmp (attr->_ident,"ins_cost") && + strncmp(attr->_ident,"ins_field_", 10) != 0 && + // Rematerialize attributes are used in formssel, no function needs to be generated. + strcmp (attr->_ident,"ins_cannot_rematerialize") != 0 && + strcmp (attr->_ident,"ins_should_rematerialize") != 0 && + // Must match function in node.hpp: return type bool, no prefix "ins_". + strcmp (attr->_ident,"ins_is_TrapBasedCheckNode") != 0 && + strcmp (attr->_ident,"ins_short_branch")) { + fprintf(fp," virtual int %s() const { return %s; }\n", attr->_ident, attr->_val); } // Check value for ins_avoid_back_to_back, and if it is true (1), set the flag if (!strcmp(attr->_ident,"ins_avoid_back_to_back") && attr->int_val(*this) != 0) avoid_back_to_back = true; + if (strcmp (attr->_ident,"ins_is_TrapBasedCheckNode") == 0) + fprintf(fp," virtual bool is_TrapBasedCheckNode() const { return %s; }\n", attr->_val); + attr = (Attribute *)attr->_next; } @@ -1579,7 +1605,12 @@ // Output the opcode function and the encode function here using the // encoding class information in the _insencode slot. if ( instr->_insencode ) { - fprintf(fp," virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;\n"); + if (instr->lateExpands()) { + fprintf(fp," virtual bool requires_late_expand() const { return true; }\n"); + fprintf(fp," virtual void lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_);\n"); + } else { + fprintf(fp," virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;\n"); + } } // virtual function for getting the size of an instruction diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/asm/assembler.cpp --- a/src/share/vm/asm/assembler.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/asm/assembler.cpp Sun May 21 11:12:48 2017 -0700 @@ -100,11 +100,11 @@ set_code_section(code()->insts()); } -// Inform CodeBuffer that incoming code and relocation will be for stubs +// Inform CodeBuffer that incoming code and relocation will be for consts address AbstractAssembler::start_a_const(int required_space, int required_align) { CodeBuffer* cb = code(); CodeSection* cs = cb->consts(); - assert(_code_section == cb->insts(), "not in insts?"); + assert(_code_section == cb->insts() || _code_section == cb->stubs(), "not in insts/stubs?"); sync(); address end = cs->end(); int pad = -(intptr_t)end & (required_align-1); @@ -121,14 +121,13 @@ } // Inform CodeBuffer that incoming code and relocation will be code -// Should not be called if start_a_const() returned NULL -void AbstractAssembler::end_a_const() { +// in section cs (insts or stubs). +void AbstractAssembler::end_a_const(CodeSection* cs) { assert(_code_section == code()->consts(), "not in consts?"); sync(); - set_code_section(code()->insts()); + set_code_section(cs); } - void AbstractAssembler::flush() { sync(); ICache::invalidate_range(addr_at(0), offset()); @@ -178,7 +177,7 @@ void AbstractAssembler::generate_stack_overflow_check( int frame_size_in_bytes) { if (UseStackBanging) { // Each code entry causes one stack bang n pages down the stack where n - // is configurable by StackBangPages. The setting depends on the maximum + // is configurable by StackShadowPages. The setting depends on the maximum // depth of VM call stack or native before going back into java code, // since only java code can raise a stack overflow exception using the // stack banging mechanism. The VM and native code does not detect stack @@ -210,6 +209,7 @@ void Label::add_patch_at(CodeBuffer* cb, int branch_loc) { assert(_loc == -1, "Label is unbound"); + if (cb->insts()->scratch_emit()) return; // Avoid patch attempts in already destroyed scratch buffer. if (_patch_index < PatchCacheSize) { _patches[_patch_index] = branch_loc; } else { @@ -277,6 +277,7 @@ bool match(BasicType t, value_fn_t cfn) { return type == t && value_fn == cfn; } + static void update(DelayedConstant* dcon); static void update_all(); }; @@ -294,6 +295,8 @@ // (cmpxchg not because this is multi-threaded but because I'm paranoid) if (Atomic::cmpxchg_ptr(CAST_FROM_FN_PTR(void*, cfn), &dcon->value_fn, NULL) == NULL) { dcon->type = type; + // Initialize value in case the constant is generated after the call to update_all(). + update(dcon); return dcon; } } @@ -304,16 +307,20 @@ return NULL; } +void DelayedConstant::update(DelayedConstant* dcon) { + typedef int (*int_fn_t)(); + typedef address (*address_fn_t)(); + switch (dcon->type) { + case T_INT: dcon->value = (intptr_t) ((int_fn_t) dcon->value_fn)(); break; + case T_ADDRESS: dcon->value = (intptr_t) ((address_fn_t)dcon->value_fn)(); break; + } +} + void DelayedConstant::update_all() { for (int i = 0; i < DC_LIMIT; i++) { DelayedConstant* dcon = &delayed_constants[i]; if (dcon->value_fn != NULL && dcon->value == 0) { - typedef int (*int_fn_t)(); - typedef address (*address_fn_t)(); - switch (dcon->type) { - case T_INT: dcon->value = (intptr_t) ((int_fn_t) dcon->value_fn)(); break; - case T_ADDRESS: dcon->value = (intptr_t) ((address_fn_t)dcon->value_fn)(); break; - } + update(dcon); } } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/asm/assembler.hpp --- a/src/share/vm/asm/assembler.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/asm/assembler.hpp Sun May 21 11:12:48 2017 -0700 @@ -206,9 +206,11 @@ address _code_pos; // current code generation position OopRecorder* _oop_recorder; // support for relocInfo::oop_type + public: // Code emission & accessing address addr_at(int pos) const { return _code_begin + pos; } + protected: // This routine is called with a label is used for an address. // Labels and displacements truck in offsets, but target must return a PC. address target(Label& L); // return _code_section->target(L) @@ -350,52 +352,60 @@ void end_a_stub(); // Ditto for constants. address start_a_const(int required_space, int required_align = sizeof(double)); - void end_a_const(); + void end_a_const(CodeSection* cs); // Pass the codesection to continue in (insts or stubs?). // constants support + // + // We must remember the code section (insts or stubs) in c1 + // so we can reset to the proper section in end_a_const(). address long_constant(jlong c) { + CodeSection* c1 = _code_section; address ptr = start_a_const(sizeof(c), sizeof(c)); if (ptr != NULL) { *(jlong*)ptr = c; _code_pos = ptr + sizeof(c); - end_a_const(); + end_a_const(c1); } return ptr; } address double_constant(jdouble c) { + CodeSection* c1 = _code_section; address ptr = start_a_const(sizeof(c), sizeof(c)); if (ptr != NULL) { *(jdouble*)ptr = c; _code_pos = ptr + sizeof(c); - end_a_const(); + end_a_const(c1); } return ptr; } address float_constant(jfloat c) { + CodeSection* c1 = _code_section; address ptr = start_a_const(sizeof(c), sizeof(c)); if (ptr != NULL) { *(jfloat*)ptr = c; _code_pos = ptr + sizeof(c); - end_a_const(); + end_a_const(c1); } return ptr; } address address_constant(address c) { + CodeSection* c1 = _code_section; address ptr = start_a_const(sizeof(c), sizeof(c)); if (ptr != NULL) { *(address*)ptr = c; _code_pos = ptr + sizeof(c); - end_a_const(); + end_a_const(c1); } return ptr; } address address_constant(address c, RelocationHolder const& rspec) { + CodeSection* c1 = _code_section; address ptr = start_a_const(sizeof(c), sizeof(c)); if (ptr != NULL) { relocate(rspec); *(address*)ptr = c; _code_pos = ptr + sizeof(c); - end_a_const(); + end_a_const(c1); } return ptr; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/asm/codeBuffer.cpp --- a/src/share/vm/asm/codeBuffer.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/asm/codeBuffer.cpp Sun May 21 11:12:48 2017 -0700 @@ -294,6 +294,8 @@ } void CodeSection::relocate(address at, RelocationHolder const& spec, int format) { + // Do not relocate in scratch buffers. + if (scratch_emit()) return; Relocation* reloc = spec.reloc(); relocInfo::relocType rtype = (relocInfo::relocType) reloc->type(); if (rtype == relocInfo::none) return; @@ -665,7 +667,18 @@ // Make the new code copy use the old copy's relocations: dest_cs->initialize_locs_from(cs); + } + // Do relocation after all sections are copied. + // This is necessary if the code uses constants in stubs, which are + // relocated when the corresponding instruction in the code (e.g., a + // call) is relocated. Stubs are placed behind the main code + // section, so that section has to be copied before relocating. + for (int n = (int) SECT_FIRST; n < (int)SECT_LIMIT; n++) { + // pull code out of each section + const CodeSection* cs = code_section(n); + if (cs->is_empty()) continue; // skip trivial section + CodeSection* dest_cs = dest->code_section(n); { // Repair the pc relative information in the code after the move RelocIterator iter(dest_cs); while (iter.next()) { @@ -825,9 +838,7 @@ void CodeBuffer::take_over_code_from(CodeBuffer* cb) { // Must already have disposed of the old blob somehow. assert(blob() == NULL, "must be empty"); -#ifdef ASSERT -#endif // Take the new blob away from cb. set_blob(cb->blob()); // Take over all the section pointers. diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/asm/codeBuffer.hpp --- a/src/share/vm/asm/codeBuffer.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/asm/codeBuffer.hpp Sun May 21 11:12:48 2017 -0700 @@ -93,6 +93,7 @@ address _locs_point; // last relocated position (grows upward) bool _locs_own; // did I allocate the locs myself? bool _frozen; // no more expansion of this section + bool _scratch_emit; // buffer is used for scratch emit, don't relocate! char _index; // my section number (SECT_INST, etc.) CodeBuffer* _outer; // enclosing CodeBuffer @@ -109,6 +110,7 @@ _locs_point = NULL; _locs_own = false; _frozen = false; + _scratch_emit = false; debug_only(_index = (char)-1); debug_only(_outer = (CodeBuffer*)badAddress); } @@ -166,6 +168,7 @@ bool is_empty() const { return _start == _end; } bool is_frozen() const { return _frozen; } bool has_locs() const { return _locs_end != NULL; } + bool scratch_emit() { return _scratch_emit; } CodeBuffer* outer() const { return _outer; } @@ -192,6 +195,7 @@ assert(allocates2(pc), "relocation addr must be in this section"); _locs_point = pc; } + void set_scratch_emit() { _scratch_emit = true; } // Code emission void emit_int8 (int8_t x) { *((int8_t*) end()) = x; set_end(end() + 1); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/c1/c1_FrameMap.cpp --- a/src/share/vm/c1/c1_FrameMap.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/c1/c1_FrameMap.cpp Sun May 21 11:12:48 2017 -0700 @@ -133,7 +133,7 @@ } } - intptr_t out_preserve = SharedRuntime::c_calling_convention(sig_bt, regs, sizeargs); + intptr_t out_preserve = SharedRuntime::c_calling_convention(sig_bt, regs, NULL, sizeargs); LIR_OprList* args = new LIR_OprList(signature->length()); for (i = 0; i < sizeargs;) { BasicType t = sig_bt[i]; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/c1/c1_IR.cpp --- a/src/share/vm/c1/c1_IR.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/c1/c1_IR.cpp Sun May 21 11:12:48 2017 -0700 @@ -499,7 +499,7 @@ _loop_map(0, 0), // initialized later with correct size _compilation(c) { - TRACE_LINEAR_SCAN(2, "***** computing linear-scan block order"); + TRACE_LINEAR_SCAN(2, tty->print_cr("***** computing linear-scan block order")); init_visited(); count_edges(start_block, NULL); @@ -665,7 +665,7 @@ } void ComputeLinearScanOrder::assign_loop_depth(BlockBegin* start_block) { - TRACE_LINEAR_SCAN(3, "----- computing loop-depth and weight"); + TRACE_LINEAR_SCAN(3, tty->print_cr("----- computing loop-depth and weight")); init_visited(); assert(_work_list.is_empty(), "work list must be empty before processing"); @@ -839,7 +839,7 @@ } void ComputeLinearScanOrder::compute_order(BlockBegin* start_block) { - TRACE_LINEAR_SCAN(3, "----- computing final block order"); + TRACE_LINEAR_SCAN(3, tty->print_cr("----- computing final block order")); // the start block is always the first block in the linear scan order _linear_scan_order = new BlockList(_num_blocks); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/c1/c1_globals.hpp --- a/src/share/vm/c1/c1_globals.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/c1/c1_globals.hpp Sun May 21 11:12:48 2017 -0700 @@ -47,6 +47,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "c1_globals_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "c1_globals_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "c1_globals_bsd.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/ci/ciUtilities.hpp --- a/src/share/vm/ci/ciUtilities.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/ci/ciUtilities.hpp Sun May 21 11:12:48 2017 -0700 @@ -93,7 +93,7 @@ CLEAR_PENDING_EXCEPTION; \ return (result); \ } \ - (0 + (void)(0 #define KILL_COMPILE_ON_ANY \ THREAD); \ @@ -101,7 +101,7 @@ fatal("unhandled ci exception"); \ CLEAR_PENDING_EXCEPTION; \ } \ -(0 +(void)(0 inline const char* bool_to_str(bool b) { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/classFileParser.cpp --- a/src/share/vm/classfile/classFileParser.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/classFileParser.cpp Sun May 21 11:12:48 2017 -0700 @@ -966,7 +966,8 @@ runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); - cfs->skip_u1(runtime_visible_annotations_length, CHECK); + cfs->guarantee_more(runtime_visible_annotations_length, CHECK); + cfs->skip_u1_fast(runtime_visible_annotations_length); } else if (PreserveAllAnnotations && attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { runtime_invisible_annotations_length = attribute_length; runtime_invisible_annotations = cfs->get_u1_buffer(); @@ -1658,6 +1659,11 @@ return index; } +// Safely increment index by val if does not pass limit +#define SAFE_ADD(index, limit, val) \ +if (index >= limit - val) return limit; \ +index += val; + // Skip an annotation value. Return >=limit if there is any problem. int ClassFileParser::skip_annotation_value(u1* buffer, int limit, int index) { // value := switch (tag:u1) { @@ -1668,19 +1674,19 @@ // case @: annotation; // case s: s_con:u2; // } - if ((index += 1) >= limit) return limit; // read tag + SAFE_ADD(index, limit, 1); // read tag u1 tag = buffer[index-1]; switch (tag) { case 'B': case 'C': case 'I': case 'S': case 'Z': case 'D': case 'F': case 'J': case 'c': case 's': - index += 2; // skip con or s_con + SAFE_ADD(index, limit, 2); // skip con or s_con break; case 'e': - index += 4; // skip e_class, e_name + SAFE_ADD(index, limit, 4); // skip e_class, e_name break; case '[': { - if ((index += 2) >= limit) return limit; // read nval + SAFE_ADD(index, limit, 2); // read nval int nval = Bytes::get_Java_u2(buffer+index-2); while (--nval >= 0 && index < limit) { index = skip_annotation_value(buffer, limit, index); @@ -1704,8 +1710,8 @@ ClassFileParser::AnnotationCollector* coll, TRAPS) { // annotations := do(nann:u2) {annotation} - int index = 0; - if ((index += 2) >= limit) return; // read nann + int index = 2; + if (index >= limit) return; // read nann int nann = Bytes::get_Java_u2(buffer+index-2); enum { // initial annotation layout atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;' @@ -1721,7 +1727,8 @@ c_size = 9, // end of 'c' annotation min_size = 6 // smallest possible size (zero members) }; - while ((--nann) >= 0 && (index-2 + min_size <= limit)) { + // Cannot add min_size to index in case of overflow MAX_INT + while ((--nann) >= 0 && (index-2 <= limit - min_size)) { int index0 = index; index = skip_annotation(buffer, limit, index); u1* abase = buffer + index0; @@ -2138,8 +2145,9 @@ runtime_visible_annotations_length = method_attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); + cfs->guarantee_more(runtime_visible_annotations_length, CHECK_(nullHandle)); parse_annotations(class_loader, runtime_visible_annotations, runtime_visible_annotations_length, cp, &parsed_annotations, CHECK_(nullHandle)); - cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle)); + cfs->skip_u1_fast(runtime_visible_annotations_length); } else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { runtime_invisible_annotations_length = method_attribute_length; runtime_invisible_annotations = cfs->get_u1_buffer(); @@ -2835,13 +2843,14 @@ runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); + cfs->guarantee_more(runtime_visible_annotations_length, CHECK); parse_annotations(class_loader, runtime_visible_annotations, runtime_visible_annotations_length, cp, parsed_annotations, CHECK); - cfs->skip_u1(runtime_visible_annotations_length, CHECK); + cfs->skip_u1_fast(runtime_visible_annotations_length); } else if (PreserveAllAnnotations && tag == vmSymbols::tag_runtime_invisible_annotations()) { runtime_invisible_annotations_length = attribute_length; runtime_invisible_annotations = cfs->get_u1_buffer(); @@ -3219,6 +3228,11 @@ // Make sure this is the end of class file stream guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle)); + if (_class_name == vmSymbols::java_lang_Object()) { + check_property(local_interfaces == Universe::the_empty_system_obj_array(), + "java.lang.Object cannot implement an interface in class file %s", + CHECK_(nullHandle)); + } // We check super class after class file is parsed and format is checked if (super_class_index > 0 && super_klass.is_null()) { Symbol* sk = cp->klass_name_at(super_class_index); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/classLoader.cpp --- a/src/share/vm/classfile/classLoader.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/classLoader.cpp Sun May 21 11:12:48 2017 -0700 @@ -68,6 +68,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/javaClasses.cpp Sun May 21 11:12:48 2017 -0700 @@ -60,6 +60,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/stackMapFrame.cpp --- a/src/share/vm/classfile/stackMapFrame.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/stackMapFrame.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -174,47 +174,8 @@ return i; } -bool StackMapFrame::has_flag_match_exception( - const StackMapFrame* target) const { - // We allow flags of {UninitThis} to assign to {} if-and-only-if the - // target frame does not depend upon the current type. - // This is slightly too strict, as we need only enforce that the - // slots that were initialized by the (the things that were - // UninitializedThis before initialize_object() converted them) are unused. - // However we didn't save that information so we'll enforce this upon - // anything that might have been initialized. This is a rare situation - // and javac never generates code that would end up here, but some profilers - // (such as NetBeans) might, when adding exception handlers in - // methods to cover the invokespecial instruction. See 7020118. - - assert(max_locals() == target->max_locals() && - stack_size() == target->stack_size(), "StackMap sizes must match"); - - VerificationType top = VerificationType::top_type(); - VerificationType this_type = verifier()->current_type(); - - if (!flag_this_uninit() || target->flags() != 0) { - return false; - } - - for (int i = 0; i < target->locals_size(); ++i) { - if (locals()[i] == this_type && target->locals()[i] != top) { - return false; - } - } - - for (int i = 0; i < target->stack_size(); ++i) { - if (stack()[i] == this_type && target->stack()[i] != top) { - return false; - } - } - - return true; -} - bool StackMapFrame::is_assignable_to( - const StackMapFrame* target, bool is_exception_handler, - ErrorContext* ctx, TRAPS) const { + const StackMapFrame* target, ErrorContext* ctx, TRAPS) const { if (_max_locals != target->max_locals()) { *ctx = ErrorContext::locals_size_mismatch( _offset, (StackMapFrame*)this, (StackMapFrame*)target); @@ -245,8 +206,7 @@ return false; } - bool match_flags = (_flags | target->flags()) == target->flags(); - if (match_flags || is_exception_handler && has_flag_match_exception(target)) { + if ((_flags | target->flags()) == target->flags()) { return true; } else { *ctx = ErrorContext::bad_flags(target->offset(), diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/stackMapFrame.hpp --- a/src/share/vm/classfile/stackMapFrame.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/stackMapFrame.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -172,8 +172,7 @@ // Return true if this stack map frame is assignable to target. bool is_assignable_to( - const StackMapFrame* target, bool is_exception_handler, - ErrorContext* ctx, TRAPS) const; + const StackMapFrame* target, ErrorContext* ctx, TRAPS) const; inline void set_mark() { #ifdef DEBUG @@ -295,8 +294,6 @@ int is_assignable_to( VerificationType* src, VerificationType* target, int32_t len, TRAPS) const; - bool has_flag_match_exception(const StackMapFrame* target) const; - TypeOrigin stack_top_ctx(); void print_on(outputStream* str) const; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/stackMapTable.cpp --- a/src/share/vm/classfile/stackMapTable.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/stackMapTable.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -70,26 +70,25 @@ bool StackMapTable::match_stackmap( StackMapFrame* frame, int32_t target, - bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const { + bool match, bool update, ErrorContext* ctx, TRAPS) const { int index = get_index_from_offset(target); - return match_stackmap(frame, target, index, match, update, handler, ctx, THREAD); + return match_stackmap(frame, target, index, match, update, ctx, THREAD); } // Match and/or update current_frame to the frame in stackmap table with // specified offset and frame index. Return true if the two frames match. -// handler is true if the frame in stackmap_table is for an exception handler. // -// The values of match and update are: _match__update__handler +// The values of match and update are: _match__update // -// checking a branch target: true false false -// checking an exception handler: true false true +// checking a branch target: true false +// checking an exception handler: true false // linear bytecode verification following an -// unconditional branch: false true false +// unconditional branch: false true // linear bytecode verification not following an -// unconditional branch: true true false +// unconditional branch: true true bool StackMapTable::match_stackmap( StackMapFrame* frame, int32_t target, int32_t frame_index, - bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const { + bool match, bool update, ErrorContext* ctx, TRAPS) const { if (frame_index < 0 || frame_index >= _frame_count) { *ctx = ErrorContext::missing_stackmap(frame->offset()); frame->verifier()->verify_error( @@ -102,7 +101,7 @@ if (match) { // Has direct control flow from last instruction, need to match the two // frames. - result = frame->is_assignable_to(stackmap_frame, handler, + result = frame->is_assignable_to(stackmap_frame, ctx, CHECK_VERIFY_(frame->verifier(), result)); } if (update) { @@ -126,7 +125,7 @@ StackMapFrame* frame, int32_t target, TRAPS) const { ErrorContext ctx; bool match = match_stackmap( - frame, target, true, false, false, &ctx, CHECK_VERIFY(frame->verifier())); + frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { frame->verifier()->verify_error(ctx, "Inconsistent stackmap frames at branch target %d", target); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/stackMapTable.hpp --- a/src/share/vm/classfile/stackMapTable.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/stackMapTable.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2016, 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 @@ -74,12 +74,12 @@ // specified offset. Return true if the two frames match. bool match_stackmap( StackMapFrame* current_frame, int32_t offset, - bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const; + bool match, bool update, ErrorContext* ctx, TRAPS) const; // Match and/or update current_frame to the frame in stackmap table with // specified offset and frame index. Return true if the two frames match. bool match_stackmap( StackMapFrame* current_frame, int32_t offset, int32_t frame_index, - bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const; + bool match, bool update, ErrorContext* ctx, TRAPS) const; // Check jump instructions. Make sure there are no uninitialized // instances on backward branch. diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/stackMapTableFormat.hpp --- a/src/share/vm/classfile/stackMapTableFormat.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/stackMapTableFormat.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2016, 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 @@ -203,6 +203,7 @@ inline bool verify(address start, address end) const; inline void print_on(outputStream* st, int current_offset) const; + inline void print_truncated(outputStream* st, int current_offset) const; // Create as_xxx and is_xxx methods for the subtypes #define FRAME_TYPE_DECL(stackmap_frame_type, arg1, arg2) \ @@ -263,6 +264,10 @@ void print_on(outputStream* st, int current_offset = -1) const { st->print("same_frame(@%d)", offset_delta() + current_offset); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + print_on(st, current_offset); + } }; class same_frame_extended : public stack_map_frame { @@ -309,6 +314,10 @@ void print_on(outputStream* st, int current_offset = -1) const { st->print("same_frame_extended(@%d)", offset_delta() + current_offset); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + print_on(st, current_offset); + } }; class same_locals_1_stack_item_frame : public stack_map_frame { @@ -381,6 +390,11 @@ types()->print_on(st); st->print(")"); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + st->print("same_locals_1_stack_item_frame(@%d), output truncated, Stackmap exceeds table size.", + offset_delta() + current_offset); + } }; class same_locals_1_stack_item_extended : public stack_map_frame { @@ -446,6 +460,11 @@ types()->print_on(st); st->print(")"); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + st->print("same_locals_1_stack_item_extended(@%d), output truncated, Stackmap exceeds table size.", + offset_delta() + current_offset); + } }; class chop_frame : public stack_map_frame { @@ -511,6 +530,10 @@ void print_on(outputStream* st, int current_offset = -1) const { st->print("chop_frame(@%d,%d)", offset_delta() + current_offset, chops()); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + print_on(st, current_offset); + } }; class append_frame : public stack_map_frame { @@ -619,6 +642,11 @@ } st->print(")"); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + st->print("append_frame(@%d), output truncated, Stackmap exceeds table size.", + offset_delta() + current_offset); + } }; class full_frame : public stack_map_frame { @@ -784,6 +812,11 @@ } st->print("})"); } + + void print_truncated(outputStream* st, int current_offset = -1) const { + st->print("full_frame(@%d), output truncated, Stackmap exceeds table size.", + offset_delta() + current_offset); + } }; #define VIRTUAL_DISPATCH(stack_frame_type, func_name, args) \ @@ -841,6 +874,10 @@ FOR_EACH_STACKMAP_FRAME_TYPE(VOID_VIRTUAL_DISPATCH, print_on, (st, offs)); } +void stack_map_frame::print_truncated(outputStream* st, int offs = -1) const { + FOR_EACH_STACKMAP_FRAME_TYPE(VOID_VIRTUAL_DISPATCH, print_truncated, (st, offs)); +} + #undef VIRTUAL_DISPATCH #undef VOID_VIRTUAL_DISPATCH diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/verifier.cpp --- a/src/share/vm/classfile/verifier.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/verifier.cpp Sun May 21 11:12:48 2017 -0700 @@ -503,8 +503,13 @@ stack_map_frame* sm_frame = sm_table->entries(); streamIndentor si2(ss); int current_offset = -1; + address end_of_sm_table = (address)sm_table + method->stackmap_data()->length(); for (u2 i = 0; i < sm_table->number_of_entries(); ++i) { ss->indent(); + if (!sm_frame->verify((address)sm_frame, end_of_sm_table)) { + sm_frame->print_truncated(ss, current_offset); + return; + } sm_frame->print_on(ss, current_offset); ss->print_cr(""); current_offset += sm_frame->offset_delta(); @@ -1786,7 +1791,7 @@ // If matched, current_frame will be updated by this method. bool matches = stackmap_table->match_stackmap( current_frame, this_offset, stackmap_index, - !no_control_flow, true, false, &ctx, CHECK_VERIFY_(this, 0)); + !no_control_flow, true, &ctx, CHECK_VERIFY_(this, 0)); if (!matches) { // report type error verify_error(ctx, "Instruction type does not match stack map"); @@ -1833,7 +1838,7 @@ } ErrorContext ctx; bool matches = stackmap_table->match_stackmap( - new_frame, handler_pc, true, false, true, &ctx, CHECK_VERIFY(this)); + new_frame, handler_pc, true, false, &ctx, CHECK_VERIFY(this)); if (!matches) { verify_error(ctx, "Stack map does not match the one at " "exception handler %d", handler_pc); @@ -2223,14 +2228,20 @@ } // Look at the method's handlers. If the bci is in the handler's try block -// then check if the handler_pc is already on the stack. If not, push it. +// then check if the handler_pc is already on the stack. If not, push it +// unless the handler has already been scanned. void ClassVerifier::push_handlers(ExceptionTable* exhandlers, + GrowableArray* handler_list, GrowableArray* handler_stack, u4 bci) { int exlength = exhandlers->length(); for(int x = 0; x < exlength; x++) { if (bci >= exhandlers->start_pc(x) && bci < exhandlers->end_pc(x)) { - handler_stack->append_if_missing(exhandlers->handler_pc(x)); + u4 exhandler_pc = exhandlers->handler_pc(x); + if (!handler_list->contains(exhandler_pc)) { + handler_stack->append_if_missing(exhandler_pc); + handler_list->append(exhandler_pc); + } } } } @@ -2248,6 +2259,10 @@ GrowableArray* bci_stack = new GrowableArray(30); // Create stack for handlers for try blocks containing this handler. GrowableArray* handler_stack = new GrowableArray(30); + // Create list of handlers that have been pushed onto the handler_stack + // so that handlers embedded inside of their own TRY blocks only get + // scanned once. + GrowableArray* handler_list = new GrowableArray(30); // Create list of visited branch opcodes (goto* and if*). GrowableArray* visited_branches = new GrowableArray(30); ExceptionTable exhandlers(_method()); @@ -2266,7 +2281,7 @@ // If the bytecode is in a TRY block, push its handlers so they // will get parsed. - push_handlers(&exhandlers, handler_stack, bci); + push_handlers(&exhandlers, handler_list, handler_stack, bci); switch (opcode) { case Bytecodes::_if_icmpeq: diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/classfile/verifier.hpp --- a/src/share/vm/classfile/verifier.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/classfile/verifier.hpp Sun May 21 11:12:48 2017 -0700 @@ -85,9 +85,9 @@ // These macros are used similarly to CHECK macros but also check // the status of the verifier and return if that has an error. #define CHECK_VERIFY(verifier) \ - CHECK); if ((verifier)->has_error()) return; (0 + CHECK); if ((verifier)->has_error()) return; ((void)0 #define CHECK_VERIFY_(verifier, result) \ - CHECK_(result)); if ((verifier)->has_error()) return (result); (0 + CHECK_(result)); if ((verifier)->has_error()) return (result); ((void)0 class TypeOrigin VALUE_OBJ_CLASS_SPEC { private: @@ -303,9 +303,10 @@ bool* this_uninit, constantPoolHandle cp, StackMapTable* stackmap_table, TRAPS); - // Used by ends_in_athrow() to push all handlers that contain bci onto - // the handler_stack, if the handler is not already on the stack. + // Used by ends_in_athrow() to push all handlers that contain bci onto the + // handler_stack, if the handler has not already been pushed on the stack. void push_handlers(ExceptionTable* exhandlers, + GrowableArray* handler_list, GrowableArray* handler_stack, u4 bci); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/compiledIC.cpp --- a/src/share/vm/code/compiledIC.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/compiledIC.cpp Sun May 21 11:12:48 2017 -0700 @@ -524,7 +524,7 @@ callee->name_and_sig_as_C_string()); } - NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + comp_to_int_load_offset); // creation also verifies the object NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); assert(method_holder->data() == 0 || method_holder->data() == (intptr_t)callee(), "a) MT-unsafe modification of inline cache"); @@ -587,7 +587,7 @@ // Reset stub address stub = static_stub->addr(); assert(stub!=NULL, "stub not found"); - NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + comp_to_int_load_offset); // creation also verifies the object NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); method_holder->set_data(0); jump->set_jump_destination((address)-1); @@ -666,11 +666,16 @@ // Verify stub address stub = find_stub(); assert(stub != NULL, "no stub found for static call"); - NativeMovConstReg* method_holder = nativeMovConstReg_at(stub); // creation also verifies the object + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + comp_to_int_load_offset); // creation also verifies the object + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); // Verify state assert(is_clean() || is_call_to_compiled() || is_call_to_interpreted(), "sanity check"); } +#endif // !PRODUCT + +#ifndef COMPILER2 +const int CompiledStaticCall::comp_to_int_load_offset = 0; #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/compiledIC.hpp --- a/src/share/vm/code/compiledIC.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/compiledIC.hpp Sun May 21 11:12:48 2017 -0700 @@ -244,6 +244,10 @@ // Misc. void print() PRODUCT_RETURN; void verify() PRODUCT_RETURN; + + // Offset of start of compiled java to interpreter stub to the load + // constant that loads the inline cache (IC). See ad file. + static const int comp_to_int_load_offset; }; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/dependencies.cpp --- a/src/share/vm/code/dependencies.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/dependencies.cpp Sun May 21 11:12:48 2017 -0700 @@ -936,7 +936,7 @@ assert(changes.involves_context(context_type), "irrelevant dependency"); klassOop new_type = changes.new_type(); - count_find_witness_calls(); + (void)count_find_witness_calls(); NOT_PRODUCT(deps_find_witness_singles++); // Current thread must be in VM (not native mode, as in CI): diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/nmethod.cpp Sun May 21 11:12:48 2017 -0700 @@ -1331,7 +1331,12 @@ } // Change state - _state = state; + // We must ensure that state transition normal -> not_entrant is + // visible before VEP patch becomes visible. We must complete + // previous memory accesses before the state change. + OrderAccess::release_store(&_state, state); + // The state change must happen before patching the entry point. + OrderAccess::storestore(); // Log the transition once log_state_change(); @@ -2506,7 +2511,8 @@ relocation_begin()-1+ip[1]); for (; ip < index_end; ip++) tty->print_cr(" (%d ?)", ip[0]); - tty->print_cr(" @" INTPTR_FORMAT ": index_size=%d", ip, *ip++); + tty->print_cr(" @" INTPTR_FORMAT ": index_size=%d", ip, *ip); + ip++; tty->print_cr("reloc_end @" INTPTR_FORMAT ":", ip); } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/nmethod.hpp --- a/src/share/vm/code/nmethod.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/nmethod.hpp Sun May 21 11:12:48 2017 -0700 @@ -69,7 +69,12 @@ friend class VMStructs; private: enum { cache_size = 4 }; - PcDesc* _pc_descs[cache_size]; // last cache_size pc_descs found + // The array elements MUST be volatile! Several threads may modify + // and read from the cache concurrently. find_pc_desc_internal has + // returned wrong results. C++ compiler (namely xlC12) may duplicate + // C++ field accesses if the elements are not volatile. + typedef PcDesc* PcDescPtr; + volatile PcDescPtr _pc_descs[cache_size]; // last cache_size pc_descs found public: PcDescCache() { debug_only(_pc_descs[0] = NULL); } void reset_to(PcDesc* initial_pc_desc); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/relocInfo.cpp --- a/src/share/vm/code/relocInfo.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/relocInfo.cpp Sun May 21 11:12:48 2017 -0700 @@ -621,6 +621,18 @@ _static_call = address_from_scaled_offset(unpack_1_int(), base); } +void trampoline_stub_Relocation::pack_data_to(CodeSection* dest ) { + short* p = (short*) dest->locs_end(); + CodeSection* insts = dest->outer()->insts(); + normalize_address(_owner, insts); + p = pack_1_int_to(p,scaled_offset(_owner, insts->start() )); + dest->set_locs_end((relocInfo*) p); +} + +void trampoline_stub_Relocation::unpack_data() { + address base = binding()->section_start(CodeBuffer::SECT_INSTS); + _owner = address_from_scaled_offset(unpack_1_int(), base); +} void external_word_Relocation::pack_data_to(CodeSection* dest) { short* p = (short*) dest->locs_end(); @@ -947,6 +959,25 @@ return NULL; } +// Finds the trampoline address for a call. If no trampoline stub is +// found NULL is returned which can be handled by the caller. +address trampoline_stub_Relocation::get_trampoline_for(address call, nmethod* code) { + // There are no relocations available when the code gets relocated + // because of CodeBuffer expansion. + if (code->relocation_size() == 0) + return NULL; + + RelocIterator iter(code, call); + while (iter.next()) { + if (iter.type() == relocInfo::trampoline_stub_type) { + if (iter.trampoline_stub_reloc()->owner() == call) { + return iter.addr(); + } + } + } + + return NULL; +} void static_stub_Relocation::clear_inline_cache() { // Call stub is only used when calling the interpreted code. @@ -1167,6 +1198,12 @@ tty->print(" | [static_call=" INTPTR_FORMAT "]", r->static_call()); break; } + case relocInfo::trampoline_stub_type: + { + trampoline_stub_Relocation* r = (trampoline_stub_Relocation*) reloc(); + tty->print(" | [trampoline owner=" INTPTR_FORMAT "]", r->owner()); + break; + } } tty->cr(); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/relocInfo.hpp --- a/src/share/vm/code/relocInfo.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/relocInfo.hpp Sun May 21 11:12:48 2017 -0700 @@ -267,8 +267,8 @@ poll_type = 10, // polling instruction for safepoints poll_return_type = 11, // polling instruction for safepoints at return breakpoint_type = 12, // an initialization barrier or safepoint - yet_unused_type = 13, // Still unused - yet_unused_type_2 = 14, // Still unused + trampoline_stub_type = 13, // stub-entry for trampoline + yet_unused_type_1 = 14, // Still unused data_prefix_tag = 15, // tag for a prefix (carries data arguments) type_mask = 15 // A mask which selects only the above values }; @@ -308,6 +308,7 @@ visitor(poll_return) \ visitor(breakpoint) \ visitor(section_word) \ + visitor(trampoline_stub) \ public: @@ -1112,6 +1113,43 @@ public: }; +// Trampoline Relocations. +// A trampoline allows to encode a small branch in the code, even if there +// is the chance that this branch can not reach all possible code locations. +// If the relocation finds that a branch is too far for the instruction +// in the code, it can patch it to jump to the trampoline where is +// sufficient space for a far branch. Needed on PPC. +class trampoline_stub_Relocation : public Relocation { + relocInfo::relocType type() { return relocInfo::trampoline_stub_type; } + + public: + static RelocationHolder spec(address static_call) { + RelocationHolder rh = newHolder(); + return (new (rh) trampoline_stub_Relocation(static_call)); + } + + private: + address _owner; // Address of the NativeCall that owns the trampoline. + + trampoline_stub_Relocation(address owner) { + _owner = owner; + } + + friend class RelocIterator; + trampoline_stub_Relocation() { } + + public: + + // Return the address of the NativeCall that owns the trampoline. + address owner() { return _owner; } + + void pack_data_to(CodeSection * dest); + void unpack_data(); + + // Find the trampoline stub for a call. + static address get_trampoline_for(address call, nmethod* code); +}; + class external_word_Relocation : public DataRelocation { relocInfo::relocType type() { return relocInfo::external_word_type; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/stubs.hpp --- a/src/share/vm/code/stubs.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/stubs.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/code/vmreg.hpp --- a/src/share/vm/code/vmreg.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/code/vmreg.hpp Sun May 21 11:12:48 2017 -0700 @@ -60,8 +60,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/adGlobals_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/adGlobals_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/adGlobals_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/adGlobals_ppc_64.hpp" #endif #endif @@ -73,6 +76,14 @@ // the current stack pointer. Warped numbers are required during compilation // when we do not yet know how big the frame will be. +#ifdef AIX +// BAD is defined in AIX system header sys/param.h. +// It conflicts with the BAD enum further down. +#ifdef BAD +#undef BAD +#endif +#endif + class VMRegImpl; typedef VMRegImpl* VMReg; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/compiler/disassembler.hpp --- a/src/share/vm/compiler/disassembler.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/compiler/disassembler.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp Sun May 21 11:12:48 2017 -0700 @@ -50,6 +50,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Sun May 21 11:12:48 2017 -0700 @@ -999,6 +999,13 @@ if (FreeChunk::indicatesFreeChunk(p)) { volatile FreeChunk* fc = (volatile FreeChunk*)p; size_t res = fc->size(); + + // Bugfix for systems with weak memory model (PPC64/IA64). The + // block's free bit was set and we have read the size of the + // block. Acquire and check the free bit again. If the block is + // still free, the read size is correct. + OrderAccess::acquire(); + // If the object is still a free chunk, return the size, else it // has been allocated so try again. if (FreeChunk::indicatesFreeChunk(p)) { @@ -1013,6 +1020,12 @@ oop o = (oop)p; assert(o->is_parsable(), "Should be parsable"); assert(o->is_oop(true /* ignore mark word */), "Should be an oop."); + + // Bugfix for systems with weak memory model (PPC64/IA64). + // The object o may be an array. Acquire to make sure that the array + // size (third word) is consistent. + OrderAccess::acquire(); + size_t res = o->size_given_klass(k->klass_part()); res = adjustObjectSize(res); assert(res != 0, "Block size should not be 0"); @@ -1042,6 +1055,13 @@ if (FreeChunk::indicatesFreeChunk(p)) { volatile FreeChunk* fc = (volatile FreeChunk*)p; size_t res = fc->size(); + + // Bugfix for systems with weak memory model (PPC64/IA64). The + // free bit of the block was set and we have read the size of + // the block. Acquire and check the free bit again. If the + // block is still free, the read size is correct. + OrderAccess::acquire(); + if (FreeChunk::indicatesFreeChunk(p)) { assert(res != 0, "Block size should not be 0"); assert(loops == 0, "Should be 0"); @@ -1057,6 +1077,12 @@ assert(k->is_oop(), "Should really be klass oop."); oop o = (oop)p; assert(o->is_oop(), "Should be an oop"); + + // Bugfix for systems with weak memory model (PPC64/IA64). + // The object o may be an array. Acquire to make sure that the array + // size (third word) is consistent. + OrderAccess::acquire(); + size_t res = o->size_given_klass(k->klass_part()); res = adjustObjectSize(res); assert(res != 0, "Block size should not be 0"); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Sun May 21 11:12:48 2017 -0700 @@ -39,6 +39,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/g1/g1AllocRegion.hpp --- a/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp Sun May 21 11:12:48 2017 -0700 @@ -55,7 +55,12 @@ // then _alloc_region is NULL and this object should not be used to // satisfy allocation requests (it was done this way to force the // correct use of init() and release()). - HeapRegion* _alloc_region; + // _alloc_region must be volatile. We saw the following problem: + // Thread A reads _alloc_region and compares against null. + // Thread B changes _alloc_region concurrently. Thread A + // reads _alloc_region again and calls a member function on + // it without another null check. + HeapRegion* volatile _alloc_region; // It keeps track of the distinct number of regions that are used // for allocation in the active interval of this object, i.e., diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp --- a/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -29,6 +29,7 @@ #include "gc_implementation/g1/g1CollectedHeap.hpp" #include "gc_implementation/g1/g1OopClosures.hpp" #include "gc_implementation/g1/g1RemSet.hpp" +#include "gc_implementation/g1/g1RemSet.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" /* diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp --- a/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -146,14 +149,20 @@ void G1SATBCardTableLoggingModRefBS::write_ref_field_work(void* field, - oop new_val) { + oop new_val, + bool release) { volatile jbyte* byte = byte_for(field); if (*byte == g1_young_gen) { return; } OrderAccess::storeload(); if (*byte != dirty_card) { - *byte = dirty_card; + // Perform a releasing store if requested. + if (release) { + OrderAccess::release_store((volatile jbyte*) byte, dirty_card); + } else { + *byte = dirty_card; + } Thread* thr = Thread::current(); if (thr->is_Java_thread()) { JavaThread* jt = (JavaThread*)thr; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp --- a/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp Sun May 21 11:12:48 2017 -0700 @@ -142,7 +142,7 @@ G1SATBCardTableModRefBS::is_a(bsn); } - void write_ref_field_work(void* field, oop new_val); + void write_ref_field_work(void* field, oop new_val, bool release = false); // Can be called from static contexts. static void write_ref_field_static(void* field, oop new_val); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/g1/ptrQueue.cpp --- a/src/share/vm/gc_implementation/g1/ptrQueue.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/g1/ptrQueue.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp Sun May 21 11:12:48 2017 -0700 @@ -39,6 +39,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -28,6 +28,7 @@ #include "gc_implementation/parallelScavenge/psOldGen.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.hpp" #include "gc_implementation/parallelScavenge/psScavenge.hpp" +#include "oops/oop.psgc.inline.hpp" inline PSPromotionManager* PSPromotionManager::manager_array(int index) { assert(_manager_array != NULL, "access of NULL manager_array"); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp --- a/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_interface/collectedHeap.cpp --- a/src/share/vm/gc_interface/collectedHeap.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_interface/collectedHeap.cpp Sun May 21 11:12:48 2017 -0700 @@ -45,6 +45,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/gc_interface/collectedHeap.inline.hpp --- a/src/share/vm/gc_interface/collectedHeap.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/gc_interface/collectedHeap.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -44,6 +44,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/abstractInterpreter.hpp --- a/src/share/vm/interpreter/abstractInterpreter.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/abstractInterpreter.hpp Sun May 21 11:12:48 2017 -0700 @@ -41,8 +41,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "interp_masm_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "interp_masm_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "interp_masm_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "interp_masm_ppc_64.hpp" #endif #ifdef TARGET_OS_FAMILY_linux # include "thread_linux.inline.hpp" @@ -53,6 +56,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/bytecodeInterpreter.cpp --- a/src/share/vm/interpreter/bytecodeInterpreter.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/bytecodeInterpreter.cpp Sun May 21 11:12:48 2017 -0700 @@ -28,13 +28,15 @@ #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeInterpreter.hpp" #include "interpreter/bytecodeInterpreter.inline.hpp" +#include "interpreter/bytecodeInterpreterProfiling.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" -#include "memory/cardTableModRefBS.hpp" #include "memory/resourceArea.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "runtime/biasedLocking.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.hpp" @@ -65,6 +67,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "orderAccess_linux_ppc.inline.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "orderAccess_aix_ppc.inline.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "orderAccess_bsd_x86.inline.hpp" #endif @@ -137,19 +142,20 @@ * is no entry point to do the transition to vm so we just * do it by hand here. */ -#define VM_JAVA_ERROR_NO_JUMP(name, msg) \ +#define VM_JAVA_ERROR_NO_JUMP(name, msg, note_a_trap) \ DECACHE_STATE(); \ SET_LAST_JAVA_FRAME(); \ { \ + InterpreterRuntime::note_a_trap(THREAD, istate->method(), BCI()); \ ThreadInVMfromJava trans(THREAD); \ Exceptions::_throw_msg(THREAD, __FILE__, __LINE__, name, msg); \ } \ RESET_LAST_JAVA_FRAME(); \ CACHE_STATE(); -// Normal throw of a java error -#define VM_JAVA_ERROR(name, msg) \ - VM_JAVA_ERROR_NO_JUMP(name, msg) \ +// Normal throw of a java error. +#define VM_JAVA_ERROR(name, msg, note_a_trap) \ + VM_JAVA_ERROR_NO_JUMP(name, msg, note_a_trap) \ goto handle_exception; #ifdef PRODUCT @@ -196,6 +202,10 @@ !THREAD->pop_frame_in_process()) { \ goto handle_Pop_Frame; \ } \ + if (THREAD->jvmti_thread_state() && \ + THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ + goto handle_Early_Return; \ + } \ opcode = *pc; \ } \ } \ @@ -328,12 +338,30 @@ if (UseLoopCounter) { \ bool do_OSR = UseOnStackReplacement; \ BACKEDGE_COUNT->increment(); \ - if (do_OSR) do_OSR = BACKEDGE_COUNT->reached_InvocationLimit(); \ + if (ProfileInterpreter) { \ + BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); \ + /* Check for overflow against MDO count. */ \ + do_OSR = do_OSR \ + && (mdo_last_branch_taken_count >= (uint)InvocationCounter::InterpreterBackwardBranchLimit)\ + /* When ProfileInterpreter is on, the backedge_count comes */ \ + /* from the methodDataOop, which value does not get reset on */ \ + /* the call to frequency_counter_overflow(). To avoid */ \ + /* excessive calls to the overflow routine while the method is */ \ + /* being compiled, add a second test to make sure the overflow */ \ + /* function is called only once every overflow_frequency. */ \ + && (!(mdo_last_branch_taken_count & 1023)); \ + } else { \ + /* check for overflow of backedge counter */ \ + do_OSR = do_OSR \ + && INVOCATION_COUNT->reached_BackwardBranchLimit(BACKEDGE_COUNT); \ + } \ if (do_OSR) { \ - nmethod* osr_nmethod; \ + nmethod* osr_nmethod; \ OSR_REQUEST(osr_nmethod, branch_pc); \ if (osr_nmethod != NULL && osr_nmethod->osr_entry_bci() != InvalidOSREntryBci) { \ - intptr_t* buf = SharedRuntime::OSR_migration_begin(THREAD); \ + intptr_t* buf; \ + /* Call OSR migration with last java frame only, no checks. */ \ + CALL_VM_NAKED_LJF(buf=SharedRuntime::OSR_migration_begin(THREAD)); \ istate->set_msg(do_osr); \ istate->set_osr_buf((address)buf); \ istate->set_osr_entry(osr_nmethod->osr_entry()); \ @@ -341,7 +369,6 @@ } \ } \ } /* UseCompiler ... */ \ - INCR_INVOCATION_COUNT; \ SAFEPOINT; \ } @@ -374,17 +401,21 @@ #undef CACHE_FRAME #define CACHE_FRAME() +// BCI() returns the current bytecode-index. +#undef BCI +#define BCI() ((int)(intptr_t)(pc - (intptr_t)istate->method()->code_base())) + /* * CHECK_NULL - Macro for throwing a NullPointerException if the object * passed is a null ref. * On some architectures/platforms it should be possible to do this implicitly */ #undef CHECK_NULL -#define CHECK_NULL(obj_) \ - if ((obj_) == NULL) { \ - VM_JAVA_ERROR(vmSymbols::java_lang_NullPointerException(), ""); \ - } \ - VERIFY_OOP(obj_) +#define CHECK_NULL(obj_) \ + if ((obj_) == NULL) { \ + VM_JAVA_ERROR(vmSymbols::java_lang_NullPointerException(), NULL, note_nullCheck_trap); \ + } \ + VERIFY_OOP(obj_) #define VMdoubleConstZero() 0.0 #define VMdoubleConstOne() 1.0 @@ -406,22 +437,30 @@ CACHE_CP(); \ CACHE_LOCALS(); -// Call the VM don't check for pending exceptions -#define CALL_VM_NOCHECK(func) \ - DECACHE_STATE(); \ - SET_LAST_JAVA_FRAME(); \ - func; \ - RESET_LAST_JAVA_FRAME(); \ - CACHE_STATE(); \ - if (THREAD->pop_frame_pending() && \ - !THREAD->pop_frame_in_process()) { \ - goto handle_Pop_Frame; \ - } +// Call the VM with last java frame only. +#define CALL_VM_NAKED_LJF(func) \ + DECACHE_STATE(); \ + SET_LAST_JAVA_FRAME(); \ + func; \ + RESET_LAST_JAVA_FRAME(); \ + CACHE_STATE(); + +// Call the VM. Don't check for pending exceptions. +#define CALL_VM_NOCHECK(func) \ + CALL_VM_NAKED_LJF(func) \ + if (THREAD->pop_frame_pending() && \ + !THREAD->pop_frame_in_process()) { \ + goto handle_Pop_Frame; \ + } \ + if (THREAD->jvmti_thread_state() && \ + THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ + goto handle_Early_Return; \ + } // Call the VM and check for pending exceptions -#define CALL_VM(func, label) { \ - CALL_VM_NOCHECK(func); \ - if (THREAD->has_pending_exception()) goto label; \ +#define CALL_VM(func, label) { \ + CALL_VM_NOCHECK(func); \ + if (THREAD->has_pending_exception()) goto label; \ } /* @@ -464,7 +503,26 @@ #ifdef ASSERT if (istate->_msg != initialize) { - assert(abs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit"); + // We have a problem here if we are running with a pre-hsx24 JDK (for example during bootstrap) + // because in that case, EnableInvokeDynamic is true by default but will be later switched off + // if java_lang_invoke_MethodHandle::compute_offsets() detects that the JDK only has the classes + // for the old JSR292 implementation. + // This leads to a situation where 'istate->_stack_limit' always accounts for + // methodOopDesc::extra_stack_entries() because it is computed in + // CppInterpreterGenerator::generate_compute_interpreter_state() which was generated while + // EnableInvokeDynamic was still true. On the other hand, istate->_method->max_stack() doesn't + // account for extra_stack_entries() anymore because at the time when it is called + // EnableInvokeDynamic was already set to false. + // So we have a second version of the assertion which handles the case where EnableInvokeDynamic was + // switched off because of the wrong classes. + if (EnableInvokeDynamic || FLAG_IS_CMDLINE(EnableInvokeDynamic)) { + assert(labs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit"); + } + else { + const int extra_stack_entries = 2; // MUST match the value in methodOopDesc::extra_stack_entries() + assert(labs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + extra_stack_entries + + 1), "bad stack limit"); + } #ifndef SHARK IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong")); #endif // !SHARK @@ -480,8 +538,6 @@ interpreterState orig = istate; #endif - static volatile jbyte* _byte_map_base; // adjusted card table base for oop store barrier - register intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ register address pc = istate->bcp(); register jubyte opcode; @@ -489,12 +545,9 @@ register constantPoolCacheOop cp = istate->constants(); // method()->constants()->cache() #ifdef LOTS_OF_REGS register JavaThread* THREAD = istate->thread(); - register volatile jbyte* BYTE_MAP_BASE = _byte_map_base; #else #undef THREAD #define THREAD istate->thread() -#undef BYTE_MAP_BASE -#define BYTE_MAP_BASE _byte_map_base #endif #ifdef USELABELS @@ -601,16 +654,20 @@ topOfStack < istate->stack_base(), "Stack top out of range"); +#ifdef CC_INTERP_PROFILE + // MethodData's last branch taken count. + uint mdo_last_branch_taken_count = 0; +#else + const uint mdo_last_branch_taken_count = 0; +#endif + switch (istate->msg()) { case initialize: { - if (initialized++) ShouldNotReachHere(); // Only one initialize call + if (initialized++) ShouldNotReachHere(); // Only one initialize call. _compiling = (UseCompiler || CountCompiledCalls); #ifdef VM_JVMTI _jvmti_interp_events = JvmtiExport::can_post_interpreter_events(); #endif - BarrierSet* bs = Universe::heap()->barrier_set(); - assert(bs->kind() == BarrierSet::CardTableModRef, "Wrong barrier set kind"); - _byte_map_base = (volatile jbyte*)(((CardTableModRefBS*)bs)->byte_map_base); return; } break; @@ -623,15 +680,12 @@ METHOD->increment_interpreter_invocation_count(); } INCR_INVOCATION_COUNT; - if (INVOCATION_COUNT->reached_InvocationLimit()) { - CALL_VM((void)InterpreterRuntime::frequency_counter_overflow(THREAD, NULL), handle_exception); - - // We no longer retry on a counter overflow - - // istate->set_msg(retry_method); - // THREAD->clr_do_not_unlock(); - // return; + if (INVOCATION_COUNT->reached_InvocationLimit(BACKEDGE_COUNT)) { + CALL_VM((void)InterpreterRuntime::frequency_counter_overflow(THREAD, NULL), handle_exception); + // We no longer retry on a counter overflow. } + // Get or create profile data. Check for pending (async) exceptions. + BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); SAFEPOINT; } @@ -653,117 +707,99 @@ } #endif // HACK - - // lock method if synchronized + // Lock method if synchronized. if (METHOD->is_synchronized()) { - // oop rcvr = locals[0].j.r; - oop rcvr; - if (METHOD->is_static()) { - rcvr = METHOD->constants()->pool_holder()->java_mirror(); - } else { - rcvr = LOCALS_OBJECT(0); - VERIFY_OOP(rcvr); - } - // The initial monitor is ours for the taking - BasicObjectLock* mon = &istate->monitor_base()[-1]; - oop monobj = mon->obj(); - assert(mon->obj() == rcvr, "method monitor mis-initialized"); - - bool success = UseBiasedLocking; - if (UseBiasedLocking) { - markOop mark = rcvr->mark(); - if (mark->has_bias_pattern()) { - // The bias pattern is present in the object's header. Need to check - // whether the bias owner and the epoch are both still current. - intptr_t xx = ((intptr_t) THREAD) ^ (intptr_t) mark; - xx = (intptr_t) rcvr->klass()->klass_part()->prototype_header() ^ xx; - intptr_t yy = (xx & ~((int) markOopDesc::age_mask_in_place)); - if (yy != 0 ) { - // At this point we know that the header has the bias pattern and - // that we are not the bias owner in the current epoch. We need to - // figure out more details about the state of the header in order to - // know what operations can be legally performed on the object's - // header. - - // If the low three bits in the xor result aren't clear, that means - // the prototype header is no longer biased and we have to revoke - // the bias on this object. - - if (yy & markOopDesc::biased_lock_mask_in_place == 0 ) { - // Biasing is still enabled for this data type. See whether the - // epoch of the current bias is still valid, meaning that the epoch - // bits of the mark word are equal to the epoch bits of the - // prototype header. (Note that the prototype header's epoch bits - // only change at a safepoint.) If not, attempt to rebias the object - // toward the current thread. Note that we must be absolutely sure - // that the current epoch is invalid in order to do this because - // otherwise the manipulations it performs on the mark word are - // illegal. - if (yy & markOopDesc::epoch_mask_in_place == 0) { - // The epoch of the current bias is still valid but we know nothing - // about the owner; it might be set or it might be clear. Try to - // acquire the bias of the object using an atomic operation. If this - // fails we will go in to the runtime to revoke the object's bias. - // Note that we first construct the presumed unbiased header so we - // don't accidentally blow away another thread's valid bias. - intptr_t unbiased = (intptr_t) mark & (markOopDesc::biased_lock_mask_in_place | - markOopDesc::age_mask_in_place | - markOopDesc::epoch_mask_in_place); - if (Atomic::cmpxchg_ptr((intptr_t)THREAD | unbiased, (intptr_t*) rcvr->mark_addr(), unbiased) != unbiased) { - CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); - } - } else { - try_rebias: - // At this point we know the epoch has expired, meaning that the - // current "bias owner", if any, is actually invalid. Under these - // circumstances _only_, we are allowed to use the current header's - // value as the comparison value when doing the cas to acquire the - // bias in the current epoch. In other words, we allow transfer of - // the bias from one thread to another directly in this situation. - xx = (intptr_t) rcvr->klass()->klass_part()->prototype_header() | (intptr_t) THREAD; - if (Atomic::cmpxchg_ptr((intptr_t)THREAD | (intptr_t) rcvr->klass()->klass_part()->prototype_header(), - (intptr_t*) rcvr->mark_addr(), - (intptr_t) mark) != (intptr_t) mark) { - CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); - } - } - } else { - try_revoke_bias: - // The prototype mark in the klass doesn't have the bias bit set any - // more, indicating that objects of this data type are not supposed - // to be biased any more. We are going to try to reset the mark of - // this object to the prototype value and fall through to the - // CAS-based locking scheme. Note that if our CAS fails, it means - // that another thread raced us for the privilege of revoking the - // bias of this particular object, so it's okay to continue in the - // normal locking code. - // - xx = (intptr_t) rcvr->klass()->klass_part()->prototype_header() | (intptr_t) THREAD; - if (Atomic::cmpxchg_ptr(rcvr->klass()->klass_part()->prototype_header(), - (intptr_t*) rcvr->mark_addr(), - mark) == mark) { - // (*counters->revoked_lock_entry_count_addr())++; - success = false; - } - } + // oop rcvr = locals[0].j.r; + oop rcvr; + if (METHOD->is_static()) { + rcvr = METHOD->constants()->pool_holder()->java_mirror(); + } else { + rcvr = LOCALS_OBJECT(0); + VERIFY_OOP(rcvr); + } + // The initial monitor is ours for the taking. + // Monitor not filled in frame manager any longer as this caused race condition with biased locking. + BasicObjectLock* mon = &istate->monitor_base()[-1]; + mon->set_obj(rcvr); + bool success = false; + uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place; + markOop mark = rcvr->mark(); + intptr_t hash = (intptr_t) markOopDesc::no_hash; + // Implies UseBiasedLocking. + if (mark->has_bias_pattern()) { + uintptr_t thread_ident; + uintptr_t anticipated_bias_locking_value; + thread_ident = (uintptr_t)istate->thread(); + anticipated_bias_locking_value = + (((uintptr_t)rcvr->klass()->klass_part()->prototype_header() | thread_ident) ^ (uintptr_t)mark) & + ~((uintptr_t) markOopDesc::age_mask_in_place); + + if (anticipated_bias_locking_value == 0) { + // Already biased towards this thread, nothing to do. + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::biased_lock_entry_count_addr())++; + } + success = true; + } else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) { + // Try to revoke bias. + markOop header = rcvr->klass()->klass_part()->prototype_header(); + if (hash != markOopDesc::no_hash) { + header = header->copy_set_hash(hash); + } + if (Atomic::cmpxchg_ptr(header, rcvr->mark_addr(), mark) == mark) { + if (PrintBiasedLockingStatistics) + (*BiasedLocking::revoked_lock_entry_count_addr())++; + } + } else if ((anticipated_bias_locking_value & epoch_mask_in_place) != 0) { + // Try to rebias. + markOop new_header = (markOop) ( (intptr_t) rcvr->klass()->klass_part()->prototype_header() | thread_ident); + if (hash != markOopDesc::no_hash) { + new_header = new_header->copy_set_hash(hash); + } + if (Atomic::cmpxchg_ptr((void*)new_header, rcvr->mark_addr(), mark) == mark) { + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::rebiased_lock_entry_count_addr())++; } } else { - cas_label: - success = false; + CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); + } + success = true; + } else { + // Try to bias towards thread in case object is anonymously biased. + markOop header = (markOop) ((uintptr_t) mark & + ((uintptr_t)markOopDesc::biased_lock_mask_in_place | + (uintptr_t)markOopDesc::age_mask_in_place | epoch_mask_in_place)); + if (hash != markOopDesc::no_hash) { + header = header->copy_set_hash(hash); + } + markOop new_header = (markOop) ((uintptr_t) header | thread_ident); + // Debugging hint. + DEBUG_ONLY(mon->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);) + if (Atomic::cmpxchg_ptr((void*)new_header, rcvr->mark_addr(), header) == header) { + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++; + } + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); + } + success = true; + } + } + + // Traditional lightweight locking. + if (!success) { + markOop displaced = rcvr->mark()->set_unlocked(); + mon->lock()->set_displaced_header(displaced); + bool call_vm = UseHeavyMonitors; + if (call_vm || Atomic::cmpxchg_ptr(mon, rcvr->mark_addr(), displaced) != displaced) { + // Is it simple recursive case? + if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { + mon->lock()->set_displaced_header(NULL); + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); } } - if (!success) { - markOop displaced = rcvr->mark()->set_unlocked(); - mon->lock()->set_displaced_header(displaced); - if (Atomic::cmpxchg_ptr(mon, rcvr->mark_addr(), displaced) != displaced) { - // Is it simple recursive case? - if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { - mon->lock()->set_displaced_header(NULL); - } else { - CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); - } - } - } + } } THREAD->clr_do_not_unlock(); @@ -785,9 +821,14 @@ case popping_frame: { // returned from a java call to pop the frame, restart the call // clear the message so we don't confuse ourselves later - ShouldNotReachHere(); // we don't return this. assert(THREAD->pop_frame_in_process(), "wrong frame pop state"); istate->set_msg(no_request); + if (_compiling) { + // Set MDX back to the ProfileData of the invoke bytecode that will be + // restarted. + SET_MDX(NULL); + BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); + } THREAD->clr_pop_frame_in_process(); goto run; } @@ -813,10 +854,19 @@ if (THREAD->pop_frame_pending() && !THREAD->pop_frame_in_process()) { goto handle_Pop_Frame; } + if (THREAD->jvmti_thread_state() && + THREAD->jvmti_thread_state()->is_earlyret_pending()) { + goto handle_Early_Return; + } if (THREAD->has_pending_exception()) goto handle_exception; // Update the pc by the saved amount of the invoke bytecode size UPDATE_PC(istate->bcp_advance()); + + if (_compiling) { + // Get or create profile data. Check for pending (async) exceptions. + BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); + } goto run; } @@ -824,6 +874,11 @@ // Returned from an opcode that will reexecute. Deopt was // a result of a PopFrame request. // + + if (_compiling) { + // Get or create profile data. Check for pending (async) exceptions. + BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); + } goto run; } @@ -846,6 +901,11 @@ } UPDATE_PC(Bytecodes::length_at(METHOD, pc)); if (THREAD->has_pending_exception()) goto handle_exception; + + if (_compiling) { + // Get or create profile data. Check for pending (async) exceptions. + BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); + } goto run; } case got_monitors: { @@ -858,15 +918,85 @@ BasicObjectLock* entry = (BasicObjectLock*) istate->stack_base(); assert(entry->obj() == NULL, "Frame manager didn't allocate the monitor"); entry->set_obj(lockee); - - markOop displaced = lockee->mark()->set_unlocked(); - entry->lock()->set_displaced_header(displaced); - if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { - // Is it simple recursive case? - if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { - entry->lock()->set_displaced_header(NULL); + bool success = false; + uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place; + + markOop mark = lockee->mark(); + intptr_t hash = (intptr_t) markOopDesc::no_hash; + // implies UseBiasedLocking + if (mark->has_bias_pattern()) { + uintptr_t thread_ident; + uintptr_t anticipated_bias_locking_value; + thread_ident = (uintptr_t)istate->thread(); + anticipated_bias_locking_value = + (((uintptr_t)lockee->klass()->klass_part()->prototype_header() | thread_ident) ^ (uintptr_t)mark) & + ~((uintptr_t) markOopDesc::age_mask_in_place); + + if (anticipated_bias_locking_value == 0) { + // already biased towards this thread, nothing to do + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::biased_lock_entry_count_addr())++; + } + success = true; + } + else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) { + // try revoke bias + markOop header = lockee->klass()->klass_part()->prototype_header(); + if (hash != markOopDesc::no_hash) { + header = header->copy_set_hash(hash); + } + if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) { + if (PrintBiasedLockingStatistics) { + (*BiasedLocking::revoked_lock_entry_count_addr())++; + } + } + } else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) { + // try rebias + markOop new_header = (markOop) ( (intptr_t) lockee->klass()->klass_part()->prototype_header() | thread_ident); + if (hash != markOopDesc::no_hash) { + new_header = new_header->copy_set_hash(hash); + } + if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) { + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::rebiased_lock_entry_count_addr())++; + } + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } + success = true; } else { - CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + // try to bias towards thread in case object is anonymously biased + markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place | + (uintptr_t)markOopDesc::age_mask_in_place | epoch_mask_in_place)); + if (hash != markOopDesc::no_hash) { + header = header->copy_set_hash(hash); + } + markOop new_header = (markOop) ((uintptr_t) header | thread_ident); + // debugging hint + DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);) + if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) { + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++; + } + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } + success = true; + } + } + + // traditional lightweight locking + if (!success) { + markOop displaced = lockee->mark()->set_unlocked(); + entry->lock()->set_displaced_header(displaced); + bool call_vm = UseHeavyMonitors; + if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { + // Is it simple recursive case? + if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { + entry->lock()->set_displaced_header(NULL); + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } } } UPDATE_PC_AND_TOS(1, -1); @@ -1027,6 +1157,11 @@ uint16_t reg = Bytes::get_Java_u2(pc + 2); opcode = pc[1]; + + // Wide and it's sub-bytecode are counted as separate instructions. If we + // don't account for this here, the bytecode trace skips the next bytecode. + DO_UPDATE_INSTRUCTION_COUNT(opcode); + switch(opcode) { case Bytecodes::_aload: VERIFY_OOP(LOCALS_OBJECT(reg)); @@ -1070,10 +1205,13 @@ UPDATE_PC_AND_CONTINUE(6); } case Bytecodes::_ret: + // Profile ret. + BI_PROFILE_UPDATE_RET(/*bci=*/((int)(intptr_t)(LOCALS_ADDR(reg)))); + // Now, update the pc. pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(reg)); UPDATE_PC_AND_CONTINUE(0); default: - VM_JAVA_ERROR(vmSymbols::java_lang_InternalError(), "undefined opcode"); + VM_JAVA_ERROR(vmSymbols::java_lang_InternalError(), "undefined opcode", note_no_trap); } } @@ -1154,7 +1292,7 @@ CASE(_i##opcname): \ if (test && (STACK_INT(-1) == 0)) { \ VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \ - "/ by zero"); \ + "/ by zero", note_div0Check_trap); \ } \ SET_STACK_INT(VMint##opname(STACK_INT(-2), \ STACK_INT(-1)), \ @@ -1166,7 +1304,7 @@ jlong l1 = STACK_LONG(-1); \ if (VMlongEqz(l1)) { \ VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \ - "/ by long zero"); \ + "/ by long zero", note_div0Check_trap); \ } \ } \ /* First long at (-1,-2) next long at (-3,-4) */ \ @@ -1379,17 +1517,23 @@ #define COMPARISON_OP(name, comparison) \ CASE(_if_icmp##name): { \ - int skip = (STACK_INT(-2) comparison STACK_INT(-1)) \ + const bool cmp = (STACK_INT(-2) comparison STACK_INT(-1)); \ + int skip = cmp \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ + /* Profile branch. */ \ + BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -2); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ } \ CASE(_if##name): { \ - int skip = (STACK_INT(-1) comparison 0) \ + const bool cmp = (STACK_INT(-1) comparison 0); \ + int skip = cmp \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ + /* Profile branch. */ \ + BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -1); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1398,9 +1542,12 @@ #define COMPARISON_OP2(name, comparison) \ COMPARISON_OP(name, comparison) \ CASE(_if_acmp##name): { \ - int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1)) \ + const bool cmp = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1)); \ + int skip = cmp \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ + /* Profile branch. */ \ + BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -2); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1408,9 +1555,12 @@ #define NULL_COMPARISON_NOT_OP(name) \ CASE(_if##name): { \ - int skip = (!(STACK_OBJECT(-1) == NULL)) \ + const bool cmp = (!(STACK_OBJECT(-1) == NULL)); \ + int skip = cmp \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ + /* Profile branch. */ \ + BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -1); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1418,9 +1568,12 @@ #define NULL_COMPARISON_OP(name) \ CASE(_if##name): { \ - int skip = ((STACK_OBJECT(-1) == NULL)) \ + const bool cmp = ((STACK_OBJECT(-1) == NULL)); \ + int skip = cmp \ ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ address branch_pc = pc; \ + /* Profile branch. */ \ + BI_PROFILE_UPDATE_BRANCH(/*is_taken=*/cmp); \ UPDATE_PC_AND_TOS(skip, -1); \ DO_BACKEDGE_CHECKS(skip, branch_pc); \ CONTINUE; \ @@ -1443,30 +1596,42 @@ int32_t high = Bytes::get_Java_u4((address)&lpc[2]); int32_t skip; key -= low; - skip = ((uint32_t) key > (uint32_t)(high - low)) - ? Bytes::get_Java_u4((address)&lpc[0]) - : Bytes::get_Java_u4((address)&lpc[key + 3]); - // Does this really need a full backedge check (osr?) + if (((uint32_t) key > (uint32_t)(high - low))) { + key = -1; + skip = Bytes::get_Java_u4((address)&lpc[0]); + } else { + skip = Bytes::get_Java_u4((address)&lpc[key + 3]); + } + // Profile switch. + BI_PROFILE_UPDATE_SWITCH(/*switch_index=*/key); + // Does this really need a full backedge check (osr)? address branch_pc = pc; UPDATE_PC_AND_TOS(skip, -1); DO_BACKEDGE_CHECKS(skip, branch_pc); CONTINUE; } - /* Goto pc whose table entry matches specified key */ + /* Goto pc whose table entry matches specified key. */ CASE(_lookupswitch): { jint* lpc = (jint*)VMalignWordUp(pc+1); int32_t key = STACK_INT(-1); int32_t skip = Bytes::get_Java_u4((address) lpc); /* default amount */ + // Remember index. + int index = -1; + int newindex = 0; int32_t npairs = Bytes::get_Java_u4((address) &lpc[1]); while (--npairs >= 0) { - lpc += 2; - if (key == (int32_t)Bytes::get_Java_u4((address)lpc)) { - skip = Bytes::get_Java_u4((address)&lpc[1]); - break; - } + lpc += 2; + if (key == (int32_t)Bytes::get_Java_u4((address)lpc)) { + skip = Bytes::get_Java_u4((address)&lpc[1]); + index = newindex; + break; + } + newindex += 1; } + // Profile switch. + BI_PROFILE_UPDATE_SWITCH(/*switch_index=*/index); address branch_pc = pc; UPDATE_PC_AND_TOS(skip, -1); DO_BACKEDGE_CHECKS(skip, branch_pc); @@ -1551,7 +1716,7 @@ if ((uint32_t)index >= (uint32_t)arrObj->length()) { \ sprintf(message, "%d", index); \ VM_JAVA_ERROR(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), \ - message); \ + message, note_rangeCheck_trap); \ } /* 32-bit loads. These handle conversion from < 32-bit types */ @@ -1570,15 +1735,18 @@ ARRAY_INTRO(-2); \ SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), -1); \ extra; \ - UPDATE_PC_AND_CONTINUE(1); \ + UPDATE_PC_AND_CONTINUE(1); \ } CASE(_iaload): ARRAY_LOADTO32(T_INT, jint, "%d", STACK_INT, 0); CASE(_faload): ARRAY_LOADTO32(T_FLOAT, jfloat, "%f", STACK_FLOAT, 0); - CASE(_aaload): - ARRAY_LOADTO32(T_OBJECT, oop, INTPTR_FORMAT, STACK_OBJECT, 0); + CASE(_aaload): { + ARRAY_INTRO(-2); + SET_STACK_OBJECT(((objArrayOop) arrObj)->obj_at(index), -2); + UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); + } CASE(_baload): ARRAY_LOADTO32(T_BYTE, jbyte, "%d", STACK_INT, 0); CASE(_caload): @@ -1630,14 +1798,17 @@ // Seems way more expensive now that we must dispatch // if (rhsKlassOop != elemKlassOop && !rhsKlassOop->klass_part()->is_subtype_of(elemKlassOop)) { // ebx->is... - VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), ""); + // Decrement counter if subtype check failed. + BI_PROFILE_SUBTYPECHECK_FAILED(rhsKlassOop); + VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), "", note_arrayCheck_trap); } + // Profile checkcast with null_seen and receiver. + BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false, rhsKlassOop); + } else { + // Profile checkcast with null_seen and receiver. + BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/true, NULL); } - oop* elem_loc = (oop*)(((address) arrObj->base(T_OBJECT)) + index * sizeof(oop)); - // *(oop*)(((address) arrObj->base(T_OBJECT)) + index * sizeof(oop)) = rhsObject; - *elem_loc = rhsObject; - // Mark the card - OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)elem_loc >> CardTableModRefBS::card_shift], 0); + ((objArrayOopDesc *) arrObj)->obj_at_put(index, rhsObject); UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); } CASE(_bastore): { @@ -1689,14 +1860,87 @@ } if (entry != NULL) { entry->set_obj(lockee); - markOop displaced = lockee->mark()->set_unlocked(); - entry->lock()->set_displaced_header(displaced); - if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { - // Is it simple recursive case? - if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { - entry->lock()->set_displaced_header(NULL); - } else { - CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + int success = false; + uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place; + + markOop mark = lockee->mark(); + intptr_t hash = (intptr_t) markOopDesc::no_hash; + // implies UseBiasedLocking + if (mark->has_bias_pattern()) { + uintptr_t thread_ident; + uintptr_t anticipated_bias_locking_value; + thread_ident = (uintptr_t)istate->thread(); + anticipated_bias_locking_value = + (((uintptr_t)lockee->klass()->klass_part()->prototype_header() | thread_ident) ^ (uintptr_t)mark) & + ~((uintptr_t) markOopDesc::age_mask_in_place); + + if (anticipated_bias_locking_value == 0) { + // already biased towards this thread, nothing to do + if (PrintBiasedLockingStatistics) { + (* BiasedLocking::biased_lock_entry_count_addr())++; + } + success = true; + } + else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) { + // try revoke bias + markOop header = lockee->klass()->klass_part()->prototype_header(); + if (hash != markOopDesc::no_hash) { + header = header->copy_set_hash(hash); + } + if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) { + if (PrintBiasedLockingStatistics) + (*BiasedLocking::revoked_lock_entry_count_addr())++; + } + } + else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) { + // try rebias + markOop new_header = (markOop) ( (intptr_t) lockee->klass()->klass_part()->prototype_header() | thread_ident); + if (hash != markOopDesc::no_hash) { + new_header = new_header->copy_set_hash(hash); + } + if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) { + if (PrintBiasedLockingStatistics) + (* BiasedLocking::rebiased_lock_entry_count_addr())++; + } + else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } + success = true; + } + else { + // try to bias towards thread in case object is anonymously biased + markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place | + (uintptr_t)markOopDesc::age_mask_in_place | + epoch_mask_in_place)); + if (hash != markOopDesc::no_hash) { + header = header->copy_set_hash(hash); + } + markOop new_header = (markOop) ((uintptr_t) header | thread_ident); + // debugging hint + DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);) + if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) { + if (PrintBiasedLockingStatistics) + (* BiasedLocking::anonymously_biased_lock_entry_count_addr())++; + } + else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } + success = true; + } + } + + // traditional lightweight locking + if (!success) { + markOop displaced = lockee->mark()->set_unlocked(); + entry->lock()->set_displaced_header(displaced); + bool call_vm = UseHeavyMonitors; + if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) { + // Is it simple recursive case? + if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { + entry->lock()->set_displaced_header(NULL); + } else { + CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); + } } } UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); @@ -1718,12 +1962,15 @@ BasicLock* lock = most_recent->lock(); markOop header = lock->displaced_header(); most_recent->set_obj(NULL); - // If it isn't recursive we either must swap old header or call the runtime - if (header != NULL) { - if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { - // restore object for the slow case - most_recent->set_obj(lockee); - CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception); + if (!lockee->mark()->has_bias_pattern()) { + bool call_vm = UseHeavyMonitors; + // If it isn't recursive we either must swap old header or call the runtime + if (header != NULL || call_vm) { + if (call_vm || Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { + // restore object for the slow case + most_recent->set_obj(lockee); + CALL_VM(InterpreterRuntime::monitorexit(THREAD, most_recent), handle_exception); + } } } UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); @@ -1795,6 +2042,9 @@ TosState tos_type = cache->flag_state(); int field_offset = cache->f2_as_index(); if (cache->is_volatile()) { + // Support ordering of "Independent Reads of Independent Writes" (see Parse::do_get_xxx). + // Solution: implement volatile read as fence-load-acquire. + PPC64_ONLY(OrderAccess::fence();) if (tos_type == atos) { VERIFY_OOP(obj->obj_field_acquire(field_offset)); SET_STACK_OBJECT(obj->obj_field_acquire(field_offset), -1); @@ -1910,7 +2160,6 @@ } else if (tos_type == atos) { VERIFY_OOP(STACK_OBJECT(-1)); obj->release_obj_field_put(field_offset, STACK_OBJECT(-1)); - OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0); } else if (tos_type == btos) { obj->release_byte_field_put(field_offset, STACK_INT(-1)); } else if (tos_type == ztos) { @@ -1933,8 +2182,12 @@ obj->int_field_put(field_offset, STACK_INT(-1)); } else if (tos_type == atos) { VERIFY_OOP(STACK_OBJECT(-1)); - obj->obj_field_put(field_offset, STACK_OBJECT(-1)); - OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0); + // On IA64 we perform all stores of references with release semantics. + // This guarantees that everybody using this reference sees a fully + // initialized object. On PPC64 we emit a storestore barrier after + // initialization and upon method return. + IA64_ONLY(obj->release_obj_field_put(field_offset, STACK_OBJECT(-1))); + NOT_IA64( obj->obj_field_put (field_offset, STACK_OBJECT(-1))); } else if (tos_type == btos) { obj->byte_field_put(field_offset, STACK_INT(-1)); } else if (tos_type == ztos) { @@ -1974,10 +2227,14 @@ if (UseTLAB) { result = (oop) THREAD->tlab().allocate(obj_size); } + // Disable non-TLAB-based fast-path, because profiling requires that all + // allocations go through InterpreterRuntime::_new() if THREAD->tlab().allocate + // returns NULL. +#ifndef CC_INTERP_PROFILE if (result == NULL) { need_zero = true; // Try allocate in shared eden - retry: + retry: HeapWord* compare_to = *Universe::heap()->top_addr(); HeapWord* new_top = compare_to + obj_size; if (new_top <= *Universe::heap()->end_addr()) { @@ -1987,6 +2244,7 @@ result = (oop) compare_to; } } +#endif if (result != NULL) { // Initialize object (if nonzero size and need) and then the header if (need_zero ) { @@ -2003,6 +2261,9 @@ } result->set_klass_gap(0); result->set_klass(k_entry); + // Must prevent reordering of stores for object initialization + // with stores that publish the new object. + OrderAccess::storestore(); SET_STACK_OBJECT(result, 0); UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); } @@ -2011,6 +2272,9 @@ // Slow case allocation CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index), handle_exception); + // Must prevent reordering of stores for object initialization + // with stores that publish the new object. + OrderAccess::storestore(); SET_STACK_OBJECT(THREAD->vm_result(), 0); THREAD->set_vm_result(NULL); UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); @@ -2020,6 +2284,9 @@ jint size = STACK_INT(-1); CALL_VM(InterpreterRuntime::anewarray(THREAD, METHOD->constants(), index, size), handle_exception); + // Must prevent reordering of stores for object initialization + // with stores that publish the new object. + OrderAccess::storestore(); SET_STACK_OBJECT(THREAD->vm_result(), -1); THREAD->set_vm_result(NULL); UPDATE_PC_AND_CONTINUE(3); @@ -2034,6 +2301,9 @@ //adjust pointer to start of stack element CALL_VM(InterpreterRuntime::multianewarray(THREAD, dimarray), handle_exception); + // Must prevent reordering of stores for object initialization + // with stores that publish the new object. + OrderAccess::storestore(); SET_STACK_OBJECT(THREAD->vm_result(), -dims); THREAD->set_vm_result(NULL); UPDATE_PC_AND_TOS_AND_CONTINUE(4, -(dims-1)); @@ -2042,12 +2312,8 @@ if (STACK_OBJECT(-1) != NULL) { VERIFY_OOP(STACK_OBJECT(-1)); u2 index = Bytes::get_Java_u2(pc+1); - if (ProfileInterpreter) { - // needs Profile_checkcast QQQ - ShouldNotReachHere(); - } // Constant pool may have actual klass or unresolved klass. If it is - // unresolved we must resolve it + // unresolved we must resolve it. if (METHOD->constants()->tag_at(index).is_unresolved_klass()) { CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception); } @@ -2055,34 +2321,36 @@ klassOop objKlassOop = STACK_OBJECT(-1)->klass(); //ebx // // Check for compatibilty. This check must not GC!! - // Seems way more expensive now that we must dispatch + // Seems way more expensive now that we must dispatch. // - if (objKlassOop != klassOf && - !objKlassOop->klass_part()->is_subtype_of(klassOf)) { + if (objKlassOop != klassOf && !objKlassOop->klass_part()->is_subtype_of(klassOf)) { + // Decrement counter at checkcast. + BI_PROFILE_SUBTYPECHECK_FAILED(objKlassOop); ResourceMark rm(THREAD); const char* objName = Klass::cast(objKlassOop)->external_name(); const char* klassName = Klass::cast(klassOf)->external_name(); char* message = SharedRuntime::generate_class_cast_message( objName, klassName); - VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message); + VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message, note_classCheck_trap); } + // Profile checkcast with null_seen and receiver. + BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false, objKlassOop); } else { - if (UncommonNullCast) { -// istate->method()->set_null_cast_seen(); -// [RGV] Not sure what to do here! - - } + // Profile checkcast with null_seen and receiver. + BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/true, NULL); } UPDATE_PC_AND_CONTINUE(3); CASE(_instanceof): if (STACK_OBJECT(-1) == NULL) { SET_STACK_INT(0, -1); + // Profile instanceof with null_seen and receiver. + BI_PROFILE_UPDATE_INSTANCEOF(/*null_seen=*/true, NULL); } else { VERIFY_OOP(STACK_OBJECT(-1)); u2 index = Bytes::get_Java_u2(pc+1); // Constant pool may have actual klass or unresolved klass. If it is - // unresolved we must resolve it + // unresolved we must resolve it. if (METHOD->constants()->tag_at(index).is_unresolved_klass()) { CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception); } @@ -2090,13 +2358,17 @@ klassOop objKlassOop = STACK_OBJECT(-1)->klass(); // // Check for compatibilty. This check must not GC!! - // Seems way more expensive now that we must dispatch + // Seems way more expensive now that we must dispatch. // if ( objKlassOop == klassOf || objKlassOop->klass_part()->is_subtype_of(klassOf)) { SET_STACK_INT(1, -1); } else { SET_STACK_INT(0, -1); + // Decrement counter at checkcast. + BI_PROFILE_SUBTYPECHECK_FAILED(objKlassOop); } + // Profile instanceof with null_seen and receiver. + BI_PROFILE_UPDATE_INSTANCEOF(/*null_seen=*/false, objKlassOop); } UPDATE_PC_AND_CONTINUE(3); @@ -2242,8 +2514,12 @@ istate->set_callee_entry_point(method->from_interpreted_entry()); istate->set_bcp_advance(5); + // Invokedynamic has got a call counter, just like an invokestatic -> increment! + BI_PROFILE_UPDATE_CALL(); + UPDATE_PC_AND_RETURN(0); // I'll be back... } + CASE(_invokehandle): { if (!EnableInvokeDynamic) { @@ -2256,12 +2532,10 @@ if (! cache->is_resolved((Bytecodes::Code) opcode)) { CALL_VM(InterpreterRuntime::resolve_invokehandle(THREAD), handle_exception); - // GC might move cache while returning from VM call. - cache = cp->entry_at(index); // reload + cache = cp->entry_at(index); } methodOop method = cache->f2_as_vfinal_method(); - VERIFY_OOP(method); if (cache->has_appendix()) { @@ -2275,6 +2549,9 @@ istate->set_callee_entry_point(method->from_interpreted_entry()); istate->set_bcp_advance(3); + // Invokehandle has got a call counter, just like a final call -> increment! + BI_PROFILE_UPDATE_FINALCALL(); + UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2302,14 +2579,18 @@ CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); if (cache->is_vfinal()) { callee = cache->f2_as_vfinal_method(); + // Profile 'special case of invokeinterface' final call. + BI_PROFILE_UPDATE_FINALCALL(); } else { - // get receiver + // Get receiver. int parms = cache->parameter_size(); - // Same comments as invokevirtual apply here - VERIFY_OOP(STACK_OBJECT(-parms)); - instanceKlass* rcvrKlass = (instanceKlass*) - STACK_OBJECT(-parms)->klass()->klass_part(); + // Same comments as invokevirtual apply here. + oop rcvr = STACK_OBJECT(-parms); + VERIFY_OOP(rcvr); + instanceKlass* rcvrKlass = (instanceKlass*)rcvr->klass()->klass_part(); callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2_as_index()]; + // Profile 'special case of invokeinterface' virtual call. + BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass()); } istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); @@ -2340,15 +2621,18 @@ // interface. The link resolver checks this but only for the first // time this interface is called. if (i == int2->itable_length()) { - VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), ""); + VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap); } int mindex = cache->f2_as_index(); itableMethodEntry* im = ki->first_method_entry(rcvr->klass()); callee = im[mindex].method(); if (callee == NULL) { - VM_JAVA_ERROR(vmSymbols::java_lang_AbstractMethodError(), ""); + VM_JAVA_ERROR(vmSymbols::java_lang_AbstractMethodError(), "", note_no_trap); } + // Profile virtual call. + BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass()); + istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); #ifdef VM_JVMTI @@ -2380,8 +2664,11 @@ methodOop callee; if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) { CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); - if (cache->is_vfinal()) callee = cache->f2_as_vfinal_method(); - else { + if (cache->is_vfinal()) { + callee = cache->f2_as_vfinal_method(); + // Profile final call. + BI_PROFILE_UPDATE_FINALCALL(); + } else { // get receiver int parms = cache->parameter_size(); // this works but needs a resourcemark and seems to create a vtable on every call: @@ -2390,8 +2677,9 @@ // this fails with an assert // instanceKlass* rcvrKlass = instanceKlass::cast(STACK_OBJECT(-parms)->klass()); // but this works - VERIFY_OOP(STACK_OBJECT(-parms)); - instanceKlass* rcvrKlass = (instanceKlass*) STACK_OBJECT(-parms)->klass()->klass_part(); + oop rcvr = STACK_OBJECT(-parms); + VERIFY_OOP(rcvr); + instanceKlass* rcvrKlass = (instanceKlass*) rcvr->klass()->klass_part(); /* Executing this code in java.lang.String: public String(char value[]) { @@ -2409,12 +2697,17 @@ */ callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2_as_index()]; + // Profile virtual call. + BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass()); } } else { if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) { CHECK_NULL(STACK_OBJECT(-(cache->parameter_size()))); } callee = cache->f1_as_method(); + + // Profile call. + BI_PROFILE_UPDATE_CALL(); } istate->set_callee(callee); @@ -2436,6 +2729,9 @@ jint size = STACK_INT(-1); CALL_VM(InterpreterRuntime::newarray(THREAD, atype, size), handle_exception); + // Must prevent reordering of stores for object initialization + // with stores that publish the new object. + OrderAccess::storestore(); SET_STACK_OBJECT(THREAD->vm_result(), -1); THREAD->set_vm_result(NULL); @@ -2466,6 +2762,8 @@ CASE(_goto): { int16_t offset = (int16_t)Bytes::get_Java_u2(pc + 1); + // Profile jump. + BI_PROFILE_UPDATE_JUMP(); address branch_pc = pc; UPDATE_PC(offset); DO_BACKEDGE_CHECKS(offset, branch_pc); @@ -2482,6 +2780,8 @@ CASE(_goto_w): { int32_t offset = Bytes::get_Java_u4(pc + 1); + // Profile jump. + BI_PROFILE_UPDATE_JUMP(); address branch_pc = pc; UPDATE_PC(offset); DO_BACKEDGE_CHECKS(offset, branch_pc); @@ -2491,6 +2791,9 @@ /* return from a jsr or jsr_w */ CASE(_ret): { + // Profile ret. + BI_PROFILE_UPDATE_RET(/*bci=*/((int)(intptr_t)(LOCALS_ADDR(pc[1])))); + // Now, update the pc. pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(pc[1])); UPDATE_PC_AND_CONTINUE(0); } @@ -2567,11 +2870,14 @@ tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), except_oop()); tty->print_cr(" thrown in interpreter method <%s>", METHOD->print_value_string()); tty->print_cr(" at bci %d, continuing at %d for thread " INTPTR_FORMAT, - pc - (intptr_t)METHOD->code_base(), + istate->bcp() - (intptr_t)METHOD->code_base(), continuation_bci, THREAD); } // for AbortVMOnException flag NOT_PRODUCT(Exceptions::debug_check_abort(except_oop)); + + // Update profiling data. + BI_PROFILE_ALIGN_TO_CURRENT_BCI(); goto run; } if (TraceExceptions) { @@ -2580,7 +2886,7 @@ tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), except_oop()); tty->print_cr(" thrown in interpreter method <%s>", METHOD->print_value_string()); tty->print_cr(" at bci %d, unwinding for thread " INTPTR_FORMAT, - pc - (intptr_t) METHOD->code_base(), + istate->bcp() - (intptr_t)METHOD->code_base(), THREAD); } // for AbortVMOnException flag @@ -2588,32 +2894,87 @@ // No handler in this activation, unwind and try again THREAD->set_pending_exception(except_oop(), NULL, 0); goto handle_return; - } /* handle_exception: */ - - + } // handle_exception: // Return from an interpreter invocation with the result of the interpretation // on the top of the Java Stack (or a pending exception) -handle_Pop_Frame: - - // We don't really do anything special here except we must be aware - // that we can get here without ever locking the method (if sync). - // Also we skip the notification of the exit. - - istate->set_msg(popping_frame); - // Clear pending so while the pop is in process - // we don't start another one if a call_vm is done. - THREAD->clr_pop_frame_pending(); - // Let interpreter (only) see the we're in the process of popping a frame - THREAD->set_pop_frame_in_process(); - -handle_return: - { + handle_Pop_Frame: { + + // We don't really do anything special here except we must be aware + // that we can get here without ever locking the method (if sync). + // Also we skip the notification of the exit. + + istate->set_msg(popping_frame); + // Clear pending so while the pop is in process + // we don't start another one if a call_vm is done. + THREAD->clr_pop_frame_pending(); + // Let interpreter (only) see the we're in the process of popping a frame + THREAD->set_pop_frame_in_process(); + + goto handle_return; + + } // handle_Pop_Frame + + // ForceEarlyReturn ends a method, and returns to the caller with a return value + // given by the invoker of the early return. + handle_Early_Return: { + + istate->set_msg(early_return); + + // Clear expression stack. + topOfStack = istate->stack_base() - Interpreter::stackElementWords; + + JvmtiThreadState *ts = THREAD->jvmti_thread_state(); + + // Push the value to be returned. + switch (istate->method()->result_type()) { + case T_BOOLEAN: + case T_SHORT: + case T_BYTE: + case T_CHAR: + case T_INT: + SET_STACK_INT(ts->earlyret_value().i, 0); + MORE_STACK(1); + break; + case T_LONG: + SET_STACK_LONG(ts->earlyret_value().j, 1); + MORE_STACK(2); + break; + case T_FLOAT: + SET_STACK_FLOAT(ts->earlyret_value().f, 0); + MORE_STACK(1); + break; + case T_DOUBLE: + SET_STACK_DOUBLE(ts->earlyret_value().d, 1); + MORE_STACK(2); + break; + case T_ARRAY: + case T_OBJECT: + SET_STACK_OBJECT(ts->earlyret_oop(), 0); + MORE_STACK(1); + break; + } + + ts->clr_earlyret_value(); + ts->set_earlyret_oop(NULL); + ts->clr_earlyret_pending(); + + // Fall through to handle_return. + + } // handle_Early_Return + + handle_return: { + // A storestore barrier is required to order initialization of + // final fields with publishing the reference to the object that + // holds the field. Without the barrier the value of final fields + // can be observed to change. + OrderAccess::storestore(); + DECACHE_STATE(); - bool suppress_error = istate->msg() == popping_frame; - bool suppress_exit_event = THREAD->has_pending_exception() || suppress_error; + bool suppress_error = istate->msg() == popping_frame || istate->msg() == early_return; + bool suppress_exit_event = THREAD->has_pending_exception() || istate->msg() == popping_frame; Handle original_exception(THREAD, THREAD->pending_exception()); Handle illegal_state_oop(THREAD, NULL); @@ -2674,15 +3035,18 @@ BasicLock* lock = end->lock(); markOop header = lock->displaced_header(); end->set_obj(NULL); - // If it isn't recursive we either must swap old header or call the runtime - if (header != NULL) { - if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { - // restore object for the slow case - end->set_obj(lockee); - { - // Prevent any HandleMarkCleaner from freeing our live handles - HandleMark __hm(THREAD); - CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, end)); + + if (!lockee->mark()->has_bias_pattern()) { + // If it isn't recursive we either must swap old header or call the runtime + if (header != NULL) { + if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), lock) != lock) { + // restore object for the slow case + end->set_obj(lockee); + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, end)); + } } } } @@ -2727,27 +3091,41 @@ oop rcvr = base->obj(); if (rcvr == NULL) { if (!suppress_error) { - VM_JAVA_ERROR_NO_JUMP(vmSymbols::java_lang_NullPointerException(), ""); + VM_JAVA_ERROR_NO_JUMP(vmSymbols::java_lang_NullPointerException(), NULL, note_nullCheck_trap); illegal_state_oop = THREAD->pending_exception(); THREAD->clear_pending_exception(); } + } else if (UseHeavyMonitors) { + { + // Prevent any HandleMarkCleaner from freeing our live handles. + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, base)); + } + if (THREAD->has_pending_exception()) { + if (!suppress_error) illegal_state_oop = THREAD->pending_exception(); + THREAD->clear_pending_exception(); + } } else { BasicLock* lock = base->lock(); markOop header = lock->displaced_header(); base->set_obj(NULL); - // If it isn't recursive we either must swap old header or call the runtime - if (header != NULL) { - if (Atomic::cmpxchg_ptr(header, rcvr->mark_addr(), lock) != lock) { - // restore object for the slow case - base->set_obj(rcvr); - { - // Prevent any HandleMarkCleaner from freeing our live handles - HandleMark __hm(THREAD); - CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, base)); - } - if (THREAD->has_pending_exception()) { - if (!suppress_error) illegal_state_oop = THREAD->pending_exception(); - THREAD->clear_pending_exception(); + + if (!rcvr->mark()->has_bias_pattern()) { + base->set_obj(NULL); + // If it isn't recursive we either must swap old header or call the runtime + if (header != NULL) { + if (Atomic::cmpxchg_ptr(header, rcvr->mark_addr(), lock) != lock) { + // restore object for the slow case + base->set_obj(rcvr); + { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(THREAD, base)); + } + if (THREAD->has_pending_exception()) { + if (!suppress_error) illegal_state_oop = THREAD->pending_exception(); + THREAD->clear_pending_exception(); + } } } } @@ -2755,6 +3133,8 @@ } } } + // Clear the do_not_unlock flag now. + THREAD->clr_do_not_unlock(); // // Notify jvmti/jvmdi @@ -2799,15 +3179,14 @@ // A pending exception that was pending prior to a possible popping frame // overrides the popping frame. // - assert(!suppress_error || suppress_error && illegal_state_oop() == NULL, "Error was not suppressed"); + assert(!suppress_error || (suppress_error && illegal_state_oop() == NULL), "Error was not suppressed"); if (illegal_state_oop() != NULL || original_exception() != NULL) { - // inform the frame manager we have no result + // Inform the frame manager we have no result. istate->set_msg(throwing_exception); if (illegal_state_oop() != NULL) THREAD->set_pending_exception(illegal_state_oop(), NULL, 0); else THREAD->set_pending_exception(original_exception(), NULL, 0); - istate->set_return_kind((Bytecodes::Code)opcode); UPDATE_PC_AND_RETURN(0); } @@ -2826,13 +3205,12 @@ LOCALS_SLOT(METHOD->size_of_parameters() - 1)); THREAD->set_popframe_condition_bit(JavaThread::popframe_force_deopt_reexecution_bit); } - THREAD->clr_pop_frame_in_process(); + } else { + istate->set_msg(return_from_method); } // Normal return // Advance the pc and return to frame manager - istate->set_msg(return_from_method); - istate->set_return_kind((Bytecodes::Code)opcode); UPDATE_PC_AND_RETURN(1); } /* handle_return: */ @@ -3107,7 +3485,6 @@ tty->print_cr("result_to_call._bcp_advance: %d ", this->_result._to_call._bcp_advance); tty->print_cr("osr._osr_buf: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_buf); tty->print_cr("osr._osr_entry: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_entry); - tty->print_cr("result_return_kind 0x%x ", (int) this->_result._return_kind); tty->print_cr("prev_link: " INTPTR_FORMAT, (uintptr_t) this->_prev_link); tty->print_cr("native_mirror: " INTPTR_FORMAT, (uintptr_t) this->_oop_temp); tty->print_cr("stack_base: " INTPTR_FORMAT, (uintptr_t) this->_stack_base); @@ -3119,16 +3496,16 @@ tty->print_cr("&native_fresult: " INTPTR_FORMAT, (uintptr_t) &this->_native_fresult); tty->print_cr("native_lresult: " INTPTR_FORMAT, (uintptr_t) this->_native_lresult); #endif -#if defined(IA64) && !defined(ZERO) +#if defined(IA64) && !defined(ZERO) && !defined(AIX) tty->print_cr("last_Java_fp: " INTPTR_FORMAT, (uintptr_t) this->_last_Java_fp); -#endif // IA64 && !ZERO +#endif tty->print_cr("self_link: " INTPTR_FORMAT, (uintptr_t) this->_self_link); } extern "C" { - void PI(uintptr_t arg) { - ((BytecodeInterpreter*)arg)->print(); - } + void PI(uintptr_t arg) { + ((BytecodeInterpreter*)arg)->print(); + } } #endif // PRODUCT diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/bytecodeInterpreter.hpp --- a/src/share/vm/interpreter/bytecodeInterpreter.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/bytecodeInterpreter.hpp Sun May 21 11:12:48 2017 -0700 @@ -115,7 +115,8 @@ more_monitors, // need a new monitor throwing_exception, // unwind stack and rethrow popping_frame, // unwind call and retry call - do_osr // request this invocation be OSR's + do_osr, // request this invocation be OSR's + early_return // early return as commanded by jvmti }; private: diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +// This file defines a set of macros which are used by the c++-interpreter +// for updating a method's methodData object. + + +#ifndef SHARE_VM_INTERPRETER_BYTECODEINTERPRETERPROFILING_HPP +#define SHARE_VM_INTERPRETER_BYTECODEINTERPRETERPROFILING_HPP + + +// Global settings ///////////////////////////////////////////////////////////// + + +// Enables profiling support. +#if defined(COMPILER2) +#define CC_INTERP_PROFILE +#endif + +// Enables assertions for profiling code (also works in product-builds). +// #define CC_INTERP_PROFILE_WITH_ASSERTIONS + + +#ifdef CC_INTERP + +// Empty dummy implementations if profiling code is switched off. ////////////// + +#ifndef CC_INTERP_PROFILE + +#define SET_MDX(mdx) + +#define BI_PROFILE_GET_OR_CREATE_METHOD_DATA(exception_handler) \ + if (ProfileInterpreter) { \ + ShouldNotReachHere(); \ + } + +#define BI_PROFILE_ALIGN_TO_CURRENT_BCI() + +#define BI_PROFILE_UPDATE_JUMP() +#define BI_PROFILE_UPDATE_BRANCH(is_taken) +#define BI_PROFILE_UPDATE_RET(bci) +#define BI_PROFILE_SUBTYPECHECK_FAILED(receiver) +#define BI_PROFILE_UPDATE_CHECKCAST(null_seen, receiver) +#define BI_PROFILE_UPDATE_INSTANCEOF(null_seen, receiver) +#define BI_PROFILE_UPDATE_CALL() +#define BI_PROFILE_UPDATE_FINALCALL() +#define BI_PROFILE_UPDATE_VIRTUALCALL(receiver) +#define BI_PROFILE_UPDATE_SWITCH(switch_index) + + +#else + + +// Non-dummy implementations /////////////////////////////////////////////////// + +// Accessors for the current method data pointer 'mdx'. +#define MDX() (istate->mdx()) +#define SET_MDX(mdx) \ + if (TraceProfileInterpreter) { \ + /* Let it look like TraceBytecodes' format. */ \ + tty->print_cr("[%d] %4d " \ + "mdx " PTR_FORMAT "(%d)" \ + " " \ + " \t-> " PTR_FORMAT "(%d)", \ + (int) THREAD->osthread()->thread_id(), \ + BCI(), \ + MDX(), \ + (MDX() == NULL \ + ? 0 \ + : istate->method()->method_data()->dp_to_di((address)MDX())), \ + mdx, \ + istate->method()->method_data()->dp_to_di((address)mdx) \ + ); \ + }; \ + istate->set_mdx(mdx); + + +// Dumps the profiling method data for the current method. +#ifdef PRODUCT +#define BI_PROFILE_PRINT_METHOD_DATA() +#else // PRODUCT +#define BI_PROFILE_PRINT_METHOD_DATA() \ + { \ + ttyLocker ttyl; \ + methodDataOop mdo = istate->method()->method_data(); \ + tty->cr(); \ + tty->print("method data at mdx " PTR_FORMAT "(0) for", \ + mdo->data_layout_at(mdo->bci_to_di(0))); \ + istate->method()->print_short_name(tty); \ + tty->cr(); \ + if (mdo != NULL) { \ + mdo->print_data_on(tty); \ + address mdx = (address) MDX(); \ + if (mdx != NULL) { \ + tty->print_cr("current mdx " PTR_FORMAT "(%d)", \ + mdx, \ + istate->method()->method_data()->dp_to_di(mdx)); \ + } \ + } else { \ + tty->print_cr("no method data"); \ + } \ + } +#endif // PRODUCT + + +// Gets or creates the profiling method data and initializes mdx. +#define BI_PROFILE_GET_OR_CREATE_METHOD_DATA(exception_handler) \ + if (ProfileInterpreter && MDX() == NULL) { \ + /* Mdx is not yet initialized for this activation. */ \ + methodDataOop mdo = istate->method()->method_data(); \ + if (mdo == NULL) { \ + /* The profiling method data doesn't exist for this method, */ \ + /* create it if the counters have overflowed. */ \ + if (INVOCATION_COUNT->reached_ProfileLimit(BACKEDGE_COUNT)) { \ + /* Must use CALL_VM, because an async exception may be pending. */ \ + CALL_VM((InterpreterRuntime::profile_method(THREAD)), \ + exception_handler); \ + mdo = istate->method()->method_data(); \ + if (mdo != NULL) { \ + if (TraceProfileInterpreter) { \ + BI_PROFILE_PRINT_METHOD_DATA(); \ + } \ + methodOop m = istate->method(); \ + int bci = m->bci_from(pc); \ + jint di = mdo->bci_to_di(bci); \ + SET_MDX(mdo->data_layout_at(di)); \ + } \ + } \ + } else { \ + /* The profiling method data exists, align the method data pointer */ \ + /* mdx to the current bytecode index. */ \ + if (TraceProfileInterpreter) { \ + BI_PROFILE_PRINT_METHOD_DATA(); \ + } \ + SET_MDX(mdo->data_layout_at(mdo->bci_to_di(BCI()))); \ + } \ + } + + +// Asserts that the current method data pointer mdx corresponds +// to the current bytecode. +#if defined(CC_INTERP_PROFILE_WITH_ASSERTIONS) +#define BI_PROFILE_CHECK_MDX() \ + { \ + methodDataOop mdo = istate->method()->method_data(); \ + address mdx = (address) MDX(); \ + address mdx2 = (address) mdo->data_layout_at(mdo->bci_to_di(BCI())); \ + guarantee(mdo != NULL, "1"); \ + guarantee(mdx != NULL, "2"); \ + guarantee(mdx2 != NULL, "3"); \ + if (mdx != mdx2) { \ + BI_PROFILE_PRINT_METHOD_DATA(); \ + fatal3("invalid mdx at bci %d:" \ + " was " PTR_FORMAT \ + " but expected " PTR_FORMAT, \ + BCI(), \ + mdx, \ + mdx2); \ + } \ + } +#else +#define BI_PROFILE_CHECK_MDX() +#endif + + +// Aligns the method data pointer mdx to the current bytecode index. +#define BI_PROFILE_ALIGN_TO_CURRENT_BCI() \ + if (ProfileInterpreter && MDX() != NULL) { \ + methodDataOop mdo = istate->method()->method_data(); \ + SET_MDX(mdo->data_layout_at(mdo->bci_to_di(BCI()))); \ + } + + +// Updates profiling data for a jump. +#define BI_PROFILE_UPDATE_JUMP() \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + JumpData::increment_taken_count_no_overflow(MDX()); \ + /* Remember last branch taken count. */ \ + mdo_last_branch_taken_count = JumpData::taken_count(MDX()); \ + SET_MDX(JumpData::advance_taken(MDX())); \ + } + + +// Updates profiling data for a taken/not taken branch. +#define BI_PROFILE_UPDATE_BRANCH(is_taken) \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + if (is_taken) { \ + BranchData::increment_taken_count_no_overflow(MDX()); \ + /* Remember last branch taken count. */ \ + mdo_last_branch_taken_count = BranchData::taken_count(MDX()); \ + SET_MDX(BranchData::advance_taken(MDX())); \ + } else { \ + BranchData::increment_not_taken_count_no_overflow(MDX()); \ + SET_MDX(BranchData::advance_not_taken(MDX())); \ + } \ + } + + +// Updates profiling data for a ret with given bci. +#define BI_PROFILE_UPDATE_RET(bci) \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + methodDataOop mdo = istate->method()->method_data(); \ +/* FIXME: there is more to do here than increment and advance(mdx)! */ \ + CounterData::increment_count_no_overflow(MDX()); \ + SET_MDX(RetData::advance(mdo, bci)); \ + } + +// Decrement counter at checkcast if the subtype check fails (as template +// interpreter does!). +#define BI_PROFILE_SUBTYPECHECK_FAILED(receiver) \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + ReceiverTypeData::increment_receiver_count_no_overflow(MDX(), receiver); \ + ReceiverTypeData::decrement_count(MDX()); \ + } + +// Updates profiling data for a checkcast (was a null seen? which receiver?). +#define BI_PROFILE_UPDATE_CHECKCAST(null_seen, receiver) \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + if (null_seen) { \ + ReceiverTypeData::set_null_seen(MDX()); \ + } else { \ + /* Template interpreter doesn't increment count. */ \ + /* ReceiverTypeData::increment_count_no_overflow(MDX()); */ \ + ReceiverTypeData::increment_receiver_count_no_overflow(MDX(), receiver); \ + } \ + SET_MDX(ReceiverTypeData::advance(MDX())); \ + } + + +// Updates profiling data for an instanceof (was a null seen? which receiver?). +#define BI_PROFILE_UPDATE_INSTANCEOF(null_seen, receiver) \ + BI_PROFILE_UPDATE_CHECKCAST(null_seen, receiver) + + +// Updates profiling data for a call. +#define BI_PROFILE_UPDATE_CALL() \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + CounterData::increment_count_no_overflow(MDX()); \ + SET_MDX(CounterData::advance(MDX())); \ + } + + +// Updates profiling data for a final call. +#define BI_PROFILE_UPDATE_FINALCALL() \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + VirtualCallData::increment_count_no_overflow(MDX()); \ + SET_MDX(VirtualCallData::advance(MDX())); \ + } + + +// Updates profiling data for a virtual call with given receiver klassOop. +#define BI_PROFILE_UPDATE_VIRTUALCALL(receiver) \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + VirtualCallData::increment_receiver_count_no_overflow(MDX(), receiver); \ + SET_MDX(VirtualCallData::advance(MDX())); \ + } + + +// Updates profiling data for a switch (tabelswitch or lookupswitch) with +// given taken index (-1 means default case was taken). +#define BI_PROFILE_UPDATE_SWITCH(switch_index) \ + if (ProfileInterpreter && MDX() != NULL) { \ + BI_PROFILE_CHECK_MDX(); \ + MultiBranchData::increment_count_no_overflow(MDX(), switch_index); \ + SET_MDX(MultiBranchData::advance(MDX(), switch_index)); \ + } + + +// The end ///////////////////////////////////////////////////////////////////// + +#endif // CC_INTERP_PROFILE + +#endif // CC_INTERP + +#endif // SHARE_VM_INTERPRETER_BYTECODECINTERPRETERPROFILING_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/interpreterRuntime.cpp --- a/src/share/vm/interpreter/interpreterRuntime.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Sun May 21 11:12:48 2017 -0700 @@ -251,18 +251,15 @@ //------------------------------------------------------------------------------------------------------------------------ // Exceptions -// Assume the compiler is (or will be) interested in this event. -// If necessary, create an MDO to hold the information, and record it. -void InterpreterRuntime::note_trap(JavaThread* thread, int reason, TRAPS) { - assert(ProfileTraps, "call me only if profiling"); - methodHandle trap_method(thread, method(thread)); - +void InterpreterRuntime::note_trap_inner(JavaThread* thread, int reason, + methodHandle trap_method, int trap_bci, TRAPS) { if (trap_method.not_null()) { methodDataHandle trap_mdo(thread, trap_method->method_data()); if (trap_mdo.is_null()) { methodOopDesc::build_interpreter_method_data(trap_method, THREAD); if (HAS_PENDING_EXCEPTION) { - assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOM error here"); + assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), + "we expect only an OOM error here"); CLEAR_PENDING_EXCEPTION; } trap_mdo = methodDataHandle(thread, trap_method->method_data()); @@ -271,12 +268,41 @@ if (trap_mdo.not_null()) { // Update per-method count of trap events. The interpreter // is updating the MDO to simulate the effect of compiler traps. - int trap_bci = trap_method->bci_from(bcp(thread)); Deoptimization::update_method_data_from_interpreter(trap_mdo, trap_bci, reason); } } } +// Assume the compiler is (or will be) interested in this event. +// If necessary, create an MDO to hold the information, and record it. +void InterpreterRuntime::note_trap(JavaThread* thread, int reason, TRAPS) { + assert(ProfileTraps, "call me only if profiling"); + methodHandle trap_method(thread, method(thread)); + int trap_bci = trap_method->bci_from(bcp(thread)); + note_trap_inner(thread, reason, trap_method, trap_bci, THREAD); +} + +#ifdef CC_INTERP +// As legacy note_trap, but we have more arguments. +IRT_ENTRY(void, InterpreterRuntime::note_trap(JavaThread* thread, int reason, methodOop method, int trap_bci)) + methodHandle trap_method(method); + note_trap_inner(thread, reason, trap_method, trap_bci, THREAD); +IRT_END + +// Class Deoptimization is not visible in BytecodeInterpreter, so we need a wrapper +// for each exception. +void InterpreterRuntime::note_nullCheck_trap(JavaThread* thread, methodOop method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_null_check, method, trap_bci); } +void InterpreterRuntime::note_div0Check_trap(JavaThread* thread, methodOop method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_div0_check, method, trap_bci); } +void InterpreterRuntime::note_rangeCheck_trap(JavaThread* thread, methodOop method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_range_check, method, trap_bci); } +void InterpreterRuntime::note_classCheck_trap(JavaThread* thread, methodOop method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_class_check, method, trap_bci); } +void InterpreterRuntime::note_arrayCheck_trap(JavaThread* thread, methodOop method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_array_check, method, trap_bci); } +#endif // CC_INTERP + static Handle get_preinitialized_exception(klassOop k, TRAPS) { // get klass instanceKlass* klass = instanceKlass::cast(k); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/interpreterRuntime.hpp --- a/src/share/vm/interpreter/interpreterRuntime.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/interpreterRuntime.hpp Sun May 21 11:12:48 2017 -0700 @@ -41,6 +41,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -77,9 +80,15 @@ static ConstantPoolCacheEntry* cache_entry_at(JavaThread *thread, int i) { return method(thread)->constants()->cache()->entry_at(i); } static ConstantPoolCacheEntry* cache_entry(JavaThread *thread) { return cache_entry_at(thread, Bytes::get_native_u2(bcp(thread) + 1)); } + static void note_trap_inner(JavaThread* thread, int reason, + methodHandle trap_method, int trap_bci, TRAPS); static void note_trap(JavaThread *thread, int reason, TRAPS); +#ifdef CC_INTERP + // Profile traps in C++ interpreter. + static void note_trap(JavaThread* thread, int reason, methodOop method, int trap_bci); +#endif // CC_INTERP - // Inner work method for Interpreter's frequency counter overflow + // Inner work method for Interpreter's frequency counter overflow. static nmethod* frequency_counter_overflow_inner(JavaThread* thread, address branch_bcp); public: @@ -109,6 +118,17 @@ static void member_name_arg_or_null(JavaThread* thread, address dmh, methodOopDesc* m, address bcp); static void throw_pending_exception(JavaThread* thread); +#ifdef CC_INTERP + // Profile traps in C++ interpreter. + static void note_nullCheck_trap (JavaThread* thread, methodOop method, int trap_bci); + static void note_div0Check_trap (JavaThread* thread, methodOop method, int trap_bci); + static void note_rangeCheck_trap(JavaThread* thread, methodOop method, int trap_bci); + static void note_classCheck_trap(JavaThread* thread, methodOop method, int trap_bci); + static void note_arrayCheck_trap(JavaThread* thread, methodOop method, int trap_bci); + // A dummy for makros that shall not profile traps. + static void note_no_trap(JavaThread* thread, methodOop method, int trap_bci) {} +#endif // CC_INTERP + // Statics & fields static void resolve_get_put(JavaThread* thread, Bytecodes::Code bytecode); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/invocationCounter.hpp --- a/src/share/vm/interpreter/invocationCounter.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/invocationCounter.hpp Sun May 21 11:12:48 2017 -0700 @@ -96,16 +96,24 @@ int get_BackwardBranchLimit() const { return InterpreterBackwardBranchLimit >> number_of_noncount_bits; } int get_ProfileLimit() const { return InterpreterProfileLimit >> number_of_noncount_bits; } +#ifdef CC_INTERP // Test counter using scaled limits like the asm interpreter would do rather than doing // the shifts to normalize the counter. - - bool reached_InvocationLimit() const { return _counter >= (unsigned int) InterpreterInvocationLimit; } - bool reached_BackwardBranchLimit() const { return _counter >= (unsigned int) InterpreterBackwardBranchLimit; } - - // Do this just like asm interpreter does for max speed - bool reached_ProfileLimit(InvocationCounter *back_edge_count) const { - return (_counter && count_mask) + back_edge_count->_counter >= (unsigned int) InterpreterProfileLimit; + // Checks sum of invocation_counter and backedge_counter as the template interpreter does. + bool reached_InvocationLimit(InvocationCounter *back_edge_count) const { + return (_counter & count_mask) + (back_edge_count->_counter & count_mask) >= + (unsigned int) InterpreterInvocationLimit; } + bool reached_BackwardBranchLimit(InvocationCounter *back_edge_count) const { + return (_counter & count_mask) + (back_edge_count->_counter & count_mask) >= + (unsigned int) InterpreterBackwardBranchLimit; + } + // Do this just like asm interpreter does for max speed. + bool reached_ProfileLimit(InvocationCounter *back_edge_count) const { + return (_counter & count_mask) + (back_edge_count->_counter & count_mask) >= + (unsigned int) InterpreterProfileLimit; + } +#endif // CC_INTERP void increment() { _counter += count_increment; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/linkResolver.cpp Sun May 21 11:12:48 2017 -0700 @@ -52,6 +52,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/interpreter/templateTable.hpp --- a/src/share/vm/interpreter/templateTable.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/interpreter/templateTable.hpp Sun May 21 11:12:48 2017 -0700 @@ -40,8 +40,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "interp_masm_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "interp_masm_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "interp_masm_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "interp_masm_ppc_64.hpp" #endif #ifndef CC_INTERP @@ -371,8 +374,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "templateTable_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "templateTable_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "templateTable_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "templateTable_ppc_64.hpp" #endif }; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/libadt/port.hpp --- a/src/share/vm/libadt/port.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/libadt/port.hpp Sun May 21 11:12:48 2017 -0700 @@ -91,8 +91,6 @@ #define IMPLEMENTATION #include #include -inline int min( int a, int b) { return a < b ? a : b; } -inline int max( int a, int b) { return a > b ? a : b; } #elif defined(_MSC_VER) // Microsoft Visual C++ @@ -192,8 +190,11 @@ typedef double float64; // 64-bit float #endif // __TANDEM +#ifndef _AIX +// These conflict with /usr/include/sys/inttypes.h on aix. typedef jlong int64; // Java long for my 64-bit type typedef julong uint64; // Java long for my 64-bit type +#endif //----------------------------------------------------------------------------- // Nice constants diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/allocation.cpp --- a/src/share/vm/memory/allocation.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/allocation.cpp Sun May 21 11:12:48 2017 -0700 @@ -42,6 +42,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/barrierSet.hpp --- a/src/share/vm/memory/barrierSet.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/barrierSet.hpp Sun May 21 11:12:48 2017 -0700 @@ -101,9 +101,9 @@ public: // ...then the post-write version. - inline void write_ref_field(void* field, oop new_val); + inline void write_ref_field(void* field, oop new_val, bool release = false); protected: - virtual void write_ref_field_work(void* field, oop new_val) = 0; + virtual void write_ref_field_work(void* field, oop new_val, bool release = false) = 0; public: // Invoke the barrier, if any, necessary when writing the "bytes"-byte diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/barrierSet.inline.hpp --- a/src/share/vm/memory/barrierSet.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/barrierSet.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -40,11 +40,11 @@ } } -void BarrierSet::write_ref_field(void* field, oop new_val) { +void BarrierSet::write_ref_field(void* field, oop new_val, bool release) { if (kind() == CardTableModRef) { - ((CardTableModRefBS*)this)->inline_write_ref_field(field, new_val); + ((CardTableModRefBS*)this)->inline_write_ref_field(field, new_val, release); } else { - write_ref_field_work(field, new_val); + write_ref_field_work(field, new_val, release); } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/cardTableModRefBS.cpp --- a/src/share/vm/memory/cardTableModRefBS.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/cardTableModRefBS.cpp Sun May 21 11:12:48 2017 -0700 @@ -389,15 +389,15 @@ } // Touch the last card of the covered region to show that it // is committed (or SEGV). - debug_only(*byte_for(_covered[ind].last());) + debug_only((void) (*byte_for(_covered[ind].last()));) debug_only(verify_guard();) } // Note that these versions are precise! The scanning code has to handle the // fact that the write barrier may be either precise or imprecise. -void CardTableModRefBS::write_ref_field_work(void* field, oop newVal) { - inline_write_ref_field(field, newVal); +void CardTableModRefBS::write_ref_field_work(void* field, oop newVal, bool release) { + inline_write_ref_field(field, newVal, release); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/cardTableModRefBS.hpp --- a/src/share/vm/memory/cardTableModRefBS.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/cardTableModRefBS.hpp Sun May 21 11:12:48 2017 -0700 @@ -297,7 +297,7 @@ // these functions here for performance. protected: void write_ref_field_work(oop obj, size_t offset, oop newVal); - virtual void write_ref_field_work(void* field, oop newVal); + virtual void write_ref_field_work(void* field, oop newVal, bool release = false); public: bool has_write_ref_array_opt() { return true; } @@ -329,9 +329,14 @@ template inline void inline_write_ref_field_pre(T* field, oop newVal) {} - template inline void inline_write_ref_field(T* field, oop newVal) { + template inline void inline_write_ref_field(T* field, oop newVal, bool release) { jbyte* byte = byte_for((void*)field); - *byte = dirty_card; + if (release) { + // Perform a releasing store if requested. + OrderAccess::release_store((volatile jbyte*) byte, dirty_card); + } else { + *byte = dirty_card; + } } // These are used by G1, when it uses the card table as a temporary data diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/collectorPolicy.cpp --- a/src/share/vm/memory/collectorPolicy.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/collectorPolicy.cpp Sun May 21 11:12:48 2017 -0700 @@ -47,6 +47,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/defNewGeneration.cpp --- a/src/share/vm/memory/defNewGeneration.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/defNewGeneration.cpp Sun May 21 11:12:48 2017 -0700 @@ -52,6 +52,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/gcLocker.hpp --- a/src/share/vm/memory/gcLocker.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/gcLocker.hpp Sun May 21 11:12:48 2017 -0700 @@ -41,6 +41,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/genMarkSweep.cpp --- a/src/share/vm/memory/genMarkSweep.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/genMarkSweep.cpp Sun May 21 11:12:48 2017 -0700 @@ -59,6 +59,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/modRefBarrierSet.hpp --- a/src/share/vm/memory/modRefBarrierSet.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/modRefBarrierSet.hpp Sun May 21 11:12:48 2017 -0700 @@ -61,7 +61,7 @@ void read_ref_field(void* field) {} void read_prim_field(HeapWord* field, size_t bytes) {} protected: - virtual void write_ref_field_work(void* field, oop new_val) = 0; + virtual void write_ref_field_work(void* field, oop new_val, bool release = false) = 0; public: void write_prim_field(HeapWord* field, size_t bytes, juint val1, juint val2) {} diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/resourceArea.cpp --- a/src/share/vm/memory/resourceArea.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/resourceArea.cpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/resourceArea.hpp --- a/src/share/vm/memory/resourceArea.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/resourceArea.hpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/space.hpp --- a/src/share/vm/memory/space.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/space.hpp Sun May 21 11:12:48 2017 -0700 @@ -44,6 +44,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/threadLocalAllocBuffer.cpp --- a/src/share/vm/memory/threadLocalAllocBuffer.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/threadLocalAllocBuffer.cpp Sun May 21 11:12:48 2017 -0700 @@ -38,6 +38,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/memory/universe.cpp --- a/src/share/vm/memory/universe.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/memory/universe.cpp Sun May 21 11:12:48 2017 -0700 @@ -89,6 +89,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -657,7 +660,9 @@ if (vt) vt->initialize_vtable(false, CHECK); if (ko->oop_is_instance()) { instanceKlass* ik = (instanceKlass*)ko; - for (KlassHandle s_h(THREAD, ik->subklass()); s_h() != NULL; s_h = (THREAD, s_h()->klass_part()->next_sibling())) { + for (KlassHandle s_h(THREAD, ik->subklass()); + s_h() != NULL; + s_h = (THREAD, s_h()->klass_part()->next_sibling())) { reinitialize_vtable_of(s_h, CHECK); } } @@ -878,7 +883,7 @@ // the correct no-access prefix. // The final value will be set in initialize_heap() below. Universe::set_narrow_oop_base((address)NarrowOopHeapMax); -#ifdef _WIN64 +#if defined(_WIN64) || defined(AIX) if (UseLargePages) { // Cannot allocate guard pages for implicit checks in indexed // addressing mode when large pages are specified on windows. @@ -956,6 +961,11 @@ // Can't reserve heap below 32Gb. Universe::set_narrow_oop_base(Universe::heap()->base() - os::vm_page_size()); Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes); +#ifdef AIX + // There is no protected page before the heap. This assures all oops + // are decoded so that NULL is preserverd, so this page will not be accessed. + Universe::set_narrow_oop_use_implicit_null_checks(false); +#endif if (verbose) { tty->print(", %s: "PTR_FORMAT, narrow_oop_mode_to_string(HeapBasedNarrowOop), @@ -1428,7 +1438,7 @@ static uintptr_t _verify_oop_data[2] = {0, (uintptr_t)-1}; static uintptr_t _verify_klass_data[2] = {0, (uintptr_t)-1}; - +#ifndef PRODUCT static void calculate_verify_data(uintptr_t verify_data[2], HeapWord* low_boundary, HeapWord* high_boundary) { @@ -1465,7 +1475,6 @@ // Oop verification (see MacroAssembler::verify_oop) -#ifndef PRODUCT uintptr_t Universe::verify_oop_mask() { MemRegion m = heap()->reserved_region(); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/constantPoolKlass.cpp --- a/src/share/vm/oops/constantPoolKlass.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/constantPoolKlass.cpp Sun May 21 11:12:48 2017 -0700 @@ -44,6 +44,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/constantPoolOop.cpp --- a/src/share/vm/oops/constantPoolOop.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/constantPoolOop.cpp Sun May 21 11:12:48 2017 -0700 @@ -1030,9 +1030,10 @@ int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2); int i1 = invoke_dynamic_bootstrap_specifier_index(index1); int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2); - bool match = compare_entry_to(k1, cp2, k2, CHECK_false) && - compare_operand_to(i1, cp2, i2, CHECK_false); - return match; + // separate statements and variables because CHECK_false is used + bool match_entry = compare_entry_to(k1, cp2, k2, CHECK_false); + bool match_operand = compare_operand_to(i1, cp2, i2, CHECK_false); + return (match_entry && match_operand); } break; case JVM_CONSTANT_UnresolvedString: diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/cpCacheOop.cpp --- a/src/share/vm/oops/cpCacheOop.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/cpCacheOop.cpp Sun May 21 11:12:48 2017 -0700 @@ -383,7 +383,9 @@ // Decode the action of set_method and set_interface_call Bytecodes::Code invoke_code = bytecode_1(); if (invoke_code != (Bytecodes::Code)0) { - oop f1 = _f1; + // Use accessor f1() to order this load relative to the load + // performed by f2(). + oop f1 = f1_ord(); if (f1 != NULL) { switch (invoke_code) { case Bytecodes::_invokeinterface: diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/cpCacheOop.hpp --- a/src/share/vm/oops/cpCacheOop.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/cpCacheOop.hpp Sun May 21 11:12:48 2017 -0700 @@ -135,7 +135,7 @@ void set_bytecode_1(Bytecodes::Code code); void set_bytecode_2(Bytecodes::Code code); - void set_f1(oop f1) { + void set_f1(oop f1) { oop existing_f1 = _f1; // read once assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change"); oop_store(&_f1, f1); @@ -307,17 +307,25 @@ return ((uintx)_indices >> secondary_cp_index_shift); } int primary_entry_indices() const { assert(!is_secondary_entry(), "must be main entry"); return _indices; } + int primary_entry_indices_ord() const { assert(!is_secondary_entry(), "must be main entry"); + return (intx)OrderAccess::load_ptr_acquire(&_indices); } int constant_pool_index() const { return (primary_entry_indices() & main_cp_index_mask); } - Bytecodes::Code bytecode_1() const { return Bytecodes::cast((primary_entry_indices() >> bytecode_1_shift) + Bytecodes::Code bytecode_1() const { return Bytecodes::cast((primary_entry_indices_ord() >> bytecode_1_shift) & bytecode_1_mask); } - Bytecodes::Code bytecode_2() const { return Bytecodes::cast((primary_entry_indices() >> bytecode_2_shift) + Bytecodes::Code bytecode_2() const { return Bytecodes::cast((primary_entry_indices_ord() >> bytecode_2_shift) & bytecode_2_mask); } - methodOop f1_as_method() const { oop f1 = _f1; assert(f1 == NULL || f1->is_method(), ""); return methodOop(f1); } - klassOop f1_as_klass() const { oop f1 = _f1; assert(f1 == NULL || f1->is_klass(), ""); return klassOop(f1); } + oop f1_ord() const {return (oop) OrderAccess::load_ptr_acquire(&_f1); } + methodOop f1_as_method() const { oop f1 = f1_ord(); assert(f1 == NULL || f1->is_method(), ""); return methodOop(f1); } + klassOop f1_as_klass() const { oop f1 = f1_ord(); assert(f1 == NULL || f1->is_klass(), ""); return klassOop(f1); } oop f1_as_klass_mirror() const { oop f1 = f1_as_instance(); return f1; } // i.e., return a java_mirror - oop f1_as_instance() const { oop f1 = _f1; assert(f1 == NULL || f1->is_instance() || f1->is_array(), ""); return f1; } + oop f1_as_instance() const { oop f1 = f1_ord(); assert(f1 == NULL || f1->is_instance() || f1->is_array(), ""); return f1; } oop f1_appendix() const { assert(has_appendix(), ""); return f1_as_instance(); } - bool is_f1_null() const { oop f1 = _f1; return f1 == NULL; } // classifies a CPC entry as unbound + // Use the accessor f1() to acquire _f1's value. This is needed for + // example in BytecodeInterpreter::run(), where is_f1_null() is + // called to check if an invokedynamic call is resolved. This load + // of _f1 must be ordered with the loads performed by + // cache->main_entry_index(). + bool is_f1_null() const { oop f1 = f1_ord(); return f1 == NULL; } // classifies a CPC entry as unbound int f2_as_index() const { assert(!is_vfinal(), ""); return (int) _f2; } methodOop f2_as_vfinal_method() const { assert(is_vfinal(), ""); return methodOop(_f2); } int field_index() const { assert(is_field_entry(), ""); return (_flags & field_index_mask); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/instanceKlass.cpp Sun May 21 11:12:48 2017 -0700 @@ -61,6 +61,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -824,7 +827,11 @@ MutexLocker x(OopMapCacheAlloc_lock); // First time use. Allocate a cache in C heap if (_oop_map_cache == NULL) { - _oop_map_cache = new OopMapCache(); + // Release stores from OopMapCache constructor before assignment + // to _oop_map_cache. C++ compilers on ppc do not emit the + // required memory barrier only because of the volatile + // qualifier of _oop_map_cache. + OrderAccess::release_store_ptr(&_oop_map_cache, new OopMapCache()); } } // _oop_map_cache is constant after init; lookup below does is own locking. @@ -1441,6 +1448,8 @@ // Retrieve a cached itable index int instanceKlass::cached_itable_index(size_t idnum) { + // First, acquire. + OrderAccess::acquire(); int* indices = methods_cached_itable_indices_acquire(); if (indices != NULL && ((size_t)indices[0]) > idnum) { // indices exist and are long enough, retrieve possible cached diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/markOop.cpp --- a/src/share/vm/oops/markOop.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/markOop.cpp Sun May 21 11:12:48 2017 -0700 @@ -33,6 +33,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/methodDataOop.cpp --- a/src/share/vm/oops/methodDataOop.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/methodDataOop.cpp Sun May 21 11:12:48 2017 -0700 @@ -350,6 +350,12 @@ } +#ifdef CC_INTERP +DataLayout* RetData::advance(methodDataOop mdo, int bci) { + return (DataLayout*) mdo->bci_to_dp(bci); +} +#endif // CC_INTERP + #ifndef PRODUCT void RetData::print_data_on(outputStream* st) { print_shared(st, "RetData"); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/methodDataOop.hpp --- a/src/share/vm/oops/methodDataOop.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/methodDataOop.hpp Sun May 21 11:12:48 2017 -0700 @@ -230,6 +230,11 @@ static ByteSize cell_offset(int index) { return byte_offset_of(DataLayout, _cells) + in_ByteSize(index * cell_size); } +#ifdef CC_INTERP + static int cell_offset_in_bytes(int index) { + return (int)offset_of(DataLayout, _cells[index]); + } +#endif // CC_INTERP // Return a value which, when or-ed as a byte into _flags, sets the flag. static int flag_number_to_byte_constant(int flag_number) { assert(0 <= flag_number && flag_number < flag_limit, "oob"); @@ -364,6 +369,41 @@ _data = data; } +#ifdef CC_INTERP + // Static low level accessors for DataLayout with ProfileData's semantics. + + static int cell_offset_in_bytes(int index) { + return DataLayout::cell_offset_in_bytes(index); + } + + static void increment_uint_at_no_overflow(DataLayout* layout, int index, + int inc = DataLayout::counter_increment) { + uint count = ((uint)layout->cell_at(index)) + inc; + if (count == 0) return; + layout->set_cell_at(index, (intptr_t) count); + } + + static int int_at(DataLayout* layout, int index) { + return (int)layout->cell_at(index); + } + + static int uint_at(DataLayout* layout, int index) { + return (uint)layout->cell_at(index); + } + + static oop oop_at(DataLayout* layout, int index) { + return (oop)layout->cell_at(index); + } + + static void set_oop_at(DataLayout* layout, int index, oop value) { + layout->set_cell_at(index, (intptr_t) value); + } + + static void set_flag_at(DataLayout* layout, int flag_number) { + layout->set_flag_at(flag_number); + } +#endif // CC_INTERP + public: // Constructor for invalid ProfileData. ProfileData(); @@ -513,6 +553,20 @@ return cell_offset(bit_cell_count); } +#ifdef CC_INTERP + static int bit_data_size_in_bytes() { + return cell_offset_in_bytes(bit_cell_count); + } + + static void set_null_seen(DataLayout* layout) { + set_flag_at(layout, null_seen_flag); + } + + static DataLayout* advance(DataLayout* layout) { + return (DataLayout*) (((address)layout) + (ssize_t)BitData::bit_data_size_in_bytes()); + } +#endif // CC_INTERP + #ifndef PRODUCT void print_data_on(outputStream* st); #endif @@ -557,6 +611,25 @@ set_uint_at(count_off, count); } +#ifdef CC_INTERP + static int counter_data_size_in_bytes() { + return cell_offset_in_bytes(counter_cell_count); + } + + static void increment_count_no_overflow(DataLayout* layout) { + increment_uint_at_no_overflow(layout, count_off); + } + + // Support counter decrementation at checkcast / subtype check failed. + static void decrement_count(DataLayout* layout) { + increment_uint_at_no_overflow(layout, count_off, -1); + } + + static DataLayout* advance(DataLayout* layout) { + return (DataLayout*) (((address)layout) + (ssize_t)CounterData::counter_data_size_in_bytes()); + } +#endif // CC_INTERP + #ifndef PRODUCT void print_data_on(outputStream* st); #endif @@ -627,6 +700,20 @@ return cell_offset(displacement_off_set); } +#ifdef CC_INTERP + static void increment_taken_count_no_overflow(DataLayout* layout) { + increment_uint_at_no_overflow(layout, taken_off_set); + } + + static DataLayout* advance_taken(DataLayout* layout) { + return (DataLayout*) (((address)layout) + (ssize_t)int_at(layout, displacement_off_set)); + } + + static uint taken_count(DataLayout* layout) { + return (uint) uint_at(layout, taken_off_set); + } +#endif // CC_INTERP + // Specific initialization. void post_initialize(BytecodeStream* stream, methodDataOop mdo); @@ -758,6 +845,43 @@ return adr_oop_at(receiver_cell_index(row)); } +#ifdef CC_INTERP + static int receiver_type_data_size_in_bytes() { + return cell_offset_in_bytes(static_cell_count()); + } + + static klassOop receiver_unchecked(DataLayout* layout, uint row) { + oop recv = oop_at(layout, receiver_cell_index(row)); + return (klassOop)recv; + } + + static void increment_receiver_count_no_overflow(DataLayout* layout, klassOop rcvr) { + const int num_rows = row_limit(); + // Receiver already exists? + for (int row = 0; row < num_rows; row++) { + if (receiver_unchecked(layout, row) == rcvr) { + increment_uint_at_no_overflow(layout, receiver_count_cell_index(row)); + return; + } + } + // New receiver, find a free slot. + for (int row = 0; row < num_rows; row++) { + if (receiver_unchecked(layout, row) == NULL) { + set_oop_at(layout, receiver_cell_index(row), rcvr); + increment_uint_at_no_overflow(layout, receiver_count_cell_index(row)); + return; + } + } + // Receiver did not match any saved receiver and there is no empty row for it. + // Increment total counter to indicate polymorphic case. + increment_count_no_overflow(layout); + } + + static DataLayout* advance(DataLayout* layout) { + return (DataLayout*) (((address)layout) + (ssize_t)ReceiverTypeData::receiver_type_data_size_in_bytes()); + } +#endif // CC_INTERP + #ifndef PRODUCT void print_receiver_data_on(outputStream* st); void print_data_on(outputStream* st); @@ -791,6 +915,16 @@ return cell_offset(static_cell_count()); } +#ifdef CC_INTERP + static int virtual_call_data_size_in_bytes() { + return cell_offset_in_bytes(static_cell_count()); + } + + static DataLayout* advance(DataLayout* layout) { + return (DataLayout*) (((address)layout) + (ssize_t)VirtualCallData::virtual_call_data_size_in_bytes()); + } +#endif // CC_INTERP + #ifndef PRODUCT void print_data_on(outputStream* st); #endif @@ -887,6 +1021,10 @@ return cell_offset(bci_displacement_cell_index(row)); } +#ifdef CC_INTERP + static DataLayout* advance(methodDataOop mdo, int bci); +#endif // CC_INTERP + // Specific initialization. void post_initialize(BytecodeStream* stream, methodDataOop mdo); @@ -951,6 +1089,20 @@ return cell_offset(branch_cell_count); } +#ifdef CC_INTERP + static int branch_data_size_in_bytes() { + return cell_offset_in_bytes(branch_cell_count); + } + + static void increment_not_taken_count_no_overflow(DataLayout* layout) { + increment_uint_at_no_overflow(layout, not_taken_off_set); + } + + static DataLayout* advance_not_taken(DataLayout* layout) { + return (DataLayout*) (((address)layout) + (ssize_t)BranchData::branch_data_size_in_bytes()); + } +#endif // CC_INTERP + // Specific initialization. void post_initialize(BytecodeStream* stream, methodDataOop mdo); @@ -990,6 +1142,20 @@ set_int_at(aindex, value); } +#ifdef CC_INTERP + // Static low level accessors for DataLayout with ArrayData's semantics. + + static void increment_array_uint_at_no_overflow(DataLayout* layout, int index) { + int aindex = index + array_start_off_set; + increment_uint_at_no_overflow(layout, aindex); + } + + static int array_int_at(DataLayout* layout, int index) { + int aindex = index + array_start_off_set; + return int_at(layout, aindex); + } +#endif // CC_INTERP + // Code generation support for subclasses. static ByteSize array_element_offset(int index) { return cell_offset(array_start_off_set + index); @@ -1108,6 +1274,28 @@ return in_ByteSize(relative_displacement_off_set) * cell_size; } +#ifdef CC_INTERP + static void increment_count_no_overflow(DataLayout* layout, int index) { + if (index == -1) { + increment_array_uint_at_no_overflow(layout, default_count_off_set); + } else { + increment_array_uint_at_no_overflow(layout, case_array_start + + index * per_case_cell_count + + relative_count_off_set); + } + } + + static DataLayout* advance(DataLayout* layout, int index) { + if (index == -1) { + return (DataLayout*) (((address)layout) + (ssize_t)array_int_at(layout, default_disaplacement_off_set)); + } else { + return (DataLayout*) (((address)layout) + (ssize_t)array_int_at(layout, case_array_start + + index * per_case_cell_count + + relative_displacement_off_set)); + } + } +#endif // CC_INTERP + // Specific initialization. void post_initialize(BytecodeStream* stream, methodDataOop mdo); @@ -1186,8 +1374,11 @@ // adjusted in the event of a change in control flow. // +CC_INTERP_ONLY(class BytecodeInterpreter;) + class methodDataOopDesc : public oopDesc { friend class VMStructs; + CC_INTERP_ONLY(friend class BytecodeInterpreter;) private: friend class ProfileData; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/methodOop.hpp --- a/src/share/vm/oops/methodOop.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/methodOop.hpp Sun May 21 11:12:48 2017 -0700 @@ -313,7 +313,10 @@ return _method_data; } void set_method_data(methodDataOop data) { - oop_store_without_check((oop*)&_method_data, (oop)data); + // The store into method must be released. On platforms without + // total store order (TSO) the reference may become visible before + // the initialization of data otherwise. + oop_store_without_check((volatile oop*)&_method_data, (oop)data); } // invocation counter diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/oop.cpp --- a/src/share/vm/oops/oop.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/oop.cpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/oop.inline.hpp --- a/src/share/vm/oops/oop.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/oop.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -526,9 +526,9 @@ return blueprint()->oop_is_conc_safe(this); } -inline void update_barrier_set(void* p, oop v) { +inline void update_barrier_set(void* p, oop v, bool release = false) { assert(oopDesc::bs() != NULL, "Uninitialized bs in oop!"); - oopDesc::bs()->write_ref_field(p, v); + oopDesc::bs()->write_ref_field(p, v, release); } template inline void update_barrier_set_pre(T* p, oop v) { @@ -541,7 +541,10 @@ } else { update_barrier_set_pre(p, v); oopDesc::encode_store_heap_oop(p, v); - update_barrier_set((void*)p, v); // cast away type + // always_do_update_barrier == false => + // Either we are at a safepoint (in GC) or CMS is not used. In both + // cases it's unnecessary to mark the card as dirty with release sematics. + update_barrier_set((void*)p, v, false /* release */); // cast away type } } @@ -549,7 +552,12 @@ update_barrier_set_pre((T*)p, v); // cast away volatile // Used by release_obj_field_put, so use release_store_ptr. oopDesc::release_encode_store_heap_oop(p, v); - update_barrier_set((void*)p, v); // cast away type + // When using CMS we must mark the card corresponding to p as dirty + // with release sematics to prevent that CMS sees the dirty card but + // not the new value v at p due to reordering of the two + // stores. Note that CMS has a concurrent precleaning phase, where + // it reads the card table while the Java threads are running. + update_barrier_set((void*)p, v, true /* release */); // cast away type } template inline void oop_store_without_check(T* p, oop v) { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/oopsHierarchy.cpp --- a/src/share/vm/oops/oopsHierarchy.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/oopsHierarchy.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/oops/typeArrayOop.hpp --- a/src/share/vm/oops/typeArrayOop.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/oops/typeArrayOop.hpp Sun May 21 11:12:48 2017 -0700 @@ -51,6 +51,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "orderAccess_linux_ppc.inline.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "orderAccess_aix_ppc.inline.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "orderAccess_bsd_x86.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/block.cpp --- a/src/share/vm/opto/block.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/block.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -153,6 +153,14 @@ _nodes.remove(find_node(n)); } +bool Block::contains( const Node *n ) const { + for( uint i = 0; i < _nodes.size(); i++ ) { + if( _nodes[i] == n ) + return true; + } + return false; +} + //------------------------------is_Empty--------------------------------------- // Return empty status of a block. Empty blocks contain only the head, other // ideal nodes, and an optional trailing goto. @@ -539,18 +547,27 @@ //------------------------------no_flip_branch--------------------------------- // Does this block end in a multiway branch that cannot have the default case // flipped for another case? -static bool no_flip_branch( Block *b ) { +static bool no_flip_branch(Block *b) { int branch_idx = b->_nodes.size() - b->_num_succs-1; - if( branch_idx < 1 ) return false; - Node *bra = b->_nodes[branch_idx]; - if( bra->is_Catch() ) + if (branch_idx < 1) { + return false; + } + Node *branch = b->_nodes[branch_idx]; + if (branch->is_Catch()) { return true; - if( bra->is_Mach() ) { - if( bra->is_MachNullCheck() ) + } + if (branch->is_Mach()) { + if (branch->is_MachNullCheck()) { return true; - int iop = bra->as_Mach()->ideal_Opcode(); - if( iop == Op_FastLock || iop == Op_FastUnlock ) + } + int iop = branch->as_Mach()->ideal_Opcode(); + if (iop == Op_FastLock || iop == Op_FastUnlock) { return true; + } + // Don't flip if branch has an implicit check. + if (branch->as_Mach()->is_TrapBasedCheckNode()) { + return true; + } } return false; } @@ -707,11 +724,67 @@ } // End of for all blocks } +Block *PhaseCFG::fixup_trap_based_check(Node *branch, Block *block, int block_pos, Block *bnext) { + // Trap based checks must fall through to the successor with + // PROB_ALWAYS. + // They should be an If with 2 successors. + assert(branch->is_MachIf(), "must be If"); + assert(block->_num_succs == 2, "must have 2 successors"); + + // Get the If node and the projection for the first successor. + MachIfNode *iff = block->_nodes[block->_nodes.size()-3]->as_MachIf(); + ProjNode *proj0 = block->_nodes[block->_nodes.size()-2]->as_Proj(); + ProjNode *proj1 = block->_nodes[block->_nodes.size()-1]->as_Proj(); + ProjNode *projt = (proj0->Opcode() == Op_IfTrue) ? proj0 : proj1; + ProjNode *projf = (proj0->Opcode() == Op_IfFalse) ? proj0 : proj1; + + // Assert that proj0 and succs[0] match up. Similarly for proj1 and succs[1]. + assert(proj0->raw_out(0) == block->_succs[0]->head(), "Mismatch successor 0"); + assert(proj1->raw_out(0) == block->_succs[1]->head(), "Mismatch successor 1"); + + ProjNode *proj_always; + ProjNode *proj_never; + // We must negate the branch if the implicit check doesn't follow + // the branch's TRUE path. Then, the new TRUE branch target will + // be the old FALSE branch target. + if (iff->_prob <= 2*PROB_NEVER) { // There are small rounding errors. + proj_never = projt; + proj_always = projf; + } else { + // We must negate the branch if the trap doesn't follow the + // branch's TRUE path. Then, the new TRUE branch target will + // be the old FALSE branch target. + proj_never = projf; + proj_always = projt; + iff->negate(); + } + assert(iff->_prob <= 2*PROB_NEVER, "Trap based checks are expected to trap never!"); + // Map the successors properly + block->_succs.map(0, _bbs[proj_never ->raw_out(0)->_idx]); // The target of the trap. + block->_succs.map(1, _bbs[proj_always->raw_out(0)->_idx]); // The fall through target. + + if (block->_nodes[block->_nodes.size() - block->_num_succs + 1] != proj_always) { + block->_nodes.map(block->_nodes.size() - block->_num_succs + 0, proj_never); + block->_nodes.map(block->_nodes.size() - block->_num_succs + 1, proj_always); + } + + // Place the fall through block after this block. + Block *bs1 = block->non_connector_successor(1); + if (bs1 != bnext && move_to_next(bs1, block_pos)) { + bnext = bs1; + } + // If the fall through block still is not the next block, insert a goto. + if (bs1 != bnext) { + insert_goto_at(block_pos, 1); + } + return bnext; +} + //-----------------------------fixup_flow-------------------------------------- // Fix up the final control flow for basic blocks. void PhaseCFG::fixup_flow() { // Fixup final control flow for the blocks. Remove jump-to-next - // block. If neither arm of a IF follows the conditional branch, we + // block. If neither arm of an IF follows the conditional branch, we // have to add a second jump after the conditional. We place the // TRUE branch target in succs[0] for both GOTOs and IFs. for (uint i=0; i < _num_blocks; i++) { @@ -732,26 +805,40 @@ // Check for multi-way branches where I cannot negate the test to // exchange the true and false targets. - if( no_flip_branch( b ) ) { + if (no_flip_branch(b)) { // Find fall through case - if must fall into its target + // Get the index of the branch's first successor. int branch_idx = b->_nodes.size() - b->_num_succs; - for (uint j2 = 0; j2 < b->_num_succs; j2++) { - const ProjNode* p = b->_nodes[branch_idx + j2]->as_Proj(); - if (p->_con == 0) { - // successor j2 is fall through case - if (b->non_connector_successor(j2) != bnext) { - // but it is not the next block => insert a goto - insert_goto_at(i, j2); + + // The branch is 1 before the branch's first successor. + Node *bra = b->_nodes[branch_idx-1]; + + // Handle no-flip branches which have implicit checks and which require + // special block ordering and individual semantics of the 'fall through + // case'. + if ((TrapBasedNullChecks || TrapBasedRangeChecks) && + bra->is_Mach() && bra->as_Mach()->is_TrapBasedCheckNode()) { + bnext = fixup_trap_based_check(bra, b, i, bnext); + } else { + // Else, default handling for no-flip branches + for (uint j2 = 0; j2 < b->_num_succs; j2++) { + const ProjNode* p = b->_nodes[branch_idx + j2]->as_Proj(); + if (p->_con == 0) { + // successor j2 is fall through case + if (b->non_connector_successor(j2) != bnext) { + // but it is not the next block => insert a goto + insert_goto_at(i, j2); + } + // Put taken branch in slot 0 + if (j2 == 0 && b->_num_succs == 2) { + // Flip targets in succs map + Block *tbs0 = b->_succs[0]; + Block *tbs1 = b->_succs[1]; + b->_succs.map(0, tbs1); + b->_succs.map(1, tbs0); + } + break; } - // Put taken branch in slot 0 - if( j2 == 0 && b->_num_succs == 2) { - // Flip targets in succs map - Block *tbs0 = b->_succs[0]; - Block *tbs1 = b->_succs[1]; - b->_succs.map( 0, tbs1 ); - b->_succs.map( 1, tbs0 ); - } - break; } } // Remove all CatchProjs @@ -853,6 +940,224 @@ } +// LateExpand: Expand nodes after register allocation. +// +// LateExpand has to be called after register allocation, just before +// output (i.e. scheduling). It only gets called if +// Matcher::require_late_expand is true, which it is only on ia64 and +// ppc64. +// +// Background: +// +// Nodes that are expandend (one compound node requiring several +// assembler instructions to be implemented split into two or more +// non-compound nodes) after register allocation are not as nice as +// the ones expanded before register allocation - they don't +// participate in optimizations as global code motion. But after +// register allocation we can expand nodes that use registers which +// are not spillable or registers that are not allocated, because the +// old compound node is simply replaced (in its location in the basic +// block) by a new subgraph which does not contain compound nodes any +// more. The scheduler called during output can later on process these +// non-compound nodes. +// +// Implementation: +// +// Nodes requiring late expand are specified in the ad file by using +// an lateExpand statement instead of ins_encode. An +// lateExpand contains a single call to an encoding, as does an +// ins_encode statement. Instead of an emit() emit function a +// lateExpand() function is generated that doesn't emit assembler but +// creates a new subgraph. The code below calls this lateExpand +// function for each node with the appropriate attribute. This +// function returns the new nodes generated in an array passed in the +// call. The old node, potential MachTemps before and potential Projs +// after it then get disconnected and replaced by the new nodes. The +// instruction generating the result has to be the last one in the +// array. In general it is assumed that Projs after the node expanded +// are kills. These kills are not required any more after expanding as +// there are now explicitly visible def-use chains and the Projs are +// removed. This does not hold for calls: They do not only have +// kill-Projs but also Projs defining values. Therefore Projs after +// the node expanded are removed for all but for calls. If a node is +// to be reused, it must be added to the nodes list returned, and it +// will be added again. +// +// Implementing the lateExpand function for a node in an enc_class is rather +// tedious. It requires knowledge about many node details, as the nodes and the +// subgraph must be hand crafted. To simplify this, adlc generates some +// utility variables into the lateExpand function, e.g., holding the +// operands as specified by the lateExpand encoding specification, e.g.: +// * unsigned idx_ holding the index of the node in the ins +// * Node *n_ holding the node loaded from the ins +// * MachOpnd *op_ holding the corresponding operand +// +// The ordering of operands can not be determined by looking at a +// rule. Especially if a match rule matches several different trees, +// several nodes are generated from one instruct specification with +// different operand orderings (see ia64.ad, +// cmpLTMask_AndI_AddI_reg_zero_reg_reg). In this case the adlc generated +// variables are the only way to access the ins and operands deterministically. +// +// If assigning a register to a node that contains an oop, don't +// forget to call ra_->set_oop() for the node. +void PhaseCFG::LateExpand(PhaseRegAlloc* _ra) { + GrowableArray new_nodes(32); // Array with new nodes filled by lateExpand function of node. + GrowableArray remove(32); + GrowableArray succs(32); + unsigned int max_idx = C->unique(); // Remember to distinguish new from old nodes. + NOT_PRODUCT(bool foundNode = false;) + + // for all blocks + for (uint i = 0; i < _num_blocks; i++) { + Block *b = _blocks[i]; + // For all instructions in the current block. + for (uint j = 0; j < b->_nodes.size(); j++) { + Node *n = b->_nodes[j]; + if (n->is_Mach() && n->as_Mach()->requires_late_expand()) { +#ifndef PRODUCT + if (TraceLateExpand) { + if (!foundNode) { + foundNode = true; + // TODO: PPC port tty->print("COMPILING %s\n", C->method()->name_and_sig_as_C_string()); + } + tty->print(" late expanding "); + n->dump(1); + } +#endif + new_nodes.clear(); + // Collect nodes that have to be removed from the block later on. + uint req = n->req(); + remove.clear(); + for (uint k = 0; k < req; ++k) { + if (n->in(k) && n->in(k)->is_MachTemp()) { + remove.push(n->in(k)); // MachTemps which are inputs to the old node have to be removed. + n->in(k)->del_req(0); + j--; + } + } + + // Check whether we can allocate enough nodes. We set a fix limit for + // the size of late expands with this. + uint unique_limit = C->unique() + 40; + if (unique_limit >= _ra->node_regs_max_index()) { + Compile::current()->record_failure("out of nodes in late expand"); + return; + } + + // Emit (i.e. generate new nodes). + n->lateExpand(&new_nodes, _ra); + + assert(C->unique() < unique_limit, "You allocated too many nodes in your late expand."); + + // Disconnect the inputs of the old node. + // + // We reuse MachSpillCopy nodes. If we need to expand them, there + // are many, so reusing pays off. If reused, the node already + // has the new ins. n must be the last node on new_nodes list. + if (!n->is_MachSpillCopy()) { + for (int k = req - 1; k >= 0; --k) { + n->del_req(k); + } + } + + // Add a comment to all new nodes that shows from which node they are derived. + // TODO: PPC port add_comments(C, &new_nodes, n, max_idx); + +#ifndef PRODUCT + // Check that all nodes have proper operands. + for (int k = 0; k < new_nodes.length(); ++k) { + if (new_nodes.at(k)->_idx < max_idx || !new_nodes.at(k)->is_Mach()) continue; // old node, Proj ... + MachNode *m = new_nodes.at(k)->as_Mach(); + for (unsigned int l = 0; l < m->num_opnds(); ++l) { + if (MachOper::notAnOper(m->_opnds[l])) { + outputStream *os = tty; // TODO: PPC port Compile::current()->get_inline_trace(); + os->print("Node %s ", m->Name()); + // TODO: PPC port Node_Notes *nn = Compile::current()->node_notes_at(m->_idx); + // TODO: PPC port if (nn && nn->comment()) os->print("(%s) ", nn->comment()); + os->print("has invalid opnd %d: %p\n", l, m->_opnds[l]); + assert(0, "Invalid operands, see inline trace in hs_err_pid file."); + } + } + } +#endif + + // Collect succs of old node in remove (for projections) and in succs (for + // all other nodes) do _not_ collect projections in remove (but in succs) + // in case the node is a call. We need the projections for calls as they are + // associated with registes (i.e. they are defs). + succs.clear(); + for (DUIterator k = n->outs(); n->has_out(k); k++) { + if (n->out(k)->is_Proj() && !n->is_MachCall() && !n->is_MachBranch()) { + remove.push(n->out(k)); + } else { + succs.push(n->out(k)); + } + } + // Replace old node n as input of its succs by last of the new nodes. + for (int k = 0; k < succs.length(); ++k) { + Node *succ = succs.at(k); + for (uint l = 0; l < succ->req(); ++l) { + if (succ->in(l) == n) { + succ->set_req(l, new_nodes.at(new_nodes.length() - 1)); + } + } + for (uint l = succ->req(); l < succ->len(); ++l) { + if (succ->in(l) == n) { + succ->set_prec(l, new_nodes.at(new_nodes.length() - 1)); + } + } + } + + // Index of old node in block. + uint index = b->find_node(n); + // Insert new nodes into block and map them in nodes->blocks array + // and remember last node in n2. + Node *n2 = NULL; + for (int k = 0; k < new_nodes.length(); ++k) { + n2 = new_nodes.at(k); + b->_nodes.insert(++index, n2); + _bbs.map(n2->_idx, b); + } + + // Add old node n to remove and remove them all from block. + remove.push(n); + j--; +#ifndef PRODUCT + if (TraceLateExpand && WizardMode) { + tty->print(" removing:\n"); + for (int k = 0; k < remove.length(); ++k) { + tty->print(" "); remove.at(k)->dump(); + } + tty->print(" inserting:\n"); + for (int k = 0; k < new_nodes.length(); ++k) { + tty->print(" "); new_nodes.at(k)->dump(); + } + } +#endif + for (int k = 0; k < remove.length(); ++k) { + if (b->contains(remove.at(k))) { + b->find_remove(remove.at(k)); + } else { + assert(remove.at(k)->is_Proj() && (remove.at(k)->in(0)->is_MachBranch()), ""); + } + } + // If anything has been inserted (n2 != NULL), continue after last node inserted. + // This does not always work. Some late expands don't insert any nodes, if they + // do optimizations (e.g., max(x,x)). In this case we decrement j accordingly. + j = n2 ? b->find_node(n2) : j; + } + } + } + +#if 0 // TODO: PPC port ifndef PRODUCT + if (foundNode) { + tty->print("FINISHED %s\n", Compile::current()->method()->name_and_sig_as_C_string()); + } +#endif +} + + //------------------------------dump------------------------------------------- #ifndef PRODUCT void PhaseCFG::_dump_cfg( const Node *end, VectorSet &visited ) const { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/block.hpp --- a/src/share/vm/opto/block.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/block.hpp Sun May 21 11:12:48 2017 -0700 @@ -276,10 +276,12 @@ // Add an instruction to an existing block. It must go after the head // instruction and before the end instruction. void add_inst( Node *n ) { _nodes.insert(end_idx(),n); } - // Find node in block + // Find node in block. Fails if node not in block. uint find_node( const Node *n ) const; // Find and remove n from block list void find_remove( const Node *n ); + // Check wether the node is in the block. + bool contains( const Node *n ) const; // helper function that adds caller save registers to MachProjNode void add_call_kills(MachProjNode *proj, RegMask& regs, const char* save_policy, bool exclude_soe); @@ -437,6 +439,7 @@ // Remove empty basic blocks void remove_empty(); + Block *fixup_trap_based_check(Node *branch, Block *block, int block_pos, Block *bnext); void fixup_flow(); bool move_to_next(Block* bx, uint b_index); void move_to_end(Block* bx, uint b_index); @@ -457,6 +460,9 @@ _bbs.map( n->_idx, b ); } + // Check all nodes and late expand them if necessary. + void LateExpand(PhaseRegAlloc* _ra); + #ifndef PRODUCT bool trace_opto_pipelining() const { return _trace_opto_pipelining; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/c2_globals.hpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_ARCH_arm # include "c2_globals_arm.hpp" #endif +#ifdef TARGET_ARCH_ppc +# include "c2_globals_ppc.hpp" +#endif #ifdef TARGET_OS_FAMILY_linux # include "c2_globals_linux.hpp" #endif @@ -44,6 +47,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "c2_globals_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "c2_globals_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "c2_globals_bsd.hpp" #endif @@ -217,7 +223,9 @@ diagnostic(bool, UnrollLimitCheck, true, \ "Additional overflow checks during loop unroll") \ \ - product(bool, OptimizeFill, true, \ + /* On Power, with optimize fill we can not parse xml files, e.g. after */ \ + /* jbb2005, 'An invalid XML character (Unicode: 0x2e) was found ...'. */ \ + product(bool, OptimizeFill, true PPC64_ONLY(&& false), \ "convert fill/copy loops into intrinsic") \ \ develop(bool, TraceOptimizeFill, false, \ @@ -445,6 +453,8 @@ product(intx, AutoBoxCacheMax, 128, \ "Sets max value cached by the java.lang.Integer autobox cache") \ \ + develop(bool, TraceLateExpand, false, "Trace late expand of nodes.") \ + \ product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/c2compiler.cpp --- a/src/share/vm/opto/c2compiler.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/c2compiler.cpp Sun May 21 11:12:48 2017 -0700 @@ -40,8 +40,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/callGenerator.cpp --- a/src/share/vm/opto/callGenerator.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/callGenerator.cpp Sun May 21 11:12:48 2017 -0700 @@ -201,7 +201,7 @@ // Block::implicit_null_check() only looks for loads and stores, not calls. ciMethod *caller = kit.method(); ciMethodData *caller_md = (caller == NULL) ? NULL : caller->method_data(); - if (!UseInlineCaches || !ImplicitNullChecks || + if (!UseInlineCaches || !ImplicitNullChecks || !os::zero_page_read_protected() || ((ImplicitNullCheckThreshold > 0) && caller_md && (caller_md->trap_count(Deoptimization::Reason_null_check) >= (uint)ImplicitNullCheckThreshold))) { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/callnode.cpp --- a/src/share/vm/opto/callnode.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/callnode.cpp Sun May 21 11:12:48 2017 -0700 @@ -342,7 +342,7 @@ st->print(" %s%d]=#"INT32_FORMAT,msg,i,t->is_int()->get_con()); break; case Type::AnyPtr: - assert( t == TypePtr::NULL_PTR, "" ); + assert( t == TypePtr::NULL_PTR || n->in_dump(), "" ); st->print(" %s%d]=#NULL",msg,i); break; case Type::AryPtr: @@ -839,7 +839,7 @@ if (!(call->req() > TypeFunc::Parms && call->in(TypeFunc::Parms) != NULL && call->in(TypeFunc::Parms)->is_Con())) { - assert(_in_dump_cnt != 0, "OK if dumping"); + assert(in_dump() != 0, "OK if dumping"); tty->print("[bad uncommon trap]"); return 0; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/chaitin.cpp --- a/src/share/vm/opto/chaitin.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/chaitin.cpp Sun May 21 11:12:48 2017 -0700 @@ -382,9 +382,9 @@ // If we spill, split and recycle the entire thing while( spills ) { - if( _trip_cnt++ > 24 ) { + if( _trip_cnt++ > (24 PPC64_ONLY(+23)) ) { DEBUG_ONLY( dump_for_spill_split_recycle(); ) - if( _trip_cnt > 27 ) { + if( _trip_cnt > (27 PPC64_ONLY(+23))) { C->record_method_not_compilable("failed spill-split-recycle sanity check"); return; } @@ -622,7 +622,7 @@ // processes as vector in RA. if (RegMask::is_vector(ireg)) lrg._is_vector = 1; - assert(n_type->isa_vect() == NULL || lrg._is_vector || ireg == Op_RegD, + assert(n_type->isa_vect() == NULL || lrg._is_vector || ireg == Op_RegD || ireg == Op_RegL, "vector must be in vector registers"); // Check for bound register masks @@ -819,7 +819,7 @@ int kreg = n->in(k)->ideal_reg(); bool is_vect = RegMask::is_vector(kreg); assert(n->in(k)->bottom_type()->isa_vect() == NULL || - is_vect || kreg == Op_RegD, + is_vect || kreg == Op_RegD || kreg == Op_RegL, "vector must be in vector registers"); if (lrgmask.is_bound(kreg)) lrg._is_bound = 1; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/compile.cpp Sun May 21 11:12:48 2017 -0700 @@ -78,8 +78,23 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" +#endif +#ifdef TARGET_ARCH_x86 +# include "compile_x86.hpp" +#endif +#ifdef TARGET_ARCH_sparc +# include "compile_sparc.hpp" +#endif +#ifdef TARGET_ARCH_zero +# include "compile_zero.hpp" +#endif +#ifdef TARGET_ARCH_ppc +# include "compile_ppc.hpp" #endif @@ -568,6 +583,9 @@ buf.consts()->initialize_shared_locs(&locs_buf[lsize * 0], lsize); buf.insts()->initialize_shared_locs( &locs_buf[lsize * 1], lsize); buf.stubs()->initialize_shared_locs( &locs_buf[lsize * 2], lsize); + buf.consts()->set_scratch_emit(); + buf.insts() ->set_scratch_emit(); + buf.stubs() ->set_scratch_emit(); // Do the emission. @@ -632,6 +650,7 @@ _dead_node_count(0), #ifndef PRODUCT _trace_opto_output(TraceOptoOutput || method()->has_option("TraceOptoOutput")), + _in_dump_cnt(0), _printer(IdealGraphPrinter::printer()), #endif _congraph(NULL), @@ -919,6 +938,7 @@ _inner_loops(0), #ifndef PRODUCT _trace_opto_output(TraceOptoOutput), + _in_dump_cnt(0), _printer(NULL), #endif _dead_node_list(comp_arena()), @@ -1039,6 +1059,15 @@ Copy::zero_to_bytes(_trap_hist, sizeof(_trap_hist)); set_decompile_count(0); + // Compute when not to trap. Used by matching trap based nodes and + // NullCheck optimization. + _allowed_reasons = 0; + for (int rs = (int)Deoptimization::Reason_none+1; rs < Compile::trapHistLength; rs++) { + assert(rs < BitsPerInt, "recode bit map"); + if (!C->too_many_traps((Deoptimization::DeoptReason) rs)) + _allowed_reasons |= nth_bit(rs); + } + set_do_freq_based_layout(BlockLayoutByFrequency || method_has_option("BlockLayoutByFrequency")); set_num_loop_opts(LoopOptsCount); set_do_inlining(Inline); @@ -2121,6 +2150,9 @@ check_node_count(0, "out of nodes matching instructions"); if (failing()) return; + // Platform dependent post matching hook (used on ppc). + PdCompile::pd_post_matching_hook(this); + // Build a proper-looking CFG PhaseCFG cfg(node_arena(), root(), m); _cfg = &cfg; @@ -2186,6 +2218,11 @@ peep.do_transform(); } + // Do late expand if CPU requires this. + if (Matcher::require_late_expand) { + cfg.LateExpand(_regalloc); + } + // Convert Nodes to instruction bits in a buffer { // %%%% workspace merge brought two timers together for one job diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/compile.hpp Sun May 21 11:12:48 2017 -0700 @@ -61,6 +61,7 @@ class Node_Array; class Node_Notes; class OptoReg; +class PdCompile; class PhaseCFG; class PhaseGVN; class PhaseIterGVN; @@ -88,6 +89,7 @@ class Compile : public Phase { friend class VMStructs; + friend class PdCompile; // Platform dependent compile hooks. public: // Fixed alias indexes. (See also MergeMemNode.) @@ -121,7 +123,7 @@ const TypePtr* _adr_type; // normalized address type ciField* _field; // relevant instance field, or null if none bool _is_rewritable; // false if the memory is write-once only - int _general_index; // if this is type is an instance, the general + int _general_index; // if this type is an instance, the general // type that this is an instance of void Init(int i, const TypePtr* at); @@ -552,6 +554,7 @@ bool trace_opto_output() const { return _trace_opto_output; } bool parsed_irreducible_loop() const { return _parsed_irreducible_loop; } void set_parsed_irreducible_loop(bool z) { _parsed_irreducible_loop = z; } + int _in_dump_cnt; // Required for dumping ir nodes. #endif bool has_irreducible_loop() const { return _has_irreducible_loop; } void set_has_irreducible_loop(bool z) { _has_irreducible_loop = z; } @@ -808,6 +811,10 @@ ciMethodData* logmd = NULL); // Report if there were too many recompiles at a method and bci. bool too_many_recompiles(ciMethod* method, int bci, Deoptimization::DeoptReason reason); + // Return a bitset with the reasons where deoptimization is allowed, + // i.e., where there were not too many uncommon traps. + int _allowed_reasons; + int allowed_deopt_reasons() { return _allowed_reasons; } // Parsing, optimization PhaseGVN* initial_gvn() { return _initial_gvn; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/gcm.cpp --- a/src/share/vm/opto/gcm.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/gcm.cpp Sun May 21 11:12:48 2017 -0700 @@ -50,9 +50,13 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" #endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" +#endif + // Portions of code courtesy of Clifford Click diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/generateOptoStub.cpp --- a/src/share/vm/opto/generateOptoStub.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/generateOptoStub.cpp Sun May 21 11:12:48 2017 -0700 @@ -46,6 +46,10 @@ bool return_pc) { ResourceMark rm; + // Do we need to convert ints to longs for c calls? + const bool convert_ints_to_longs = + SharedRuntime::c_calling_convention_requires_ints_as_longs(); + const TypeTuple *jdomain = C->tf()->domain(); const TypeTuple *jrange = C->tf()->range(); @@ -88,12 +92,12 @@ thread, in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::last_Java_pc_offset())); -#if defined(SPARC) || defined(IA64) +#if defined(SPARC) || (defined(IA64) && !defined(AIX)) Node* adr_flags = basic_plus_adr(top(), thread, in_bytes(JavaThread::frame_anchor_offset()) + in_bytes(JavaFrameAnchor::flags_offset())); -#endif /* defined(SPARC) || defined(IA64) */ +#endif // Drop in the last_Java_sp. last_Java_fp is not touched. @@ -102,7 +106,7 @@ // users will look at the other fields. // Node *adr_sp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_sp_offset())); -#ifndef IA64 +#if !defined(IA64) || defined(AIX) Node *last_sp = basic_plus_adr(top(), frameptr(), (intptr_t) STACK_BIAS); store_to_memory(NULL, adr_sp, last_sp, T_ADDRESS, NoAlias); #endif @@ -111,7 +115,6 @@ // The order of stores into TLS is critical! Setting _thread_in_native MUST // be last, because a GC is allowed at any time after setting it and the GC // will require last_Java_pc and last_Java_sp. - Node* adr_state = basic_plus_adr(top(), thread, in_bytes(JavaThread::thread_state_offset())); //----------------------------- // Compute signature for C call. Varies from the Java signature! @@ -119,8 +122,16 @@ uint cnt = TypeFunc::Parms; // The C routines gets the base of thread-local storage passed in as an // extra argument. Not all calls need it, but its cheap to add here. - for( ; cntfield_at(cnt); + for (uint pcnt = cnt; pcnt < parm_cnt; pcnt++, cnt++) { + // Convert ints to longs if required. + if (convert_ints_to_longs && jdomain->field_at(pcnt)->isa_int()) { + fields[cnt++] = TypeLong::LONG; + fields[cnt] = Type::HALF; // must add an additional half for a long + } else { + fields[cnt] = jdomain->field_at(pcnt); + } + } + fields[cnt++] = TypeRawPtr::BOTTOM; // Thread-local storage // Also pass in the caller's PC, if asked for. if( return_pc ) @@ -171,12 +182,20 @@ // Set fixed predefined input arguments cnt = 0; - for( i=0; iinit_req( cnt++, map()->in(i) ); + for (i = 0; i < TypeFunc::Parms; i++) + call->init_req(cnt++, map()->in(i)); // A little too aggressive on the parm copy; return address is not an input call->set_req(TypeFunc::ReturnAdr, top()); - for( ; iinit_req( cnt++, map()->in(i) ); + for (; i < parm_cnt; i++) { // Regular input arguments + // Convert ints to longs if required. + if (convert_ints_to_longs && jdomain->field_at(i)->isa_int()) { + Node* int_as_long = _gvn.transform(new (C) ConvI2LNode(map()->in(i))); + call->init_req(cnt++, int_as_long); // long + call->init_req(cnt++, top()); // half + } else { + call->init_req(cnt++, map()->in(i)); + } + } call->init_req( cnt++, thread ); if( return_pc ) // Return PC, if asked for @@ -209,29 +228,21 @@ //----------------------------- - // Clear last_Java_sp -#ifdef IA64 - if( os::is_MP() ) insert_mem_bar(Op_MemBarRelease); + // Clear last_Java_sp. + store_to_memory(NULL, adr_sp, null(), T_ADDRESS, NoAlias); + // Clear last_Java_pc and (optionally)_flags0. + store_to_memory(NULL, adr_last_Java_pc, null(), T_ADDRESS, NoAlias); +#if defined(SPARC) || (defined(IA64) && !defined(AIX)) + store_to_memory(NULL, adr_flags, intcon(0), T_INT, NoAlias); +#endif +#if (defined(IA64) && !defined(AIX)) + Node* adr_last_Java_fp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_fp_offset())); + store_to_memory(NULL, adr_last_Java_fp, null(), T_ADDRESS, NoAlias); #endif - store_to_memory(NULL, adr_sp, null(), T_ADDRESS, NoAlias); -#ifdef IA64 - if (os::is_MP() && UseMembar) insert_mem_bar(new MemBarVolatileNode()); -#endif // def IA64 - // Clear last_Java_pc and (optionally)_flags - store_to_memory(NULL, adr_last_Java_pc, null(), T_ADDRESS, NoAlias); -#if defined(SPARC) || defined(IA64) - store_to_memory(NULL, adr_flags, intcon(0), T_INT, NoAlias); -#endif /* defined(SPARC) || defined(IA64) */ -#ifdef IA64 - Node* adr_last_Java_fp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_fp_offset())); - if( os::is_MP() ) insert_mem_bar(Op_MemBarRelease); - store_to_memory(NULL, adr_last_Java_fp, null(), T_ADDRESS, NoAlias); -#endif - - // For is-fancy-jump, the C-return value is also the branch target + // For is-fancy-jump, the C-return value is also the branch target. Node* target = map()->in(TypeFunc::Parms); - // Runtime call returning oop in TLS? Fetch it out + // Runtime call returning oop in TLS? Fetch it out. if( pass_tls ) { Node* adr = basic_plus_adr(top(), thread, in_bytes(JavaThread::vm_result_offset())); Node* vm_result = make_load(NULL, adr, TypeOopPtr::BOTTOM, T_OBJECT, NoAlias, false); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/graphKit.cpp Sun May 21 11:12:48 2017 -0700 @@ -595,7 +595,8 @@ Node *adr = basic_plus_adr(ex_node, ex_node, offset); const TypeOopPtr* val_type = TypeOopPtr::make_from_klass(env()->String_klass()); - Node *store = store_oop_to_object(control(), ex_node, adr, adr_typ, null(), val_type, T_OBJECT); + // Conservatively release stores of object references. + Node *store = store_oop_to_object(control(), ex_node, adr, adr_typ, null(), val_type, T_OBJECT, StoreNode::release); add_exception_state(make_exception_state(ex_node)); return; @@ -1438,32 +1439,33 @@ // factory methods in "int adr_idx" Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx, - bool require_atomic_access) { + bool require_atomic_access, LoadNode::Sem sem) { assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); const TypePtr* adr_type = NULL; // debug-mode-only argument debug_only(adr_type = C->get_adr_type(adr_idx)); Node* mem = memory(adr_idx); Node* ld; if (require_atomic_access && bt == T_LONG) { - ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t); + ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t, sem); } else { - ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt); + ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, sem); } return _gvn.transform(ld); } Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, - bool require_atomic_access) { + bool require_atomic_access, + StoreNode::Sem sem) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); const TypePtr* adr_type = NULL; debug_only(adr_type = C->get_adr_type(adr_idx)); Node *mem = memory(adr_idx); Node* st; if (require_atomic_access && bt == T_LONG) { - st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val); + st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val, sem); } else { - st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt); + st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, sem); } st = _gvn.transform(st); set_memory(st, adr_idx); @@ -1544,7 +1546,8 @@ Node* val, const TypeOopPtr* val_type, BasicType bt, - bool use_precise) { + bool use_precise, + StoreNode::Sem sem) { // Transformation of a value which could be NULL pointer (CastPP #NULL) // could be delayed during Parse (for example, in adjust_map_after_if()). // Execute transformation here to avoid barrier generation in such case. @@ -1564,7 +1567,7 @@ NULL /* pre_val */, bt); - Node* store = store_to_memory(control(), adr, val, bt, adr_idx); + Node* store = store_to_memory(control(), adr, val, bt, adr_idx, false, sem); post_barrier(control(), store, obj, adr, adr_idx, val, bt, use_precise); return store; } @@ -1575,7 +1578,8 @@ Node* adr, // actual adress to store val at const TypePtr* adr_type, Node* val, - BasicType bt) { + BasicType bt, + StoreNode::Sem sem) { Compile::AliasType* at = C->alias_type(adr_type); const TypeOopPtr* val_type = NULL; if (adr_type->isa_instptr()) { @@ -1594,7 +1598,7 @@ if (val_type == NULL) { val_type = TypeInstPtr::BOTTOM; } - return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true); + return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true, sem); } @@ -3636,7 +3640,7 @@ // Now get the buffer location we will log the previous value into and store it Node *log_addr = __ AddP(no_base, buffer, next_index); - __ store(__ ctrl(), log_addr, pre_val, T_OBJECT, Compile::AliasIdxRaw); + __ store(__ ctrl(), log_addr, pre_val, T_OBJECT, Compile::AliasIdxRaw, StoreNode::release); // update the index __ store(__ ctrl(), index_adr, next_index, index_bt, Compile::AliasIdxRaw); @@ -3678,8 +3682,8 @@ Node* next_index = _gvn.transform(new (C) SubXNode(index, __ ConX(sizeof(intptr_t)))); Node* log_addr = __ AddP(no_base, buffer, next_index); - __ store(__ ctrl(), log_addr, card_adr, T_ADDRESS, Compile::AliasIdxRaw); - __ store(__ ctrl(), index_adr, next_index, TypeX_X->basic_type(), Compile::AliasIdxRaw); + __ store(__ ctrl(), log_addr, card_adr, T_ADDRESS, Compile::AliasIdxRaw, StoreNode::release); + __ store(__ ctrl(), index_adr, next_index, TypeX_X->basic_type(), Compile::AliasIdxRaw, StoreNode::release); } __ else_(); { __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), "g1_wb_post", card_adr, __ thread()); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/graphKit.hpp --- a/src/share/vm/opto/graphKit.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/graphKit.hpp Sun May 21 11:12:48 2017 -0700 @@ -486,37 +486,51 @@ // Create a LoadNode, reading from the parser's memory state. // (Note: require_atomic_access is useful only with T_LONG.) + // + // We choose the unordered semantics by default because we have + // adapted the `do_put_xxx' and `do_get_xxx' procedures for the case + // of volatile fields. Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, - bool require_atomic_access = false) { + bool require_atomic_access = false, LoadNode::Sem sem = LoadNode::unordered) { // This version computes alias_index from bottom_type return make_load(ctl, adr, t, bt, adr->bottom_type()->is_ptr(), - require_atomic_access); + require_atomic_access, sem); } - Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, const TypePtr* adr_type, bool require_atomic_access = false) { + Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, const TypePtr* adr_type, + bool require_atomic_access = false, LoadNode::Sem sem = LoadNode::unordered) { // This version computes alias_index from an address type assert(adr_type != NULL, "use other make_load factory"); return make_load(ctl, adr, t, bt, C->get_alias_index(adr_type), - require_atomic_access); + require_atomic_access, sem); } // This is the base version which is given an alias index. - Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx, bool require_atomic_access = false); + Node* make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, int adr_idx, + bool require_atomic_access = false, LoadNode::Sem sem = LoadNode::unordered); // Create & transform a StoreNode and store the effect into the // parser's memory state. + // + // We must ensure that stores of object references will be visible + // only after the object's initialization. So the clients of this + // procedure must indicate that the store requires `release' + // semantics, if the stored value is an object reference that might + // point to a new object and may become externally visible. Node* store_to_memory(Node* ctl, Node* adr, Node* val, BasicType bt, const TypePtr* adr_type, - bool require_atomic_access = false) { + bool require_atomic_access = false, + StoreNode::Sem sem = StoreNode::unordered) { // This version computes alias_index from an address type assert(adr_type != NULL, "use other store_to_memory factory"); return store_to_memory(ctl, adr, val, bt, C->get_alias_index(adr_type), - require_atomic_access); + require_atomic_access, sem); } // This is the base version which is given alias index // Return the new StoreXNode Node* store_to_memory(Node* ctl, Node* adr, Node* val, BasicType bt, int adr_idx, - bool require_atomic_access = false); + bool require_atomic_access = false, + StoreNode::Sem = StoreNode::unordered); // All in one pre-barrier, store, post_barrier @@ -538,7 +552,8 @@ Node* val, const TypeOopPtr* val_type, BasicType bt, - bool use_precise); + bool use_precise, + StoreNode::Sem sem = StoreNode::unordered); Node* store_oop_to_object(Node* ctl, Node* obj, // containing obj @@ -546,8 +561,9 @@ const TypePtr* adr_type, Node* val, const TypeOopPtr* val_type, - BasicType bt) { - return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, false); + BasicType bt, + StoreNode::Sem sem = StoreNode::unordered) { + return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, false, sem); } Node* store_oop_to_array(Node* ctl, @@ -556,8 +572,9 @@ const TypePtr* adr_type, Node* val, const TypeOopPtr* val_type, - BasicType bt) { - return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true); + BasicType bt, + StoreNode::Sem sem = StoreNode::unordered) { + return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true, sem); } // Could be an array or object we don't know at compile time (unsafe ref.) @@ -566,7 +583,8 @@ Node* adr, // actual adress to store val at const TypePtr* adr_type, Node* val, - BasicType bt); + BasicType bt, + StoreNode::Sem sem = StoreNode::unordered); // For the few case where the barriers need special help void pre_barrier(bool do_load, Node* ctl, diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/idealGraphPrinter.cpp --- a/src/share/vm/opto/idealGraphPrinter.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/idealGraphPrinter.cpp Sun May 21 11:12:48 2017 -0700 @@ -404,7 +404,7 @@ Node *node = n; #ifndef PRODUCT - node->_in_dump_cnt++; + Compile::current()->_in_dump_cnt++; print_prop(NODE_NAME_PROPERTY, (const char *)node->Name()); const Type *t = node->bottom_type(); print_prop("type", (const char *)Type::msg[t->base()]); @@ -619,7 +619,7 @@ print_prop("lrg", _chaitin->n2lidx(node)); } - node->_in_dump_cnt--; + Compile::current()->_in_dump_cnt--; #endif tail(PROPERTIES_ELEMENT); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/idealKit.cpp --- a/src/share/vm/opto/idealKit.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/idealKit.cpp Sun May 21 11:12:48 2017 -0700 @@ -368,16 +368,16 @@ Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, - bool require_atomic_access) { - assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); + bool require_atomic_access, StoreNode::Sem sem) { + assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory"); const TypePtr* adr_type = NULL; debug_only(adr_type = C->get_adr_type(adr_idx)); Node *mem = memory(adr_idx); Node* st; if (require_atomic_access && bt == T_LONG) { - st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val); + st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val, sem); } else { - st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt); + st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, sem); } st = transform(st); set_memory(st, adr_idx); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/idealKit.hpp --- a/src/share/vm/opto/idealKit.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/idealKit.hpp Sun May 21 11:12:48 2017 -0700 @@ -226,7 +226,8 @@ Node* val, BasicType bt, int adr_idx, - bool require_atomic_access = false); + bool require_atomic_access = false, + StoreNode::Sem sem = StoreNode::release); // Store a card mark ordered after store_oop Node* storeCM(Node* ctl, diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/lcm.cpp --- a/src/share/vm/opto/lcm.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/lcm.cpp Sun May 21 11:12:48 2017 -0700 @@ -45,12 +45,52 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif // Optimization - Graph Style +// Check whether val is not-null-decoded compressed oop, +// i.e. will grab into the base of the heap if it represents NULL. +static bool accesses_heap_base_zone(Node *val) { + if (Universe::narrow_oop_base() > 0) { // Implies UseCompressedOops. + if (val && val->is_Mach()) { + if (val->as_Mach()->ideal_Opcode() == Op_DecodeN) { + // This assumes all Decodes with TypePtr::NotNull are matched to nodes that + // decode NULL to point to the heap base (Decode_NN). + if (val->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull) { + return true; + } + } + // Must recognize load operation with Decode matched in memory operand. + // We should not reach here exept for PPC/AIX, as os::zero_page_read_protected() + // returns true everywhere else. On PPC, no such memory operands + // exist, therefore we did not yet implement a check for such operands. + NOT_AIX(Unimplemented()); + } + } + return false; +} + +static bool needs_explicit_null_check_for_read(Node *val) { + // On some OSes (AIX) the page at address 0 is only write protected. + // If so, only Store operations will trap. + if (os::zero_page_read_protected()) { + return false; // Implicit null check will work. + } + // Also a read accessing the base of a heap-based compressed heap will trap. + if (accesses_heap_base_zone(val) && // Hits the base zone page. + Universe::narrow_oop_use_implicit_null_checks()) { // Base zone page is protected. + return false; + } + + return true; +} + //------------------------------implicit_null_check---------------------------- // Detect implicit-null-check opportunities. Basically, find NULL checks // with suitable memory ops nearby. Use the memory op to do the NULL check. @@ -204,6 +244,14 @@ } break; } + + // On some OSes (AIX) the page at address 0 is only write protected. + // If so, only Store operations will trap. + // But a read accessing the base of a heap-based compressed heap will trap. + if (!was_store && needs_explicit_null_check_for_read(val)) { + continue; + } + // check if the offset is not too high for implicit exception { intptr_t offset = 0; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/library_call.cpp Sun May 21 11:12:48 2017 -0700 @@ -1872,7 +1872,7 @@ runtime_math(OptoRuntime::Math_D_D_Type(), FN_PTR(SharedRuntime::dlog10), "LOG10"); // These intrinsics are supported on all hardware - case vmIntrinsics::_dsqrt: return Matcher::has_match_rule(Op_SqrtD) ? inline_math(id) : false; + case vmIntrinsics::_dsqrt: return Matcher::match_rule_supported(Op_SqrtD) ? inline_math(id) : false; case vmIntrinsics::_dabs: return Matcher::has_match_rule(Op_AbsD) ? inline_math(id) : false; case vmIntrinsics::_dexp: return Matcher::has_match_rule(Op_ExpD) ? inline_exp() : @@ -2432,6 +2432,12 @@ // For Stores, place a memory ordering barrier now. if (is_store) insert_mem_bar(Op_MemBarRelease); +#ifdef PPC64 + // Support ordering of "Independent Reads of Independent Writes" (see Parse::do_get_xxx). + // Solution: implement volatile read as fence-load-acquire + else + insert_mem_bar(Op_MemBarVolatile); +#endif } // Memory barrier to prevent normal and 'unsafe' accesses from @@ -2487,13 +2493,14 @@ break; } + StoreNode::Sem sem = is_volatile ? StoreNode::release : StoreNode::unordered; if (type != T_OBJECT ) { - (void) store_to_memory(control(), adr, val, type, adr_type, is_volatile); + (void) store_to_memory(control(), adr, val, type, adr_type, is_volatile, sem); } else { // Possibly an oop being stored to Java heap or native memory if (!TypePtr::NULL_PTR->higher_equal(_gvn.type(heap_base_oop))) { // oop to Java heap. - (void) store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type); + (void) store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type, sem); } else { // We can't tell at compile time if we are storing in the Java heap or outside // of it. So we need to emit code to conditionally do the proper type of @@ -2505,11 +2512,11 @@ __ if_then(heap_base_oop, BoolTest::ne, null(), PROB_UNLIKELY(0.999)); { // Sync IdealKit and graphKit. sync_kit(ideal); - Node* st = store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type); + Node* st = store_oop_to_unknown(control(), heap_base_oop, adr, adr_type, val, type, sem); // Update IdealKit memory. __ sync_kit(this); } __ else_(); { - __ store(__ ctrl(), adr, val, type, alias_type->index(), is_volatile); + __ store(__ ctrl(), adr, val, type, alias_type->index(), is_volatile, sem); } __ end_if(); // Final sync IdealKit and GraphKit. final_sync(ideal); @@ -2521,8 +2528,11 @@ if (is_volatile) { if (!is_store) insert_mem_bar(Op_MemBarAcquire); +#ifndef PPC64 + // Changed volatiles/Unsafe: lwsync-store, fence-load-acquire. else insert_mem_bar(Op_MemBarVolatile); +#endif } if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); @@ -2796,7 +2806,13 @@ // Add the trailing membar surrounding the access insert_mem_bar(Op_MemBarCPUOrder); + // On power we need a fence to prevent succeeding loads from floating + // above the store of the compare-exchange. +#ifdef PPC64 + insert_mem_bar(Op_MemBarVolatile); +#else insert_mem_bar(Op_MemBarAcquire); +#endif #ifdef _LP64 if (type == T_OBJECT && adr->bottom_type()->is_ptr_to_narrowoop() && kind == LS_xchg) { @@ -5183,7 +5199,7 @@ // Store a zero to the immediately preceding jint: Node* x1 = _gvn.transform( new(C) AddXNode(start, MakeConX(-bump_bit)) ); Node* p1 = basic_plus_adr(dest, x1); - mem = StoreNode::make(_gvn, control(), mem, p1, adr_type, intcon(0), T_INT); + mem = StoreNode::make(_gvn, control(), mem, p1, adr_type, intcon(0), T_INT, StoreNode::unordered); mem = _gvn.transform(mem); } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/locknode.hpp --- a/src/share/vm/opto/locknode.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/locknode.hpp Sun May 21 11:12:48 2017 -0700 @@ -43,8 +43,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif //------------------------------BoxLockNode------------------------------------ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/loopTransform.cpp --- a/src/share/vm/opto/loopTransform.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/loopTransform.cpp Sun May 21 11:12:48 2017 -0700 @@ -2719,26 +2719,43 @@ _igvn.register_new_node_with_optimizer(store_value); } + if (SharedRuntime::c_calling_convention_requires_ints_as_longs() && + // see StubRoutines::select_fill_function for types. FLOAT has been converted to INT + (t == T_FLOAT || t == T_INT || t == T_SHORT || t == T_CHAR || t == T_BOOLEAN || t == T_BYTE)) { + store_value = new (C) ConvI2LNode(store_value); + _igvn.register_new_node_with_optimizer(store_value); + } + Node* mem_phi = store->in(MemNode::Memory); Node* result_ctrl; Node* result_mem; const TypeFunc* call_type = OptoRuntime::array_fill_Type(); CallLeafNode *call = new (C) CallLeafNoFPNode(call_type, fill, fill_name, TypeAryPtr::get_array_body_type(t)); - call->init_req(TypeFunc::Parms+0, from); - call->init_req(TypeFunc::Parms+1, store_value); + uint cnt = 0; + call->init_req(TypeFunc::Parms + cnt++, from); + call->init_req(TypeFunc::Parms + cnt++, store_value); + if (SharedRuntime::c_calling_convention_requires_ints_as_longs()) { + call->init_req(TypeFunc::Parms + cnt++, C->top()); + } #ifdef _LP64 len = new (C) ConvI2LNode(len); _igvn.register_new_node_with_optimizer(len); #endif - call->init_req(TypeFunc::Parms+2, len); + call->init_req(TypeFunc::Parms + cnt++, len); #ifdef _LP64 - call->init_req(TypeFunc::Parms+3, C->top()); + call->init_req(TypeFunc::Parms + cnt++, C->top()); #endif call->init_req( TypeFunc::Control, head->init_control()); call->init_req( TypeFunc::I_O , C->top() ) ; // does no i/o call->init_req( TypeFunc::Memory , mem_phi->in(LoopNode::EntryControl) ); + // PPC64 needs top here as later we will set MachConstantBase. +#ifdef PPC64 + call->init_req( TypeFunc::ReturnAdr, C->top() ); +#else call->init_req( TypeFunc::ReturnAdr, C->start()->proj_out(TypeFunc::ReturnAdr) ); +#endif + call->init_req( TypeFunc::FramePtr, C->start()->proj_out(TypeFunc::FramePtr) ); _igvn.register_new_node_with_optimizer(call); result_ctrl = new (C) ProjNode(call,TypeFunc::Control); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/machnode.cpp --- a/src/share/vm/opto/machnode.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/machnode.cpp Sun May 21 11:12:48 2017 -0700 @@ -388,6 +388,17 @@ return skipped; } +int MachNode::operand_index( const MachOper *oper ) const { + uint skipped = oper_input_base(); // Sum of leaves skipped so far + uint opcnt; + for (opcnt = 1; opcnt < num_opnds(); opcnt++) { + if (_opnds[opcnt] == oper) break; + uint num_edges = _opnds[opcnt]->num_edges(); // leaves for operand + skipped += num_edges; + } + if (_opnds[opcnt] != oper) return -1; + return skipped; +} //------------------------------peephole--------------------------------------- // Apply peephole rule(s) to this instruction @@ -496,6 +507,9 @@ return _constant.offset(); } +int MachConstantNode::constant_offset_unchecked() { + return _constant.offset(); +} //============================================================================= #ifndef PRODUCT diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/machnode.hpp --- a/src/share/vm/opto/machnode.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/machnode.hpp Sun May 21 11:12:48 2017 -0700 @@ -31,6 +31,7 @@ #include "opto/node.hpp" #include "opto/regmask.hpp" +class BiasedLockingCounters; class BufferBlob; class CodeBuffer; class JVMState; @@ -155,6 +156,14 @@ virtual void ext_format(PhaseRegAlloc *,const MachNode *node,int idx, outputStream *st) const=0; virtual void dump_spec(outputStream *st) const; // Print per-operand info + + // Check whether o is a valid oper. + static bool notAnOper(const MachOper *o) { + if (o == NULL) return true; + if (((intptr_t)o & 1) != 0) return true; + if (*(address*)o == badAddress) return true; // kill by Node::destruct + return false; + } #endif }; @@ -195,6 +204,7 @@ // First index in _in[] corresponding to operand, or -1 if there is none int operand_index(uint operand) const; + int operand_index(const MachOper *oper) const; // Register class input is expected in virtual const RegMask &in_RegMask(uint) const; @@ -236,6 +246,9 @@ // Return number of relocatable values contained in this instruction virtual int reloc() const { return 0; } + // Return number of words used for double constants in this instruction + virtual int ins_num_consts() const { return 0; } + // Hash and compare over operands. Used to do GVN on machine Nodes. virtual uint hash() const; virtual uint cmp( const Node &n ) const; @@ -293,6 +306,15 @@ static const Pipeline *pipeline_class(); virtual const Pipeline *pipeline() const; + // Node is replaced by several nodes in late expand phase. + // Corresponding methods are generated for nodes if they specify + // lateExpand. See block.cpp for more documentation. + virtual bool requires_late_expand() const { return false; } + + // Some nodes require that the MachTableBase node (toc) is added after matching. + // See compile_ppc.hpp. + virtual int ins_requires_toc() const { return 0; } + #ifndef PRODUCT virtual const char *Name() const = 0; // Machine-specific name virtual void dump_spec(outputStream *st) const; // Print per-node info @@ -356,6 +378,9 @@ virtual uint ideal_reg() const { return Op_RegP; } virtual uint oper_input_base() const { return 1; } + virtual bool requires_late_expand() const; + virtual void lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_); + virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const; virtual uint size(PhaseRegAlloc* ra_) const; virtual bool pinned() const { return UseRDPCForConstantTableBase; } @@ -399,6 +424,9 @@ int constant_offset(); int constant_offset() const { return ((MachConstantNode*) this)->constant_offset(); } + // Unchecked version to avoid assertions in debug output. + int constant_offset_unchecked(); + int constant_offset_unchecked() const { return ((MachConstantNode*) this)->constant_offset_unchecked(); } }; //------------------------------MachUEPNode----------------------------------- diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/macro.cpp --- a/src/share/vm/opto/macro.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/macro.cpp Sun May 21 11:12:48 2017 -0700 @@ -1020,7 +1020,7 @@ Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { Node* adr = basic_plus_adr(base, offset); - mem = StoreNode::make(_igvn, ctl, mem, adr, NULL, value, bt); + mem = StoreNode::make(_igvn, ctl, mem, adr, NULL, value, bt, StoreNode::unordered); transform_later(mem); return mem; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/matcher.cpp --- a/src/share/vm/opto/matcher.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/matcher.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -53,8 +53,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif OptoReg::Name OptoReg::c_frame_pointer; @@ -2388,6 +2391,69 @@ return false; } +// Check whether node n is a branch to an uncommon trap that we could +// optimize as test with very high branch costs in case of going to +// the uncommon trap. The code must be able to be recompiled to use +// a cheaper test. +bool Matcher::branches_to_uncommon_trap(const Node *n) { + // Don't do it for natives, adapters, or runtime stubs + Compile *Co = Compile::current(); + if (!Co->is_method_compilation()) return false; + + assert(n->is_If(), "You should only call this on if nodes."); + IfNode *ifn = n->as_If(); + + Node *ifFalse = NULL; + for (DUIterator_Fast imax, i = ifn->fast_outs(imax); i < imax; i++) { + if (ifn->fast_out(i)->is_IfFalse()) { + ifFalse = ifn->fast_out(i); + break; + } + } + assert(ifFalse, "An If should have an ifFalse. Graph is broken."); + + Node *reg = ifFalse; + int cnt = 4; // We must protect against cycles. Limit to 4 iterations. + // Alternatively use visited set? Seems too expensive. + while (reg != NULL && cnt > 0) { + CallNode *call = NULL; + RegionNode *nxt_reg = NULL; + for (DUIterator_Fast imax, i = reg->fast_outs(imax); i < imax; i++) { + Node *o = reg->fast_out(i); + if (o->is_Call()) { + call = o->as_Call(); + } + if (o->is_Region()) { + nxt_reg = o->as_Region(); + } + } + + if (call && + call->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point()) { + const Type* trtype = call->in(TypeFunc::Parms)->bottom_type(); + if (trtype->isa_int() && trtype->is_int()->is_con()) { + jint tr_con = trtype->is_int()->get_con(); + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(tr_con); + Deoptimization::DeoptAction action = Deoptimization::trap_request_action(tr_con); + assert((int)reason < (int)BitsPerInt, "recode bit map"); + + if (is_set_nth_bit(Co->allowed_deopt_reasons(), (int)reason) + && action != Deoptimization::Action_none) { + // This uncommon trap is sure to recompile, eventually. + // When that happens, C->too_many_traps will prevent + // this transformation from happening again. + return true; + } + } + } + + reg = nxt_reg; + cnt--; + } + + return false; +} + //============================================================================= //---------------------------State--------------------------------------------- State::State(void) { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/matcher.hpp --- a/src/share/vm/opto/matcher.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/matcher.hpp Sun May 21 11:12:48 2017 -0700 @@ -425,6 +425,9 @@ // aligned. static const bool misaligned_doubles_ok; + // Does the CPU require late expand (see block.cpp for description of late expand)? + static const bool require_late_expand; + // Perform a platform dependent implicit null fixup. This is needed // on windows95 to take care of some unusual register constraints. void pd_implicit_null_fixup(MachNode *load, uint idx); @@ -457,6 +460,8 @@ // retain the Node to act as a compiler ordering barrier. static bool post_store_load_barrier(const Node* mb); + // Does n lead to an uncommon trap that can cause deoptimization? + static bool branches_to_uncommon_trap(const Node *n); #ifdef ASSERT void dump_old2new_map(); // machine-independent to machine-dependent diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/memnode.cpp --- a/src/share/vm/opto/memnode.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/memnode.cpp Sun May 21 11:12:48 2017 -0700 @@ -873,7 +873,7 @@ //----------------------------LoadNode::make----------------------------------- // Polymorphic factory method: -Node *LoadNode::make( PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypePtr* adr_type, const Type *rt, BasicType bt ) { +Node *LoadNode::make(PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypePtr* adr_type, const Type *rt, BasicType bt, Sem sem) { Compile* C = gvn.C; // sanity check the alias category against the created node type @@ -889,34 +889,34 @@ rt->isa_oopptr() || is_immutable_value(adr), "raw memory operations should have control edge"); switch (bt) { - case T_BOOLEAN: return new (C) LoadUBNode(ctl, mem, adr, adr_type, rt->is_int() ); - case T_BYTE: return new (C) LoadBNode (ctl, mem, adr, adr_type, rt->is_int() ); - case T_INT: return new (C) LoadINode (ctl, mem, adr, adr_type, rt->is_int() ); - case T_CHAR: return new (C) LoadUSNode(ctl, mem, adr, adr_type, rt->is_int() ); - case T_SHORT: return new (C) LoadSNode (ctl, mem, adr, adr_type, rt->is_int() ); - case T_LONG: return new (C) LoadLNode (ctl, mem, adr, adr_type, rt->is_long() ); - case T_FLOAT: return new (C) LoadFNode (ctl, mem, adr, adr_type, rt ); - case T_DOUBLE: return new (C) LoadDNode (ctl, mem, adr, adr_type, rt ); - case T_ADDRESS: return new (C) LoadPNode (ctl, mem, adr, adr_type, rt->is_ptr() ); + case T_BOOLEAN: return new (C) LoadUBNode(ctl, mem, adr, adr_type, rt->is_int(), sem); + case T_BYTE: return new (C) LoadBNode (ctl, mem, adr, adr_type, rt->is_int(), sem); + case T_INT: return new (C) LoadINode (ctl, mem, adr, adr_type, rt->is_int(), sem); + case T_CHAR: return new (C) LoadUSNode(ctl, mem, adr, adr_type, rt->is_int(), sem); + case T_SHORT: return new (C) LoadSNode (ctl, mem, adr, adr_type, rt->is_int(), sem); + case T_LONG: return new (C) LoadLNode (ctl, mem, adr, adr_type, rt->is_long(), false, sem); + case T_FLOAT: return new (C) LoadFNode (ctl, mem, adr, adr_type, rt, sem); + case T_DOUBLE: return new (C) LoadDNode (ctl, mem, adr, adr_type, rt, sem); + case T_ADDRESS: return new (C) LoadPNode (ctl, mem, adr, adr_type, rt->is_ptr(), sem); case T_OBJECT: #ifdef _LP64 if (adr->bottom_type()->is_ptr_to_narrowoop()) { - Node* load = gvn.transform(new (C) LoadNNode(ctl, mem, adr, adr_type, rt->make_narrowoop())); + Node* load = gvn.transform(new (C) LoadNNode(ctl, mem, adr, adr_type, rt->make_narrowoop(), sem)); return new (C) DecodeNNode(load, load->bottom_type()->make_ptr()); } else #endif { assert(!adr->bottom_type()->is_ptr_to_narrowoop(), "should have got back a narrow oop"); - return new (C) LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr()); + return new (C) LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr(), sem); } } ShouldNotReachHere(); return (LoadNode*)NULL; } -LoadLNode* LoadLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt) { +LoadLNode* LoadLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, Sem sem) { bool require_atomic = true; - return new (C) LoadLNode(ctl, mem, adr, adr_type, rt->is_long(), require_atomic); + return new (C) LoadLNode(ctl, mem, adr, adr_type, rt->is_long(), require_atomic, sem); } @@ -2220,20 +2220,21 @@ //============================================================================= //---------------------------StoreNode::make----------------------------------- // Polymorphic factory method: -StoreNode* StoreNode::make( PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, BasicType bt ) { +StoreNode* StoreNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, BasicType bt, Sem sem) { + assert((sem == unordered || sem == release), "unexpected"); Compile* C = gvn.C; assert( C->get_alias_index(adr_type) != Compile::AliasIdxRaw || ctl != NULL, "raw memory operations should have control edge"); switch (bt) { case T_BOOLEAN: val = gvn.transform(new (C) AndINode(val, gvn.intcon(0x1))); // Fall through to T_BYTE case - case T_BYTE: return new (C) StoreBNode(ctl, mem, adr, adr_type, val); - case T_INT: return new (C) StoreINode(ctl, mem, adr, adr_type, val); + case T_BYTE: return new (C) StoreBNode(ctl, mem, adr, adr_type, val, sem); + case T_INT: return new (C) StoreINode(ctl, mem, adr, adr_type, val, sem); case T_CHAR: - case T_SHORT: return new (C) StoreCNode(ctl, mem, adr, adr_type, val); - case T_LONG: return new (C) StoreLNode(ctl, mem, adr, adr_type, val); - case T_FLOAT: return new (C) StoreFNode(ctl, mem, adr, adr_type, val); - case T_DOUBLE: return new (C) StoreDNode(ctl, mem, adr, adr_type, val); + case T_SHORT: return new (C) StoreCNode(ctl, mem, adr, adr_type, val, sem); + case T_LONG: return new (C) StoreLNode(ctl, mem, adr, adr_type, val, false, sem); + case T_FLOAT: return new (C) StoreFNode(ctl, mem, adr, adr_type, val, sem); + case T_DOUBLE: return new (C) StoreDNode(ctl, mem, adr, adr_type, val, sem); case T_ADDRESS: case T_OBJECT: #ifdef _LP64 @@ -2241,20 +2242,20 @@ (UseCompressedOops && val->bottom_type()->isa_klassptr() && adr->bottom_type()->isa_rawptr())) { val = gvn.transform(new (C) EncodePNode(val, val->bottom_type()->make_narrowoop())); - return new (C) StoreNNode(ctl, mem, adr, adr_type, val); + return new (C) StoreNNode(ctl, mem, adr, adr_type, val, sem); } else #endif { - return new (C) StorePNode(ctl, mem, adr, adr_type, val); + return new (C) StorePNode(ctl, mem, adr, adr_type, val, sem); } } ShouldNotReachHere(); return (StoreNode*)NULL; } -StoreLNode* StoreLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val) { +StoreLNode* StoreLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, Sem sem) { bool require_atomic = true; - return new (C) StoreLNode(ctl, mem, adr, adr_type, val, require_atomic); + return new (C) StoreLNode(ctl, mem, adr, adr_type, val, require_atomic, sem); } @@ -2696,7 +2697,7 @@ Node* adr = new (C) AddPNode(dest, dest, phase->MakeConX(offset)); adr = phase->transform(adr); const TypePtr* atp = TypeRawPtr::BOTTOM; - mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT); + mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, StoreNode::unordered); mem = phase->transform(mem); offset += BytesPerInt; } @@ -2757,7 +2758,7 @@ Node* adr = new (C) AddPNode(dest, dest, phase->MakeConX(done_offset)); adr = phase->transform(adr); const TypePtr* atp = TypeRawPtr::BOTTOM; - mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT); + mem = StoreNode::make(*phase, ctl, mem, adr, atp, phase->zerocon(T_INT), T_INT, StoreNode::unordered); mem = phase->transform(mem); done_offset += BytesPerInt; } @@ -3599,14 +3600,14 @@ ++new_long; off[nst] = offset; st[nst++] = StoreNode::make(*phase, ctl, zmem, adr, atp, - phase->longcon(con), T_LONG); + phase->longcon(con), T_LONG, StoreNode::unordered); } else { // Omit either if it is a zero. if (con0 != 0) { ++new_int; off[nst] = offset; st[nst++] = StoreNode::make(*phase, ctl, zmem, adr, atp, - phase->intcon(con0), T_INT); + phase->intcon(con0), T_INT, StoreNode::unordered); } if (con1 != 0) { ++new_int; @@ -3614,7 +3615,7 @@ adr = make_raw_address(offset, phase); off[nst] = offset; st[nst++] = StoreNode::make(*phase, ctl, zmem, adr, atp, - phase->intcon(con1), T_INT); + phase->intcon(con1), T_INT, StoreNode::unordered); } } @@ -4299,7 +4300,7 @@ } } #else // !ASSERT -#define verify_memory_slice(m,i,n) (0) // PRODUCT version is no-op +#define verify_memory_slice(m,i,n) (void)(0) // PRODUCT version is no-op #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/memnode.hpp --- a/src/share/vm/opto/memnode.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/memnode.hpp Sun May 21 11:12:48 2017 -0700 @@ -134,20 +134,35 @@ //------------------------------LoadNode--------------------------------------- // Load value; requires Memory and Address class LoadNode : public MemNode { +public: + typedef enum { unordered = 0, acquire } Sem; + +private: + // On platforms with weak memory ordering (e.g., PPC, Ia64) we distinguish + // loads that can be reordered, and such requiring acquire semantics to + // adhere to the Java specification. The required behaviour is stored in + // this field. + const Sem _sem; + protected: - virtual uint cmp( const Node &n ) const; + virtual uint cmp(const Node &n) const; virtual uint size_of() const; // Size is bigger const Type* const _type; // What kind of value is loaded? public: - LoadNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt ) - : MemNode(c,mem,adr,at), _type(rt) { + LoadNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, Sem sem = LoadNode::unordered) + : MemNode(c,mem,adr,at), _type(rt), _sem(sem) { init_class_id(Class_Load); } + inline bool is_unordered() const { return !is_acquire(); } + inline bool is_acquire() const { + assert(_sem == unordered || _sem == acquire, "unexpected"); + return _sem == acquire; + } // Polymorphic factory method: - static Node* make( PhaseGVN& gvn, Node *c, Node *mem, Node *adr, - const TypePtr* at, const Type *rt, BasicType bt ); + static Node* make(PhaseGVN& gvn, Node *c, Node *mem, Node *adr, + const TypePtr* at, const Type *rt, BasicType bt, Sem sem = LoadNode::unordered); virtual uint hash() const; // Check the type @@ -221,8 +236,8 @@ // Load a byte (8bits signed) from memory class LoadBNode : public LoadNode { public: - LoadBNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::BYTE ) - : LoadNode(c,mem,adr,at,ti) {} + LoadBNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::BYTE, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, ti, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -235,8 +250,8 @@ // Load a unsigned byte (8bits unsigned) from memory class LoadUBNode : public LoadNode { public: - LoadUBNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt* ti = TypeInt::UBYTE ) - : LoadNode(c, mem, adr, at, ti) {} + LoadUBNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeInt* ti = TypeInt::UBYTE , Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, ti, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); @@ -249,8 +264,8 @@ // Load an unsigned short/char (16bits unsigned) from memory class LoadUSNode : public LoadNode { public: - LoadUSNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::CHAR ) - : LoadNode(c,mem,adr,at,ti) {} + LoadUSNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::CHAR, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, ti, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -263,8 +278,8 @@ // Load a short (16bits signed) from memory class LoadSNode : public LoadNode { public: - LoadSNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT ) - : LoadNode(c,mem,adr,at,ti) {} + LoadSNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, ti, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -277,8 +292,8 @@ // Load an integer from memory class LoadINode : public LoadNode { public: - LoadINode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::INT ) - : LoadNode(c,mem,adr,at,ti) {} + LoadINode(Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::INT, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, ti, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual int store_Opcode() const { return Op_StoreI; } @@ -309,18 +324,16 @@ const bool _require_atomic_access; // is piecewise load forbidden? public: - LoadLNode( Node *c, Node *mem, Node *adr, const TypePtr* at, - const TypeLong *tl = TypeLong::LONG, - bool require_atomic_access = false ) - : LoadNode(c,mem,adr,at,tl) - , _require_atomic_access(require_atomic_access) - {} + LoadLNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeLong *tl = TypeLong::LONG, + bool require_atomic_access = false, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, tl, sem), _require_atomic_access(require_atomic_access) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegL; } virtual int store_Opcode() const { return Op_StoreL; } virtual BasicType memory_type() const { return T_LONG; } bool require_atomic_access() { return _require_atomic_access; } - static LoadLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt); + static LoadLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, + const Type* rt, Sem sem = LoadNode::unordered); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { LoadNode::dump_spec(st); @@ -333,8 +346,8 @@ // Load a long from unaligned memory class LoadL_unalignedNode : public LoadLNode { public: - LoadL_unalignedNode( Node *c, Node *mem, Node *adr, const TypePtr* at ) - : LoadLNode(c,mem,adr,at) {} + LoadL_unalignedNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Sem sem = LoadNode::unordered) + : LoadLNode(c, mem, adr, at, TypeLong::LONG, false, sem) {} virtual int Opcode() const; }; @@ -342,8 +355,8 @@ // Load a float (64 bits) from memory class LoadFNode : public LoadNode { public: - LoadFNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *t = Type::FLOAT ) - : LoadNode(c,mem,adr,at,t) {} + LoadFNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *t = Type::FLOAT, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, t, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegF; } virtual int store_Opcode() const { return Op_StoreF; } @@ -354,8 +367,8 @@ // Load a double (64 bits) from memory class LoadDNode : public LoadNode { public: - LoadDNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *t = Type::DOUBLE ) - : LoadNode(c,mem,adr,at,t) {} + LoadDNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *t = Type::DOUBLE, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, t, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegD; } virtual int store_Opcode() const { return Op_StoreD; } @@ -366,8 +379,8 @@ // Load a double from unaligned memory class LoadD_unalignedNode : public LoadDNode { public: - LoadD_unalignedNode( Node *c, Node *mem, Node *adr, const TypePtr* at ) - : LoadDNode(c,mem,adr,at) {} + LoadD_unalignedNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Sem sem = LoadNode::unordered) + : LoadDNode(c, mem, adr, at, Type::DOUBLE, sem) {} virtual int Opcode() const; }; @@ -375,8 +388,8 @@ // Load a pointer from memory (either object or array) class LoadPNode : public LoadNode { public: - LoadPNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const TypePtr* t ) - : LoadNode(c,mem,adr,at,t) {} + LoadPNode(Node *c, Node *mem, Node *adr, const TypePtr *at, const TypePtr* t, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, t, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } virtual int store_Opcode() const { return Op_StoreP; } @@ -388,8 +401,8 @@ // Load a narrow oop from memory (either object or array) class LoadNNode : public LoadNode { public: - LoadNNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const Type* t ) - : LoadNode(c,mem,adr,at,t) {} + LoadNNode(Node *c, Node *mem, Node *adr, const TypePtr *at, const Type* t, Sem sem = LoadNode::unordered) + : LoadNode(c, mem, adr, at, t, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegN; } virtual int store_Opcode() const { return Op_StoreN; } @@ -400,8 +413,8 @@ // Load a Klass from an object class LoadKlassNode : public LoadPNode { public: - LoadKlassNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeKlassPtr *tk ) - : LoadPNode(c,mem,adr,at,tk) {} + LoadKlassNode(Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeKlassPtr *tk, Sem sem = LoadNode::unordered) + : LoadPNode(c, mem, adr, at, tk, sem) {} virtual int Opcode() const; virtual const Type *Value( PhaseTransform *phase ) const; virtual Node *Identity( PhaseTransform *phase ); @@ -416,8 +429,8 @@ // Load a narrow Klass from an object. class LoadNKlassNode : public LoadNNode { public: - LoadNKlassNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeNarrowOop *tk ) - : LoadNNode(c,mem,adr,at,tk) {} + LoadNKlassNode(Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeNarrowOop *tk, Sem sem = LoadNode::unordered) + : LoadNNode(c, mem, adr, at, tk, sem) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegN; } virtual int store_Opcode() const { return Op_StoreN; } @@ -432,6 +445,16 @@ //------------------------------StoreNode-------------------------------------- // Store value; requires Store, Address and Value class StoreNode : public MemNode { +public: + typedef enum { unordered = 0, release } Sem; +private: + // On platforms with weak memory ordering (e.g., PPC, Ia64) we distinguish + // stores that can be reordered, and such requiring release semantics to + // adhere to the Java specification. The required behaviour is stored in + // this field. + const Sem _sem; + // Needed for proper cloning. + virtual uint size_of() const { return sizeof(*this); } protected: virtual uint cmp( const Node &n ) const; virtual bool depends_only_on_test() const { return false; } @@ -440,18 +463,44 @@ Node *Ideal_sign_extended_input(PhaseGVN *phase, int num_bits); public: - StoreNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) - : MemNode(c,mem,adr,at,val) { + // We must ensure that stores of object references will be visible + // only after the object's initialization. So the callers of this + // procedure must indicate that the store requires `release' + // semantics, if the stored value is an object reference that might + // point to a new object and may become externally visible. + StoreNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem) + : MemNode(c, mem, adr, at, val), _sem(sem) { init_class_id(Class_Store); } - StoreNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store ) - : MemNode(c,mem,adr,at,val,oop_store) { + StoreNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store, Sem sem) + : MemNode(c, mem, adr, at, val, oop_store), _sem(sem) { init_class_id(Class_Store); } - // Polymorphic factory method: - static StoreNode* make( PhaseGVN& gvn, Node *c, Node *mem, Node *adr, - const TypePtr* at, Node *val, BasicType bt ); + inline bool is_unordered() const { return !is_release(); } + inline bool is_release() const { + assert((_sem == unordered || _sem == release), "unexpected"); + return _sem == release; + } + + // Conservatively release stores of object references in order to + // ensure visibility of object initialization. + static inline Sem release_if_reference(const BasicType t) { + const Sem s = (t == T_ARRAY || + t == T_ADDRESS || // Might be the address of an object reference (`boxing'). + t == T_OBJECT) ? release : unordered; + return s; + } + + // Polymorphic factory method + // + // We must ensure that stores of object references will be visible + // only after the object's initialization. So the callers of this + // procedure must indicate that the store requires `release' + // semantics, if the stored value is an object reference that might + // point to a new object and may become externally visible. + static StoreNode* make(PhaseGVN& gvn, Node *c, Node *mem, Node *adr, + const TypePtr* at, Node *val, BasicType bt, Sem sem); virtual uint hash() const; // Check the type @@ -482,7 +531,8 @@ // Store byte to memory class StoreBNode : public StoreNode { public: - StoreBNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StoreBNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual BasicType memory_type() const { return T_BYTE; } @@ -492,7 +542,8 @@ // Store char/short to memory class StoreCNode : public StoreNode { public: - StoreCNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StoreCNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual BasicType memory_type() const { return T_CHAR; } @@ -502,7 +553,8 @@ // Store int to memory class StoreINode : public StoreNode { public: - StoreINode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StoreINode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_INT; } }; @@ -519,15 +571,12 @@ const bool _require_atomic_access; // is piecewise store forbidden? public: - StoreLNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, - bool require_atomic_access = false ) - : StoreNode(c,mem,adr,at,val) - , _require_atomic_access(require_atomic_access) - {} + StoreLNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, bool require_atomic_access = false, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem), _require_atomic_access(require_atomic_access) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_LONG; } bool require_atomic_access() { return _require_atomic_access; } - static StoreLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val); + static StoreLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, Sem sem); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { StoreNode::dump_spec(st); @@ -540,7 +589,8 @@ // Store float to memory class StoreFNode : public StoreNode { public: - StoreFNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StoreFNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_FLOAT; } }; @@ -549,7 +599,8 @@ // Store double to memory class StoreDNode : public StoreNode { public: - StoreDNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StoreDNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_DOUBLE; } }; @@ -558,7 +609,8 @@ // Store pointer to memory class StorePNode : public StoreNode { public: - StorePNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StorePNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_ADDRESS; } }; @@ -567,7 +619,8 @@ // Store narrow oop to memory class StoreNNode : public StoreNode { public: - StoreNNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNode(c,mem,adr,at,val) {} + StoreNNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Sem sem = StoreNode::unordered) + : StoreNode(c, mem, adr, at, val, sem) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_NARROWOOP; } }; @@ -588,7 +641,7 @@ public: StoreCMNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store, int oop_alias_idx ) : - StoreNode(c,mem,adr,at,val,oop_store), + StoreNode(c, mem, adr, at, val, oop_store, StoreNode::release), _oop_alias_idx(oop_alias_idx) { assert(_oop_alias_idx >= Compile::AliasIdxRaw || _oop_alias_idx == Compile::AliasIdxBot && Compile::current()->AliasLevel() == 0, @@ -608,8 +661,8 @@ // On PowerPC and friends it's a real load-locked. class LoadPLockedNode : public LoadPNode { public: - LoadPLockedNode( Node *c, Node *mem, Node *adr ) - : LoadPNode(c,mem,adr,TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM) {} + LoadPLockedNode(Node *c, Node *mem, Node *adr, Sem sem = LoadNode::acquire) + : LoadPNode(c, mem, adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, sem) {} virtual int Opcode() const; virtual int store_Opcode() const { return Op_StorePConditional; } virtual bool depends_only_on_test() const { return true; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/node.cpp --- a/src/share/vm/opto/node.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/node.cpp Sun May 21 11:12:48 2017 -0700 @@ -947,6 +947,9 @@ //------------------------------emit------------------------------------------- // Emit bytes starting at parameter 'ptr'. void Node::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {} +//------------------------------lateExpand------------------------------------- +// Expand node after register allocation. +void Node::lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_) {} //------------------------------size------------------------------------------- // Size of instruction in bytes uint Node::size(PhaseRegAlloc *ra_) const { return 0; } @@ -1513,7 +1516,6 @@ #ifndef PRODUCT -int Node::_in_dump_cnt = 0; // -----------------------------Name------------------------------------------- extern const char *NodeClassNames[]; @@ -1585,7 +1587,7 @@ void Node::dump(const char* suffix, outputStream *st) const { Compile* C = Compile::current(); bool is_new = C->node_arena()->contains(this); - _in_dump_cnt++; + C->_in_dump_cnt++; st->print("%c%d\t%s\t=== ", is_new ? ' ' : 'o', _idx, Name()); // Dump the required and precedence inputs @@ -1600,7 +1602,7 @@ dump_orig(debug_orig(), st); #endif st->cr(); - _in_dump_cnt--; + C->_in_dump_cnt--; return; // don't process dead nodes } @@ -1652,7 +1654,7 @@ } } if (suffix) st->print(suffix); - _in_dump_cnt--; + C->_in_dump_cnt--; } //------------------------------dump_req-------------------------------------- diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/node.hpp --- a/src/share/vm/opto/node.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/node.hpp Sun May 21 11:12:48 2017 -0700 @@ -352,6 +352,8 @@ // Reference to the i'th input Node. Error if out of bounds. Node* in(uint i) const { assert(i < _max, err_msg_res("oob: i=%d, _max=%d", i, _max)); return _in[i]; } + // Reference to the i'th input Node. NULL if out of bounds. + Node* lookup(uint i) const { return ((i < _max) ? _in[i] : NULL); } // Reference to the i'th output Node. Error if out of bounds. // Use this accessor sparingly. We are going trying to use iterators instead. Node* raw_out(uint i) const { assert(i < _outcnt,"oob"); return _out[i]; } @@ -379,6 +381,10 @@ // Set a required input edge, also updates corresponding output edge void add_req( Node *n ); // Append a NEW required input + void add_req( Node *n0, Node *n1 ) { + add_req(n0); add_req(n1); } + void add_req( Node *n0, Node *n1, Node *n2 ) { + add_req(n0); add_req(n1); add_req(n2); } void add_req_batch( Node* n, uint m ); // Append m NEW required inputs (all n). void del_req( uint idx ); // Delete required edge & compact void ins_req( uint i, Node *n ); // Insert a NEW required input @@ -783,6 +789,9 @@ // is_Copy() returns copied edge index (0 or 1) uint is_Copy() const { return (_flags & Flag_is_Copy); } + // Returns true if this node is a check that can be implemented with a trap. + virtual bool is_TrapBasedCheckNode() const { return false; } + virtual bool is_CFG() const { return false; } // If this node is control-dependent on a test, can it be @@ -921,6 +930,8 @@ // Emit bytes starting at parameter 'ptr' // Bump 'ptr' by the number of output bytes virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + // Expand node after register allocation. + virtual void lateExpand(GrowableArray *nodes, PhaseRegAlloc *ra_); // Size of instruction in bytes virtual uint size(PhaseRegAlloc *ra_) const; @@ -1008,8 +1019,7 @@ // RegMask Print Functions void dump_in_regmask(int idx) { in_RegMask(idx).dump(); } void dump_out_regmask() { out_RegMask().dump(); } - static int _in_dump_cnt; - static bool in_dump() { return _in_dump_cnt > 0; } + static bool in_dump() { return Compile::current()->_in_dump_cnt > 0; } void fast_dump() const { tty->print("%4d: %-17s", _idx, Name()); for (uint i = 0; i < len(); i++) diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/output.cpp Sun May 21 11:12:48 2017 -0700 @@ -1095,7 +1095,7 @@ // Compute prolog code size _method_size = 0; _frame_slots = OptoReg::reg2stack(_matcher->_old_SP)+_regalloc->_framesize; -#ifdef IA64 +#if defined(IA64) && !defined(AIX) if (save_argument_registers()) { // 4815101: this is a stub with implicit and unknown precision fp args. // The usual spill mechanism can only generate stfd's in this case, which @@ -1113,6 +1113,7 @@ assert(_frame_slots >= 0 && _frame_slots < 1000000, "sanity check"); if (has_mach_constant_base_node()) { + uint add_size = 0; // Fill the constant table. // Note: This must happen before shorten_branches. for (uint i = 0; i < _cfg->_num_blocks; i++) { @@ -1126,6 +1127,9 @@ if (n->is_MachConstant()) { MachConstantNode* machcon = n->as_MachConstant(); machcon->eval_constant(C); + } else if (n->is_Mach()) { + // On Power there are more nodes that issue constants. + add_size += (n->as_Mach()->ins_num_consts() * 8); } } } @@ -1133,9 +1137,14 @@ // Calculate the offsets of the constants and the size of the // constant table (including the padding to the next section). constant_table().calculate_offsets_and_size(); - const_req = constant_table().size(); + const_req = constant_table().size() + add_size; } +#if defined(ASSERT) && defined(PPC64) + // Increase const_req by 40 for better behaviour of constant-pool overflow assertions below. + const_req += 40; +#endif + // Initialize the space for the BufferBlob used to find and verify // instruction size in MachNode::emit_size() init_scratch_buffer_blob(const_req); @@ -1404,7 +1413,7 @@ int offset = blk_starts[block_num] - current_offset; if (block_num >= i) { // Current and following block's offset are not - // finilized yet, adjust distance by the difference + // finalized yet, adjust distance by the difference // between calculated and final offsets of current block. offset -= (blk_starts[i] - blk_offset); } @@ -1485,6 +1494,12 @@ // Intel all the time, with add-to-memory kind of opcodes. previous_offset = current_offset; } + + // Not an else-if! + // If this is a trap based cmp then add its offset to the list. + if (mach->is_TrapBasedCheckNode()) { + inct_starts[inct_cnt++] = current_offset; + } } // Verify that there is sufficient space remaining @@ -1751,6 +1766,12 @@ _inc_table.append( inct_starts[inct_cnt++], blk_labels[block_num].loc_pos() ); continue; } + // Handle implicit exception table updates: trap instructions. + if (n->is_TrapBasedCheckNode()) { + uint block_num = b->non_connector_successor(0)->_pre_order; + _inc_table.append(inct_starts[inct_cnt++], blk_labels[block_num].loc_pos()); + continue; + } } // End of for all blocks fill in exception table entries } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/output.hpp --- a/src/share/vm/opto/output.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/output.hpp Sun May 21 11:12:48 2017 -0700 @@ -42,8 +42,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif class Arena; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/parse.hpp --- a/src/share/vm/opto/parse.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/parse.hpp Sun May 21 11:12:48 2017 -0700 @@ -328,6 +328,8 @@ GraphKit _exits; // Record all normal returns and throws here. bool _wrote_final; // Did we write a final field? + // Add MemBarRelease for constructors which write volatile field (PPC64). + PPC64_ONLY(bool _wrote_volatile;) bool _count_invocations; // update and test invocation counter bool _method_data_update; // update method data oop @@ -368,6 +370,11 @@ GraphKit& exits() { return _exits; } bool wrote_final() const { return _wrote_final; } void set_wrote_final(bool z) { _wrote_final = z; } +#ifdef PPC64 + // Add MemBarRelease for constructors which write volatile field (PPC64). + bool wrote_volatile() const { return _wrote_volatile; } + void set_wrote_volatile(bool z) { _wrote_volatile = z; } +#endif bool count_invocations() const { return _count_invocations; } bool method_data_update() const { return _method_data_update; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/parse1.cpp --- a/src/share/vm/opto/parse1.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/parse1.cpp Sun May 21 11:12:48 2017 -0700 @@ -390,6 +390,8 @@ _expected_uses = expected_uses; _depth = 1 + (caller->has_method() ? caller->depth() : 0); _wrote_final = false; + // Add MemBarRelease for constructors which write volatile field (PPC64). + PPC64_ONLY(_wrote_volatile = false;) _entry_bci = InvocationEntryBci; _tf = NULL; _block = NULL; @@ -934,7 +936,10 @@ Node* iophi = _exits.i_o(); _exits.set_i_o(gvn().transform(iophi)); - if (wrote_final()) { + // Add MemBarRelease for constructors which write volatile field (PPC64). + // Intention is to avoid that other threads can observe initial values even though the + // constructor has set the volatile field. Java programmers wouldn't like it and we wanna be nice. + if (wrote_final() PPC64_ONLY(|| (wrote_volatile() && method()->is_initializer()))) { // This method (which must be a constructor by the rules of Java) // wrote a final. The effects of all initializations must be // committed to memory before any code after the constructor diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/parse2.cpp --- a/src/share/vm/opto/parse2.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/parse2.cpp Sun May 21 11:12:48 2017 -0700 @@ -66,7 +66,7 @@ if (elem == TypeInt::BOOL) { elem_type = T_BOOLEAN; } - store_to_memory(control(), adr, val, elem_type, adr_type); + store_to_memory(control(), adr, val, elem_type, adr_type, false, StoreNode::release_if_reference(elem_type)); } @@ -1641,7 +1641,7 @@ a = pop(); // the array itself const TypeOopPtr* elemtype = _gvn.type(a)->is_aryptr()->elem()->make_oopptr(); const TypeAryPtr* adr_type = TypeAryPtr::OOPS; - Node* store = store_oop_to_array(control(), a, d, adr_type, c, elemtype, T_OBJECT); + Node* store = store_oop_to_array(control(), a, d, adr_type, c, elemtype, T_OBJECT, StoreNode::release); break; } case Bytecodes::_lastore: { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/parse3.cpp --- a/src/share/vm/opto/parse3.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/parse3.cpp Sun May 21 11:12:48 2017 -0700 @@ -199,8 +199,27 @@ } else { type = Type::get_const_basic_type(bt); } +#ifdef PPC64 + // Support VolatileIRIWTest (Independent Reads of Independent Writes). + // Example: volatile x=0, y=0 + // __________ __________ __________ __________ + // | Thread 0 | | Thread 1 | | Thread 2 | | Thread 3 | + // + // write(x=1) read(x) write(y=1) read(y) + // read(y) read(x) + // + // Disallowed: x=1, y=0 y=1, x=0 + // however, this case is observable on PPC64 (allowed by architecture) + // + // Solution: implement volatile read as fence-load-acquire + if (field->is_volatile()) { + insert_mem_bar(Op_MemBarVolatile); + } +#endif // Build the load. - Node* ld = make_load(NULL, adr, type, bt, adr_type, is_vol); + // + LoadNode::Sem sem = is_vol ? LoadNode::acquire : LoadNode::unordered; + Node* ld = make_load(NULL, adr, type, bt, adr_type, is_vol, sem); // Adjust Java stack if (type2size[bt] == 1) @@ -260,6 +279,16 @@ // Round doubles before storing if (bt == T_DOUBLE) val = dstore_rounding(val); + // Conservatively release stores of object references. + const StoreNode::Sem sem = + is_vol ? + // Volatile fields need releasing stores. + StoreNode::release : + // Non-volatile fields also need releasing stores if they hold an + // object reference, because the object reference might point to + // a freshly created object. + StoreNode::release_if_reference(bt); + // Store the value. Node* store; if (bt == T_OBJECT) { @@ -269,9 +298,9 @@ } else { field_type = TypeOopPtr::make_from_klass(field->type()->as_klass()); } - store = store_oop_to_object( control(), obj, adr, adr_type, val, field_type, bt); + store = store_oop_to_object(control(), obj, adr, adr_type, val, field_type, bt, sem); } else { - store = store_to_memory( control(), adr, val, bt, adr_type, is_vol ); + store = store_to_memory(control(), adr, val, bt, adr_type, is_vol, sem); } // If reference is volatile, prevent following volatiles ops from @@ -287,6 +316,13 @@ if (is_field && field->is_final()) { set_wrote_final(true); } + +#ifdef PPC64 + // Add MemBarRelease for constructors which write volatile field (PPC64). + if (is_field && is_vol) { + set_wrote_volatile(true); + } +#endif } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/postaloc.cpp --- a/src/share/vm/opto/postaloc.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/postaloc.cpp Sun May 21 11:12:48 2017 -0700 @@ -109,9 +109,12 @@ } else if (old->is_Copy()) { // Dead copy of a callee-save value return (old == orig_old); } else if (old->is_MachTemp()) { - return orig_old->is_Con(); + // Temps are all safe to yank. + return true; } else if (old->is_Phi() || old->is_MachConstantBase()) { - return (orig_old->is_Con() && orig_old->is_MachConstant()); + return ((orig_old->is_Con() && orig_old->is_MachConstant()) || + orig_old->is_Phi() || + orig_old->is_MachSpillCopy()); } return false; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/regalloc.cpp --- a/src/share/vm/opto/regalloc.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/regalloc.cpp Sun May 21 11:12:48 2017 -0700 @@ -77,7 +77,9 @@ assert( reg < _matcher._old_SP || (reg >= OptoReg::add(_matcher._old_SP,C->out_preserve_stack_slots()) && reg < _matcher._in_arg_limit) || - reg >= OptoReg::add(_matcher._new_SP,C->out_preserve_stack_slots()), + reg >= OptoReg::add(_matcher._new_SP,C->out_preserve_stack_slots()) || + // Allow return_addr in the out-preserve area. + reg == _matcher.return_addr(), "register allocated in a preserve area" ); return reg2offset_unchecked( reg ); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/regmask.cpp --- a/src/share/vm/opto/regmask.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/regmask.cpp Sun May 21 11:12:48 2017 -0700 @@ -40,8 +40,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif #define RM_SIZE _RM_SIZE /* a constant private to the class RegMask */ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/regmask.hpp --- a/src/share/vm/opto/regmask.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/regmask.hpp Sun May 21 11:12:48 2017 -0700 @@ -43,8 +43,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/adGlobals_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/adGlobals_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/adGlobals_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/adGlobals_ppc_64.hpp" #endif // Some fun naming (textual) substitutions: diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/runtime.cpp --- a/src/share/vm/opto/runtime.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/runtime.cpp Sun May 21 11:12:48 2017 -0700 @@ -83,8 +83,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif @@ -794,11 +797,20 @@ const TypeFunc* OptoRuntime::array_fill_Type() { - // create input type (domain): pointer, int, size_t - const Type** fields = TypeTuple::fields(3 LP64_ONLY( + 1)); + const Type** fields; int argp = TypeFunc::Parms; - fields[argp++] = TypePtr::NOTNULL; - fields[argp++] = TypeInt::INT; + if (SharedRuntime::c_calling_convention_requires_ints_as_longs()) { + // create input type (domain): pointer, int, size_t + fields = TypeTuple::fields(3 LP64_ONLY( + 2)); + fields[argp++] = TypePtr::NOTNULL; + fields[argp++] = TypeLong::LONG; + fields[argp++] = Type::HALF; + } else { + // create input type (domain): pointer, int, size_t + fields = TypeTuple::fields(3 LP64_ONLY( + 1)); + fields[argp++] = TypePtr::NOTNULL; + fields[argp++] = TypeInt::INT; + } fields[argp++] = TypeX_X; // size in whatevers (size_t) LP64_ONLY(fields[argp++] = Type::HALF); // other half of long length const TypeTuple *domain = TypeTuple::make(argp, fields); @@ -979,7 +991,7 @@ nm = CodeCache::find_nmethod(pc); assert(nm != NULL, "No NMethod found"); if (nm->is_native_method()) { - fatal("Native mathod should not have path to exception handling"); + fatal("Native method should not have path to exception handling"); } else { // we are switching to old paradigm: search for exception handler in caller_frame // instead in exception handler of caller_frame.sender() @@ -1008,7 +1020,7 @@ } // If we are forcing an unwind because of stack overflow then deopt is - // irrelevant sice we are throwing the frame away anyway. + // irrelevant since we are throwing the frame away anyway. if (deopting && !force_unwind) { handler_address = SharedRuntime::deopt_blob()->unpack_with_exception(); @@ -1051,7 +1063,7 @@ // Note we enter without the usual JRT wrapper. We will call a helper routine that // will do the normal VM entry. We do it this way so that we can see if the nmethod // we looked up the handler for has been deoptimized in the meantime. If it has been -// we must not use the handler and instread return the deopt blob. +// we must not use the handler and instead return the deopt blob. address OptoRuntime::handle_exception_C(JavaThread* thread) { // // We are in Java not VM and in debug mode we have a NoHandleMark diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/type.cpp --- a/src/share/vm/opto/type.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/type.cpp Sun May 21 11:12:48 2017 -0700 @@ -1971,6 +1971,7 @@ switch (Matcher::vector_ideal_reg(size)) { case Op_VecS: return (TypeVect*)(new TypeVectS(elem, length))->hashcons(); + case Op_RegL: case Op_VecD: case Op_RegD: return (TypeVect*)(new TypeVectD(elem, length))->hashcons(); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/type.hpp --- a/src/share/vm/opto/type.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/type.hpp Sun May 21 11:12:48 2017 -0700 @@ -483,6 +483,9 @@ bool is_con(int i) const { return is_con() && _lo == i; } jlong get_con() const { assert( is_con(), "" ); return _lo; } + // Check for positive 32-bit value. + int is_positive_int() const { return _lo >= 0 && _hi <= (jlong)max_jint; } + virtual bool is_finite() const; // Has a finite value virtual const Type *xmeet( const Type *t ) const; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/opto/vectornode.hpp --- a/src/share/vm/opto/vectornode.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/opto/vectornode.hpp Sun May 21 11:12:48 2017 -0700 @@ -380,7 +380,7 @@ class StoreVectorNode : public StoreNode { public: StoreVectorNode(Node* c, Node* mem, Node* adr, const TypePtr* at, Node* val) - : StoreNode(c, mem, adr, at, val) { + : StoreNode(c, mem, adr, at, val, StoreNode::unordered) { assert(val->is_Vector() || val->is_LoadVector(), "sanity"); init_class_id(Class_StoreVector); } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/forte.cpp --- a/src/share/vm/prims/forte.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/forte.cpp Sun May 21 11:12:48 2017 -0700 @@ -56,7 +56,7 @@ // Native interfaces for use by Forte tools. -#ifndef IA64 +#if !defined(IA64) && !defined(PPC64) class vframeStreamForte : public vframeStreamCommon { public: @@ -640,19 +640,19 @@ void* null_argument_3); #pragma weak collector_func_load #define collector_func_load(x0,x1,x2,x3,x4,x5,x6) \ - ( collector_func_load ? collector_func_load(x0,x1,x2,x3,x4,x5,x6),0 : 0 ) + ( collector_func_load ? collector_func_load(x0,x1,x2,x3,x4,x5,x6),(void)0 : (void)0 ) #endif // __APPLE__ #endif // !_WINDOWS } // end extern "C" -#endif // !IA64 +#endif // !IA64 && !PPC64 void Forte::register_stub(const char* name, address start, address end) { -#if !defined(_WINDOWS) && !defined(IA64) +#if !defined(_WINDOWS) && !defined(IA64) && !defined(PPC64) assert(pointer_delta(end, start, sizeof(jbyte)) < INT_MAX, "Code size exceeds maximum range"); collector_func_load((char*)name, NULL, NULL, start, pointer_delta(end, start, sizeof(jbyte)), 0, NULL); -#endif // !_WINDOWS && !IA64 +#endif // !_WINDOWS && !IA64 && !PPC64 } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/jni.cpp --- a/src/share/vm/prims/jni.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/jni.cpp Sun May 21 11:12:48 2017 -0700 @@ -87,6 +87,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/jvm.cpp --- a/src/share/vm/prims/jvm.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/jvm.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, 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 @@ -75,6 +75,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "jvm_windows.h" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "jvm_aix.h" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "jvm_bsd.h" #endif @@ -2452,7 +2455,6 @@ switch (cp->tag_at(cp_index).value()) { case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_Methodref: - case JVM_CONSTANT_NameAndType: // for invokedynamic return cp->uncached_name_ref_at(cp_index)->as_utf8(); default: fatal("JVM_GetCPMethodNameUTF: illegal constant"); @@ -2470,7 +2472,6 @@ switch (cp->tag_at(cp_index).value()) { case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_Methodref: - case JVM_CONSTANT_NameAndType: // for invokedynamic return cp->uncached_signature_ref_at(cp_index)->as_utf8(); default: fatal("JVM_GetCPMethodSignatureUTF: illegal constant"); @@ -2695,7 +2696,18 @@ int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { // see bug 4399518, 4417214 if ((intptr_t)count <= 0) return -1; - return vsnprintf(str, count, fmt, args); + + int result = vsnprintf(str, count, fmt, args); + // Note: on truncation vsnprintf(3) on Unix returns number of + // characters which would have been written had the buffer been large + // enough; on Windows, it returns -1. We handle both cases here and + // always return -1, and perform null termination. + if ((result > 0 && (size_t)result >= count) || result == -1) { + str[count - 1] = '\0'; + result = -1; + } + + return result; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/jvm.h --- a/src/share/vm/prims/jvm.h Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/jvm.h Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "jvm_windows.h" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "jvm_aix.h" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "jvm_bsd.h" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/jvmtiEnv.cpp --- a/src/share/vm/prims/jvmtiEnv.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/jvmtiEnv.cpp Sun May 21 11:12:48 2017 -0700 @@ -68,6 +68,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/jvmtiImpl.cpp --- a/src/share/vm/prims/jvmtiImpl.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/jvmtiImpl.cpp Sun May 21 11:12:48 2017 -0700 @@ -54,6 +54,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/jvmtiManageCapabilities.cpp --- a/src/share/vm/prims/jvmtiManageCapabilities.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/jvmtiManageCapabilities.cpp Sun May 21 11:12:48 2017 -0700 @@ -117,10 +117,10 @@ jvmtiCapabilities jc; memset(&jc, 0, sizeof(jc)); -#ifndef CC_INTERP +#ifndef ZERO jc.can_pop_frame = 1; jc.can_force_early_return = 1; -#endif // !CC_INTERP +#endif // !ZERO jc.can_get_source_debug_extension = 1; jc.can_access_local_variables = 1; jc.can_maintain_original_method_order = 1; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/methodHandles.cpp --- a/src/share/vm/prims/methodHandles.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/methodHandles.cpp Sun May 21 11:12:48 2017 -0700 @@ -66,9 +66,9 @@ ResourceMark rm; TraceTime timer("MethodHandles adapters generation", TraceStartupTime); - _adapter_code = MethodHandlesAdapterBlob::create(adapter_code_size); + _adapter_code = MethodHandlesAdapterBlob::create(MethodHandles::adapter_code_size()); if (_adapter_code == NULL) - vm_exit_out_of_memory(adapter_code_size, "CodeCache: no room for MethodHandles adapters"); + vm_exit_out_of_memory(MethodHandles::adapter_code_size(), "CodeCache: no room for MethodHandles adapters"); { CodeBuffer code(_adapter_code); MethodHandlesAdapterGenerator g(&code); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/nativeLookup.cpp --- a/src/share/vm/prims/nativeLookup.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/nativeLookup.cpp Sun May 21 11:12:48 2017 -0700 @@ -49,6 +49,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/prims/unsafe.cpp --- a/src/share/vm/prims/unsafe.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/prims/unsafe.cpp Sun May 21 11:12:48 2017 -0700 @@ -170,9 +170,11 @@ oop p = JNIHandles::resolve(obj); \ *(type_name*)index_oop_from_field_offset_long(p, offset) = truncate_##type_name(x) +// Support ordering of "Independent Reads of Independent Writes" (see Parse::do_get_xxx). +// Solution: implement volatile read as fence-load-acquire. #define GET_FIELD_VOLATILE(obj, offset, type_name, v) \ oop p = JNIHandles::resolve(obj); \ - volatile type_name v = OrderAccess::load_acquire((volatile type_name*)index_oop_from_field_offset_long(p, offset)); + PPC64_ONLY(OrderAccess::fence();) volatile type_name v = OrderAccess::load_acquire((volatile type_name*)index_oop_from_field_offset_long(p, offset)); #define SET_FIELD_VOLATILE(obj, offset, type_name, x) \ oop p = JNIHandles::resolve(obj); \ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/arguments.cpp Sun May 21 11:12:48 2017 -0700 @@ -47,6 +47,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif @@ -526,8 +529,9 @@ (os::file_name_strcmp(ext, ".jar") == 0 || os::file_name_strcmp(ext, ".zip") == 0); if (isJarOrZip) { - char* jarpath = NEW_C_HEAP_ARRAY(char, directory_len + 2 + strlen(name), mtInternal); - sprintf(jarpath, "%s%s%s", directory, dir_sep, name); + size_t length = directory_len + 2 + strlen(name); + char* jarpath = NEW_C_HEAP_ARRAY(char, length, mtInternal); + jio_snprintf(jarpath, length, "%s%s%s", directory, dir_sep, name); path = add_to_path(path, jarpath, false); FREE_C_HEAP_ARRAY(char, jarpath, mtInternal); } @@ -670,9 +674,10 @@ } else if (new_len == 0) { value = old_value; } else { - char* buf = NEW_C_HEAP_ARRAY(char, old_len + 1 + new_len + 1, mtInternal); + size_t length = old_len + 1 + new_len + 1; + char* buf = NEW_C_HEAP_ARRAY(char, length, mtInternal); // each new setting adds another LINE to the switch: - sprintf(buf, "%s\n%s", old_value, new_value); + jio_snprintf(buf, length, "%s\n%s", old_value, new_value); value = buf; free_this_too = buf; } @@ -779,15 +784,17 @@ if (args == NULL || count == 0) { return NULL; } - size_t length = strlen(args[0]) + 1; // add 1 for the null terminator - for (int i = 1; i < count; i++) { - length += strlen(args[i]) + 1; // add 1 for a space + size_t length = 0; + for (int i = 0; i < count; i++) { + length += strlen(args[i]) + 1; // add 1 for a space or NULL terminating character } char* s = NEW_RESOURCE_ARRAY(char, length); - strcpy(s, args[0]); - for (int j = 1; j < count; j++) { - strcat(s, " "); - strcat(s, args[j]); + char* dst = s; + for (int j = 0; j < count; j++) { + size_t offset = strlen(args[j]) + 1; // add 1 for a space or NULL terminating character + jio_snprintf(dst, length, "%s ", args[j]); // jio_snprintf will replace the last space character with NULL character + dst += offset; + length -= offset; } return (const char*) s; } @@ -1657,7 +1664,7 @@ // Feed the cache size setting into the JDK char buffer[1024]; - sprintf(buffer, "java.lang.Integer.IntegerCache.high=" INTX_FORMAT, AutoBoxCacheMax); + jio_snprintf(buffer, 1024, "java.lang.Integer.IntegerCache.high=" INTX_FORMAT, AutoBoxCacheMax); add_property(buffer); } if (AggressiveOpts && FLAG_IS_DEFAULT(BiasedLockingStartupDelay)) { @@ -2319,7 +2326,9 @@ char *options = NULL; if(pos != NULL) { - options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(pos + 1) + 1, mtInternal), pos + 1); + size_t length = strlen(pos + 1) + 1; + options = NEW_C_HEAP_ARRAY(char, length, mtInternal); + jio_snprintf(options, length, "%s", pos + 1); } add_init_agent(name, options, is_absolute_path); @@ -2327,7 +2336,9 @@ // -javaagent } else if (match_option(option, "-javaagent:", &tail)) { if(tail != NULL) { - char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1, mtInternal), tail); + size_t length = strlen(tail) + 1; + char *options = NEW_C_HEAP_ARRAY(char, length, mtInternal); + jio_snprintf(options, length, "%s", tail); add_init_agent("instrument", options, false); } // -Xnoclassgc @@ -3090,8 +3101,7 @@ } } else { char buffer[256]; - strcpy(buffer, "java.awt.headless="); - strcat(buffer, envbuffer); + jio_snprintf(buffer, 256, "java.awt.headless=%s", envbuffer); if (!add_property(buffer)) { return JNI_ENOMEM; } @@ -3482,8 +3492,8 @@ UseBiasedLocking = false; } -#ifdef CC_INTERP - // Clear flags not supported by the C++ interpreter +#ifdef ZERO + // Clear flags not supported on zero. FLAG_SET_DEFAULT(ProfileInterpreter, false); FLAG_SET_DEFAULT(UseBiasedLocking, false); LP64_ONLY(FLAG_SET_DEFAULT(UseCompressedOops, false)); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/atomic.cpp --- a/src/share/vm/runtime/atomic.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/atomic.cpp Sun May 21 11:12:48 2017 -0700 @@ -33,6 +33,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif @@ -66,6 +69,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "atomic_linux_ppc.inline.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "atomic_aix_ppc.inline.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "atomic_bsd_x86.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/biasedLocking.cpp --- a/src/share/vm/runtime/biasedLocking.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/biasedLocking.cpp Sun May 21 11:12:48 2017 -0700 @@ -233,8 +233,10 @@ // Fix up highest lock to contain displaced header and point // object at it highest_lock->set_displaced_header(unbiased_prototype); - // Reset object header to point to displaced mark - obj->set_mark(markOopDesc::encode(highest_lock)); + // Reset object header to point to displaced mark. + // Must release storing the lock address for platforms without TSO + // ordering (e.g. ppc). + obj->release_set_mark(markOopDesc::encode(highest_lock)); assert(!obj->mark()->has_bias_pattern(), "illegal mark state: stack lock used bias bit"); if (TraceBiasedLocking && (Verbose || !is_bulk)) { tty->print_cr(" Revoked bias of currently-locked object"); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/deoptimization.cpp --- a/src/share/vm/runtime/deoptimization.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/deoptimization.cpp Sun May 21 11:12:48 2017 -0700 @@ -81,10 +81,13 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/ad_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/ad_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/ad_ppc_32.hpp" #endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/ad_ppc_64.hpp" #endif +#endif // COMPILER2 bool DeoptimizationMarker::_is_active = false; @@ -1793,8 +1796,6 @@ trap_reason_name(reason), recomp_flag ? " recompiled" : ""); } - if (len >= buflen) - buf[buflen-1] = '\0'; return buf; } @@ -1862,8 +1863,6 @@ len = jio_snprintf(buf, buflen, "reason='%s' action='%s' index='%d'", reason, action, unloaded_class_index); } - if (len >= buflen) - buf[buflen-1] = '\0'; return buf; } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/fprofiler.hpp --- a/src/share/vm/runtime/fprofiler.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/fprofiler.hpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/frame.cpp --- a/src/share/vm/runtime/frame.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/frame.cpp Sun May 21 11:12:48 2017 -0700 @@ -930,22 +930,11 @@ if (istate->msg() == BytecodeInterpreter::call_method) { f->do_oop((oop*)&istate->_result._to_call._callee); } - #endif /* CC_INTERP */ -#if !defined(PPC) || defined(ZERO) - if (m->is_native()) { -#ifdef CC_INTERP - f->do_oop((oop*)&istate->_oop_temp); -#else - f->do_oop((oop*)( fp() + interpreter_frame_oop_temp_offset )); -#endif /* CC_INTERP */ + if (m->is_native() PPC32_ONLY(&& m->is_static())) { + f->do_oop(interpreter_frame_temp_oop_addr()); } -#else // PPC - if (m->is_native() && m->is_static()) { - f->do_oop(interpreter_frame_mirror_addr()); - } -#endif // PPC int max_locals = m->is_native() ? m->size_of_parameters() : m->max_locals(); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/frame.hpp --- a/src/share/vm/runtime/frame.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/frame.hpp Sun May 21 11:12:48 2017 -0700 @@ -47,10 +47,13 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/adGlobals_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/adGlobals_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/adGlobals_ppc_32.hpp" #endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/adGlobals_ppc_64.hpp" #endif +#endif // COMPILER2 #ifdef ZERO #ifdef TARGET_ARCH_zero # include "stack_zero.hpp" @@ -312,6 +315,9 @@ void interpreter_frame_set_monitor_end(BasicObjectLock* value); #endif // CC_INTERP + // Address of the temp oop in the frame. Needed as GC root. + oop* interpreter_frame_temp_oop_addr() const; + // BasicObjectLocks: // // interpreter_frame_monitor_begin is higher in memory than interpreter_frame_monitor_end @@ -348,9 +354,6 @@ void interpreter_frame_set_method(methodOop method); methodOop* interpreter_frame_method_addr() const; constantPoolCacheOop* interpreter_frame_cache_addr() const; -#ifdef PPC - oop* interpreter_frame_mirror_addr() const; -#endif public: // Entry frames diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/frame.inline.hpp --- a/src/share/vm/runtime/frame.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/frame.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -87,6 +87,13 @@ return is_entry_frame() && entry_frame_is_first(); } +#ifdef CC_INTERP +inline oop* frame::interpreter_frame_temp_oop_addr() const { + interpreterState istate = get_interpreterState(); + return (oop *)&istate->_oop_temp; +} +#endif // CC_INTERP + // here are the platform-dependent bodies: #ifdef TARGET_ARCH_x86 diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/globals.hpp Sun May 21 11:12:48 2017 -0700 @@ -61,6 +61,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "globals_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "globals_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "globals_bsd.hpp" #endif @@ -88,6 +91,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "globals_linux_ppc.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "globals_aix_ppc.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "globals_bsd_x86.hpp" #endif @@ -116,6 +122,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "c1_globals_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "c1_globals_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "c1_globals_bsd.hpp" #endif @@ -130,6 +139,9 @@ #ifdef TARGET_ARCH_arm # include "c2_globals_arm.hpp" #endif +#ifdef TARGET_ARCH_ppc +# include "c2_globals_ppc.hpp" +#endif #ifdef TARGET_OS_FAMILY_linux # include "c2_globals_linux.hpp" #endif @@ -139,6 +151,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "c2_globals_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "c2_globals_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "c2_globals_bsd.hpp" #endif @@ -167,7 +182,6 @@ define_pd_global(intx, OnStackReplacePercentage, 0); define_pd_global(bool, ResizeTLAB, false); define_pd_global(intx, FreqInlineSize, 0); -define_pd_global(intx, InlineSmallCode, 0); define_pd_global(intx, NewSizeThreadIncrease, 4*K); define_pd_global(intx, InlineClassNatives, true); define_pd_global(intx, InlineUnsafeOps, true); @@ -2714,6 +2728,11 @@ product_pd(bool, ProfileInterpreter, \ "Profile at the bytecode level during interpretation") \ \ + develop(bool, TraceProfileInterpreter, false, \ + "Trace profiling at the bytecode level during interpretation. " \ + "This outputs the profiling information collected to improve " \ + "jit compilation.") \ + \ develop_pd(bool, ProfileTraps, \ "Profile deoptimization traps at the bytecode level") \ \ @@ -3154,7 +3173,8 @@ "disable this feature") \ \ /* code cache parameters */ \ - develop(uintx, CodeCacheSegmentSize, 64, \ + /* ppc64 has large code-entry alignment. */ \ + develop(uintx, CodeCacheSegmentSize, 64 PPC64_ONLY(+64), \ "Code cache segment size (in bytes) - smallest unit of " \ "allocation") \ \ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/handles.cpp --- a/src/share/vm/runtime/handles.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/handles.cpp Sun May 21 11:12:48 2017 -0700 @@ -38,6 +38,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/handles.inline.hpp --- a/src/share/vm/runtime/handles.inline.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/handles.inline.hpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/interfaceSupport.hpp --- a/src/share/vm/runtime/interfaceSupport.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/interfaceSupport.hpp Sun May 21 11:12:48 2017 -0700 @@ -44,6 +44,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -118,6 +121,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "interfaceSupport_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "interfaceSupport_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "interfaceSupport_bsd.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/java.cpp --- a/src/share/vm/runtime/java.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/java.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, 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 @@ -88,6 +88,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -769,25 +772,36 @@ } void JDK_Version::to_string(char* buffer, size_t buflen) const { + assert(buffer && buflen > 0, "call with useful buffer"); size_t index = 0; if (!is_valid()) { jio_snprintf(buffer, buflen, "%s", "(uninitialized)"); } else if (is_partially_initialized()) { jio_snprintf(buffer, buflen, "%s", "(uninitialized) pre-1.6.0"); } else { - index += jio_snprintf( + int rc = jio_snprintf( &buffer[index], buflen - index, "%d.%d", _major, _minor); + if (rc == -1) return; + index += rc; if (_micro > 0) { - index += jio_snprintf(&buffer[index], buflen - index, ".%d", _micro); + rc = jio_snprintf(&buffer[index], buflen - index, ".%d", _micro); + if (rc == -1) return; + index += rc; } if (_update > 0) { - index += jio_snprintf(&buffer[index], buflen - index, "_%02d", _update); + rc = jio_snprintf(&buffer[index], buflen - index, "_%02d", _update); + if (rc == -1) return; + index += rc; } if (_special > 0) { - index += jio_snprintf(&buffer[index], buflen - index, "%c", _special); + rc = jio_snprintf(&buffer[index], buflen - index, "%c", _special); + if (rc == -1) return; + index += rc; } if (_build > 0) { - index += jio_snprintf(&buffer[index], buflen - index, "-b%02d", _build); + rc = jio_snprintf(&buffer[index], buflen - index, "-b%02d", _build); + if (rc == -1) return; + index += rc; } } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/javaCalls.cpp --- a/src/share/vm/runtime/javaCalls.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/javaCalls.cpp Sun May 21 11:12:48 2017 -0700 @@ -48,6 +48,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/javaCalls.hpp --- a/src/share/vm/runtime/javaCalls.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/javaCalls.hpp Sun May 21 11:12:48 2017 -0700 @@ -54,6 +54,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/javaFrameAnchor.hpp --- a/src/share/vm/runtime/javaFrameAnchor.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/javaFrameAnchor.hpp Sun May 21 11:12:48 2017 -0700 @@ -50,6 +50,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "orderAccess_linux_ppc.inline.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "orderAccess_aix_ppc.inline.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "orderAccess_bsd_x86.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/jniHandles.cpp --- a/src/share/vm/runtime/jniHandles.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/jniHandles.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/memprofiler.cpp --- a/src/share/vm/runtime/memprofiler.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/memprofiler.cpp Sun May 21 11:12:48 2017 -0700 @@ -46,6 +46,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/mutex.cpp --- a/src/share/vm/runtime/mutex.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/mutex.cpp Sun May 21 11:12:48 2017 -0700 @@ -39,6 +39,10 @@ # include "mutex_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "mutex_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "mutex_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/mutexLocker.cpp --- a/src/share/vm/runtime/mutexLocker.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/mutexLocker.cpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/mutexLocker.hpp --- a/src/share/vm/runtime/mutexLocker.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/mutexLocker.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/objectMonitor.cpp --- a/src/share/vm/runtime/objectMonitor.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/objectMonitor.cpp Sun May 21 11:12:48 2017 -0700 @@ -52,19 +52,15 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" #endif -#if defined(__GNUC__) && !defined(IA64) - // Need to inhibit inlining for older versions of GCC to avoid build-time failures - #define ATTR __attribute__((noinline)) -#else - #define ATTR -#endif - - #ifdef DTRACE_ENABLED // Only bother with this argument setup if dtrace is available @@ -316,7 +312,7 @@ } } -void ATTR ObjectMonitor::enter(TRAPS) { +void ObjectMonitor::enter(TRAPS) { // The following code is ordered to check the most common cases first // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors. Thread * const Self = THREAD ; @@ -500,7 +496,7 @@ } } -void ATTR ObjectMonitor::EnterI (TRAPS) { +void ObjectMonitor::EnterI (TRAPS) { Thread * Self = THREAD ; assert (Self->is_Java_thread(), "invariant") ; assert (((JavaThread *) Self)->thread_state() == _thread_blocked , "invariant") ; @@ -749,7 +745,7 @@ // Knob_Reset and Knob_SpinAfterFutile support and restructuring the // loop accordingly. -void ATTR ObjectMonitor::ReenterI (Thread * Self, ObjectWaiter * SelfNode) { +void ObjectMonitor::ReenterI (Thread * Self, ObjectWaiter * SelfNode) { assert (Self != NULL , "invariant") ; assert (SelfNode != NULL , "invariant") ; assert (SelfNode->_thread == Self , "invariant") ; @@ -954,7 +950,7 @@ // Both impinge on OS scalability. Given that, at most one thread parked on // a monitor will use a timer. -void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) { +void ObjectMonitor::exit(bool not_suspended, TRAPS) { Thread * Self = THREAD ; if (THREAD != _owner) { if (THREAD->is_lock_owned((address) _owner)) { diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/os.cpp --- a/src/share/vm/runtime/os.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/os.cpp Sun May 21 11:12:48 2017 -0700 @@ -61,6 +61,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" @@ -985,7 +989,7 @@ // if C stack is walkable beyond current frame. The check for fp() is not // necessary on Sparc, but it's harmless. bool os::is_first_C_frame(frame* fr) { -#ifdef IA64 +#if (defined(IA64) && !defined(AIX)) // In order to walk native frames on Itanium, we need to access the unwind // table, which is inside ELF. We don't want to parse ELF after fatal error, // so return true for IA64. If we need to support C stack walking on IA64, diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/os.hpp --- a/src/share/vm/runtime/os.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/os.hpp Sun May 21 11:12:48 2017 -0700 @@ -44,6 +44,10 @@ #ifdef TARGET_OS_FAMILY_windows # include "jvm_windows.h" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "jvm_aix.h" +# include +#endif #ifdef TARGET_OS_FAMILY_bsd # include "jvm_bsd.h" # include @@ -757,6 +761,10 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.hpp" +# include "os_posix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.hpp" # include "os_posix.hpp" @@ -785,6 +793,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "os_linux_ppc.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "os_aix_ppc.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "os_bsd_x86.hpp" #endif @@ -938,7 +949,9 @@ // It'd also be eligible for inlining on many platforms. extern "C" int SpinPause () ; +#ifndef SAFEFETCH_STUBS extern "C" int SafeFetch32 (int * adr, int errValue) ; extern "C" intptr_t SafeFetchN (intptr_t * adr, intptr_t errValue) ; +#endif #endif // SHARE_VM_RUNTIME_OS_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/osThread.hpp --- a/src/share/vm/runtime/osThread.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/osThread.hpp Sun May 21 11:12:48 2017 -0700 @@ -105,6 +105,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "osThread_windows.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "osThread_aix.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "osThread_bsd.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/safepoint.cpp --- a/src/share/vm/runtime/safepoint.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/safepoint.cpp Sun May 21 11:12:48 2017 -0700 @@ -80,6 +80,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -751,6 +754,7 @@ #define PTR_PAD " " #endif +#ifdef SPARC static void print_ptrs(intptr_t oldptr, intptr_t newptr, bool wasoop) { bool is_oop = newptr ? ((oop)newptr)->is_oop() : false; tty->print_cr(PTR_FORMAT PTR_PAD " %s %c " PTR_FORMAT PTR_PAD " %s %s", @@ -765,7 +769,6 @@ newptr, is_oop?"oop":" ", (wasoop && !is_oop) ? "STALE" : ((wasoop==false&&is_oop==false&&oldptr !=newptr)?"STOMP":" ")); } -#ifdef SPARC static void print_me(intptr_t *new_sp, intptr_t *old_sp, bool *was_oops) { #ifdef _LP64 tty->print_cr("--------+------address-----+------before-----------+-------after----------+"); diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/sharedRuntime.cpp --- a/src/share/vm/runtime/sharedRuntime.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/sharedRuntime.cpp Sun May 21 11:12:48 2017 -0700 @@ -404,7 +404,7 @@ #endif -#if defined(__SOFTFP__) || defined(PPC) +#if defined(__SOFTFP__) || defined(PPC32) double SharedRuntime::dsqrt(double f) { return sqrt(f); } @@ -2712,6 +2712,69 @@ } #endif // ndef HAVE_DTRACE_H +int SharedRuntime::convert_ints_to_longints_argcnt(int in_args_count, BasicType* in_sig_bt) { + assert(SharedRuntime::c_calling_convention_requires_ints_as_longs(), ""); + + int argcnt = in_args_count; + for (int in = 0; in < in_args_count; in++) { + BasicType bt = in_sig_bt[in]; + switch (bt) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + argcnt++; + break; + default: + break; + } + } + + return argcnt; +} + +void SharedRuntime::convert_ints_to_longints(int i2l_argcnt, int& in_args_count, + BasicType*& in_sig_bt, + VMRegPair*& in_regs) { + assert(SharedRuntime::c_calling_convention_requires_ints_as_longs(), ""); + + VMRegPair *new_in_regs = NEW_RESOURCE_ARRAY(VMRegPair, i2l_argcnt); + BasicType *new_in_sig_bt = NEW_RESOURCE_ARRAY(BasicType, i2l_argcnt); + + int argcnt = 0; + for (int in = 0; in < in_args_count; in++, argcnt++) { + BasicType bt = in_sig_bt[in]; + VMRegPair reg = in_regs[in]; + switch (bt) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + // convert (bt) to (T_LONG,bt) + new_in_sig_bt[argcnt ] = T_LONG; + new_in_sig_bt[argcnt+1] = bt; + assert(reg.first()->is_valid() && !reg.second()->is_valid(), ""); + new_in_regs[argcnt ].set2(reg.first()); + new_in_regs[argcnt+1].set_bad(); + argcnt++; + break; + default: + // no conversion needed + new_in_sig_bt[argcnt] = bt; + new_in_regs[argcnt] = reg; + break; + } + } + assert(argcnt == i2l_argcnt, "must match"); + + in_regs = new_in_regs; + in_sig_bt = new_in_sig_bt; + in_args_count = i2l_argcnt; +} + + // ------------------------------------------------------------------------- // Java-Java calling convention // (what you use when Java calls Java) @@ -2732,9 +2795,7 @@ // ResourceObject, so do not put any ResourceMarks in here. char *s = sig->as_C_string(); int len = (int)strlen(s); - *s++; len--; // Skip opening paren - char *t = s+len; - while( *(--t) != ')' ) ; // Find close paren + s++; len--; // Skip opening paren BasicType *sig_bt = NEW_RESOURCE_ARRAY( BasicType, 256 ); VMRegPair *regs = NEW_RESOURCE_ARRAY( VMRegPair, 256 ); @@ -2743,7 +2804,7 @@ sig_bt[cnt++] = T_OBJECT; // Receiver is argument 0; not in signature } - while( s < t ) { + while( *s != ')' ) { // Find closing right paren switch( *s++ ) { // Switch on signature character case 'B': sig_bt[cnt++] = T_BYTE; break; case 'C': sig_bt[cnt++] = T_CHAR; break; @@ -2816,9 +2877,9 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *thread) ) -#ifdef IA64 +#if defined(IA64) && !defined(AIX) ShouldNotReachHere(); // NYI -#endif /* IA64 */ +#endif // // This code is dependent on the memory layout of the interpreter local diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/sharedRuntime.hpp --- a/src/share/vm/runtime/sharedRuntime.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/sharedRuntime.hpp Sun May 21 11:12:48 2017 -0700 @@ -139,7 +139,7 @@ static double dabs(double f); #endif -#if defined(__SOFTFP__) || defined(PPC) +#if defined(__SOFTFP__) || defined(PPC32) static double dsqrt(double f); #endif @@ -355,7 +355,28 @@ const VMRegPair* regs) NOT_DEBUG_RETURN; // Ditto except for calling C - static int c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed); + // + // C argument in register AND stack slot. + // Some architectures require that an argument must be passed in a register + // AND in a stack slot. These architectures provide a second VMRegPair array + // to be filled by the c_calling_convention method. On other architectures, + // NULL is being passed as the second VMRegPair array, so arguments are either + // passed in a register OR in a stack slot. + static int c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, VMRegPair *regs2, + int total_args_passed); + + // Querry whether this platforms requires that 32 bit integers are + // passed as 64 bit wide entities in C calls. (This is the case on PPC + // and z/Arch.) + static bool c_calling_convention_requires_ints_as_longs(); + // Compute the new number of arguments in the signature if 32 bit ints + // must be converted to longs. + static int convert_ints_to_longints_argcnt(int in_args_count, BasicType* in_sig_bt); + // Adapt a method's signature if it contains 32 bit integers that must + // be converted to longs. + static void convert_ints_to_longints(int i2l_argcnt, int& in_args_count, + BasicType*& in_sig_bt, + VMRegPair*& in_regs ); // Generate I2C and C2I adapters. These adapters are simple argument marshalling // blobs. Unlike adapters in the tiger and earlier releases the code in these @@ -369,7 +390,7 @@ // location for the interpreter to record. This is used by the frame code // to correct the sender code to match up with the stack pointer when the // thread left the compiled code. In addition it allows the interpreter - // to remove the space the c2i adapter allocated to do it argument conversion. + // to remove the space the c2i adapter allocated to do its argument conversion. // Although a c2i blob will always run interpreted even if compiled code is // present if we see that compiled code is present the compiled call site diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/sharedRuntimeTrans.cpp --- a/src/share/vm/runtime/sharedRuntimeTrans.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/sharedRuntimeTrans.cpp Sun May 21 11:12:48 2017 -0700 @@ -56,10 +56,12 @@ # define __LO(x) *(1+(int*)&x) #endif +#if !defined(AIX) double copysign(double x, double y) { __HI(x) = (__HI(x)&0x7fffffff)|(__HI(y)&0x80000000); return x; } +#endif /* * ==================================================== @@ -85,6 +87,7 @@ hugeX = 1.0e+300, tiny = 1.0e-300; +#if !defined(AIX) double scalbn (double x, int n) { int k,hx,lx; hx = __HI(x); @@ -111,6 +114,7 @@ __HI(x) = (hx&0x800fffff)|(k<<20); return x*twom54; } +#endif /* __ieee754_log(x) * Return the logrithm of x diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/sharedRuntimeTrig.cpp --- a/src/share/vm/runtime/sharedRuntimeTrig.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/sharedRuntimeTrig.cpp Sun May 21 11:12:48 2017 -0700 @@ -656,6 +656,13 @@ C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ +// Aix defines hz to be 100, see sys/m_param.h. +#ifdef AIX +#ifdef hz +#undef hz +#endif +#endif + static double __kernel_cos(double x, double y) { double a,hz,z,r,qx; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/signature.cpp --- a/src/share/vm/runtime/signature.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/signature.cpp Sun May 21 11:12:48 2017 -0700 @@ -224,7 +224,49 @@ _index = 0; expect('('); Symbol* sig = _signature; - while (sig->byte_at(_index) != ')') _index++; + // Need to skip over each type in the signature's argument list until a + // closing ')' is found., then get the return type. We cannot just scan + // for the first ')' because ')' is a legal character in a type name. + while (sig->byte_at(_index) != ')') { + switch(sig->byte_at(_index)) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 'V': + { + _index++; + } + break; + case 'L': + { + while (sig->byte_at(_index++) != ';') ; + } + break; + case '[': + { + int begin = ++_index; + skip_optional_size(); + while (sig->byte_at(_index) == '[') { + _index++; + skip_optional_size(); + } + if (sig->byte_at(_index) == 'L') { + while (sig->byte_at(_index++) != ';') ; + } else { + _index++; + } + } + break; + default: + ShouldNotReachHere(); + break; + } + } expect(')'); // Parse return type _parameter_index = -1; diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/stubRoutines.cpp --- a/src/share/vm/runtime/stubRoutines.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/stubRoutines.cpp Sun May 21 11:12:48 2017 -0700 @@ -133,6 +133,15 @@ double (* StubRoutines::_intrinsic_cos )(double) = NULL; double (* StubRoutines::_intrinsic_tan )(double) = NULL; +#ifdef SAFEFETCH_STUBS +address StubRoutines::_safefetch32_entry = NULL; +address StubRoutines::_safefetch32_fault_pc = NULL; +address StubRoutines::_safefetch32_continuation_pc = NULL; +address StubRoutines::_safefetchN_entry = NULL; +address StubRoutines::_safefetchN_fault_pc = NULL; +address StubRoutines::_safefetchN_continuation_pc = NULL; +#endif + // Initialization // // Note: to break cycle with universe initialization, stubs are generated in two phases. diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/stubRoutines.hpp --- a/src/share/vm/runtime/stubRoutines.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/stubRoutines.hpp Sun May 21 11:12:48 2017 -0700 @@ -114,8 +114,11 @@ #ifdef TARGET_ARCH_MODEL_arm # include "stubRoutines_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "stubRoutines_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "stubRoutines_ppc_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "stubRoutines_ppc_64.hpp" #endif @@ -218,6 +221,19 @@ static double (*_intrinsic_cos)(double); static double (*_intrinsic_tan)(double); +#ifdef SAFEFETCH_STUBS + // On platforms where the compiler does not properly support inline + // assembly safefetch can be implemented as stub routines. This also + // allows to use a single implementation if an architecture is + // supported on several os platforms. + static address _safefetch32_entry; + static address _safefetch32_fault_pc; + static address _safefetch32_continuation_pc; + static address _safefetchN_entry; + static address _safefetchN_fault_pc; + static address _safefetchN_continuation_pc; +#endif + public: // Initialization/Testing static void initialize1(); // must happen before universe::genesis @@ -238,7 +254,9 @@ static jint verify_oop_count() { return _verify_oop_count; } static jint* verify_oop_count_addr() { return &_verify_oop_count; } // a subroutine for debugging the GC - static address verify_oop_subroutine_entry_address() { return (address)&_verify_oop_subroutine_entry; } + static address verify_oop_subroutine_entry_address() { return (address)&_verify_oop_subroutine_entry; } + // We need this address to lazily jump to in interpreter. + static address throw_StackOverflowError_entry_address() { return (address)&_throw_StackOverflowError_entry; } static address catch_exception_entry() { return _catch_exception_entry; } @@ -375,6 +393,32 @@ return _intrinsic_tan(d); } +#ifdef SAFEFETCH_STUBS + typedef int (*SafeFetch32Stub)(int* adr, int errValue); + typedef intptr_t (*SafeFetchNStub) (intptr_t* adr, intptr_t errValue); + + static SafeFetch32Stub SafeFetch32_stub() { return CAST_TO_FN_PTR(SafeFetch32Stub, _safefetch32_entry); } + static SafeFetchNStub SafeFetchN_stub() { return CAST_TO_FN_PTR(SafeFetchNStub, _safefetchN_entry); } + + static bool is_safefetch_fault(address pc) { + return pc != NULL && + (pc == _safefetch32_fault_pc || + pc == _safefetchN_fault_pc); + } + + static address continuation_for_safefetch_fault(address pc) { + assert(_safefetch32_continuation_pc != NULL && + _safefetchN_continuation_pc != NULL, + "not initialized"); + + if (pc == _safefetch32_fault_pc) return _safefetch32_continuation_pc; + if (pc == _safefetchN_fault_pc) return _safefetchN_continuation_pc; + + ShouldNotReachHere(); + return NULL; + } +#endif + // // Default versions of the above arraycopy functions for platforms which do // not have specialized versions @@ -394,4 +438,19 @@ static void arrayof_oop_copy_uninit(HeapWord* src, HeapWord* dest, size_t count); }; +#ifdef SAFEFETCH_STUBS + static int SafeFetch32(int* adr, int errValue) { + return StubRoutines::SafeFetch32_stub()(adr, errValue); + } + static intptr_t SafeFetchN(intptr_t* adr, intptr_t errValue) { + return StubRoutines::SafeFetchN_stub()(adr, errValue); + } + static bool CanUseSafeFetch32() { return StubRoutines::SafeFetch32_stub() ? true : false; } + static bool CanUseSafeFetchN() { return StubRoutines::SafeFetchN_stub() ? true : false; } +#else + // Declared as extern "C" in os.hpp. + static bool CanUseSafeFetch32() { return true; } + static bool CanUseSafeFetchN() { return true; } +#endif + #endif // SHARE_VM_RUNTIME_STUBROUTINES_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/sweeper.cpp --- a/src/share/vm/runtime/sweeper.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/sweeper.cpp Sun May 21 11:12:48 2017 -0700 @@ -252,7 +252,8 @@ sweep_code_cache(); _invocations--; } - _sweep_started = 0; + // Release work, because another compiler thread could continue. + OrderAccess::release_store((int*)&_sweep_started, 0); } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/synchronizer.cpp --- a/src/share/vm/runtime/synchronizer.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/synchronizer.cpp Sun May 21 11:12:48 2017 -0700 @@ -51,18 +51,15 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" #endif -#if defined(__GNUC__) && !defined(IA64) - // Need to inhibit inlining for older versions of GCC to avoid build-time failures - #define ATTR __attribute__((noinline)) -#else - #define ATTR -#endif - // The "core" versions of monitor enter and exit reside in this file. // The interpreter and compilers contain specialized transliterated // variants of the enter-exit fast-path operations. See i486.ad fast_lock(), @@ -452,7 +449,7 @@ // and explicit fences (barriers) to control for architectural reordering performed // by the CPU(s) or platform. -static int MBFence (int x) { OrderAccess::fence(); return x; } +// static int MBFence (int x) { OrderAccess::fence(); return x; } struct SharedGlobals { // These are highly shared mostly-read variables. @@ -945,7 +942,7 @@ assert(freetally == Self->omFreeCount, "free count off"); } */ -ObjectMonitor * ATTR ObjectSynchronizer::omAlloc (Thread * Self) { +ObjectMonitor * ObjectSynchronizer::omAlloc (Thread * Self) { // A large MAXPRIVATE value reduces both list lock contention // and list coherency traffic, but also tends to increase the // number of objectMonitors in circulation as well as the STW @@ -1192,7 +1189,7 @@ // multiple locks occupy the same $ line. Padding might be appropriate. -ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) { +ObjectMonitor * ObjectSynchronizer::inflate (Thread * Self, oop object) { // Inflate mutates the heap ... // Relaxing assertion for bug 6320749. assert (Universe::verify_in_progress() || diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/task.cpp --- a/src/share/vm/runtime/task.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/task.cpp Sun May 21 11:12:48 2017 -0700 @@ -39,6 +39,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/thread.cpp --- a/src/share/vm/runtime/thread.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/thread.cpp Sun May 21 11:12:48 2017 -0700 @@ -93,6 +93,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" @@ -311,6 +315,9 @@ void Thread::record_stack_base_and_size() { set_stack_base(os::current_stack_base()); set_stack_size(os::current_stack_size()); + if (is_Java_thread()) { + ((JavaThread*) this)->set_stack_overflow_limit(); + } // CR 7190089: on Solaris, primordial thread's stack is adjusted // in initialize_thread(). Without the adjustment, stack size is // incorrect if stack is set to unlimited (ulimit -s unlimited). diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/thread.hpp --- a/src/share/vm/runtime/thread.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/thread.hpp Sun May 21 11:12:48 2017 -0700 @@ -41,6 +41,7 @@ #include "runtime/stubRoutines.hpp" #include "runtime/threadLocalStorage.hpp" #include "runtime/unhandledOops.hpp" +#include "utilities/macros.hpp" #include "services/memRecorder.hpp" #include "trace/traceBackend.hpp" #include "trace/traceMacros.hpp" @@ -895,7 +896,11 @@ private: - StackGuardState _stack_guard_state; + StackGuardState _stack_guard_state; + + // Precompute the limit of the stack as used in stack overflow checks. + // We load it from here to simplify the stack overflow check in assembly. + address _stack_overflow_limit; // Compiler exception handling (NOTE: The _exception_oop is *NOT* the same as _pending_exception. It is // used to temp. parsing values into and out of the runtime system during exception handling for compiled @@ -1029,8 +1034,19 @@ address last_Java_pc(void) { return _anchor.last_Java_pc(); } // Safepoint support +#if defined(PPC64) + // Use membars when accessing volatile _thread_state. See + // Threads::create_vm() for size checks. + JavaThreadState thread_state() const { + return (JavaThreadState) OrderAccess::load_acquire((volatile jint*)&_thread_state); + } + void set_thread_state(JavaThreadState s) { + OrderAccess::release_store((volatile jint*)&_thread_state, (jint)s); + } +#else JavaThreadState thread_state() const { return _thread_state; } - void set_thread_state(JavaThreadState s) { _thread_state=s; } + void set_thread_state(JavaThreadState s) { _thread_state = s; } +#endif ThreadSafepointState *safepoint_state() const { return _safepoint_state; } void set_safepoint_state(ThreadSafepointState *state) { _safepoint_state = state; } bool is_at_poll_safepoint() { return _safepoint_state->is_at_poll_safepoint(); } @@ -1301,6 +1317,14 @@ // and reguard if possible. bool reguard_stack(void); + address stack_overflow_limit() { return _stack_overflow_limit; } + void set_stack_overflow_limit() { + _stack_overflow_limit = _stack_base - _stack_size + + ((StackShadowPages + + StackYellowPages + + StackRedPages) * os::vm_page_size()); + } + // Misc. accessors/mutators void set_do_not_unlock(void) { _do_not_unlock_if_synchronized = true; } void clr_do_not_unlock(void) { _do_not_unlock_if_synchronized = false; } @@ -1335,6 +1359,7 @@ static ByteSize exception_oop_offset() { return byte_offset_of(JavaThread, _exception_oop ); } static ByteSize exception_pc_offset() { return byte_offset_of(JavaThread, _exception_pc ); } static ByteSize exception_handler_pc_offset() { return byte_offset_of(JavaThread, _exception_handler_pc); } + static ByteSize stack_overflow_limit_offset() { return byte_offset_of(JavaThread, _stack_overflow_limit); } static ByteSize is_method_handle_return_offset() { return byte_offset_of(JavaThread, _is_method_handle_return); } static ByteSize stack_guard_state_offset() { return byte_offset_of(JavaThread, _stack_guard_state ); } static ByteSize suspend_flags_offset() { return byte_offset_of(JavaThread, _suspend_flags ); } @@ -1695,6 +1720,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "thread_linux_ppc.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "thread_aix_ppc.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "thread_bsd_x86.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/threadLocalStorage.cpp --- a/src/share/vm/runtime/threadLocalStorage.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/threadLocalStorage.cpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/threadLocalStorage.hpp --- a/src/share/vm/runtime/threadLocalStorage.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/threadLocalStorage.hpp Sun May 21 11:12:48 2017 -0700 @@ -68,6 +68,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "threadLS_linux_ppc.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "threadLS_aix_ppc.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "threadLS_bsd_x86.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/timer.cpp --- a/src/share/vm/runtime/timer.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/timer.cpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/virtualspace.cpp --- a/src/share/vm/runtime/virtualspace.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/virtualspace.cpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/vmStructs.cpp Sun May 21 11:12:48 2017 -0700 @@ -134,6 +134,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif @@ -161,6 +164,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "vmStructs_linux_ppc.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "vmStructs_aix_ppc.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "vmStructs_bsd_x86.hpp" #endif @@ -218,10 +224,13 @@ #ifdef TARGET_ARCH_MODEL_arm # include "adfiles/adGlobals_arm.hpp" #endif -#ifdef TARGET_ARCH_MODEL_ppc -# include "adfiles/adGlobals_ppc.hpp" +#ifdef TARGET_ARCH_MODEL_ppc_32 +# include "adfiles/adGlobals_ppc_32.hpp" #endif +#ifdef TARGET_ARCH_MODEL_ppc_64 +# include "adfiles/adGlobals_ppc_64.hpp" #endif +#endif // COMPILER2 // Note: the cross-product of (c1, c2, product, nonproduct, ...), // (nonstatic, static), and (unchecked, checked) has not been taken. diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/vmThread.cpp --- a/src/share/vm/runtime/vmThread.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/vmThread.cpp Sun May 21 11:12:48 2017 -0700 @@ -47,6 +47,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/vmThread.hpp --- a/src/share/vm/runtime/vmThread.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/vmThread.hpp Sun May 21 11:12:48 2017 -0700 @@ -36,6 +36,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/vm_operations.cpp --- a/src/share/vm/runtime/vm_operations.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/vm_operations.cpp Sun May 21 11:12:48 2017 -0700 @@ -46,6 +46,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/runtime/vm_version.cpp --- a/src/share/vm/runtime/vm_version.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/runtime/vm_version.cpp Sun May 21 11:12:48 2017 -0700 @@ -175,6 +175,7 @@ #define OS LINUX_ONLY("linux") \ WINDOWS_ONLY("windows") \ SOLARIS_ONLY("solaris") \ + AIX_ONLY("aix") \ BSD_ONLY("bsd") #ifdef ZERO @@ -184,7 +185,8 @@ IA64_ONLY("ia64") \ AMD64_ONLY("amd64") \ ARM_ONLY("arm") \ - PPC_ONLY("ppc") \ + PPC32_ONLY("ppc") \ + PPC64_ONLY("ppc64") \ SPARC_ONLY("sparc") #endif // ZERO @@ -230,6 +232,9 @@ #endif #elif defined(__GNUC__) #define HOTSPOT_BUILD_COMPILER "gcc " __VERSION__ + #elif defined(__IBMCPP__) + #define HOTSPOT_BUILD_COMPILER "xlC " XSTR(__IBMCPP__) + #else #define HOTSPOT_BUILD_COMPILER "unknown compiler" #endif @@ -242,7 +247,7 @@ #define FLOAT_ARCH_STR "-e500v2" #elif defined(ARM) #define FLOAT_ARCH_STR "-vfp" - #elif defined(PPC) + #elif defined(PPC32) #define FLOAT_ARCH_STR "-hflt" #else #define FLOAT_ARCH_STR "" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/services/diagnosticArgument.cpp --- a/src/share/vm/services/diagnosticArgument.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/services/diagnosticArgument.cpp Sun May 21 11:12:48 2017 -0700 @@ -247,7 +247,7 @@ } else { _value._time = 0; _value._nanotime = 0; - strcmp(_value._unit, "ns"); + strcpy(_value._unit, "ns"); } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/accessFlags.cpp --- a/src/share/vm/utilities/accessFlags.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/accessFlags.cpp Sun May 21 11:12:48 2017 -0700 @@ -34,6 +34,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/array.cpp --- a/src/share/vm/utilities/array.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/array.cpp Sun May 21 11:12:48 2017 -0700 @@ -34,6 +34,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/bitMap.cpp --- a/src/share/vm/utilities/bitMap.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/bitMap.cpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/debug.cpp --- a/src/share/vm/utilities/debug.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/debug.cpp Sun May 21 11:12:48 2017 -0700 @@ -62,6 +62,10 @@ # include "os_windows.inline.hpp" # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" # include "thread_bsd.inline.hpp" diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/debug.hpp --- a/src/share/vm/utilities/debug.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/debug.hpp Sun May 21 11:12:48 2017 -0700 @@ -25,8 +25,8 @@ #ifndef SHARE_VM_UTILITIES_DEBUG_HPP #define SHARE_VM_UTILITIES_DEBUG_HPP +#include "utilities/globalDefinitions.hpp" #include "prims/jvm.h" -#include "utilities/globalDefinitions.hpp" #include diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/decoder.cpp --- a/src/share/vm/utilities/decoder.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/decoder.cpp Sun May 21 11:12:48 2017 -0700 @@ -33,6 +33,8 @@ #include "decoder_windows.hpp" #elif defined(__APPLE__) #include "decoder_machO.hpp" +#elif defined(AIX) + #include "decoder_aix.hpp" #else #include "decoder_elf.hpp" #endif @@ -67,6 +69,8 @@ decoder = new (std::nothrow) WindowsDecoder(); #elif defined (__APPLE__) decoder = new (std::nothrow)MachODecoder(); +#elif defined(AIX) + decoder = new (std::nothrow)AIXDecoder(); #else decoder = new (std::nothrow)ElfDecoder(); #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/decoder_elf.cpp --- a/src/share/vm/utilities/decoder_elf.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/decoder_elf.cpp Sun May 21 11:12:48 2017 -0700 @@ -73,4 +73,4 @@ return file; } -#endif +#endif // !_WINDOWS && !__APPLE__ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/decoder_elf.hpp --- a/src/share/vm/utilities/decoder_elf.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/decoder_elf.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, 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 @@ -55,5 +55,5 @@ ElfFile* _opened_elf_files; }; -#endif +#endif // !_WINDOWS && !__APPLE__ #endif // SHARE_VM_UTILITIES_DECODER_ELF_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfFile.cpp --- a/src/share/vm/utilities/elfFile.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/elfFile.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -34,6 +34,7 @@ #include "memory/allocation.inline.hpp" #include "utilities/decoder.hpp" #include "utilities/elfFile.hpp" +#include "utilities/elfFuncDescTable.hpp" #include "utilities/elfStringTable.hpp" #include "utilities/elfSymbolTable.hpp" @@ -43,6 +44,7 @@ memset(&m_elfHdr, 0, sizeof(m_elfHdr)); m_string_tables = NULL; m_symbol_tables = NULL; + m_funcDesc_table = NULL; m_next = NULL; m_status = NullDecoder::no_error; @@ -119,8 +121,8 @@ m_status = NullDecoder::file_invalid; return false; } - // string table if (shdr.sh_type == SHT_STRTAB) { + // string tables ElfStringTable* table = new (std::nothrow) ElfStringTable(m_file, shdr, index); if (table == NULL) { m_status = NullDecoder::out_of_memory; @@ -128,6 +130,7 @@ } add_string_table(table); } else if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) { + // symbol tables ElfSymbolTable* table = new (std::nothrow) ElfSymbolTable(m_file, shdr); if (table == NULL) { m_status = NullDecoder::out_of_memory; @@ -136,6 +139,46 @@ add_symbol_table(table); } } + +#if defined(PPC64) && !defined(ABI_ELFv2) + // Now read the .opd section wich contains the PPC64 function descriptor table. + // The .opd section is only available on PPC64 (see for example: + // http://refspecs.linuxfoundation.org/LSB_3.1.1/LSB-Core-PPC64/LSB-Core-PPC64/specialsections.html) + // so this code should do no harm on other platforms but because of performance reasons we only + // execute it on PPC64 platforms. + // Notice that we can only find the .opd section after we have successfully read in the string + // tables in the previous loop, because we need to query the name of each section which is + // contained in one of the string tables (i.e. the one with the index m_elfHdr.e_shstrndx). + + // Reset the file pointer + if (fseek(m_file, m_elfHdr.e_shoff, SEEK_SET)) { + m_status = NullDecoder::file_invalid; + return false; + } + for (int index = 0; index < m_elfHdr.e_shnum; index ++) { + if (fread((void*)&shdr, sizeof(Elf_Shdr), 1, m_file) != 1) { + m_status = NullDecoder::file_invalid; + return false; + } + if (m_elfHdr.e_shstrndx != SHN_UNDEF && shdr.sh_type == SHT_PROGBITS) { + ElfStringTable* string_table = get_string_table(m_elfHdr.e_shstrndx); + if (string_table == NULL) { + m_status = NullDecoder::file_invalid; + return false; + } + char buf[8]; // '8' is enough because we only want to read ".opd" + if (string_table->string_at(shdr.sh_name, buf, sizeof(buf)) && !strncmp(".opd", buf, 4)) { + m_funcDesc_table = new (std::nothrow) ElfFuncDescTable(m_file, shdr, index); + if (m_funcDesc_table == NULL) { + m_status = NullDecoder::out_of_memory; + return false; + } + break; + } + } + } +#endif + } return true; } @@ -151,8 +194,9 @@ int off = INT_MAX; bool found_symbol = false; while (symbol_table != NULL) { - if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off)) { + if (symbol_table->lookup(addr, &string_table_index, &pos_in_string_table, &off, m_funcDesc_table)) { found_symbol = true; + break; } symbol_table = symbol_table->m_next; } @@ -221,4 +265,4 @@ } #endif -#endif // _WINDOWS +#endif // !_WINDOWS && !__APPLE__ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfFile.hpp --- a/src/share/vm/utilities/elfFile.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/elfFile.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -75,6 +75,7 @@ class ElfStringTable; class ElfSymbolTable; +class ElfFuncDescTable; // On Solaris/Linux platforms, libjvm.so does contain all private symbols. @@ -150,9 +151,12 @@ // string tables ElfStringTable* m_string_tables; + // function descriptors table + ElfFuncDescTable* m_funcDesc_table; + NullDecoder::decoder_status m_status; }; -#endif // _WINDOWS +#endif // !_WINDOWS && !__APPLE__ #endif // SHARE_VM_UTILITIES_ELF_FILE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfFuncDescTable.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/utilities/elfFuncDescTable.cpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#include "precompiled.hpp" + +#if !defined(_WINDOWS) && !defined(__APPLE__) + +#include "memory/allocation.inline.hpp" +#include "utilities/elfFuncDescTable.hpp" + +ElfFuncDescTable::ElfFuncDescTable(FILE* file, Elf_Shdr shdr, int index) { + assert(file, "null file handle"); + // The actual function address (i.e. function entry point) is always the + // first value in the function descriptor (on IA64 and PPC64 they look as follows): + // PPC64: [function entry point, TOC pointer, environment pointer] + // IA64 : [function entry point, GP (global pointer) value] + // Unfortunately 'shdr.sh_entsize' doesn't always seem to contain this size (it's zero on PPC64) so we can't assert + // assert(IA64_ONLY(2) PPC64_ONLY(3) * sizeof(address) == shdr.sh_entsize, "Size mismatch for '.opd' section entries"); + + m_funcDescs = NULL; + m_file = file; + m_index = index; + m_status = NullDecoder::no_error; + + // try to load the function descriptor table + long cur_offset = ftell(file); + if (cur_offset != -1) { + // call malloc so we can back up if memory allocation fails. + m_funcDescs = (address*)os::malloc(shdr.sh_size, mtInternal); + if (m_funcDescs) { + if (fseek(file, shdr.sh_offset, SEEK_SET) || + fread((void*)m_funcDescs, shdr.sh_size, 1, file) != 1 || + fseek(file, cur_offset, SEEK_SET)) { + m_status = NullDecoder::file_invalid; + os::free(m_funcDescs); + m_funcDescs = NULL; + } + } + if (!NullDecoder::is_error(m_status)) { + memcpy(&m_shdr, &shdr, sizeof(Elf_Shdr)); + } + } else { + m_status = NullDecoder::file_invalid; + } +} + +ElfFuncDescTable::~ElfFuncDescTable() { + if (m_funcDescs != NULL) { + os::free(m_funcDescs); + } +} + +address ElfFuncDescTable::lookup(Elf_Word index) { + if (NullDecoder::is_error(m_status)) { + return NULL; + } + + if (m_funcDescs != NULL) { + if (m_shdr.sh_size > 0 && m_shdr.sh_addr <= index && index <= m_shdr.sh_addr + m_shdr.sh_size) { + // Notice that 'index' is a byte-offset into the function descriptor table. + return m_funcDescs[(index - m_shdr.sh_addr) / sizeof(address)]; + } + return NULL; + } else { + long cur_pos; + address addr; + if (!(m_shdr.sh_size > 0 && m_shdr.sh_addr <= index && index <= m_shdr.sh_addr + m_shdr.sh_size)) { + // don't put the whole decoder in error mode if we just tried a wrong index + return NULL; + } + if ((cur_pos = ftell(m_file)) == -1 || + fseek(m_file, m_shdr.sh_offset + index - m_shdr.sh_addr, SEEK_SET) || + fread(&addr, sizeof(addr), 1, m_file) != 1 || + fseek(m_file, cur_pos, SEEK_SET)) { + m_status = NullDecoder::file_invalid; + return NULL; + } + return addr; + } +} + +#endif // !_WINDOWS && !__APPLE__ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfFuncDescTable.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/utilities/elfFuncDescTable.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef SHARE_VM_UTILITIES_ELF_FUNC_DESC_TABLE_HPP +#define SHARE_VM_UTILITIES_ELF_FUNC_DESC_TABLE_HPP + +#if !defined(_WINDOWS) && !defined(__APPLE__) + + +#include "memory/allocation.hpp" +#include "utilities/decoder.hpp" +#include "utilities/elfFile.hpp" + +/* + +On PowerPC-64 (and other architectures like for example IA64) a pointer to a +function is not just a plain code address, but instead a pointer to a so called +function descriptor (which is simply a structure containing 3 pointers). +This fact is also reflected in the ELF ABI for PowerPC-64. + +On architectures like x86 or SPARC, the ELF symbol table contains the start +address and size of an object. So for example for a function object (i.e. type +'STT_FUNC') the symbol table's 'st_value' and 'st_size' fields directly +represent the starting address and size of that function. On PPC64 however, the +symbol table's 'st_value' field only contains an index into another, PPC64 +specific '.opd' (official procedure descriptors) section, while the 'st_size' +field still holds the size of the corresponding function. In order to get the +actual start address of a function, it is necessary to read the corresponding +function descriptor entry in the '.opd' section at the corresponding index and +extract the start address from there. + +That's exactly what this 'ElfFuncDescTable' class is used for. If the HotSpot +runs on a PPC64 machine, and the corresponding ELF files contains an '.opd' +section (which is actually mandatory on PPC64) it will be read into an object +of type 'ElfFuncDescTable' just like the string and symbol table sections. +Later on, during symbol lookup in 'ElfSymbolTable::lookup()' this function +descriptor table will be used if available to find the real function address. + +All this is how things work today (2013) on contemporary Linux distributions +(i.e. SLES 10) and new version of GCC (i.e. > 4.0). However there is a history, +and it goes like this: + +In SLES 9 times (sometimes before GCC 3.4) gcc/ld on PPC64 generated two +entries in the symbol table for every function. The value of the symbol with +the name of the function was the address of the function descriptor while the +dot '.' prefixed name was reserved to hold the actual address of that function +(http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES). + +For a C-function 'foo' this resulted in two symbol table entries like this +(extracted from the output of 'readelf -a '): + +Section Headers: + [ 9] .text PROGBITS 0000000000000a20 00000a20 + 00000000000005a0 0000000000000000 AX 0 0 16 + [21] .opd PROGBITS 00000000000113b8 000013b8 + 0000000000000138 0000000000000000 WA 0 0 8 + +Symbol table '.symtab' contains 86 entries: + Num: Value Size Type Bind Vis Ndx Name + 76: 00000000000114c0 24 FUNC GLOBAL DEFAULT 21 foo + 78: 0000000000000bb0 76 FUNC GLOBAL DEFAULT 9 .foo + +You can see now that the '.foo' entry actually points into the '.text' segment +('Ndx'=9) and its value and size fields represent the functions actual address +and size. On the other hand, the entry for plain 'foo' points into the '.opd' +section ('Ndx'=21) and its value and size fields are the index into the '.opd' +section and the size of the corresponding '.opd' section entry (3 pointers on +PPC64). + +These so called 'dot symbols' were dropped around gcc 3.4 from GCC and BINUTILS, +see http://gcc.gnu.org/ml/gcc-patches/2004-08/msg00557.html. +But nevertheless it may still be necessary to support both formats because we +either run on an old system or because it is possible at any time that functions +appear in the stack trace which come from old-style libraries. + +Therefore we not only have to check for the presence of the function descriptor +table during symbol lookup in 'ElfSymbolTable::lookup()'. We additionally have +to check that the symbol table entry references the '.opd' section. Only in +that case we can resolve the actual function address from there. Otherwise we +use the plain 'st_value' field from the symbol table as function address. This +way we can also lookup the symbols in old-style ELF libraries (although we get +the 'dotted' versions in that case). However, if present, the 'dot' will be +conditionally removed on PPC64 from the symbol in 'ElfDecoder::demangle()' in +decoder_linux.cpp. + +Notice that we can not reliably get the function address from old-style +libraries because the 'st_value' field of the symbol table entries which point +into the '.opd' section denote the size of the corresponding '.opd' entry and +not that of the corresponding function. This has changed for the symbol table +entries in new-style libraries as described at the beginning of this +documentation. + +*/ + +class ElfFuncDescTable: public CHeapObj { + friend class ElfFile; + public: + ElfFuncDescTable(FILE* file, Elf_Shdr shdr, int index); + ~ElfFuncDescTable(); + + // return the function address for the function descriptor at 'index' or NULL on error + address lookup(Elf_Word index); + + int get_index() { return m_index; }; + + NullDecoder::decoder_status get_status() { return m_status; }; + + protected: + // holds the complete function descriptor section if + // we can allocate enough memory + address* m_funcDescs; + + // file contains string table + FILE* m_file; + + // section header + Elf_Shdr m_shdr; + + // The section index of this function descriptor (i.e. '.opd') section in the ELF file + int m_index; + + NullDecoder::decoder_status m_status; +}; + +#endif // !_WINDOWS && !__APPLE__ + +#endif // SHARE_VM_UTILITIES_ELF_FUNC_DESC_TABLE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfStringTable.cpp --- a/src/share/vm/utilities/elfStringTable.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/elfStringTable.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -87,4 +87,4 @@ } } -#endif // _WINDOWS +#endif // !_WINDOWS && !__APPLE__ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfStringTable.hpp --- a/src/share/vm/utilities/elfStringTable.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/elfStringTable.hpp Sun May 21 11:12:48 2017 -0700 @@ -70,6 +70,6 @@ NullDecoder::decoder_status m_status; }; -#endif // _WINDOWS and _APPLE +#endif // !_WINDOWS && !__APPLE__ #endif // SHARE_VM_UTILITIES_ELF_STRING_TABLE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfSymbolTable.cpp --- a/src/share/vm/utilities/elfSymbolTable.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/elfSymbolTable.cpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -27,6 +27,7 @@ #if !defined(_WINDOWS) && !defined(__APPLE__) #include "memory/allocation.inline.hpp" +#include "utilities/elfFuncDescTable.hpp" #include "utilities/elfSymbolTable.hpp" ElfSymbolTable::ElfSymbolTable(FILE* file, Elf_Shdr shdr) { @@ -68,7 +69,7 @@ } } -bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex, int* offset) { +bool ElfSymbolTable::lookup(address addr, int* stringtableIndex, int* posIndex, int* offset, ElfFuncDescTable* funcDescTable) { assert(stringtableIndex, "null string table index pointer"); assert(posIndex, "null string table offset pointer"); assert(offset, "null offset pointer"); @@ -77,19 +78,25 @@ return false; } - address pc = 0; size_t sym_size = sizeof(Elf_Sym); assert((m_shdr.sh_size % sym_size) == 0, "check size"); int count = m_shdr.sh_size / sym_size; if (m_symbols != NULL) { for (int index = 0; index < count; index ++) { if (STT_FUNC == ELF_ST_TYPE(m_symbols[index].st_info)) { - address sym_addr = (address)m_symbols[index].st_value; - if (sym_addr < addr && (addr - sym_addr) < *offset) { - pc = (address)m_symbols[index].st_value; - *offset = (int)(addr - pc); + Elf_Word st_size = m_symbols[index].st_size; + address sym_addr; + if (funcDescTable != NULL && funcDescTable->get_index() == m_symbols[index].st_shndx) { + // We need to go another step trough the function descriptor table (currently PPC64 only) + sym_addr = funcDescTable->lookup(m_symbols[index].st_value); + } else { + sym_addr = (address)m_symbols[index].st_value; + } + if (sym_addr <= addr && (Elf_Word)(addr - sym_addr) < st_size) { + *offset = (int)(addr - sym_addr); *posIndex = m_symbols[index].st_name; *stringtableIndex = m_shdr.sh_link; + return true; } } } @@ -105,12 +112,19 @@ for (int index = 0; index < count; index ++) { if (fread(&sym, sym_size, 1, m_file) == 1) { if (STT_FUNC == ELF_ST_TYPE(sym.st_info)) { - address sym_addr = (address)sym.st_value; - if (sym_addr < addr && (addr - sym_addr) < *offset) { - pc = (address)sym.st_value; - *offset = (int)(addr - pc); + Elf_Word st_size = sym.st_size; + address sym_addr; + if (funcDescTable != NULL && funcDescTable->get_index() == sym.st_shndx) { + // We need to go another step trough the function descriptor table (currently PPC64 only) + sym_addr = funcDescTable->lookup(sym.st_value); + } else { + sym_addr = (address)sym.st_value; + } + if (sym_addr <= addr && (Elf_Word)(addr - sym_addr) < st_size) { + *offset = (int)(addr - sym_addr); *posIndex = sym.st_name; *stringtableIndex = m_shdr.sh_link; + return true; } } } else { @@ -123,4 +137,4 @@ return true; } -#endif // _WINDOWS +#endif // !_WINDOWS && !__APPLE__ diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/elfSymbolTable.hpp --- a/src/share/vm/utilities/elfSymbolTable.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/elfSymbolTable.hpp Sun May 21 11:12:48 2017 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -45,7 +45,7 @@ ~ElfSymbolTable(); // search the symbol that is nearest to the specified address. - bool lookup(address addr, int* stringtableIndex, int* posIndex, int* offset); + bool lookup(address addr, int* stringtableIndex, int* posIndex, int* offset, ElfFuncDescTable* funcDescTable); NullDecoder::decoder_status get_status() { return m_status; }; @@ -65,6 +65,6 @@ NullDecoder::decoder_status m_status; }; -#endif // _WINDOWS and _APPLE +#endif // !_WINDOWS and !__APPLE__ #endif // SHARE_VM_UTILITIES_ELF_SYMBOL_TABLE_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/events.cpp --- a/src/share/vm/utilities/events.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/events.cpp Sun May 21 11:12:48 2017 -0700 @@ -39,6 +39,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/exceptions.cpp --- a/src/share/vm/utilities/exceptions.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/exceptions.cpp Sun May 21 11:12:48 2017 -0700 @@ -42,6 +42,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/exceptions.hpp --- a/src/share/vm/utilities/exceptions.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/exceptions.hpp Sun May 21 11:12:48 2017 -0700 @@ -194,15 +194,15 @@ #define HAS_PENDING_EXCEPTION (((ThreadShadow*)THREAD)->has_pending_exception()) #define CLEAR_PENDING_EXCEPTION (((ThreadShadow*)THREAD)->clear_pending_exception()) -#define CHECK THREAD); if (HAS_PENDING_EXCEPTION) return ; (0 -#define CHECK_(result) THREAD); if (HAS_PENDING_EXCEPTION) return result; (0 +#define CHECK THREAD); if (HAS_PENDING_EXCEPTION) return ; (void)(0 +#define CHECK_(result) THREAD); if (HAS_PENDING_EXCEPTION) return result; (void)(0 #define CHECK_0 CHECK_(0) #define CHECK_NH CHECK_(Handle()) #define CHECK_NULL CHECK_(NULL) #define CHECK_false CHECK_(false) -#define CHECK_AND_CLEAR THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return; } (0 -#define CHECK_AND_CLEAR_(result) THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return result; } (0 +#define CHECK_AND_CLEAR THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return; } (void)(0 +#define CHECK_AND_CLEAR_(result) THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return result; } (void)(0 #define CHECK_AND_CLEAR_0 CHECK_AND_CLEAR_(0) #define CHECK_AND_CLEAR_NH CHECK_AND_CLEAR_(Handle()) #define CHECK_AND_CLEAR_NULL CHECK_AND_CLEAR_(NULL) @@ -281,7 +281,7 @@ CLEAR_PENDING_EXCEPTION; \ ex->print(); \ ShouldNotReachHere(); \ - } (0 + } (void)(0 // ExceptionMark is a stack-allocated helper class for local exception handling. // It is used with the EXCEPTION_MARK macro. diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/globalDefinitions.hpp --- a/src/share/vm/utilities/globalDefinitions.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/globalDefinitions.hpp Sun May 21 11:12:48 2017 -0700 @@ -38,6 +38,9 @@ #ifdef TARGET_COMPILER_sparcWorks # include "utilities/globalDefinitions_sparcWorks.hpp" #endif +#ifdef TARGET_COMPILER_xlc +# include "utilities/globalDefinitions_xlc.hpp" +#endif #include "utilities/macros.hpp" @@ -47,6 +50,9 @@ //---------------------------------------------------------------------------------------------------- // Constants +// "Word" here means the processor's register size (as opposed to a +// double-byte (16 bits) or a JVM word (32 bits)). + const int LogBytesPerShort = 1; const int LogBytesPerInt = 2; #ifdef _LP64 diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/globalDefinitions_xlc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/utilities/globalDefinitions_xlc.hpp Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2013 SAP AG. 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. + * + */ + +#ifndef SHARE_VM_UTILITIES_GLOBALDEFINITIONS_XLC_HPP +#define SHARE_VM_UTILITIES_GLOBALDEFINITIONS_XLC_HPP + +#include "prims/jni.h" + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +#include +#include +#include +#include +#include +#include +#include + +#include +#ifndef FP_PZERO +// Linux doesn't have positive/negative zero +#define FP_PZERO FP_ZERO +#endif +#if (!defined fpclass) +#define fpclass fpclassify +#endif + +#include +#include +#include +#include + +#include +#include + +#include + +// Use XLC compiler builtins instead of inline assembler +#define USE_XLC_BUILTINS +#ifdef USE_XLC_BUILTINS +#include + #if __IBMCPP__ < 1000 + // the funtion prototype for __dcbtst(void *) is missing in XLC V8.0 + // I could compile a little test, where I provided the prototype. + // The generated code was correct there. This is the prototype: + // extern "builtin" void __dcbtst (void *); + // For now we don't make use of it when compiling with XLC V8.0 + #else + // __IBMCPP__ >= 1000 + // XLC V10 provides the prototype for __dcbtst (void *); + #define USE_XLC_PREFETCH_WRITE_BUILTIN + #endif +#endif // USE_XLC_BUILTINS + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. On some platforms, sizeof(intptr_t) > +// sizeof(void*), so here we want something which is integer type, but has the +// same size as a pointer. +#ifdef __GNUC__ + #error XLC and __GNUC__? +#else + #define NULL_WORD NULL +#endif + +// AIX also needs a 64 bit NULL to work as a null address pointer. +// Most system includes on AIX would define it as an int 0 if not already defined with one +// exception: /usr/include/dirent.h will unconditionally redefine NULL to int 0 again. +// In this case you need to copy the following defines to a position after #include +// (see jmv_aix.h). +#ifdef AIX + #ifdef _LP64 + #undef NULL + #define NULL 0L + #else + #ifndef NULL + #define NULL 0 + #endif + #endif +#endif // AIX + +// Compiler-specific primitive types +// All defs of int (uint16_6 etc) are defined in AIX' /usr/include/stdint.h + +// Additional Java basic types + +typedef uint8_t jubyte; +typedef uint16_t jushort; +typedef uint32_t juint; +typedef uint64_t julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern +// %%%%%% These seem like standard C++ to me--how about factoring them out? - Ungar + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long canstant is C++ compiler specific) + +// Build a 64bit integer constant +#define CONST64(x) (x ## LL) +#define UCONST64(x) (x ## ULL) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#define DEBUG_EXCEPTION ::abort(); + +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() + +// checking for nanness +#ifdef AIX +inline int g_isnan(float f) { return isnan(f); } +inline int g_isnan(double f) { return isnan(f); } +#else +#error "missing platform-specific definition here" +#endif + +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return finite(f); } +inline int g_isfinite(jdouble f) { return finite(f); } + + +// Wide characters + +inline int wcslen(const jchar* x) { return wcslen((const wchar_t*)x); } + + +// Portability macros +#define PRAGMA_INTERFACE #pragma interface +#define PRAGMA_IMPLEMENTATION #pragma implementation +#define VALUE_OBJ_CLASS_SPEC + +// Formatting. +#ifdef _LP64 +#define FORMAT64_MODIFIER "l" +#else // !_LP64 +#define FORMAT64_MODIFIER "ll" +#endif // _LP64 + +// Cannot use xlc's offsetof as implementation of hotspot's +// offset_of(), because xlc warns about applying offsetof() to non-POD +// object and xlc cannot compile the expression offsetof(DataLayout, +// _cells[index]) in DataLayout::cell_offset() . Therefore we define +// offset_of as it is defined for gcc. +#define offset_of(klass,field) (size_t)((intx)&(((klass*)16)->field) - 16) + +// Some constant sizes used throughout the AIX port +#define SIZE_1K ((uint64_t) 0x400ULL) +#define SIZE_4K ((uint64_t) 0x1000ULL) +#define SIZE_64K ((uint64_t) 0x10000ULL) +#define SIZE_1M ((uint64_t) 0x100000ULL) +#define SIZE_4M ((uint64_t) 0x400000ULL) +#define SIZE_8M ((uint64_t) 0x800000ULL) +#define SIZE_16M ((uint64_t) 0x1000000ULL) +#define SIZE_256M ((uint64_t) 0x10000000ULL) +#define SIZE_1G ((uint64_t) 0x40000000ULL) +#define SIZE_2G ((uint64_t) 0x80000000ULL) +#define SIZE_4G ((uint64_t) 0x100000000ULL) +#define SIZE_16G ((uint64_t) 0x400000000ULL) +#define SIZE_32G ((uint64_t) 0x800000000ULL) +#define SIZE_64G ((uint64_t) 0x1000000000ULL) +#define SIZE_1T ((uint64_t) 0x10000000000ULL) + + +#endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_XLC_HPP diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/growableArray.cpp --- a/src/share/vm/utilities/growableArray.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/growableArray.cpp Sun May 21 11:12:48 2017 -0700 @@ -34,6 +34,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/histogram.hpp --- a/src/share/vm/utilities/histogram.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/histogram.hpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/macros.hpp --- a/src/share/vm/utilities/macros.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/macros.hpp Sun May 21 11:12:48 2017 -0700 @@ -125,6 +125,14 @@ #define NOT_LINUX(code) code #endif +#ifdef AIX +#define AIX_ONLY(code) code +#define NOT_AIX(code) +#else +#define AIX_ONLY(code) +#define NOT_AIX(code) code +#endif + #ifdef SOLARIS #define SOLARIS_ONLY(code) code #define NOT_SOLARIS(code) @@ -191,7 +199,11 @@ #define NOT_IA32(code) code #endif -#ifdef IA64 +// This is a REALLY BIG HACK, but on AIX unconditionally defines IA64. +// At least on AIX 7.1 this is a real problem because 'systemcfg.h' is indirectly included +// by 'pthread.h' and other common system headers. + +#if defined(IA64) && !defined(AIX) #define IA64_ONLY(code) code #define NOT_IA64(code) #else @@ -215,14 +227,34 @@ #define NOT_SPARC(code) code #endif -#ifdef PPC +#if defined(PPC32) || defined(PPC64) +#ifndef PPC +#define PPC +#endif #define PPC_ONLY(code) code #define NOT_PPC(code) #else +#undef PPC #define PPC_ONLY(code) #define NOT_PPC(code) code #endif +#ifdef PPC32 +#define PPC32_ONLY(code) code +#define NOT_PPC32(code) +#else +#define PPC32_ONLY(code) +#define NOT_PPC32(code) code +#endif + +#ifdef PPC64 +#define PPC64_ONLY(code) code +#define NOT_PPC64(code) +#else +#define PPC64_ONLY(code) +#define NOT_PPC64(code) code +#endif + #ifdef E500V2 #define E500V2_ONLY(code) code #define NOT_E500V2(code) diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/ostream.cpp --- a/src/share/vm/utilities/ostream.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/ostream.cpp Sun May 21 11:12:48 2017 -0700 @@ -39,6 +39,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "os_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif @@ -1334,7 +1337,7 @@ } } -#if defined(SOLARIS) || defined(LINUX) || defined(_ALLBSD_SOURCE) +#if defined(SOLARIS) || defined(LINUX) || defined(AIX) || defined(_ALLBSD_SOURCE) #include #include #include diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/preserveException.hpp --- a/src/share/vm/utilities/preserveException.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/preserveException.hpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/taskqueue.cpp --- a/src/share/vm/utilities/taskqueue.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/taskqueue.cpp Sun May 21 11:12:48 2017 -0700 @@ -37,6 +37,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/taskqueue.hpp --- a/src/share/vm/utilities/taskqueue.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/taskqueue.hpp Sun May 21 11:12:48 2017 -0700 @@ -53,6 +53,9 @@ #ifdef TARGET_OS_ARCH_linux_ppc # include "orderAccess_linux_ppc.inline.hpp" #endif +#ifdef TARGET_OS_ARCH_aix_ppc +# include "orderAccess_aix_ppc.inline.hpp" +#endif #ifdef TARGET_OS_ARCH_bsd_x86 # include "orderAccess_bsd_x86.inline.hpp" #endif @@ -138,22 +141,32 @@ // Internal type for indexing the queue; also used for the tag. typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t; +private: // The first free element after the last one pushed (mod N). - volatile uint _bottom; + // We keep _bottom private and DO NOT USE IT DIRECTLY. + volatile uint _private_bottom; + +protected: + // Access to _bottom must be ordered. Use OrderAccess. + inline uint get_bottom() const { + return OrderAccess::load_acquire((volatile juint*)&_private_bottom); + } + + inline void set_bottom(uint new_bottom) { + OrderAccess::release_store(&_private_bottom, new_bottom); + } enum { MOD_N_MASK = N - 1 }; + // Simple field access here, so no OrderAccess and no volatiles. class Age { public: Age(size_t data = 0) { _data = data; } Age(const Age& age) { _data = age._data; } Age(idx_t top, idx_t tag) { _fields._top = top; _fields._tag = tag; } - Age get() const volatile { return _data; } - void set(Age age) volatile { _data = age._data; } - - idx_t top() const volatile { return _fields._top; } - idx_t tag() const volatile { return _fields._tag; } + idx_t top() const { return _fields._top; } + idx_t tag() const { return _fields._tag; } // Increment top; if it wraps, increment tag also. void increment() { @@ -161,15 +174,9 @@ if (_fields._top == 0) ++_fields._tag; } - Age cmpxchg(const Age new_age, const Age old_age) volatile { - return (size_t) Atomic::cmpxchg_ptr((intptr_t)new_age._data, - (volatile intptr_t *)&_data, - (intptr_t)old_age._data); - } - bool operator ==(const Age& other) const { return _data == other._data; } - private: + public: struct fields { idx_t _top; idx_t _tag; @@ -180,7 +187,30 @@ }; }; - volatile Age _age; + +private: + // Keep _age private and DO NOT USE IT DIRECTLY. + volatile Age _private_age; + +protected: + inline idx_t get_age_top() const { + return OrderAccess::load_acquire((volatile idx_t*) &(_private_age._fields._top)); + } + + inline Age get_age() { + size_t res = OrderAccess::load_ptr_acquire((volatile intptr_t*) &_private_age); + return *(Age*)(&res); + } + + inline void set_age(Age a) { + OrderAccess::release_store_ptr((volatile intptr_t*) &_private_age, *(size_t*)(&a)); + } + + Age cmpxchg_age(const Age new_age, const Age old_age) volatile { + return (Age)(size_t)Atomic::cmpxchg_ptr((intptr_t)new_age._data, + (volatile intptr_t*) &(_private_age._data), + (intptr_t)old_age._data); + } // These both operate mod N. static uint increment_index(uint ind) { @@ -217,26 +247,29 @@ } public: - TaskQueueSuper() : _bottom(0), _age() {} + TaskQueueSuper() : _private_bottom(0), _private_age() {} // Return true if the TaskQueue contains/does not contain any tasks. - bool peek() const { return _bottom != _age.top(); } + // Use getters/setters with OrderAccess when accessing _bottom and _age. + bool peek() { + return get_bottom() != get_age_top(); + } bool is_empty() const { return size() == 0; } // Return an estimate of the number of elements in the queue. // The "careful" version admits the possibility of pop_local/pop_global // races. uint size() const { - return size(_bottom, _age.top()); + return size(get_bottom(), get_age_top()); } uint dirty_size() const { - return dirty_size(_bottom, _age.top()); + return dirty_size(get_bottom(), get_age_top()); } void set_empty() { - _bottom = 0; - _age.set(0); + set_bottom(0); + set_age(0); } // Maximum number of elements allowed in the queue. This is two less @@ -258,11 +291,11 @@ typedef typename TaskQueueSuper::Age Age; typedef typename TaskQueueSuper::idx_t idx_t; - using TaskQueueSuper::_bottom; - using TaskQueueSuper::_age; using TaskQueueSuper::increment_index; using TaskQueueSuper::decrement_index; using TaskQueueSuper::dirty_size; + using TaskQueueSuper::get_age_top; + using TaskQueueSuper::get_age; public: using TaskQueueSuper::max_elems; @@ -322,7 +355,7 @@ void GenericTaskQueue::oops_do(OopClosure* f) { // tty->print_cr("START OopTaskQueue::oops_do"); uint iters = size(); - uint index = _bottom; + uint index = this->get_bottom(); for (uint i = 0; i < iters; ++i) { index = decrement_index(index); // tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T, @@ -339,10 +372,14 @@ bool GenericTaskQueue::push_slow(E t, uint dirty_n_elems) { if (dirty_n_elems == N - 1) { // Actually means 0, so do the push. - uint localBot = _bottom; - // g++ complains if the volatile result of the assignment is unused. - const_cast(_elems[localBot] = t); - OrderAccess::release_store(&_bottom, increment_index(localBot)); + uint localBot = this->get_bottom(); + // g++ complains if the volatile result of the assignment is + // unused, so we cast the volatile away. We cannot cast directly + // to void, because gcc treats that as not using the result of the + // assignment. However, casting to E& means that we trigger an + // unused-value warning. So, we cast the E& to void. + (void)const_cast(_elems[localBot] = t); + this->set_bottom(increment_index(localBot)); TASKQUEUE_STATS_ONLY(stats.record_push()); return true; } @@ -372,10 +409,10 @@ if (localBot == oldAge.top()) { // No competing pop_global has yet incremented "top"; we'll try to // install new_age, thus claiming the element. - Age tempAge = _age.cmpxchg(newAge, oldAge); + Age tempAge = this->cmpxchg_age(newAge, oldAge); if (tempAge == oldAge) { // We win. - assert(dirty_size(localBot, _age.top()) != N - 1, "sanity"); + assert(dirty_size(localBot, get_age_top()) != N - 1, "sanity"); TASKQUEUE_STATS_ONLY(stats.record_pop_slow()); return true; } @@ -383,14 +420,14 @@ // We lose; a completing pop_global gets the element. But the queue is empty // and top is greater than bottom. Fix this representation of the empty queue // to become the canonical one. - _age.set(newAge); - assert(dirty_size(localBot, _age.top()) != N - 1, "sanity"); + this->set_age(newAge); + assert(dirty_size(localBot, get_age_top()) != N - 1, "sanity"); return false; } template bool GenericTaskQueue::pop_global(E& t) { - Age oldAge = _age.get(); + Age oldAge = get_age(); // Architectures with weak memory model require a fence here. The // fence has a cumulative effect on getting age and getting bottom. // This way it is guaranteed that bottom is not older than age, @@ -398,16 +435,22 @@ #if !(defined SPARC || defined IA32 || defined AMD64) OrderAccess::fence(); #endif - uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom); + + uint localBot = this->get_bottom(); uint n_elems = size(localBot, oldAge.top()); if (n_elems == 0) { return false; } - const_cast(t = _elems[oldAge.top()]); + // g++ complains if the volatile result of the assignment is + // unused, so we cast the volatile away. We cannot cast directly + // to void, because gcc treats that as not using the result of the + // assignment. However, casting to E& means that we trigger an + // unused-value warning. So, we cast the E& to void. + (void) const_cast(t = _elems[oldAge.top()]); Age newAge(oldAge); newAge.increment(); - Age resAge = _age.cmpxchg(newAge, oldAge); + Age resAge = this->cmpxchg_age(newAge, oldAge); // Note that using "_bottom" here might fail, since a pop_local might // have decremented it. @@ -683,15 +726,19 @@ template inline bool GenericTaskQueue::push(E t) { - uint localBot = _bottom; + uint localBot = this->get_bottom(); assert(localBot < N, "_bottom out of range."); - idx_t top = _age.top(); + idx_t top = get_age_top(); uint dirty_n_elems = dirty_size(localBot, top); assert(dirty_n_elems < N, "n_elems out of range."); if (dirty_n_elems < max_elems()) { - // g++ complains if the volatile result of the assignment is unused. - const_cast(_elems[localBot] = t); - OrderAccess::release_store(&_bottom, increment_index(localBot)); + // g++ complains if the volatile result of the assignment is + // unused, so we cast the volatile away. We cannot cast directly + // to void, because gcc treats that as not using the result of the + // assignment. However, casting to E& means that we trigger an + // unused-value warning. So, we cast the E& to void. + (void) const_cast(_elems[localBot] = t); + this->set_bottom(increment_index(localBot)); TASKQUEUE_STATS_ONLY(stats.record_push()); return true; } else { @@ -701,25 +748,30 @@ template inline bool GenericTaskQueue::pop_local(E& t) { - uint localBot = _bottom; + uint localBot = this->get_bottom(); // This value cannot be N-1. That can only occur as a result of // the assignment to bottom in this method. If it does, this method // resets the size to 0 before the next call (which is sequential, // since this is pop_local.) - uint dirty_n_elems = dirty_size(localBot, _age.top()); + uint dirty_n_elems = dirty_size(localBot, get_age_top()); assert(dirty_n_elems != N - 1, "Shouldn't be possible..."); if (dirty_n_elems == 0) return false; localBot = decrement_index(localBot); - _bottom = localBot; + this->set_bottom(localBot); // This is necessary to prevent any read below from being reordered // before the store just above. OrderAccess::fence(); - const_cast(t = _elems[localBot]); + // g++ complains if the volatile result of the assignment is + // unused, so we cast the volatile away. We cannot cast directly + // to void, because gcc treats that as not using the result of the + // assignment. However, casting to E& means that we trigger an + // unused-value warning. So, we cast the E& to void. + (void) const_cast(t = _elems[localBot]); // This is a second read of "age"; the "size()" above is the first. // If there's still at least one element in the queue, based on the // "_bottom" and "age" we've read, then there can be no interference with // a "pop_global" operation, and we're done. - idx_t tp = _age.top(); // XXX + idx_t tp = get_age_top(); // XXX if (size(localBot, tp) > 0) { assert(dirty_size(localBot, tp) != N - 1, "sanity"); TASKQUEUE_STATS_ONLY(stats.record_pop()); @@ -727,7 +779,7 @@ } else { // Otherwise, the queue contained exactly one element; we take the slow // path. - return pop_local_slow(localBot, _age.get()); + return pop_local_slow(localBot, get_age()); } } diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/vmError.cpp --- a/src/share/vm/utilities/vmError.cpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/vmError.cpp Sun May 21 11:12:48 2017 -0700 @@ -22,7 +22,6 @@ * */ -#include #include "precompiled.hpp" #include "compiler/compileBroker.hpp" #include "gc_interface/collectedHeap.hpp" @@ -43,6 +42,8 @@ #include "utilities/top.hpp" #include "utilities/vmError.hpp" +#include + // List of environment variables that should be reported in error log file. const char *env_list[] = { // All platforms diff -r 368df6a4b403 -r d982b0f5b59a src/share/vm/utilities/workgroup.hpp --- a/src/share/vm/utilities/workgroup.hpp Thu Apr 13 17:18:04 2017 -0400 +++ b/src/share/vm/utilities/workgroup.hpp Sun May 21 11:12:48 2017 -0700 @@ -35,6 +35,9 @@ #ifdef TARGET_OS_FAMILY_windows # include "thread_windows.inline.hpp" #endif +#ifdef TARGET_OS_FAMILY_aix +# include "thread_aix.inline.hpp" +#endif #ifdef TARGET_OS_FAMILY_bsd # include "thread_bsd.inline.hpp" #endif diff -r 368df6a4b403 -r d982b0f5b59a test/compiler/codegen/IntRotateWithImmediate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/codegen/IntRotateWithImmediate.java Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright 2015 SAP AG. 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. + */ + +/* + * @test + * @bug 8080190 + * @key regression + * @summary Test that the rotate distance used in the rotate instruction is properly masked with 0x1f + * @run main/othervm -Xbatch -XX:-UseOnStackReplacement IntRotateWithImmediate + * @author volker.simonis@gmail.com + */ + +public class IntRotateWithImmediate { + + // This is currently the same as Integer.rotateRight() + static int rotateRight(int i, int distance) { + // On some architectures (i.e. x86_64 and ppc64) the following computation is + // matched in the .ad file into a single MachNode which emmits a single rotate + // machine instruction. It is important that the shift amount is masked to match + // corresponding immediate width in the native instruction. On x86_64 the rotate + // left instruction ('rol') encodes an 8-bit immediate while the corresponding + // 'rotlwi' instruction on Power only encodes a 5-bit immediate. + return ((i >>> distance) | (i << -distance)); + } + + static int compute(int x) { + return rotateRight(x, 3); + } + + public static void main(String args[]) { + int val = 4096; + + int firstResult = compute(val); + + for (int i = 0; i < 100000; i++) { + int newResult = compute(val); + if (firstResult != newResult) { + throw new InternalError(firstResult + " != " + newResult); + } + } + System.out.println("OK"); + } + +} diff -r 368df6a4b403 -r d982b0f5b59a test/runtime/handlerInTry/HandlerInTry.jasm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/handlerInTry/HandlerInTry.jasm Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * HandlerInTry contains a try block in a ctor whose handler is inside + * the same try block. The try block starts at line 74 (try t2;), ends at + * line 106 (endtry t2;), but its handler starts at line 101 (catch t2 #0;). + */ +super public class HandlerInTry + version 51:0 +{ + +public static final synthetic Field ___transactionFactory_2002349702336125:"Ljava/lang/Object;"; + +public Method "":"(Ljava/lang/Object;)V" + stack 5 locals 5 +{ + invokestatic Method ThreadLocalTransaction.getThreadLocalTransaction:"()Ljava/lang/Object;"; + checkcast class java/lang/Object; + astore_2; + aload_2; + invokestatic Method TransactionLogicDonor.isActiveTransaction:"(Ljava/lang/Object;)Z"; + ifeq L21; + aload_0; + aload_1; + aload_2; + invokespecial Method "":"(Ljava/lang/Object;Ljava/lang/Object;)V"; + return; + L21: stack_frame_type append; + locals_map class java/lang/Object; + aload_2; + getstatic Field ___transactionFactory_2002349702336125:"Ljava/lang/Object;"; + invokestatic Method TransactionLogicDonor.createTransaction:"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; + astore_2; + aload_2; + iconst_1; + pop; + aload_2; + invokestatic Method ThreadLocalTransaction.setThreadLocalTransaction:"(Ljava/lang/Object;)V"; + try t0, t1; + aload_0; + aload_1; + aload_2; + invokespecial Method "":"(Ljava/lang/Object;Ljava/lang/Object;)V"; + aload_2; + pop; + aconst_null; + astore_2; + endtry t0, t1; + invokestatic Method ThreadLocalTransaction.clearThreadLocalTransaction:"()V"; + pop; + goto L107; + catch t0 java/lang/Throwable; + try t2; + stack_frame_type full; + locals_map bogus, class java/lang/Object, class java/lang/Object; + stack_map class java/lang/Throwable; + astore_3; + aload_2; + pop; + aload_3; + instanceof class ControlFlowError; + ifeq L82; + new class java/lang/NullPointerException; + dup; + invokespecial Method java/lang/NullPointerException."":"()V"; + athrow; + L82: stack_frame_type append; + locals_map class java/lang/Throwable; + aload_3; + instanceof class java/lang/Error; + ifeq L94; + aload_3; + checkcast class java/lang/Error; + athrow; + L94: stack_frame_type same; + aload_3; + checkcast class java/lang/Exception; + athrow; + catch t1 #0; + catch t2 #0; + stack_frame_type full; + locals_map bogus, class java/lang/Object, class java/lang/Object; + stack_map class java/lang/Throwable; + astore 4; + endtry t2; + invokestatic Method ThreadLocalTransaction.clearThreadLocalTransaction:"()V"; + aload 4; + athrow; + L107: stack_frame_type full; + locals_map class HandlerInTry, class java/lang/Object, null; + return; +} + +} // end Class HandlerInTry diff -r 368df6a4b403 -r d982b0f5b59a test/runtime/handlerInTry/IsolatedHandlerInTry.jasm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/handlerInTry/IsolatedHandlerInTry.jasm Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * IsolatedHandlerInTry contains a try block in a ctor whose handler is inside + * the same try block but the handler can only be reached if an exception + * occurs. The handler does a return. So, a VerifyException should be thrown. + * The try block starts at line 77 (try t2;) and ends at line 113 (endtry t2;). + * Its handler starts at line 107 (catch t2 #0;). The handler can only be reached + * by exception because of the athrow at line 106. + */ +super public class IsolatedHandlerInTry + version 51:0 +{ + +public static final synthetic Field ___transactionFactory_2002349702336125:"Ljava/lang/Object;"; + +public Method "":"(Ljava/lang/Object;)V" + stack 5 locals 5 +{ + invokestatic Method ThreadLocalTransaction.getThreadLocalTransaction:"()Ljava/lang/Object;"; + checkcast class java/lang/Object; + astore_2; + aload_2; + invokestatic Method TransactionLogicDonor.isActiveTransaction:"(Ljava/lang/Object;)Z"; + ifeq L21; + aload_0; + aload_1; + aload_2; + invokespecial Method "":"(Ljava/lang/Object;Ljava/lang/Object;)V"; + return; + L21: stack_frame_type append; + locals_map class java/lang/Object; + aload_2; + getstatic Field ___transactionFactory_2002349702336125:"Ljava/lang/Object;"; + invokestatic Method TransactionLogicDonor.createTransaction:"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; + astore_2; + aload_2; + iconst_1; + pop; + aload_2; + invokestatic Method ThreadLocalTransaction.setThreadLocalTransaction:"(Ljava/lang/Object;)V"; + try t0, t1; + aload_0; + aload_1; + aload_2; + invokespecial Method "":"(Ljava/lang/Object;Ljava/lang/Object;)V"; + aload_2; + pop; + aconst_null; + astore_2; + endtry t0, t1; + invokestatic Method ThreadLocalTransaction.clearThreadLocalTransaction:"()V"; + pop; + goto L107; + catch t0 java/lang/Throwable; + try t2; + stack_frame_type full; + locals_map bogus, class java/lang/Object, class java/lang/Object; + stack_map class java/lang/Throwable; + astore_3; + aload_2; + pop; + aload_3; + instanceof class ControlFlowError; + ifeq L82; + new class java/lang/NullPointerException; + dup; + invokespecial Method java/lang/NullPointerException."":"()V"; + athrow; + L82: stack_frame_type append; + locals_map class java/lang/Throwable; + aload_3; + instanceof class java/lang/Error; + ifeq L94; + aload_3; + checkcast class java/lang/Error; + athrow; + L94: stack_frame_type same; + aload_3; + checkcast class java/lang/Exception; + catch t1 #0; + stack_frame_type full; + locals_map bogus, class java/lang/Object, class java/lang/Object; + stack_map class java/lang/Throwable; + athrow; + catch t2 #0; + stack_frame_type full; + locals_map bogus, class java/lang/Object, class java/lang/Object; + stack_map class java/lang/Throwable; + astore 4; + return; + endtry t2; + stack_frame_type full; + locals_map bogus, class java/lang/Object, class java/lang/Object, class java/lang/Object; + stack_map class java/lang/Throwable; + invokestatic Method ThreadLocalTransaction.clearThreadLocalTransaction:"()V"; + athrow; + L107: stack_frame_type full; + locals_map class IsolatedHandlerInTry, class java/lang/Object, null; + return; +} + +} // end Class IsolatedHandlerInTry diff -r 368df6a4b403 -r d982b0f5b59a test/runtime/handlerInTry/LoadHandlerInTry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/handlerInTry/LoadHandlerInTry.java Sun May 21 11:12:48 2017 -0700 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 2016, 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. + */ + +/* + * @test + * @bug 8075118 + * @summary JVM stuck in infinite loop during verification + * @compile HandlerInTry.jasm + * @compile IsolatedHandlerInTry.jasm + * @run main/othervm -Xverify:all LoadHandlerInTry + */ + +/* + * This test has two cases: + * + * 1. class HandlerInTry: Class HandlerInTry contains a TRY block in a + * constructor whose handler is inside the same TRY block. The last + * few bytecodes and exception table look like this: + * + * ... + * 87: athrow + * 88: astore 4 + * 90: invokestatic #9 + * 93: aload 4 + * 95: athrow + * 96: return + * Exception table: + * from to target type + * 36 46 53 Class java/lang/Throwable + * 36 46 88 any + * 53 90 88 any + * + * Note that the target for the third handler in the Exception table is + * inside its TRY block. + * Without the fix for bug JDK-8075118, this test will time out. + * + * + * 2. class IsolatedHandlerInTry: Class IsolatedHandlerInTry also contains + * a TRY block in a constructoer whose handler is inside its TRY block. + * But the handler is only reachable if an exception is thrown. The + * handler's bytecodes will not get parsed as part of parsing the TRY + * block. They will only get parsed as a handler for the TRY block. + * Since the isolated handler does a 'return', a VerifyError exception + * should get thrown. + */ + +public class LoadHandlerInTry { + + public static void main(String[] args) throws Exception { + System.out.println("Regression test for bug 8075118"); + try { + Class newClass = Class.forName("HandlerInTry"); + throw new RuntimeException( + "Failed to throw VerifyError for HandlerInTry"); + } catch (java.lang.VerifyError e) { + System.out.println("Passed: VerifyError exception was thrown"); + } + + try { + Class newClass = Class.forName("IsolatedHandlerInTry"); + throw new RuntimeException( + "Failed to throw VerifyError for IsolatedHandlerInTry"); + } catch (java.lang.VerifyError e) { + System.out.println("Passed: VerifyError exception was thrown"); + } + } +}