# HG changeset patch # User Gilles Duboscq # Date 1413381770 -7200 # Node ID 52b4284cb4967c0a73c8e3ad3a7cb90d5435437e # Parent 45d7b2c7029d5010542889c5bb19d6c6e5a5b5aa# Parent eaa4074a7e3975cd33ec55e6b584586e2ac681bd Merge with jdk8u20-b26 diff -r 45d7b2c7029d -r 52b4284cb496 .hgtags --- a/.hgtags Thu Oct 16 10:21:29 2014 +0200 +++ b/.hgtags Wed Oct 15 16:02:50 2014 +0200 @@ -408,6 +408,8 @@ 55fb97c4c58d6ed4db8ec02a382ba518d9265815 hs25-b65 d3521d8e562a782f66fc0dfdebeffba2c7e3471d jdk8-b122 591135a7d6f96c0ef281d078cee9a8d8c342d45c jdk8-b123 +c89630a122b43d0eabd78b74f6498a1c3cf04ca3 jdk8u20-b00 +c89630a122b43d0eabd78b74f6498a1c3cf04ca3 hs25.20-b00 9b9816164447214f21b06ccf646893c281c76a42 hs25-b66 df333ee12bba67e2e928f8ce1da37afd9bf95b48 jdk8-b124 3585183c191aa6b4d0375ea659515335e1804417 hs25-b67 @@ -436,6 +438,7 @@ 17a75e692af397532e2b296b24f6b9b6c239c633 jdk8u5-b11 9b289963cb9a14636fbe8faaa2dd6d3678464a7b jdk8u5-b12 8a67179106085689906732013a282efeeb9bd5f4 jdk8u5-b13 +5c7ef8e396835b82c0460b73f23cac86ba34846f jdk8u5-b31 f0d759a6a2309a1c149d530b29db24eda885f267 jdk8u11-b01 3c079aebb516765784dd8097887daadda5a76ac1 jdk8u11-b02 0037e964ce486c009984171f004259263628079f jdk8u11-b03 @@ -447,6 +450,56 @@ 34de1e8eeabbcc6e690f92766fd619beb9f3f049 jdk8u11-b09 7e4ae023277bef5b82361fd985262f4009eb2fe8 jdk8u11-b10 e6b7384074325d5a4ede728d6928ecb7f1cc1326 jdk8u11-b11 +78df957d46ebd98ba5bb68f4d9654c8bea3f1587 jdk8u11-b12 +13f04650aa09df696d62a1912febe25fe4a64082 jdk8u11-b31 +412d3b5fe90e54c0ff9d9ac7374b98607c561d5a hs25.20-b01 +4638c4d7ff106db0f29ef7f18b128dd7e69bc470 hs25.20-b02 +e56d11f8cc2158d4280f80e56d196193349c150a hs25.20-b03 +757fe22ae90681e2b6cff50699c5abbe2563dd2c jdk8u20-b01 +9c2ddd17626e375554044a3082a6dc5e68184ed9 jdk8u20-b02 +ecf3678d5736a645aea893b525a9eb5fa1a8e072 hs25.20-b04 +51e1bb81df8680bd237630323de5e0704fb25607 jdk8u20-b03 +54436d3b2a915ff50a8d6b34f61d5afb45be7bb6 hs25.20-b05 +d4e18f0633c662588cc0875be7759721c7d85af4 jdk8u20-b04 +57eb3e69397e9d5818c5fdaef65b47d9b03f7f88 jdk8u20-b05 +804f89b6ff46728d60a69e9a338e63f362f7ac68 hs25.20-b06 +c3d92e04873788275eeebec6bcd2948cdbd143a7 jdk8u20-b06 +39eae002499704438142e78f5e0e24d46d0b266f hs25.20-b07 +f0ea4d3df1299b6c958e1a72f892c695fca055ad jdk8u20-b07 +2627c7be4279478b880d7f643a252d185e4915ec hs25.20-b08 +e9ffa408f7af28205a7114ca78bce29846f5a8df jdk8u20-b08 +5186bc5047c1725888ed99f423bdfaa116e05abe hs25.20-b09 +4d73f1e99f97d1444e16ee5ef4634eb2129969ad jdk8u20-b09 +27a9e6a96a8ced7b7ee892d5d0f1a735b9010abb hs25.20-b10 +300e2c5eeb2710de3630d14ffe4592214633dbff jdk8u20-b10 +70dc2c030c69470a5d9099b7f54e4cfef89276fd jdk8u20-b11 +b6a2ba7d3ea7259a76c8ff1ec22fac9094494c1c hs25.20-b11 +3c291bc2aa7c58efb1219701f38c41731609e595 hs25.20-b12 +18ae0dac7620474547aa1721bc3fd748af07b8b5 jdk8u20-b12 +47951595af60460a479b8574622375bfbf5c8ed2 jdk8u20-b13 +798f5b02be897151fdad44d695446088b1cca6b1 hs25.20-b13 +28bbbecff5f08c1e343fc0c40923c05d86b7cf82 hs25.20-b14 +c20d8a452574c85c8fc1f7f2d4e788cd6b156bc9 jdk8u20-b14 +87bdb86f0aedbd9b9ef8e9999b273114c8be4748 hs25.20-b15 +8c785f9bde6f603cbd13eecd2ee6acd699b376f8 jdk8u20-b15 +50e5d560367b94275a91d5d579c32f1164eb5fa5 hs25.20-b16 +c36ef639e6d3c2d238f4e4f8b2f5803a60de8be8 jdk8u20-b16 +ee8b934668694dba5dc0ac039f8d56e52499c0f9 hs25.20-b17 +8ea4732884ccd5586f0afe9478b80add90231455 jdk8u20-b17 +b685b4e870b159ea5731984199d275879d427038 hs25.20-b18 +11159d7ec80462a422e39c9b3a39ae932923622d jdk8u20-b18 +3e1cec358ab95ef985f821219104141b9ffda83f hs25.20-b19 +b15553cde967dfd7781a4a5c669e4cb7db734317 jdk8u20-b19 +4f18dea0312d601d0515976bc0c643ea7acc829d hs25.20-b20 +9e4d27da4ac04c6e19291087f7c68a5c5803c7ca jdk8u20-b20 +4828415ebbf11e205dcc08e97ad5ae7dd03522f9 jdk8u20-b21 +e4a6e7f1b90b85270aee1c54edaca3ef737082f1 hs25.20-b21 +f7429096a202cab5c36a0f20dea33c554026010f jdk8u20-b22 +7c56530b11496459e66cb9ea933035002311672c hs25.20-b22 +f09d1f6a401e25a54dad44bb7bea482e47558af5 jdk8u20-b23 +42ddd0bbcb6630fe463ec9bc1893c838d5edff1b jdk8u20-b24 +00cf2b6f51b9560b01030e8f4c28c466f0b21fe3 hs25.20-b23 +19408d5fd31c25ce60c43dd33e92b96e8df4a4ea jdk8u20-b25 b124e22eb772806c13d942cc110de38da0108147 graal-0.1 483d05bf77a7c2a762aca1e06c4191bc06647176 graal-0.2 9535eccd2a115f6c6f0b15efb508b11ff74cc0d3 graal-0.3 diff -r 45d7b2c7029d -r 52b4284cb496 THIRD_PARTY_README --- a/THIRD_PARTY_README Thu Oct 16 10:21:29 2014 +0200 +++ b/THIRD_PARTY_README Wed Oct 15 16:02:50 2014 +0200 @@ -2,7 +2,7 @@ ----------------------------- %% This notice is provided with respect to ASM Bytecode Manipulation -Framework v5.0, which may be included with JRE 8, and JDK 8, and +Framework v5.0.3, which may be included with JRE 8, and JDK 8, and OpenJDK 8. --- begin of LICENSE --- @@ -1471,7 +1471,7 @@ version 2.0. The NSS libraries are supplied in executable form, built from unmodified -NSS source code labeled with the "NSS_3.13.1_RTM" release tag. +NSS source code labeled with the "NSS_3_16_RTM" HG tag. The NSS source code is available in the OpenJDK source code repository at: jdk/test/sun/security/pkcs11/nss/src @@ -3349,14 +3349,14 @@ ------------------------------------------------------------------------------- -%% This notice is provided with respect to zlib v1.2.5, which may be included +%% This notice is provided with respect to zlib v1.2.8, which may be included with JRE 8, JDK 8, and OpenJDK 8. --- begin of LICENSE --- - version 1.2.5, July 18th, 2005 - - Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -3382,11 +3382,11 @@ ------------------------------------------------------------------------------- %% This notice is provided with respect to the following which may be -included with JRE 8, JDK 8, and OpenJDK 8, except where noted: - - Apache Commons Math 2.2 - Apache Derby 10.10.1.2 [included with JDK 8] - Apache Jakarta BCEL 5.2 +included with JRE 8, JDK 8, and OpenJDK 8. + + Apache Commons Math 3.2 + Apache Derby 10.10.1.3 + Apache Jakarta BCEL 5.1 Apache Jakarta Regexp 1.4 Apache Santuario XML Security for Java 1.5.4 Apache Xalan-Java 2.7.1 diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/os/bsd/MacosxDebuggerLocal.m --- a/agent/src/os/bsd/MacosxDebuggerLocal.m Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/os/bsd/MacosxDebuggerLocal.m Wed Oct 15 16:02:50 2014 +0200 @@ -95,7 +95,9 @@ #define CHECK_EXCEPTION_CLEAR_(value) if ((*env)->ExceptionOccurred(env)) { (*env)->ExceptionClear(env); return value; } static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { - (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg); + jclass exceptionClass = (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"); + CHECK_EXCEPTION; + (*env)->ThrowNew(env, exceptionClass, errMsg); } static struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj) { @@ -129,6 +131,7 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0(JNIEnv *env, jclass cls) { symbolicatorID = (*env)->GetFieldID(env, cls, "symbolicator", "J"); + CHECK_EXCEPTION; taskID = (*env)->GetFieldID(env, cls, "task", "J"); CHECK_EXCEPTION; @@ -236,13 +239,16 @@ (JNIEnv *env, jobject this_obj, jlong addr) { uintptr_t offset; const char* sym = NULL; + jstring sym_string; struct ps_prochandle* ph = get_proc_handle(env, this_obj); if (ph != NULL && ph->core != NULL) { sym = symbol_for_pc(ph, (uintptr_t) addr, &offset); if (sym == NULL) return 0; + sym_string = (*env)->NewStringUTF(env, sym); + CHECK_EXCEPTION_(0); return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, - (*env)->NewStringUTF(env, sym), (jlong)offset); + sym_string, (jlong)offset); } return 0; } @@ -749,11 +755,14 @@ const char* name; jobject loadObject; jobject loadObjectList; + jstring nameString; base = get_lib_base(ph, i); name = get_lib_name(ph, i); + nameString = (*env)->NewStringUTF(env, name); + CHECK_EXCEPTION; loadObject = (*env)->CallObjectMethod(env, this_obj, createLoadObject_ID, - (*env)->NewStringUTF(env, name), (jlong)0, (jlong)base); + nameString, (jlong)0, (jlong)base); CHECK_EXCEPTION; loadObjectList = (*env)->GetObjectField(env, this_obj, loadObjectList_ID); CHECK_EXCEPTION; diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/os/linux/libproc.h --- a/agent/src/os/linux/libproc.h Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/os/linux/libproc.h Wed Oct 15 16:02:50 2014 +0200 @@ -34,19 +34,7 @@ #include "libproc_md.h" #endif -#if defined(sparc) || defined(sparcv9) -/* - If _LP64 is defined ptrace.h should be taken from /usr/include/asm-sparc64 - otherwise it should be from /usr/include/asm-sparc - These two files define pt_regs structure differently -*/ -#ifdef _LP64 -#include "asm-sparc64/ptrace.h" -#else -#include "asm-sparc/ptrace.h" -#endif - -#endif //sparc or sparcv9 +#include /************************************************************************************ @@ -80,7 +68,7 @@ *************************************************************************************/ -#if defined(sparc) || defined(sparcv9) +#if defined(sparc) || defined(sparcv9) || defined(ppc64) #define user_regs_struct pt_regs #endif diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java Wed Oct 15 16:02:50 2014 +0200 @@ -95,9 +95,15 @@ int entryBci = task.osrBci(); int compLevel = task.compLevel(); Klass holder = method.getMethodHolder(); - out.println("compile " + holder.getName().asString() + " " + - OopUtilities.escapeString(method.getName().asString()) + " " + - method.getSignature().asString() + " " + - entryBci + " " + compLevel); + out.print("compile " + holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString() + " " + + entryBci + " " + compLevel); + Compile compiler = compilerData(); + if (compiler != null) { + // Dump inlining data. + compiler.dumpInlineData(out); + } + out.println(); } } diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java --- a/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java Wed Oct 15 16:02:50 2014 +0200 @@ -51,9 +51,9 @@ static private CIntegerField summaryBytesUsedField; // G1MonitoringSupport* _g1mm; static private AddressField g1mmField; - // MasterOldRegionSet _old_set; + // HeapRegionSet _old_set; static private long oldSetFieldOffset; - // MasterHumongousRegionSet _humongous_set; + // HeapRegionSet _humongous_set; static private long humongousSetFieldOffset; static { diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetBase.java --- a/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetBase.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -40,12 +40,8 @@ // Mirror class for HeapRegionSetBase. Represents a group of regions. public class HeapRegionSetBase extends VMObject { - // uint _length; - static private CIntegerField lengthField; - // uint _region_num; - static private CIntegerField regionNumField; - // size_t _total_used_bytes; - static private CIntegerField totalUsedBytesField; + + static private long countField; static { VM.registerVMInitializedObserver(new Observer() { @@ -58,21 +54,13 @@ static private synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("HeapRegionSetBase"); - lengthField = type.getCIntegerField("_length"); - regionNumField = type.getCIntegerField("_region_num"); - totalUsedBytesField = type.getCIntegerField("_total_used_bytes"); + countField = type.getField("_count").getOffset(); } - public long length() { - return lengthField.getValue(addr); - } - public long regionNum() { - return regionNumField.getValue(addr); - } - - public long totalUsedBytes() { - return totalUsedBytesField.getValue(addr); + public HeapRegionSetCount count() { + Address countFieldAddr = addr.addOffsetTo(countField); + return (HeapRegionSetCount) VMObjectFactory.newObject(HeapRegionSetCount.class, countFieldAddr); } public HeapRegionSetBase(Address addr) { diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetCount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetCount.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.jvm.hotspot.gc_implementation.g1; + +import java.util.Iterator; +import java.util.Observable; +import java.util.Observer; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.VMObject; +import sun.jvm.hotspot.runtime.VMObjectFactory; +import sun.jvm.hotspot.types.AddressField; +import sun.jvm.hotspot.types.CIntegerField; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; + +// Mirror class for HeapRegionSetCount. Represents a group of regions. + +public class HeapRegionSetCount extends VMObject { + + static private CIntegerField lengthField; + static private CIntegerField capacityField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + static private synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("HeapRegionSetCount"); + + lengthField = type.getCIntegerField("_length"); + capacityField = type.getCIntegerField("_capacity"); + } + + public long length() { + return lengthField.getValue(addr); + } + + public long capacity() { + return capacityField.getValue(addr); + } + + public HeapRegionSetCount(Address addr) { + super(addr); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/opto/Compile.java --- a/agent/src/share/classes/sun/jvm/hotspot/opto/Compile.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/opto/Compile.java Wed Oct 15 16:02:50 2014 +0200 @@ -25,6 +25,7 @@ package sun.jvm.hotspot.opto; import java.util.*; +import java.io.PrintStream; import sun.jvm.hotspot.ci.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.runtime.*; @@ -92,4 +93,13 @@ } return null; } + + public void dumpInlineData(PrintStream out) { + InlineTree inlTree = ilt(); + if (inlTree != null) { + out.print(" inline " + inlTree.count()); + inlTree.dumpReplayData(out); + } + } + } diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/opto/InlineTree.java --- a/agent/src/share/classes/sun/jvm/hotspot/opto/InlineTree.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/opto/InlineTree.java Wed Oct 15 16:02:50 2014 +0200 @@ -87,6 +87,11 @@ return GrowableArray.create(addr, inlineTreeConstructor); } + public int inlineLevel() { + JVMState jvms = callerJvms(); + return (jvms != null) ? jvms.depth() : 0; + } + public void printImpl(PrintStream st, int indent) { for (int i = 0; i < indent; i++) st.print(" "); st.printf(" @ %d ", callerBci()); @@ -101,4 +106,28 @@ public void print(PrintStream st) { printImpl(st, 2); } + + // Count number of nodes in this subtree + public int count() { + int result = 1; + GrowableArray subt = subtrees(); + for (int i = 0 ; i < subt.length(); i++) { + result += subt.at(i).count(); + } + return result; + } + + public void dumpReplayData(PrintStream out) { + out.printf(" %d %d ", inlineLevel(), callerBci()); + Method method = (Method)method().getMetadata(); + Klass holder = method.getMethodHolder(); + out.print(holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString()); + + GrowableArray subt = subtrees(); + for (int i = 0 ; i < subt.length(); i++) { + subt.at(i).dumpReplayData(out); + } + } } diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/opto/JVMState.java --- a/agent/src/share/classes/sun/jvm/hotspot/opto/JVMState.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/opto/JVMState.java Wed Oct 15 16:02:50 2014 +0200 @@ -88,6 +88,10 @@ return (int)bciField.getValue(getAddress()); } + public int depth() { + return (int)depthField.getValue(getAddress()); + } + public JVMState caller() { return create(callerField.getValue(getAddress())); } diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java --- a/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Wed Oct 15 16:02:50 2014 +0200 @@ -114,7 +114,8 @@ long survivorRegionNum = g1mm.survivorRegionNum(); HeapRegionSetBase oldSet = g1h.oldSet(); HeapRegionSetBase humongousSet = g1h.humongousSet(); - long oldRegionNum = oldSet.regionNum() + humongousSet.regionNum(); + long oldRegionNum = oldSet.count().length() + + humongousSet.count().capacity() / HeapRegion.grainBytes(); printG1Space("G1 Heap:", g1h.n_regions(), g1h.used(), g1h.capacity()); System.out.println("G1 Young Generation:"); diff -r 45d7b2c7029d -r 52b4284cb496 agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java --- a/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java Thu Oct 16 10:21:29 2014 +0200 +++ b/agent/src/share/classes/sun/jvm/hotspot/utilities/Hashtable.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -61,8 +61,9 @@ long h = 0; int s = 0; int len = buf.length; + // Emulate the unsigned int in java_lang_String::hash_code while (len-- > 0) { - h = 31*h + (0xFFL & buf[s]); + h = 31*h + (0xFFFFFFFFL & buf[s]); s++; } return h & 0xFFFFFFFFL; diff -r 45d7b2c7029d -r 52b4284cb496 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Thu Oct 16 10:21:29 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -1354,6 +1354,7 @@ @HotSpotVMConstant(name = "DataLayout::call_type_data_tag") @Stable public int dataLayoutCallTypeDataTag; @HotSpotVMConstant(name = "DataLayout::virtual_call_type_data_tag") @Stable public int dataLayoutVirtualCallTypeDataTag; @HotSpotVMConstant(name = "DataLayout::parameters_type_data_tag") @Stable public int dataLayoutParametersTypeDataTag; + @HotSpotVMConstant(name = "DataLayout::speculative_trap_data_tag") @Stable public int dataLayoutSpeculativeTrapDataTag; @HotSpotVMFlag(name = "BciProfileWidth") @Stable public int bciProfileWidth; @HotSpotVMFlag(name = "TypeProfileWidth") @Stable public int typeProfileWidth; diff -r 45d7b2c7029d -r 52b4284cb496 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java Thu Oct 16 10:21:29 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java Wed Oct 15 16:02:50 2014 +0200 @@ -62,6 +62,7 @@ null, // call_type_data_tag null, // virtual_call_type_data_tag null, // parameters_type_data_tag + null, // speculative_trap_data_tag }; // @formatter:on diff -r 45d7b2c7029d -r 52b4284cb496 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodDataAccessor.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodDataAccessor.java Thu Oct 16 10:21:29 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodDataAccessor.java Wed Oct 15 16:02:50 2014 +0200 @@ -52,7 +52,8 @@ ArgInfoData(config().dataLayoutArgInfoDataTag), CallTypeData(config().dataLayoutCallTypeDataTag), VirtualCallTypeData(config().dataLayoutVirtualCallTypeDataTag), - ParametersTypeData(config().dataLayoutParametersTypeDataTag); + ParametersTypeData(config().dataLayoutParametersTypeDataTag), + SpeculativeTrapData(config().dataLayoutSpeculativeTrapDataTag); private final int value; diff -r 45d7b2c7029d -r 52b4284cb496 make/Makefile --- a/make/Makefile Thu Oct 16 10:21:29 2014 +0200 +++ b/make/Makefile Wed Oct 15 16:02:50 2014 +0200 @@ -90,6 +90,7 @@ # Typical C1/C2 targets made available with this Makefile C1_VM_TARGETS=product1 fastdebug1 optimized1 debug1 C2_VM_TARGETS=product fastdebug optimized debug +CORE_VM_TARGETS=productcore fastdebugcore optimizedcore debugcore ZERO_VM_TARGETS=productzero fastdebugzero optimizedzero debugzero SHARK_VM_TARGETS=productshark fastdebugshark optimizedshark debugshark MINIMAL1_VM_TARGETS=productminimal1 fastdebugminimal1 debugminimal1 @@ -140,6 +141,12 @@ all_debugshark: debugshark 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: debugcore docs export_debug +all_optimizedcore: optimizedcore docs export_optimized + allgraal: all_productgraal all_fastdebuggraal all_productgraal: productgraal docs export_product all_fastdebuggraal: fastdebuggraal docs export_fastdebug @@ -164,6 +171,7 @@ # Output directories C1_DIR =$(OUTPUTDIR)/$(VM_PLATFORM)_compiler1 C2_DIR =$(OUTPUTDIR)/$(VM_PLATFORM)_compiler2 +CORE_DIR =$(OUTPUTDIR)/$(VM_PLATFORM)_core GRAAL_DIR =$(OUTPUTDIR)/$(VM_PLATFORM)_graal MINIMAL1_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_minimal1 ZERO_DIR =$(OUTPUTDIR)/$(VM_PLATFORM)_zero @@ -179,6 +187,10 @@ $(CD) $(GAMMADIR)/make; \ $(MAKE) BUILD_DIR=$(C2_DIR) BUILD_FLAVOR=$@ VM_TARGET=$@ generic_build2 $(ALT_OUT) +$(CORE_VM_TARGETS): + $(CD) $(GAMMADIR)/make; \ + $(MAKE) BUILD_DIR=$(CORE_DIR) BUILD_FLAVOR=$(@:%core=%) VM_TARGET=$@ generic_buildcore $(ALT_OUT) + $(ZERO_VM_TARGETS): $(CD) $(GAMMADIR)/make; \ $(MAKE) BUILD_DIR=$(ZERO_DIR) BUILD_FLAVOR=$(@:%zero=%) VM_TARGET=$@ generic_buildzero $(ALT_OUT) @@ -240,6 +252,20 @@ $(MAKE_ARGS) $(VM_TARGET) endif +generic_buildcore: $(HOTSPOT_SCRIPT) +ifeq ($(HS_ARCH),ppc) + ifeq ($(ARCH_DATA_MODEL),64) + $(MKDIR) -p $(OUTPUTDIR) + $(CD) $(OUTPUTDIR); \ + $(MAKE) -f $(ABS_OS_MAKEFILE) \ + $(MAKE_ARGS) $(VM_TARGET) + else + @$(ECHO) "No ($(VM_TARGET)) for ppc ARCH_DATA_MODEL=$(ARCH_DATA_MODEL)" + endif +else + @$(ECHO) "No ($(VM_TARGET)) for $(HS_ARCH)" +endif + generic_buildzero: $(HOTSPOT_SCRIPT) $(MKDIR) -p $(OUTPUTDIR) $(CD) $(OUTPUTDIR); \ @@ -310,12 +336,13 @@ DOCS_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_docs C1_BUILD_DIR =$(C1_DIR)/$(BUILD_FLAVOR) C2_BUILD_DIR =$(C2_DIR)/$(BUILD_FLAVOR) +CORE_BUILD_DIR =$(CORE_DIR)/$(BUILD_FLAVOR) GRAAL_BUILD_DIR =$(GRAAL_DIR)/$(BUILD_FLAVOR) MINIMAL1_BUILD_DIR=$(MINIMAL1_DIR)/$(BUILD_FLAVOR) ZERO_BUILD_DIR =$(ZERO_DIR)/$(BUILD_FLAVOR) SHARK_BUILD_DIR =$(SHARK_DIR)/$(BUILD_FLAVOR) -# Server (C2) and Graal +# Server (C2) ifeq ($(JVM_VARIANT_SERVER), true) # Common $(EXPORT_SERVER_DIR)/%.diz: $(C2_BUILD_DIR)/%.diz @@ -537,6 +564,28 @@ $(install-dir) endif +# Core +ifeq ($(JVM_VARIANT_CORE), true) +# Common +$(EXPORT_LIB_DIR)/%.jar: $(CORE_BUILD_DIR)/../generated/%.jar + $(install-file) +$(EXPORT_INCLUDE_DIR)/%: $(CORE_BUILD_DIR)/../generated/jvmtifiles/% + $(install-file) +# Unix +$(EXPORT_JRE_LIB_ARCH_DIR)/%.$(LIBRARY_SUFFIX): $(CORE_BUILD_DIR)/%.$(LIBRARY_SUFFIX) + $(install-file) +$(EXPORT_JRE_LIB_ARCH_DIR)/%.debuginfo: $(CORE_BUILD_DIR)/%.debuginfo + $(install-file) +$(EXPORT_JRE_LIB_ARCH_DIR)/%.diz: $(CORE_BUILD_DIR)/%.diz + $(install-file) +$(EXPORT_SERVER_DIR)/%.$(LIBRARY_SUFFIX): $(CORE_BUILD_DIR)/%.$(LIBRARY_SUFFIX) + $(install-file) +$(EXPORT_SERVER_DIR)/%.debuginfo: $(CORE_BUILD_DIR)/%.debuginfo + $(install-file) +$(EXPORT_SERVER_DIR)/%.diz: $(CORE_BUILD_DIR)/%.diz + $(install-file) +endif + # Shark ifeq ($(JVM_VARIANT_ZEROSHARK), true) # Common @@ -611,6 +660,7 @@ $(RM) -r $(SHARED_DIR) $(RM) -r $(C1_DIR) $(RM) -r $(C2_DIR) + $(RM) -r $(CORE_DIR) $(RM) -r $(GRAAL_DIR) $(RM) -r $(ZERO_DIR) $(RM) -r $(SHARK_DIR) diff -r 45d7b2c7029d -r 52b4284cb496 make/aix/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/Makefile Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,381 @@ +# +# 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. +# +# + +# 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 built before building libjvm.so +# Please refer to ./makefiles/sa.make +# 2. libsa.so - Native library for SA - This is built after +# libjsig.so (signal interposition library) +# Please refer to ./makefiles/vm.make +# If $(GAMMADIR)/agent dir is not present, SA components are not built. + +# No tests on Aix. +TEST_IN_BUILD=false + +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(le), so we cannot build a tiered VM: +ifneq (,$(filter $(ARCH),ppc64 pp64le)) + 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 +# optimized compiler2 __compiler2/optimized +# product compiler2 __compiler2/product +# +# debug1 compiler1 __compiler1/debug +# fastdebug1 compiler1 __compiler1/fastdebug +# optimized1 compiler1 __compiler1/optimized +# product1 compiler1 __compiler1/product +# +# debugcore core __core/debug +# fastdebugcore core __core/fastdebug +# optimizedcore core __core/optimized +# productcore core __core/product +# +# debugzero zero __zero/debug +# fastdebugzero zero __zero/fastdebug +# optimizedzero zero __zero/optimized +# productzero zero __zero/product +# +# debugshark shark __shark/debug +# fastdebugshark shark __shark/fastdebug +# optimizedshark shark __shark/optimized +# productshark shark __shark/product +# +# fastdebugminimal1 minimal1 __minimal1/fastdebug +# productminimal1 minimal1 __minimal1/product +# +# What you get with each target: +# +# debug* - debug compile with asserts enabled +# fastdebug* - optimized compile, but with asserts enabled +# optimized* - optimized compile, no asserts +# 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 fastdebug optimized 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)) +SUBDIRS_MINIMAL1 = $(addprefix $(OSNAME)_$(BUILDARCH)_minimal1/,$(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)) +TARGETS_MINIMAL1 = $(addsuffix minimal1,$(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)" + @echo " $(TARGETS_MINIMAL1)" + +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) + +$(SUBDIRS_MINIMAL1): $(BUILDTREE_MAKE) + $(QUIETLY) $(MAKE) -f $(GAMMADIR)/make/$(OSNAME)/Makefile checks + $(BUILDTREE) VARIANT=minimal1 + + +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) +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler2/$@ && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_TIERED): $(SUBDIRS_TIERED) + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_tiered/$(patsubst %tiered,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_C1): $(SUBDIRS_C1) + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_compiler1/$(patsubst %1,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_CORE): $(SUBDIRS_CORE) + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_ZERO): $(SUBDIRS_ZERO) + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) +ifdef INSTALL + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_SHARK): $(SUBDIRS_SHARK) + cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) +ifdef INSTALL + cd $(OSNAME)_$(VARIANTARCH)_shark/$(patsubst %shark,%,$@) && $(MAKE) $(MFLAGS) install +endif + +$(TARGETS_MINIMAL1): $(SUBDIRS_MINIMAL1) + cd $(OSNAME)_$(BUILDARCH)_minimal1/$(patsubst %minimal1,%,$@) && $(MAKE) $(MFLAGS) +ifdef INSTALL + cd $(OSNAME)_$(BUILDARCH)_minimal1/$(patsubst %minimal1,%,$@) && $(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) +treeminimal1: $(SUBDIRS_MINIMAL1) + +# 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: debug product + +compiler1: debug1 product1 + +core: debugcore productcore + +zero: debugzero productzero + +shark: debugshark productshark + +clean_docs: + rm -rf $(SUBDIR_DOCS) + +clean_compiler1 clean_compiler2 clean_core clean_zero clean_shark clean_minimal1: + rm -rf $(OSNAME)_$(BUILDARCH)_$(subst clean_,,$@) + +clean: clean_compiler2 clean_compiler1 clean_core clean_zero clean_shark clean_minimal1 clean_docs + +include $(GAMMADIR)/make/cscope.make + +#------------------------------------------------------------------------------- + +.PHONY: $(TARGETS_C2) $(TARGETS_C1) $(TARGETS_CORE) $(TARGETS_ZERO) $(TARGETS_SHARK) $(TARGETS_MINIMAL1) +.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 45d7b2c7029d -r 52b4284cb496 make/aix/adlc_updater --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/adlc_updater Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/adjust-mflags.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/adjust-mflags.sh Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,87 @@ +#! /bin/sh +# +# 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 +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/adlc.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/adlc.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,231 @@ +# +# 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 +# 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) +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) +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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/buildtree.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/buildtree.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,364 @@ +# +# Copyright (c) 2005, 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. +# +# + +# 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 +# +# 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 $(SPEC) +include $(GAMMADIR)/make/scm.make +include $(GAMMADIR)/make/defs.make +include $(GAMMADIR)/make/altsrc.make + + +# 'gmake MAKE_VERBOSE=y' or 'gmake QUIETLY=' gives all the gory details. +QUIETLY$(MAKE_VERBOSE) = @ + +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 optimized product +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 jvmti.make trace.make sa.make + +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))) + +# This bit is needed to enable local rebuilds. +# Unless the makefile itself sets LP64, any environmental +# setting of LP64 will interfere with the build. +LP64_SETTING/32 = LP64 = \#empty +LP64_SETTING/64 = LP64 = 1 + +DATA_MODE/ppc64 = 64 + +DATA_MODE = $(DATA_MODE/$(BUILDARCH)) + +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 "HS_ALT_MAKE = $(HS_ALT_MAKE)"; \ + echo "OSNAME = $(OSNAME)"; \ + 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 "$(LP64_SETTING/$(DATA_MODE))"; \ + 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)"; \ + [ -n "$(INCLUDE_TRACE)" ] && \ + echo && echo "INCLUDE_TRACE = $(INCLUDE_TRACE)"; \ + echo; \ + [ -n "$(SPEC)" ] && \ + echo "include $(SPEC)"; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(VARIANT).make"; \ + echo "include \$$(GAMMADIR)/make/excludeSrc.make"; \ + echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(COMPILER).make"; \ + ) > $@ + +flags_vm.make: $(BUILDTREE_MAKE) ../shared_dirs.lst + @echo Creating $@ ... + $(QUIETLY) ( \ + $(BUILDTREE_COMMENT); \ + echo; \ + 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)"; \ + ) > $@ + +FORCE: + +.PHONY: all FORCE diff -r 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/compiler2.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/compiler2.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,32 @@ +# +# 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. +# +# + +# Sets make macros for making server version of VM + +TYPE=COMPILER2 + +VM_SUBDIR = server + +CFLAGS += -DCOMPILER2 diff -r 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/core.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/core.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,33 @@ +# +# 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 +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/debug.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/debug.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,41 @@ +# +# 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. +# +# + +# 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 + +VERSION = debug +SYSDEFS += -DASSERT -DDEBUG +PICFLAGS = DEFAULT diff -r 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/defs.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/defs.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,231 @@ +# +# Copyright (c) 2006, 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. +# +# + +# The common definitions for hotspot AIX 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 + +# PPC64 +ifeq ($(ARCH), ppc64) + #ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = aix-ppc64 + VM_PLATFORM = aix_ppc64 + 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 + +# 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 + +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 +EXPORT_MINIMAL_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/minimal + +ifeq ($(findstring true, $(JVM_VARIANT_SERVER) $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK) $(JVM_VARIANT_CORE)), 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/dtrace.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/dtrace.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,27 @@ +# +# Copyright (c) 2005, 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 +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/fastdebug.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/fastdebug.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,73 @@ +# +# 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. +# +# + +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/jsig.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/jsig.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,87 @@ +# +# Copyright (c) 2005, 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. +# +# + +# Rules to build signal interposition library, used by vm.make + +# libjsig.so: signal interposition library +JSIG = jsig +LIBJSIG = lib$(JSIG).so + +LIBJSIG_DEBUGINFO = lib$(JSIG).debuginfo +LIBJSIG_DIZ = lib$(JSIG).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 + +#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 +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# $(ZIPEXE) -q -y $(LIBJSIG_DIZ) $(LIBJSIG_DEBUGINFO) +# $(RM) $(LIBJSIG_DEBUGINFO) +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/jvmti.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/jvmti.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,118 @@ +# +# Copyright (c) 2003, 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 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/mapfile-vers-debug --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/mapfile-vers-debug Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,275 @@ +# +# Copyright (c) 2002, 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 +# 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_GetClassTypeAnnotations; + JVM_GetComponentType; + JVM_GetDeclaredClasses; + JVM_GetDeclaringClass; + JVM_GetEnclosingMethodInfo; + JVM_GetFieldAnnotations; + JVM_GetFieldIxModifiers; + JVM_GetFieldTypeAnnotations; + 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_GetMethodParameters; + JVM_GetMethodTypeAnnotations; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetTemporaryDirectory; + 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_IsVMGeneratedMethodIx; + 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_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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/mapfile-vers-jsig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/mapfile-vers-jsig Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2005, 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 +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/mapfile-vers-product --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/mapfile-vers-product Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,268 @@ +# +# Copyright (c) 2002, 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 +# 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_GetClassTypeAnnotations; + 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_GetMethodParameters; + JVM_GetPrimitiveArrayElement; + JVM_GetProtectionDomain; + JVM_GetSockName; + JVM_GetSockOpt; + JVM_GetStackAccessControlContext; + JVM_GetStackTraceDepth; + JVM_GetStackTraceElement; + JVM_GetSystemPackage; + JVM_GetSystemPackages; + JVM_GetTemporaryDirectory; + 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_IsVMGeneratedMethodIx; + 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_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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/ppc64.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/ppc64.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,94 @@ +# +# Copyright (c) 2004, 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. +# +# + +# Produce 64 bits object files. +CFLAGS += -q64 + +# Balanced tuning for recent versions of the POWER architecture (if supported by xlc). +QTUNE=$(if $(CXX_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 + +# Suppress those "implicit private" warnings xlc gives. +# - The omitted keyword "private" is assumed for base class "...". +CFLAGS += -qsuppress=1540-0198 + +# Suppress 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 + +# Suppress +# - 540-1088 (W) The exception specification is being ignored. +# caused by throw() in declaration of new() in nmethod.hpp. +CFLAGS += -qsuppress=1540-1088 + +# 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 10.01 parameters for ipa compile. +QIPA_COMPILE=$(if $(CXX_IS_V10),-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 $(CXX_IS_V10),-qxflag=ASMMIDCOALFIX -qxflag=asmfastsync) +QV10_OPT_AGGRESSIVE=$(if $(CXX_IS_V10),-qhot=level=1 -qignerrno -qinline) +QV10_OPT_CONSERVATIVE=$(if $(CXX_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 + +# Also build launcher as 64 bit executable. +LAUNCHERFLAGS += -q64 diff -r 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/product.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/product.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,58 @@ +# +# 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. +# +# + +# 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= + +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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/rules.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/rules.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,203 @@ +# +# Copyright (c) 2003, 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 +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/sa.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/sa.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,116 @@ +# +# Copyright (c) 2003, 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 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/saproc.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/saproc.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,117 @@ +# +# Copyright (c) 2005, 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 $(GAMMADIR)/make/defs.make + +# Rules to build serviceability agent library, used by vm.make + +# libsaproc.so: serviceability agent + +SAPROC = saproc +LIBSAPROC = lib$(SAPROC).so + +LIBSAPROC_DEBUGINFO = lib$(SAPROC).debuginfo +LIBSAPROC_DIZ = lib$(SAPROC).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 +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 + ifeq ($(ZIP_DEBUGINFO_FILES),1) + $(ZIPEXE) -q -y $(LIBSAPROC_DIZ) $(LIBSAPROC_DEBUGINFO) + $(RM) $(LIBSAPROC_DEBUGINFO) + 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/top.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/top.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,144 @@ +# +# 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 +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/trace.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/trace.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,120 @@ +# +# Copyright (c) 2003, 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 +# 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/linux/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 + +ifneq ($(INCLUDE_TRACE), false) +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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/vm.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/vm.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,377 @@ +# +# 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. +# +# + +# 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. +CXXFLAGS/vm_version.o += ${JRE_VERSION} + +CXXFLAGS/BYFILE = $(CXXFLAGS/$@) + +# File specific flags +CXXFLAGS += $(CXXFLAGS/BYFILE) + + +# 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 + +CFLAGS += -DALLOW_OPERATOR_NEW_USAGE + +LIBJVM_DEBUGINFO = lib$(JVM).debuginfo +LIBJVM_DIZ = lib$(JVM).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 + +CORE_PATHS=$(foreach path,$(SOURCE_PATHS),$(call altsrc,$(path)) $(path)) +CORE_PATHS+=$(GENERATED)/jvmtifiles $(GENERATED)/tracefiles + +ifneq ($(INCLUDE_TRACE), false) +CORE_PATHS+=$(shell if [ -d $(HS_ALT_SRC)/share/vm/jfr ]; then \ + find $(HS_ALT_SRC)/share/vm/jfr -type d; \ + fi) +endif + +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 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 $@ $(sort $(LIBJVM.o)) $(LIBS_VM); \ + $(LINK_LIB.CXX/POST_HOOK) \ + rm -f $@.1; ln -s $@ $@.1; \ + } +# No security contexts on AIX +# 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 ($(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 +# ifeq ($(ZIP_DEBUGINFO_FILES),1) +# $(ZIPEXE) -q -y $(LIBJVM_DIZ) $(LIBJVM_DEBUGINFO) +# $(RM) $(LIBJVM_DEBUGINFO) +# 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 + +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/makefiles/xlc.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/makefiles/xlc.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,159 @@ +# +# Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2013 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 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 + +# 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 + +# Build shared library +SHARED_FLAG = -q64 -b64 -bexpall -G -bnoentry -qmkshrobj -brtl -bnolibpath + +#------------------------------------------------------------------------ +# Debug flags + +# Always compile with '-g' to get symbols in the stacktraces in the hs_err file +DEBUG_CFLAGS += -g +FASTDEBUG_CFLAGS += -g +OPT_CFLAGS += -g + +# 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 45d7b2c7029d -r 52b4284cb496 make/aix/platform_ppc64 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/aix/platform_ppc64 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 make/bsd/makefiles/mapfile-vers-debug diff -r 45d7b2c7029d -r 52b4284cb496 make/bsd/makefiles/mapfile-vers-product diff -r 45d7b2c7029d -r 52b4284cb496 make/bsd/makefiles/universal.gmk --- a/make/bsd/makefiles/universal.gmk Thu Oct 16 10:21:29 2014 +0200 +++ b/make/bsd/makefiles/universal.gmk Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 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 @@ -74,19 +74,21 @@ # Replace arch specific binaries with universal binaries +# Do not touch jre/lib/{client,server}/libjsig.$(LIBRARY_SUFFIX) +# That symbolic link belongs to the 'jdk' build. export_universal: $(RM) -r $(EXPORT_PATH)/jre/lib/{i386,amd64} $(RM) -r $(JDK_IMAGE_DIR)/jre/lib/{i386,amd64} - $(RM) $(JDK_IMAGE_DIR)/jre/lib/{client,server}/libjsig.$(LIBRARY_SUFFIX) ($(CD) $(EXPORT_PATH) && \ $(TAR) -cf - *) | \ ($(CD) $(JDK_IMAGE_DIR) && $(TAR) -xpf -) # Overlay universal binaries +# Do not touch jre/lib/{client,server}/libjsig.$(LIBRARY_SUFFIX) +# That symbolic link belongs to the 'jdk' build. copy_universal: $(RM) -r $(JDK_IMAGE_DIR)$(COPY_SUBDIR)/jre/lib/{i386,amd64} - $(RM) $(JDK_IMAGE_DIR)$(COPY_SUBDIR)/jre/lib/{client,server}/libjsig.$(LIBRARY_SUFFIX) ($(CD) $(EXPORT_PATH)$(COPY_SUBDIR) && \ $(TAR) -cf - *) | \ ($(CD) $(JDK_IMAGE_DIR)$(COPY_SUBDIR) && $(TAR) -xpf -) diff -r 45d7b2c7029d -r 52b4284cb496 make/defs.make --- a/make/defs.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/defs.make Wed Oct 15 16:02:50 2014 +0200 @@ -176,11 +176,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 @@ -269,7 +273,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 @@ -295,6 +299,11 @@ BUILDARCH = sparcv9 endif endif + ifeq ($(BUILDARCH), ppc) + ifdef LP64 + BUILDARCH = ppc64 + endif + endif # LIBARCH is 1:1 mapping from BUILDARCH LIBARCH = $(LIBARCH/$(BUILDARCH)) @@ -303,12 +312,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 diff -r 45d7b2c7029d -r 52b4284cb496 make/excludeSrc.make --- a/make/excludeSrc.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/excludeSrc.make Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 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 @@ -86,10 +86,11 @@ concurrentMark.cpp concurrentMarkThread.cpp dirtyCardQueue.cpp g1AllocRegion.cpp \ g1BlockOffsetTable.cpp g1CardCounts.cpp g1CollectedHeap.cpp g1CollectorPolicy.cpp \ g1ErgoVerbose.cpp g1GCPhaseTimes.cpp g1HRPrinter.cpp g1HotCardCache.cpp g1Log.cpp \ - g1MMUTracker.cpp g1MarkSweep.cpp g1MemoryPool.cpp g1MonitoringSupport.cpp \ - g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1_globals.cpp heapRegion.cpp \ + g1MMUTracker.cpp g1MarkSweep.cpp g1MemoryPool.cpp g1MonitoringSupport.cpp g1OopClosures.cpp \ + g1RemSet.cpp g1RemSetSummary.cpp g1SATBCardTableModRefBS.cpp g1StringDedup.cpp g1StringDedupStat.cpp \ + g1StringDedupTable.cpp g1StringDedupThread.cpp g1StringDedupQueue.cpp g1_globals.cpp heapRegion.cpp \ g1BiasedArray.cpp heapRegionRemSet.cpp heapRegionSeq.cpp heapRegionSet.cpp heapRegionSets.cpp \ - ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp \ + ptrQueue.cpp satbQueue.cpp sparsePRT.cpp survRateGroup.cpp vm_operations_g1.cpp g1CodeCacheRemSet.cpp \ adjoiningGenerations.cpp adjoiningVirtualSpaces.cpp asPSOldGen.cpp asPSYoungGen.cpp \ cardTableExtension.cpp gcTaskManager.cpp gcTaskThread.cpp objectStartArray.cpp \ parallelScavengeHeap.cpp parMarkBitMap.cpp pcTasks.cpp psAdaptiveSizePolicy.cpp \ diff -r 45d7b2c7029d -r 52b4284cb496 make/hotspot_version --- a/make/hotspot_version Thu Oct 16 10:21:29 2014 +0200 +++ b/make/hotspot_version Wed Oct 15 16:02:50 2014 +0200 @@ -34,8 +34,8 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2014 HS_MAJOR_VER=25 -HS_MINOR_VER=11 -HS_BUILD_NUMBER=03 +HS_MINOR_VER=20 +HS_BUILD_NUMBER=23 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff -r 45d7b2c7029d -r 52b4284cb496 make/jprt.properties --- a/make/jprt.properties Thu Oct 16 10:21:29 2014 +0200 +++ b/make/jprt.properties Wed Oct 15 16:02:50 2014 +0200 @@ -33,7 +33,7 @@ # This tells jprt what default release we want to build -jprt.hotspot.default.release=jdk8 +jprt.hotspot.default.release=jdk8u20 jprt.tools.default.release=${jprt.submit.option.release?${jprt.submit.option.release}:${jprt.hotspot.default.release}} @@ -47,70 +47,65 @@ # sparc etc. # Define the Solaris platforms we want for the various releases -jprt.my.solaris.sparcv9.jdk8=solaris_sparcv9_5.10 +jprt.my.solaris.sparcv9.jdk8u20=solaris_sparcv9_5.10 jprt.my.solaris.sparcv9.jdk7=solaris_sparcv9_5.10 jprt.my.solaris.sparcv9.jdk7u8=${jprt.my.solaris.sparcv9.jdk7} jprt.my.solaris.sparcv9=${jprt.my.solaris.sparcv9.${jprt.tools.default.release}} -jprt.my.solaris.x64.jdk8=solaris_x64_5.10 +jprt.my.solaris.x64.jdk8u20=solaris_x64_5.10 jprt.my.solaris.x64.jdk7=solaris_x64_5.10 jprt.my.solaris.x64.jdk7u8=${jprt.my.solaris.x64.jdk7} jprt.my.solaris.x64=${jprt.my.solaris.x64.${jprt.tools.default.release}} -jprt.my.linux.i586.jdk8=linux_i586_2.6 +jprt.my.linux.i586.jdk8u20=linux_i586_2.6 jprt.my.linux.i586.jdk7=linux_i586_2.6 jprt.my.linux.i586.jdk7u8=${jprt.my.linux.i586.jdk7} jprt.my.linux.i586=${jprt.my.linux.i586.${jprt.tools.default.release}} -jprt.my.linux.x64.jdk8=linux_x64_2.6 +jprt.my.linux.x64.jdk8u20=linux_x64_2.6 jprt.my.linux.x64.jdk7=linux_x64_2.6 jprt.my.linux.x64.jdk7u8=${jprt.my.linux.x64.jdk7} jprt.my.linux.x64=${jprt.my.linux.x64.${jprt.tools.default.release}} -jprt.my.linux.ppc.jdk8=linux_ppc_2.6 +jprt.my.linux.ppc.jdk8u20=linux_ppc_2.6 jprt.my.linux.ppc.jdk7=linux_ppc_2.6 jprt.my.linux.ppc.jdk7u8=${jprt.my.linux.ppc.jdk7} jprt.my.linux.ppc=${jprt.my.linux.ppc.${jprt.tools.default.release}} -jprt.my.linux.ppcv2.jdk8=linux_ppcv2_2.6 +jprt.my.linux.ppcv2.jdk8u20=linux_ppcv2_2.6 jprt.my.linux.ppcv2.jdk7=linux_ppcv2_2.6 jprt.my.linux.ppcv2.jdk7u8=${jprt.my.linux.ppcv2.jdk7} jprt.my.linux.ppcv2=${jprt.my.linux.ppcv2.${jprt.tools.default.release}} -jprt.my.linux.ppcsflt.jdk8=linux_ppcsflt_2.6 -jprt.my.linux.ppcsflt.jdk7=linux_ppcsflt_2.6 -jprt.my.linux.ppcsflt.jdk7u8=${jprt.my.linux.ppcsflt.jdk7} -jprt.my.linux.ppcsflt=${jprt.my.linux.ppcsflt.${jprt.tools.default.release}} - -jprt.my.linux.armvfpsflt.jdk8=linux_armvfpsflt_2.6 +jprt.my.linux.armvfpsflt.jdk8u20=linux_armvfpsflt_2.6 jprt.my.linux.armvfpsflt=${jprt.my.linux.armvfpsflt.${jprt.tools.default.release}} -jprt.my.linux.armvfphflt.jdk8=linux_armvfphflt_2.6 +jprt.my.linux.armvfphflt.jdk8u20=linux_armvfphflt_2.6 jprt.my.linux.armvfphflt=${jprt.my.linux.armvfphflt.${jprt.tools.default.release}} # The ARM GP vfp-sflt build is not currently supported -#jprt.my.linux.armvs.jdk8=linux_armvs_2.6 +#jprt.my.linux.armvs.jdk8u20=linux_armvs_2.6 #jprt.my.linux.armvs=${jprt.my.linux.armvs.${jprt.tools.default.release}} -jprt.my.linux.armvh.jdk8=linux_armvh_2.6 +jprt.my.linux.armvh.jdk8u20=linux_armvh_2.6 jprt.my.linux.armvh=${jprt.my.linux.armvh.${jprt.tools.default.release}} -jprt.my.linux.armsflt.jdk8=linux_armsflt_2.6 +jprt.my.linux.armsflt.jdk8u20=linux_armsflt_2.6 jprt.my.linux.armsflt.jdk7=linux_armsflt_2.6 jprt.my.linux.armsflt.jdk7u8=${jprt.my.linux.armsflt.jdk7} jprt.my.linux.armsflt=${jprt.my.linux.armsflt.${jprt.tools.default.release}} -jprt.my.macosx.x64.jdk8=macosx_x64_10.7 +jprt.my.macosx.x64.jdk8u20=macosx_x64_10.7 jprt.my.macosx.x64.jdk7=macosx_x64_10.7 jprt.my.macosx.x64.jdk7u8=${jprt.my.macosx.x64.jdk7} jprt.my.macosx.x64=${jprt.my.macosx.x64.${jprt.tools.default.release}} -jprt.my.windows.i586.jdk8=windows_i586_6.1 +jprt.my.windows.i586.jdk8u20=windows_i586_6.1 jprt.my.windows.i586.jdk7=windows_i586_6.1 jprt.my.windows.i586.jdk7u8=${jprt.my.windows.i586.jdk7} jprt.my.windows.i586=${jprt.my.windows.i586.${jprt.tools.default.release}} -jprt.my.windows.x64.jdk8=windows_x64_6.1 +jprt.my.windows.x64.jdk8u20=windows_x64_6.1 jprt.my.windows.x64.jdk7=windows_x64_6.1 jprt.my.windows.x64.jdk7u8=${jprt.my.windows.x64.jdk7} jprt.my.windows.x64=${jprt.my.windows.x64.${jprt.tools.default.release}} @@ -135,7 +130,6 @@ ${jprt.my.linux.i586}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.ppc}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.ppcv2}-{productEmb|fastdebugEmb}, \ - ${jprt.my.linux.ppcsflt}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.armvfpsflt}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.armvfphflt}-{productEmb|fastdebugEmb}, \ ${jprt.my.linux.armsflt}-{productEmb|fastdebugEmb} @@ -143,7 +137,7 @@ jprt.build.targets.all=${jprt.build.targets.standard}, \ ${jprt.build.targets.embedded}, ${jprt.build.targets.open} -jprt.build.targets.jdk8=${jprt.build.targets.all} +jprt.build.targets.jdk8u20=${jprt.build.targets.all} jprt.build.targets.jdk7=${jprt.build.targets.all} jprt.build.targets.jdk7u8=${jprt.build.targets.all} jprt.build.targets=${jprt.build.targets.${jprt.tools.default.release}} @@ -349,7 +343,7 @@ ${jprt.my.windows.i586.test.targets}, \ ${jprt.my.windows.x64.test.targets} -jprt.test.targets.jdk8=${jprt.test.targets.standard} +jprt.test.targets.jdk8u20=${jprt.test.targets.standard} jprt.test.targets.jdk7=${jprt.test.targets.standard} jprt.test.targets.jdk7u8=${jprt.test.targets.jdk7} jprt.test.targets=${jprt.test.targets.${jprt.tools.default.release}} @@ -399,7 +393,7 @@ jprt.make.rule.test.targets.embedded = \ ${jprt.make.rule.test.targets.standard.client} -jprt.make.rule.test.targets.jdk8=${jprt.make.rule.test.targets.standard} +jprt.make.rule.test.targets.jdk8u20=${jprt.make.rule.test.targets.standard} jprt.make.rule.test.targets.jdk7=${jprt.make.rule.test.targets.standard} jprt.make.rule.test.targets.jdk7u8=${jprt.make.rule.test.targets.jdk7} jprt.make.rule.test.targets=${jprt.make.rule.test.targets.${jprt.tools.default.release}} diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/Makefile --- a/make/linux/Makefile Thu Oct 16 10:21:29 2014 +0200 +++ b/make/linux/Makefile Wed Oct 15 16:02:50 2014 +0200 @@ -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))","") diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/buildtree.make --- a/make/linux/makefiles/buildtree.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/linux/makefiles/buildtree.make Wed Oct 15 16:02:50 2014 +0200 @@ -193,6 +193,7 @@ DATA_MODE/sparc = 32 DATA_MODE/sparcv9 = 64 DATA_MODE/amd64 = 64 +DATA_MODE/ppc64 = 64 DATA_MODE = $(DATA_MODE/$(BUILDARCH)) diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/defs.make --- a/make/linux/makefiles/defs.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/linux/makefiles/defs.make Wed Oct 15 16:02:50 2014 +0200 @@ -33,6 +33,11 @@ # ARCH can be set explicitly in spec.gmk ifndef ARCH ARCH := $(shell uname -m) + # Fold little endian PowerPC64 into big-endian (if ARCH is set in + # hotspot-spec.gmk, this will be done by the configure script). + ifeq ($(ARCH),ppc64le) + ARCH := ppc64 + endif endif PATH_SEP ?= : @@ -120,6 +125,15 @@ HS_ARCH = ppc endif +# PPC64 +ifeq ($(ARCH), ppc64) + ARCH_DATA_MODEL = 64 + MAKE_ARGS += LP64=1 + PLATFORM = linux-ppc64 + VM_PLATFORM = linux_ppc64 + HS_ARCH = ppc +endif + # On 32 bit linux we build server and client, on 64 bit just server. ifeq ($(JVM_VARIANTS),) ifeq ($(ARCH_DATA_MODEL), 32) @@ -255,7 +269,7 @@ EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client EXPORT_MINIMAL_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/minimal -ifeq ($(findstring true, $(JVM_VARIANT_SERVER) $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK)), true) +ifeq ($(findstring true, $(JVM_VARIANT_SERVER) $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK) $(JVM_VARIANT_CORE)), true) EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.$(LIBRARY_SUFFIX) ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/gcc.make --- a/make/linux/makefiles/gcc.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/linux/makefiles/gcc.make Wed Oct 15 16:02:50 2014 +0200 @@ -181,6 +181,7 @@ ifndef E500V2 ARCHFLAG/ppc = -mcpu=powerpc endif +ARCHFLAG/ppc64 = -m64 CFLAGS += $(ARCHFLAG) AOUT_FLAGS += $(ARCHFLAG) @@ -346,6 +347,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)),) ifeq ($(USE_CLANG), true) @@ -361,6 +363,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)),) ifeq ($(USE_CLANG), true) @@ -375,6 +378,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)),) ifeq ($(USE_CLANG), true) diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/mapfile-vers-debug diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/mapfile-vers-product diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/ppc64.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/makefiles/ppc64.make Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,51 @@ +# +# Copyright (c) 2004, 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 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) + # 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 +else + # Little endian machine uses ELFv2 ABI. + CFLAGS += -DVM_LITTLE_ENDIAN -DABI_ELFv2 + + # 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 45d7b2c7029d -r 52b4284cb496 make/linux/makefiles/zeroshark.make --- a/make/linux/makefiles/zeroshark.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/linux/makefiles/zeroshark.make Wed Oct 15 16:02:50 2014 +0200 @@ -25,6 +25,9 @@ # Setup common to Zero (non-Shark) and Shark versions of VM +# override this from the main file because some version of llvm do not like -Wundef +WARNING_FLAGS = -Wpointer-arith -Wsign-compare -Wunused-function -Wunused-value + # The copied fdlibm routines in sharedRuntimeTrig.o must not be optimized OPT_CFLAGS/sharedRuntimeTrig.o = $(OPT_CFLAGS/NOOPT) # The copied fdlibm routines in sharedRuntimeTrans.o must not be optimized diff -r 45d7b2c7029d -r 52b4284cb496 make/linux/platform_ppc --- a/make/linux/platform_ppc Thu Oct 16 10:21:29 2014 +0200 +++ b/make/linux/platform_ppc Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 make/linux/platform_ppc64 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/platform_ppc64 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 make/solaris/makefiles/mapfile-vers diff -r 45d7b2c7029d -r 52b4284cb496 make/windows/makefiles/defs.make --- a/make/windows/makefiles/defs.make Thu Oct 16 10:21:29 2014 +0200 +++ b/make/windows/makefiles/defs.make Wed Oct 15 16:02:50 2014 +0200 @@ -260,7 +260,6 @@ EXPORT_LIST += $(EXPORT_SERVER_DIR)/jvm.map endif endif - EXPORT_LIST += $(EXPORT_LIB_DIR)/jvm.lib endif ifeq ($(JVM_VARIANT_CLIENT),true) EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt @@ -275,6 +274,8 @@ endif endif +EXPORT_LIST += $(EXPORT_LIB_DIR)/jvm.lib + ifeq ($(BUILD_WIN_SA), 1) EXPORT_LIST += $(EXPORT_JRE_BIN_DIR)/sawindbg.$(LIBRARY_SUFFIX) ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 "asm/assembler.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" +#if INCLUDE_ALL_GCS +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#endif // INCLUDE_ALL_GCS + +#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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,1976 @@ +/* + * 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 + +#include "asm/register.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), + + 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), + + 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, + }; + + // Elemental Memory Barriers (>=Power 8) + enum Elemental_Membar_mask_bits { + StoreStore = 1 << 0, + StoreLoad = 1 << 1, + LoadStore = 1 << 2, + LoadLoad = 1 << 3 + }; + + // 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 + + // 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_rta_field(int x) { return inv_opp_u_field(x, 15, 11); } + 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 e1215( int x) { return opp_u_field(x, 15, 12); } + 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_int32(int); // shadows AbstractAssembler::emit_int32 + 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); // d = b - a "Sub_from", as in ppc spec. + inline void sub( Register d, Register a, Register b); // d = a - b Swap operands of subf for readability. + 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); + + // 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 + 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 elemental_membar(int e); // Elemental Memory Barriers (>=Power 8) + + // 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 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 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 +}; + + +#endif // CPU_PPC_VM_ASSEMBLER_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,823 @@ +/* + * 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" + +inline void Assembler::emit_int32(int x) { + AbstractAssembler::emit_int32(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(); + emit_address(addr); + 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::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::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; + Register 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::elemental_membar(int e) { assert(0 < e && e < 16, "invalid encoding"); emit_int32( SYNC_OPCODE | e1215(e)); } + +// 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::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::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); +} + + +#endif // CPU_PPC_VM_ASSEMBLER_PPC_INLINE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,106 @@ +/* + * 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) (cast_to_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 = \ + ((VMJavaVal64*)(addr))->d) + + +#endif // CPU_PPC_VM_BYTECODEINTERPRETER_PPC_PP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,98 @@ +/* + * 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_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, MinJumpTableSize, 10); +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); +define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); + +define_pd_global(bool, TrapBasedRangeChecks, true); + +// Heap related flags +define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); + +// Ergonomics related flags +define_pd_global(bool, NeverActAsServerClassMachine, false); + +#endif // CPU_PPC_VM_C2_GLOBALS_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/cpu/ppc/vm/compiledIC_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/compiledIC_ppc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,261 @@ +/* + * 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 + * 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/macroAssembler.inline.hpp" +#include "code/compiledIC.hpp" +#include "code/icBuffer.hpp" +#include "code/nmethod.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/safepoint.hpp" +#ifdef COMPILER2 +#include "opto/matcher.hpp" +#endif + +// Release the CompiledICHolder* associated with this call site is there is one. +void CompiledIC::cleanup_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + if (is_icholder_entry(call->destination())) { + NativeMovConstReg* value = nativeMovConstReg_at(call_site->cached_value()); + InlineCacheBuffer::queue_for_release((CompiledICHolder*)value->data()); + } +} + +bool CompiledIC::is_icholder_call_site(virtual_call_Relocation* call_site) { + // This call site might have become stale so inspect it carefully. + NativeCall* call = nativeCall_at(call_site->addr()); + return is_icholder_entry(call->destination()); +} + +//----------------------------------------------------------------------------- +// High-level access to an inline cache. Guaranteed to be MT-safe. + +CompiledIC::CompiledIC(nmethod* nm, NativeCall* call) + : _ic_call(call) +{ + address ic_call = call->instruction_address(); + + assert(ic_call != NULL, "ic_call address must be set"); + assert(nm != NULL, "must pass nmethod"); + assert(nm->contains(ic_call), "must be in nmethod"); + + // Search for the ic_call at the given address. + RelocIterator iter(nm, ic_call, ic_call+1); + bool ret = iter.next(); + assert(ret == true, "relocInfo must exist at this address"); + assert(iter.addr() == ic_call, "must find ic_call"); + if (iter.type() == relocInfo::virtual_call_type) { + virtual_call_Relocation* r = iter.virtual_call_reloc(); + _is_optimized = false; + _value = nativeMovConstReg_at(r->cached_value()); + } else { + assert(iter.type() == relocInfo::opt_virtual_call_type, "must be a virtual call"); + _is_optimized = true; + _value = NULL; + } +} + +// ---------------------------------------------------------------------------- + +// A PPC CompiledStaticCall looks like this: +// +// >>>> consts +// +// [call target1] +// [IC cache] +// [call target2] +// +// <<<< consts +// >>>> insts +// +// bl offset16 -+ -+ ??? // How many bits available? +// | | +// <<<< insts | | +// >>>> stubs | | +// | |- trampoline_stub_Reloc +// trampoline stub: | <-+ +// r2 = toc | +// r2 = [r2 + offset] | // 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 + offset] // Load call target2 from const section +// mtctr r1 +// bctr +// +// <<<< stubs +// +// The call instruction in the code either +// - branches directly to a compiled method if offset encodable in instruction +// - branches to the trampoline stub if offset to compiled method not encodable +// - branches to the compiled_to_interp stub if target 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. + +const int IC_pos_in_java_to_interp_stub = 8; +#define __ _masm. +void CompiledStaticCall::emit_to_interp_stub(CodeBuffer &cbuf) { +#ifdef COMPILER2 + // Get the mark within main instrs section which is set to the address of the call. + address call_addr = cbuf.insts_mark(); + + // Note that the code buffer's insts_mark is always relative to insts. + // That's why we must use the macroassembler to generate a stub. + MacroAssembler _masm(&cbuf); + + // Start the stub. + address stub = __ start_a_stub(CompiledStaticCall::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(call_addr)); + 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 ic = __ allocate_metadata_address((Metadata *)NULL); + __ load_const_from_method_toc(as_Register(Matcher::inline_cache_reg_encode()), ic, 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((__ offset() - stub_start_offset) <= CompiledStaticCall::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(); +#else + ShouldNotReachHere(); +#endif +} +#undef __ + +// 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. +int CompiledStaticCall::to_interp_stub_size() { + return 12 * BytesPerInstWord; +} + +// Relocation entries for call stub, compiled java to interpreter. +// Used for optimization in Compile::Shorten_branches. +int CompiledStaticCall::reloc_to_interp_stub() { + return 5; +} + +void CompiledStaticCall::set_to_interpreted(methodHandle callee, address entry) { + address stub = find_stub(); + guarantee(stub != NULL, "stub not found"); + + if (TraceICs) { + ResourceMark rm; + tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", + instruction_address(), + callee->name_and_sig_as_C_string()); + } + + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + IC_pos_in_java_to_interp_stub); + 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"); + assert(jump->jump_destination() == (address)-1 || jump->jump_destination() == entry, + "b) MT-unsafe modification of inline cache"); + + // Update stub. + method_holder->set_data((intptr_t)callee()); + jump->set_jump_destination(entry); + + // Update jump to call. + set_destination_mt_safe(stub); +} + +void CompiledStaticCall::set_stub_to_clean(static_stub_Relocation* static_stub) { + assert (CompiledIC_lock->is_locked() || SafepointSynchronize::is_at_safepoint(), "mt unsafe call"); + // Reset stub. + address stub = static_stub->addr(); + assert(stub != NULL, "stub not found"); + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + IC_pos_in_java_to_interp_stub); + NativeJump* jump = nativeJump_at(method_holder->next_instruction_address()); + method_holder->set_data(0); + jump->set_jump_destination((address)-1); +} + +//----------------------------------------------------------------------------- +// Non-product mode code +#ifndef PRODUCT + +void CompiledStaticCall::verify() { + // Verify call. + NativeCall::verify(); + if (os::is_MP()) { + verify_alignment(); + } + + // Verify stub. + address stub = find_stub(); + assert(stub != NULL, "no stub found for static call"); + // Creation also verifies the object. + NativeMovConstReg* method_holder = nativeMovConstReg_at(stub + IC_pos_in_java_to_interp_stub); + 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 diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,3074 @@ + +/* + * Copyright (c) 1997, 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "asm/macroAssembler.inline.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/methodData.hpp" +#include "oops/method.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, Bytecodes::Code code) { + 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 Method +// 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 == sizeof(AccessFlags), "unexpected field size"); + __ lwa(access_flags, method_(access_flags)); + + // parameter_count = method->constMethod->size_of_parameters(); + // TODO: PPC port: assert(2 == ConstMethod::sz_size_of_parameters(), "unexpected field size"); + __ ld(max_stack, in_bytes(Method::const_offset()), R19_method); // Max_stack holds constMethod for a while. + __ lhz(parameter_count, in_bytes(ConstMethod::size_of_parameters_offset()), max_stack); + + // local_count = method->constMethod()->max_locals(); + // TODO: PPC port: assert(2 == ConstMethod::sz_max_locals(), "unexpected field size"); + __ lhz(local_count, in_bytes(ConstMethod::size_of_locals_offset()), max_stack); + + // max_stack = method->constMethod()->max_stack(); + // TODO: PPC port: assert(2 == ConstMethod::sz_max_stack(), "unexpected field size"); + __ lhz(max_stack, in_bytes(ConstMethod::max_stack_offset()), max_stack); + + if (EnableInvokeDynamic) { + // Take into account 'extra_stack_entries' needed by method handles (see method.hpp). + __ addi(max_stack, max_stack, Method::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(ConstMethod::codes_offset())); + // } + __ bind(L); + } + + __ ld(constants, in_bytes(Method::const_offset()), R19_method); + __ ld(constants, in_bytes(ConstMethod::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, ConstantPool::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*/, ConstantPool::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. +// +// R19_method must contain Method* of method to profile. +void CppInterpreterGenerator::generate_counter_incr(Label& overflow) { + Label done; + const Register Rcounters = R12_scratch2; + const Register iv_be_count = R11_scratch1; + const Register invocation_limit = R12_scratch2; + const Register invocation_limit_addr = invocation_limit; + + // Load and ev. allocate MethodCounters object. + __ get_method_counters(R19_method, Rcounters, done); + + // Update standard invocation counters. + __ increment_invocation_counter(Rcounters, iv_be_count, R0); + + // Compare against limit. + BLOCK_COMMENT("Compare counter 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, iv_be_count, invocation_limit); + __ bge(CCR0, overflow); + __ bind(done); +} + +// +// 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 Method + // 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 == sizeof(AccessFlags), "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(Method::const_offset()), R19_method); + __ ld(R11_scratch1/*constants*/, in_bytes(ConstMethod::constants_offset()), R11_scratch1); + // pool_holder = method->constants()->pool_holder(); + __ ld(R11_scratch1/*pool_holder*/, ConstantPool::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); + + assert(StubRoutines::throw_StackOverflowError_entry() != NULL, "generated in wrong order"); + // 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 Method (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 Method + // 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; + + // Method 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 Method + // 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 Method, 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. + + __ ld(parameter_count, in_bytes(Method::const_offset()), R19_method); + __ lhz(parameter_count, in_bytes(ConstMethod::size_of_parameters_offset()), parameter_count); + __ sldi(parameter_count, parameter_count, Interpreter::logStackElementSize); + __ add(R17_tos, R17_tos, parameter_count); + + // Result stub address array index + // TODO: PPC port: assert(4 == sizeof(AccessFlags), "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 Method + // 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. + + assert(StubRoutines::throw_StackOverflowError_entry() != NULL, "generated in wrong order"); + __ 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 Method + // 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 Method + // 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 Method). + __ 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 Method + // 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 Method + // 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, 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(Method* 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, + Method* 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 Method 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 Methods, + // 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; +} + +// Computes monitor_size and top_frame_size in bytes. +static void frame_size_helper(int max_stack, + int monitors, + int& monitor_size, + int& top_frame_size) { + monitor_size = frame::interpreter_frame_monitor_size_in_bytes() * monitors; + top_frame_size = round_to(frame::interpreter_frame_cinterpreterstate_size_in_bytes() + + monitor_size + + max_stack * Interpreter::stackElementSize + + 2 * Interpreter::stackElementSize, + frame::alignment_in_bytes) + + frame::top_ijava_frame_abi_size; +} + +// Returns number of stackElementWords needed for the interpreter frame with the +// given sections. +int AbstractInterpreter::size_activation(int max_stack, + int temps, + int extra_args, + int monitors, + int callee_params, + int callee_locals, + bool is_top_frame) { + int monitor_size = 0; + int top_frame_size = 0; + frame_size_helper(max_stack, monitors, monitor_size, top_frame_size); + + int frame_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::stackElementSize + + 2 * Interpreter::stackElementSize, + frame::alignment_in_bytes) + + frame::parent_ijava_frame_abi_size; + assert(extra_args == 0, "non-zero for top_frame only"); + } + + return frame_size / Interpreter::stackElementSize; +} + +void AbstractInterpreter::layout_activation(Method* 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 = 0; + int top_frame_size = 0; + frame_size_helper(method->max_stack(), monitors, monitor_size, top_frame_size); + + 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()); +} + +#endif // CC_INTERP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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/method.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. + Method* 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"); + Method* 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) ? (oop)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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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(ConstantPoolCache* 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: + + ConstantPoolCache** 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 Method** frame::interpreter_frame_method_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_method; +} + +// Constant pool cache + +inline ConstantPoolCache** frame::interpreter_frame_cpoolcache_addr() const { + interpreterState istate = get_interpreterState(); + return &istate->_constants; // should really use accessor +} + +inline ConstantPoolCache** 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(); +} + +// SAPJVM ASc 2012-11-21. Return register stack slot addr at which currently interpreted method is found +inline Method** frame::interpreter_frame_method_addr() const { + return (Method**) &(get_ijava_state()->method); +} +inline ConstantPoolCache** frame::interpreter_frame_cpoolcache_addr() const { + return (ConstantPoolCache**) &(get_ijava_state()->cpoolCache); +} +inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { + return (ConstantPoolCache**) &(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(ConstantPoolCache* 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,43 @@ +/* + * 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; + +// The PPC CPUs are NOT multiple-copy-atomic. +#define CPU_NOT_MULTIPLE_COPY_ATOMIC + +#endif // CPU_PPC_VM_GLOBALDEFINITIONS_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,126 @@ +/* + * 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, TrapBasedNullChecks, true); +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(uintx, CMSYoungGenPerWorker, 16*M); // Default max size of CMS young gen, per GC worker thread. + +define_pd_global(uintx, TypeProfileLevel, 0); + +// 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, 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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, void* cached_value, address entry_point) { + ResourceMark rm; + CodeBuffer code(code_begin, ic_stub_code_size()); + MacroAssembler masm(&code); + // Note: even though the code contains an embedded metadata, we do not need reloc info + // because + // (1) the metadata 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_value, 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(); +} + +void* InlineCacheBuffer::ic_buffer_cached_value(address code_begin) { + NativeMovConstReg* move = nativeMovConstReg_at(code_begin); // creation also verifies the object + void* o = (void*)move->data(); + return o; +} + diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,2209 @@ +/* + * 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 "asm/assembler.hpp" +#include "asm/macroAssembler.inline.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. + call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, + Interpreter::remove_activation_preserving_args_entry), + relocInfo::none); + + // 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 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 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 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) { + // Read Java big endian format. + if (is_signed == Signed) { + lha(Rdst, bcp_offset, R14_bcp); + } else { + lhz(Rdst, bcp_offset, R14_bcp); + } +#if 0 + assert(Rtmp != Rdst, "need separate temp register"); + Register Rfirst = Rtmp; + lbz(Rfirst, bcp_offset, R14_bcp); // first byte + lbz(Rdst, bcp_offset+1, R14_bcp); // second byte + + // Rdst = ((Rfirst<<8) & 0xFF00) | (Rdst &~ 0xFF00) + rldimi(/*RA=*/Rdst, /*RS=*/Rfirst, /*sh=*/8, /*mb=*/48); + if (is_signed == Signed) { + extsh(Rdst, Rdst); + } +#endif +} + +void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(int bcp_offset, + Register Rdst, + signedOrNot is_signed) { + // 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); + } + } +} + +// 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"); + if (index_size == sizeof(u2)) { + get_2_byte_integer_at_bcp(bcp_offset, Rdst, Unsigned); + } else if (index_size == sizeof(u4)) { + assert(EnableInvokeDynamic, "giant index used only for JSR 292"); + get_4_byte_integer_at_bcp(bcp_offset, Rdst, Signed); + assert(ConstantPool::decode_invokedynamic_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 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); +} + +// 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(Method::const_offset()), R19_method); +} + +void InterpreterMacroAssembler::get_constant_pool(Register Rdst) { + get_const(Rdst); + ld(Rdst, in_bytes(ConstMethod::constants_offset()), Rdst); +} + +void InterpreterMacroAssembler::get_constant_pool_cache(Register Rdst) { + get_constant_pool(Rdst); + ld(Rdst, ConstantPool::cache_offset_in_bytes(), Rdst); +} + +void InterpreterMacroAssembler::get_cpool_and_tags(Register Rcpool, Register Rtags) { + get_constant_pool(Rcpool); + ld(Rtags, ConstantPool::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(Method::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 +} + +// 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_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). + fence(); // TODO: replace by MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq ? + 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(Method::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(Method::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(Method::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(MethodData::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(Method::const_offset()), R19_method); + addi(R11_scratch1, R11_scratch1, in_bytes(ConstMethod::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); + assert(return_bci->is_nonvolatile(), "need to protect return_bci"); + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), return_bci); + pop(state); +} + +// Increments the backedge counter. +// Returns backedge counter + invocation counter in Rdst. +void InterpreterMacroAssembler::increment_backedge_counter(const Register Rcounters, 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(MethodCounters::backedge_counter_offset()) + + in_bytes(InvocationCounter::counter_offset()), Rcounters); + // Load invocation counter. + lwz(invocation_counter, in_bytes(MethodCounters::invocation_counter_offset()) + + in_bytes(InvocationCounter::counter_offset()), Rcounters); + + // 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(MethodCounters::backedge_counter_offset()) + + in_bytes(InvocationCounter::counter_offset()), Rcounters); + // 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 bcp_and_mdx_only) { + ld(scratch, 0, R1_SP); + ld(R14_bcp, _ijava_state_neg(bcp), scratch); // Changed by VM code (exception). + if (ProfileInterpreter) { ld(R28_mdx, _ijava_state_neg(mdx), scratch); } // Changed by VM code. + if (!bcp_and_mdx_only) { + // Following ones are Metadata. + ld(R19_method, _ijava_state_neg(method), scratch); + ld(R27_constPoolCache, _ijava_state_neg(cpoolCache), scratch); + // Following ones are stack addresses and don't require reload. + ld(R15_esp, _ijava_state_neg(esp), scratch); + ld(R18_locals, _ijava_state_neg(locals), scratch); + ld(R26_monitor, _ijava_state_neg(monitors), 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) { + 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); +} + +void InterpreterMacroAssembler::increment_invocation_counter(Register Rcounters, Register iv_be_count, Register Rtmp_r0) { + assert(UseCompiler, "incrementing must be useful"); + Register invocation_count = iv_be_count; + Register backedge_count = Rtmp_r0; + 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(MethodCounters::invocation_counter_offset() + + InvocationCounter::counter_offset()); + int be_counter_offset = in_bytes(MethodCounters::backedge_counter_offset() + + InvocationCounter::counter_offset()); + + BLOCK_COMMENT("Increment profiling counters {"); + + // Load the backedge counter. + lwz(backedge_count, be_counter_offset, Rcounters); // is unsigned int + // Mask the backedge counter. + Register tmp = invocation_count; + li(tmp, InvocationCounter::count_mask_value); + andr(backedge_count, tmp, backedge_count); // Cannot use andi, need sign extension of count_mask_value. + + // Load the invocation counter. + lwz(invocation_count, inv_counter_offset, Rcounters); // is unsigned int + // Add the delta to the invocation counter and store the result. + addi(invocation_count, invocation_count, delta); + // Store value. + stw(invocation_count, inv_counter_offset, Rcounters); + + // Add invocation counter + backedge counter. + add(iv_be_count, backedge_count, invocation_count); + + // Note that this macro must leave the backedge_count + invocation_count in + // register iv_be_count! + 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(Method* m, int bci) { +#ifndef PRODUCT + address pc = (address)(m->constMethod()) + in_bytes(ConstMethod::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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,301 @@ +/* + * 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 "asm/macroAssembler.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(Method::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)); + + + // 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); + + // 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(const Register Rcounters, 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 iv_be_count, Register Rtmp1, Register Rtmp2_r0); + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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/method.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, Method* 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, Method* method)) + methodHandle m(thread, method); + assert(m->is_native(), "sanity check"); + return AbstractInterpreter::result_handler(m->result_type()); +IRT_END diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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, Method* method); + +// A function to get the signature. +static address get_signature(JavaThread* thread, Method* method); + +#endif // CPU_PPC_VM_INTERPRETERRT_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,787 @@ +/* + * Copyright (c) 1997, 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.hpp" +#include "asm/macroAssembler.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/methodData.hpp" +#include "oops/method.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); + + // Get the result handler. + __ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::get_result_handler), R16_thread, R19_method); + + { + 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, + // "MethodDesc._access_flags == MethodDesc._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) + __ stfs(floatSlot, 4, arg_c); +#elif defined(AIX) + __ 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)); + + // 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(Method::const_offset()), R19_method); + __ ld(Rcpool_cache, in_bytes(ConstMethod::constants_offset()), Rconst_method); + + __ lhz(Rcodes, in_bytes(ConstMethod::codes_offset()) + 2, Rconst_method); // Lower half of 32 bit field. + __ ld(Rcpool_cache, ConstantPool::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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,3169 @@ +/* + * 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/macroAssembler.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" +#if INCLUDE_ALL_GCS +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#endif // INCLUDE_ALL_GCS + +#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 or klass constants. +// Assembler sequence is +// 1) compressed oops: +// lis rx = const.hi +// ori rx = rx | const.lo +// 2) compressed klass: +// lis rx = const.hi +// clrldi rx = rx & 0xFFFFffff // clearMS32b, optional +// ori rx = rx | const.lo +// Clrldi will be passed by. +int MacroAssembler::patch_set_narrow_oop(address a, address bound, narrowOop 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 ori, + // and the ori reads and writes the same register dst. + const int dst = inv_rta_field(inst2); + assert(is_ori(inst2) && inv_rs_field(inst2) == dst, "must be ori 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"); + + int xc = (data >> 16) & 0xffff; + int xd = (data >> 0) & 0xffff; + + set_imm((int *)inst1_addr, (short)(xc)); // see enc_load_con_narrow_hi/_lo + set_imm((int *)inst2_addr, (xd)); // unsigned int + return (int)((intptr_t)inst2_addr - (intptr_t)inst1_addr); +} + +// Get compressed oop or klass constant. +narrowOop 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 ori, + // and the ori reads and writes the same register dst. + const int dst = inv_rta_field(inst2); + assert(is_ori(inst2) && inv_rs_field(inst2) == dst, "must be ori 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 = (((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. + 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_metadata_address(Metadata* obj) { + assert(oop_recorder() != NULL, "this assembler needs a Recorder"); + int index = oop_recorder()->allocate_metadata_index(obj); + RelocationHolder rspec = metadata_Relocation::spec(index); + return AddressLiteral((address)obj, rspec); +} + +AddressLiteral MacroAssembler::constant_metadata_address(Metadata* obj) { + assert(oop_recorder() != NULL, "this assembler needs a Recorder"); + int index = oop_recorder()->find_index(obj); + RelocationHolder rspec = metadata_Relocation::spec(index); + return AddressLiteral((address)obj, rspec); +} + +AddressLiteral MacroAssembler::allocate_oop_address(jobject obj) { + assert(oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int oop_index = oop_recorder()->allocate_oop_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: +#if defined(ABI_ELFv2) + nop(); + calculate_address_from_global_toc(R12, dest, true, true, false); + mtctr(R12); + nop(); + nop(); +#else + 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(); +#endif + + // 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 = Array::length_offset_in_bytes(); + int base_offset = Array::base_offset_in_bytes(); + + 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 + + mtctr(temp); // load ctr + + bind(loop); + // Oops in table are NO MORE compressed. + ld(temp, base_offset, array_ptr); + cmpd(CCR0, temp, super_klass); + beq(CCR0, hit); + addi(array_ptr, array_ptr, BytesPerWord); + bdnz(loop); + + bind(failure); + if (result_reg!=noreg) li(result_reg, 1); // load non-zero result (indicates a miss) + b(fallthru); + + bind(hit); + 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_klass(temp_reg, 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_klass(temp2_reg, 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_klass(temp_reg, 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::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); +} + +#if INCLUDE_ALL_GCS +// 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); + cmpwi(CCR0, Rtmp3, (int)G1SATBCardTableModRefBS::g1_young_card_val()); + beq(CCR0, filtered); + + membar(Assembler::StoreLoad); + lbzx(/*card value*/ Rtmp3, Rbase, Rcard_addr); // Reload after membar. + cmpwi(CCR0, Rtmp3 /* card value */, CardTableModRefBS::dirty_card_val()); + beq(CCR0, filtered); + + // Storing a region crossing, non-NULL oop, card is clean. + // Dirty card and log. + li(Rtmp3, CardTableModRefBS::dirty_card_val()); + //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 // INCLUDE_ALL_GCS + +// 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); +} + +void MacroAssembler::get_vm_result_2(Register metadata_result) { + // Read: + // R16_thread + // R16_thread->in_bytes(JavaThread::vm_result_2_offset()) + // + // Updated: + // metadata_result + // R16_thread->in_bytes(JavaThread::vm_result_2_offset()) + + ld(metadata_result, in_bytes(JavaThread::vm_result_2_offset()), R16_thread); + li(R0, 0); + std(R0, in_bytes(JavaThread::vm_result_2_offset()), R16_thread); +} + + +void MacroAssembler::encode_klass_not_null(Register dst, Register src) { + Register current = (src != noreg) ? src : dst; // Klass is in dst if no src provided. + if (Universe::narrow_klass_base() != 0) { + // Use dst as temp if it is free. + load_const(R0, Universe::narrow_klass_base(), (dst != current && dst != R0) ? dst : noreg); + sub(dst, current, R0); + current = dst; + } + if (Universe::narrow_klass_shift() != 0) { + srdi(dst, current, Universe::narrow_klass_shift()); + current = dst; + } + mr_if_needed(dst, current); // Move may be required. +} + +void MacroAssembler::store_klass(Register dst_oop, Register klass, Register ck) { + if (UseCompressedClassPointers) { + encode_klass_not_null(ck, klass); + stw(ck, oopDesc::klass_offset_in_bytes(), dst_oop); + } else { + std(klass, oopDesc::klass_offset_in_bytes(), dst_oop); + } +} + +void MacroAssembler::store_klass_gap(Register dst_oop, Register val) { + if (UseCompressedClassPointers) { + if (val == noreg) { + val = R0; + li(val, 0); + } + stw(val, oopDesc::klass_gap_offset_in_bytes(), dst_oop); // klass gap if compressed + } +} + +int MacroAssembler::instr_size_for_decode_klass_not_null() { + if (!UseCompressedClassPointers) return 0; + int num_instrs = 1; // shift or move + if (Universe::narrow_klass_base() != 0) num_instrs = 7; // shift + load const + add + return num_instrs * BytesPerInstWord; +} + +void MacroAssembler::decode_klass_not_null(Register dst, Register src) { + if (src == noreg) src = dst; + Register shifted_src = src; + if (Universe::narrow_klass_shift() != 0 || + Universe::narrow_klass_base() == 0 && src != dst) { // Move required. + shifted_src = dst; + sldi(shifted_src, src, Universe::narrow_klass_shift()); + } + if (Universe::narrow_klass_base() != 0) { + load_const(R0, Universe::narrow_klass_base()); + add(dst, shifted_src, R0); + } +} + +void MacroAssembler::load_klass(Register dst, Register src) { + if (UseCompressedClassPointers) { + lwz(dst, oopDesc::klass_offset_in_bytes(), src); + // Attention: no null check here! + decode_klass_not_null(dst, dst); + } else { + ld(dst, oopDesc::klass_offset_in_bytes(), src); + } +} + +void MacroAssembler::load_klass_with_trap_null_check(Register dst, Register src) { + if (!os::zero_page_read_protected()) { + if (TrapBasedNullChecks) { + trap_null_check(src); + } + } + load_klass(dst, src); +} + +void MacroAssembler::reinit_heapbase(Register d, Register tmp) { + if (Universe::heap() != NULL) { + if (Universe::narrow_oop_base() == NULL) { + Assembler::xorr(R30, R30, R30); + } else { + load_const(R30, Universe::narrow_ptrs_base(), tmp); + } + } else { + load_const(R30, Universe::narrow_ptrs_base_addr(), tmp); + ld(R30, 0, R30); + } +} + +// 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,716 @@ +/* + * 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, narrowOop data); + static narrowOop 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); + + // Metadata in code that we have to keep track of. + AddressLiteral allocate_metadata_address(Metadata* obj); // allocate_index + AddressLiteral constant_metadata_address(Metadata* obj); // find_index + // 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); + +#if INCLUDE_ALL_GCS + // 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 + + // 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); + void get_vm_result_2(Register metadata_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); + + // 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); + + // 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); + + // Load/Store klass oop from klass field. Compress. + void load_klass(Register dst, Register src); + void load_klass_with_trap_null_check(Register dst, Register src); + void store_klass(Register dst_oop, Register klass, Register tmp = R0); + void store_klass_gap(Register dst_oop, Register val = noreg); // Will store 0 if val not specified. + static int instr_size_for_decode_klass_not_null(); + void decode_klass_not_null(Register dst, Register src = noreg); + void encode_klass_not_null(Register dst, Register src = noreg); + + // Load common heap base into register. + void reinit_heapbase(Register d, Register tmp = noreg); + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,407 @@ +/* + * 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 "asm/macroAssembler.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 ori. + if (!is_ori(inst2)) return false; + + // The ori reads and writes the same register dst. + const int dst = inv_rta_field(inst2); + if (inv_rs_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); +} + +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 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 45d7b2c7029d -r 52b4284cb496 src/cpu/ppc/vm/metaspaceShared_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/metaspaceShared_ppc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,61 @@ +/* + * 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/macroAssembler.inline.hpp" +#include "asm/codeBuffer.hpp" +#include "memory/metaspaceShared.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 MetaspaceShared::generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, + char* md_end, + char** mc_top, + char* mc_end) { + Unimplemented(); +} + diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 "asm/macroAssembler.inline.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, SystemDictionary::WK_KLASS_ENUM_NAME(java_lang_Class), temp_reg, temp2_reg, + "MH argument is a Class"); + __ ld(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, SystemDictionary::WKID klass_id, + Register temp_reg, Register temp2_reg, + const char* error_message) { + Klass** klass_addr = SystemDictionary::well_known_klass_addr(klass_id); + KlassHandle klass = SystemDictionary::well_known_klass(klass_id); + 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); + __ load_const_optimized(temp2_reg, (address) klass_addr); + __ 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); + + 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(Method::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 ? Method::from_compiled_offset() : + Method::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 Method* is normally compressed in the vmtarget field: + __ ld(method_temp, NONZERO(java_lang_invoke_MemberName::vmtarget_offset_in_bytes()), method_temp); + + if (VerifyMethodHandles && !for_compiler_entry) { + // make sure recv is already on stack + __ ld(temp2, in_bytes(Method::const_offset()), method_temp); + __ load_sized_value(temp2, in_bytes(ConstMethod::size_of_parameters_offset()), temp2, + sizeof(u2), /*is_signed*/ false); + // assert(sizeof(u2) == sizeof(ConstMethod::_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, Method::intrinsic_id_offset_in_bytes(), R19_method, + sizeof(u1), /*is_signed*/ false); + // assert(sizeof(u1) == sizeof(Method::_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 Method*:intrinsic_id"); + } + __ stop("bad Method*::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)) { + __ ld(param_size, in_bytes(Method::const_offset()), R19_method); + __ load_sized_value(param_size, in_bytes(ConstMethod::size_of_parameters_offset()), param_size, + sizeof(u2), /*is_signed*/ false); + // assert(sizeof(u2) == sizeof(ConstMethod::_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) { + if (tmp_mh != noreg) + __ mr(R23_method_handle, tmp_mh); // make stub happy + 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, SystemDictionary::WK_KLASS_ENUM_NAME(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_klass_ptr(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_klass_ptr(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_klass_ptr(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); + } + __ ld(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); + } + __ ld(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 Method* & 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_klass_ptr(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_method_ptr(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 ? "R23_method_handle" : "G23"; + 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, R23_method_handle); + __ 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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. + * + */ + +// 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); +//} +enum /* platform_dependent_constants */ { + adapter_code_size = NOT_LP64(16000 DEBUG_ONLY(+ 25000)) LP64_ONLY(32000 DEBUG_ONLY(+ 150000)) +}; + +// 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, SystemDictionary::WKID klass_id, + 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) { + Unimplemented(); + } + + 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,391 @@ +/* + * 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/macroAssembler.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(nm); + } + + 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); + + if (MacroAssembler::is_load_const_at(addr)) { + return MacroAssembler::get_const(addr); + } + + CodeBlob* cb = CodeCache::find_blob_unsafe(addr); + if (MacroAssembler::is_set_narrow_oop(addr, cb->content_begin())) { + narrowOop no = (narrowOop)MacroAssembler::get_narrow_oop(addr, cb->content_begin()); + return cast_from_oop(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; + Metadata** metadata_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 = cast_to_oop(data); + } else { + assert(oop_addr == r->oop_addr(), "must be only one set-oop here") ; + } + } + if (iter.type() == relocInfo::metadata_type) { + metadata_Relocation *r = iter.metadata_reloc(); + if (metadata_addr == NULL) { + metadata_addr = r->metadata_addr(); + *metadata_addr = (Metadata*)data; + } else { + assert(metadata_addr == r->metadata_addr(), "must be only one set-metadata here"); + } + } + } + } +} + +void NativeMovConstReg::set_narrow_oop(narrowOop 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); + if (! MacroAssembler::is_load_const_at(addr) && + ! MacroAssembler::is_load_const_from_method_toc_at(addr)) { + CodeBlob* cb = CodeCache::find_blob_unsafe(addr); // find_nmethod() asserts if nmethod is zombie. + if (! (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(nmethod *nm) const { + CodeBlob* cb = nm ? nm : CodeCache::find_blob_unsafe(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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,398 @@ +/* + * 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 "asm/macroAssembler.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); + +#ifdef COMPILER2 + // SIGTRAP-based implicit range checks + bool is_sigtrap_range_check() { + assert(UseSIGTRAP && TrapBasedRangeChecks, "precondition"); + return MacroAssembler::is_trap_range_check(long_at(0)); + } +#endif + + // '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. Use this also for narrow klass. + void set_narrow_oop(narrowOop 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(nmethod *nm = NULL) 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 45d7b2c7029d -r 52b4284cb496 src/cpu/ppc/vm/ppc.ad --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/ppc/vm/ppc.ad Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,12117 @@ +// +// 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 +); + +// Used to hold the TOC to avoid collisions with expanded DynamicCall +// which uses r19 as inline cache internally and expanded LeafCall which uses +// r2, r11 and r12 internally. +reg_class bits64_constant_table_base( +/*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 + +// **************************************************************************** + +// 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 == Method::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); +} + +//============================================================================= + +%} // interrupt source + +source_hpp %{ // Header information of the source block. + +//-------------------------------------------------------------- +//---< Used for optimization in Compile::Shorten_branches >--- +//-------------------------------------------------------------- + +const uint trampoline_stub_size = 6 * BytesPerInstWord; + +class CallStubImpl { + + public: + + // Emit call stub, compiled java to interpreter. + static void emit_trampoline_stub(MacroAssembler &_masm, int destination_toc_offset, int insts_call_instruction_offset); + + // Size of call trampoline stub. + // This doesn't need to be accurate to the byte, but it + // must be larger than or equal to the real size of the stub. + static uint size_call_trampoline() { + return trampoline_stub_size; + } + + // number of relocations needed by a call trampoline stub + static uint reloc_call_trampoline() { + return 5; + } + +}; + +%} // end source_hpp + +source %{ + +// 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) + +void CallStubImpl::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(); +} + +//============================================================================= + +// 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. + CallStubImpl::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; +} + +//============================================================================= + +const RegMask& MachConstantBaseNode::_out_RegMask = BITS64_CONSTANT_TABLE_BASE_mask(); +int Compile::ConstantTable::calculate_table_base_offset() const { + return 0; // absolute addressing, no offset +} + +bool MachConstantBaseNode::requires_postalloc_expand() const { return true; } +void MachConstantBaseNode::postalloc_expand(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 postalloc 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 = C->frame_size_in_bytes(); + 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. + + int bangsize = C->bang_size_in_bytes(); + assert(bangsize >= framesize || bangsize <= 0, "stack bang size incorrect"); + if (C->need_stack_bang(bangsize) && 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 (bangsize > page_size) { + bang_end += bangsize; + } + + 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)(-offset), 16) && + !(false /* ConstantsALot TODO: PPC port*/)) { + ___(or) mr(callers_sp, R1_SP); + ___(std) std(return_pc, _abi(lr), R1_SP); + ___(stdu) stdu(R1_SP, -offset, 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 +#undef ___advance + +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 +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_); +} + +//============================================================================= + +%} // interrupt source + +source_hpp %{ // Header information of the source block. + +class HandlerImpl { + + public: + + static int emit_exception_handler(CodeBuffer &cbuf); + static int emit_deopt_handler(CodeBuffer& cbuf); + + static uint size_exception_handler() { + // The exception_handler is a b64_patchable. + return MacroAssembler::b64_patchable_size; + } + + static uint size_deopt_handler() { + // The deopt_handler is a bl64_patchable. + return MacroAssembler::bl64_patchable_size; + } + +}; + +%} // end source_hpp + +source %{ + +int HandlerImpl::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 HandlerImpl::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; +} + +//============================================================================= + +// Use a frame slots bias for frameless methods if accessing the stack. +static int frame_slots_bias(int reg_enc, PhaseRegAlloc* ra_) { + if (as_Register(reg_enc) == R1_SP) { + return 0; // TODO: PPC port ra_->C->frame_slots_sp_bias_in_bytes(); + } + return 0; +} + +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. +} + +// 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; +} + +// PPC AES support not yet implemented +const bool Matcher::pass_original_key_for_aes() { + 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 postalloc expand (see block.cpp for description of postalloc expand). +const bool Matcher::require_postalloc_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? +// Off, as masks are generated in expand rules where required. +// Constant shift counts are handled in Ideal phase. +const bool Matcher::need_masked_shift_count = false; + +// 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; +} + +bool Matcher::narrow_klass_use_complex_address() { + NOT_LP64(ShouldNotCallThis()); + assert(UseCompressedClassPointers, "only for compressed klass code"); + // 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 + %} + + enc_class enc_lbz(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lbz); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lbz($dst$$Register, Idisp, $mem$$base$$Register); + %} + + // Load acquire. + enc_class enc_lbz_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lbz($dst$$Register, Idisp, $mem$$base$$Register); + __ twi_0($dst$$Register); + __ isync(); + %} + + enc_class enc_lhz(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lhz); + + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lhz($dst$$Register, Idisp, $mem$$base$$Register); + %} + + // Load acquire. + enc_class enc_lhz_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lhz($dst$$Register, Idisp, $mem$$base$$Register); + __ twi_0($dst$$Register); + __ isync(); + %} + + enc_class enc_lwz(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwz); + + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lwz($dst$$Register, Idisp, $mem$$base$$Register); + %} + + // Load acquire. + enc_class enc_lwz_ac(iRegIdst dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lwz($dst$$Register, Idisp, $mem$$base$$Register); + __ twi_0($dst$$Register); + __ isync(); + %} + + enc_class enc_ld(iRegLdst dst, memoryAlg4 mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + // Operand 'ds' requires 4-alignment. + assert((Idisp & 0x3) == 0, "unaligned offset"); + __ ld($dst$$Register, Idisp, $mem$$base$$Register); + %} + + // Load acquire. + enc_class enc_ld_ac(iRegLdst dst, memoryAlg4 mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + // Operand 'ds' requires 4-alignment. + assert((Idisp & 0x3) == 0, "unaligned offset"); + __ ld($dst$$Register, Idisp, $mem$$base$$Register); + __ twi_0($dst$$Register); + __ isync(); + %} + + enc_class enc_lfd(RegF dst, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfd); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lfd($dst$$FloatRegister, Idisp, $mem$$base$$Register); + %} + + enc_class enc_load_long_constL(iRegLdst dst, immL src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + + MacroAssembler _masm(&cbuf); + int toc_offset = 0; + + if (!ra_->C->in_scratch_emit_size()) { + address const_toc_addr; + // Create a non-oop constant, no relocation needed. + // If it is an IC, it has a virtual_call_Relocation. + 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($dst$$Register, toc_offset, $toc$$Register); + %} + + enc_class enc_load_long_constL_hi(iRegLdst dst, iRegLdst toc, immL src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + + if (!ra_->C->in_scratch_emit_size()) { + address const_toc_addr; + // Create a non-oop constant, no relocation needed. + // If it is an IC, it has a virtual_call_Relocation. + 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($dst$$Register, $toc$$Register, MacroAssembler::largeoffset_si16_si16_hi(_const_toc_offset)); + %} + +%} // encode + +source %{ + +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, + OptoReg::Name reg_second, OptoReg::Name reg_first) { + loadConLNodesTuple nodes; + + 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 + m2->_opnds[0] = new (C) iRegLdstOper(); // dst + m2->_opnds[1] = immSrc; // src + m2->_opnds[2] = new (C) iRegLdstOper(); // base + + // 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 + + // 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; +} + +%} // source + +encode %{ + // Postalloc expand emitter for loading a long constant from the method's TOC. + // Enc_class needed as consttanttablebase is not supported by postalloc + // expand. + enc_class postalloc_expand_load_long_constant(iRegLdst dst, immL src, iRegLdst toc) %{ + // Create new nodes. + loadConLNodesTuple loadConLNodes = + loadConLNodesTuple_create(C, ra_, n_toc, op_src, + 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"); + %} + + enc_class enc_load_long_constP(iRegLdst dst, immP src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + + MacroAssembler _masm(&cbuf); + int toc_offset = 0; + + if (!ra_->C->in_scratch_emit_size()) { + intptr_t val = $src$$constant; + relocInfo::relocType constant_reloc = $src->constant_reloc(); // src + address const_toc_addr; + if (constant_reloc == relocInfo::oop_type) { + // Create an oop constant and a corresponding relocation. + AddressLiteral a = __ allocate_oop_address((jobject)val); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); + } else if (constant_reloc == relocInfo::metadata_type) { + AddressLiteral a = __ allocate_metadata_address((Metadata *)val); + 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. + toc_offset = __ offset_to_method_toc(const_toc_addr); + } + + __ ld($dst$$Register, toc_offset, $toc$$Register); + %} + + enc_class enc_load_long_constP_hi(iRegLdst dst, immP src, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + + MacroAssembler _masm(&cbuf); + if (!ra_->C->in_scratch_emit_size()) { + intptr_t val = $src$$constant; + relocInfo::relocType constant_reloc = $src->constant_reloc(); // src + address const_toc_addr; + if (constant_reloc == relocInfo::oop_type) { + // Create an oop constant and a corresponding relocation. + AddressLiteral a = __ allocate_oop_address((jobject)val); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); + } else if (constant_reloc == relocInfo::metadata_type) { + AddressLiteral a = __ allocate_metadata_address((Metadata *)val); + const_toc_addr = __ address_constant((address)a.value(), RelocationHolder::none); + __ relocate(a.rspec()); + } else { // non-oop pointers, e.g. card mark base, heap top + // 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($dst$$Register, $toc$$Register, MacroAssembler::largeoffset_si16_si16_hi(_const_toc_offset)); + %} + + // Postalloc expand emitter for loading a ptr constant from the method's TOC. + // Enc_class needed as consttanttablebase is not supported by postalloc + // expand. + enc_class postalloc_expand_load_ptr_constant(iRegPdst dst, immP src, iRegLdst toc) %{ + 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, n_toc); + m2->add_req(NULL, m1); + + // operands for new nodes + m1->_opnds[0] = new (C) iRegPdstOper(); // dst + m1->_opnds[1] = op_src; // src + m1->_opnds[2] = new (C) iRegPdstOper(); // toc + m2->_opnds[0] = new (C) iRegPdstOper(); // dst + m2->_opnds[1] = op_src; // src + m2->_opnds[2] = new (C) iRegLdstOper(); // base + + // 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, 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); + assert(m2->bottom_type()->isa_ptr(), "must be ptr"); + } else { + loadConPNode *m2 = new (C) loadConPNode(); + + // inputs for new nodes + m2->add_req(NULL, n_toc); + + // operands for new nodes + m2->_opnds[0] = new (C) iRegPdstOper(); // dst + m2->_opnds[1] = op_src; // src + m2->_opnds[2] = new (C) iRegPdstOper(); // toc + + // Register allocation for new nodes. + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + + nodes->push(m2); + assert(m2->bottom_type()->isa_ptr(), "must be ptr"); + } + %} + + // Enc_class needed as consttanttablebase is not supported by postalloc + // expand. + enc_class postalloc_expand_load_float_constant(regF dst, immF src, iRegLdst 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 needed as consttanttablebase is not supported by postalloc + // expand. + enc_class postalloc_expand_load_double_constant(regD dst, immD src, iRegLdst 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_stw(iRegIsrc src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stw); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ stw($src$$Register, Idisp, $mem$$base$$Register); + %} + + enc_class enc_std(iRegIsrc src, memoryAlg4 mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_std); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + // Operand 'ds' requires 4-alignment. + assert((Idisp & 0x3) == 0, "unaligned offset"); + __ std($src$$Register, Idisp, $mem$$base$$Register); + %} + + enc_class enc_stfs(RegF src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stfs); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ stfs($src$$FloatRegister, Idisp, $mem$$base$$Register); + %} + + enc_class enc_stfd(RegF src, memory mem) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stfd); + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ stfd($src$$FloatRegister, Idisp, $mem$$base$$Register); + %} + + // 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_storestore; + +#if 0 // TODO: PPC port + // Check CMSCollectorCardTableModRefBSExt::_requires_release and do the + // StoreStore barrier conditionally. + __ lwz(R0, 0, $releaseFieldAddr$$Register); + __ cmpwi(CCR0, R0, 0); + __ beq_predict_taken(CCR0, skip_storestore); +#endif + __ li(R0, 0); + __ membar(Assembler::StoreStore); +#if 0 // TODO: PPC port + __ bind(skip_storestore); +#endif + + // Do the store. + if ($mem$$index == 0) { + __ stb(R0, $mem$$disp, $mem$$base$$Register); + } else { + assert(0 == $mem$$disp, "no displacement possible with indexed load/stores on ppc"); + __ stbx(R0, $mem$$base$$Register, $mem$$index$$Register); + } + %} + + enc_class postalloc_expand_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 postalloc_expand_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 postalloc_expand_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 postalloc_expand_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 enc_cmove_reg(iRegIdst dst, flagsReg crx, iRegIsrc src, cmpOp cmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + 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($dst$$Register, $src$$Register); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + + enc_class enc_cmove_imm(iRegIdst dst, flagsReg crx, immI16 src, cmpOp cmp) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + Label done; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + // Branch if not (cmp crx). + __ bc(cc_to_inverse_boint($cmp$$cmpcode), cc_to_biint($cmp$$cmpcode, $crx$$reg), done); + __ li($dst$$Register, $src$$constant); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + + // New atomics. + 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 = $res$$Register; + Register Rsrc = $src$$Register; + Register Rptr = $mem_ptr$$Register; + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ lwarx(Rold, Rptr, MacroAssembler::cmpxchgx_hint_atomic_update()); + __ add(Rtmp, Rsrc, Rold); + __ stwcx_(Rtmp, Rptr); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + __ bne_predict_not_taken(CCR0, Lretry); + } else { + __ bne( CCR0, Lretry); + } + if (RegCollision) __ subf(Rres, Rsrc, Rtmp); + __ fence(); + %} + + 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 = $res$$Register; + Register Rsrc = $src$$Register; + Register Rptr = $mem_ptr$$Register; + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ ldarx(Rold, Rptr, MacroAssembler::cmpxchgx_hint_atomic_update()); + __ add(Rtmp, Rsrc, Rold); + __ stdcx_(Rtmp, Rptr); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + __ bne_predict_not_taken(CCR0, Lretry); + } else { + __ bne( CCR0, Lretry); + } + if (RegCollision) __ subf(Rres, Rsrc, Rtmp); + __ fence(); + %} + + 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 = $res$$Register; + Register Rsrc = $src$$Register; + Register Rptr = $mem_ptr$$Register; + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ lwarx(Rold, Rptr, MacroAssembler::cmpxchgx_hint_atomic_update()); + __ stwcx_(Rsrc, Rptr); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + __ bne_predict_not_taken(CCR0, Lretry); + } else { + __ bne( CCR0, Lretry); + } + if (RegCollision) __ mr(Rres, Rtmp); + __ fence(); + %} + + 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 = $res$$Register; + Register Rsrc = $src$$Register; + Register Rptr = $mem_ptr$$Register; + bool RegCollision = (Rres == Rsrc) || (Rres == Rptr); + Register Rold = RegCollision ? Rtmp : Rres; + + Label Lretry; + __ bind(Lretry); + __ ldarx(Rold, Rptr, MacroAssembler::cmpxchgx_hint_atomic_update()); + __ stdcx_(Rsrc, Rptr); + if (UseStaticBranchPredictionInCompareAndSwapPPC64) { + __ bne_predict_not_taken(CCR0, Lretry); + } else { + __ bne( CCR0, Lretry); + } + if (RegCollision) __ mr(Rres, Rtmp); + __ fence(); + %} + + // This enc_class is needed so that scheduler gets proper + // input mapping for latency computation. + enc_class enc_andc(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andc); + MacroAssembler _masm(&cbuf); + __ andc($dst$$Register, $src1$$Register, $src2$$Register); + %} + + enc_class enc_convI2B_regI__cmove(iRegIdst dst, iRegIsrc src, flagsReg crx, immI16 zero, immI16 notzero) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + + Label done; + __ cmpwi($crx$$CondRegister, $src$$Register, 0); + __ li($dst$$Register, $zero$$constant); + __ beq($crx$$CondRegister, done); + __ li($dst$$Register, $notzero$$constant); + __ bind(done); + %} + + enc_class enc_convP2B_regP__cmove(iRegIdst dst, iRegPsrc src, flagsReg crx, immI16 zero, immI16 notzero) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + + MacroAssembler _masm(&cbuf); + + Label done; + __ cmpdi($crx$$CondRegister, $src$$Register, 0); + __ li($dst$$Register, $zero$$constant); + __ beq($crx$$CondRegister, done); + __ li($dst$$Register, $notzero$$constant); + __ bind(done); + %} + + enc_class enc_cmove_bso_stackSlotL(iRegLdst dst, flagsReg crx, stackSlotL mem ) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + + MacroAssembler _masm(&cbuf); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + Label done; + __ bso($crx$$CondRegister, done); + __ ld($dst$$Register, Idisp, $mem$$base$$Register); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + + 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(); + %} + + // Postalloc expand emitter for loading a replicatef float constant from + // the method's TOC. + // Enc_class needed as consttanttablebase is not supported by postalloc + // expand. + enc_class postalloc_expand_load_replF_constant(iRegLdst dst, immF src, iRegLdst 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, + 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"); + %} + + // This enc_class is needed so that scheduler gets proper + // input mapping for latency computation. + 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); + %} + + // 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); + address entry_point = (address)$meth$$method; + + if (!_method) { + // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap. + emit_call_with_trampoline_stub(_masm, entry_point, relocInfo::runtime_call_type); + } else { + // Remember the offset not the address. + const int start_offset = __ offset(); + // The trampoline stub. + if (!Compile::current()->in_scratch_emit_size()) { + // No entry point given, use the current pc. + // Make sure branch fits into + if (entry_point == 0) entry_point = __ pc(); + + // 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. + CallStubImpl::emit_trampoline_stub(_masm, entry_point_toc_offset, start_offset); + __ relocate(_optimized_virtual ? + relocInfo::opt_virtual_call_type : relocInfo::static_call_type); + } + + // The real call. + // 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. + cbuf.set_insts_mark(); + __ bl(__ pc()); // Emits a relocation. + + // The stub for call to interpreter. + CompiledStaticCall::emit_to_interp_stub(cbuf); + } + %} + + // 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); + address entry_point = (address)$meth$$method; + + // Remember the offset not the address. + const int start_offset = __ offset(); + // The trampoline stub. + if (!ra_->C->in_scratch_emit_size()) { + // No entry point given, use the current pc. + // Make sure branch fits into + if (entry_point == 0) entry_point = __ pc(); + + // 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. + CallStubImpl::emit_trampoline_stub(_masm, entry_point_toc_offset, start_offset); + assert(_optimized_virtual, "methodHandle call should be a virtual call"); + __ relocate(relocInfo::opt_virtual_call_type); + } + + // The real call. + // 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. + cbuf.set_insts_mark(); + __ bl(__ pc()); // Emits a relocation. + + assert(_method, "execute next statement conditionally"); + // The stub for call to interpreter. + CompiledStaticCall::emit_to_interp_stub(cbuf); + + // Restore original sp. + __ ld(R11_scratch1, 0, R1_SP); // Load caller sp. + const long framesize = ra_->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; + } + %} + + // 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); + CallStubImpl::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()); + %} + + // postalloc expand emitter for virtual calls. + enc_class postalloc_expand_java_dynamic_call_sched(method meth, iRegLdst toc) %{ + + // Create the nodes for loading the IC from the TOC. + loadConLNodesTuple loadConLNodes_IC = + loadConLNodesTuple_create(C, ra_, n_toc, new (C) immLOper((jlong)Universe::non_oop_word()), + 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) { + // The expanded node does not need toc any more. + // Add the inline cache constant here instead. This expresses the + // register of the inline cache must be live at the call. + // Else we would have to adapt JVMState by -1. + if (i == mach_constant_base_node_input()) { + call->add_req(loadConLNodes_IC._last); + } else { + call->add_req(in(i)); + } + } + // ...as well as prec + for (uint i = req(); i < len(); ++i) { + call->add_prec(in(i)); + } + + // 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); + %} + + // Compound version of call dynamic + // Toc is only passed so that it can be used in ins_encode statement. + // In the code we have to use $constanttablebase. + enc_class enc_java_dynamic_call(method meth, iRegLdst toc) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + MacroAssembler _masm(&cbuf); + int start_offset = __ offset(); + + Register Rtoc = (ra_) ? $constanttablebase : R2_TOC; +#if 0 + int vtable_index = this->_vtable_index; + if (_vtable_index < 0) { + // Must be invalid_vtable_index, not nonvirtual_vtable_index. + assert(_vtable_index == Method::invalid_vtable_index, "correct sentinel value"); + Register ic_reg = as_Register(Matcher::inline_cache_reg_encode()); + + // Virtual call relocation will point to ic load. + address virtual_call_meta_addr = __ pc(); + // Load a clear inline cache. + AddressLiteral empty_ic((address) Universe::non_oop_word()); + __ load_const_from_method_toc(ic_reg, empty_ic, Rtoc); + // CALL to fixup routine. Fixup routine uses ScopeDesc info + // to determine who we intended to call. + __ relocate(virtual_call_Relocation::spec(virtual_call_meta_addr)); + emit_call_with_trampoline_stub(_masm, (address)$meth$$method, relocInfo::none); + assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == __ 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_klass(R11_scratch1, 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(Method::from_compiled_offset()), R19_method); + // Call target. Either compiled code or C2I adapter. + __ mtctr(R11_scratch1); + __ bctrl(); + if (((MachCallDynamicJavaNode*)this)->ret_addr_offset() != __ offset() - start_offset) { + tty->print(" %d, %d\n", ((MachCallDynamicJavaNode*)this)->ret_addr_offset(),__ offset() - start_offset); + } + assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == __ offset() - start_offset, + "Fix constant in ret_addr_offset()"); + } +#endif + Unimplemented(); // ret_addr_offset not yet fixed. Depends on compressed oops (load klass!). + %} + + // 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()"); + %} + + // Move to ctr for leaf call. + // This enc_class is needed so that scheduler gets proper + // input mapping for latency computation. + enc_class enc_leaf_call_mtctr(iRegLsrc src) %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mtctr); + MacroAssembler _masm(&cbuf); + __ mtctr($src$$Register); + %} + + // Postalloc expand emitter for runtime leaf calls. + enc_class postalloc_expand_java_to_runtime_call(method meth, iRegLdst toc) %{ + 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_, n_toc, new (C) immLOper(entry_address), + 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_, n_toc, new (C) immLOper(entry_address), + 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_, n_toc, new (C) immLOper((jlong) fd->env()), + 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_, n_toc, new (C) immLOper((jlong) fd->toc()), + 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; + guarantee(!_jvms, "You must clone the jvms and adapt the offsets by fix_jvms()."); + call->_jvms = NULL; + 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 != mach_constant_base_node_input()) { + call->add_req(in(i)); + } + } + + // These must be reqired edges, as the registers are live up to + // the call. Else the constants are handled as kills. + call->add_req(mtctr); +#if !defined(ABI_ELFv2) + call->add_req(loadConLNodes_Env._last); + call->add_req(loadConLNodes_Toc._last); +#endif + + // ...as well as prec + for (uint i = req(); i < len(); ++i) { + call->add_prec(in(i)); + } + + // 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); + %} +%} + +//----------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 method 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); + +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 postalloc_expand 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 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 postalloc 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); +%} + +// This operand can be used to avoid matching of an instruct +// with chain rule. +operand immNKlass_NM() %{ + match(ConNKlass); + predicate(false); + 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 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); + match(DecodeNKlass 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); + match(DecodeNKlass 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); + match(AddP (DecodeNKlass 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); + match(AddP (DecodeNKlass 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsb); + __ extsb($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lha); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lha($dst$$Register, Idisp, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lha($dst$$Register, Idisp, $mem$$base$$Register); + __ twi_0($dst$$Register); + __ isync(); + %} + 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); +%} + +// Match loading integer and casting it to long. +instruct loadI2L(iRegLdst dst, memory 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwa); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lwa($dst$$Register, Idisp, $mem$$base$$Register); + %} + ins_pipe(pipe_class_memory); +%} + +// Match loading integer and casting it to long - acquire. +instruct loadI2L_ac(iRegLdst dst, memory 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwa); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lwa($dst$$Register, Idisp, $mem$$base$$Register); + __ twi_0($dst$$Register); + __ isync(); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfs); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ lfs($dst$$FloatRegister, Idisp, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + Label next; + __ lfs($dst$$FloatRegister, Idisp, $mem$$base$$Register); + __ fcmpu(CCR0, $dst$$FloatRegister, $dst$$FloatRegister); + __ bne(CCR0, next); + __ bind(next); + __ isync(); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + Label next; + __ lfd($dst$$FloatRegister, Idisp, $mem$$base$$Register); + __ fcmpu(CCR0, $dst$$FloatRegister, $dst$$FloatRegister); + __ bne(CCR0, next); + __ bind(next); + __ isync(); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + __ calculate_address_from_global_toc_hi16only($dst$$Register, __ method_toc()); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + __ calculate_address_from_global_toc_lo16only($dst$$Register, __ method_toc()); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($src$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + // Lis sign extends 16-bit src then shifts it 16 bit to the left. + __ lis($dst$$Register, (int)((short)(($src$$constant & 0xFFFF0000) >> 16))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + __ ori($dst$$Register, $src1$$Register, ($src2$$constant) & 0xFFFF); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short) ($src$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + __ lis($dst$$Register, (int)((short)(($src$$constant & 0xFFFF0000) >> 16))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + __ ori($dst$$Register, $src1$$Register, ($src2$$constant) & 0xFFFF); + %} + 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) %{ + effect(DEF dst, USE src, USE toc); + 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 $src from TOC" %} + size(4); + ins_encode( enc_load_long_constL(dst, src, toc) ); + ins_pipe(pipe_class_memory); +%} + +// Expand node for constant pool load: large offset. +instruct loadConL_hi(iRegLdst dst, immL src, iRegLdst toc) %{ + effect(DEF dst, USE src, USE toc); + 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 $src from TOC (hi)" %} + size(4); + ins_encode( enc_load_long_constL_hi(dst, toc, src) ); + ins_pipe(pipe_class_default); +%} + +// Expand node for constant pool load: large offset. +// No constant pool entries required. +instruct loadConL_lo(iRegLdst dst, immL src, iRegLdst base) %{ + effect(DEF dst, USE src, USE base); + predicate(false); + + ins_field_const_toc_offset_hi_node(loadConL_hiNode*); + + format %{ "LD $dst, offset, $base \t// load long $src from TOC (lo)" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + int offset = ra_->C->in_scratch_emit_size() ? 0 : _const_toc_offset_hi_node->_const_toc_offset; + __ ld($dst$$Register, MacroAssembler::largeoffset_si16_si16_lo(offset), $base$$Register); + %} + 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, postalloc expanded" %} + // We can not inline the enc_class for the expand as that does not support constanttablebase. + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, 0); + %} + ins_pipe(pipe_class_default); +%} + +// Load hi part of compressed oop constant. +instruct loadConN_hi(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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + __ lis($dst$$Register, (int)(short)(($src$$constant >> 16) & 0xffff)); + %} + ins_pipe(pipe_class_default); +%} + +// Add lo part of compressed oop constant to already loaded hi part. +instruct loadConN_lo(iRegNdst dst, iRegNsrc src1, immN src2) %{ + effect(DEF dst, USE src1, USE src2); + ins_cost(DEFAULT_COST); + + format %{ "ORI $dst, $src1, $src2 \t// narrow oop lo" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + 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); + __ ori($dst$$Register, $src1$$Register, $src2$$constant & 0xffff); + %} + ins_pipe(pipe_class_default); +%} + +// Needed to postalloc 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src$$Register, 0x20); + %} + ins_pipe(pipe_class_default); +%} + +// Loading ConN must be postalloc 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// postalloc expanded" %} // mask + postalloc_expand %{ + MachNode *m1 = new (C) loadConN_hiNode(); + MachNode *m2 = new (C) loadConN_loNode(); + 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); + %} +%} + +instruct loadConNKlass_hi(iRegNdst dst, immNKlass src) %{ + effect(DEF dst, USE src); + ins_cost(DEFAULT_COST); + + format %{ "LIS $dst, $src \t// narrow oop hi" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + intptr_t Csrc = Klass::encode_klass((Klass *)$src$$constant); + __ lis($dst$$Register, (int)(short)((Csrc >> 16) & 0xffff)); + %} + ins_pipe(pipe_class_default); +%} + +// This needs a match rule so that build_oop_map knows this is +// not a narrow oop. +instruct loadConNKlass_lo(iRegNdst dst, immNKlass_NM src1, iRegNsrc src2) %{ + match(Set dst src1); + effect(TEMP src2); + ins_cost(DEFAULT_COST); + + format %{ "ADDI $dst, $src1, $src2 \t// narrow oop lo" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + intptr_t Csrc = Klass::encode_klass((Klass *)$src1$$constant); + assert(__ oop_recorder() != NULL, "this assembler needs an OopRecorder"); + int klass_index = __ oop_recorder()->find_index((Klass *)$src1$$constant); + RelocationHolder rspec = metadata_Relocation::spec(klass_index); + + __ relocate(rspec, 1); + __ ori($dst$$Register, $src2$$Register, Csrc & 0xffff); + %} + ins_pipe(pipe_class_default); +%} + +// Loading ConNKlass must be postalloc expanded so that edges between +// the nodes are safe. They may not interfere with a safepoint. +instruct loadConNKlass_Ex(iRegNdst dst, immNKlass src) %{ + match(Set dst src); + ins_cost(DEFAULT_COST*2); + + format %{ "LoadN $dst, $src \t// postalloc expanded" %} // mask + postalloc_expand %{ + // Load high bits into register. Sign extended. + MachNode *m1 = new (C) loadConNKlass_hiNode(); + m1->add_req(NULL); + m1->_opnds[0] = op_dst; + m1->_opnds[1] = op_src; + ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m1); + + MachNode *m2 = m1; + if (!Assembler::is_uimm((jlong)Klass::encode_klass((Klass *)op_src->constant()), 31)) { + // Value might be 1-extended. Mask out these bits. + m2 = new (C) clearMs32bNode(); + m2->add_req(NULL, m1); + m2->_opnds[0] = op_dst; + m2->_opnds[1] = op_dst; + ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m2); + } + + MachNode *m3 = new (C) loadConNKlass_loNode(); + m3->add_req(NULL, m2); + m3->_opnds[0] = op_dst; + m3->_opnds[1] = op_src; + m3->_opnds[2] = op_dst; + ra_->set_pair(m3->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); + nodes->push(m3); + %} +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($src$$constant & 0xFFFF))); + %} + 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) %{ + match(Set dst src); + effect(TEMP toc); + + ins_num_consts(1); + + format %{ "LD $dst, offset, $toc \t// load ptr $src from TOC" %} + size(4); + ins_encode( enc_load_long_constP(dst, src, toc) ); + ins_pipe(pipe_class_memory); +%} + +// Expand node for constant pool load: large offset. +instruct loadConP_hi(iRegPdst dst, immP_NM src, iRegLdst toc) %{ + effect(DEF dst, USE src, USE toc); + predicate(false); + + ins_num_consts(1); + ins_field_const_toc_offset(int); + + format %{ "ADDIS $dst, $toc, offset \t// load ptr $src from TOC (hi)" %} + size(4); + ins_encode( enc_load_long_constP_hi(dst, src, toc) ); + ins_pipe(pipe_class_default); +%} + +// Expand node for constant pool load: large offset. +instruct loadConP_lo(iRegPdst dst, immP_NM src, iRegLdst base) %{ + match(Set dst src); + effect(TEMP base); + + ins_field_const_toc_offset_hi_node(loadConP_hiNode*); + + format %{ "LD $dst, offset, $base \t// load ptr $src from TOC (lo)" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ld); + int offset = ra_->C->in_scratch_emit_size() ? 0 : _const_toc_offset_hi_node->_const_toc_offset; + __ ld($dst$$Register, MacroAssembler::largeoffset_si16_si16_lo(offset), $base$$Register); + %} + 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, postalloc expanded" %} + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfs); + address float_address = __ float_constant($src$$constant); + __ lfs($dst$$FloatRegister, __ offset_to_method_toc(float_address), $toc$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + FloatRegister Rdst = $dst$$FloatRegister; + Register Rtoc = $toc$$Register; + address float_address = __ float_constant($src$$constant); + int offset = __ offset_to_method_toc(float_address); + int hi = (offset + (1<<15))>>16; + int lo = offset - hi * (1<<16); + + __ addis(Rtoc, Rtoc, hi); + __ lfs(Rdst, lo, Rtoc); + __ addis(Rtoc, Rtoc, -hi); + %} + 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, postalloc expanded" %} + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfd); + int offset = __ offset_to_method_toc(__ double_constant($src$$constant)); + __ lfd($dst$$FloatRegister, offset, $toc$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + FloatRegister Rdst = $dst$$FloatRegister; + Register Rtoc = $toc$$Register; + address float_address = __ double_constant($src$$constant); + int offset = __ offset_to_method_toc(float_address); + int hi = (offset + (1<<15))>>16; + int lo = offset - hi * (1<<16); + + __ addis(Rtoc, Rtoc, hi); + __ lfd(Rdst, lo, Rtoc); + __ addis(Rtoc, Rtoc, -hi); + %} + 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, postalloc expanded" %} + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbt); + __ dcbt($src$$Register, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbt); + __ dcbt($mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + __ dcbtst($src$$Register, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + __ dcbtst($mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + __ dcbz($src$$Register, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + __ dcbz($mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + __ dcbtst($src$$Register, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_dcbtst); + __ dcbtst($mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_stb); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ stb($src$$Register, Idisp, $mem$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sth); + int Idisp = $mem$$disp + frame_slots_bias($mem$$base, ra_); + __ sth($src$$Register, Idisp, $mem$$base$$Register); + %} + 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 Compressed KLass +instruct storeNKlass(memory dst, iRegN_P2N src) %{ + match(Set dst (StoreNKlass dst src)); + ins_cost(MEMORY_REF_COST); + + format %{ "STW $src, $dst \t// compressed klass" %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ li(R0, 0); + //__ release(); // G1: oops are allowed to get visible after dirty marking + guarantee($mem$$base$$Register != R1_SP, "use frame_slots_bias"); + __ stb(R0, $mem$$disp, $mem$$base$$Register); + %} + ins_pipe(pipe_class_memory); +%} + +// Convert oop pointer into compressed form. + +// Nodes for postalloc 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ srdi($dst$$Register, $src$$Register, Universe::narrow_oop_shift() & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + __ subf($dst$$Register, R30, $src$$Register); + %} + 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 (InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 12 : 8); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + Label done; + __ beq($crx$$CondRegister, done); + __ subf($dst$$Register, R30, $src1$$Register); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + 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 %{ + // This is a Power7 instruction for which no machine description exists. + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// base != 0 +// 32G aligned narrow oop base. +instruct encodeP_32GAligned(iRegNdst dst, 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ rldicl($dst$$Register, $src$$Register, 64-Universe::narrow_oop_shift(), 32); + %} + 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// postalloc expanded" %} + postalloc_expand( postalloc_expand_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, postalloc expanded" %} + postalloc_expand( postalloc_expand_encode_oop_not_null(dst, src) ); +%} + +// shift != 0, base == 0 +// TODO: This is the same as encodeP_shift. Merge! +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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ srdi($dst$$Register, $src$$Register, Universe::narrow_oop_shift() & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ sldi($dst$$Register, $src$$Register, 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src$$Register, 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 (InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling()) */? 12 : 8); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + Label done; + __ beq($crx$$CondRegister, done); + __ add($dst$$Register, $src1$$Register, R30); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + 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 %{ + // This is a Power7 instruction for which no machine description exists. + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ isel_0($dst$$Register, $crx$$CondRegister, Assembler::equal, $src1$$Register); + %} + 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, postalloc expanded" %} + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ sldi($dst$$Register, $src$$Register, 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, postalloc expanded" %} + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// Convert klass pointer into compressed form. + +// Nodes for postalloc expand. + +// Shift node for expand. +instruct encodePKlass_shift(iRegNdst dst, iRegNsrc src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (EncodePKlass src)); + predicate(false); + + format %{ "SRDI $dst, $src, 3 \t// encode" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ srdi($dst$$Register, $src$$Register, Universe::narrow_klass_shift()); + %} + ins_pipe(pipe_class_default); +%} + +// Add node for expand. +instruct encodePKlass_sub_base(iRegPdst dst, iRegLsrc base, iRegPdst src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (EncodePKlass (Binary base src))); + predicate(false); + + format %{ "SUB $dst, $base, $src \t// encode" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + __ subf($dst$$Register, $base$$Register, $src$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// base != 0 +// 32G aligned narrow oop base. +instruct encodePKlass_32GAligned(iRegNdst dst, iRegPsrc src) %{ + match(Set dst (EncodePKlass src)); + predicate(false /* TODO: PPC port Universe::narrow_klass_base_disjoint()*/); + + format %{ "EXTRDI $dst, $src, #32, #3 \t// encode with 32G aligned base" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ rldicl($dst$$Register, $src$$Register, 64-Universe::narrow_oop_shift(), 32); + %} + ins_pipe(pipe_class_default); +%} + +// shift != 0, base != 0 +instruct encodePKlass_not_null_Ex(iRegNdst dst, iRegLsrc base, iRegPsrc src) %{ + match(Set dst (EncodePKlass (Binary base src))); + predicate(false); + + format %{ "EncodePKlass $dst, $src\t// $src != Null, postalloc expanded" %} + postalloc_expand %{ + encodePKlass_sub_baseNode *n1 = new (C) encodePKlass_sub_baseNode(); + n1->add_req(n_region, n_base, n_src); + n1->_opnds[0] = op_dst; + n1->_opnds[1] = op_base; + n1->_opnds[2] = op_src; + n1->_bottom_type = _bottom_type; + + encodePKlass_shiftNode *n2 = new (C) encodePKlass_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); + %} +%} + +// shift != 0, base != 0 +instruct encodePKlass_not_null_ExEx(iRegNdst dst, iRegPsrc src) %{ + match(Set dst (EncodePKlass src)); + //predicate(Universe::narrow_klass_shift() != 0 && + // true /* TODO: PPC port Universe::narrow_klass_base_overlaps()*/); + + //format %{ "EncodePKlass $dst, $src\t// $src != Null, postalloc expanded" %} + ins_cost(DEFAULT_COST*2); // Don't count constant. + expand %{ + immL baseImm %{ (jlong)(intptr_t)Universe::narrow_klass_base() %} + iRegLdst base; + loadConL_Ex(base, baseImm); + encodePKlass_not_null_Ex(dst, base, src); + %} +%} + +// Decode nodes. + +// Shift node for expand. +instruct decodeNKlass_shift(iRegPdst dst, iRegPsrc src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (DecodeNKlass src)); + predicate(false); + + format %{ "SLDI $dst, $src, #3 \t// DecodeNKlass" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ sldi($dst$$Register, $src$$Register, Universe::narrow_klass_shift()); + %} + ins_pipe(pipe_class_default); +%} + +// Add node for expand. + +instruct decodeNKlass_add_base(iRegPdst dst, iRegLsrc base, iRegPdst src) %{ + // The match rule is needed to make it a 'MachTypeNode'! + match(Set dst (DecodeNKlass (Binary base src))); + predicate(false); + + format %{ "ADD $dst, $base, $src \t// DecodeNKlass, add klass base" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $base$$Register, $src$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// src != 0, shift != 0, base != 0 +instruct decodeNKlass_notNull_addBase_Ex(iRegPdst dst, iRegLsrc base, iRegNsrc src) %{ + match(Set dst (DecodeNKlass (Binary base src))); + //effect(kill src); // We need a register for the immediate result after shifting. + predicate(false); + + format %{ "DecodeNKlass $dst = $base + ($src << 3) \t// $src != NULL, postalloc expanded" %} + postalloc_expand %{ + decodeNKlass_add_baseNode *n1 = new (C) decodeNKlass_add_baseNode(); + n1->add_req(n_region, n_base, n_src); + n1->_opnds[0] = op_dst; + n1->_opnds[1] = op_base; + n1->_opnds[2] = op_src; + n1->_bottom_type = _bottom_type; + + decodeNKlass_shiftNode *n2 = new (C) decodeNKlass_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); + %} +%} + +// src != 0, shift != 0, base != 0 +instruct decodeNKlass_notNull_addBase_ExEx(iRegPdst dst, iRegNsrc src) %{ + match(Set dst (DecodeNKlass src)); + // predicate(Universe::narrow_klass_shift() != 0 && + // Universe::narrow_klass_base() != 0); + + //format %{ "DecodeNKlass $dst, $src \t// $src != NULL, expanded" %} + + ins_cost(DEFAULT_COST*2); // Don't count constant. + expand %{ + // We add first, then we shift. Like this, we can get along with one register less. + // But we have to load the base pre-shifted. + immL baseImm %{ (jlong)((intptr_t)Universe::narrow_klass_base() >> Universe::narrow_klass_shift()) %} + iRegLdst base; + loadConL_Ex(base, baseImm); + decodeNKlass_notNull_addBase_Ex(dst, base, src); + %} +%} + +//----------MemBar Instructions----------------------------------------------- +// Memory barrier flavors + +instruct membar_acquire() %{ + match(LoadFence); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-acquire" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwsync); + __ 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); + match(StoreFence); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-release" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwsync); + __ release(); + %} + ins_pipe(pipe_class_default); +%} + +instruct membar_storestore() %{ + match(MemBarStoreStore); + ins_cost(4*MEMORY_REF_COST); + + format %{ "MEMBAR-store-store" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lwsync); + __ membar(Assembler::StoreStore); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sync); + __ fence(); + %} + 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); +%} + +//----------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); + + format %{ "CMOVE $cmp, $crx, $dst, $src\n\t" %} + size(4); + ins_encode %{ + // 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); + int cc = $cmp$$cmpcode; + __ isel($dst$$Register, $crx$$CondRegister, + (Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register); + %} + 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 %{ + // 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); + int cc = $cmp$$cmpcode; + __ isel($dst$$Register, $crx$$CondRegister, + (Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register); + %} + 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 %{ + // 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); + int cc = $cmp$$cmpcode; + __ isel($dst$$Register, $crx$$CondRegister, + (Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register); + %} + 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 %{ + // 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); + int cc = $cmp$$cmpcode; + __ isel($dst$$Register, $crx$$CondRegister, + (Assembler::Condition)(cc & 3), /*invert*/((~cc) & 8), $src$$Register); + %} + 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 (InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 12 : 8); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmovef); + Label done; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + // Branch if not (cmp crx). + __ bc(cc_to_inverse_boint($cmp$$cmpcode), cc_to_biint($cmp$$cmpcode, $crx$$reg), done); + __ fmr($dst$$FloatRegister, $src$$FloatRegister); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + 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 (InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 12 : 8); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmovef); + Label done; + assert((Assembler::bcondCRbiIs1 & ~Assembler::bcondCRbiIs0) == 8, "check encoding"); + // Branch if not (cmp crx). + __ bc(cc_to_inverse_boint($cmp$$cmpcode), cc_to_biint($cmp$$cmpcode, $crx$$reg), done); + __ fmr($dst$$FloatRegister, $src$$FloatRegister); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ cmpxchgd($crx$$CondRegister, R0, $oldVal$$Register, $newVal$$Register, $mem_ptr$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, NULL, true); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ cmpxchgd($crx$$CondRegister, R0, $oldVal$$Register, $newVal$$Register, $mem_ptr$$Register, + MacroAssembler::MemBarNone, MacroAssembler::cmpxchgx_hint_atomic_update(), + noreg, NULL, true); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + // CmpxchgX sets CCR0 to cmpX(src1, src2) and Rres to 'true'/'false'. + __ cmpxchgw(CCR0, R0, $src1$$Register, $src2$$Register, $mem_ptr$$Register, + MacroAssembler::MemBarFenceAfter, MacroAssembler::cmpxchgx_hint_atomic_update(), + $res$$Register, true); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + // CmpxchgX sets CCR0 to cmpX(src1, src2) and Rres to 'true'/'false'. + __ cmpxchgw(CCR0, R0, $src1$$Register, $src2$$Register, $mem_ptr$$Register, + MacroAssembler::MemBarFenceAfter, MacroAssembler::cmpxchgx_hint_atomic_update(), + $res$$Register, true); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + // CmpxchgX sets CCR0 to cmpX(src1, src2) and Rres to 'true'/'false'. + __ cmpxchgd(CCR0, R0, $src1$$Register, $src2$$Register, $mem_ptr$$Register, + MacroAssembler::MemBarFenceAfter, MacroAssembler::cmpxchgx_hint_atomic_update(), + $res$$Register, NULL, true); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + // CmpxchgX sets CCR0 to cmpX(src1, src2) and Rres to 'true'/'false'. + __ cmpxchgd(CCR0, R0, $src1$$Register, $src2$$Register, $mem_ptr$$Register, + MacroAssembler::MemBarFenceAfter, MacroAssembler::cmpxchgx_hint_atomic_update(), + $res$$Register, NULL, true); + %} + 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 + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ addi($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + __ addis($dst$$Register, $src1$$Register, ($src2$$constant)>>16); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src1$$Register, $src2$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// No constant pool entries required. +instruct addL_reg_imm16(iRegLdst dst, iRegLsrc src1, immL16 src2) %{ + match(Set dst (AddL src1 src2)); + + format %{ "ADDI $dst, $src1, $src2" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ addi($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + __ addis($dst$$Register, $src1$$Register, ($src2$$constant)>>16); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_add); + __ add($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ addi($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addis); + __ addis($dst$$Register, $src1$$Register, ($src2$$constant)>>16); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + __ subf($dst$$Register, $src2$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ addi($dst$$Register, $src1$$Register, ($src2$$constant) * (-1)); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subfic); + __ subfic($dst$$Register, $src2$$Register, $src1$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srawi); + __ srawi($dst$$Register, $src$$Register, 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_neg); + __ neg($dst$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + __ subf($dst$$Register, $src2$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + __ subf($dst$$Register, $src2$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ addi($dst$$Register, $src1$$Register, ($src2$$constant) * (-1)); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sradi); + __ sradi($dst$$Register, $src$$Register, 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_neg); + __ neg($dst$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_neg); + __ neg($dst$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mullw); + __ mullw($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulli); + __ mulli($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulld); + __ mulld($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulhd); + __ mulhd($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mulli); + __ mulli($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_neg); + __ neg($dst$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_divw); + __ divw($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 (InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 12 : 8); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + Label done; + __ bne($crx$$CondRegister, done); + __ neg($dst$$Register, $src1$$Register); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_neg); + __ neg($dst$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_divd); + __ divd($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 (InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 12 : 8); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + Label done; + __ bne($crx$$CondRegister, done); + __ neg($dst$$Register, $src1$$Register); + // TODO PPC port __ endgroup_if_needed(_size == 12); + __ bind(done); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src$$Register, $mask$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_slw); + __ slw($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + __ slwi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x1f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); // FIXME: assert that rlwinm is equal to addi + long src2 = $src2$$constant; + long src3 = $src3$$constant; + long maskbits = src3 + log2_long((jlong) (julong) (juint) -src2); + if (maskbits >= 32) { + __ li($dst$$Register, 0); // addi + } else { + __ rlwinm($dst$$Register, $src1$$Register, src3 & 0x1f, 0, (31-maskbits) & 0x1f); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); // FIXME: assert that rlwinm is equal to addi + long src2 = $src2$$constant; + long src3 = $src3$$constant; + long maskbits = src3 + log2_long((jlong) (julong) (juint) -src2); + if (maskbits >= 32) { + __ li($dst$$Register, 0); // addi + } else { + __ rlwinm($dst$$Register, $src1$$Register, 0, 0, (31-maskbits) & 0x1f); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sld); + __ sld($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ sldi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ sldi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldic); + __ clrlsldi($dst$$Register, $src1$$Register, 0x20, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sraw); + __ sraw($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srawi); + __ srawi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x1f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srad); + __ srad($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sradi); + __ sradi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_sradi); + __ sradi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srw); + __ srw($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + __ srwi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x1f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srd); + __ srd($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ srdi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ srdi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ srdi($dst$$Register, $src1$$Register, ($src2$$constant) & 0x3f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsw); + __ extsw($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + __ rotlwi($dst$$Register, $src$$Register, $lshift$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + __ rotrwi($dst$$Register, $src$$Register, $rshift$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fadds); + __ fadds($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fadd); + __ fadd($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsubs); + __ fsubs($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsub); + __ fsub($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmuls); + __ fmuls($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmul); + __ fmul($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fdivs); + __ fdivs($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fdiv); + __ fdiv($dst$$FloatRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fabs); + __ fabs($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fabs); + __ fabs($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fneg); + __ fneg($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fneg); + __ fneg($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fnabs); + __ fnabs($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fnabs); + __ fnabs($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsqrt); + __ fsqrt($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fsqrts); + __ fsqrts($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_and); + __ andr($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andi_); + // FIXME: avoid andi_ ? + __ andi_($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ clrrdi($dst$$Register, $src1$$Register, log2_long((jlong)(julong)(juint)-($src2$$constant))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src1$$Register, 64-log2_long((((jlong) $src2$$constant)+1))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + __ rlwinm($dst$$Register, $src1$$Register, 0, + (31-log2_long((jlong) $src2$$constant)) & 0x1f, (31-log2_long((jlong) $src2$$constant)) & 0x1f); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_and); + __ andr($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andi_); + // FIXME: avoid andi_ ? + __ andi_($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ clrrdi($dst$$Register, $src1$$Register, log2_long((jlong)-$src2$$constant)); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src1$$Register, 64-log2_long((((jlong) $src2$$constant)+1))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src1$$Register, 64-log2_long((((jlong) $src2$$constant)+1))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + __ ori($dst$$Register, $src1$$Register, ($src2$$constant) & 0xFFFF); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ or_unchecked($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_ori); + __ ori($dst$$Register, $src1$$Register, ($con$$constant) & 0xFFFF); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xor); + __ xorr($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xor); + __ xorr($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xori); + __ xori($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xor); + __ xorr($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xor); + __ xorr($dst$$Register, $src1$$Register, $src2$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// Immediate Xor Long +instruct xorL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2) %{ + match(Set dst (XorL src1 src2)); + ins_cost(DEFAULT_COST); + + format %{ "XORI $dst, $src1, $src2 \t// long" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_xori); + __ xori($dst$$Register, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_nor); + __ nor($dst$$Register, $src1$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_nor); + __ nor($dst$$Register, $src1$$Register, $src1$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andc); + __ andc($dst$$Register, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_lfs); + int Idisp = $src$$disp + frame_slots_bias($src$$base, ra_); + __ lfs($dst$$FloatRegister, Idisp, $src$$base$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwinm); + __ rlwinm($dst$$Register, $src$$Register, (32-log2_long((jlong)$mask$$constant)) & 0x1f, 31, 31); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_srawi); + __ srawi($dst$$Register, $src1$$Register, 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsb); + __ extsb($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsh); + __ extsh($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsw); + __ extsw($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_or); + __ mr_if_needed($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fctiwz);; + __ fctiwz($dst$$FloatRegister, $src$$FloatRegister); + %} + 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(InsertEndGroupPPC64 && 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 mem) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE mem); + predicate(false); + + format %{ "CmovI $dst, $crx, $mem \t// postalloc expanded" %} + postalloc_expand %{ + // + // 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); + %} +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fctiwz); + __ fctiwz($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_extsw); + __ extsw($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src$$Register, 32); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicl); + __ clrldi($dst$$Register, $src$$Register, 32); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fctiwz); + __ fctidz($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 mem) %{ + // no match-rule, false predicate + effect(DEF dst, USE crx, USE mem); + predicate(false); + + format %{ "CmovL $dst, $crx, $mem \t// postalloc expanded" %} + postalloc_expand %{ + // + // 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); + 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); + %} +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fctiwz); + __ fctidz($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcfid); + __ fcfid($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_frsp); + __ frsp($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcfid); + __ fcfids($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + __ fmr_if_needed($dst$$FloatRegister, $src$$FloatRegister); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmp); + __ cmpw($crx$$CondRegister, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpi); + __ cmpwi($crx$$CondRegister, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andi_); + // FIXME: avoid andi_ ? + __ andi_(R0, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmp); + __ cmpd($crx$$CondRegister, $src1$$Register, $src2$$Register); + %} + ins_pipe(pipe_class_compare); +%} + +instruct cmpL_reg_imm16(flagsReg crx, iRegLsrc src1, immL16 src2) %{ + match(Set crx (CmpL src1 src2)); + format %{ "CMPDI $crx, $src1, $src2" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpi); + __ cmpdi($crx$$CondRegister, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_and_); + __ and_(R0, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_andi_); + // FIXME: avoid andi_ ? + __ andi_(R0, $src1$$Register, $src2$$constant); + %} + 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 PORTInsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 20 : 16); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmove); + Label done; + // li(Rdst, 0); // equal -> 0 + __ beq($crx$$CondRegister, done); + __ li($dst$$Register, 1); // greater -> +1 + __ bgt($crx$$CondRegister, done); + __ li($dst$$Register, -1); // unordered or less -> -1 + // TODO: PPC port__ endgroup_if_needed(_size == 20); + __ bind(done); + %} + 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// postalloc expanded" %} + postalloc_expand %{ + // + // 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); + 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); + %} +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_twi); + if ($cmp$$cmpcode == 0x1 /* less_equal */) { + __ trap_range_check_le($src_length$$Register, $index$$constant); + } 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($src_length$$Register, $index$$constant); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tw); + if ($cmp$$cmpcode == 0x0 /* greater_equal */) { + __ trap_range_check_ge($src_index$$Register, $src_length$$Register); + } 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($src_index$$Register, $src_length$$Register); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_twi); + if ($cmp$$cmpcode == 0x0 /* greater_equal */) { + __ trap_range_check_ge($src_index$$Register, $length$$constant); + } 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($src_index$$Register, $length$$constant); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); + __ cmplw($crx$$CondRegister, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpli); + __ cmplwi($crx$$CondRegister, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tdi); + if ($cmp$$cmpcode == 0xA) { + __ trap_null_check($value$$Register); + } 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($value$$Register, Assembler::traptoGreaterThanUnsigned); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); + __ cmplw($crx$$CondRegister, $src1$$Register, $src2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpli); + __ cmplwi($crx$$CondRegister, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tdi); + if ($cmp$$cmpcode == 0xA) { + __ trap_null_check($value$$Register); + } 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($value$$Register, Assembler::traptoGreaterThanUnsigned); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpl); + __ cmpld($crx$$CondRegister, $src1$$Register, $src2$$Register); + %} + ins_pipe(pipe_class_compare); +%} + +// Used in postalloc expand. +instruct cmpP_reg_imm16(flagsReg crx, iRegPsrc src1, immL16 src2) %{ + // 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 src2)); + predicate(false); + + format %{ "CMPDI $crx, $src1, $src2" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmpi); + __ cmpdi($crx$$CondRegister, $src1$$Register, $src2$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcmpu); + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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(InsertEndGroupPPC64 && Compile::current()->do_hb_scheduling())*/ ? 16 : 12); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cmovecr); + Label done; + __ bns($crx$$CondRegister, done); // not unordered -> keep crx + __ li(R0, 0); + __ cmpwi($crx$$CondRegister, R0, 1); // unordered -> set crx to 'less' + // TODO PPC port __ endgroup_if_needed(_size == 16); + __ bind(done); + %} + 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// postalloc expanded" %} + postalloc_expand %{ + // + // 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); + 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); + %} +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fcmpu); + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + %} + 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// postalloc expanded" %} + postalloc_expand %{ + // + // 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); + 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); + %} +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_b); + Label d; // dummy + __ bind(d); + Label* p = $labl$$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); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ check_klass_subtype_slow_path($subklass$$Register, $superklass$$Register, $tmp_arrayptr$$Register, + $tmp_klass$$Register, NULL, $result$$Register); + %} + ins_pipe(pipe_class_default); +%} + +// inlined locking and unlocking + +instruct cmpFastLock(flagsReg crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3) %{ + match(Set crx (FastLock oop box)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + // TODO PPC port predicate(!UseNewFastLockPPC64 || UseBiasedLocking); + + format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2, $tmp3" %} + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ compiler_fast_lock_object($crx$$CondRegister, $oop$$Register, $box$$Register, + $tmp3$$Register, $tmp1$$Register, $tmp2$$Register); + // If locking was successfull, crx should indicate 'EQ'. + // The compiler generates a branch to the runtime call to + // _complete_monitor_locking_Java for the case where crx is 'NE'. + %} + ins_pipe(pipe_class_compare); +%} + +instruct cmpFastUnlock(flagsReg crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3) %{ + match(Set crx (FastUnlock oop box)); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); + + format %{ "FASTUNLOCK $oop, $box, $tmp1, $tmp2" %} + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ compiler_fast_unlock_object($crx$$CondRegister, $oop$$Register, $box$$Register, + $tmp3$$Register, $tmp1$$Register, $tmp2$$Register); + // If unlocking was successfull, crx should indicate 'EQ'. + // The compiler generates a branch to the runtime call to + // _complete_monitor_unlocking_Java for the case where crx is 'NE'. + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldicr); + __ clrrdi($dst$$Register, $src$$Register, log2_long((jlong)-$mask$$constant)); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_subf); + __ subf($dst$$Register, $start$$Register, $end$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ clear_memory_doubleword($base$$Register, $cnt$$Register); // kills cnt, base, R0 + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + immPOper *needleOper = (immPOper *)$needleImm; + const TypeOopPtr *t = needleOper->type()->isa_oopptr(); + ciTypeArray* needle_values = t->const_oop()->as_type_array(); // Pointer to live char * + + __ string_indexof_1($result$$Register, + $haystack$$Register, $haycnt$$Register, + R0, needle_values->char_at(0), + $tmp1$$Register, $tmp2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + 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); + } + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + 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, $needlecntImm$$constant, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ 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); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ char_arrays_equalsImm($str1$$Register, $str2$$Register, $cntImm$$constant, + $result$$Register, $tmp1$$Register, $tmp2$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ char_arrays_equals($str1$$Register, $str2$$Register, $cnt$$Register, $result$$Register, + $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, $tmp5$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ string_compare($str1$$Register, $str2$$Register, $cnt1$$Register, $cnt2$$Register, + $result$$Register, $tmp$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_popcntb); + __ popcntw($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_popcntb); + __ popcntd($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cntlzw); + __ cntlzw($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cntlzd); + __ cntlzd($dst$$Register, $src$$Register); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_cntlzd); + __ cntlzd($dst$$Register, $src$$Register); + %} + 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 pos, immI16 shift) %{ + effect(DEF dst, USE src, USE pos, USE shift); + predicate(false); + + format %{ "INSRWI $dst, $src, $pos, $shift" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwimi); + __ insrwi($dst$$Register, $src$$Register, $shift$$constant, $pos$$constant); + %} + ins_pipe(pipe_class_default); +%} + +// As insrwi_a, but with USE_DEF. +instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 pos, immI16 shift) %{ + effect(USE_DEF dst, USE src, USE pos, USE shift); + predicate(false); + + format %{ "INSRWI $dst, $src, $pos, $shift" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rlwimi); + __ insrwi($dst$$Register, $src$$Register, $shift$$constant, $pos$$constant); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldimi); + __ insrdi($dst$$Register, $dst$$Register, 32, 0); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldimi); + __ insrdi($dst$$Register, $dst$$Register, 48, 0); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_rldimi); + __ insrdi($dst$$Register, $dst$$Register, 56, 0); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($zero$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($src$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($zero$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($src$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($zero$$constant & 0xFFFF))); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, (int)((short)($src$$constant & 0xFFFF))); + %} + 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, postalloc expanded" %} + postalloc_expand( postalloc_expand_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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_addi); + __ li($dst$$Register, 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); +%} + +// 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 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 postalloc expand of CallDynamicJavaDirectSchedEx (actual call). +// Loading of IC was postalloc 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. postalloc expanded) version of call dynamic java. +// We use postalloc 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 postalloc 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_num_consts(2 /* 2 patchable constants: inline cache, call destination. */); + + format %{ "CALL,dynamic $meth \t// postalloc expanded" %} + postalloc_expand( postalloc_expand_java_dynamic_call_sched(meth, constanttablebase) ); +%} + +// Compound version of call dynamic java +// We use postalloc 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); + + format %{ "CALL,dynamic $meth \t// ==> " %} + ins_encode( enc_java_dynamic_call(meth, constanttablebase) ); + 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 postalloc expand of CallLeafDirect_Ex (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 postalloc expand of CallLeafDirect_Ex (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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_bctrl); + __ bctrl(); + %} + ins_pipe(pipe_class_call); +%} + +// postalloc 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); + + // Postalloc_expand_java_to_runtime_call needs up to 3 constants: call target, + // env for callee, C-toc. + ins_num_consts(3); + + format %{ "CALL,runtime leaf $meth \t// postalloc expanded" %} + postalloc_expand( postalloc_expand_java_to_runtime_call(meth, constanttablebase) ); +%} + +// Call runtime without safepoint - same as CallLeaf. +// postalloc 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); + + format %{ "CALL,runtime leaf nofp $meth \t// postalloc expanded" %} + postalloc_expand( postalloc_expand_java_to_runtime_call(meth, constanttablebase) ); +%} + +// 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ mtctr($jump_target$$Register); + __ bctr(); + %} + ins_pipe(pipe_class_call); +%} + +// Return Instruction +instruct Ret() %{ + match(Return); + format %{ "BLR \t// branch to link register" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_blr); + // LR is restored in MachEpilogNode. Just do the RET here. + __ blr(); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + __ ld(R4_ARG2/* issuing pc */, _abi(lr), R1_SP); + __ mtctr($jump_target$$Register); + __ bctr(); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_compound); + cbuf.set_insts_mark(); + __ b64_patchable((address)OptoRuntime::rethrow_stub(), relocInfo::runtime_call_type); + %} + ins_pipe(pipe_class_call); +%} + +// Die now. +instruct ShouldNotReachHere() %{ + match(Halt); + ins_cost(CALL_COST); + + format %{ "ShouldNotReachHere" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_tdi); + __ trap_should_not_reach_here(); + %} + 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_endgroup); + __ endgroup(); + %} + ins_pipe(pipe_class_default); +%} + +// Nop instructions + +instruct fxNop() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "fxNop" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + __ nop(); + %} + ins_pipe(pipe_class_default); +%} + +instruct fpNop0() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "fpNop0" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + __ fpnop0(); + %} + ins_pipe(pipe_class_default); +%} + +instruct fpNop1() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "fpNop1" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_fmr); + __ fpnop1(); + %} + ins_pipe(pipe_class_default); +%} + +instruct brNop0() %{ + ins_cost(0); + size(4); + format %{ "brNop0" %} + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mcrf); + __ 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 %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mcrf); + __ brnop1(); + %} + ins_pipe(pipe_class_default); +%} + +instruct brNop2() %{ + ins_cost(0); + + ins_is_nop(true); + + format %{ "brNop2" %} + size(4); + ins_encode %{ + // TODO: PPC port $archOpcode(ppc64Opcode_mcrf); + __ 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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/macroAssembler.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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,663 @@ +/* + * 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) +#define R23_method_handle AS_REGISTER(Register, R23) +#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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,133 @@ +/* + * 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 { + assert(type() == relocInfo::oop_type || type() == relocInfo::metadata_type, + "how to encode else?"); + narrowOop no = (type() == relocInfo::oop_type) ? + oopDesc::encode_heap_oop((oop)x) : Klass::encode_klass((Klass*)x); + nativeMovConstReg_at(addr())->set_narrow_oop(no, 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()); +} + +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) { +} + +void metadata_Relocation::pd_fix_value(address x) { +} diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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.inline.hpp" +#include "asm/macroAssembler.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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,3255 @@ +/* + * 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/macroAssembler.inline.hpp" +#include "code/debugInfoRec.hpp" +#include "code/icBuffer.hpp" +#include "code/vtableStubs.hpp" +#include "interpreter/interpreter.hpp" +#include "oops/compiledICHolder.hpp" +#include "prims/jvmtiRedefineClassesTrace.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/vframeArray.hpp" +#include "vmreg_ppc.inline.hpp" +#include "adfiles/ad_ppc_64.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 ":") + + +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: + case T_METADATA: + // 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 (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 LINUX_ONLY(+1)); + regs2[i].set1(reg2); + stk += inc_stk_for_intfloat; + } + + } else { + // Put float on stack. + reg = VMRegImpl::stack2reg(stk LINUX_ONLY(+1)); + 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 + +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 method. + __ ld(R12_scratch2, in_bytes(Method::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, CompiledICHolder::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 from inline cache, verified entry point needs it. + __ ld(R19_method, CompiledICHolder::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_klass(receiver_klass, 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(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 = -HandlerImpl::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 method. + __ get_vm_result_2(R19_method); + + __ 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,2117 @@ +/* + * 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/macroAssembler.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "nativeInst_ppc.hpp" +#include "oops/instanceOop.hpp" +#include "oops/method.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 +#include "runtime/thread.inline.hpp" + +#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 : Method + // 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 - Method + // 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. + __ reinit_heapbase(R30, R11_scratch1); + + // 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", 93); + 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(); + + if (UseAESIntrinsics) { + guarantee(!UseAESIntrinsics, "not yet implemented."); + } + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,1828 @@ +/* + * 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 "asm/macroAssembler.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/methodData.hpp" +#include "oops/method.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/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, size_t index_size) { + 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 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; + __ get_cache_and_index_at_bcp(cache, 1, index_size); + + // Big Endian (get least significant byte of 64 bit value): + __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()) + 7, cache); + __ sldi(size, size, Interpreter::logStackElementSize); + __ add(R15_esp, R15_esp, size); + __ dispatch_next(state, step); + 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 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 method 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; + if (ProfileInterpreter) { + const Register Rmdo = Rscratch1; + // If no method data exists, go to profile_continue. + __ ld(Rmdo, in_bytes(Method::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(MethodData::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 MethodCounters*. + const int mo_bc_offs = in_bytes(MethodCounters::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()); + __ bind(no_mdo); + __ get_method_counters(R19_method, R3_counters, done); + __ lwz(Rscratch2, mo_bc_offs, R3_counters); + __ addi(Rscratch2, Rscratch2, increment); + __ stw(Rscratch2, mo_bc_offs, R3_counters); + __ 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; + __ get_method_counters(R19_method, R3_counters, done); + __ increment_invocation_counter(R3_counters, Rsum_ivc_bec, R12_scratch2); + // Increment interpreter invocation counter. + if (ProfileInterpreter) { // %%% Merge this into methodDataOop. + __ lwz(R12_scratch2, in_bytes(MethodCounters::interpreter_invocation_counter_offset()), R3_counters); + __ addi(R12_scratch2, R12_scratch2, 1); + __ stw(R12_scratch2, in_bytes(MethodCounters::interpreter_invocation_counter_offset()), R3_counters); + } + // 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); + } + + __ bind(done); + } +} + +// 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(Method::const_offset()), R19_method); + __ ld(Robj_to_lock, in_bytes(ConstMethod::constants_offset()), Robj_to_lock); + __ ld(Robj_to_lock, ConstantPool::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 method. +// 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); + + __ ld(Rconst_method, method_(const)); + __ lhz(Rsize_of_parameters /* number of params */, + in_bytes(ConstMethod::size_of_parameters_offset()), Rconst_method); + 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! + assert(Rsize_of_locals == noreg, "Rsize_of_locals not initialized"); // Only relevant value is Rsize_of_parameters. + } else { + __ lhz(Rsize_of_locals /* number of params */, in_bytes(ConstMethod::size_of_locals_offset()), Rconst_method); + __ sldi(Rsize_of_parameters, Rsize_of_parameters, Interpreter::logStackElementSize); + __ sldi(Rsize_of_locals, Rsize_of_locals, Interpreter::logStackElementSize); + __ lhz(top_frame_size, in_bytes(ConstMethod::max_stack_offset()), Rconst_method); + __ 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); + __ ld(R27_constPoolCache, in_bytes(ConstMethod::constants_offset()), Rconst_method); + __ ld(R27_constPoolCache, ConstantPool::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(MethodData::data_offset())); + __ bind(zero_continue); + } + + if (native_call) { + __ li(R14_bcp, 0); // Must initialize. + } else { + __ add(R14_bcp, in_bytes(ConstMethod::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); + __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R11_scratch1); + __ 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) { + // Provide math entry with debugging on demand. + // Note: Debugging changes which code will get executed: + // Debugging or disabled InlineIntrinsics: java method will get interpreted and performs a native call. + // Not debugging and enabled InlineIntrinics: processor instruction will get used. + // Result might differ slightly due to rounding etc. + if (!InlineIntrinsics && (!FLAG_IS_ERGO(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)) { + 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(); + + // 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); + __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R12_scratch2); + __ 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 - method +// 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; + + // ----------------------------------------------------------------------------- + // 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, noreg /* 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. + assert(4 == sizeof(AccessFlags), "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); + __ ld(signature_handler_fd, 0, signature_handler_fd); + + __ call_stub(signature_handler_fd); + + // 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(Method::const_offset()), R19_method); + __ ld(R11_scratch1, in_bytes(ConstMethod::constants_offset()), R11_scratch1); + // pool_holder = method->constants()->pool_holder(); + __ ld(R11_scratch1/*pool_holder*/, ConstantPool::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); + __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, JavaThread::check_special_condition_for_native_trans), + relocInfo::none); + + __ 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(); + + // 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(Method::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(Method* 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; +} + +// Returns number of stackElementWords needed for the interpreter frame with the +// given sections. +// This overestimates the stack by one slot in case of alignments. +int AbstractInterpreter::size_activation(int max_stack, + int temps, + int extra_args, + int monitors, + int callee_params, + int callee_locals, + bool is_top_frame) { + // Note: This calculation must exactly parallel the frame setup + // in AbstractInterpreterGenerator::generate_method_entry. + assert(Interpreter::stackElementWords == 1, "sanity"); + const int max_alignment_space = StackAlignmentInBytes / Interpreter::stackElementSize; + const int abi_scratch = is_top_frame ? (frame::abi_reg_args_size / Interpreter::stackElementSize) : + (frame::abi_minframe_size / Interpreter::stackElementSize); + const int size = + max_stack + + (callee_locals - callee_params) + + monitors * frame::interpreter_frame_monitor_size() + + max_alignment_space + + abi_scratch + + frame::ijava_state_size / Interpreter::stackElementSize; + + // Fixed size of an interpreter frame, align to 16-byte. + return (size & -2); +} + +// Fills a sceletal interpreter frame generated during deoptimizations. +// +// Parameters: +// +// 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. +// +void AbstractInterpreter::layout_activation(Method* method, + int tempcount, + int popframe_extra_args, + int moncount, + int caller_actual_parameters, + int callee_param_count, + int callee_locals_count, + frame* caller, + frame* interpreter_frame, + bool is_top_frame, + bool is_bottom_frame) { + + const int abi_scratch = is_top_frame ? (frame::abi_reg_args_size / Interpreter::stackElementSize) : + (frame::abi_minframe_size / Interpreter::stackElementSize); + + 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_count + 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); + } +} + +// ============================================================================= +// 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. + __ ld(R4_ARG2, in_bytes(Method::const_offset()), R19_method); + __ lhz(R4_ARG2 /* number of params */, in_bytes(ConstMethod::size_of_parameters_offset()), R4_ARG2); + __ 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) { + case ltos: + case btos: + case ctos: + case stos: + case atos: + case itos: __ 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 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,4082 @@ +/* + * 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 "asm/macroAssembler.inline.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "interpreter/templateInterpreter.hpp" +#include "interpreter/templateTable.hpp" +#include "memory/universe.inline.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-> + +// ============================================================================ +// 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) { +#if INCLUDE_ALL_GCS + 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 // INCLUDE_ALL_GCS + 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 method flag. + if (!RewriteBytecodes) return; + Label L_patch_done; + + switch (new_bc) { + case Bytecodes::_fast_aputfield: + case Bytecodes::_fast_bputfield: + 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); + // Big Endian: ((*(cache+indices))>>((1+byte_no)*8))&0xFF + __ lbz(Rnew_bc, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (1 + byte_no), Rtemp); + __ 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, 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 = ConstantPool::header_size() * wordSize; + const int tags_offset = Array::base_offset_in_bytes(); + + // Get type from tags. + __ addi(Rscratch2, Rscratch2, tags_offset); + __ lbzx(Rscratch2, Rscratch2, Rscratch1); + + __ cmpwi(CCR0, Rscratch2, JVM_CONSTANT_UnresolvedClass); // Unresolved class? + __ 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); +#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); + + int index_size = wide ? sizeof(u2) : sizeof(u1); + const Register Rscratch = R11_scratch1; + Label resolved; + + // We are resolved if the resolved reference cache entry contains a + // non-null object (CallSite, etc.) + __ get_cache_index_at_bcp(Rscratch, 1, index_size); // Load index. + __ load_resolved_reference_at_index(R17_tos, Rscratch); + __ cmpdi(CCR0, R17_tos, 0); + __ bne(CCR0, resolved); + __ load_const_optimized(R3_ARG1, (int)bytecode()); + + address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc); + + // First time invocation - must resolve first. + __ call_VM(R17_tos, entry, R3_ARG1); + + __ align(32, 12); + __ bind(resolved); + __ verify_oop(R17_tos); +} + +void TemplateTable::ldc2_w() { + transition(vtos, vtos); + Label Llong, Lexit; + + Register Rindex = R11_scratch1, + Rcpool = R12_scratch2, + Rtag = R3_ARG1; + __ get_cpool_and_tags(Rcpool, Rtag); + __ get_2_byte_integer_at_bcp(1, Rindex, InterpreterMacroAssembler::Unsigned); + + const int base_offset = ConstantPool::header_size() * wordSize; + const int tags_offset = Array::base_offset_in_bytes(); + // 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 ConstantPool::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); + // tos: val + // Rarray: array ptr (popped by index_check) + __ index_check(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_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(Method::const_offset()), R19_method); + __ addi(Rscratch2, R14_bcp, -in_bytes(ConstMethod::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(Method::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(MethodData::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 method. + const int mo_bc_offs = in_bytes(MethodCounters::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset()); + __ bind(Lno_mdo); + __ lwz(Rscratch2, mo_bc_offs, R4_counters); + __ 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(R4_counters, 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(Method::const_offset()), R19_method); + __ add(R11_scratch1, R17_tos, R11_scratch1); + __ addi(R14_bcp, R11_scratch1, in_bytes(ConstMethod::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(Method::const_offset()), R19_method); + __ addi(Rscratch2, R17_tos, in_bytes(ConstMethod::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. + __ lwz(Rlow_byte, BytesPerInt, Rdef_offset_addr); + __ lwz(Rhigh_byte, BytesPerInt * 2, Rdef_offset_addr); + + // 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); + __ lwax(Roffset, Rdef_offset_addr, Rindex); + __ b(Ldispatch); + + __ bind(Ldefault_case); + __ profile_switch_default(Rhigh_byte, Rscratch1); + __ lwa(Roffset, 0, Rdef_offset_addr); + + __ 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. So on little endian machines, we have to revers offset and count and cmp value. +void TemplateTable::fast_linearswitch() { + transition(itos, vtos); + + Label Lloop_entry, Lsearch_loop, Lfound, 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. + __ lwz(Rcount, BytesPerInt, Rdef_offset_addr); // Load count. + __ addi(Rcurrent_pair, Rdef_offset_addr, 2 * BytesPerInt); // Rcurrent_pair now points to first pair. + + // Set up search loop. + __ cmpwi(CCR0, Rcount, 0); + __ beq(CCR0, Ldefault_case); + + __ mtctr(Rcount); + + // linear table search + __ bind(Lsearch_loop); + + __ lwz(Rvalue, 0, Rcurrent_pair); + __ lwa(Roffset, 1 * BytesPerInt, Rcurrent_pair); + + __ cmpw(CCR0, Rvalue, Rcmp_value); + __ beq(CCR0, Lfound); + + __ addi(Rcurrent_pair, Rcurrent_pair, 2 * BytesPerInt); + __ bdnz(Lsearch_loop); + + // default case + __ bind(Ldefault_case); + + __ lwa(Roffset, 0, Rdef_offset_addr); + if (ProfileInterpreter) { + __ profile_switch_default(Rdef_offset_addr, Rcount/* scratch */); + __ b(Lcontinue_execution); + } + + // Entry found, skip Roffset bytecodes and continue. + __ bind(Lfound); + if (ProfileInterpreter) { + // Calc the num of the pair we hit. Careful, Rcurrent_pair points 2 ints + // beyond the actual current pair due to the auto update load above! + __ sub(Rcurrent_pair, Rcurrent_pair, Rdef_offset_addr); + __ addi(Rcurrent_pair, Rcurrent_pair, - 2 * BytesPerInt); + __ srdi(Rcurrent_pair, Rcurrent_pair, LogBytesPerInt + 1); + __ 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); + __ lwz(Rj, -BytesPerInt, Rarray); + + // 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); + __ lwzx(Rscratch, Rscratch, Rarray); + + // 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); + __ lwz(Rscratch, 0, Ri); + + Label not_found; + // Ri = offset offset + __ cmpw(CCR0, Rkey, Rscratch); + __ beq(CCR0, not_found); + // entry not found -> j = default offset + __ lwz(Rj, -2 * BytesPerInt, Rarray); + __ b(default_case); + + __ bind(not_found); + // entry found -> j = offset + __ profile_switch_case(Rh, Rj, Rscratch, Rkey); + __ lwz(Rj, BytesPerInt, Ri); + + 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) { + case ltos: + case btos: + case ctos: + case stos: + case atos: + case itos: __ 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 Rcache, Register Rscratch, size_t index_size) { + + __ get_cache_and_index_at_bcp(Rcache, 1, index_size); + Label Lresolved, Ldone; + + 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. + // Big Endian: + __ lbz(Rscratch, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (byte_no + 1), Rcache); + // 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; + default : ShouldNotReachHere(); break; + } + __ li(R4_ARG2, (int)bytecode()); + __ call_VM(noreg, entry, R4_ARG2, true); + + // Update registers with resolved info. + __ get_cache_and_index_at_bcp(Rcache, 1, index_size); + __ 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 = ConstantPoolCache::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 = ConstantPoolCache::base_offset(); + // Determine constant pool cache field offsets. + assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant"); + 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); + } else { + resolve_cache_and_index(byte_no, Rcache, R0, is_invokedynamic ? sizeof(u4) : 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 = ConstantPoolCache::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 Lacquire, Lisync; + + 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, 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); + { + Label acquire_double; + __ beq(CCR6, acquire_double); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ bind(acquire_double); + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ beq_predict_taken(CCR0, Lisync); + __ b(Lisync); // 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); } + { + Label acquire_float; + __ beq(CCR6, acquire_float); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ bind(acquire_float); + __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync. + __ beq_predict_taken(CCR0, Lisync); + __ b(Lisync); // 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); + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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); + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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); + __ 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); + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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); + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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); + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ align(32, 12); + __ bind(Lacquire); + __ twi_0(R17_tos); + __ bind(Lisync); + __ isync(); // acquire + +#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 = ConstantPoolCache::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_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_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 Lvolatile; + + 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, 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ 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) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ align(32, 12); + __ bind(Lvolatile); + __ fence(); + } + // fallthru: __ b(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_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 LVolatile; + __ beq(CR_is_vol, LVolatile); + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + + __ align(32, 12); + __ bind(LVolatile); + __ fence(); + } +} + +void TemplateTable::fast_accessfield(TosState state) { + transition(atos, state); + + Label LisVolatile; + ByteSize cp_base_offset = ConstantPoolCache::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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode())); + + __ 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(); + } +} + +void TemplateTable::fast_xaccess(TosState state) { + transition(vtos, state); + + Label LisVolatile; + ByteSize cp_base_offset = ConstantPoolCache::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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode()) - 1); // Undo bcp increment. + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode()) - 1); // Undo bcp increment. + + __ 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); + __ dispatch_epilog(state, Bytecodes::length_for(bytecode()) - 1); // Undo bcp increment. + + __ 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(); + } + __ addi(R14_bcp, R14_bcp, -1); +} + +// ============================================================================ +// Calls + +// 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); + } +} + +// 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 method & 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::invoke_return_entry_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_klass(Rrecv_klass, Rrecv); + __ verify_klass_ptr(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 Rmethod, Register Rflags, Register Rscratch1, Register Rscratch2) { + + assert_different_registers(Rmethod, Rflags, Rscratch1, Rscratch2); + + // Load receiver from stack slot. + Register Rrecv = Rscratch2; + Register Rnum_params = Rrecv; + + __ ld(Rnum_params, in_bytes(Method::const_offset()), Rmethod); + __ lhz(Rnum_params /* number of params */, in_bytes(ConstMethod::size_of_parameters_offset()), Rnum_params); + + // 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::invoke_return_entry_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(Rmethod, 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_addr = R4_ARG2, + Rflags = R5_ARG3, + Rreceiver = R6_ARG4, + Rmethod = R31; + + prepare_invoke(byte_no, Rmethod, Rret_addr, noreg, Rreceiver, Rflags, R11_scratch1); + + // Receiver NULL check. + __ null_check_throw(Rreceiver, -1, R11_scratch1); + + __ profile_call(R11_scratch1, R12_scratch2); + __ 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_addr = R4_ARG2, + Rflags = R5_ARG3; + + prepare_invoke(byte_no, R19_method, Rret_addr, noreg, noreg, Rflags, R11_scratch1); + + __ profile_call(R11_scratch1, R12_scratch2); + __ 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 method. + __ 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; + + prepare_invoke(byte_no, Rinterface_klass, Rret_addr, Rindex, Rreceiver, Rflags, Rscratch1); + + // Get receiver klass. + __ null_check_throw(Rreceiver, oopDesc::klass_offset_in_bytes(), Rscratch3); + __ load_klass(Rrecv_klass, Rreceiver); + + // 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); +} + +void TemplateTable::invokedynamic(int byte_no) { + transition(vtos, vtos); + + const Register Rret_addr = R3_ARG1, + Rflags = R4_ARG2, + Rmethod = R22_tmp2, + 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_invoke(byte_no, Rmethod, Rret_addr, Rscratch1, noreg, Rflags, Rscratch2); + + // 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(Rmethod, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */); +} + +void TemplateTable::invokehandle(int byte_no) { + transition(vtos, vtos); + + const Register Rret_addr = R3_ARG1, + Rflags = R4_ARG2, + Rrecv = R5_ARG3, + Rmethod = R22_tmp2, + Rscratch1 = R11_scratch1, + Rscratch2 = R12_scratch2; + + if (!EnableInvokeDynamic) { + // Rewriter does not generate this bytecode. + __ should_not_reach_here(); + return; + } + + prepare_invoke(byte_no, Rmethod, Rret_addr, Rscratch1, Rrecv, Rflags, Rscratch2); + __ verify_method_ptr(Rmethod); + __ 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; + + const bool allow_shared_alloc = Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode; + + // -------------------------------------------------------------------------- + // Check if fast case is possible. + + // Load pointers to const pool and const pool's tags array. + __ get_cpool_and_tags(Rcpool, Rtags); + // Load index of constant pool entry. + __ get_2_byte_integer_at_bcp(1, Rindex, InterpreterMacroAssembler::Unsigned); + + if (UseTLAB) { + // 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 ConstantPoolCache::klass_at_put). + __ addi(Rtags, Rtags, Array::base_offset_in_bytes()); + __ lbzx(Rtags, Rindex, Rtags); + + __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class); + __ bne(CCR0, Lslow_case); + + // Get instanceKlass (load from Rcpool + sizeof(ConstantPool) + Rindex*BytesPerWord). + __ sldi(Roffset, Rindex, LogBytesPerWord); + __ addi(Rscratch, Rcpool, sizeof(ConstantPool)); + __ isync(); // Order load of instance Klass wrt. tags. + __ ldx(RinstanceKlass, Roffset, Rscratch); + + // 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(CCR1, Rscratch, InstanceKlass::fully_initialized); + // Make sure klass does not have has_finalizer, or is abstract, or interface or java/lang/Class. + __ andi_(R0, Rinstance_size, Klass::_lh_instance_slow_path_bit); // slow path bit equals 0? + + __ crnand(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); // slow path bit set or not fully initialized? + __ beq(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.). + + 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. + __ store_klass_gap(RallocatedObject); + __ store_klass(RallocatedObject, RinstanceKlass, Rscratch); // 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, Array::base_offset_in_bytes()); + __ lbzx(Rtags, Rtags, Roffset); + + __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class); + __ beq(CCR0, Lquicked); + + // Call into the VM to "quicken" instanceof. + __ push_ptr(); // for GC + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); + __ get_vm_result_2(RspecifiedKlass); + __ pop_ptr(); // Restore receiver. + __ b(Lresolved); + + // Extract target class from constant pool. + __ bind(Lquicked); + __ sldi(Roffset, Roffset, LogBytesPerWord); + __ addi(Rcpool, Rcpool, sizeof(ConstantPool)); + __ isync(); // Order load of specified Klass wrt. tags. + __ 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, Array::base_offset_in_bytes()); + __ lbzx(Rtags, Rtags, Roffset); + + __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class); + __ beq(CCR0, Lquicked); + + // Call into the VM to "quicken" instanceof. + __ push_ptr(); // for GC + call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc)); + __ get_vm_result_2(RspecifiedKlass); + __ pop_ptr(); // Restore receiver. + __ b(Lresolved); + + // Extract target class from constant pool. + __ bind(Lquicked); + __ sldi(Roffset, Roffset, LogBytesPerWord); + __ addi(Rcpool, Rcpool, sizeof(ConstantPool)); + __ isync(); // Order load of specified Klass wrt. tags. + __ 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 prepare_invoke(int byte_no, Register Rmethod, Register Rret_addr, Register Rindex, Register Rrecv, Register Rflags, Register Rscratch); + static void invokevfinal_helper(Register Rmethod, 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); + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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) + +#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) + +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) + +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) + +#endif // CPU_PPC_VM_VMSTRUCTS_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,488 @@ +/* + * Copyright (c) 1997, 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. + * + */ + +#include "precompiled.hpp" +#include "asm/assembler.inline.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.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); + FLAG_SET_ERGO(bool, TrapBasedNotEntrantChecks, false); + FLAG_SET_ERGO(bool, TrapBasedNullChecks, false); + FLAG_SET_ERGO(bool, TrapBasedICMissChecks, false); + } + +#ifdef COMPILER2 + if (!UseSIGTRAP) { + MSG(TrapBasedRangeChecks); + FLAG_SET_ERGO(bool, TrapBasedRangeChecks, false); + } + + // 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(), (int) 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; + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,268 @@ +/* + * 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/macroAssembler.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_klass_with_trap_null_check(rcvr_klass, R3); + + // Set method (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(Method::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_klass_with_trap_null_check(rcvr_klass, 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 method 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("method 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(Method::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(Method::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 { + int decode_klass_size = MacroAssembler::instr_size_for_decode_klass_not_null(); + if (is_vtable_stub) { + return 20 + decode_klass_size + 8 + 8; // Plain + cOops + Traps + safety + } else { + return 96 + decode_klass_size + 12 + 8; // Plain + cOops + Traps + safety + } + } +} + +int VtableStub::pd_code_alignment() { + const unsigned int icache_line_size = 32; + return icache_line_size; +} diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/assembler_sparc.hpp --- a/src/cpu/sparc/vm/assembler_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/assembler_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -88,6 +88,7 @@ orncc_op3 = 0x16, xnorcc_op3 = 0x17, addccc_op3 = 0x18, + aes4_op3 = 0x19, umulcc_op3 = 0x1a, smulcc_op3 = 0x1b, subccc_op3 = 0x1c, @@ -121,7 +122,14 @@ fpop1_op3 = 0x34, fpop2_op3 = 0x35, impdep1_op3 = 0x36, + aes3_op3 = 0x36, + alignaddr_op3 = 0x36, + faligndata_op3 = 0x36, + flog3_op3 = 0x36, + edge_op3 = 0x36, + fsrc_op3 = 0x36, impdep2_op3 = 0x37, + stpartialf_op3 = 0x37, jmpl_op3 = 0x38, rett_op3 = 0x39, trap_op3 = 0x3a, @@ -172,41 +180,62 @@ enum opfs { // selected opfs - fmovs_opf = 0x01, - fmovd_opf = 0x02, + edge8n_opf = 0x01, + + fmovs_opf = 0x01, + fmovd_opf = 0x02, - fnegs_opf = 0x05, - fnegd_opf = 0x06, + fnegs_opf = 0x05, + fnegd_opf = 0x06, + + alignaddr_opf = 0x18, - fadds_opf = 0x41, - faddd_opf = 0x42, - fsubs_opf = 0x45, - fsubd_opf = 0x46, + fadds_opf = 0x41, + faddd_opf = 0x42, + fsubs_opf = 0x45, + fsubd_opf = 0x46, + + faligndata_opf = 0x48, - fmuls_opf = 0x49, - fmuld_opf = 0x4a, - fdivs_opf = 0x4d, - fdivd_opf = 0x4e, + fmuls_opf = 0x49, + fmuld_opf = 0x4a, + fdivs_opf = 0x4d, + fdivd_opf = 0x4e, + + fcmps_opf = 0x51, + fcmpd_opf = 0x52, - fcmps_opf = 0x51, - fcmpd_opf = 0x52, + fstox_opf = 0x81, + fdtox_opf = 0x82, + fxtos_opf = 0x84, + fxtod_opf = 0x88, + fitos_opf = 0xc4, + fdtos_opf = 0xc6, + fitod_opf = 0xc8, + fstod_opf = 0xc9, + fstoi_opf = 0xd1, + fdtoi_opf = 0xd2, - fstox_opf = 0x81, - fdtox_opf = 0x82, - fxtos_opf = 0x84, - fxtod_opf = 0x88, - fitos_opf = 0xc4, - fdtos_opf = 0xc6, - fitod_opf = 0xc8, - fstod_opf = 0xc9, - fstoi_opf = 0xd1, - fdtoi_opf = 0xd2, + mdtox_opf = 0x110, + mstouw_opf = 0x111, + mstosw_opf = 0x113, + mxtod_opf = 0x118, + mwtos_opf = 0x119, + + aes_kexpand0_opf = 0x130, + aes_kexpand2_opf = 0x131 + }; - mdtox_opf = 0x110, - mstouw_opf = 0x111, - mstosw_opf = 0x113, - mxtod_opf = 0x118, - mwtos_opf = 0x119 + enum op5s { + aes_eround01_op5 = 0x00, + aes_eround23_op5 = 0x01, + aes_dround01_op5 = 0x02, + aes_dround23_op5 = 0x03, + aes_eround01_l_op5 = 0x04, + aes_eround23_l_op5 = 0x05, + aes_dround01_l_op5 = 0x06, + aes_dround23_l_op5 = 0x07, + aes_kexpand1_op5 = 0x08 }; enum RCondition { rc_z = 1, rc_lez = 2, rc_lz = 3, rc_nz = 5, rc_gz = 6, rc_gez = 7, rc_last = rc_gez }; @@ -330,6 +359,8 @@ ASI_PRIMARY = 0x80, ASI_PRIMARY_NOFAULT = 0x82, ASI_PRIMARY_LITTLE = 0x88, + // 8x8-bit partial store + ASI_PST8_PRIMARY = 0xC0, // Block initializing store ASI_ST_BLKINIT_PRIMARY = 0xE2, // Most-Recently-Used (MRU) BIS variant @@ -427,6 +458,7 @@ static int immed( bool i) { return u_field(i ? 1 : 0, 13, 13); } static int opf_low6( int w) { return u_field(w, 10, 5); } static int opf_low5( int w) { return u_field(w, 9, 5); } + static int op5( int x) { return u_field(x, 8, 5); } static int trapcc( CC cc) { return u_field(cc, 12, 11); } static int sx( int i) { return u_field(i, 12, 12); } // shift x=1 means 64-bit static int opf( int x) { return u_field(x, 13, 5); } @@ -451,6 +483,7 @@ static int fd( FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 29, 25); }; static int fs1(FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 18, 14); }; static int fs2(FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 4, 0); }; + static int fs3(FloatRegister r, FloatRegisterImpl::Width fwa) { return u_field(r->encoding(fwa), 13, 9); }; // some float instructions use this encoding on the op3 field static int alt_op3(int op, FloatRegisterImpl::Width w) { @@ -559,6 +592,15 @@ return x & ((1 << 10) - 1); } + // AES crypto instructions supported only on certain processors + static void aes_only() { assert( VM_Version::has_aes(), "This instruction only works on SPARC with AES instructions support"); } + + // instruction only in VIS1 + static void vis1_only() { assert( VM_Version::has_vis1(), "This instruction only works on SPARC with VIS1"); } + + // instruction only in VIS2 + static void vis2_only() { assert( VM_Version::has_vis2(), "This instruction only works on SPARC with VIS2"); } + // instruction only in VIS3 static void vis3_only() { assert( VM_Version::has_vis3(), "This instruction only works on SPARC with VIS3"); } @@ -604,11 +646,20 @@ } protected: + // Insert a nop if the previous is cbcond + void insert_nop_after_cbcond() { + if (UseCBCond && cbcond_before()) { + nop(); + } + } // Delay slot helpers // cti is called when emitting control-transfer instruction, // BEFORE doing the emitting. // Only effective when assertion-checking is enabled. void cti() { + // A cbcond instruction immediately followed by a CTI + // instruction introduces pipeline stalls, we need to avoid that. + no_cbcond_before(); #ifdef CHECK_DELAY assert_not_delayed("cti should not be in delay slot"); #endif @@ -632,7 +683,6 @@ void no_cbcond_before() { assert(offset() == 0 || !cbcond_before(), "cbcond should not follow an other cbcond"); } - public: bool use_cbcond(Label& L) { @@ -682,6 +732,24 @@ void addccc( Register s1, int simm13a, Register d ) { emit_int32( op(arith_op) | rd(d) | op3(addc_op3 | cc_bit_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } + // 4-operand AES instructions + + void aes_eround01( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_eround01_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_eround23( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_eround23_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_dround01( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_dround01_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_dround23( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_dround23_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_eround01_l( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_eround01_l_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_eround23_l( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_eround23_l_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_dround01_l( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_dround01_l_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_dround23_l( FloatRegister s1, FloatRegister s2, FloatRegister s3, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | fs3(s3, FloatRegisterImpl::D) | op5(aes_dround23_l_op5) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_kexpand1( FloatRegister s1, FloatRegister s2, int imm5a, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes4_op3) | fs1(s1, FloatRegisterImpl::D) | u_field(imm5a, 13, 9) | op5(aes_kexpand1_op5) | fs2(s2, FloatRegisterImpl::D) ); } + + + // 3-operand AES instructions + + void aes_kexpand0( FloatRegister s1, FloatRegister s2, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes3_op3) | fs1(s1, FloatRegisterImpl::D) | opf(aes_kexpand0_opf) | fs2(s2, FloatRegisterImpl::D) ); } + void aes_kexpand2( FloatRegister s1, FloatRegister s2, FloatRegister d ) { aes_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(aes3_op3) | fs1(s1, FloatRegisterImpl::D) | opf(aes_kexpand2_opf) | fs2(s2, FloatRegisterImpl::D) ); } + // pp 136 inline void bpr(RCondition c, bool a, Predict p, Register s1, address d, relocInfo::relocType rt = relocInfo::none); @@ -784,6 +852,10 @@ void fmul( FloatRegisterImpl::Width sw, FloatRegisterImpl::Width dw, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_int32( op(arith_op) | fd(d, dw) | op3(fpop1_op3) | fs1(s1, sw) | opf(0x60 + sw + dw*4) | fs2(s2, sw)); } void fdiv( FloatRegisterImpl::Width w, FloatRegister s1, FloatRegister s2, FloatRegister d ) { emit_int32( op(arith_op) | fd(d, w) | op3(fpop1_op3) | fs1(s1, w) | opf(0x4c + w) | fs2(s2, w)); } + // FXORs/FXORd instructions + + void fxor( FloatRegisterImpl::Width w, FloatRegister s1, FloatRegister s2, FloatRegister d ) { vis1_only(); emit_int32( op(arith_op) | fd(d, w) | op3(flog3_op3) | fs1(s1, w) | opf(0x6E - w) | fs2(s2, w)); } + // pp 164 void fsqrt( FloatRegisterImpl::Width w, FloatRegister s, FloatRegister d ) { emit_int32( op(arith_op) | fd(d, w) | op3(fpop1_op3) | opf(0x28 + w) | fs2(s, w)); } @@ -1108,6 +1180,20 @@ inline void wrfprs( Register d) { v9_only(); emit_int32( op(arith_op) | rs1(d) | op3(wrreg_op3) | u_field(6, 29, 25)); } + // VIS1 instructions + + void alignaddr( Register s1, Register s2, Register d ) { vis1_only(); emit_int32( op(arith_op) | rd(d) | op3(alignaddr_op3) | rs1(s1) | opf(alignaddr_opf) | rs2(s2)); } + + void faligndata( FloatRegister s1, FloatRegister s2, FloatRegister d ) { vis1_only(); emit_int32( op(arith_op) | fd(d, FloatRegisterImpl::D) | op3(faligndata_op3) | fs1(s1, FloatRegisterImpl::D) | opf(faligndata_opf) | fs2(s2, FloatRegisterImpl::D)); } + + void fsrc2( FloatRegisterImpl::Width w, FloatRegister s2, FloatRegister d ) { vis1_only(); emit_int32( op(arith_op) | fd(d, w) | op3(fsrc_op3) | opf(0x7A - w) | fs2(s2, w)); } + + void stpartialf( Register s1, Register s2, FloatRegister d, int ia = -1 ) { vis1_only(); emit_int32( op(ldst_op) | fd(d, FloatRegisterImpl::D) | op3(stpartialf_op3) | rs1(s1) | imm_asi(ia) | rs2(s2)); } + + // VIS2 instructions + + void edge8n( Register s1, Register s2, Register d ) { vis2_only(); emit_int32( op(arith_op) | rd(d) | op3(edge_op3) | rs1(s1) | opf(edge8n_opf) | rs2(s2)); } + // VIS3 instructions void movstosw( FloatRegister s, Register d ) { vis3_only(); emit_int32( op(arith_op) | rd(d) | op3(mftoi_op3) | opf(mstosw_opf) | fs2(s, FloatRegisterImpl::S)); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/assembler_sparc.inline.hpp --- a/src/cpu/sparc/vm/assembler_sparc.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/assembler_sparc.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -54,33 +54,33 @@ inline void Assembler::add(Register s1, Register s2, Register d ) { emit_int32( op(arith_op) | rd(d) | op3(add_op3) | rs1(s1) | rs2(s2) ); } inline void Assembler::add(Register s1, int simm13a, Register d ) { emit_int32( op(arith_op) | rd(d) | op3(add_op3) | rs1(s1) | immed(true) | simm(simm13a, 13) ); } -inline void Assembler::bpr( RCondition c, bool a, Predict p, Register s1, address d, relocInfo::relocType rt ) { v9_only(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(bpr_op2) | wdisp16(intptr_t(d), intptr_t(pc())) | predict(p) | rs1(s1), rt); has_delay_slot(); } -inline void Assembler::bpr( RCondition c, bool a, Predict p, Register s1, Label& L) { bpr( c, a, p, s1, target(L)); } +inline void Assembler::bpr( RCondition c, bool a, Predict p, Register s1, address d, relocInfo::relocType rt ) { v9_only(); insert_nop_after_cbcond(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(bpr_op2) | wdisp16(intptr_t(d), intptr_t(pc())) | predict(p) | rs1(s1), rt); has_delay_slot(); } +inline void Assembler::bpr( RCondition c, bool a, Predict p, Register s1, Label& L) { insert_nop_after_cbcond(); bpr( c, a, p, s1, target(L)); } -inline void Assembler::fb( Condition c, bool a, address d, relocInfo::relocType rt ) { v9_dep(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(fb_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } -inline void Assembler::fb( Condition c, bool a, Label& L ) { fb(c, a, target(L)); } +inline void Assembler::fb( Condition c, bool a, address d, relocInfo::relocType rt ) { v9_dep(); insert_nop_after_cbcond(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(fb_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } +inline void Assembler::fb( Condition c, bool a, Label& L ) { insert_nop_after_cbcond(); fb(c, a, target(L)); } -inline void Assembler::fbp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { v9_only(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(fbp_op2) | branchcc(cc) | predict(p) | wdisp(intptr_t(d), intptr_t(pc()), 19), rt); has_delay_slot(); } -inline void Assembler::fbp( Condition c, bool a, CC cc, Predict p, Label& L ) { fbp(c, a, cc, p, target(L)); } +inline void Assembler::fbp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { v9_only(); insert_nop_after_cbcond(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(fbp_op2) | branchcc(cc) | predict(p) | wdisp(intptr_t(d), intptr_t(pc()), 19), rt); has_delay_slot(); } +inline void Assembler::fbp( Condition c, bool a, CC cc, Predict p, Label& L ) { insert_nop_after_cbcond(); fbp(c, a, cc, p, target(L)); } -inline void Assembler::br( Condition c, bool a, address d, relocInfo::relocType rt ) { v9_dep(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(br_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } -inline void Assembler::br( Condition c, bool a, Label& L ) { br(c, a, target(L)); } +inline void Assembler::br( Condition c, bool a, address d, relocInfo::relocType rt ) { v9_dep(); insert_nop_after_cbcond(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(br_op2) | wdisp(intptr_t(d), intptr_t(pc()), 22), rt); has_delay_slot(); } +inline void Assembler::br( Condition c, bool a, Label& L ) { insert_nop_after_cbcond(); br(c, a, target(L)); } -inline void Assembler::bp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { v9_only(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(bp_op2) | branchcc(cc) | predict(p) | wdisp(intptr_t(d), intptr_t(pc()), 19), rt); has_delay_slot(); } -inline void Assembler::bp( Condition c, bool a, CC cc, Predict p, Label& L ) { bp(c, a, cc, p, target(L)); } +inline void Assembler::bp( Condition c, bool a, CC cc, Predict p, address d, relocInfo::relocType rt ) { v9_only(); insert_nop_after_cbcond(); cti(); emit_data( op(branch_op) | annul(a) | cond(c) | op2(bp_op2) | branchcc(cc) | predict(p) | wdisp(intptr_t(d), intptr_t(pc()), 19), rt); has_delay_slot(); } +inline void Assembler::bp( Condition c, bool a, CC cc, Predict p, Label& L ) { insert_nop_after_cbcond(); bp(c, a, cc, p, target(L)); } // compare and branch inline void Assembler::cbcond(Condition c, CC cc, Register s1, Register s2, Label& L) { cti(); no_cbcond_before(); emit_data(op(branch_op) | cond_cbcond(c) | op2(bpr_op2) | branchcc(cc) | wdisp10(intptr_t(target(L)), intptr_t(pc())) | rs1(s1) | rs2(s2)); } inline void Assembler::cbcond(Condition c, CC cc, Register s1, int simm5, Label& L) { cti(); no_cbcond_before(); emit_data(op(branch_op) | cond_cbcond(c) | op2(bpr_op2) | branchcc(cc) | wdisp10(intptr_t(target(L)), intptr_t(pc())) | rs1(s1) | immed(true) | simm(simm5, 5)); } -inline void Assembler::call( address d, relocInfo::relocType rt ) { cti(); emit_data( op(call_op) | wdisp(intptr_t(d), intptr_t(pc()), 30), rt); has_delay_slot(); assert(rt != relocInfo::virtual_call_type, "must use virtual_call_Relocation::spec"); } -inline void Assembler::call( Label& L, relocInfo::relocType rt ) { call( target(L), rt); } +inline void Assembler::call( address d, relocInfo::relocType rt ) { insert_nop_after_cbcond(); cti(); emit_data( op(call_op) | wdisp(intptr_t(d), intptr_t(pc()), 30), rt); has_delay_slot(); assert(rt != relocInfo::virtual_call_type, "must use virtual_call_Relocation::spec"); } +inline void Assembler::call( Label& L, relocInfo::relocType rt ) { insert_nop_after_cbcond(); call( target(L), rt); } inline void Assembler::flush( Register s1, Register s2) { emit_int32( op(arith_op) | op3(flush_op3) | rs1(s1) | rs2(s2)); } inline void Assembler::flush( Register s1, int simm13a) { emit_data( op(arith_op) | op3(flush_op3) | rs1(s1) | immed(true) | simm(simm13a, 13)); } -inline void Assembler::jmpl( Register s1, Register s2, Register d ) { cti(); emit_int32( op(arith_op) | rd(d) | op3(jmpl_op3) | rs1(s1) | rs2(s2)); has_delay_slot(); } -inline void Assembler::jmpl( Register s1, int simm13a, Register d, RelocationHolder const& rspec ) { cti(); emit_data( op(arith_op) | rd(d) | op3(jmpl_op3) | rs1(s1) | immed(true) | simm(simm13a, 13), rspec); has_delay_slot(); } +inline void Assembler::jmpl( Register s1, Register s2, Register d ) { insert_nop_after_cbcond(); cti(); emit_int32( op(arith_op) | rd(d) | op3(jmpl_op3) | rs1(s1) | rs2(s2)); has_delay_slot(); } +inline void Assembler::jmpl( Register s1, int simm13a, Register d, RelocationHolder const& rspec ) { insert_nop_after_cbcond(); cti(); emit_data( op(arith_op) | rd(d) | op3(jmpl_op3) | rs1(s1) | immed(true) | simm(simm13a, 13), rspec); has_delay_slot(); } inline void Assembler::ldf(FloatRegisterImpl::Width w, Register s1, Register s2, FloatRegister d) { emit_int32( op(ldst_op) | fd(d, w) | alt_op3(ldf_op3, w) | rs1(s1) | rs2(s2) ); } inline void Assembler::ldf(FloatRegisterImpl::Width w, Register s1, int simm13a, FloatRegister d, RelocationHolder const& rspec) { emit_data( op(ldst_op) | fd(d, w) | alt_op3(ldf_op3, w) | rs1(s1) | immed(true) | simm(simm13a, 13), rspec); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/bytecodeInterpreter_sparc.hpp --- a/src/cpu/sparc/vm/bytecodeInterpreter_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/bytecodeInterpreter_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -83,7 +83,7 @@ #define LOCALS_ADDR(offset) ((address)locals[-(offset)]) #define LOCALS_INT(offset) (*((jint*)&locals[-(offset)])) #define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) -#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) +#define LOCALS_OBJECT(offset) (cast_to_oop(locals[-(offset)])) #define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) #define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) #define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp --- a/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -152,7 +152,7 @@ } -int LIR_Assembler::initial_frame_size_in_bytes() { +int LIR_Assembler::initial_frame_size_in_bytes() const { return in_bytes(frame_map()->framesize_in_bytes()); } @@ -182,7 +182,7 @@ int number_of_locks = entry_state->locks_size(); // Create a frame for the compiled activation. - __ build_frame(initial_frame_size_in_bytes()); + __ build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes()); // OSR buffer is // @@ -1315,7 +1315,7 @@ } Address LIR_Assembler::as_Address(LIR_Address* addr) { - Register reg = addr->base()->as_register(); + Register reg = addr->base()->as_pointer_register(); LIR_Opr index = addr->index(); if (index->is_illegal()) { return Address(reg, addr->disp()); @@ -3100,7 +3100,145 @@ } void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) { - fatal("Type profiling not implemented on this platform"); + Register obj = op->obj()->as_register(); + Register tmp1 = op->tmp()->as_pointer_register(); + Register tmp2 = G1; + Address mdo_addr = as_Address(op->mdp()->as_address_ptr()); + ciKlass* exact_klass = op->exact_klass(); + intptr_t current_klass = op->current_klass(); + bool not_null = op->not_null(); + bool no_conflict = op->no_conflict(); + + Label update, next, none; + + bool do_null = !not_null; + bool exact_klass_set = exact_klass != NULL && ciTypeEntries::valid_ciklass(current_klass) == exact_klass; + bool do_update = !TypeEntries::is_type_unknown(current_klass) && !exact_klass_set; + + assert(do_null || do_update, "why are we here?"); + assert(!TypeEntries::was_null_seen(current_klass) || do_update, "why are we here?"); + + __ verify_oop(obj); + + if (tmp1 != obj) { + __ mov(obj, tmp1); + } + if (do_null) { + __ br_notnull_short(tmp1, Assembler::pt, update); + if (!TypeEntries::was_null_seen(current_klass)) { + __ ld_ptr(mdo_addr, tmp1); + __ or3(tmp1, TypeEntries::null_seen, tmp1); + __ st_ptr(tmp1, mdo_addr); + } + if (do_update) { + __ ba(next); + __ delayed()->nop(); + } +#ifdef ASSERT + } else { + __ br_notnull_short(tmp1, Assembler::pt, update); + __ stop("unexpect null obj"); +#endif + } + + __ bind(update); + + if (do_update) { +#ifdef ASSERT + if (exact_klass != NULL) { + Label ok; + __ load_klass(tmp1, tmp1); + metadata2reg(exact_klass->constant_encoding(), tmp2); + __ cmp_and_br_short(tmp1, tmp2, Assembler::equal, Assembler::pt, ok); + __ stop("exact klass and actual klass differ"); + __ bind(ok); + } +#endif + + Label do_update; + __ ld_ptr(mdo_addr, tmp2); + + if (!no_conflict) { + if (exact_klass == NULL || TypeEntries::is_type_none(current_klass)) { + if (exact_klass != NULL) { + metadata2reg(exact_klass->constant_encoding(), tmp1); + } else { + __ load_klass(tmp1, tmp1); + } + + __ xor3(tmp1, tmp2, tmp1); + __ btst(TypeEntries::type_klass_mask, tmp1); + // klass seen before, nothing to do. The unknown bit may have been + // set already but no need to check. + __ brx(Assembler::zero, false, Assembler::pt, next); + __ delayed()-> + + btst(TypeEntries::type_unknown, tmp1); + // already unknown. Nothing to do anymore. + __ brx(Assembler::notZero, false, Assembler::pt, next); + + if (TypeEntries::is_type_none(current_klass)) { + __ delayed()->btst(TypeEntries::type_mask, tmp2); + __ brx(Assembler::zero, true, Assembler::pt, do_update); + // first time here. Set profile type. + __ delayed()->or3(tmp2, tmp1, tmp2); + } else { + __ delayed()->nop(); + } + } else { + assert(ciTypeEntries::valid_ciklass(current_klass) != NULL && + ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "conflict only"); + + __ btst(TypeEntries::type_unknown, tmp2); + // already unknown. Nothing to do anymore. + __ brx(Assembler::notZero, false, Assembler::pt, next); + __ delayed()->nop(); + } + + // different than before. Cannot keep accurate profile. + __ or3(tmp2, TypeEntries::type_unknown, tmp2); + } else { + // There's a single possible klass at this profile point + assert(exact_klass != NULL, "should be"); + if (TypeEntries::is_type_none(current_klass)) { + metadata2reg(exact_klass->constant_encoding(), tmp1); + __ xor3(tmp1, tmp2, tmp1); + __ btst(TypeEntries::type_klass_mask, tmp1); + __ brx(Assembler::zero, false, Assembler::pt, next); +#ifdef ASSERT + + { + Label ok; + __ delayed()->btst(TypeEntries::type_mask, tmp2); + __ brx(Assembler::zero, true, Assembler::pt, ok); + __ delayed()->nop(); + + __ stop("unexpected profiling mismatch"); + __ bind(ok); + } + // first time here. Set profile type. + __ or3(tmp2, tmp1, tmp2); +#else + // first time here. Set profile type. + __ delayed()->or3(tmp2, tmp1, tmp2); +#endif + + } else { + assert(ciTypeEntries::valid_ciklass(current_klass) != NULL && + ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "inconsistent"); + + // already unknown. Nothing to do anymore. + __ btst(TypeEntries::type_unknown, tmp2); + __ brx(Assembler::notZero, false, Assembler::pt, next); + __ delayed()->or3(tmp2, TypeEntries::type_unknown, tmp2); + } + } + + __ bind(do_update); + __ st_ptr(tmp2, mdo_addr); + + __ bind(next); + } } void LIR_Assembler::align_backward_branch_target() { @@ -3320,9 +3458,14 @@ void LIR_Assembler::leal(LIR_Opr addr_opr, LIR_Opr dest) { LIR_Address* addr = addr_opr->as_address_ptr(); - assert(addr->index()->is_illegal() && addr->scale() == LIR_Address::times_1 && Assembler::is_simm13(addr->disp()), "can't handle complex addresses yet"); - - __ add(addr->base()->as_pointer_register(), addr->disp(), dest->as_pointer_register()); + assert(addr->index()->is_illegal() && addr->scale() == LIR_Address::times_1, "can't handle complex addresses yet"); + + if (Assembler::is_simm13(addr->disp())) { + __ add(addr->base()->as_pointer_register(), addr->disp(), dest->as_pointer_register()); + } else { + __ set(addr->disp(), G3_scratch); + __ add(addr->base()->as_pointer_register(), G3_scratch, dest->as_pointer_register()); + } } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp --- a/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1221,10 +1221,8 @@ bool is_obj = (type == T_ARRAY || type == T_OBJECT); LIR_Opr offset = off.result(); - if (data != dst) { - __ move(data, dst); - data = dst; - } + // Because we want a 2-arg form of xchg + __ move(data, dst); assert (!x->is_add() && (type == T_INT || (is_obj LP64_ONLY(&& UseCompressedOops))), "unexpected type"); LIR_Address* addr; @@ -1254,7 +1252,7 @@ pre_barrier(ptr, LIR_OprFact::illegalOpr /* pre_val */, true /* do_load */, false /* patch */, NULL); } - __ xchg(LIR_OprFact::address(addr), data, dst, tmp); + __ xchg(LIR_OprFact::address(addr), dst, dst, tmp); if (is_obj) { // Seems to be a precise address post_barrier(ptr, data); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp --- a/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/c1_MacroAssembler_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -55,9 +55,9 @@ } -void C1_MacroAssembler::build_frame(int frame_size_in_bytes) { - - generate_stack_overflow_check(frame_size_in_bytes); +void C1_MacroAssembler::build_frame(int frame_size_in_bytes, int bang_size_in_bytes) { + assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect"); + generate_stack_overflow_check(bang_size_in_bytes); // Create the frame. save_frame_c1(frame_size_in_bytes); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/c2_globals_sparc.hpp --- a/src/cpu/sparc/vm/c2_globals_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/c2_globals_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -92,6 +92,8 @@ define_pd_global(uintx, CodeCacheMinBlockLength, 4); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); +define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on sparc. + // Heap related flags define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/copy_sparc.hpp --- a/src/cpu/sparc/vm/copy_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/copy_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -184,7 +184,7 @@ assert(MinObjAlignmentInBytes >= BytesPerLong, "need alternate implementation"); if (value == 0 && UseBlockZeroing && - (count > (BlockZeroingLowLimit >> LogHeapWordSize))) { + (count > (size_t)(BlockZeroingLowLimit >> LogHeapWordSize))) { // Call it only when block zeroing is used ((_zero_Fn)StubRoutines::zero_aligned_words())(tohw, count); } else { diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/cppInterpreter_sparc.cpp --- a/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -960,7 +960,7 @@ // reset handle block __ ld_ptr(G2_thread, in_bytes(JavaThread::active_handles_offset()), G3_scratch); - __ st_ptr(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); + __ st(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); // handle exceptions (exception handling will handle unlocking!) @@ -2101,7 +2101,7 @@ int monitor_size = method->is_synchronized() ? 1*frame::interpreter_frame_monitor_size() : 0; return size_activation_helper(method->max_locals(), method->max_stack(), - monitor_size) + call_stub_size; + monitor_size) + call_stub_size; } void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill, @@ -2185,31 +2185,31 @@ istate->_last_Java_pc = (intptr_t*) last_Java_pc; } - -int AbstractInterpreter::layout_activation(Method* method, - int tempcount, // Number of slots on java expression stack in use - int popframe_extra_args, - int moncount, // Number of active monitors - int caller_actual_parameters, - int callee_param_size, - int callee_locals_size, - frame* caller, - frame* interpreter_frame, - bool is_top_frame, - bool is_bottom_frame) { +static int frame_size_helper(int max_stack, + int moncount, + int callee_param_size, + int callee_locals_size, + bool is_top_frame, + int& monitor_size, + int& full_frame_words) { + int extra_locals_size = callee_locals_size - callee_param_size; + monitor_size = (sizeof(BasicObjectLock) * moncount) / wordSize; + full_frame_words = size_activation_helper(extra_locals_size, max_stack, monitor_size); + int short_frame_words = size_activation_helper(extra_locals_size, max_stack, monitor_size); + int frame_words = is_top_frame ? full_frame_words : short_frame_words; - assert(popframe_extra_args == 0, "NEED TO FIX"); - // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() - // does as far as allocating an interpreter frame. - // If 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 + return frame_words; +} + +int AbstractInterpreter::size_activation(int max_stack, + int tempcount, + int extra_args, + int moncount, + int callee_param_size, + int callee_locals_size, + bool is_top_frame) { + assert(extra_args == 0, "NEED TO FIX"); // NOTE: return size is in words not bytes - // NOTE: tempcount is the current size of the java expression stack. For top most - // frames we will allocate a full sized expression stack and not the curback - // version that non-top frames have. - // Calculate the amount our frame will be adjust by the callee. For top frame // this is zero. @@ -2218,87 +2218,108 @@ // to it. So it ignores last_frame_adjust value. Seems suspicious as far // as getting sender_sp correct. - int extra_locals_size = callee_locals_size - callee_param_size; - int monitor_size = (sizeof(BasicObjectLock) * moncount) / wordSize; - int full_frame_words = size_activation_helper(extra_locals_size, method->max_stack(), monitor_size); - int short_frame_words = size_activation_helper(extra_locals_size, method->max_stack(), monitor_size); - int frame_words = is_top_frame ? full_frame_words : short_frame_words; + int unused_monitor_size = 0; + int unused_full_frame_words = 0; + return frame_size_helper(max_stack, moncount, callee_param_size, callee_locals_size, is_top_frame, + unused_monitor_size, unused_full_frame_words); +} +void AbstractInterpreter::layout_activation(Method* method, + int tempcount, // Number of slots on java expression stack in use + int popframe_extra_args, + int moncount, // Number of active monitors + int caller_actual_parameters, + int callee_param_size, + int callee_locals_size, + frame* caller, + frame* interpreter_frame, + bool is_top_frame, + bool is_bottom_frame) { + assert(popframe_extra_args == 0, "NEED TO FIX"); + // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() + // does as far as allocating an interpreter frame. + // Set up the method, locals, and monitors. + // The frame interpreter_frame is guaranteed to be the right size, + // as determined by a previous call to the size_activation() method. + // It is also guaranteed to be walkable even though it is in a skeletal state + // NOTE: tempcount is the current size of the java expression stack. For top most + // frames we will allocate a full sized expression stack and not the curback + // version that non-top frames have. + int monitor_size = 0; + int full_frame_words = 0; + int frame_words = frame_size_helper(method->max_stack(), moncount, callee_param_size, callee_locals_size, + is_top_frame, monitor_size, full_frame_words); /* - if we actually have a frame to layout we must now fill in all the pieces. This means both + We must now fill in all the pieces of the frame. This means both the interpreterState and the registers. */ - if (interpreter_frame != NULL) { - // MUCHO HACK + // MUCHO HACK - intptr_t* frame_bottom = interpreter_frame->sp() - (full_frame_words - frame_words); - // 'interpreter_frame->sp()' is unbiased while 'frame_bottom' must be a biased value in 64bit mode. - assert(((intptr_t)frame_bottom & 0xf) == 0, "SP biased in layout_activation"); - frame_bottom = (intptr_t*)((intptr_t)frame_bottom - STACK_BIAS); + intptr_t* frame_bottom = interpreter_frame->sp() - (full_frame_words - frame_words); + // 'interpreter_frame->sp()' is unbiased while 'frame_bottom' must be a biased value in 64bit mode. + assert(((intptr_t)frame_bottom & 0xf) == 0, "SP biased in layout_activation"); + frame_bottom = (intptr_t*)((intptr_t)frame_bottom - STACK_BIAS); - /* Now fillin the interpreterState object */ + /* Now fillin the interpreterState object */ - interpreterState cur_state = (interpreterState) ((intptr_t)interpreter_frame->fp() - sizeof(BytecodeInterpreter)); + interpreterState cur_state = (interpreterState) ((intptr_t)interpreter_frame->fp() - sizeof(BytecodeInterpreter)); - intptr_t* locals; + intptr_t* locals; + + // Calculate the postion of locals[0]. This is painful because of + // stack alignment (same as ia64). The problem is that we can + // not compute the location of locals from fp(). fp() will account + // for the extra locals but it also accounts for aligning the stack + // and we can't determine if the locals[0] was misaligned but max_locals + // was enough to have the + // calculate postion of locals. fp already accounts for extra locals. + // +2 for the static long no_params() issue. - // Calculate the postion of locals[0]. This is painful because of - // stack alignment (same as ia64). The problem is that we can - // not compute the location of locals from fp(). fp() will account - // for the extra locals but it also accounts for aligning the stack - // and we can't determine if the locals[0] was misaligned but max_locals - // was enough to have the - // calculate postion of locals. fp already accounts for extra locals. - // +2 for the static long no_params() issue. + 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(); + // stack() is prepushed. + locals = prev->stack() + method->size_of_parameters(); + } else { + // Lay out locals block in the caller adjacent to the register window save area. + // + // Compiled frames do not allocate a varargs area which is why this if + // statement is needed. + // + intptr_t* fp = interpreter_frame->fp(); + int local_words = method->max_locals() * Interpreter::stackElementWords; - 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(); - // stack() is prepushed. - locals = prev->stack() + method->size_of_parameters(); + if (caller->is_compiled_frame()) { + locals = fp + frame::register_save_words + local_words - 1; } else { - // Lay out locals block in the caller adjacent to the register window save area. - // - // Compiled frames do not allocate a varargs area which is why this if - // statement is needed. - // - intptr_t* fp = interpreter_frame->fp(); - int local_words = method->max_locals() * Interpreter::stackElementWords; + locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; + } - if (caller->is_compiled_frame()) { - locals = fp + frame::register_save_words + local_words - 1; - } else { - locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; - } + } + // END MUCHO HACK - } - // END MUCHO HACK - - intptr_t* monitor_base = (intptr_t*) cur_state; - intptr_t* stack_base = monitor_base - monitor_size; - /* +1 because stack is always prepushed */ - intptr_t* stack = stack_base - (tempcount + 1); + intptr_t* monitor_base = (intptr_t*) cur_state; + intptr_t* stack_base = monitor_base - monitor_size; + /* +1 because stack is always prepushed */ + intptr_t* stack = stack_base - (tempcount + 1); - BytecodeInterpreter::layout_interpreterState(cur_state, - caller, - interpreter_frame, - method, - locals, - stack, - stack_base, - monitor_base, - frame_bottom, - is_top_frame); + BytecodeInterpreter::layout_interpreterState(cur_state, + caller, + interpreter_frame, + method, + locals, + stack, + stack_base, + monitor_base, + frame_bottom, + is_top_frame); - BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, interpreter_frame->fp()); - - } - return frame_words; + BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, interpreter_frame->fp()); } #endif // CC_INTERP diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/frame_sparc.hpp --- a/src/cpu/sparc/vm/frame_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/frame_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -259,8 +259,8 @@ // next two fns read and write Lmonitors value, private: - BasicObjectLock* interpreter_frame_monitors() const { return *interpreter_frame_monitors_addr(); } - void interpreter_frame_set_monitors(BasicObjectLock* monitors) { *interpreter_frame_monitors_addr() = monitors; } + BasicObjectLock* interpreter_frame_monitors() const; + void interpreter_frame_set_monitors(BasicObjectLock* monitors); #else public: inline interpreterState get_interpreterState() const { diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/frame_sparc.inline.hpp --- a/src/cpu/sparc/vm/frame_sparc.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/frame_sparc.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -226,6 +226,13 @@ return (Method**)sp_addr_at( Lmethod->sp_offset_in_saved_window()); } +inline BasicObjectLock* frame::interpreter_frame_monitors() const { + return *interpreter_frame_monitors_addr(); +} + +inline void frame::interpreter_frame_set_monitors(BasicObjectLock* monitors) { + *interpreter_frame_monitors_addr() = monitors; +} // Constant pool cache @@ -237,6 +244,10 @@ inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { return (ConstantPoolCache**)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 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/globalDefinitions_sparc.hpp --- a/src/cpu/sparc/vm/globalDefinitions_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/globalDefinitions_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -30,6 +30,12 @@ const int StackAlignmentInBytes = (2*wordSize); +// 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 = false; + #define SUPPORTS_NATIVE_CX8 #endif // CPU_SPARC_VM_GLOBALDEFINITIONS_SPARC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/globals_sparc.hpp --- a/src/cpu/sparc/vm/globals_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/globals_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -43,7 +43,8 @@ define_pd_global(bool, NeedsDeoptSuspend, true); // 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 past to check cast +define_pd_global(bool, TrapBasedNullChecks, false); // Not needed on sparc. +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast define_pd_global(intx, CodeEntryAlignment, 32); // The default setting 16/16 seems to work best. diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/interp_masm_sparc.cpp --- a/src/cpu/sparc/vm/interp_masm_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/interp_masm_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1942,6 +1942,220 @@ } } +void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr, Register tmp) { + Label not_null, do_nothing, do_update; + + assert_different_registers(obj, mdo_addr.base(), tmp); + + verify_oop(obj); + + ld_ptr(mdo_addr, tmp); + + br_notnull_short(obj, pt, not_null); + or3(tmp, TypeEntries::null_seen, tmp); + ba_short(do_update); + + bind(not_null); + load_klass(obj, obj); + + xor3(obj, tmp, obj); + btst(TypeEntries::type_klass_mask, obj); + // klass seen before, nothing to do. The unknown bit may have been + // set already but no need to check. + brx(zero, false, pt, do_nothing); + delayed()-> + + btst(TypeEntries::type_unknown, obj); + // already unknown. Nothing to do anymore. + brx(notZero, false, pt, do_nothing); + delayed()-> + + btst(TypeEntries::type_mask, tmp); + brx(zero, true, pt, do_update); + // first time here. Set profile type. + delayed()->or3(tmp, obj, tmp); + + // different than before. Cannot keep accurate profile. + or3(tmp, TypeEntries::type_unknown, tmp); + + bind(do_update); + // update profile + st_ptr(tmp, mdo_addr); + + bind(do_nothing); +} + +void InterpreterMacroAssembler::profile_arguments_type(Register callee, Register tmp1, Register tmp2, bool is_virtual) { + if (!ProfileInterpreter) { + return; + } + + assert_different_registers(callee, tmp1, tmp2, ImethodDataPtr); + + if (MethodData::profile_arguments() || MethodData::profile_return()) { + Label profile_continue; + + test_method_data_pointer(profile_continue); + + int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); + + ldub(ImethodDataPtr, in_bytes(DataLayout::tag_offset()) - off_to_start, tmp1); + cmp_and_br_short(tmp1, is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag, notEqual, pn, profile_continue); + + if (MethodData::profile_arguments()) { + Label done; + int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); + add(ImethodDataPtr, off_to_args, ImethodDataPtr); + + for (int i = 0; i < TypeProfileArgsLimit; i++) { + if (i > 0 || MethodData::profile_return()) { + // If return value type is profiled we may have no argument to profile + ld_ptr(ImethodDataPtr, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args, tmp1); + sub(tmp1, i*TypeStackSlotEntries::per_arg_count(), tmp1); + cmp_and_br_short(tmp1, TypeStackSlotEntries::per_arg_count(), less, pn, done); + } + ld_ptr(Address(callee, Method::const_offset()), tmp1); + lduh(Address(tmp1, ConstMethod::size_of_parameters_offset()), tmp1); + // stack offset o (zero based) from the start of the argument + // list, for n arguments translates into offset n - o - 1 from + // the end of the argument list. But there's an extra slot at + // the stop of the stack. So the offset is n - o from Lesp. + ld_ptr(ImethodDataPtr, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args, tmp2); + sub(tmp1, tmp2, tmp1); + + // Can't use MacroAssembler::argument_address() which needs Gargs to be set up + sll(tmp1, Interpreter::logStackElementSize, tmp1); + ld_ptr(Lesp, tmp1, tmp1); + + Address mdo_arg_addr(ImethodDataPtr, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args); + profile_obj_type(tmp1, mdo_arg_addr, tmp2); + + int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); + add(ImethodDataPtr, to_add, ImethodDataPtr); + off_to_args += to_add; + } + + if (MethodData::profile_return()) { + ld_ptr(ImethodDataPtr, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args, tmp1); + sub(tmp1, TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count(), tmp1); + } + + bind(done); + + if (MethodData::profile_return()) { + // We're right after the type profile for the last + // argument. tmp1 is the number of cells left in the + // CallTypeData/VirtualCallTypeData to reach its end. Non null + // if there's a return to profile. + assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); + sll(tmp1, exact_log2(DataLayout::cell_size), tmp1); + add(ImethodDataPtr, tmp1, ImethodDataPtr); + } + } else { + assert(MethodData::profile_return(), "either profile call args or call ret"); + update_mdp_by_constant(in_bytes(TypeEntriesAtCall::return_only_size())); + } + + // mdp points right after the end of the + // CallTypeData/VirtualCallTypeData, right after the cells for the + // return value type if there's one. + + bind(profile_continue); + } +} + +void InterpreterMacroAssembler::profile_return_type(Register ret, Register tmp1, Register tmp2) { + assert_different_registers(ret, tmp1, tmp2); + if (ProfileInterpreter && MethodData::profile_return()) { + Label profile_continue, done; + + test_method_data_pointer(profile_continue); + + if (MethodData::profile_return_jsr292_only()) { + // If we don't profile all invoke bytecodes we must make sure + // it's a bytecode we indeed profile. We can't go back to the + // begining of the ProfileData we intend to update to check its + // type because we're right after it and we don't known its + // length. + Label do_profile; + ldub(Lbcp, 0, tmp1); + cmp_and_br_short(tmp1, Bytecodes::_invokedynamic, equal, pn, do_profile); + cmp(tmp1, Bytecodes::_invokehandle); + br(equal, false, pn, do_profile); + delayed()->ldub(Lmethod, Method::intrinsic_id_offset_in_bytes(), tmp1); + cmp_and_br_short(tmp1, vmIntrinsics::_compiledLambdaForm, notEqual, pt, profile_continue); + + bind(do_profile); + } + + Address mdo_ret_addr(ImethodDataPtr, -in_bytes(ReturnTypeEntry::size())); + mov(ret, tmp1); + profile_obj_type(tmp1, mdo_ret_addr, tmp2); + + bind(profile_continue); + } +} + +void InterpreterMacroAssembler::profile_parameters_type(Register tmp1, Register tmp2, Register tmp3, Register tmp4) { + if (ProfileInterpreter && MethodData::profile_parameters()) { + Label profile_continue, done; + + test_method_data_pointer(profile_continue); + + // Load the offset of the area within the MDO used for + // parameters. If it's negative we're not profiling any parameters. + lduw(ImethodDataPtr, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset()), tmp1); + cmp_and_br_short(tmp1, 0, less, pn, profile_continue); + + // Compute a pointer to the area for parameters from the offset + // and move the pointer to the slot for the last + // parameters. Collect profiling from last parameter down. + // mdo start + parameters offset + array length - 1 + + // Pointer to the parameter area in the MDO + Register mdp = tmp1; + add(ImethodDataPtr, tmp1, mdp); + + // offset of the current profile entry to update + Register entry_offset = tmp2; + // entry_offset = array len in number of cells + ld_ptr(mdp, ArrayData::array_len_offset(), entry_offset); + + int off_base = in_bytes(ParametersTypeData::stack_slot_offset(0)); + assert(off_base % DataLayout::cell_size == 0, "should be a number of cells"); + + // entry_offset (number of cells) = array len - size of 1 entry + offset of the stack slot field + sub(entry_offset, TypeStackSlotEntries::per_arg_count() - (off_base / DataLayout::cell_size), entry_offset); + // entry_offset in bytes + sll(entry_offset, exact_log2(DataLayout::cell_size), entry_offset); + + Label loop; + bind(loop); + + // load offset on the stack from the slot for this parameter + ld_ptr(mdp, entry_offset, tmp3); + sll(tmp3,Interpreter::logStackElementSize, tmp3); + neg(tmp3); + // read the parameter from the local area + ld_ptr(Llocals, tmp3, tmp3); + + // make entry_offset now point to the type field for this parameter + int type_base = in_bytes(ParametersTypeData::type_offset(0)); + assert(type_base > off_base, "unexpected"); + add(entry_offset, type_base - off_base, entry_offset); + + // profile the parameter + Address arg_type(mdp, entry_offset); + profile_obj_type(tmp3, arg_type, tmp4); + + // go to next parameter + sub(entry_offset, TypeStackSlotEntries::per_arg_count() * DataLayout::cell_size + (type_base - off_base), entry_offset); + cmp_and_br_short(entry_offset, off_base, greaterEqual, pt, loop); + + bind(profile_continue); + } +} + // add a InterpMonitorElem to stack (see frame_sparc.hpp) void InterpreterMacroAssembler::add_monitor_to_stack( bool stack_is_empty, diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/interp_masm_sparc.hpp --- a/src/cpu/sparc/vm/interp_masm_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/interp_masm_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -330,6 +330,11 @@ Register scratch2, Register scratch3); + void profile_obj_type(Register obj, const Address& mdo_addr, Register tmp); + void profile_arguments_type(Register callee, Register tmp1, Register tmp2, bool is_virtual); + void profile_return_type(Register ret, Register tmp1, Register tmp2); + void profile_parameters_type(Register tmp1, Register tmp2, Register tmp3, Register tmp4); + // Debugging void interp_verify_oop(Register reg, TosState state, const char * file, int line); // only if +VerifyOops && state == atos void verify_oop_or_return_address(Register reg, Register rtmp); // for astore diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/macroAssembler_sparc.cpp --- a/src/cpu/sparc/vm/macroAssembler_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/macroAssembler_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -3531,7 +3531,7 @@ // was post-decremented.) Skip this address by starting at i=1, and // touch a few more pages below. N.B. It is important to touch all // the way down to and including i=StackShadowPages. - for (int i = 1; i <= StackShadowPages; i++) { + for (int i = 1; i < StackShadowPages; i++) { set((-i*offset)+STACK_BIAS, Rscratch); st(G0, Rtsp, Rscratch); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/macroAssembler_sparc.inline.hpp --- a/src/cpu/sparc/vm/macroAssembler_sparc.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/macroAssembler_sparc.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -233,6 +233,7 @@ } inline void MacroAssembler::br( Condition c, bool a, Predict p, Label& L ) { + insert_nop_after_cbcond(); br(c, a, p, target(L)); } @@ -248,6 +249,7 @@ } inline void MacroAssembler::brx( Condition c, bool a, Predict p, Label& L ) { + insert_nop_after_cbcond(); brx(c, a, p, target(L)); } @@ -269,6 +271,7 @@ } inline void MacroAssembler::fb( Condition c, bool a, Predict p, Label& L ) { + insert_nop_after_cbcond(); fb(c, a, p, target(L)); } @@ -318,6 +321,7 @@ } inline void MacroAssembler::call( Label& L, relocInfo::relocType rt ) { + insert_nop_after_cbcond(); MacroAssembler::call( target(L), rt); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/sharedRuntime_sparc.cpp --- a/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1132,7 +1132,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 @@ -2110,7 +2112,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 @@ -2711,7 +2713,7 @@ if (!is_critical_native) { // reset handle block __ ld_ptr(G2_thread, in_bytes(JavaThread::active_handles_offset()), L5); - __ st_ptr(G0, L5, JNIHandleBlock::top_offset_in_bytes()); + __ st(G0, L5, JNIHandleBlock::top_offset_in_bytes()); __ ld_ptr(G2_thread, in_bytes(Thread::pending_exception_offset()), G3_scratch); check_forward_pending_exception(masm, G3_scratch); @@ -2857,7 +2859,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. @@ -3379,13 +3381,16 @@ Register O4array_size = O4; Label loop; - // Before we make new frames, check to see if stack is available. - // Do this after the caller's return address is on top of stack +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. if (UseStackBanging) { // Get total frame size for interpreted frames __ ld(O2UnrollBlock, Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes(), O4); __ bang_stack_size(O4, O3, G3_scratch); } +#endif __ ld(O2UnrollBlock, Deoptimization::UnrollBlock::number_of_frames_offset_in_bytes(), O4array_size); __ ld_ptr(O2UnrollBlock, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes(), G3pcs); @@ -3433,9 +3438,11 @@ ResourceMark rm; // setup code generation tools int pad = VerifyThread ? 512 : 0;// Extra slop space for more verify code +#ifdef ASSERT if (UseStackBanging) { pad += StackShadowPages*16 + 32; } +#endif #ifdef GRAAL pad += 1000; // Increase the buffer size when compiling for GRAAL #endif @@ -3735,9 +3742,11 @@ ResourceMark rm; // setup code generation tools int pad = VerifyThread ? 512 : 0; +#ifdef ASSERT if (UseStackBanging) { pad += StackShadowPages*16 + 32; } +#endif #ifdef _LP64 CodeBuffer buffer("uncommon_trap_blob", 2700+pad, 512); #else diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/sparc.ad Wed Oct 15 16:02:50 2014 +0200 @@ -457,6 +457,13 @@ // 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. + // Must be visible to the DFA in dfa_sparc.cpp extern bool can_branch_register( Node *bol, Node *cmp ); @@ -468,6 +475,46 @@ #define LONG_HI_REG(x) (x) #define LONG_LO_REG(x) (x) +class CallStubImpl { + + //-------------------------------------------------------------- + //---< Used for optimization in Compile::Shorten_branches >--- + //-------------------------------------------------------------- + + public: + // Size of call trampoline stub. + static uint size_call_trampoline() { + return 0; // no call trampolines on this platform + } + + // number of relocations needed by a call trampoline stub + static uint reloc_call_trampoline() { + return 0; // no call trampolines on this platform + } +}; + +class HandlerImpl { + + public: + + static int emit_exception_handler(CodeBuffer &cbuf); + static int emit_deopt_handler(CodeBuffer& cbuf); + + static uint size_exception_handler() { + if (TraceJumps) { + return (400); // just a guess + } + return ( NativeJump::instruction_size ); // sethi;jmp;nop + } + + static uint size_deopt_handler() { + if (TraceJumps) { + return (400); // just a guess + } + return ( 4+ NativeJump::instruction_size ); // save;sethi;jmp;restore + } +}; + %} source %{ @@ -1040,6 +1087,11 @@ } } +bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } +void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { Compile* C = ra_->C; Compile::ConstantTable& constant_table = C->constant_table(); @@ -1141,15 +1193,16 @@ st->print_cr("Verify_Thread"); st->print("\t"); } - size_t framesize = C->frame_slots() << LogBytesPerInt; + size_t framesize = C->frame_size_in_bytes(); + int bangsize = C->bang_size_in_bytes(); // 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)) { - st->print_cr("! stack bang"); st->print("\t"); + if (C->need_stack_bang(bangsize)) { + st->print_cr("! stack bang (%d bytes)", bangsize); st->print("\t"); } if (Assembler::is_simm13(-framesize)) { @@ -1173,17 +1226,18 @@ __ verify_thread(); - size_t framesize = C->frame_slots() << LogBytesPerInt; + size_t framesize = C->frame_size_in_bytes(); assert(framesize >= 16*wordSize, "must have room for reg. save area"); assert(framesize%(2*wordSize) == 0, "must preserve 2*wordSize alignment"); + int bangsize = C->bang_size_in_bytes(); // 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)) { - __ generate_stack_overflow_check(framesize); + if (C->need_stack_bang(bangsize)) { + __ generate_stack_overflow_check(bangsize); } if (Assembler::is_simm13(-framesize)) { @@ -1216,7 +1270,7 @@ void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { Compile* C = ra_->C; - if( do_polling() && ra_->C->is_method_compilation() ) { + if(do_polling() && ra_->C->is_method_compilation()) { st->print("SETHI #PollAddr,L0\t! Load Polling address\n\t"); #ifdef _LP64 st->print("LDX [L0],G0\t!Poll for Safepointing\n\t"); @@ -1225,8 +1279,12 @@ #endif } - if( do_polling() ) + if(do_polling()) { + if (UseCBCond && !ra_->C->is_method_compilation()) { + st->print("NOP\n\t"); + } st->print("RET\n\t"); + } st->print("RESTORE"); } @@ -1239,15 +1297,20 @@ __ verify_thread(); // If this does safepoint polling, then do it here - if( do_polling() && ra_->C->is_method_compilation() ) { + if(do_polling() && ra_->C->is_method_compilation()) { AddressLiteral polling_page(os::get_polling_page()); __ sethi(polling_page, L0); __ relocate(relocInfo::poll_return_type); - __ ld_ptr( L0, 0, G0 ); + __ ld_ptr(L0, 0, G0); } // If this is a return, then stuff the restore in the delay slot - if( do_polling() ) { + if(do_polling()) { + if (UseCBCond && !ra_->C->is_method_compilation()) { + // Insert extra padding for the case when the epilogue is preceded by + // a cbcond jump, which can't be followed by a CTI instruction + __ nop(); + } __ ret(); __ delayed()->restore(); } else { @@ -1705,22 +1768,9 @@ //============================================================================= -uint size_exception_handler() { - if (TraceJumps) { - return (400); // just a guess - } - return ( NativeJump::instruction_size ); // sethi;jmp;nop -} - -uint size_deopt_handler() { - if (TraceJumps) { - return (400); // just a guess - } - return ( 4+ NativeJump::instruction_size ); // save;sethi;jmp;restore -} // Emit exception handler code. -int emit_exception_handler(CodeBuffer& cbuf) { +int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf) { Register temp_reg = G3; AddressLiteral exception_blob(OptoRuntime::exception_blob()->entry_point()); MacroAssembler _masm(&cbuf); @@ -1741,7 +1791,7 @@ return offset; } -int emit_deopt_handler(CodeBuffer& cbuf) { +int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { // Can't use any of the current frame's registers as we may have deopted // at a poll and everything (including G3) can be live. Register temp_reg = L0; @@ -1848,6 +1898,12 @@ return false; } +// Current (2013) SPARC platforms need to read original key +// to construct decryption expanded key +const bool Matcher::pass_original_key_for_aes() { + return true; +} + // USII supports fxtof through the whole range of number, USIII doesn't const bool Matcher::convL2FSupported(void) { return VM_Version::has_fast_fxtof(); @@ -1885,6 +1941,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_postalloc_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 @@ -2023,19 +2082,6 @@ return L7_REGP_mask(); } -const RegMask Matcher::mathExactI_result_proj_mask() { - return G1_REGI_mask(); -} - -const RegMask Matcher::mathExactL_result_proj_mask() { - return G1_REGL_mask(); -} - -const RegMask Matcher::mathExactI_flags_proj_mask() { - return INT_FLAGS_mask(); -} - - %} @@ -2503,7 +2549,7 @@ enc_class call_epilog %{ if( VerifyStackAtCalls ) { MacroAssembler _masm(&cbuf); - int framesize = ra_->C->frame_slots() << LogBytesPerInt; + int framesize = ra_->C->frame_size_in_bytes(); Register temp_reg = G3; __ add(SP, framesize, temp_reg); __ cmp(temp_reg, FP); @@ -3242,7 +3288,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 @@ -3295,7 +3341,18 @@ //----------Instruction Attributes--------------------------------------------- ins_attrib ins_cost(DEFAULT_COST); // Required cost attribute ins_attrib ins_size(32); // Required size attribute (in bits) -ins_attrib ins_avoid_back_to_back(0); // instruction should not be generated back to back + +// avoid_back_to_back attribute is an expression that must return +// one of the following values defined in MachNode: +// AVOID_NONE - instruction can be placed anywhere +// AVOID_BEFORE - instruction cannot be placed after an +// instruction with MachNode::AVOID_AFTER +// AVOID_AFTER - the next instruction cannot be the one +// with MachNode::AVOID_BEFORE +// AVOID_BEFORE_AND_AFTER - BEFORE and AFTER attributes at +// the same time +ins_attrib ins_avoid_back_to_back(MachNode::AVOID_NONE); + ins_attrib ins_short_branch(0); // Required flag: is this instruction a // non-matching short branch variant of some // long branch? @@ -6595,6 +6652,7 @@ ins_encode %{ __ encode_heap_oop($src$$Register, $dst$$Register); %} + ins_avoid_back_to_back(Universe::narrow_oop_base() == NULL ? AVOID_NONE : AVOID_BEFORE); ins_pipe(ialu_reg); %} @@ -6653,6 +6711,7 @@ instruct membar_acquire() %{ match(MemBarAcquire); + match(LoadFence); ins_cost(4*MEMORY_REF_COST); size(0); @@ -6673,6 +6732,7 @@ instruct membar_release() %{ match(MemBarRelease); + match(StoreFence); ins_cost(4*MEMORY_REF_COST); size(0); @@ -9162,6 +9222,7 @@ __ ba(*L); __ delayed()->nop(); %} + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br); %} @@ -9174,13 +9235,13 @@ 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); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_imm); %} @@ -9194,6 +9255,7 @@ format %{ "BP$cmp $icc,$labl" %} // Prim = bits 24-22, Secnd = bits 31-30 ins_encode( enc_bp( labl, cmp, icc ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_cc); %} @@ -9205,6 +9267,7 @@ format %{ "BP$cmp $icc,$labl" %} // Prim = bits 24-22, Secnd = bits 31-30 ins_encode( enc_bp( labl, cmp, icc ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_cc); %} @@ -9223,6 +9286,7 @@ __ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::ptr_cc, predict_taken, *L); __ delayed()->nop(); %} + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_cc); %} @@ -9241,6 +9305,7 @@ __ fbp( (Assembler::Condition)($cmp$$cmpcode), false, (Assembler::CC)($fcc$$reg), predict_taken, *L); __ delayed()->nop(); %} + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_fcc); %} @@ -9253,6 +9318,7 @@ format %{ "BP$cmp $icc,$labl\t! Loop end" %} // Prim = bits 24-22, Secnd = bits 31-30 ins_encode( enc_bp( labl, cmp, icc ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_cc); %} @@ -9265,6 +9331,7 @@ format %{ "BP$cmp $icc,$labl\t! Loop end" %} // Prim = bits 24-22, Secnd = bits 31-30 ins_encode( enc_bp( labl, cmp, icc ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_cc); %} @@ -9515,7 +9582,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9533,7 +9600,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$constant, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_imm); %} @@ -9551,7 +9618,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9569,7 +9636,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$constant, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_imm); %} @@ -9587,7 +9654,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$Register, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9605,7 +9672,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$constant, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_imm); %} @@ -9628,7 +9695,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::ptr_cc, $op1$$Register, $op2$$Register, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9650,7 +9717,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::ptr_cc, $op1$$Register, G0, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9668,7 +9735,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9686,7 +9753,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, G0, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9705,7 +9772,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_reg); %} @@ -9723,7 +9790,7 @@ __ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$constant, *L); %} ins_short_branch(1); - ins_avoid_back_to_back(1); + ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER); ins_pipe(cbcond_reg_imm); %} @@ -9740,6 +9807,7 @@ ins_cost(BRANCH_COST); format %{ "BR$cmp $op1,$labl" %} ins_encode( enc_bpr( labl, cmp, op1 ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_reg); %} @@ -9752,6 +9820,7 @@ ins_cost(BRANCH_COST); format %{ "BR$cmp $op1,$labl" %} ins_encode( enc_bpr( labl, cmp, op1 ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_reg); %} @@ -9764,6 +9833,7 @@ ins_cost(BRANCH_COST); format %{ "BR$cmp $op1,$labl" %} ins_encode( enc_bpr( labl, cmp, op1 ) ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_reg); %} @@ -9804,6 +9874,7 @@ __ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L); __ delayed()->nop(); %} + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(br_cc); %} @@ -9931,6 +10002,7 @@ ins_cost(CALL_COST); format %{ "CALL,static ; NOP ==> " %} ins_encode( Java_Static_Call( meth ), call_epilog ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(simple_call); %} @@ -9967,6 +10039,7 @@ format %{ "CALL,runtime" %} ins_encode( Java_To_Runtime( meth ), call_epilog, adjust_long_from_native_call ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(simple_call); %} @@ -9979,6 +10052,7 @@ ins_encode( Java_To_Runtime( meth ), call_epilog, adjust_long_from_native_call ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(simple_call); %} @@ -9991,6 +10065,7 @@ ins_encode( Java_To_Runtime( meth ), call_epilog, adjust_long_from_native_call ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(simple_call); %} @@ -10004,6 +10079,7 @@ ins_cost(CALL_COST); format %{ "Jmp $jump_target ; NOP \t! $method_oop holds method oop" %} ins_encode(form_jmpl(jump_target)); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(tail_call); %} @@ -10035,6 +10111,7 @@ // opcode(Assembler::jmpl_op3, Assembler::arith_op); // The hack duplicates the exception oop into G3, so that CreateEx can use it there. // ins_encode( form3_rs1_simm13_rd( jump_target, 0x00, R_G0 ), move_return_pc_to_o1() ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(tail_call); %} @@ -10065,6 +10142,7 @@ // use the following format syntax format %{ "Jmp rethrow_stub" %} ins_encode(enc_rethrow); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(tail_call); %} @@ -10093,6 +10171,7 @@ ins_cost(DEFAULT_COST*10); format %{ "CALL PartialSubtypeCheck\n\tNOP" %} ins_encode( enc_PartialSubtypeCheck() ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(partial_subtype_check_pipe); %} @@ -10102,6 +10181,7 @@ ins_cost(DEFAULT_COST*10); format %{ "CALL PartialSubtypeCheck\n\tNOP\t# (sets condition codes)" %} ins_encode( enc_PartialSubtypeCheck() ); + ins_avoid_back_to_back(AVOID_BEFORE); ins_pipe(partial_subtype_check_pipe); %} diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/stubGenerator_sparc.cpp --- a/src/cpu/sparc/vm/stubGenerator_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/stubGenerator_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -83,7 +83,7 @@ private: #ifdef PRODUCT -#define inc_counter_np(a,b,c) (0) +#define inc_counter_np(a,b,c) #else #define inc_counter_np(counter, t1, t2) \ BLOCK_COMMENT("inc_counter " #counter); \ @@ -1055,7 +1055,7 @@ Label& L_loop, bool use_prefetch, bool use_bis); void disjoint_copy_core(Register from, Register to, Register count, int log2_elem_size, - int iter_size, CopyLoopFunc copy_loop_func) { + int iter_size, StubGenerator::CopyLoopFunc copy_loop_func) { Label L_copy; assert(log2_elem_size <= 3, "the following code should be changed"); @@ -1206,7 +1206,7 @@ __ inc(from, 8); __ sllx(O3, left_shift, O3); - disjoint_copy_core(from, to, count, log2_elem_size, 16, copy_16_bytes_shift_loop); + disjoint_copy_core(from, to, count, log2_elem_size, 16, &StubGenerator::copy_16_bytes_shift_loop); __ inccc(count, count_dec>>1 ); // + 8 bytes __ brx(Assembler::negative, true, Assembler::pn, L_copy_last_bytes); @@ -2085,7 +2085,7 @@ __ dec(count, 4); // The cmp at the beginning guaranty count >= 4 __ sllx(O3, 32, O3); - disjoint_copy_core(from, to, count, 2, 16, copy_16_bytes_loop); + disjoint_copy_core(from, to, count, 2, 16, &StubGenerator::copy_16_bytes_loop); __ br(Assembler::always, false, Assembler::pt, L_copy_4_bytes); __ delayed()->inc(count, 4); // restore 'count' @@ -2366,7 +2366,7 @@ // count >= 0 (original count - 8) __ mov(from, from64); - disjoint_copy_core(from64, to64, count, 3, 64, copy_64_bytes_loop); + disjoint_copy_core(from64, to64, count, 3, 64, &StubGenerator::copy_64_bytes_loop); // Restore O4(offset0), O5(offset8) __ sub(from64, from, offset0); @@ -3304,6 +3304,1277 @@ } } + address generate_aescrypt_encryptBlock() { + // required since we read expanded key 'int' array starting first element without alignment considerations + assert((arrayOopDesc::base_offset_in_bytes(T_INT) & 7) == 0, + "the following code assumes that first element of an int array is aligned to 8 bytes"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "aescrypt_encryptBlock"); + Label L_load_misaligned_input, L_load_expanded_key, L_doLast128bit, L_storeOutput, L_store_misaligned_output; + address start = __ pc(); + Register from = O0; // source byte array + Register to = O1; // destination byte array + Register key = O2; // expanded key array + const Register keylen = O4; //reg for storing expanded key array length + + // read expanded key length + __ ldsw(Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)), keylen, 0); + + // Method to address arbitrary alignment for load instructions: + // Check last 3 bits of 'from' address to see if it is aligned to 8-byte boundary + // If zero/aligned then continue with double FP load instructions + // If not zero/mis-aligned then alignaddr will set GSR.align with number of bytes to skip during faligndata + // alignaddr will also convert arbitrary aligned 'from' address to nearest 8-byte aligned address + // load 3 * 8-byte components (to read 16 bytes input) in 3 different FP regs starting at this aligned address + // faligndata will then extract (based on GSR.align value) the appropriate 8 bytes from the 2 source regs + + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_input); + __ delayed()->alignaddr(from, G0, from); + + // aligned case: load input into F54-F56 + __ ldf(FloatRegisterImpl::D, from, 0, F54); + __ ldf(FloatRegisterImpl::D, from, 8, F56); + __ ba_short(L_load_expanded_key); + + __ BIND(L_load_misaligned_input); + __ ldf(FloatRegisterImpl::D, from, 0, F54); + __ ldf(FloatRegisterImpl::D, from, 8, F56); + __ ldf(FloatRegisterImpl::D, from, 16, F58); + __ faligndata(F54, F56, F54); + __ faligndata(F56, F58, F56); + + __ BIND(L_load_expanded_key); + // Since we load expanded key buffers starting first element, 8-byte alignment is guaranteed + for ( int i = 0; i <= 38; i += 2 ) { + __ ldf(FloatRegisterImpl::D, key, i*4, as_FloatRegister(i)); + } + + // perform cipher transformation + __ fxor(FloatRegisterImpl::D, F0, F54, F54); + __ fxor(FloatRegisterImpl::D, F2, F56, F56); + // rounds 1 through 8 + for ( int i = 4; i <= 28; i += 8 ) { + __ aes_eround01(as_FloatRegister(i), F54, F56, F58); + __ aes_eround23(as_FloatRegister(i+2), F54, F56, F60); + __ aes_eround01(as_FloatRegister(i+4), F58, F60, F54); + __ aes_eround23(as_FloatRegister(i+6), F58, F60, F56); + } + __ aes_eround01(F36, F54, F56, F58); //round 9 + __ aes_eround23(F38, F54, F56, F60); + + // 128-bit original key size + __ cmp_and_brx_short(keylen, 44, Assembler::equal, Assembler::pt, L_doLast128bit); + + for ( int i = 40; i <= 50; i += 2 ) { + __ ldf(FloatRegisterImpl::D, key, i*4, as_FloatRegister(i) ); + } + __ aes_eround01(F40, F58, F60, F54); //round 10 + __ aes_eround23(F42, F58, F60, F56); + __ aes_eround01(F44, F54, F56, F58); //round 11 + __ aes_eround23(F46, F54, F56, F60); + + // 192-bit original key size + __ cmp_and_brx_short(keylen, 52, Assembler::equal, Assembler::pt, L_storeOutput); + + __ ldf(FloatRegisterImpl::D, key, 208, F52); + __ aes_eround01(F48, F58, F60, F54); //round 12 + __ aes_eround23(F50, F58, F60, F56); + __ ldf(FloatRegisterImpl::D, key, 216, F46); + __ ldf(FloatRegisterImpl::D, key, 224, F48); + __ ldf(FloatRegisterImpl::D, key, 232, F50); + __ aes_eround01(F52, F54, F56, F58); //round 13 + __ aes_eround23(F46, F54, F56, F60); + __ ba_short(L_storeOutput); + + __ BIND(L_doLast128bit); + __ ldf(FloatRegisterImpl::D, key, 160, F48); + __ ldf(FloatRegisterImpl::D, key, 168, F50); + + __ BIND(L_storeOutput); + // perform last round of encryption common for all key sizes + __ aes_eround01_l(F48, F58, F60, F54); //last round + __ aes_eround23_l(F50, F58, F60, F56); + + // Method to address arbitrary alignment for store instructions: + // Check last 3 bits of 'dest' address to see if it is aligned to 8-byte boundary + // If zero/aligned then continue with double FP store instructions + // If not zero/mis-aligned then edge8n will generate edge mask in result reg (O3 in below case) + // Example: If dest address is 0x07 and nearest 8-byte aligned address is 0x00 then edge mask will be 00000001 + // Compute (8-n) where n is # of bytes skipped by partial store(stpartialf) inst from edge mask, n=7 in this case + // We get the value of n from the andcc that checks 'dest' alignment. n is available in O5 in below case. + // Set GSR.align to (8-n) using alignaddr + // Circular byte shift store values by n places so that the original bytes are at correct position for stpartialf + // Set the arbitrarily aligned 'dest' address to nearest 8-byte aligned address + // Store (partial) the original first (8-n) bytes starting at the original 'dest' address + // Negate the edge mask so that the subsequent stpartialf can store the original (8-n-1)th through 8th bytes at appropriate address + // We need to execute this process for both the 8-byte result values + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, O5); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output); + __ delayed()->edge8n(to, G0, O3); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F54, to, 0); + __ retl(); + __ delayed()->stf(FloatRegisterImpl::D, F56, to, 8); + + __ BIND(L_store_misaligned_output); + __ add(to, 8, O4); + __ mov(8, O2); + __ sub(O2, O5, O2); + __ alignaddr(O2, G0, O2); + __ faligndata(F54, F54, F54); + __ faligndata(F56, F56, F56); + __ and3(to, -8, to); + __ and3(O4, -8, O4); + __ stpartialf(to, O3, F54, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(O4, O3, F56, Assembler::ASI_PST8_PRIMARY); + __ add(to, 8, to); + __ add(O4, 8, O4); + __ orn(G0, O3, O3); + __ stpartialf(to, O3, F54, Assembler::ASI_PST8_PRIMARY); + __ retl(); + __ delayed()->stpartialf(O4, O3, F56, Assembler::ASI_PST8_PRIMARY); + + return start; + } + + address generate_aescrypt_decryptBlock() { + assert((arrayOopDesc::base_offset_in_bytes(T_INT) & 7) == 0, + "the following code assumes that first element of an int array is aligned to 8 bytes"); + // required since we read original key 'byte' array as well in the decryption stubs + assert((arrayOopDesc::base_offset_in_bytes(T_BYTE) & 7) == 0, + "the following code assumes that first element of a byte array is aligned to 8 bytes"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "aescrypt_decryptBlock"); + address start = __ pc(); + Label L_load_misaligned_input, L_load_original_key, L_expand192bit, L_expand256bit, L_reload_misaligned_input; + Label L_256bit_transform, L_common_transform, L_store_misaligned_output; + Register from = O0; // source byte array + Register to = O1; // destination byte array + Register key = O2; // expanded key array + Register original_key = O3; // original key array only required during decryption + const Register keylen = O4; // reg for storing expanded key array length + + // read expanded key array length + __ ldsw(Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)), keylen, 0); + + // save 'from' since we may need to recheck alignment in case of 256-bit decryption + __ mov(from, G1); + + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_input); + __ delayed()->alignaddr(from, G0, from); + + // aligned case: load input into F52-F54 + __ ldf(FloatRegisterImpl::D, from, 0, F52); + __ ldf(FloatRegisterImpl::D, from, 8, F54); + __ ba_short(L_load_original_key); + + __ BIND(L_load_misaligned_input); + __ ldf(FloatRegisterImpl::D, from, 0, F52); + __ ldf(FloatRegisterImpl::D, from, 8, F54); + __ ldf(FloatRegisterImpl::D, from, 16, F56); + __ faligndata(F52, F54, F52); + __ faligndata(F54, F56, F54); + + __ BIND(L_load_original_key); + // load original key from SunJCE expanded decryption key + // Since we load original key buffer starting first element, 8-byte alignment is guaranteed + for ( int i = 0; i <= 3; i++ ) { + __ ldf(FloatRegisterImpl::S, original_key, i*4, as_FloatRegister(i)); + } + + // 256-bit original key size + __ cmp_and_brx_short(keylen, 60, Assembler::equal, Assembler::pn, L_expand256bit); + + // 192-bit original key size + __ cmp_and_brx_short(keylen, 52, Assembler::equal, Assembler::pn, L_expand192bit); + + // 128-bit original key size + // perform key expansion since SunJCE decryption-key expansion is not compatible with SPARC crypto instructions + for ( int i = 0; i <= 36; i += 4 ) { + __ aes_kexpand1(as_FloatRegister(i), as_FloatRegister(i+2), i/4, as_FloatRegister(i+4)); + __ aes_kexpand2(as_FloatRegister(i+2), as_FloatRegister(i+4), as_FloatRegister(i+6)); + } + + // perform 128-bit key specific inverse cipher transformation + __ fxor(FloatRegisterImpl::D, F42, F54, F54); + __ fxor(FloatRegisterImpl::D, F40, F52, F52); + __ ba_short(L_common_transform); + + __ BIND(L_expand192bit); + + // start loading rest of the 192-bit key + __ ldf(FloatRegisterImpl::S, original_key, 16, F4); + __ ldf(FloatRegisterImpl::S, original_key, 20, F5); + + // perform key expansion since SunJCE decryption-key expansion is not compatible with SPARC crypto instructions + for ( int i = 0; i <= 36; i += 6 ) { + __ aes_kexpand1(as_FloatRegister(i), as_FloatRegister(i+4), i/6, as_FloatRegister(i+6)); + __ aes_kexpand2(as_FloatRegister(i+2), as_FloatRegister(i+6), as_FloatRegister(i+8)); + __ aes_kexpand2(as_FloatRegister(i+4), as_FloatRegister(i+8), as_FloatRegister(i+10)); + } + __ aes_kexpand1(F42, F46, 7, F48); + __ aes_kexpand2(F44, F48, F50); + + // perform 192-bit key specific inverse cipher transformation + __ fxor(FloatRegisterImpl::D, F50, F54, F54); + __ fxor(FloatRegisterImpl::D, F48, F52, F52); + __ aes_dround23(F46, F52, F54, F58); + __ aes_dround01(F44, F52, F54, F56); + __ aes_dround23(F42, F56, F58, F54); + __ aes_dround01(F40, F56, F58, F52); + __ ba_short(L_common_transform); + + __ BIND(L_expand256bit); + + // load rest of the 256-bit key + for ( int i = 4; i <= 7; i++ ) { + __ ldf(FloatRegisterImpl::S, original_key, i*4, as_FloatRegister(i)); + } + + // perform key expansion since SunJCE decryption-key expansion is not compatible with SPARC crypto instructions + for ( int i = 0; i <= 40; i += 8 ) { + __ aes_kexpand1(as_FloatRegister(i), as_FloatRegister(i+6), i/8, as_FloatRegister(i+8)); + __ aes_kexpand2(as_FloatRegister(i+2), as_FloatRegister(i+8), as_FloatRegister(i+10)); + __ aes_kexpand0(as_FloatRegister(i+4), as_FloatRegister(i+10), as_FloatRegister(i+12)); + __ aes_kexpand2(as_FloatRegister(i+6), as_FloatRegister(i+12), as_FloatRegister(i+14)); + } + __ aes_kexpand1(F48, F54, 6, F56); + __ aes_kexpand2(F50, F56, F58); + + for ( int i = 0; i <= 6; i += 2 ) { + __ fsrc2(FloatRegisterImpl::D, as_FloatRegister(58-i), as_FloatRegister(i)); + } + + // reload original 'from' address + __ mov(G1, from); + + // re-check 8-byte alignment + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_reload_misaligned_input); + __ delayed()->alignaddr(from, G0, from); + + // aligned case: load input into F52-F54 + __ ldf(FloatRegisterImpl::D, from, 0, F52); + __ ldf(FloatRegisterImpl::D, from, 8, F54); + __ ba_short(L_256bit_transform); + + __ BIND(L_reload_misaligned_input); + __ ldf(FloatRegisterImpl::D, from, 0, F52); + __ ldf(FloatRegisterImpl::D, from, 8, F54); + __ ldf(FloatRegisterImpl::D, from, 16, F56); + __ faligndata(F52, F54, F52); + __ faligndata(F54, F56, F54); + + // perform 256-bit key specific inverse cipher transformation + __ BIND(L_256bit_transform); + __ fxor(FloatRegisterImpl::D, F0, F54, F54); + __ fxor(FloatRegisterImpl::D, F2, F52, F52); + __ aes_dround23(F4, F52, F54, F58); + __ aes_dround01(F6, F52, F54, F56); + __ aes_dround23(F50, F56, F58, F54); + __ aes_dround01(F48, F56, F58, F52); + __ aes_dround23(F46, F52, F54, F58); + __ aes_dround01(F44, F52, F54, F56); + __ aes_dround23(F42, F56, F58, F54); + __ aes_dround01(F40, F56, F58, F52); + + for ( int i = 0; i <= 7; i++ ) { + __ ldf(FloatRegisterImpl::S, original_key, i*4, as_FloatRegister(i)); + } + + // perform inverse cipher transformations common for all key sizes + __ BIND(L_common_transform); + for ( int i = 38; i >= 6; i -= 8 ) { + __ aes_dround23(as_FloatRegister(i), F52, F54, F58); + __ aes_dround01(as_FloatRegister(i-2), F52, F54, F56); + if ( i != 6) { + __ aes_dround23(as_FloatRegister(i-4), F56, F58, F54); + __ aes_dround01(as_FloatRegister(i-6), F56, F58, F52); + } else { + __ aes_dround23_l(as_FloatRegister(i-4), F56, F58, F54); + __ aes_dround01_l(as_FloatRegister(i-6), F56, F58, F52); + } + } + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, O5); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output); + __ delayed()->edge8n(to, G0, O3); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F52, to, 0); + __ retl(); + __ delayed()->stf(FloatRegisterImpl::D, F54, to, 8); + + __ BIND(L_store_misaligned_output); + __ add(to, 8, O4); + __ mov(8, O2); + __ sub(O2, O5, O2); + __ alignaddr(O2, G0, O2); + __ faligndata(F52, F52, F52); + __ faligndata(F54, F54, F54); + __ and3(to, -8, to); + __ and3(O4, -8, O4); + __ stpartialf(to, O3, F52, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(O4, O3, F54, Assembler::ASI_PST8_PRIMARY); + __ add(to, 8, to); + __ add(O4, 8, O4); + __ orn(G0, O3, O3); + __ stpartialf(to, O3, F52, Assembler::ASI_PST8_PRIMARY); + __ retl(); + __ delayed()->stpartialf(O4, O3, F54, Assembler::ASI_PST8_PRIMARY); + + return start; + } + + address generate_cipherBlockChaining_encryptAESCrypt() { + assert((arrayOopDesc::base_offset_in_bytes(T_INT) & 7) == 0, + "the following code assumes that first element of an int array is aligned to 8 bytes"); + assert((arrayOopDesc::base_offset_in_bytes(T_BYTE) & 7) == 0, + "the following code assumes that first element of a byte array is aligned to 8 bytes"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "cipherBlockChaining_encryptAESCrypt"); + Label L_cbcenc128, L_load_misaligned_input_128bit, L_128bit_transform, L_store_misaligned_output_128bit; + Label L_check_loop_end_128bit, L_cbcenc192, L_load_misaligned_input_192bit, L_192bit_transform; + Label L_store_misaligned_output_192bit, L_check_loop_end_192bit, L_cbcenc256, L_load_misaligned_input_256bit; + Label L_256bit_transform, L_store_misaligned_output_256bit, L_check_loop_end_256bit; + address start = __ pc(); + Register from = I0; // source byte array + Register to = I1; // destination byte array + Register key = I2; // expanded key array + Register rvec = I3; // init vector + const Register len_reg = I4; // cipher length + const Register keylen = I5; // reg for storing expanded key array length + + __ save_frame(0); + // save cipher len to return in the end + __ mov(len_reg, L0); + + // read expanded key length + __ ldsw(Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)), keylen, 0); + + // load initial vector, 8-byte alignment is guranteed + __ ldf(FloatRegisterImpl::D, rvec, 0, F60); + __ ldf(FloatRegisterImpl::D, rvec, 8, F62); + // load key, 8-byte alignment is guranteed + __ ldx(key,0,G1); + __ ldx(key,8,G5); + + // start loading expanded key, 8-byte alignment is guranteed + for ( int i = 0, j = 16; i <= 38; i += 2, j += 8 ) { + __ ldf(FloatRegisterImpl::D, key, j, as_FloatRegister(i)); + } + + // 128-bit original key size + __ cmp_and_brx_short(keylen, 44, Assembler::equal, Assembler::pt, L_cbcenc128); + + for ( int i = 40, j = 176; i <= 46; i += 2, j += 8 ) { + __ ldf(FloatRegisterImpl::D, key, j, as_FloatRegister(i)); + } + + // 192-bit original key size + __ cmp_and_brx_short(keylen, 52, Assembler::equal, Assembler::pt, L_cbcenc192); + + for ( int i = 48, j = 208; i <= 54; i += 2, j += 8 ) { + __ ldf(FloatRegisterImpl::D, key, j, as_FloatRegister(i)); + } + + // 256-bit original key size + __ ba_short(L_cbcenc256); + + __ align(OptoLoopAlignment); + __ BIND(L_cbcenc128); + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_input_128bit); + __ delayed()->mov(from, L1); // save original 'from' address before alignaddr + + // aligned case: load input into G3 and G4 + __ ldx(from,0,G3); + __ ldx(from,8,G4); + __ ba_short(L_128bit_transform); + + __ BIND(L_load_misaligned_input_128bit); + // can clobber F48, F50 and F52 as they are not used in 128 and 192-bit key encryption + __ alignaddr(from, G0, from); + __ ldf(FloatRegisterImpl::D, from, 0, F48); + __ ldf(FloatRegisterImpl::D, from, 8, F50); + __ ldf(FloatRegisterImpl::D, from, 16, F52); + __ faligndata(F48, F50, F48); + __ faligndata(F50, F52, F50); + __ movdtox(F48, G3); + __ movdtox(F50, G4); + __ mov(L1, from); + + __ BIND(L_128bit_transform); + __ xor3(G1,G3,G3); + __ xor3(G5,G4,G4); + __ movxtod(G3,F56); + __ movxtod(G4,F58); + __ fxor(FloatRegisterImpl::D, F60, F56, F60); + __ fxor(FloatRegisterImpl::D, F62, F58, F62); + + // TEN_EROUNDS + for ( int i = 0; i <= 32; i += 8 ) { + __ aes_eround01(as_FloatRegister(i), F60, F62, F56); + __ aes_eround23(as_FloatRegister(i+2), F60, F62, F58); + if (i != 32 ) { + __ aes_eround01(as_FloatRegister(i+4), F56, F58, F60); + __ aes_eround23(as_FloatRegister(i+6), F56, F58, F62); + } else { + __ aes_eround01_l(as_FloatRegister(i+4), F56, F58, F60); + __ aes_eround23_l(as_FloatRegister(i+6), F56, F58, F62); + } + } + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, L1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_128bit); + __ delayed()->edge8n(to, G0, L2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F60, to, 0); + __ stf(FloatRegisterImpl::D, F62, to, 8); + __ ba_short(L_check_loop_end_128bit); + + __ BIND(L_store_misaligned_output_128bit); + __ add(to, 8, L3); + __ mov(8, L4); + __ sub(L4, L1, L4); + __ alignaddr(L4, G0, L4); + // save cipher text before circular right shift + // as it needs to be stored as iv for next block (see code before next retl) + __ movdtox(F60, L6); + __ movdtox(F62, L7); + __ faligndata(F60, F60, F60); + __ faligndata(F62, F62, F62); + __ mov(to, L5); + __ and3(to, -8, to); + __ and3(L3, -8, L3); + __ stpartialf(to, L2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(L3, L2, F62, Assembler::ASI_PST8_PRIMARY); + __ add(to, 8, to); + __ add(L3, 8, L3); + __ orn(G0, L2, L2); + __ stpartialf(to, L2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(L3, L2, F62, Assembler::ASI_PST8_PRIMARY); + __ mov(L5, to); + __ movxtod(L6, F60); + __ movxtod(L7, F62); + + __ BIND(L_check_loop_end_128bit); + __ add(from, 16, from); + __ add(to, 16, to); + __ subcc(len_reg, 16, len_reg); + __ br(Assembler::notEqual, false, Assembler::pt, L_cbcenc128); + __ delayed()->nop(); + // re-init intial vector for next block, 8-byte alignment is guaranteed + __ stf(FloatRegisterImpl::D, F60, rvec, 0); + __ stf(FloatRegisterImpl::D, F62, rvec, 8); + __ mov(L0, I0); + __ ret(); + __ delayed()->restore(); + + __ align(OptoLoopAlignment); + __ BIND(L_cbcenc192); + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_input_192bit); + __ delayed()->mov(from, L1); // save original 'from' address before alignaddr + + // aligned case: load input into G3 and G4 + __ ldx(from,0,G3); + __ ldx(from,8,G4); + __ ba_short(L_192bit_transform); + + __ BIND(L_load_misaligned_input_192bit); + // can clobber F48, F50 and F52 as they are not used in 128 and 192-bit key encryption + __ alignaddr(from, G0, from); + __ ldf(FloatRegisterImpl::D, from, 0, F48); + __ ldf(FloatRegisterImpl::D, from, 8, F50); + __ ldf(FloatRegisterImpl::D, from, 16, F52); + __ faligndata(F48, F50, F48); + __ faligndata(F50, F52, F50); + __ movdtox(F48, G3); + __ movdtox(F50, G4); + __ mov(L1, from); + + __ BIND(L_192bit_transform); + __ xor3(G1,G3,G3); + __ xor3(G5,G4,G4); + __ movxtod(G3,F56); + __ movxtod(G4,F58); + __ fxor(FloatRegisterImpl::D, F60, F56, F60); + __ fxor(FloatRegisterImpl::D, F62, F58, F62); + + // TWELEVE_EROUNDS + for ( int i = 0; i <= 40; i += 8 ) { + __ aes_eround01(as_FloatRegister(i), F60, F62, F56); + __ aes_eround23(as_FloatRegister(i+2), F60, F62, F58); + if (i != 40 ) { + __ aes_eround01(as_FloatRegister(i+4), F56, F58, F60); + __ aes_eround23(as_FloatRegister(i+6), F56, F58, F62); + } else { + __ aes_eround01_l(as_FloatRegister(i+4), F56, F58, F60); + __ aes_eround23_l(as_FloatRegister(i+6), F56, F58, F62); + } + } + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, L1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_192bit); + __ delayed()->edge8n(to, G0, L2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F60, to, 0); + __ stf(FloatRegisterImpl::D, F62, to, 8); + __ ba_short(L_check_loop_end_192bit); + + __ BIND(L_store_misaligned_output_192bit); + __ add(to, 8, L3); + __ mov(8, L4); + __ sub(L4, L1, L4); + __ alignaddr(L4, G0, L4); + __ movdtox(F60, L6); + __ movdtox(F62, L7); + __ faligndata(F60, F60, F60); + __ faligndata(F62, F62, F62); + __ mov(to, L5); + __ and3(to, -8, to); + __ and3(L3, -8, L3); + __ stpartialf(to, L2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(L3, L2, F62, Assembler::ASI_PST8_PRIMARY); + __ add(to, 8, to); + __ add(L3, 8, L3); + __ orn(G0, L2, L2); + __ stpartialf(to, L2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(L3, L2, F62, Assembler::ASI_PST8_PRIMARY); + __ mov(L5, to); + __ movxtod(L6, F60); + __ movxtod(L7, F62); + + __ BIND(L_check_loop_end_192bit); + __ add(from, 16, from); + __ subcc(len_reg, 16, len_reg); + __ add(to, 16, to); + __ br(Assembler::notEqual, false, Assembler::pt, L_cbcenc192); + __ delayed()->nop(); + // re-init intial vector for next block, 8-byte alignment is guaranteed + __ stf(FloatRegisterImpl::D, F60, rvec, 0); + __ stf(FloatRegisterImpl::D, F62, rvec, 8); + __ mov(L0, I0); + __ ret(); + __ delayed()->restore(); + + __ align(OptoLoopAlignment); + __ BIND(L_cbcenc256); + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_input_256bit); + __ delayed()->mov(from, L1); // save original 'from' address before alignaddr + + // aligned case: load input into G3 and G4 + __ ldx(from,0,G3); + __ ldx(from,8,G4); + __ ba_short(L_256bit_transform); + + __ BIND(L_load_misaligned_input_256bit); + // cannot clobber F48, F50 and F52. F56, F58 can be used though + __ alignaddr(from, G0, from); + __ movdtox(F60, L2); // save F60 before overwriting + __ ldf(FloatRegisterImpl::D, from, 0, F56); + __ ldf(FloatRegisterImpl::D, from, 8, F58); + __ ldf(FloatRegisterImpl::D, from, 16, F60); + __ faligndata(F56, F58, F56); + __ faligndata(F58, F60, F58); + __ movdtox(F56, G3); + __ movdtox(F58, G4); + __ mov(L1, from); + __ movxtod(L2, F60); + + __ BIND(L_256bit_transform); + __ xor3(G1,G3,G3); + __ xor3(G5,G4,G4); + __ movxtod(G3,F56); + __ movxtod(G4,F58); + __ fxor(FloatRegisterImpl::D, F60, F56, F60); + __ fxor(FloatRegisterImpl::D, F62, F58, F62); + + // FOURTEEN_EROUNDS + for ( int i = 0; i <= 48; i += 8 ) { + __ aes_eround01(as_FloatRegister(i), F60, F62, F56); + __ aes_eround23(as_FloatRegister(i+2), F60, F62, F58); + if (i != 48 ) { + __ aes_eround01(as_FloatRegister(i+4), F56, F58, F60); + __ aes_eround23(as_FloatRegister(i+6), F56, F58, F62); + } else { + __ aes_eround01_l(as_FloatRegister(i+4), F56, F58, F60); + __ aes_eround23_l(as_FloatRegister(i+6), F56, F58, F62); + } + } + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, L1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_256bit); + __ delayed()->edge8n(to, G0, L2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F60, to, 0); + __ stf(FloatRegisterImpl::D, F62, to, 8); + __ ba_short(L_check_loop_end_256bit); + + __ BIND(L_store_misaligned_output_256bit); + __ add(to, 8, L3); + __ mov(8, L4); + __ sub(L4, L1, L4); + __ alignaddr(L4, G0, L4); + __ movdtox(F60, L6); + __ movdtox(F62, L7); + __ faligndata(F60, F60, F60); + __ faligndata(F62, F62, F62); + __ mov(to, L5); + __ and3(to, -8, to); + __ and3(L3, -8, L3); + __ stpartialf(to, L2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(L3, L2, F62, Assembler::ASI_PST8_PRIMARY); + __ add(to, 8, to); + __ add(L3, 8, L3); + __ orn(G0, L2, L2); + __ stpartialf(to, L2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(L3, L2, F62, Assembler::ASI_PST8_PRIMARY); + __ mov(L5, to); + __ movxtod(L6, F60); + __ movxtod(L7, F62); + + __ BIND(L_check_loop_end_256bit); + __ add(from, 16, from); + __ subcc(len_reg, 16, len_reg); + __ add(to, 16, to); + __ br(Assembler::notEqual, false, Assembler::pt, L_cbcenc256); + __ delayed()->nop(); + // re-init intial vector for next block, 8-byte alignment is guaranteed + __ stf(FloatRegisterImpl::D, F60, rvec, 0); + __ stf(FloatRegisterImpl::D, F62, rvec, 8); + __ mov(L0, I0); + __ ret(); + __ delayed()->restore(); + + return start; + } + + address generate_cipherBlockChaining_decryptAESCrypt_Parallel() { + assert((arrayOopDesc::base_offset_in_bytes(T_INT) & 7) == 0, + "the following code assumes that first element of an int array is aligned to 8 bytes"); + assert((arrayOopDesc::base_offset_in_bytes(T_BYTE) & 7) == 0, + "the following code assumes that first element of a byte array is aligned to 8 bytes"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "cipherBlockChaining_decryptAESCrypt"); + Label L_cbcdec_end, L_expand192bit, L_expand256bit, L_dec_first_block_start; + Label L_dec_first_block128, L_dec_first_block192, L_dec_next2_blocks128, L_dec_next2_blocks192, L_dec_next2_blocks256; + Label L_load_misaligned_input_first_block, L_transform_first_block, L_load_misaligned_next2_blocks128, L_transform_next2_blocks128; + Label L_load_misaligned_next2_blocks192, L_transform_next2_blocks192, L_load_misaligned_next2_blocks256, L_transform_next2_blocks256; + Label L_store_misaligned_output_first_block, L_check_decrypt_end, L_store_misaligned_output_next2_blocks128; + Label L_check_decrypt_loop_end128, L_store_misaligned_output_next2_blocks192, L_check_decrypt_loop_end192; + Label L_store_misaligned_output_next2_blocks256, L_check_decrypt_loop_end256; + address start = __ pc(); + Register from = I0; // source byte array + Register to = I1; // destination byte array + Register key = I2; // expanded key array + Register rvec = I3; // init vector + const Register len_reg = I4; // cipher length + const Register original_key = I5; // original key array only required during decryption + const Register keylen = L6; // reg for storing expanded key array length + + __ save_frame(0); //args are read from I* registers since we save the frame in the beginning + // save cipher len to return in the end + __ mov(len_reg, L7); + + // load original key from SunJCE expanded decryption key + // Since we load original key buffer starting first element, 8-byte alignment is guaranteed + for ( int i = 0; i <= 3; i++ ) { + __ ldf(FloatRegisterImpl::S, original_key, i*4, as_FloatRegister(i)); + } + + // load initial vector, 8-byte alignment is guaranteed + __ ldx(rvec,0,L0); + __ ldx(rvec,8,L1); + + // read expanded key array length + __ ldsw(Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)), keylen, 0); + + // 256-bit original key size + __ cmp_and_brx_short(keylen, 60, Assembler::equal, Assembler::pn, L_expand256bit); + + // 192-bit original key size + __ cmp_and_brx_short(keylen, 52, Assembler::equal, Assembler::pn, L_expand192bit); + + // 128-bit original key size + // perform key expansion since SunJCE decryption-key expansion is not compatible with SPARC crypto instructions + for ( int i = 0; i <= 36; i += 4 ) { + __ aes_kexpand1(as_FloatRegister(i), as_FloatRegister(i+2), i/4, as_FloatRegister(i+4)); + __ aes_kexpand2(as_FloatRegister(i+2), as_FloatRegister(i+4), as_FloatRegister(i+6)); + } + + // load expanded key[last-1] and key[last] elements + __ movdtox(F40,L2); + __ movdtox(F42,L3); + + __ and3(len_reg, 16, L4); + __ br_null_short(L4, Assembler::pt, L_dec_next2_blocks128); + __ nop(); + + __ ba_short(L_dec_first_block_start); + + __ BIND(L_expand192bit); + // load rest of the 192-bit key + __ ldf(FloatRegisterImpl::S, original_key, 16, F4); + __ ldf(FloatRegisterImpl::S, original_key, 20, F5); + + // perform key expansion since SunJCE decryption-key expansion is not compatible with SPARC crypto instructions + for ( int i = 0; i <= 36; i += 6 ) { + __ aes_kexpand1(as_FloatRegister(i), as_FloatRegister(i+4), i/6, as_FloatRegister(i+6)); + __ aes_kexpand2(as_FloatRegister(i+2), as_FloatRegister(i+6), as_FloatRegister(i+8)); + __ aes_kexpand2(as_FloatRegister(i+4), as_FloatRegister(i+8), as_FloatRegister(i+10)); + } + __ aes_kexpand1(F42, F46, 7, F48); + __ aes_kexpand2(F44, F48, F50); + + // load expanded key[last-1] and key[last] elements + __ movdtox(F48,L2); + __ movdtox(F50,L3); + + __ and3(len_reg, 16, L4); + __ br_null_short(L4, Assembler::pt, L_dec_next2_blocks192); + __ nop(); + + __ ba_short(L_dec_first_block_start); + + __ BIND(L_expand256bit); + // load rest of the 256-bit key + for ( int i = 4; i <= 7; i++ ) { + __ ldf(FloatRegisterImpl::S, original_key, i*4, as_FloatRegister(i)); + } + + // perform key expansion since SunJCE decryption-key expansion is not compatible with SPARC crypto instructions + for ( int i = 0; i <= 40; i += 8 ) { + __ aes_kexpand1(as_FloatRegister(i), as_FloatRegister(i+6), i/8, as_FloatRegister(i+8)); + __ aes_kexpand2(as_FloatRegister(i+2), as_FloatRegister(i+8), as_FloatRegister(i+10)); + __ aes_kexpand0(as_FloatRegister(i+4), as_FloatRegister(i+10), as_FloatRegister(i+12)); + __ aes_kexpand2(as_FloatRegister(i+6), as_FloatRegister(i+12), as_FloatRegister(i+14)); + } + __ aes_kexpand1(F48, F54, 6, F56); + __ aes_kexpand2(F50, F56, F58); + + // load expanded key[last-1] and key[last] elements + __ movdtox(F56,L2); + __ movdtox(F58,L3); + + __ and3(len_reg, 16, L4); + __ br_null_short(L4, Assembler::pt, L_dec_next2_blocks256); + + __ BIND(L_dec_first_block_start); + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_input_first_block); + __ delayed()->mov(from, G1); // save original 'from' address before alignaddr + + // aligned case: load input into L4 and L5 + __ ldx(from,0,L4); + __ ldx(from,8,L5); + __ ba_short(L_transform_first_block); + + __ BIND(L_load_misaligned_input_first_block); + __ alignaddr(from, G0, from); + // F58, F60, F62 can be clobbered + __ ldf(FloatRegisterImpl::D, from, 0, F58); + __ ldf(FloatRegisterImpl::D, from, 8, F60); + __ ldf(FloatRegisterImpl::D, from, 16, F62); + __ faligndata(F58, F60, F58); + __ faligndata(F60, F62, F60); + __ movdtox(F58, L4); + __ movdtox(F60, L5); + __ mov(G1, from); + + __ BIND(L_transform_first_block); + __ xor3(L2,L4,G1); + __ movxtod(G1,F60); + __ xor3(L3,L5,G1); + __ movxtod(G1,F62); + + // 128-bit original key size + __ cmp_and_brx_short(keylen, 44, Assembler::equal, Assembler::pn, L_dec_first_block128); + + // 192-bit original key size + __ cmp_and_brx_short(keylen, 52, Assembler::equal, Assembler::pn, L_dec_first_block192); + + __ aes_dround23(F54, F60, F62, F58); + __ aes_dround01(F52, F60, F62, F56); + __ aes_dround23(F50, F56, F58, F62); + __ aes_dround01(F48, F56, F58, F60); + + __ BIND(L_dec_first_block192); + __ aes_dround23(F46, F60, F62, F58); + __ aes_dround01(F44, F60, F62, F56); + __ aes_dround23(F42, F56, F58, F62); + __ aes_dround01(F40, F56, F58, F60); + + __ BIND(L_dec_first_block128); + for ( int i = 38; i >= 6; i -= 8 ) { + __ aes_dround23(as_FloatRegister(i), F60, F62, F58); + __ aes_dround01(as_FloatRegister(i-2), F60, F62, F56); + if ( i != 6) { + __ aes_dround23(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01(as_FloatRegister(i-6), F56, F58, F60); + } else { + __ aes_dround23_l(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01_l(as_FloatRegister(i-6), F56, F58, F60); + } + } + + __ movxtod(L0,F56); + __ movxtod(L1,F58); + __ mov(L4,L0); + __ mov(L5,L1); + __ fxor(FloatRegisterImpl::D, F56, F60, F60); + __ fxor(FloatRegisterImpl::D, F58, F62, F62); + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, G1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_first_block); + __ delayed()->edge8n(to, G0, G2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F60, to, 0); + __ stf(FloatRegisterImpl::D, F62, to, 8); + __ ba_short(L_check_decrypt_end); + + __ BIND(L_store_misaligned_output_first_block); + __ add(to, 8, G3); + __ mov(8, G4); + __ sub(G4, G1, G4); + __ alignaddr(G4, G0, G4); + __ faligndata(F60, F60, F60); + __ faligndata(F62, F62, F62); + __ mov(to, G1); + __ and3(to, -8, to); + __ and3(G3, -8, G3); + __ stpartialf(to, G2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(G3, G2, F62, Assembler::ASI_PST8_PRIMARY); + __ add(to, 8, to); + __ add(G3, 8, G3); + __ orn(G0, G2, G2); + __ stpartialf(to, G2, F60, Assembler::ASI_PST8_PRIMARY); + __ stpartialf(G3, G2, F62, Assembler::ASI_PST8_PRIMARY); + __ mov(G1, to); + + __ BIND(L_check_decrypt_end); + __ add(from, 16, from); + __ add(to, 16, to); + __ subcc(len_reg, 16, len_reg); + __ br(Assembler::equal, false, Assembler::pt, L_cbcdec_end); + __ delayed()->nop(); + + // 256-bit original key size + __ cmp_and_brx_short(keylen, 60, Assembler::equal, Assembler::pn, L_dec_next2_blocks256); + + // 192-bit original key size + __ cmp_and_brx_short(keylen, 52, Assembler::equal, Assembler::pn, L_dec_next2_blocks192); + + __ align(OptoLoopAlignment); + __ BIND(L_dec_next2_blocks128); + __ nop(); + + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_next2_blocks128); + __ delayed()->mov(from, G1); // save original 'from' address before alignaddr + + // aligned case: load input into G4, G5, L4 and L5 + __ ldx(from,0,G4); + __ ldx(from,8,G5); + __ ldx(from,16,L4); + __ ldx(from,24,L5); + __ ba_short(L_transform_next2_blocks128); + + __ BIND(L_load_misaligned_next2_blocks128); + __ alignaddr(from, G0, from); + // F40, F42, F58, F60, F62 can be clobbered + __ ldf(FloatRegisterImpl::D, from, 0, F40); + __ ldf(FloatRegisterImpl::D, from, 8, F42); + __ ldf(FloatRegisterImpl::D, from, 16, F60); + __ ldf(FloatRegisterImpl::D, from, 24, F62); + __ ldf(FloatRegisterImpl::D, from, 32, F58); + __ faligndata(F40, F42, F40); + __ faligndata(F42, F60, F42); + __ faligndata(F60, F62, F60); + __ faligndata(F62, F58, F62); + __ movdtox(F40, G4); + __ movdtox(F42, G5); + __ movdtox(F60, L4); + __ movdtox(F62, L5); + __ mov(G1, from); + + __ BIND(L_transform_next2_blocks128); + // F40:F42 used for first 16-bytes + __ xor3(L2,G4,G1); + __ movxtod(G1,F40); + __ xor3(L3,G5,G1); + __ movxtod(G1,F42); + + // F60:F62 used for next 16-bytes + __ xor3(L2,L4,G1); + __ movxtod(G1,F60); + __ xor3(L3,L5,G1); + __ movxtod(G1,F62); + + for ( int i = 38; i >= 6; i -= 8 ) { + __ aes_dround23(as_FloatRegister(i), F40, F42, F44); + __ aes_dround01(as_FloatRegister(i-2), F40, F42, F46); + __ aes_dround23(as_FloatRegister(i), F60, F62, F58); + __ aes_dround01(as_FloatRegister(i-2), F60, F62, F56); + if (i != 6 ) { + __ aes_dround23(as_FloatRegister(i-4), F46, F44, F42); + __ aes_dround01(as_FloatRegister(i-6), F46, F44, F40); + __ aes_dround23(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01(as_FloatRegister(i-6), F56, F58, F60); + } else { + __ aes_dround23_l(as_FloatRegister(i-4), F46, F44, F42); + __ aes_dround01_l(as_FloatRegister(i-6), F46, F44, F40); + __ aes_dround23_l(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01_l(as_FloatRegister(i-6), F56, F58, F60); + } + } + + __ movxtod(L0,F46); + __ movxtod(L1,F44); + __ fxor(FloatRegisterImpl::D, F46, F40, F40); + __ fxor(FloatRegisterImpl::D, F44, F42, F42); + + __ movxtod(G4,F56); + __ movxtod(G5,F58); + __ mov(L4,L0); + __ mov(L5,L1); + __ fxor(FloatRegisterImpl::D, F56, F60, F60); + __ fxor(FloatRegisterImpl::D, F58, F62, F62); + + // For mis-aligned store of 32 bytes of result we can do: + // Circular right-shift all 4 FP registers so that 'head' and 'tail' + // parts that need to be stored starting at mis-aligned address are in a FP reg + // the other 3 FP regs can thus be stored using regular store + // we then use the edge + partial-store mechanism to store the 'head' and 'tail' parts + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, G1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_next2_blocks128); + __ delayed()->edge8n(to, G0, G2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F40, to, 0); + __ stf(FloatRegisterImpl::D, F42, to, 8); + __ stf(FloatRegisterImpl::D, F60, to, 16); + __ stf(FloatRegisterImpl::D, F62, to, 24); + __ ba_short(L_check_decrypt_loop_end128); + + __ BIND(L_store_misaligned_output_next2_blocks128); + __ mov(8, G4); + __ sub(G4, G1, G4); + __ alignaddr(G4, G0, G4); + __ faligndata(F40, F42, F56); // F56 can be clobbered + __ faligndata(F42, F60, F42); + __ faligndata(F60, F62, F60); + __ faligndata(F62, F40, F40); + __ mov(to, G1); + __ and3(to, -8, to); + __ stpartialf(to, G2, F40, Assembler::ASI_PST8_PRIMARY); + __ stf(FloatRegisterImpl::D, F56, to, 8); + __ stf(FloatRegisterImpl::D, F42, to, 16); + __ stf(FloatRegisterImpl::D, F60, to, 24); + __ add(to, 32, to); + __ orn(G0, G2, G2); + __ stpartialf(to, G2, F40, Assembler::ASI_PST8_PRIMARY); + __ mov(G1, to); + + __ BIND(L_check_decrypt_loop_end128); + __ add(from, 32, from); + __ add(to, 32, to); + __ subcc(len_reg, 32, len_reg); + __ br(Assembler::notEqual, false, Assembler::pt, L_dec_next2_blocks128); + __ delayed()->nop(); + __ ba_short(L_cbcdec_end); + + __ align(OptoLoopAlignment); + __ BIND(L_dec_next2_blocks192); + __ nop(); + + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_next2_blocks192); + __ delayed()->mov(from, G1); // save original 'from' address before alignaddr + + // aligned case: load input into G4, G5, L4 and L5 + __ ldx(from,0,G4); + __ ldx(from,8,G5); + __ ldx(from,16,L4); + __ ldx(from,24,L5); + __ ba_short(L_transform_next2_blocks192); + + __ BIND(L_load_misaligned_next2_blocks192); + __ alignaddr(from, G0, from); + // F48, F50, F52, F60, F62 can be clobbered + __ ldf(FloatRegisterImpl::D, from, 0, F48); + __ ldf(FloatRegisterImpl::D, from, 8, F50); + __ ldf(FloatRegisterImpl::D, from, 16, F60); + __ ldf(FloatRegisterImpl::D, from, 24, F62); + __ ldf(FloatRegisterImpl::D, from, 32, F52); + __ faligndata(F48, F50, F48); + __ faligndata(F50, F60, F50); + __ faligndata(F60, F62, F60); + __ faligndata(F62, F52, F62); + __ movdtox(F48, G4); + __ movdtox(F50, G5); + __ movdtox(F60, L4); + __ movdtox(F62, L5); + __ mov(G1, from); + + __ BIND(L_transform_next2_blocks192); + // F48:F50 used for first 16-bytes + __ xor3(L2,G4,G1); + __ movxtod(G1,F48); + __ xor3(L3,G5,G1); + __ movxtod(G1,F50); + + // F60:F62 used for next 16-bytes + __ xor3(L2,L4,G1); + __ movxtod(G1,F60); + __ xor3(L3,L5,G1); + __ movxtod(G1,F62); + + for ( int i = 46; i >= 6; i -= 8 ) { + __ aes_dround23(as_FloatRegister(i), F48, F50, F52); + __ aes_dround01(as_FloatRegister(i-2), F48, F50, F54); + __ aes_dround23(as_FloatRegister(i), F60, F62, F58); + __ aes_dround01(as_FloatRegister(i-2), F60, F62, F56); + if (i != 6 ) { + __ aes_dround23(as_FloatRegister(i-4), F54, F52, F50); + __ aes_dround01(as_FloatRegister(i-6), F54, F52, F48); + __ aes_dround23(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01(as_FloatRegister(i-6), F56, F58, F60); + } else { + __ aes_dround23_l(as_FloatRegister(i-4), F54, F52, F50); + __ aes_dround01_l(as_FloatRegister(i-6), F54, F52, F48); + __ aes_dround23_l(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01_l(as_FloatRegister(i-6), F56, F58, F60); + } + } + + __ movxtod(L0,F54); + __ movxtod(L1,F52); + __ fxor(FloatRegisterImpl::D, F54, F48, F48); + __ fxor(FloatRegisterImpl::D, F52, F50, F50); + + __ movxtod(G4,F56); + __ movxtod(G5,F58); + __ mov(L4,L0); + __ mov(L5,L1); + __ fxor(FloatRegisterImpl::D, F56, F60, F60); + __ fxor(FloatRegisterImpl::D, F58, F62, F62); + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, G1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_next2_blocks192); + __ delayed()->edge8n(to, G0, G2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F48, to, 0); + __ stf(FloatRegisterImpl::D, F50, to, 8); + __ stf(FloatRegisterImpl::D, F60, to, 16); + __ stf(FloatRegisterImpl::D, F62, to, 24); + __ ba_short(L_check_decrypt_loop_end192); + + __ BIND(L_store_misaligned_output_next2_blocks192); + __ mov(8, G4); + __ sub(G4, G1, G4); + __ alignaddr(G4, G0, G4); + __ faligndata(F48, F50, F56); // F56 can be clobbered + __ faligndata(F50, F60, F50); + __ faligndata(F60, F62, F60); + __ faligndata(F62, F48, F48); + __ mov(to, G1); + __ and3(to, -8, to); + __ stpartialf(to, G2, F48, Assembler::ASI_PST8_PRIMARY); + __ stf(FloatRegisterImpl::D, F56, to, 8); + __ stf(FloatRegisterImpl::D, F50, to, 16); + __ stf(FloatRegisterImpl::D, F60, to, 24); + __ add(to, 32, to); + __ orn(G0, G2, G2); + __ stpartialf(to, G2, F48, Assembler::ASI_PST8_PRIMARY); + __ mov(G1, to); + + __ BIND(L_check_decrypt_loop_end192); + __ add(from, 32, from); + __ add(to, 32, to); + __ subcc(len_reg, 32, len_reg); + __ br(Assembler::notEqual, false, Assembler::pt, L_dec_next2_blocks192); + __ delayed()->nop(); + __ ba_short(L_cbcdec_end); + + __ align(OptoLoopAlignment); + __ BIND(L_dec_next2_blocks256); + __ nop(); + + // check for 8-byte alignment since source byte array may have an arbitrary alignment if offset mod 8 is non-zero + __ andcc(from, 7, G0); + __ br(Assembler::notZero, true, Assembler::pn, L_load_misaligned_next2_blocks256); + __ delayed()->mov(from, G1); // save original 'from' address before alignaddr + + // aligned case: load input into G4, G5, L4 and L5 + __ ldx(from,0,G4); + __ ldx(from,8,G5); + __ ldx(from,16,L4); + __ ldx(from,24,L5); + __ ba_short(L_transform_next2_blocks256); + + __ BIND(L_load_misaligned_next2_blocks256); + __ alignaddr(from, G0, from); + // F0, F2, F4, F60, F62 can be clobbered + __ ldf(FloatRegisterImpl::D, from, 0, F0); + __ ldf(FloatRegisterImpl::D, from, 8, F2); + __ ldf(FloatRegisterImpl::D, from, 16, F60); + __ ldf(FloatRegisterImpl::D, from, 24, F62); + __ ldf(FloatRegisterImpl::D, from, 32, F4); + __ faligndata(F0, F2, F0); + __ faligndata(F2, F60, F2); + __ faligndata(F60, F62, F60); + __ faligndata(F62, F4, F62); + __ movdtox(F0, G4); + __ movdtox(F2, G5); + __ movdtox(F60, L4); + __ movdtox(F62, L5); + __ mov(G1, from); + + __ BIND(L_transform_next2_blocks256); + // F0:F2 used for first 16-bytes + __ xor3(L2,G4,G1); + __ movxtod(G1,F0); + __ xor3(L3,G5,G1); + __ movxtod(G1,F2); + + // F60:F62 used for next 16-bytes + __ xor3(L2,L4,G1); + __ movxtod(G1,F60); + __ xor3(L3,L5,G1); + __ movxtod(G1,F62); + + __ aes_dround23(F54, F0, F2, F4); + __ aes_dround01(F52, F0, F2, F6); + __ aes_dround23(F54, F60, F62, F58); + __ aes_dround01(F52, F60, F62, F56); + __ aes_dround23(F50, F6, F4, F2); + __ aes_dround01(F48, F6, F4, F0); + __ aes_dround23(F50, F56, F58, F62); + __ aes_dround01(F48, F56, F58, F60); + // save F48:F54 in temp registers + __ movdtox(F54,G2); + __ movdtox(F52,G3); + __ movdtox(F50,G6); + __ movdtox(F48,G1); + for ( int i = 46; i >= 14; i -= 8 ) { + __ aes_dround23(as_FloatRegister(i), F0, F2, F4); + __ aes_dround01(as_FloatRegister(i-2), F0, F2, F6); + __ aes_dround23(as_FloatRegister(i), F60, F62, F58); + __ aes_dround01(as_FloatRegister(i-2), F60, F62, F56); + __ aes_dround23(as_FloatRegister(i-4), F6, F4, F2); + __ aes_dround01(as_FloatRegister(i-6), F6, F4, F0); + __ aes_dround23(as_FloatRegister(i-4), F56, F58, F62); + __ aes_dround01(as_FloatRegister(i-6), F56, F58, F60); + } + // init F48:F54 with F0:F6 values (original key) + __ ldf(FloatRegisterImpl::D, original_key, 0, F48); + __ ldf(FloatRegisterImpl::D, original_key, 8, F50); + __ ldf(FloatRegisterImpl::D, original_key, 16, F52); + __ ldf(FloatRegisterImpl::D, original_key, 24, F54); + __ aes_dround23(F54, F0, F2, F4); + __ aes_dround01(F52, F0, F2, F6); + __ aes_dround23(F54, F60, F62, F58); + __ aes_dround01(F52, F60, F62, F56); + __ aes_dround23_l(F50, F6, F4, F2); + __ aes_dround01_l(F48, F6, F4, F0); + __ aes_dround23_l(F50, F56, F58, F62); + __ aes_dround01_l(F48, F56, F58, F60); + // re-init F48:F54 with their original values + __ movxtod(G2,F54); + __ movxtod(G3,F52); + __ movxtod(G6,F50); + __ movxtod(G1,F48); + + __ movxtod(L0,F6); + __ movxtod(L1,F4); + __ fxor(FloatRegisterImpl::D, F6, F0, F0); + __ fxor(FloatRegisterImpl::D, F4, F2, F2); + + __ movxtod(G4,F56); + __ movxtod(G5,F58); + __ mov(L4,L0); + __ mov(L5,L1); + __ fxor(FloatRegisterImpl::D, F56, F60, F60); + __ fxor(FloatRegisterImpl::D, F58, F62, F62); + + // check for 8-byte alignment since dest byte array may have arbitrary alignment if offset mod 8 is non-zero + __ andcc(to, 7, G1); + __ br(Assembler::notZero, true, Assembler::pn, L_store_misaligned_output_next2_blocks256); + __ delayed()->edge8n(to, G0, G2); + + // aligned case: store output into the destination array + __ stf(FloatRegisterImpl::D, F0, to, 0); + __ stf(FloatRegisterImpl::D, F2, to, 8); + __ stf(FloatRegisterImpl::D, F60, to, 16); + __ stf(FloatRegisterImpl::D, F62, to, 24); + __ ba_short(L_check_decrypt_loop_end256); + + __ BIND(L_store_misaligned_output_next2_blocks256); + __ mov(8, G4); + __ sub(G4, G1, G4); + __ alignaddr(G4, G0, G4); + __ faligndata(F0, F2, F56); // F56 can be clobbered + __ faligndata(F2, F60, F2); + __ faligndata(F60, F62, F60); + __ faligndata(F62, F0, F0); + __ mov(to, G1); + __ and3(to, -8, to); + __ stpartialf(to, G2, F0, Assembler::ASI_PST8_PRIMARY); + __ stf(FloatRegisterImpl::D, F56, to, 8); + __ stf(FloatRegisterImpl::D, F2, to, 16); + __ stf(FloatRegisterImpl::D, F60, to, 24); + __ add(to, 32, to); + __ orn(G0, G2, G2); + __ stpartialf(to, G2, F0, Assembler::ASI_PST8_PRIMARY); + __ mov(G1, to); + + __ BIND(L_check_decrypt_loop_end256); + __ add(from, 32, from); + __ add(to, 32, to); + __ subcc(len_reg, 32, len_reg); + __ br(Assembler::notEqual, false, Assembler::pt, L_dec_next2_blocks256); + __ delayed()->nop(); + + __ BIND(L_cbcdec_end); + // re-init intial vector for next block, 8-byte alignment is guaranteed + __ stx(L0, rvec, 0); + __ stx(L1, rvec, 8); + __ mov(L7, I0); + __ ret(); + __ delayed()->restore(); + + return start; + } + void generate_initial() { // Generates all stubs and initializes the entry points @@ -3368,6 +4639,14 @@ generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, &StubRoutines::_safefetchN_fault_pc, &StubRoutines::_safefetchN_continuation_pc); + + // generate AES intrinsics code + if (UseAESIntrinsics) { + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); + StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); + } } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/stubRoutines_sparc.hpp --- a/src/cpu/sparc/vm/stubRoutines_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/stubRoutines_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -41,7 +41,7 @@ enum /* platform_dependent_constants */ { // %%%%%%%% May be able to shrink this a lot 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) + code_size2 = 22000 // simply increase if too small (assembler will crash if too small) }; class Sparc { diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/templateInterpreter_sparc.cpp --- a/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -156,6 +156,10 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, size_t index_size) { address entry = __ pc(); + if (state == atos) { + __ profile_return_type(O0, G3_scratch, G1_scratch); + } + #if !defined(_LP64) && defined(COMPILER2) // All return values are where we want them, except for Longs. C2 returns // longs in G1 in the 32-bit build whereas the interpreter wants them in O0/O1. @@ -1160,7 +1164,7 @@ // reset handle block __ ld_ptr(G2_thread, JavaThread::active_handles_offset(), G3_scratch); - __ st_ptr(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); + __ st(G0, G3_scratch, JNIHandleBlock::top_offset_in_bytes()); // If we have an oop result store it where it will be safe for any further gc // until we return now that we've released the handle it might be protected by @@ -1350,6 +1354,7 @@ __ movbool(true, G3_scratch); __ stbool(G3_scratch, do_not_unlock_if_synchronized); + __ profile_parameters_type(G1_scratch, G3_scratch, G4_scratch, Lscratch); // increment invocation counter and check for overflow // // Note: checking for negative value instead of overflow @@ -1576,37 +1581,23 @@ int monitor_size = method->is_synchronized() ? 1*frame::interpreter_frame_monitor_size() : 0; return size_activation_helper(method->max_locals(), method->max_stack(), - monitor_size) + call_stub_size; + monitor_size) + call_stub_size; } -int AbstractInterpreter::layout_activation(Method* method, - int tempcount, - int popframe_extra_args, - int moncount, - int caller_actual_parameters, - int callee_param_count, - int callee_local_count, - frame* caller, - frame* interpreter_frame, - bool is_top_frame, - bool is_bottom_frame) { +int AbstractInterpreter::size_activation(int max_stack, + int temps, + int extra_args, + int monitors, + int callee_params, + int callee_locals, + bool is_top_frame) { // Note: This calculation must exactly parallel the frame setup // in InterpreterGenerator::generate_fixed_frame. - // If f!=NULL, set up the following variables: - // - Lmethod - // - Llocals - // - Lmonitors (to the indicated number of monitors) - // - Lesp (to the indicated number of temps) - // The frame f (if not NULL) on entry is a description of the caller of the frame - // we are about to layout. We are guaranteed that we will be able to fill in a - // new interpreter frame as its callee (i.e. the stack space is allocated and - // the amount was determined by an earlier call to this method with f == NULL). - // On return f (if not NULL) while describe the interpreter frame we just layed out. - int monitor_size = moncount * frame::interpreter_frame_monitor_size(); - int rounded_vm_local_words = round_to(frame::interpreter_frame_vm_local_words,WordsPerLong); + int monitor_size = monitors * frame::interpreter_frame_monitor_size(); assert(monitor_size == round_to(monitor_size, WordsPerLong), "must align"); + // // Note: if you look closely this appears to be doing something much different // than generate_fixed_frame. What is happening is this. On sparc we have to do @@ -1631,151 +1622,175 @@ // there is no sense in messing working code. // - int rounded_cls = round_to((callee_local_count - callee_param_count), WordsPerLong); + int rounded_cls = round_to((callee_locals - callee_params), WordsPerLong); assert(rounded_cls == round_to(rounded_cls, WordsPerLong), "must align"); - int raw_frame_size = size_activation_helper(rounded_cls, method->max_stack(), - monitor_size); + int raw_frame_size = size_activation_helper(rounded_cls, max_stack, monitor_size); - if (interpreter_frame != NULL) { - // The skeleton frame must already look like an interpreter frame - // even if not fully filled out. - assert(interpreter_frame->is_interpreted_frame(), "Must be interpreted frame"); - - intptr_t* fp = interpreter_frame->fp(); + return raw_frame_size; +} - JavaThread* thread = JavaThread::current(); - RegisterMap map(thread, false); - // More verification that skeleton frame is properly walkable - assert(fp == caller->sp(), "fp must match"); - - intptr_t* montop = fp - rounded_vm_local_words; +void AbstractInterpreter::layout_activation(Method* method, + int tempcount, + int popframe_extra_args, + int moncount, + int caller_actual_parameters, + int callee_param_count, + int callee_local_count, + frame* caller, + frame* interpreter_frame, + bool is_top_frame, + bool is_bottom_frame) { + // Set up the following variables: + // - Lmethod + // - Llocals + // - Lmonitors (to the indicated number of monitors) + // - Lesp (to the indicated number of temps) + // The frame caller on entry is a description of the caller of the + // frame we are about to layout. We are guaranteed that we will be + // able to fill in a new interpreter frame as its callee (i.e. the + // stack space is allocated and the amount was determined by an + // earlier call to the size_activation() method). On return caller + // while describe the interpreter frame we just layed out. - // preallocate monitors (cf. __ add_monitor_to_stack) - intptr_t* monitors = montop - monitor_size; + // The skeleton frame must already look like an interpreter frame + // even if not fully filled out. + assert(interpreter_frame->is_interpreted_frame(), "Must be interpreted frame"); + + int rounded_vm_local_words = round_to(frame::interpreter_frame_vm_local_words,WordsPerLong); + int monitor_size = moncount * frame::interpreter_frame_monitor_size(); + assert(monitor_size == round_to(monitor_size, WordsPerLong), "must align"); + + intptr_t* fp = interpreter_frame->fp(); - // preallocate stack space - intptr_t* esp = monitors - 1 - - (tempcount * Interpreter::stackElementWords) - - popframe_extra_args; + JavaThread* thread = JavaThread::current(); + RegisterMap map(thread, false); + // More verification that skeleton frame is properly walkable + assert(fp == caller->sp(), "fp must match"); + + intptr_t* montop = fp - rounded_vm_local_words; + + // preallocate monitors (cf. __ add_monitor_to_stack) + intptr_t* monitors = montop - monitor_size; + + // preallocate stack space + intptr_t* esp = monitors - 1 - + (tempcount * Interpreter::stackElementWords) - + popframe_extra_args; - int local_words = method->max_locals() * Interpreter::stackElementWords; - NEEDS_CLEANUP; - intptr_t* locals; - if (caller->is_interpreted_frame()) { - // Can force the locals area to end up properly overlapping the top of the expression stack. - intptr_t* Lesp_ptr = caller->interpreter_frame_tos_address() - 1; - // Note that this computation means we replace size_of_parameters() values from the caller - // interpreter frame's expression stack with our argument locals - int parm_words = caller_actual_parameters * Interpreter::stackElementWords; - locals = Lesp_ptr + parm_words; - int delta = local_words - parm_words; - int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0; - *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS; - if (!is_bottom_frame) { - // Llast_SP is set below for the current frame to SP (with the - // extra space for the callee's locals). Here we adjust - // Llast_SP for the caller's frame, removing the extra space - // for the current method's locals. - *caller->register_addr(Llast_SP) = *interpreter_frame->register_addr(I5_savedSP); - } else { - assert(*caller->register_addr(Llast_SP) >= *interpreter_frame->register_addr(I5_savedSP), "strange Llast_SP"); - } + int local_words = method->max_locals() * Interpreter::stackElementWords; + NEEDS_CLEANUP; + intptr_t* locals; + if (caller->is_interpreted_frame()) { + // Can force the locals area to end up properly overlapping the top of the expression stack. + intptr_t* Lesp_ptr = caller->interpreter_frame_tos_address() - 1; + // Note that this computation means we replace size_of_parameters() values from the caller + // interpreter frame's expression stack with our argument locals + int parm_words = caller_actual_parameters * Interpreter::stackElementWords; + locals = Lesp_ptr + parm_words; + int delta = local_words - parm_words; + int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0; + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS; + if (!is_bottom_frame) { + // Llast_SP is set below for the current frame to SP (with the + // extra space for the callee's locals). Here we adjust + // Llast_SP for the caller's frame, removing the extra space + // for the current method's locals. + *caller->register_addr(Llast_SP) = *interpreter_frame->register_addr(I5_savedSP); } else { - assert(caller->is_compiled_frame() || caller->is_entry_frame(), "only possible cases"); - // Don't have Lesp available; lay out locals block in the caller - // adjacent to the register window save area. - // - // Compiled frames do not allocate a varargs area which is why this if - // statement is needed. - // - if (caller->is_compiled_frame()) { - locals = fp + frame::register_save_words + local_words - 1; - } else { - locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; - } - if (!caller->is_entry_frame()) { - // Caller wants his own SP back - int caller_frame_size = caller->cb()->frame_size(); - *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS; + assert(*caller->register_addr(Llast_SP) >= *interpreter_frame->register_addr(I5_savedSP), "strange Llast_SP"); + } + } else { + assert(caller->is_compiled_frame() || caller->is_entry_frame(), "only possible cases"); + // Don't have Lesp available; lay out locals block in the caller + // adjacent to the register window save area. + // + // Compiled frames do not allocate a varargs area which is why this if + // statement is needed. + // + if (caller->is_compiled_frame()) { + locals = fp + frame::register_save_words + local_words - 1; + } else { + locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1; + } + if (!caller->is_entry_frame()) { + // Caller wants his own SP back + int caller_frame_size = caller->cb()->frame_size(); + *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS; + } + } + if (TraceDeoptimization) { + if (caller->is_entry_frame()) { + // make sure I5_savedSP and the entry frames notion of saved SP + // agree. This assertion duplicate a check in entry frame code + // but catches the failure earlier. + /* + * Sanzinger: This does not make sense to me, since when we call stub_call -> i2c, the i2c may change the + * sp, which then is not in sync with Lscratch anymore. + */ + /*assert(*caller->register_addr(Lscratch) == *interpreter_frame->register_addr(I5_savedSP), + "would change callers SP");*/ + } + if (caller->is_entry_frame()) { + tty->print("entry "); + } + if (caller->is_compiled_frame()) { + tty->print("compiled "); + if (caller->is_deoptimized_frame()) { + tty->print("(deopt) "); } } - if (TraceDeoptimization) { - if (caller->is_entry_frame()) { - // make sure I5_savedSP and the entry frames notion of saved SP - // agree. This assertion duplicate a check in entry frame code - // but catches the failure earlier. - /* - * Sanzinger: This does not make sense to me, since when we call stub_call -> i2c, the i2c may change the - * sp, which then is not in sync with Lscratch anymore. - */ - /**assert(*caller->register_addr(Lscratch) == *interpreter_frame->register_addr(I5_savedSP), - "would change callers SP"); - */ - } - if (caller->is_entry_frame()) { - tty->print("entry "); - } - if (caller->is_compiled_frame()) { - tty->print("compiled "); - if (caller->is_deoptimized_frame()) { - tty->print("(deopt) "); - } - } - if (caller->is_interpreted_frame()) { - tty->print("interpreted "); - } - tty->print_cr("caller fp=0x%x sp=0x%x", caller->fp(), caller->sp()); - tty->print_cr("save area = 0x%x, 0x%x", caller->sp(), caller->sp() + 16); - tty->print_cr("save area = 0x%x, 0x%x", caller->fp(), caller->fp() + 16); - tty->print_cr("interpreter fp=0x%x sp=0x%x", interpreter_frame->fp(), interpreter_frame->sp()); - tty->print_cr("save area = 0x%x, 0x%x", interpreter_frame->sp(), interpreter_frame->sp() + 16); - tty->print_cr("save area = 0x%x, 0x%x", interpreter_frame->fp(), interpreter_frame->fp() + 16); - tty->print_cr("Llocals = 0x%x", locals); - tty->print_cr("Lesp = 0x%x", esp); - tty->print_cr("Lmonitors = 0x%x", monitors); + if (caller->is_interpreted_frame()) { + tty->print("interpreted "); } + tty->print_cr("caller fp=0x%x sp=0x%x", caller->fp(), caller->sp()); + tty->print_cr("save area = 0x%x, 0x%x", caller->sp(), caller->sp() + 16); + tty->print_cr("save area = 0x%x, 0x%x", caller->fp(), caller->fp() + 16); + tty->print_cr("interpreter fp=0x%x sp=0x%x", interpreter_frame->fp(), interpreter_frame->sp()); + tty->print_cr("save area = 0x%x, 0x%x", interpreter_frame->sp(), interpreter_frame->sp() + 16); + tty->print_cr("save area = 0x%x, 0x%x", interpreter_frame->fp(), interpreter_frame->fp() + 16); + tty->print_cr("Llocals = 0x%x", locals); + tty->print_cr("Lesp = 0x%x", esp); + tty->print_cr("Lmonitors = 0x%x", monitors); + } - if (method->max_locals() > 0) { - assert(locals < caller->sp() || locals >= (caller->sp() + 16), "locals in save area"); - assert(locals < caller->fp() || locals > (caller->fp() + 16), "locals in save area"); - assert(locals < interpreter_frame->sp() || locals > (interpreter_frame->sp() + 16), "locals in save area"); - assert(locals < interpreter_frame->fp() || locals >= (interpreter_frame->fp() + 16), "locals in save area"); - } + if (method->max_locals() > 0) { + assert(locals < caller->sp() || locals >= (caller->sp() + 16), "locals in save area"); + assert(locals < caller->fp() || locals > (caller->fp() + 16), "locals in save area"); + assert(locals < interpreter_frame->sp() || locals > (interpreter_frame->sp() + 16), "locals in save area"); + assert(locals < interpreter_frame->fp() || locals >= (interpreter_frame->fp() + 16), "locals in save area"); + } #ifdef _LP64 - assert(*interpreter_frame->register_addr(I5_savedSP) & 1, "must be odd"); + assert(*interpreter_frame->register_addr(I5_savedSP) & 1, "must be odd"); #endif - *interpreter_frame->register_addr(Lmethod) = (intptr_t) method; - *interpreter_frame->register_addr(Llocals) = (intptr_t) locals; - *interpreter_frame->register_addr(Lmonitors) = (intptr_t) monitors; - *interpreter_frame->register_addr(Lesp) = (intptr_t) esp; - // Llast_SP will be same as SP as there is no adapter space - *interpreter_frame->register_addr(Llast_SP) = (intptr_t) interpreter_frame->sp() - STACK_BIAS; - *interpreter_frame->register_addr(LcpoolCache) = (intptr_t) method->constants()->cache(); + *interpreter_frame->register_addr(Lmethod) = (intptr_t) method; + *interpreter_frame->register_addr(Llocals) = (intptr_t) locals; + *interpreter_frame->register_addr(Lmonitors) = (intptr_t) monitors; + *interpreter_frame->register_addr(Lesp) = (intptr_t) esp; + // Llast_SP will be same as SP as there is no adapter space + *interpreter_frame->register_addr(Llast_SP) = (intptr_t) interpreter_frame->sp() - STACK_BIAS; + *interpreter_frame->register_addr(LcpoolCache) = (intptr_t) method->constants()->cache(); #ifdef FAST_DISPATCH - *interpreter_frame->register_addr(IdispatchTables) = (intptr_t) Interpreter::dispatch_table(); + *interpreter_frame->register_addr(IdispatchTables) = (intptr_t) Interpreter::dispatch_table(); #endif #ifdef ASSERT - BasicObjectLock* mp = (BasicObjectLock*)monitors; - - assert(interpreter_frame->interpreter_frame_method() == method, "method matches"); - assert(interpreter_frame->interpreter_frame_local_at(9) == (intptr_t *)((intptr_t)locals - (9 * Interpreter::stackElementSize)), "locals match"); - assert(interpreter_frame->interpreter_frame_monitor_end() == mp, "monitor_end matches"); - assert(((intptr_t *)interpreter_frame->interpreter_frame_monitor_begin()) == ((intptr_t *)mp)+monitor_size, "monitor_begin matches"); - assert(interpreter_frame->interpreter_frame_tos_address()-1 == esp, "esp matches"); + BasicObjectLock* mp = (BasicObjectLock*)monitors; - // check bounds - intptr_t* lo = interpreter_frame->sp() + (frame::memory_parameter_word_sp_offset - 1); - intptr_t* hi = interpreter_frame->fp() - rounded_vm_local_words; - assert(lo < monitors && montop <= hi, "monitors in bounds"); - assert(lo <= esp && esp < monitors, "esp in bounds"); + assert(interpreter_frame->interpreter_frame_method() == method, "method matches"); + assert(interpreter_frame->interpreter_frame_local_at(9) == (intptr_t *)((intptr_t)locals - (9 * Interpreter::stackElementSize)), "locals match"); + assert(interpreter_frame->interpreter_frame_monitor_end() == mp, "monitor_end matches"); + assert(((intptr_t *)interpreter_frame->interpreter_frame_monitor_begin()) == ((intptr_t *)mp)+monitor_size, "monitor_begin matches"); + assert(interpreter_frame->interpreter_frame_tos_address()-1 == esp, "esp matches"); + + // check bounds + intptr_t* lo = interpreter_frame->sp() + (frame::memory_parameter_word_sp_offset - 1); + intptr_t* hi = interpreter_frame->fp() - rounded_vm_local_words; + assert(lo < monitors && montop <= hi, "monitors in bounds"); + assert(lo <= esp && esp < monitors, "esp in bounds"); #endif // ASSERT - } - - return raw_frame_size; } //---------------------------------------------------------------------------------------------------- diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/templateTable_sparc.cpp --- a/src/cpu/sparc/vm/templateTable_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/templateTable_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -2942,12 +2942,12 @@ void TemplateTable::generate_vtable_call(Register Rrecv, Register Rindex, Register Rret) { - Register Rtemp = G4_scratch; Register Rcall = Rindex; assert_different_registers(Rcall, G5_method, Gargs, Rret); // get target Method* & entry point __ lookup_virtual_method(Rrecv, Rindex, G5_method); + __ profile_arguments_type(G5_method, Rcall, Gargs, true); #ifdef GRAAL __ profile_called_method(G5_method, Rtemp); #endif @@ -3025,6 +3025,7 @@ __ null_check(O0); __ profile_final_call(O4); + __ profile_arguments_type(G5_method, Rscratch, Gargs, true); // get return address AddressLiteral table(Interpreter::invoke_return_entry_table()); @@ -3054,6 +3055,7 @@ // do the call __ profile_call(O4); + __ profile_arguments_type(G5_method, Rscratch, Gargs, false); __ call_from_interpreter(Rscratch, Gargs, Rret); } @@ -3069,6 +3071,7 @@ // do the call __ profile_call(O4); + __ profile_arguments_type(G5_method, Rscratch, Gargs, false); __ call_from_interpreter(Rscratch, Gargs, Rret); } @@ -3094,6 +3097,7 @@ // do the call - the index (f2) contains the Method* assert_different_registers(G5_method, Gargs, Rcall); __ mov(Rindex, G5_method); + __ profile_arguments_type(G5_method, Rcall, Gargs, true); __ call_from_interpreter(Rcall, Gargs, Rret); __ bind(notFinal); @@ -3200,10 +3204,10 @@ Register Rcall = Rinterface; assert_different_registers(Rcall, G5_method, Gargs, Rret); + __ profile_arguments_type(G5_method, Rcall, Gargs, true); #ifdef GRAAL __ profile_called_method(G5_method, Rscratch); #endif - __ call_from_interpreter(Rcall, Gargs, Rret); } @@ -3233,6 +3237,7 @@ // do the call __ verify_oop(G4_mtype); __ profile_final_call(O4); // FIXME: profile the LambdaForm also + __ profile_arguments_type(G5_method, Rscratch, Gargs, true); __ call_from_interpreter(Rscratch, Gargs, Rret); } @@ -3269,6 +3274,7 @@ // do the call __ verify_oop(G4_callsite); + __ profile_arguments_type(G5_method, Rscratch, Gargs, false); __ call_from_interpreter(Rscratch, Gargs, Rret); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/vm_version_sparc.cpp --- a/src/cpu/sparc/vm/vm_version_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/vm_version_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -234,7 +234,7 @@ assert((OptoLoopAlignment % relocInfo::addr_unit()) == 0, "alignment is not a multiple of NOP size"); char buf[512]; - jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (has_v9() ? ", v9" : (has_v8() ? ", v8" : "")), (has_hardware_popc() ? ", popc" : ""), (has_vis1() ? ", vis1" : ""), @@ -242,6 +242,7 @@ (has_vis3() ? ", vis3" : ""), (has_blk_init() ? ", blk_init" : ""), (has_cbcond() ? ", cbcond" : ""), + (has_aes() ? ", aes" : ""), (is_ultra3() ? ", ultra3" : ""), (is_sun4v() ? ", sun4v" : ""), (is_niagara_plus() ? ", niagara_plus" : (is_niagara() ? ", niagara" : "")), @@ -265,6 +266,41 @@ if (!has_vis1()) // Drop to 0 if no VIS1 support UseVIS = 0; + // SPARC T4 and above should have support for AES instructions + if (has_aes()) { + if (UseVIS > 2) { // AES intrinsics use MOVxTOd/MOVdTOx which are VIS3 + if (FLAG_IS_DEFAULT(UseAES)) { + FLAG_SET_DEFAULT(UseAES, true); + } + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + FLAG_SET_DEFAULT(UseAESIntrinsics, true); + } + // we disable both the AES flags if either of them is disabled on the command line + if (!UseAES || !UseAESIntrinsics) { + FLAG_SET_DEFAULT(UseAES, false); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + } else { + if (UseAES || UseAESIntrinsics) { + warning("SPARC AES intrinsics require VIS3 instruction support. Intrinsics will be disabled."); + if (UseAES) { + FLAG_SET_DEFAULT(UseAES, false); + } + if (UseAESIntrinsics) { + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + } + } + } else if (UseAES || UseAESIntrinsics) { + warning("AES instructions are not available on this CPU"); + if (UseAES) { + FLAG_SET_DEFAULT(UseAES, false); + } + if (UseAESIntrinsics) { + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + } + if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && (cache_line_size > ContendedPaddingWidth)) ContendedPaddingWidth = cache_line_size; @@ -282,22 +318,22 @@ tty->print("BIS"); } if (AllocatePrefetchLines > 1) { - tty->print_cr(" at distance %d, %d lines of %d bytes", AllocatePrefetchDistance, AllocatePrefetchLines, AllocatePrefetchStepSize); + tty->print_cr(" at distance %d, %d lines of %d bytes", (int) AllocatePrefetchDistance, (int) AllocatePrefetchLines, (int) AllocatePrefetchStepSize); } else { - tty->print_cr(" at distance %d, one line of %d bytes", AllocatePrefetchDistance, AllocatePrefetchStepSize); + tty->print_cr(" at distance %d, one line of %d bytes", (int) AllocatePrefetchDistance, (int) AllocatePrefetchStepSize); } } if (PrefetchCopyIntervalInBytes > 0) { - tty->print_cr("PrefetchCopyIntervalInBytes %d", PrefetchCopyIntervalInBytes); + tty->print_cr("PrefetchCopyIntervalInBytes %d", (int) PrefetchCopyIntervalInBytes); } if (PrefetchScanIntervalInBytes > 0) { - tty->print_cr("PrefetchScanIntervalInBytes %d", PrefetchScanIntervalInBytes); + tty->print_cr("PrefetchScanIntervalInBytes %d", (int) PrefetchScanIntervalInBytes); } if (PrefetchFieldsAhead > 0) { - tty->print_cr("PrefetchFieldsAhead %d", PrefetchFieldsAhead); + tty->print_cr("PrefetchFieldsAhead %d", (int) PrefetchFieldsAhead); } if (ContendedPaddingWidth > 0) { - tty->print_cr("ContendedPaddingWidth %d", ContendedPaddingWidth); + tty->print_cr("ContendedPaddingWidth %d", (int) ContendedPaddingWidth); } } #endif // PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/sparc/vm/vm_version_sparc.hpp --- a/src/cpu/sparc/vm/vm_version_sparc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/sparc/vm/vm_version_sparc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -49,7 +49,9 @@ sparc64_family = 14, M_family = 15, T_family = 16, - T1_model = 17 + T1_model = 17, + sparc5_instructions = 18, + aes_instructions = 19 }; enum Feature_Flag_Set { @@ -74,6 +76,8 @@ M_family_m = 1 << M_family, T_family_m = 1 << T_family, T1_model_m = 1 << T1_model, + sparc5_instructions_m = 1 << sparc5_instructions, + aes_instructions_m = 1 << aes_instructions, generic_v8_m = v8_instructions_m | hardware_mul32_m | hardware_div32_m | hardware_fsmuld_m, generic_v9_m = generic_v8_m | v9_instructions_m, @@ -124,6 +128,8 @@ static bool has_vis3() { return (_features & vis3_instructions_m) != 0; } static bool has_blk_init() { return (_features & blk_init_instructions_m) != 0; } static bool has_cbcond() { return (_features & cbcond_instructions_m) != 0; } + static bool has_sparc5_instr() { return (_features & sparc5_instructions_m) != 0; } + static bool has_aes() { return (_features & aes_instructions_m) != 0; } static bool supports_compare_and_exchange() { return has_v9(); } @@ -134,6 +140,7 @@ static bool is_M_series() { return is_M_family(_features); } static bool is_T4() { return is_T_family(_features) && has_cbcond(); } + static bool is_T7() { return is_T_family(_features) && has_sparc5_instr(); } // Fujitsu SPARC64 static bool is_sparc64() { return (_features & sparc64_family_m) != 0; } @@ -153,7 +160,7 @@ static const char* cpu_features() { return _features_str; } static intx prefetch_data_size() { - return is_T4() ? 32 : 64; // default prefetch block size on sparc + return is_T4() && !is_T7() ? 32 : 64; // default prefetch block size on sparc } // Prefetch diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/assembler_x86.cpp --- a/src/cpu/x86/vm/assembler_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/assembler_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -522,11 +522,11 @@ // these asserts are somewhat nonsensical #ifndef _LP64 assert(which == imm_operand || which == disp32_operand, - err_msg("which %d is_64_bit %d ip " INTPTR_FORMAT, which, is_64bit, ip)); + err_msg("which %d is_64_bit %d ip " INTPTR_FORMAT, which, is_64bit, p2i(ip))); #else assert((which == call32_operand || which == imm_operand) && is_64bit || which == narrow_oop_operand && !is_64bit, - err_msg("which %d is_64_bit %d ip " INTPTR_FORMAT, which, is_64bit, ip)); + err_msg("which %d is_64_bit %d ip " INTPTR_FORMAT, which, is_64bit, p2i(ip))); #endif // _LP64 return ip; @@ -1103,6 +1103,21 @@ emit_arith(0x23, 0xC0, dst, src); } +void Assembler::andnl(Register dst, Register src1, Register src2) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode(dst, src1, src2); + emit_int8((unsigned char)0xF2); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::andnl(Register dst, Register src1, Address src2) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38(dst, src1, src2); + emit_int8((unsigned char)0xF2); + emit_operand(dst, src2); +} + void Assembler::bsfl(Register dst, Register src) { int encode = prefix_and_encode(dst->encoding(), src->encoding()); emit_int8(0x0F); @@ -1111,7 +1126,6 @@ } void Assembler::bsrl(Register dst, Register src) { - assert(!VM_Version::supports_lzcnt(), "encoding is treated as LZCNT"); int encode = prefix_and_encode(dst->encoding(), src->encoding()); emit_int8(0x0F); emit_int8((unsigned char)0xBD); @@ -1124,6 +1138,51 @@ emit_int8((unsigned char)(0xC8 | encode)); } +void Assembler::blsil(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode(rbx, dst, src); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::blsil(Register dst, Address src) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38(rbx, dst, src); + emit_int8((unsigned char)0xF3); + emit_operand(rbx, src); +} + +void Assembler::blsmskl(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode(rdx, dst, src); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::blsmskl(Register dst, Address src) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38(rdx, dst, src); + emit_int8((unsigned char)0xF3); + emit_operand(rdx, src); +} + +void Assembler::blsrl(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode(rcx, dst, src); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::blsrl(Register dst, Address src) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38(rcx, dst, src); + emit_int8((unsigned char)0xF3); + emit_operand(rcx, src); +} + void Assembler::call(Label& L, relocInfo::relocType rtype) { // suspect disp32 is always good int operand = LP64_ONLY(disp32_operand) NOT_LP64(imm_operand); @@ -1721,7 +1780,7 @@ // Move Unaligned 256bit Vector void Assembler::vmovdqu(XMMRegister dst, XMMRegister src) { - assert(UseAVX, ""); + assert(UseAVX > 0, ""); bool vector256 = true; int encode = vex_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_F3, vector256); emit_int8(0x6F); @@ -1729,7 +1788,7 @@ } void Assembler::vmovdqu(XMMRegister dst, Address src) { - assert(UseAVX, ""); + assert(UseAVX > 0, ""); InstructionMark im(this); bool vector256 = true; vex_prefix(dst, xnoreg, src, VEX_SIMD_F3, vector256); @@ -1738,7 +1797,7 @@ } void Assembler::vmovdqu(Address dst, XMMRegister src) { - assert(UseAVX, ""); + assert(UseAVX > 0, ""); InstructionMark im(this); bool vector256 = true; // swap src<->dst for encoding @@ -2297,6 +2356,11 @@ emit_int8(imm8); } +void Assembler::pause() { + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)0x90); +} + void Assembler::pcmpestri(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse4_2(), ""); InstructionMark im(this); @@ -2621,6 +2685,11 @@ } } +void Assembler::rdtsc() { + emit_int8((unsigned char)0x0F); + emit_int8((unsigned char)0x31); +} + // copies data from [esi] to [edi] using rcx pointer sized words // generic void Assembler::rep_mov() { @@ -2892,6 +2961,24 @@ emit_operand(dst, src); } +void Assembler::tzcntl(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "tzcnt instruction not supported"); + emit_int8((unsigned char)0xF3); + int encode = prefix_and_encode(dst->encoding(), src->encoding()); + emit_int8(0x0F); + emit_int8((unsigned char)0xBC); + emit_int8((unsigned char)0xC0 | encode); +} + +void Assembler::tzcntq(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "tzcnt instruction not supported"); + emit_int8((unsigned char)0xF3); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_int8(0x0F); + emit_int8((unsigned char)0xBC); + emit_int8((unsigned char)(0xC0 | encode)); +} + void Assembler::ucomisd(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); emit_simd_arith_nonds(0x2E, dst, src, VEX_SIMD_66); @@ -2912,6 +2999,11 @@ emit_simd_arith_nonds(0x2E, dst, src, VEX_SIMD_NONE); } +void Assembler::xabort(int8_t imm8) { + emit_int8((unsigned char)0xC6); + emit_int8((unsigned char)0xF8); + emit_int8((unsigned char)(imm8 & 0xFF)); +} void Assembler::xaddl(Address dst, Register src) { InstructionMark im(this); @@ -2921,6 +3013,24 @@ emit_operand(src, dst); } +void Assembler::xbegin(Label& abort, relocInfo::relocType rtype) { + InstructionMark im(this); + relocate(rtype); + if (abort.is_bound()) { + address entry = target(abort); + assert(entry != NULL, "abort entry NULL"); + intptr_t offset = entry - pc(); + emit_int8((unsigned char)0xC7); + emit_int8((unsigned char)0xF8); + emit_int32(offset - 6); // 2 opcode + 4 address + } else { + abort.add_patch_at(code(), locator()); + emit_int8((unsigned char)0xC7); + emit_int8((unsigned char)0xF8); + emit_int32(0); + } +} + void Assembler::xchgl(Register dst, Address src) { // xchg InstructionMark im(this); prefix(src, dst); @@ -2934,6 +3044,12 @@ emit_int8((unsigned char)(0xC0 | encode)); } +void Assembler::xend() { + emit_int8((unsigned char)0x0F); + emit_int8((unsigned char)0x01); + emit_int8((unsigned char)0xD5); +} + void Assembler::xgetbv() { emit_int8(0x0F); emit_int8(0x01); @@ -4851,6 +4967,21 @@ emit_arith(0x23, 0xC0, dst, src); } +void Assembler::andnq(Register dst, Register src1, Register src2) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode_q(dst, src1, src2); + emit_int8((unsigned char)0xF2); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::andnq(Register dst, Register src1, Address src2) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38_q(dst, src1, src2); + emit_int8((unsigned char)0xF2); + emit_operand(dst, src2); +} + void Assembler::bsfq(Register dst, Register src) { int encode = prefixq_and_encode(dst->encoding(), src->encoding()); emit_int8(0x0F); @@ -4859,7 +4990,6 @@ } void Assembler::bsrq(Register dst, Register src) { - assert(!VM_Version::supports_lzcnt(), "encoding is treated as LZCNT"); int encode = prefixq_and_encode(dst->encoding(), src->encoding()); emit_int8(0x0F); emit_int8((unsigned char)0xBD); @@ -4872,6 +5002,51 @@ emit_int8((unsigned char)(0xC8 | encode)); } +void Assembler::blsiq(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode_q(rbx, dst, src); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::blsiq(Register dst, Address src) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38_q(rbx, dst, src); + emit_int8((unsigned char)0xF3); + emit_operand(rbx, src); +} + +void Assembler::blsmskq(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode_q(rdx, dst, src); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::blsmskq(Register dst, Address src) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38_q(rdx, dst, src); + emit_int8((unsigned char)0xF3); + emit_operand(rdx, src); +} + +void Assembler::blsrq(Register dst, Register src) { + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + int encode = vex_prefix_0F38_and_encode_q(rcx, dst, src); + emit_int8((unsigned char)0xF3); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::blsrq(Register dst, Address src) { + InstructionMark im(this); + assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported"); + vex_prefix_0F38_q(rcx, dst, src); + emit_int8((unsigned char)0xF3); + emit_operand(rcx, src); +} + void Assembler::cdqq() { prefix(REX_W); emit_int8((unsigned char)0x99); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/assembler_x86.hpp --- a/src/cpu/x86/vm/assembler_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/assembler_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -594,10 +594,35 @@ vex_prefix(src, nds_enc, dst_enc, pre, VEX_OPCODE_0F, false, vector256); } + void vex_prefix_0F38(Register dst, Register nds, Address src) { + bool vex_w = false; + bool vector256 = false; + vex_prefix(src, nds->encoding(), dst->encoding(), + VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector256); + } + + void vex_prefix_0F38_q(Register dst, Register nds, Address src) { + bool vex_w = true; + bool vector256 = false; + vex_prefix(src, nds->encoding(), dst->encoding(), + VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector256); + } int vex_prefix_and_encode(int dst_enc, int nds_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc, bool vex_w, bool vector256); + int vex_prefix_0F38_and_encode(Register dst, Register nds, Register src) { + bool vex_w = false; + bool vector256 = false; + return vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), + VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector256); + } + int vex_prefix_0F38_and_encode_q(Register dst, Register nds, Register src) { + bool vex_w = true; + bool vector256 = false; + return vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), + VEX_SIMD_NONE, VEX_OPCODE_0F_38, vex_w, vector256); + } int vex_prefix_and_encode(XMMRegister dst, XMMRegister nds, XMMRegister src, VexSimdPrefix pre, bool vector256 = false, VexOpcode opc = VEX_OPCODE_0F) { @@ -901,6 +926,27 @@ void andq(Register dst, Address src); void andq(Register dst, Register src); + // BMI instructions + void andnl(Register dst, Register src1, Register src2); + void andnl(Register dst, Register src1, Address src2); + void andnq(Register dst, Register src1, Register src2); + void andnq(Register dst, Register src1, Address src2); + + void blsil(Register dst, Register src); + void blsil(Register dst, Address src); + void blsiq(Register dst, Register src); + void blsiq(Register dst, Address src); + + void blsmskl(Register dst, Register src); + void blsmskl(Register dst, Address src); + void blsmskq(Register dst, Register src); + void blsmskq(Register dst, Address src); + + void blsrl(Register dst, Register src); + void blsrl(Register dst, Address src); + void blsrq(Register dst, Register src); + void blsrq(Register dst, Address src); + void bsfl(Register dst, Register src); void bsrl(Register dst, Register src); @@ -1409,6 +1455,8 @@ // Pemutation of 64bit words void vpermq(XMMRegister dst, XMMRegister src, int imm8, bool vector256); + void pause(); + // SSE4.2 string instructions void pcmpestri(XMMRegister xmm1, XMMRegister xmm2, int imm8); void pcmpestri(XMMRegister xmm1, Address src, int imm8); @@ -1493,6 +1541,8 @@ void rclq(Register dst, int imm8); + void rdtsc(); + void ret(int imm16); void sahf(); @@ -1578,6 +1628,9 @@ void testq(Register dst, int32_t imm32); void testq(Register dst, Register src); + // BMI - count trailing zeros + void tzcntl(Register dst, Register src); + void tzcntq(Register dst, Register src); // Unordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS void ucomisd(XMMRegister dst, Address src); @@ -1587,16 +1640,22 @@ void ucomiss(XMMRegister dst, Address src); void ucomiss(XMMRegister dst, XMMRegister src); + void xabort(int8_t imm8); + void xaddl(Address dst, Register src); void xaddq(Address dst, Register src); + void xbegin(Label& abort, relocInfo::relocType rtype = relocInfo::none); + void xchgl(Register reg, Address adr); void xchgl(Register dst, Register src); void xchgq(Register reg, Address adr); void xchgq(Register dst, Register src); + void xend(); + // Get Value of Extended Control Register void xgetbv(); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/bytecodeInterpreter_x86.hpp --- a/src/cpu/x86/vm/bytecodeInterpreter_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/bytecodeInterpreter_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -94,7 +94,7 @@ #define LOCALS_ADDR(offset) ((address)locals[-(offset)]) #define LOCALS_INT(offset) ((jint)(locals[-(offset)])) #define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) -#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) +#define LOCALS_OBJECT(offset) (cast_to_oop(locals[-(offset)])) #define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) #define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) #define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/c1_LIRAssembler_x86.cpp --- a/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -288,7 +288,7 @@ // build frame ciMethod* m = compilation()->method(); - __ build_frame(initial_frame_size_in_bytes()); + __ build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes()); // OSR buffer is // @@ -376,7 +376,7 @@ } // This specifies the rsp decrement needed to build the frame -int LIR_Assembler::initial_frame_size_in_bytes() { +int LIR_Assembler::initial_frame_size_in_bytes() const { // if rounding, must let FrameMap know! // The frame_map records size in slots (32bit word) @@ -802,9 +802,9 @@ __ movl(as_Address(addr), (int32_t)NULL_WORD); } else { #ifdef _LP64 - __ xorptr(r10, r10); + __ xorptr(rscratch1, rscratch1); null_check_here = code_offset(); - __ movptr(as_Address(addr), r10); + __ movptr(as_Address(addr), rscratch1); #else __ movptr(as_Address(addr), NULL_WORD); #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/c1_LinearScan_x86.cpp --- a/src/cpu/x86/vm/c1_LinearScan_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/c1_LinearScan_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -95,7 +95,7 @@ #ifndef PRODUCT if (TraceFPURegisterUsage) { - tty->print("FPU regs for block %d, LIR instr %d): ", b->block_id(), id); regs.print_on(tty); tty->print_cr(""); + tty->print("FPU regs for block %d, LIR instr %d): ", b->block_id(), id); regs.print_on(tty); tty->cr(); } #endif } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/c1_MacroAssembler_x86.cpp --- a/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/c1_MacroAssembler_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -349,13 +349,14 @@ } -void C1_MacroAssembler::build_frame(int frame_size_in_bytes) { +void C1_MacroAssembler::build_frame(int frame_size_in_bytes, int bang_size_in_bytes) { + assert(bang_size_in_bytes >= frame_size_in_bytes, "stack bang size incorrect"); // Make sure there is enough stack space for this method's activation. // Note that we do this before doing an enter(). This matches the // ordering of C2's stack overflow check / rsp decrement and allows // the SharedRuntime stack overflow handling to be consistent // between the two compilers. - generate_stack_overflow_check(frame_size_in_bytes); + generate_stack_overflow_check(bang_size_in_bytes); push(rbp); #ifdef TIERED diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/c2_globals_x86.hpp --- a/src/cpu/x86/vm/c2_globals_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/c2_globals_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -94,6 +94,8 @@ define_pd_global(uintx, CodeCacheMinBlockLength, 4); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); +define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86. + // Heap related flags define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(16*M)); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/compiledIC_x86.cpp --- a/src/cpu/x86/vm/compiledIC_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/compiledIC_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -127,7 +127,7 @@ if (TraceICs) { ResourceMark rm; tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_interpreted %s", - instruction_address(), + p2i(instruction_address()), callee->name_and_sig_as_C_string()); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/cppInterpreter_x86.cpp --- a/src/cpu/x86/vm/cppInterpreter_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/cppInterpreter_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1354,7 +1354,7 @@ // reset handle block __ movptr(t, Address(thread, JavaThread::active_handles_offset())); - __ movptr(Address(t, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); + __ movl(Address(t, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); // If result was an oop then unbox and save it in the frame { Label L; @@ -2336,29 +2336,42 @@ "Stack top out of range"); } -int AbstractInterpreter::layout_activation(Method* 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) { - - assert(popframe_extra_args == 0, "FIX ME"); - // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() - // does as far as allocating an interpreter frame. - // If 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 + +static int frame_size_helper(int max_stack, + int tempcount, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame, + int& monitor_size, + int& full_frame_size) { + int extra_locals_size = (callee_locals - callee_param_count) * BytesPerWord; + monitor_size = sizeof(BasicObjectLock) * moncount; + + // First calculate the frame size without any java expression stack + int short_frame_size = size_activation_helper(extra_locals_size, + monitor_size); + + // Now with full size expression stack + full_frame_size = short_frame_size + max_stack * BytesPerWord; + + // and now with only live portion of the expression stack + short_frame_size = short_frame_size + tempcount * BytesPerWord; + + // the size the activation is right now. Only top frame is full size + int frame_size = (is_top_frame ? full_frame_size : short_frame_size); + return frame_size; +} + +int AbstractInterpreter::size_activation(int max_stack, + int tempcount, + int extra_args, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame) { + assert(extra_args == 0, "FIX ME"); // NOTE: return size is in words not bytes - // NOTE: tempcount is the current size of the java expression stack. For top most - // frames we will allocate a full sized expression stack and not the curback - // version that non-top frames have. // Calculate the amount our frame will be adjust by the callee. For top frame // this is zero. @@ -2368,87 +2381,102 @@ // to it. So it ignores last_frame_adjust value. Seems suspicious as far // as getting sender_sp correct. - int extra_locals_size = (callee_locals - callee_param_count) * BytesPerWord; - int monitor_size = sizeof(BasicObjectLock) * moncount; - - // First calculate the frame size without any java expression stack - int short_frame_size = size_activation_helper(extra_locals_size, - monitor_size); - - // Now with full size expression stack - int full_frame_size = short_frame_size + method->max_stack() * BytesPerWord; - - // and now with only live portion of the expression stack - short_frame_size = short_frame_size + tempcount * BytesPerWord; - - // the size the activation is right now. Only top frame is full size - int frame_size = (is_top_frame ? full_frame_size : short_frame_size); - - if (interpreter_frame != NULL) { + int unused_monitor_size = 0; + int unused_full_frame_size = 0; + return frame_size_helper(max_stack, tempcount, moncount, callee_param_count, callee_locals, + is_top_frame, unused_monitor_size, unused_full_frame_size)/BytesPerWord; +} + +void AbstractInterpreter::layout_activation(Method* 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) { + + assert(popframe_extra_args == 0, "FIX ME"); + // NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state() + // does as far as allocating an interpreter frame. + // Set up the method, locals, and monitors. + // The frame interpreter_frame is guaranteed to be the right size, + // as determined by a previous call to the size_activation() method. + // It is also guaranteed to be walkable even though it is in a skeletal state + // NOTE: tempcount is the current size of the java expression stack. For top most + // frames we will allocate a full sized expression stack and not the curback + // version that non-top frames have. + + int monitor_size = 0; + int full_frame_size = 0; + int frame_size = frame_size_helper(method->max_stack(), tempcount, moncount, callee_param_count, callee_locals, + is_top_frame, monitor_size, full_frame_size); + #ifdef ASSERT - assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); + assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); #endif - // MUCHO HACK - - intptr_t* frame_bottom = (intptr_t*) ((intptr_t)interpreter_frame->sp() - (full_frame_size - frame_size)); - - /* Now fillin the interpreterState object */ - - // The state object is the first thing on the frame and easily located - - interpreterState cur_state = (interpreterState) ((intptr_t)interpreter_frame->fp() - sizeof(BytecodeInterpreter)); - - - // Find the locals pointer. This is rather simple on x86 because there is no - // confusing rounding at the callee to account for. We can trivially locate - // our locals based on the current fp(). - // Note: the + 2 is for handling the "static long no_params() method" issue. - // (too bad I don't really remember that issue well...) - - intptr_t* locals; - // If the caller is interpreted we need to make sure that locals points to the first - // argument that the caller passed and not in an area where the stack might have been extended. - // because the stack to stack to converter needs a proper locals value in order to remove the - // arguments from the caller and place the result in the proper location. Hmm maybe it'd be - // simpler if we simply stored the result in the BytecodeInterpreter object and let the c++ code - // adjust the stack?? HMMM QQQ - // - 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(); - // stack() is prepushed. - locals = prev->stack() + method->size_of_parameters(); - // locals = caller->unextended_sp() + (method->size_of_parameters() - 1); - if (locals != interpreter_frame->fp() + frame::sender_sp_offset + (method->max_locals() - 1) + 2) { - // os::breakpoint(); - } - } else { - // this is where a c2i would have placed locals (except for the +2) - locals = interpreter_frame->fp() + frame::sender_sp_offset + (method->max_locals() - 1) + 2; + // MUCHO HACK + + intptr_t* frame_bottom = (intptr_t*) ((intptr_t)interpreter_frame->sp() - (full_frame_size - frame_size)); + + /* Now fillin the interpreterState object */ + + // The state object is the first thing on the frame and easily located + + interpreterState cur_state = (interpreterState) ((intptr_t)interpreter_frame->fp() - sizeof(BytecodeInterpreter)); + + + // Find the locals pointer. This is rather simple on x86 because there is no + // confusing rounding at the callee to account for. We can trivially locate + // our locals based on the current fp(). + // Note: the + 2 is for handling the "static long no_params() method" issue. + // (too bad I don't really remember that issue well...) + + intptr_t* locals; + // If the caller is interpreted we need to make sure that locals points to the first + // argument that the caller passed and not in an area where the stack might have been extended. + // because the stack to stack to converter needs a proper locals value in order to remove the + // arguments from the caller and place the result in the proper location. Hmm maybe it'd be + // simpler if we simply stored the result in the BytecodeInterpreter object and let the c++ code + // adjust the stack?? HMMM QQQ + // + 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(); + // stack() is prepushed. + locals = prev->stack() + method->size_of_parameters(); + // locals = caller->unextended_sp() + (method->size_of_parameters() - 1); + if (locals != interpreter_frame->fp() + frame::sender_sp_offset + (method->max_locals() - 1) + 2) { + // os::breakpoint(); } - - intptr_t* monitor_base = (intptr_t*) cur_state; - intptr_t* stack_base = (intptr_t*) ((intptr_t) monitor_base - monitor_size); - /* +1 because stack is always prepushed */ - intptr_t* stack = (intptr_t*) ((intptr_t) stack_base - (tempcount + 1) * BytesPerWord); - - - BytecodeInterpreter::layout_interpreterState(cur_state, - caller, - interpreter_frame, - method, - locals, - stack, - stack_base, - monitor_base, - frame_bottom, - is_top_frame); - - // BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, interpreter_frame->fp()); + } else { + // this is where a c2i would have placed locals (except for the +2) + locals = interpreter_frame->fp() + frame::sender_sp_offset + (method->max_locals() - 1) + 2; } - return frame_size/BytesPerWord; + + intptr_t* monitor_base = (intptr_t*) cur_state; + intptr_t* stack_base = (intptr_t*) ((intptr_t) monitor_base - monitor_size); + /* +1 because stack is always prepushed */ + intptr_t* stack = (intptr_t*) ((intptr_t) stack_base - (tempcount + 1) * BytesPerWord); + + + BytecodeInterpreter::layout_interpreterState(cur_state, + caller, + interpreter_frame, + method, + locals, + stack, + stack_base, + monitor_base, + frame_bottom, + is_top_frame); + + // BytecodeInterpreter::pd_layout_interpreterState(cur_state, interpreter_return_address, interpreter_frame->fp()); } #endif // CC_INTERP (all) diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/frame_x86.cpp --- a/src/cpu/x86/vm/frame_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/frame_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -48,6 +48,7 @@ } #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // Profiling/safepoint support diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/frame_x86.inline.hpp --- a/src/cpu/x86/vm/frame_x86.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/frame_x86.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -247,6 +247,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 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/globalDefinitions_x86.hpp --- a/src/cpu/x86/vm/globalDefinitions_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/globalDefinitions_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -27,6 +27,12 @@ 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 = false; + #define SUPPORTS_NATIVE_CX8 #endif // CPU_X86_VM_GLOBALDEFINITIONS_X86_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/globals_x86.hpp --- a/src/cpu/x86/vm/globals_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/globals_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -37,7 +37,8 @@ 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 past to check cast +define_pd_global(bool, TrapBasedNullChecks, false); // Not needed on x86. +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs passed to check cast // See 4827828 for this change. There is no globals_core_i486.hpp. I can't // assign a different value for C2 without touching a number of files. Use @@ -129,11 +130,53 @@ product(bool, UseFastStosb, false, \ "Use fast-string operation for zeroing: rep stosb") \ \ + /* Use Restricted Transactional Memory for lock eliding */ \ + experimental(bool, UseRTMLocking, false, \ + "Enable RTM lock eliding for inflated locks in compiled code") \ + \ + experimental(bool, UseRTMForStackLocks, false, \ + "Enable RTM lock eliding for stack locks in compiled code") \ + \ + experimental(bool, UseRTMDeopt, false, \ + "Perform deopt and recompilation based on RTM abort ratio") \ + \ + experimental(uintx, RTMRetryCount, 5, \ + "Number of RTM retries on lock abort or busy") \ + \ + experimental(intx, RTMSpinLoopCount, 100, \ + "Spin count for lock to become free before RTM retry") \ + \ + experimental(intx, RTMAbortThreshold, 1000, \ + "Calculate abort ratio after this number of aborts") \ + \ + experimental(intx, RTMLockingThreshold, 10000, \ + "Lock count at which to do RTM lock eliding without " \ + "abort ratio calculation") \ + \ + experimental(intx, RTMAbortRatio, 50, \ + "Lock abort ratio at which to stop use RTM lock eliding") \ + \ + experimental(intx, RTMTotalCountIncrRate, 64, \ + "Increment total RTM attempted lock count once every n times") \ + \ + experimental(intx, RTMLockingCalculationDelay, 0, \ + "Number of milliseconds to wait before start calculating aborts " \ + "for RTM locking") \ + \ + experimental(bool, UseRTMXendForLockBusy, true, \ + "Use RTM Xend instead of Xabort when lock busy") \ + \ /* assembler */ \ product(bool, Use486InstrsOnly, false, \ "Use 80486 Compliant instruction subset") \ \ product(bool, UseCountLeadingZerosInstruction, false, \ "Use count leading zeros instruction") \ + \ + product(bool, UseCountTrailingZerosInstruction, false, \ + "Use count trailing zeros instruction") \ + \ + product(bool, UseBMI1Instructions, false, \ + "Use BMI instructions") #endif // CPU_X86_VM_GLOBALS_X86_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/interp_masm_x86.cpp --- a/src/cpu/x86/vm/interp_masm_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/interp_masm_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -127,7 +127,7 @@ if (MethodData::profile_return()) { // We're right after the type profile for the last - // argument. tmp is the number of cell left in the + // argument. tmp is the number of cells left in the // CallTypeData/VirtualCallTypeData to reach its end. Non null // if there's a return to profile. assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); @@ -137,7 +137,7 @@ movptr(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp); } else { assert(MethodData::profile_return(), "either profile call args or call ret"); - update_mdp_by_constant(mdp, in_bytes(ReturnTypeEntry::size())); + update_mdp_by_constant(mdp, in_bytes(TypeEntriesAtCall::return_only_size())); } // mdp points right after the end of the @@ -198,7 +198,7 @@ // parameters. Collect profiling from last parameter down. // mdo start + parameters offset + array length - 1 addptr(mdp, tmp1); - movptr(tmp1, Address(mdp, in_bytes(ArrayData::array_len_offset()))); + movptr(tmp1, Address(mdp, ArrayData::array_len_offset())); decrement(tmp1, TypeStackSlotEntries::per_arg_count()); Label loop; diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/interpreter_x86_64.cpp --- a/src/cpu/x86/vm/interpreter_x86_64.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/interpreter_x86_64.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -51,6 +51,7 @@ #define __ _masm-> +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC #ifdef _WIN64 address AbstractInterpreterGenerator::generate_slow_signature_handler() { diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/macroAssembler_x86.cpp --- a/src/cpu/x86/vm/macroAssembler_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/macroAssembler_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -55,6 +55,7 @@ #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC #ifdef ASSERT bool AbstractAssembler::pd_check_instruction_mark() { return true; } @@ -98,217 +99,6 @@ return Address::make_array(adr); } -int MacroAssembler::biased_locking_enter(Register lock_reg, - Register obj_reg, - Register swap_reg, - Register tmp_reg, - bool swap_reg_contains_mark, - Label& done, - Label* slow_case, - BiasedLockingCounters* counters) { - assert(UseBiasedLocking, "why call this otherwise?"); - assert(swap_reg == rax, "swap_reg must be rax, for cmpxchg"); - assert_different_registers(lock_reg, obj_reg, swap_reg); - - if (PrintBiasedLockingStatistics && counters == NULL) - counters = BiasedLocking::counters(); - - bool need_tmp_reg = false; - if (tmp_reg == noreg) { - need_tmp_reg = true; - tmp_reg = lock_reg; - } else { - assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg); - } - assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); - Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); - Address klass_addr (obj_reg, oopDesc::klass_offset_in_bytes()); - Address saved_mark_addr(lock_reg, 0); - - // 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 - // First check to see whether biasing is even enabled for this object - Label cas_label; - int null_check_offset = -1; - if (!swap_reg_contains_mark) { - null_check_offset = offset(); - movl(swap_reg, mark_addr); - } - if (need_tmp_reg) { - push(tmp_reg); - } - movl(tmp_reg, swap_reg); - andl(tmp_reg, markOopDesc::biased_lock_mask_in_place); - cmpl(tmp_reg, markOopDesc::biased_lock_pattern); - if (need_tmp_reg) { - pop(tmp_reg); - } - jcc(Assembler::notEqual, cas_label); - // The bias pattern is present in the object's header. Need to check - // whether the bias owner and the epoch are both still current. - // Note that because there is no current thread register on x86 we - // need to store off the mark word we read out of the object to - // avoid reloading it and needing to recheck invariants below. This - // store is unfortunate but it makes the overall code shorter and - // simpler. - movl(saved_mark_addr, swap_reg); - if (need_tmp_reg) { - push(tmp_reg); - } - get_thread(tmp_reg); - xorl(swap_reg, tmp_reg); - if (swap_reg_contains_mark) { - null_check_offset = offset(); - } - movl(tmp_reg, klass_addr); - xorl(swap_reg, Address(tmp_reg, Klass::prototype_header_offset())); - andl(swap_reg, ~((int) markOopDesc::age_mask_in_place)); - if (need_tmp_reg) { - pop(tmp_reg); - } - if (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address)counters->biased_lock_entry_count_addr())); - } - jcc(Assembler::equal, 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. - testl(swap_reg, markOopDesc::biased_lock_mask_in_place); - jcc(Assembler::notZero, 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. - testl(swap_reg, markOopDesc::epoch_mask_in_place); - jcc(Assembler::notZero, 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. - movl(swap_reg, saved_mark_addr); - andl(swap_reg, - markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); - if (need_tmp_reg) { - push(tmp_reg); - } - get_thread(tmp_reg); - orl(tmp_reg, swap_reg); - if (os::is_MP()) { - lock(); - } - cmpxchgptr(tmp_reg, Address(obj_reg, 0)); - if (need_tmp_reg) { - pop(tmp_reg); - } - // 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 (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address)counters->anonymously_biased_lock_entry_count_addr())); - } - if (slow_case != NULL) { - jcc(Assembler::notZero, *slow_case); - } - jmp(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. - // - // FIXME: due to a lack of registers we currently blow away the age - // bits in this situation. Should attempt to preserve them. - if (need_tmp_reg) { - push(tmp_reg); - } - get_thread(tmp_reg); - movl(swap_reg, klass_addr); - orl(tmp_reg, Address(swap_reg, Klass::prototype_header_offset())); - movl(swap_reg, saved_mark_addr); - if (os::is_MP()) { - lock(); - } - cmpxchgptr(tmp_reg, Address(obj_reg, 0)); - if (need_tmp_reg) { - pop(tmp_reg); - } - // If the biasing toward our thread failed, then another thread - // succeeded in biasing it toward itself and we need to revoke that - // bias. The revocation will occur in the runtime in the slow case. - if (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address)counters->rebiased_lock_entry_count_addr())); - } - if (slow_case != NULL) { - jcc(Assembler::notZero, *slow_case); - } - jmp(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. - // - // FIXME: due to a lack of registers we currently blow away the age - // bits in this situation. Should attempt to preserve them. - movl(swap_reg, saved_mark_addr); - if (need_tmp_reg) { - push(tmp_reg); - } - movl(tmp_reg, klass_addr); - movl(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset())); - if (os::is_MP()) { - lock(); - } - cmpxchgptr(tmp_reg, Address(obj_reg, 0)); - if (need_tmp_reg) { - pop(tmp_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 (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address)counters->revoked_lock_entry_count_addr())); - } - - bind(cas_label); - - return null_check_offset; -} void MacroAssembler::call_VM_leaf_base(address entry_point, int number_of_arguments) { call(RuntimeAddress(entry_point)); @@ -512,7 +302,9 @@ mov_literal32(dst, (int32_t)obj, metadata_Relocation::spec_for_immediate()); } -void MacroAssembler::movptr(Register dst, AddressLiteral src) { +void MacroAssembler::movptr(Register dst, AddressLiteral src, Register scratch) { + // scratch register is not used, + // it is defined to match parameters of 64-bit version of this method. if (src.is_lval()) { mov_literal32(dst, (intptr_t)src.target(), src.rspec()); } else { @@ -726,165 +518,6 @@ return array; } -int MacroAssembler::biased_locking_enter(Register lock_reg, - Register obj_reg, - Register swap_reg, - Register tmp_reg, - bool swap_reg_contains_mark, - Label& done, - Label* slow_case, - BiasedLockingCounters* counters) { - assert(UseBiasedLocking, "why call this otherwise?"); - assert(swap_reg == rax, "swap_reg must be rax for cmpxchgq"); - assert(tmp_reg != noreg, "tmp_reg must be supplied"); - assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg); - assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); - Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); - Address saved_mark_addr(lock_reg, 0); - - if (PrintBiasedLockingStatistics && counters == NULL) - counters = BiasedLocking::counters(); - - // 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 - // First check to see whether biasing is even enabled for this object - Label cas_label; - int null_check_offset = -1; - if (!swap_reg_contains_mark) { - null_check_offset = offset(); - movq(swap_reg, mark_addr); - } - movq(tmp_reg, swap_reg); - andq(tmp_reg, markOopDesc::biased_lock_mask_in_place); - cmpq(tmp_reg, markOopDesc::biased_lock_pattern); - jcc(Assembler::notEqual, cas_label); - // The bias pattern is present in the object's header. Need to check - // whether the bias owner and the epoch are both still current. - load_prototype_header(tmp_reg, obj_reg); - orq(tmp_reg, r15_thread); - xorq(tmp_reg, swap_reg); - andq(tmp_reg, ~((int) markOopDesc::age_mask_in_place)); - if (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr())); - } - jcc(Assembler::equal, 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. - testq(tmp_reg, markOopDesc::biased_lock_mask_in_place); - jcc(Assembler::notZero, 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. - testq(tmp_reg, markOopDesc::epoch_mask_in_place); - jcc(Assembler::notZero, 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. - andq(swap_reg, - markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); - movq(tmp_reg, swap_reg); - orq(tmp_reg, r15_thread); - if (os::is_MP()) { - lock(); - } - cmpxchgq(tmp_reg, Address(obj_reg, 0)); - // 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 (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr())); - } - if (slow_case != NULL) { - jcc(Assembler::notZero, *slow_case); - } - jmp(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. - // - // FIXME: due to a lack of registers we currently blow away the age - // bits in this situation. Should attempt to preserve them. - load_prototype_header(tmp_reg, obj_reg); - orq(tmp_reg, r15_thread); - if (os::is_MP()) { - lock(); - } - cmpxchgq(tmp_reg, Address(obj_reg, 0)); - // If the biasing toward our thread failed, then another thread - // succeeded in biasing it toward itself and we need to revoke that - // bias. The revocation will occur in the runtime in the slow case. - if (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address) counters->rebiased_lock_entry_count_addr())); - } - if (slow_case != NULL) { - jcc(Assembler::notZero, *slow_case); - } - jmp(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. - // - // FIXME: due to a lack of registers we currently blow away the age - // bits in this situation. Should attempt to preserve them. - load_prototype_header(tmp_reg, obj_reg); - if (os::is_MP()) { - lock(); - } - cmpxchgq(tmp_reg, Address(obj_reg, 0)); - // 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 (counters != NULL) { - cond_inc32(Assembler::zero, - ExternalAddress((address) counters->revoked_lock_entry_count_addr())); - } - - bind(cas_label); - - return null_check_offset; -} - void MacroAssembler::call_VM_leaf_base(address entry_point, int num_args) { Label L, E; @@ -983,6 +616,15 @@ /* else */ { subq(dst, value) ; return; } } +void MacroAssembler::incrementq(AddressLiteral dst) { + if (reachable(dst)) { + incrementq(as_Address(dst)); + } else { + lea(rscratch1, dst); + incrementq(Address(rscratch1, 0)); + } +} + void MacroAssembler::incrementq(Register reg, int value) { if (value == min_jint) { addq(reg, value); return; } if (value < 0) { decrementq(reg, -value); return; } @@ -1051,15 +693,15 @@ movq(dst, rscratch1); } -void MacroAssembler::movptr(Register dst, AddressLiteral src) { +void MacroAssembler::movptr(Register dst, AddressLiteral src, Register scratch) { if (src.is_lval()) { mov_literal64(dst, (intptr_t)src.target(), src.rspec()); } else { if (reachable(src)) { movq(dst, as_Address(src)); } else { - lea(rscratch1, src); - movq(dst, Address(rscratch1,0)); + lea(scratch, src); + movq(dst, Address(scratch, 0)); } } } @@ -1358,13 +1000,37 @@ LP64_ONLY(andq(dst, imm32)) NOT_LP64(andl(dst, imm32)); } -void MacroAssembler::atomic_incl(AddressLiteral counter_addr) { - pushf(); +void MacroAssembler::atomic_incl(Address counter_addr) { if (os::is_MP()) lock(); incrementl(counter_addr); - popf(); -} +} + +void MacroAssembler::atomic_incl(AddressLiteral counter_addr, Register scr) { + if (reachable(counter_addr)) { + atomic_incl(as_Address(counter_addr)); + } else { + lea(scr, counter_addr); + atomic_incl(Address(scr, 0)); + } +} + +#ifdef _LP64 +void MacroAssembler::atomic_incq(Address counter_addr) { + if (os::is_MP()) + lock(); + incrementq(counter_addr); +} + +void MacroAssembler::atomic_incq(AddressLiteral counter_addr, Register scr) { + if (reachable(counter_addr)) { + atomic_incq(as_Address(counter_addr)); + } else { + lea(scr, counter_addr); + atomic_incq(Address(scr, 0)); + } +} +#endif // Writes to stack successive pages until offset reached to check for // stack overflow + shadow pages. This clobbers tmp. @@ -1386,13 +1052,241 @@ // was post-decremented.) Skip this address by starting at i=1, and // touch a few more pages below. N.B. It is important to touch all // the way down to and including i=StackShadowPages. - for (int i = 1; i <= StackShadowPages; i++) { + for (int i = 1; i < StackShadowPages; i++) { // this could be any sized move but this is can be a debugging crumb // so the bigger the better. movptr(Address(tmp, (-i*os::vm_page_size())), size ); } } +int MacroAssembler::biased_locking_enter(Register lock_reg, + Register obj_reg, + Register swap_reg, + Register tmp_reg, + bool swap_reg_contains_mark, + Label& done, + Label* slow_case, + BiasedLockingCounters* counters) { + assert(UseBiasedLocking, "why call this otherwise?"); + assert(swap_reg == rax, "swap_reg must be rax for cmpxchgq"); + LP64_ONLY( assert(tmp_reg != noreg, "tmp_reg must be supplied"); ) + bool need_tmp_reg = false; + if (tmp_reg == noreg) { + need_tmp_reg = true; + tmp_reg = lock_reg; + assert_different_registers(lock_reg, obj_reg, swap_reg); + } else { + assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg); + } + assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout"); + Address mark_addr (obj_reg, oopDesc::mark_offset_in_bytes()); + Address saved_mark_addr(lock_reg, 0); + + if (PrintBiasedLockingStatistics && counters == NULL) { + counters = BiasedLocking::counters(); + } + // 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 + // First check to see whether biasing is even enabled for this object + Label cas_label; + int null_check_offset = -1; + if (!swap_reg_contains_mark) { + null_check_offset = offset(); + movptr(swap_reg, mark_addr); + } + if (need_tmp_reg) { + push(tmp_reg); + } + movptr(tmp_reg, swap_reg); + andptr(tmp_reg, markOopDesc::biased_lock_mask_in_place); + cmpptr(tmp_reg, markOopDesc::biased_lock_pattern); + if (need_tmp_reg) { + pop(tmp_reg); + } + jcc(Assembler::notEqual, cas_label); + // The bias pattern is present in the object's header. Need to check + // whether the bias owner and the epoch are both still current. +#ifndef _LP64 + // Note that because there is no current thread register on x86_32 we + // need to store off the mark word we read out of the object to + // avoid reloading it and needing to recheck invariants below. This + // store is unfortunate but it makes the overall code shorter and + // simpler. + movptr(saved_mark_addr, swap_reg); +#endif + if (need_tmp_reg) { + push(tmp_reg); + } + if (swap_reg_contains_mark) { + null_check_offset = offset(); + } + load_prototype_header(tmp_reg, obj_reg); +#ifdef _LP64 + orptr(tmp_reg, r15_thread); + xorptr(tmp_reg, swap_reg); + Register header_reg = tmp_reg; +#else + xorptr(tmp_reg, swap_reg); + get_thread(swap_reg); + xorptr(swap_reg, tmp_reg); + Register header_reg = swap_reg; +#endif + andptr(header_reg, ~((int) markOopDesc::age_mask_in_place)); + if (need_tmp_reg) { + pop(tmp_reg); + } + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->biased_lock_entry_count_addr())); + } + jcc(Assembler::equal, 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. + testptr(header_reg, markOopDesc::biased_lock_mask_in_place); + jccb(Assembler::notZero, 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. + testptr(header_reg, markOopDesc::epoch_mask_in_place); + jccb(Assembler::notZero, 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. + NOT_LP64( movptr(swap_reg, saved_mark_addr); ) + andptr(swap_reg, + markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place); + if (need_tmp_reg) { + push(tmp_reg); + } +#ifdef _LP64 + movptr(tmp_reg, swap_reg); + orptr(tmp_reg, r15_thread); +#else + get_thread(tmp_reg); + orptr(tmp_reg, swap_reg); +#endif + if (os::is_MP()) { + lock(); + } + cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg + if (need_tmp_reg) { + pop(tmp_reg); + } + // 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 (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->anonymously_biased_lock_entry_count_addr())); + } + if (slow_case != NULL) { + jcc(Assembler::notZero, *slow_case); + } + jmp(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. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + if (need_tmp_reg) { + push(tmp_reg); + } + load_prototype_header(tmp_reg, obj_reg); +#ifdef _LP64 + orptr(tmp_reg, r15_thread); +#else + get_thread(swap_reg); + orptr(tmp_reg, swap_reg); + movptr(swap_reg, saved_mark_addr); +#endif + if (os::is_MP()) { + lock(); + } + cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg + if (need_tmp_reg) { + pop(tmp_reg); + } + // If the biasing toward our thread failed, then another thread + // succeeded in biasing it toward itself and we need to revoke that + // bias. The revocation will occur in the runtime in the slow case. + if (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->rebiased_lock_entry_count_addr())); + } + if (slow_case != NULL) { + jcc(Assembler::notZero, *slow_case); + } + jmp(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. + // + // FIXME: due to a lack of registers we currently blow away the age + // bits in this situation. Should attempt to preserve them. + NOT_LP64( movptr(swap_reg, saved_mark_addr); ) + if (need_tmp_reg) { + push(tmp_reg); + } + load_prototype_header(tmp_reg, obj_reg); + if (os::is_MP()) { + lock(); + } + cmpxchgptr(tmp_reg, mark_addr); // compare tmp_reg and swap_reg + if (need_tmp_reg) { + pop(tmp_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 (counters != NULL) { + cond_inc32(Assembler::zero, + ExternalAddress((address) counters->revoked_lock_entry_count_addr())); + } + + bind(cas_label); + + return null_check_offset; +} + void MacroAssembler::biased_locking_exit(Register obj_reg, Register temp_reg, Label& done) { assert(UseBiasedLocking, "why call this otherwise?"); @@ -1408,6 +1302,992 @@ jcc(Assembler::equal, done); } +#ifdef COMPILER2 + +#if INCLUDE_RTM_OPT + +// Update rtm_counters based on abort status +// input: abort_status +// rtm_counters (RTMLockingCounters*) +// flags are killed +void MacroAssembler::rtm_counters_update(Register abort_status, Register rtm_counters) { + + atomic_incptr(Address(rtm_counters, RTMLockingCounters::abort_count_offset())); + if (PrintPreciseRTMLockingStatistics) { + for (int i = 0; i < RTMLockingCounters::ABORT_STATUS_LIMIT; i++) { + Label check_abort; + testl(abort_status, (1< 0) { + // Delay calculation + movptr(tmpReg, ExternalAddress((address) RTMLockingCounters::rtm_calculation_flag_addr()), tmpReg); + testptr(tmpReg, tmpReg); + jccb(Assembler::equal, L_done); + } + // Abort ratio calculation only if abort_count > RTMAbortThreshold + // Aborted transactions = abort_count * 100 + // All transactions = total_count * RTMTotalCountIncrRate + // Set no_rtm bit if (Aborted transactions >= All transactions * RTMAbortRatio) + + movptr(tmpReg, Address(rtm_counters_Reg, RTMLockingCounters::abort_count_offset())); + cmpptr(tmpReg, RTMAbortThreshold); + jccb(Assembler::below, L_check_always_rtm2); + imulptr(tmpReg, tmpReg, 100); + + Register scrReg = rtm_counters_Reg; + movptr(scrReg, Address(rtm_counters_Reg, RTMLockingCounters::total_count_offset())); + imulptr(scrReg, scrReg, RTMTotalCountIncrRate); + imulptr(scrReg, scrReg, RTMAbortRatio); + cmpptr(tmpReg, scrReg); + jccb(Assembler::below, L_check_always_rtm1); + if (method_data != NULL) { + // set rtm_state to "no rtm" in MDO + mov_metadata(tmpReg, method_data); + if (os::is_MP()) { + lock(); + } + orl(Address(tmpReg, MethodData::rtm_state_offset_in_bytes()), NoRTM); + } + jmpb(L_done); + bind(L_check_always_rtm1); + // Reload RTMLockingCounters* address + lea(rtm_counters_Reg, ExternalAddress((address)rtm_counters)); + bind(L_check_always_rtm2); + movptr(tmpReg, Address(rtm_counters_Reg, RTMLockingCounters::total_count_offset())); + cmpptr(tmpReg, RTMLockingThreshold / RTMTotalCountIncrRate); + jccb(Assembler::below, L_done); + if (method_data != NULL) { + // set rtm_state to "always rtm" in MDO + mov_metadata(tmpReg, method_data); + if (os::is_MP()) { + lock(); + } + orl(Address(tmpReg, MethodData::rtm_state_offset_in_bytes()), UseRTM); + } + bind(L_done); +} + +// Update counters and perform abort ratio calculation +// input: abort_status_Reg +// rtm_counters_Reg, flags are killed +void MacroAssembler::rtm_profiling(Register abort_status_Reg, + Register rtm_counters_Reg, + RTMLockingCounters* rtm_counters, + Metadata* method_data, + bool profile_rtm) { + + assert(rtm_counters != NULL, "should not be NULL when profiling RTM"); + // update rtm counters based on rax value at abort + // reads abort_status_Reg, updates flags + lea(rtm_counters_Reg, ExternalAddress((address)rtm_counters)); + rtm_counters_update(abort_status_Reg, rtm_counters_Reg); + if (profile_rtm) { + // Save abort status because abort_status_Reg is used by following code. + if (RTMRetryCount > 0) { + push(abort_status_Reg); + } + assert(rtm_counters != NULL, "should not be NULL when profiling RTM"); + rtm_abort_ratio_calculation(abort_status_Reg, rtm_counters_Reg, rtm_counters, method_data); + // restore abort status + if (RTMRetryCount > 0) { + pop(abort_status_Reg); + } + } +} + +// Retry on abort if abort's status is 0x6: can retry (0x2) | memory conflict (0x4) +// inputs: retry_count_Reg +// : abort_status_Reg +// output: retry_count_Reg decremented by 1 +// flags are killed +void MacroAssembler::rtm_retry_lock_on_abort(Register retry_count_Reg, Register abort_status_Reg, Label& retryLabel) { + Label doneRetry; + assert(abort_status_Reg == rax, ""); + // The abort reason bits are in eax (see all states in rtmLocking.hpp) + // 0x6 = conflict on which we can retry (0x2) | memory conflict (0x4) + // if reason is in 0x6 and retry count != 0 then retry + andptr(abort_status_Reg, 0x6); + jccb(Assembler::zero, doneRetry); + testl(retry_count_Reg, retry_count_Reg); + jccb(Assembler::zero, doneRetry); + pause(); + decrementl(retry_count_Reg); + jmp(retryLabel); + bind(doneRetry); +} + +// Spin and retry if lock is busy, +// inputs: box_Reg (monitor address) +// : retry_count_Reg +// output: retry_count_Reg decremented by 1 +// : clear z flag if retry count exceeded +// tmp_Reg, scr_Reg, flags are killed +void MacroAssembler::rtm_retry_lock_on_busy(Register retry_count_Reg, Register box_Reg, + Register tmp_Reg, Register scr_Reg, Label& retryLabel) { + Label SpinLoop, SpinExit, doneRetry; + // Clean monitor_value bit to get valid pointer + int owner_offset = ObjectMonitor::owner_offset_in_bytes() - markOopDesc::monitor_value; + + testl(retry_count_Reg, retry_count_Reg); + jccb(Assembler::zero, doneRetry); + decrementl(retry_count_Reg); + movptr(scr_Reg, RTMSpinLoopCount); + + bind(SpinLoop); + pause(); + decrementl(scr_Reg); + jccb(Assembler::lessEqual, SpinExit); + movptr(tmp_Reg, Address(box_Reg, owner_offset)); + testptr(tmp_Reg, tmp_Reg); + jccb(Assembler::notZero, SpinLoop); + + bind(SpinExit); + jmp(retryLabel); + bind(doneRetry); + incrementl(retry_count_Reg); // clear z flag +} + +// Use RTM for normal stack locks +// Input: objReg (object to lock) +void MacroAssembler::rtm_stack_locking(Register objReg, Register tmpReg, Register scrReg, + Register retry_on_abort_count_Reg, + RTMLockingCounters* stack_rtm_counters, + Metadata* method_data, bool profile_rtm, + Label& DONE_LABEL, Label& IsInflated) { + assert(UseRTMForStackLocks, "why call this otherwise?"); + assert(!UseBiasedLocking, "Biased locking is not supported with RTM locking"); + assert(tmpReg == rax, ""); + assert(scrReg == rdx, ""); + Label L_rtm_retry, L_decrement_retry, L_on_abort; + + if (RTMRetryCount > 0) { + movl(retry_on_abort_count_Reg, RTMRetryCount); // Retry on abort + bind(L_rtm_retry); + } + movptr(tmpReg, Address(objReg, 0)); + testptr(tmpReg, markOopDesc::monitor_value); // inflated vs stack-locked|neutral|biased + jcc(Assembler::notZero, IsInflated); + + if (PrintPreciseRTMLockingStatistics || profile_rtm) { + Label L_noincrement; + if (RTMTotalCountIncrRate > 1) { + // tmpReg, scrReg and flags are killed + branch_on_random_using_rdtsc(tmpReg, scrReg, (int)RTMTotalCountIncrRate, L_noincrement); + } + assert(stack_rtm_counters != NULL, "should not be NULL when profiling RTM"); + atomic_incptr(ExternalAddress((address)stack_rtm_counters->total_count_addr()), scrReg); + bind(L_noincrement); + } + xbegin(L_on_abort); + movptr(tmpReg, Address(objReg, 0)); // fetch markword + andptr(tmpReg, markOopDesc::biased_lock_mask_in_place); // look at 3 lock bits + cmpptr(tmpReg, markOopDesc::unlocked_value); // bits = 001 unlocked + jcc(Assembler::equal, DONE_LABEL); // all done if unlocked + + Register abort_status_Reg = tmpReg; // status of abort is stored in RAX + if (UseRTMXendForLockBusy) { + xend(); + movptr(abort_status_Reg, 0x2); // Set the abort status to 2 (so we can retry) + jmp(L_decrement_retry); + } + else { + xabort(0); + } + bind(L_on_abort); + if (PrintPreciseRTMLockingStatistics || profile_rtm) { + rtm_profiling(abort_status_Reg, scrReg, stack_rtm_counters, method_data, profile_rtm); + } + bind(L_decrement_retry); + if (RTMRetryCount > 0) { + // retry on lock abort if abort status is 'can retry' (0x2) or 'memory conflict' (0x4) + rtm_retry_lock_on_abort(retry_on_abort_count_Reg, abort_status_Reg, L_rtm_retry); + } +} + +// Use RTM for inflating locks +// inputs: objReg (object to lock) +// boxReg (on-stack box address (displaced header location) - KILLED) +// tmpReg (ObjectMonitor address + 2(monitor_value)) +void MacroAssembler::rtm_inflated_locking(Register objReg, Register boxReg, Register tmpReg, + Register scrReg, Register retry_on_busy_count_Reg, + Register retry_on_abort_count_Reg, + RTMLockingCounters* rtm_counters, + Metadata* method_data, bool profile_rtm, + Label& DONE_LABEL) { + assert(UseRTMLocking, "why call this otherwise?"); + assert(tmpReg == rax, ""); + assert(scrReg == rdx, ""); + Label L_rtm_retry, L_decrement_retry, L_on_abort; + // Clean monitor_value bit to get valid pointer + int owner_offset = ObjectMonitor::owner_offset_in_bytes() - markOopDesc::monitor_value; + + // Without cast to int32_t a movptr will destroy r10 which is typically obj + movptr(Address(boxReg, 0), (int32_t)intptr_t(markOopDesc::unused_mark())); + movptr(boxReg, tmpReg); // Save ObjectMonitor address + + if (RTMRetryCount > 0) { + movl(retry_on_busy_count_Reg, RTMRetryCount); // Retry on lock busy + movl(retry_on_abort_count_Reg, RTMRetryCount); // Retry on abort + bind(L_rtm_retry); + } + if (PrintPreciseRTMLockingStatistics || profile_rtm) { + Label L_noincrement; + if (RTMTotalCountIncrRate > 1) { + // tmpReg, scrReg and flags are killed + branch_on_random_using_rdtsc(tmpReg, scrReg, (int)RTMTotalCountIncrRate, L_noincrement); + } + assert(rtm_counters != NULL, "should not be NULL when profiling RTM"); + atomic_incptr(ExternalAddress((address)rtm_counters->total_count_addr()), scrReg); + bind(L_noincrement); + } + xbegin(L_on_abort); + movptr(tmpReg, Address(objReg, 0)); + movptr(tmpReg, Address(tmpReg, owner_offset)); + testptr(tmpReg, tmpReg); + jcc(Assembler::zero, DONE_LABEL); + if (UseRTMXendForLockBusy) { + xend(); + jmp(L_decrement_retry); + } + else { + xabort(0); + } + bind(L_on_abort); + Register abort_status_Reg = tmpReg; // status of abort is stored in RAX + if (PrintPreciseRTMLockingStatistics || profile_rtm) { + rtm_profiling(abort_status_Reg, scrReg, rtm_counters, method_data, profile_rtm); + } + if (RTMRetryCount > 0) { + // retry on lock abort if abort status is 'can retry' (0x2) or 'memory conflict' (0x4) + rtm_retry_lock_on_abort(retry_on_abort_count_Reg, abort_status_Reg, L_rtm_retry); + } + + movptr(tmpReg, Address(boxReg, owner_offset)) ; + testptr(tmpReg, tmpReg) ; + jccb(Assembler::notZero, L_decrement_retry) ; + + // Appears unlocked - try to swing _owner from null to non-null. + // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. +#ifdef _LP64 + Register threadReg = r15_thread; +#else + get_thread(scrReg); + Register threadReg = scrReg; +#endif + if (os::is_MP()) { + lock(); + } + cmpxchgptr(threadReg, Address(boxReg, owner_offset)); // Updates tmpReg + + if (RTMRetryCount > 0) { + // success done else retry + jccb(Assembler::equal, DONE_LABEL) ; + bind(L_decrement_retry); + // Spin and retry if lock is busy. + rtm_retry_lock_on_busy(retry_on_busy_count_Reg, boxReg, tmpReg, scrReg, L_rtm_retry); + } + else { + bind(L_decrement_retry); + } +} + +#endif // INCLUDE_RTM_OPT + +// Fast_Lock and Fast_Unlock used by C2 + +// Because the transitions from emitted code to the runtime +// monitorenter/exit helper stubs are so slow it's critical that +// we inline both the stack-locking fast-path and the inflated fast path. +// +// See also: cmpFastLock and cmpFastUnlock. +// +// What follows is a specialized inline transliteration of the code +// in slow_enter() and slow_exit(). If we're concerned about I$ bloat +// another option would be to emit TrySlowEnter and TrySlowExit methods +// at startup-time. These methods would accept arguments as +// (rax,=Obj, rbx=Self, rcx=box, rdx=Scratch) and return success-failure +// indications in the icc.ZFlag. Fast_Lock and Fast_Unlock would simply +// marshal the arguments and emit calls to TrySlowEnter and TrySlowExit. +// In practice, however, the # of lock sites is bounded and is usually small. +// Besides the call overhead, TrySlowEnter and TrySlowExit might suffer +// if the processor uses simple bimodal branch predictors keyed by EIP +// Since the helper routines would be called from multiple synchronization +// sites. +// +// An even better approach would be write "MonitorEnter()" and "MonitorExit()" +// in java - using j.u.c and unsafe - and just bind the lock and unlock sites +// to those specialized methods. That'd give us a mostly platform-independent +// implementation that the JITs could optimize and inline at their pleasure. +// Done correctly, the only time we'd need to cross to native could would be +// to park() or unpark() threads. We'd also need a few more unsafe operators +// to (a) prevent compiler-JIT reordering of non-volatile accesses, and +// (b) explicit barriers or fence operations. +// +// TODO: +// +// * Arrange for C2 to pass "Self" into Fast_Lock and Fast_Unlock in one of the registers (scr). +// This avoids manifesting the Self pointer in the Fast_Lock and Fast_Unlock terminals. +// Given TLAB allocation, Self is usually manifested in a register, so passing it into +// the lock operators would typically be faster than reifying Self. +// +// * Ideally I'd define the primitives as: +// fast_lock (nax Obj, nax box, EAX tmp, nax scr) where box, tmp and scr are KILLED. +// fast_unlock (nax Obj, EAX box, nax tmp) where box and tmp are KILLED +// Unfortunately ADLC bugs prevent us from expressing the ideal form. +// Instead, we're stuck with a rather awkward and brittle register assignments below. +// Furthermore the register assignments are overconstrained, possibly resulting in +// sub-optimal code near the synchronization site. +// +// * Eliminate the sp-proximity tests and just use "== Self" tests instead. +// Alternately, use a better sp-proximity test. +// +// * Currently ObjectMonitor._Owner can hold either an sp value or a (THREAD *) value. +// Either one is sufficient to uniquely identify a thread. +// TODO: eliminate use of sp in _owner and use get_thread(tr) instead. +// +// * Intrinsify notify() and notifyAll() for the common cases where the +// object is locked by the calling thread but the waitlist is empty. +// avoid the expensive JNI call to JVM_Notify() and JVM_NotifyAll(). +// +// * use jccb and jmpb instead of jcc and jmp to improve code density. +// But beware of excessive branch density on AMD Opterons. +// +// * Both Fast_Lock and Fast_Unlock set the ICC.ZF to indicate success +// or failure of the fast-path. If the fast-path fails then we pass +// control to the slow-path, typically in C. In Fast_Lock and +// Fast_Unlock we often branch to DONE_LABEL, just to find that C2 +// will emit a conditional branch immediately after the node. +// So we have branches to branches and lots of ICC.ZF games. +// Instead, it might be better to have C2 pass a "FailureLabel" +// into Fast_Lock and Fast_Unlock. In the case of success, control +// will drop through the node. ICC.ZF is undefined at exit. +// In the case of failure, the node will branch directly to the +// FailureLabel + + +// obj: object to lock +// box: on-stack box address (displaced header location) - KILLED +// rax,: tmp -- KILLED +// scr: tmp -- KILLED +void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg, + Register scrReg, Register cx1Reg, Register cx2Reg, + BiasedLockingCounters* counters, + RTMLockingCounters* rtm_counters, + RTMLockingCounters* stack_rtm_counters, + Metadata* method_data, + bool use_rtm, bool profile_rtm) { + // Ensure the register assignents are disjoint + assert(tmpReg == rax, ""); + + if (use_rtm) { + assert_different_registers(objReg, boxReg, tmpReg, scrReg, cx1Reg, cx2Reg); + } else { + assert(cx1Reg == noreg, ""); + assert(cx2Reg == noreg, ""); + assert_different_registers(objReg, boxReg, tmpReg, scrReg); + } + + if (counters != NULL) { + atomic_incl(ExternalAddress((address)counters->total_entry_count_addr()), scrReg); + } + if (EmitSync & 1) { + // set box->dhw = unused_mark (3) + // Force all sync thru slow-path: slow_enter() and slow_exit() + movptr (Address(boxReg, 0), (int32_t)intptr_t(markOopDesc::unused_mark())); + cmpptr (rsp, (int32_t)NULL_WORD); + } else + if (EmitSync & 2) { + Label DONE_LABEL ; + if (UseBiasedLocking) { + // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. + biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, counters); + } + + movptr(tmpReg, Address(objReg, 0)); // fetch markword + orptr (tmpReg, 0x1); + movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS + if (os::is_MP()) { + lock(); + } + cmpxchgptr(boxReg, Address(objReg, 0)); // Updates tmpReg + jccb(Assembler::equal, DONE_LABEL); + // Recursive locking + subptr(tmpReg, rsp); + andptr(tmpReg, (int32_t) (NOT_LP64(0xFFFFF003) LP64_ONLY(7 - os::vm_page_size())) ); + movptr(Address(boxReg, 0), tmpReg); + bind(DONE_LABEL); + } else { + // Possible cases that we'll encounter in fast_lock + // ------------------------------------------------ + // * Inflated + // -- unlocked + // -- Locked + // = by self + // = by other + // * biased + // -- by Self + // -- by other + // * neutral + // * stack-locked + // -- by self + // = sp-proximity test hits + // = sp-proximity test generates false-negative + // -- by other + // + + Label IsInflated, DONE_LABEL; + + // it's stack-locked, biased or neutral + // TODO: optimize away redundant LDs of obj->mark and improve the markword triage + // order to reduce the number of conditional branches in the most common cases. + // Beware -- there's a subtle invariant that fetch of the markword + // at [FETCH], below, will never observe a biased encoding (*101b). + // If this invariant is not held we risk exclusion (safety) failure. + if (UseBiasedLocking && !UseOptoBiasInlining) { + biased_locking_enter(boxReg, objReg, tmpReg, scrReg, true, DONE_LABEL, NULL, counters); + } + +#if INCLUDE_RTM_OPT + if (UseRTMForStackLocks && use_rtm) { + rtm_stack_locking(objReg, tmpReg, scrReg, cx2Reg, + stack_rtm_counters, method_data, profile_rtm, + DONE_LABEL, IsInflated); + } +#endif // INCLUDE_RTM_OPT + + movptr(tmpReg, Address(objReg, 0)); // [FETCH] + testptr(tmpReg, markOopDesc::monitor_value); // inflated vs stack-locked|neutral|biased + jccb(Assembler::notZero, IsInflated); + + // Attempt stack-locking ... + orptr (tmpReg, markOopDesc::unlocked_value); + movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS + if (os::is_MP()) { + lock(); + } + cmpxchgptr(boxReg, Address(objReg, 0)); // Updates tmpReg + if (counters != NULL) { + cond_inc32(Assembler::equal, + ExternalAddress((address)counters->fast_path_entry_count_addr())); + } + jcc(Assembler::equal, DONE_LABEL); // Success + + // Recursive locking. + // The object is stack-locked: markword contains stack pointer to BasicLock. + // Locked by current thread if difference with current SP is less than one page. + subptr(tmpReg, rsp); + // Next instruction set ZFlag == 1 (Success) if difference is less then one page. + andptr(tmpReg, (int32_t) (NOT_LP64(0xFFFFF003) LP64_ONLY(7 - os::vm_page_size())) ); + movptr(Address(boxReg, 0), tmpReg); + if (counters != NULL) { + cond_inc32(Assembler::equal, + ExternalAddress((address)counters->fast_path_entry_count_addr())); + } + jmp(DONE_LABEL); + + bind(IsInflated); + // The object is inflated. tmpReg contains pointer to ObjectMonitor* + 2(monitor_value) + +#if INCLUDE_RTM_OPT + // Use the same RTM locking code in 32- and 64-bit VM. + if (use_rtm) { + rtm_inflated_locking(objReg, boxReg, tmpReg, scrReg, cx1Reg, cx2Reg, + rtm_counters, method_data, profile_rtm, DONE_LABEL); + } else { +#endif // INCLUDE_RTM_OPT + +#ifndef _LP64 + // The object is inflated. + // + // TODO-FIXME: eliminate the ugly use of manifest constants: + // Use markOopDesc::monitor_value instead of "2". + // use markOop::unused_mark() instead of "3". + // The tmpReg value is an objectMonitor reference ORed with + // markOopDesc::monitor_value (2). We can either convert tmpReg to an + // objectmonitor pointer by masking off the "2" bit or we can just + // use tmpReg as an objectmonitor pointer but bias the objectmonitor + // field offsets with "-2" to compensate for and annul the low-order tag bit. + // + // I use the latter as it avoids AGI stalls. + // As such, we write "mov r, [tmpReg+OFFSETOF(Owner)-2]" + // instead of "mov r, [tmpReg+OFFSETOF(Owner)]". + // + #define OFFSET_SKEWED(f) ((ObjectMonitor::f ## _offset_in_bytes())-2) + + // boxReg refers to the on-stack BasicLock in the current frame. + // We'd like to write: + // set box->_displaced_header = markOop::unused_mark(). Any non-0 value suffices. + // This is convenient but results a ST-before-CAS penalty. The following CAS suffers + // additional latency as we have another ST in the store buffer that must drain. + + if (EmitSync & 8192) { + movptr(Address(boxReg, 0), 3); // results in ST-before-CAS penalty + get_thread (scrReg); + movptr(boxReg, tmpReg); // consider: LEA box, [tmp-2] + movptr(tmpReg, NULL_WORD); // consider: xor vs mov + if (os::is_MP()) { + lock(); + } + cmpxchgptr(scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)); + } else + if ((EmitSync & 128) == 0) { // avoid ST-before-CAS + movptr(scrReg, boxReg); + movptr(boxReg, tmpReg); // consider: LEA box, [tmp-2] + + // Using a prefetchw helps avoid later RTS->RTO upgrades and cache probes + if ((EmitSync & 2048) && VM_Version::supports_3dnow_prefetch() && os::is_MP()) { + // prefetchw [eax + Offset(_owner)-2] + prefetchw(Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + } + + if ((EmitSync & 64) == 0) { + // Optimistic form: consider XORL tmpReg,tmpReg + movptr(tmpReg, NULL_WORD); + } else { + // Can suffer RTS->RTO upgrades on shared or cold $ lines + // Test-And-CAS instead of CAS + movptr(tmpReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); // rax, = m->_owner + testptr(tmpReg, tmpReg); // Locked ? + jccb (Assembler::notZero, DONE_LABEL); + } + + // Appears unlocked - try to swing _owner from null to non-null. + // Ideally, I'd manifest "Self" with get_thread and then attempt + // to CAS the register containing Self into m->Owner. + // But we don't have enough registers, so instead we can either try to CAS + // rsp or the address of the box (in scr) into &m->owner. If the CAS succeeds + // we later store "Self" into m->Owner. Transiently storing a stack address + // (rsp or the address of the box) into m->owner is harmless. + // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. + if (os::is_MP()) { + lock(); + } + cmpxchgptr(scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)); + movptr(Address(scrReg, 0), 3); // box->_displaced_header = 3 + jccb (Assembler::notZero, DONE_LABEL); + get_thread (scrReg); // beware: clobbers ICCs + movptr(Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2), scrReg); + xorptr(boxReg, boxReg); // set icc.ZFlag = 1 to indicate success + + // If the CAS fails we can either retry or pass control to the slow-path. + // We use the latter tactic. + // Pass the CAS result in the icc.ZFlag into DONE_LABEL + // If the CAS was successful ... + // Self has acquired the lock + // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. + // Intentional fall-through into DONE_LABEL ... + } else { + movptr(Address(boxReg, 0), intptr_t(markOopDesc::unused_mark())); // results in ST-before-CAS penalty + movptr(boxReg, tmpReg); + + // Using a prefetchw helps avoid later RTS->RTO upgrades and cache probes + if ((EmitSync & 2048) && VM_Version::supports_3dnow_prefetch() && os::is_MP()) { + // prefetchw [eax + Offset(_owner)-2] + prefetchw(Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + } + + if ((EmitSync & 64) == 0) { + // Optimistic form + xorptr (tmpReg, tmpReg); + } else { + // Can suffer RTS->RTO upgrades on shared or cold $ lines + movptr(tmpReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); // rax, = m->_owner + testptr(tmpReg, tmpReg); // Locked ? + jccb (Assembler::notZero, DONE_LABEL); + } + + // Appears unlocked - try to swing _owner from null to non-null. + // Use either "Self" (in scr) or rsp as thread identity in _owner. + // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. + get_thread (scrReg); + if (os::is_MP()) { + lock(); + } + cmpxchgptr(scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)); + + // If the CAS fails we can either retry or pass control to the slow-path. + // We use the latter tactic. + // Pass the CAS result in the icc.ZFlag into DONE_LABEL + // If the CAS was successful ... + // Self has acquired the lock + // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. + // Intentional fall-through into DONE_LABEL ... + } +#else // _LP64 + // It's inflated + + // TODO: someday avoid the ST-before-CAS penalty by + // relocating (deferring) the following ST. + // We should also think about trying a CAS without having + // fetched _owner. If the CAS is successful we may + // avoid an RTO->RTS upgrade on the $line. + + // Without cast to int32_t a movptr will destroy r10 which is typically obj + movptr(Address(boxReg, 0), (int32_t)intptr_t(markOopDesc::unused_mark())); + + movptr (boxReg, tmpReg); + movptr (tmpReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)); + testptr(tmpReg, tmpReg); + jccb (Assembler::notZero, DONE_LABEL); + + // It's inflated and appears unlocked + if (os::is_MP()) { + lock(); + } + cmpxchgptr(r15_thread, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)); + // Intentional fall-through into DONE_LABEL ... +#endif // _LP64 + +#if INCLUDE_RTM_OPT + } // use_rtm() +#endif + // DONE_LABEL is a hot target - we'd really like to place it at the + // start of cache line by padding with NOPs. + // See the AMD and Intel software optimization manuals for the + // most efficient "long" NOP encodings. + // Unfortunately none of our alignment mechanisms suffice. + bind(DONE_LABEL); + + // At DONE_LABEL the icc ZFlag is set as follows ... + // Fast_Unlock uses the same protocol. + // ZFlag == 1 -> Success + // ZFlag == 0 -> Failure - force control through the slow-path + } +} + +// obj: object to unlock +// box: box address (displaced header location), killed. Must be EAX. +// tmp: killed, cannot be obj nor box. +// +// Some commentary on balanced locking: +// +// Fast_Lock and Fast_Unlock are emitted only for provably balanced lock sites. +// Methods that don't have provably balanced locking are forced to run in the +// interpreter - such methods won't be compiled to use fast_lock and fast_unlock. +// The interpreter provides two properties: +// I1: At return-time the interpreter automatically and quietly unlocks any +// objects acquired the current activation (frame). Recall that the +// interpreter maintains an on-stack list of locks currently held by +// a frame. +// I2: If a method attempts to unlock an object that is not held by the +// the frame the interpreter throws IMSX. +// +// Lets say A(), which has provably balanced locking, acquires O and then calls B(). +// B() doesn't have provably balanced locking so it runs in the interpreter. +// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O +// is still locked by A(). +// +// The only other source of unbalanced locking would be JNI. The "Java Native Interface: +// Programmer's Guide and Specification" claims that an object locked by jni_monitorenter +// should not be unlocked by "normal" java-level locking and vice-versa. The specification +// doesn't specify what will occur if a program engages in such mixed-mode locking, however. + +void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpReg, bool use_rtm) { + assert(boxReg == rax, ""); + assert_different_registers(objReg, boxReg, tmpReg); + + if (EmitSync & 4) { + // Disable - inhibit all inlining. Force control through the slow-path + cmpptr (rsp, 0); + } else + if (EmitSync & 8) { + Label DONE_LABEL; + if (UseBiasedLocking) { + biased_locking_exit(objReg, tmpReg, DONE_LABEL); + } + // Classic stack-locking code ... + // Check whether the displaced header is 0 + //(=> recursive unlock) + movptr(tmpReg, Address(boxReg, 0)); + testptr(tmpReg, tmpReg); + jccb(Assembler::zero, DONE_LABEL); + // If not recursive lock, reset the header to displaced header + if (os::is_MP()) { + lock(); + } + cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses RAX which is box + bind(DONE_LABEL); + } else { + Label DONE_LABEL, Stacked, CheckSucc; + + // Critically, the biased locking test must have precedence over + // and appear before the (box->dhw == 0) recursive stack-lock test. + if (UseBiasedLocking && !UseOptoBiasInlining) { + biased_locking_exit(objReg, tmpReg, DONE_LABEL); + } + +#if INCLUDE_RTM_OPT + if (UseRTMForStackLocks && use_rtm) { + assert(!UseBiasedLocking, "Biased locking is not supported with RTM locking"); + Label L_regular_unlock; + movptr(tmpReg, Address(objReg, 0)); // fetch markword + andptr(tmpReg, markOopDesc::biased_lock_mask_in_place); // look at 3 lock bits + cmpptr(tmpReg, markOopDesc::unlocked_value); // bits = 001 unlocked + jccb(Assembler::notEqual, L_regular_unlock); // if !HLE RegularLock + xend(); // otherwise end... + jmp(DONE_LABEL); // ... and we're done + bind(L_regular_unlock); + } +#endif + + cmpptr(Address(boxReg, 0), (int32_t)NULL_WORD); // Examine the displaced header + jcc (Assembler::zero, DONE_LABEL); // 0 indicates recursive stack-lock + movptr(tmpReg, Address(objReg, 0)); // Examine the object's markword + testptr(tmpReg, markOopDesc::monitor_value); // Inflated? + jccb (Assembler::zero, Stacked); + + // It's inflated. +#if INCLUDE_RTM_OPT + if (use_rtm) { + Label L_regular_inflated_unlock; + // Clean monitor_value bit to get valid pointer + int owner_offset = ObjectMonitor::owner_offset_in_bytes() - markOopDesc::monitor_value; + movptr(boxReg, Address(tmpReg, owner_offset)); + testptr(boxReg, boxReg); + jccb(Assembler::notZero, L_regular_inflated_unlock); + xend(); + jmpb(DONE_LABEL); + bind(L_regular_inflated_unlock); + } +#endif + + // Despite our balanced locking property we still check that m->_owner == Self + // as java routines or native JNI code called by this thread might + // have released the lock. + // Refer to the comments in synchronizer.cpp for how we might encode extra + // state in _succ so we can avoid fetching EntryList|cxq. + // + // I'd like to add more cases in fast_lock() and fast_unlock() -- + // such as recursive enter and exit -- but we have to be wary of + // I$ bloat, T$ effects and BP$ effects. + // + // If there's no contention try a 1-0 exit. That is, exit without + // a costly MEMBAR or CAS. See synchronizer.cpp for details on how + // we detect and recover from the race that the 1-0 exit admits. + // + // Conceptually Fast_Unlock() must execute a STST|LDST "release" barrier + // before it STs null into _owner, releasing the lock. Updates + // to data protected by the critical section must be visible before + // we drop the lock (and thus before any other thread could acquire + // the lock and observe the fields protected by the lock). + // IA32's memory-model is SPO, so STs are ordered with respect to + // each other and there's no need for an explicit barrier (fence). + // See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html. +#ifndef _LP64 + get_thread (boxReg); + if ((EmitSync & 4096) && VM_Version::supports_3dnow_prefetch() && os::is_MP()) { + // prefetchw [ebx + Offset(_owner)-2] + prefetchw(Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + } + + // Note that we could employ various encoding schemes to reduce + // the number of loads below (currently 4) to just 2 or 3. + // Refer to the comments in synchronizer.cpp. + // In practice the chain of fetches doesn't seem to impact performance, however. + if ((EmitSync & 65536) == 0 && (EmitSync & 256)) { + // Attempt to reduce branch density - AMD's branch predictor. + xorptr(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + orptr(boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)); + orptr(boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)); + orptr(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)); + jccb (Assembler::notZero, DONE_LABEL); + movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), NULL_WORD); + jmpb (DONE_LABEL); + } else { + xorptr(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + orptr(boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)); + jccb (Assembler::notZero, DONE_LABEL); + movptr(boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)); + orptr(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)); + jccb (Assembler::notZero, CheckSucc); + movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), NULL_WORD); + jmpb (DONE_LABEL); + } + + // The Following code fragment (EmitSync & 65536) improves the performance of + // contended applications and contended synchronization microbenchmarks. + // Unfortunately the emission of the code - even though not executed - causes regressions + // in scimark and jetstream, evidently because of $ effects. Replacing the code + // with an equal number of never-executed NOPs results in the same regression. + // We leave it off by default. + + if ((EmitSync & 65536) != 0) { + Label LSuccess, LGoSlowPath ; + + bind (CheckSucc); + + // Optional pre-test ... it's safe to elide this + if ((EmitSync & 16) == 0) { + cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int32_t)NULL_WORD); + jccb (Assembler::zero, LGoSlowPath); + } + + // We have a classic Dekker-style idiom: + // ST m->_owner = 0 ; MEMBAR; LD m->_succ + // There are a number of ways to implement the barrier: + // (1) lock:andl &m->_owner, 0 + // is fast, but mask doesn't currently support the "ANDL M,IMM32" form. + // LOCK: ANDL [ebx+Offset(_Owner)-2], 0 + // Encodes as 81 31 OFF32 IMM32 or 83 63 OFF8 IMM8 + // (2) If supported, an explicit MFENCE is appealing. + // In older IA32 processors MFENCE is slower than lock:add or xchg + // particularly if the write-buffer is full as might be the case if + // if stores closely precede the fence or fence-equivalent instruction. + // In more modern implementations MFENCE appears faster, however. + // (3) In lieu of an explicit fence, use lock:addl to the top-of-stack + // The $lines underlying the top-of-stack should be in M-state. + // The locked add instruction is serializing, of course. + // (4) Use xchg, which is serializing + // mov boxReg, 0; xchgl boxReg, [tmpReg + Offset(_owner)-2] also works + // (5) ST m->_owner = 0 and then execute lock:orl &m->_succ, 0. + // The integer condition codes will tell us if succ was 0. + // Since _succ and _owner should reside in the same $line and + // we just stored into _owner, it's likely that the $line + // remains in M-state for the lock:orl. + // + // We currently use (3), although it's likely that switching to (2) + // is correct for the future. + + movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), NULL_WORD); + if (os::is_MP()) { + if (VM_Version::supports_sse2() && 1 == FenceInstruction) { + mfence(); + } else { + lock (); addptr(Address(rsp, 0), 0); + } + } + // Ratify _succ remains non-null + cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), 0); + jccb (Assembler::notZero, LSuccess); + + xorptr(boxReg, boxReg); // box is really EAX + if (os::is_MP()) { lock(); } + cmpxchgptr(rsp, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + jccb (Assembler::notEqual, LSuccess); + // Since we're low on registers we installed rsp as a placeholding in _owner. + // Now install Self over rsp. This is safe as we're transitioning from + // non-null to non=null + get_thread (boxReg); + movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), boxReg); + // Intentional fall-through into LGoSlowPath ... + + bind (LGoSlowPath); + orptr(boxReg, 1); // set ICC.ZF=0 to indicate failure + jmpb (DONE_LABEL); + + bind (LSuccess); + xorptr(boxReg, boxReg); // set ICC.ZF=1 to indicate success + jmpb (DONE_LABEL); + } + + bind (Stacked); + // It's not inflated and it's not recursively stack-locked and it's not biased. + // It must be stack-locked. + // Try to reset the header to displaced header. + // The "box" value on the stack is stable, so we can reload + // and be assured we observe the same value as above. + movptr(tmpReg, Address(boxReg, 0)); + if (os::is_MP()) { + lock(); + } + cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses RAX which is box + // Intention fall-thru into DONE_LABEL + + // DONE_LABEL is a hot target - we'd really like to place it at the + // start of cache line by padding with NOPs. + // See the AMD and Intel software optimization manuals for the + // most efficient "long" NOP encodings. + // Unfortunately none of our alignment mechanisms suffice. + if ((EmitSync & 65536) == 0) { + bind (CheckSucc); + } +#else // _LP64 + // It's inflated + movptr(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + xorptr(boxReg, r15_thread); + orptr (boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)); + jccb (Assembler::notZero, DONE_LABEL); + movptr(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)); + orptr (boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)); + jccb (Assembler::notZero, CheckSucc); + movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), (int32_t)NULL_WORD); + jmpb (DONE_LABEL); + + if ((EmitSync & 65536) == 0) { + Label LSuccess, LGoSlowPath ; + bind (CheckSucc); + cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int32_t)NULL_WORD); + jccb (Assembler::zero, LGoSlowPath); + + // I'd much rather use lock:andl m->_owner, 0 as it's faster than the + // the explicit ST;MEMBAR combination, but masm doesn't currently support + // "ANDQ M,IMM". Don't use MFENCE here. lock:add to TOS, xchg, etc + // are all faster when the write buffer is populated. + movptr (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), (int32_t)NULL_WORD); + if (os::is_MP()) { + lock (); addl (Address(rsp, 0), 0); + } + cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int32_t)NULL_WORD); + jccb (Assembler::notZero, LSuccess); + + movptr (boxReg, (int32_t)NULL_WORD); // box is really EAX + if (os::is_MP()) { lock(); } + cmpxchgptr(r15_thread, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); + jccb (Assembler::notEqual, LSuccess); + // Intentional fall-through into slow-path + + bind (LGoSlowPath); + orl (boxReg, 1); // set ICC.ZF=0 to indicate failure + jmpb (DONE_LABEL); + + bind (LSuccess); + testl (boxReg, 0); // set ICC.ZF=1 to indicate success + jmpb (DONE_LABEL); + } + + bind (Stacked); + movptr(tmpReg, Address (boxReg, 0)); // re-fetch + if (os::is_MP()) { lock(); } + cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses RAX which is box + + if (EmitSync & 65536) { + bind (CheckSucc); + } +#endif + bind(DONE_LABEL); + // Avoid branch to branch on AMD processors + if (EmitSync & 32768) { + nop(); + } + } +} +#endif // COMPILER2 + void MacroAssembler::c2bool(Register x) { // implements x == 0 ? 0 : 1 // note: must only look at least-significant byte of x @@ -1969,7 +2849,9 @@ Condition negated_cond = negate_condition(cond); Label L; jcc(negated_cond, L); + pushf(); // Preserve flags atomic_incl(counter_addr); + popf(); bind(L); } @@ -2271,10 +3153,12 @@ // if fast computation is not possible, result is NaN. Requires // fallback from user of this macro. // increase precision for intermediate steps of the computation + BLOCK_COMMENT("fast_pow {"); increase_precision(); fyl2x(); // Stack: (Y*log2(X)) ... pow_exp_core_encoding(); // Stack: exp(X) ... restore_precision(); + BLOCK_COMMENT("} fast_pow"); } void MacroAssembler::fast_exp() { @@ -5212,7 +6096,7 @@ // C2 compiled method's prolog code. -void MacroAssembler::verified_entry(int framesize, bool stack_bang, bool fp_mode_24b) { +void MacroAssembler::verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b) { // WARNING: Initial instruction MUST be 5 bytes or longer so that // NativeJump::patch_verified_entry will be able to patch out the entry @@ -5220,18 +6104,20 @@ // the frame allocation can be either 3 or 6 bytes. So if we don't do // stack bang then we must use the 6 byte frame allocation even if // we have no frame. :-( + assert(stack_bang_size >= framesize || stack_bang_size <= 0, "stack bang size incorrect"); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove word for return addr framesize -= wordSize; + stack_bang_size -= wordSize; // 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 (stack_bang) { - generate_stack_overflow_check(framesize); + if (stack_bang_size > 0) { + generate_stack_overflow_check(stack_bang_size); // We always push rbp, so that on return to interpreter rbp, will be // restored correctly and we can correct the stack. diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/macroAssembler_x86.hpp --- a/src/cpu/x86/vm/macroAssembler_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/macroAssembler_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -27,6 +27,7 @@ #include "asm/assembler.hpp" #include "utilities/macros.hpp" +#include "runtime/rtmLocking.hpp" // MacroAssembler extends Assembler by frequently used macros. @@ -111,7 +112,8 @@ op == 0xE9 /* jmp */ || op == 0xEB /* short jmp */ || (op & 0xF0) == 0x70 /* short jcc */ || - op == 0x0F && (branch[1] & 0xF0) == 0x80 /* jcc */, + op == 0x0F && (branch[1] & 0xF0) == 0x80 /* jcc */ || + op == 0xC7 && branch[1] == 0xF8 /* xbegin */, "Invalid opcode at patch point"); if (op == 0xEB || (op & 0xF0) == 0x70) { @@ -121,7 +123,7 @@ guarantee(this->is8bit(imm8), "Short forward jump exceeds 8-bit offset"); *disp = imm8; } else { - int* disp = (int*) &branch[(op == 0x0F)? 2: 1]; + int* disp = (int*) &branch[(op == 0x0F || op == 0xC7)? 2: 1]; int imm32 = target - (address) &disp[1]; *disp = imm32; } @@ -161,7 +163,6 @@ void incrementq(Register reg, int value = 1); void incrementq(Address dst, int value = 1); - // Support optimal SSE move instructions. void movflt(XMMRegister dst, XMMRegister src) { if (UseXmmRegToRegMoveAll) { movaps(dst, src); return; } @@ -187,6 +188,8 @@ void incrementl(AddressLiteral dst); void incrementl(ArrayAddress dst); + void incrementq(AddressLiteral dst); + // Alignment void align(int modulus); @@ -651,7 +654,40 @@ Label& done, Label* slow_case = NULL, BiasedLockingCounters* counters = NULL); void biased_locking_exit (Register obj_reg, Register temp_reg, Label& done); - +#ifdef COMPILER2 + // Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file. + // See full desription in macroAssembler_x86.cpp. + void fast_lock(Register obj, Register box, Register tmp, + Register scr, Register cx1, Register cx2, + BiasedLockingCounters* counters, + RTMLockingCounters* rtm_counters, + RTMLockingCounters* stack_rtm_counters, + Metadata* method_data, + bool use_rtm, bool profile_rtm); + void fast_unlock(Register obj, Register box, Register tmp, bool use_rtm); +#if INCLUDE_RTM_OPT + void rtm_counters_update(Register abort_status, Register rtm_counters); + void branch_on_random_using_rdtsc(Register tmp, Register scr, int count, Label& brLabel); + void rtm_abort_ratio_calculation(Register tmp, Register rtm_counters_reg, + RTMLockingCounters* rtm_counters, + Metadata* method_data); + void rtm_profiling(Register abort_status_Reg, Register rtm_counters_Reg, + RTMLockingCounters* rtm_counters, Metadata* method_data, bool profile_rtm); + void rtm_retry_lock_on_abort(Register retry_count, Register abort_status, Label& retryLabel); + void rtm_retry_lock_on_busy(Register retry_count, Register box, Register tmp, Register scr, Label& retryLabel); + void rtm_stack_locking(Register obj, Register tmp, Register scr, + Register retry_on_abort_count, + RTMLockingCounters* stack_rtm_counters, + Metadata* method_data, bool profile_rtm, + Label& DONE_LABEL, Label& IsInflated); + void rtm_inflated_locking(Register obj, Register box, Register tmp, + Register scr, Register retry_on_busy_count, + Register retry_on_abort_count, + RTMLockingCounters* rtm_counters, + Metadata* method_data, bool profile_rtm, + Label& DONE_LABEL); +#endif +#endif Condition negate_condition(Condition cond); @@ -716,6 +752,7 @@ void imulptr(Register dst, Register src) { LP64_ONLY(imulq(dst, src)) NOT_LP64(imull(dst, src)); } + void imulptr(Register dst, Register src, int imm32) { LP64_ONLY(imulq(dst, src, imm32)) NOT_LP64(imull(dst, src, imm32)); } void negptr(Register dst) { LP64_ONLY(negq(dst)) NOT_LP64(negl(dst)); } @@ -757,7 +794,14 @@ // Conditionally (atomically, on MPs) increments passed counter address, preserving condition codes. void cond_inc32(Condition cond, AddressLiteral counter_addr); // Unconditional atomic increment. - void atomic_incl(AddressLiteral counter_addr); + void atomic_incl(Address counter_addr); + void atomic_incl(AddressLiteral counter_addr, Register scr = rscratch1); +#ifdef _LP64 + void atomic_incq(Address counter_addr); + void atomic_incq(AddressLiteral counter_addr, Register scr = rscratch1); +#endif + void atomic_incptr(AddressLiteral counter_addr, Register scr = rscratch1) { LP64_ONLY(atomic_incq(counter_addr, scr)) NOT_LP64(atomic_incl(counter_addr, scr)) ; } + void atomic_incptr(Address counter_addr) { LP64_ONLY(atomic_incq(counter_addr)) NOT_LP64(atomic_incl(counter_addr)) ; } void lea(Register dst, AddressLiteral adr); void lea(Address dst, AddressLiteral adr); @@ -1069,7 +1113,11 @@ void movptr(Register dst, Address src); - void movptr(Register dst, AddressLiteral src); +#ifdef _LP64 + void movptr(Register dst, AddressLiteral src, Register scratch=rscratch1); +#else + void movptr(Register dst, AddressLiteral src, Register scratch=noreg); // Scratch reg is ignored in 32-bit +#endif void movptr(Register dst, intptr_t src); void movptr(Register dst, Register src); @@ -1122,7 +1170,7 @@ void movl2ptr(Register dst, Register src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(if (dst != src) movl(dst, src)); } // C2 compiled method's prolog code. - void verified_entry(int framesize, bool stack_bang, bool fp_mode_24b); + void verified_entry(int framesize, int stack_bang_size, bool fp_mode_24b); // clear memory of size 'cnt' qwords, starting at 'base'. void clear_mem(Register base, Register cnt, Register rtmp); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/methodHandles_x86.cpp --- a/src/cpu/x86/vm/methodHandles_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/methodHandles_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -29,6 +29,8 @@ #include "memory/allocation.inline.hpp" #include "prims/methodHandles.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #define __ _masm-> #ifdef PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/nativeInst_x86.cpp --- a/src/cpu/x86/vm/nativeInst_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/nativeInst_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -35,6 +35,8 @@ #include "c1/c1_Runtime1.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void NativeInstruction::wrote(int offset) { ICache::invalidate_word(addr_at(offset)); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/rtmLocking.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/x86/vm/rtmLocking.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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 + * 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 "memory/allocation.inline.hpp" +#include "runtime/task.hpp" +#include "runtime/rtmLocking.hpp" + +// One-shot PeriodicTask subclass for enabling RTM locking +uintx RTMLockingCounters::_calculation_flag = 0; + +class RTMLockingCalculationTask : public PeriodicTask { + public: + RTMLockingCalculationTask(size_t interval_time) : PeriodicTask(interval_time){ } + + virtual void task() { + RTMLockingCounters::_calculation_flag = 1; + // Reclaim our storage and disenroll ourself + delete this; + } +}; + +void RTMLockingCounters::init() { + if (UseRTMLocking && RTMLockingCalculationDelay > 0) { + RTMLockingCalculationTask* task = new RTMLockingCalculationTask(RTMLockingCalculationDelay); + task->enroll(); + } else { + _calculation_flag = 1; + } +} + +//------------------------------print_on------------------------------- +void RTMLockingCounters::print_on(outputStream* st) { + tty->print_cr("# rtm locks total (estimated): " UINTX_FORMAT, _total_count * RTMTotalCountIncrRate); + tty->print_cr("# rtm lock aborts : " UINTX_FORMAT, _abort_count); + for (int i = 0; i < ABORT_STATUS_LIMIT; i++) { + tty->print_cr("# rtm lock aborts %d: " UINTX_FORMAT, i, _abortX_count[i]); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/sharedRuntime_x86_32.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -979,7 +979,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. @@ -1626,7 +1628,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. @@ -1817,6 +1819,13 @@ // Frame is now completed as far as size and linkage. int frame_complete = ((intptr_t)__ pc()) - start; + if (UseRTMLocking) { + // Abort RTM transaction before calling JNI + // because critical section will be large and will be + // aborted anyway. Also nmethod could be deoptimized. + __ xabort(0); + } + // Calculate the difference between rsp and rbp,. We need to know it // after the native call because on windows Java Natives will pop // the arguments and it is painful to do rsp relative addressing @@ -2259,7 +2268,7 @@ if (!is_critical_native) { // reset handle block __ movptr(rcx, Address(thread, JavaThread::active_handles_offset())); - __ movptr(Address(rcx, JNIHandleBlock::top_offset_in_bytes()), NULL_WORD); + __ movl(Address(rcx, JNIHandleBlock::top_offset_in_bytes()), NULL_WORD); // Any exception pending? __ cmpptr(Address(thread, in_bytes(Thread::pending_exception_offset())), (int32_t)NULL_WORD); @@ -2497,7 +2506,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. @@ -3007,11 +3016,15 @@ // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset_in_bytes())); - // Stack bang to make sure there's enough room for these interpreter frames. +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. if (UseStackBanging) { __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); __ bang_stack_size(rbx, rcx); } +#endif // Load array of frame pcs into ECX __ movptr(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); @@ -3170,6 +3183,12 @@ }; address start = __ pc(); + + if (UseRTMLocking) { + // Abort RTM transaction before possible nmethod deoptimization. + __ xabort(0); + } + // Push self-frame. __ subptr(rsp, return_off*wordSize); // Epilog! @@ -3227,12 +3246,15 @@ // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset_in_bytes())); - // Stack bang to make sure there's enough room for these interpreter frames. +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. if (UseStackBanging) { __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); __ bang_stack_size(rbx, rcx); } - +#endif // Load array of frame pcs into ECX __ movl(rcx,Address(rdi,Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); @@ -3355,6 +3377,14 @@ address call_pc = NULL; bool cause_return = (poll_type == POLL_AT_RETURN); bool save_vectors = (poll_type == POLL_AT_VECTOR_LOOP); + + if (UseRTMLocking) { + // Abort RTM transaction before calling runtime + // because critical section will be large and will be + // aborted anyway. Also nmethod could be deoptimized. + __ xabort(0); + } + // If cause_return is true we are at a poll_return and there is // the return address on the stack to the caller on the nmethod // that is safepoint. We can leave this return on the stack and diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/sharedRuntime_x86_64.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -940,7 +940,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. @@ -1908,7 +1910,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 @@ -2061,6 +2063,13 @@ // Frame is now completed as far as size and linkage. int frame_complete = ((intptr_t)__ pc()) - start; + if (UseRTMLocking) { + // Abort RTM transaction before calling JNI + // because critical section will be large and will be + // aborted anyway. Also nmethod could be deoptimized. + __ xabort(0); + } + #ifdef ASSERT { Label L; @@ -2551,7 +2560,7 @@ if (!is_critical_native) { // reset handle block __ movptr(rcx, Address(r15_thread, JavaThread::active_handles_offset())); - __ movptr(Address(rcx, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); + __ movl(Address(rcx, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); } // pop our frame @@ -2812,7 +2821,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. @@ -3558,11 +3567,15 @@ // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset_in_bytes())); - // Stack bang to make sure there's enough room for these interpreter frames. +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. if (UseStackBanging) { __ movl(rbx, Address(rdi, Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); __ bang_stack_size(rbx, rcx); } +#endif // Load address of array of frame pcs into rcx __ movptr(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); @@ -3696,6 +3709,11 @@ address start = __ pc(); + if (UseRTMLocking) { + // Abort RTM transaction before possible nmethod deoptimization. + __ xabort(0); + } + // Push self-frame. We get here with a return address on the // stack, so rsp is 8-byte aligned until we allocate our frame. __ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog! @@ -3754,11 +3772,15 @@ // restore rbp before stack bang because if stack overflow is thrown it needs to be pushed (and preserved) __ movptr(rbp, Address(rdi, Deoptimization::UnrollBlock::initial_info_offset_in_bytes())); - // Stack bang to make sure there's enough room for these interpreter frames. +#ifdef ASSERT + // Compilers generate code that bang the stack by as much as the + // interpreter would need. So this stack banging should never + // trigger a fault. Verify that it does not on non product builds. if (UseStackBanging) { __ movl(rbx, Address(rdi ,Deoptimization::UnrollBlock::total_frame_sizes_offset_in_bytes())); __ bang_stack_size(rbx, rcx); } +#endif // Load address of array of frame pcs into rcx (address*) __ movptr(rcx, Address(rdi, Deoptimization::UnrollBlock::frame_pcs_offset_in_bytes())); @@ -3876,6 +3898,13 @@ bool cause_return = (poll_type == POLL_AT_RETURN); bool save_vectors = (poll_type == POLL_AT_VECTOR_LOOP); + if (UseRTMLocking) { + // Abort RTM transaction before calling runtime + // because critical section will be large and will be + // aborted anyway. Also nmethod could be deoptimized. + __ xabort(0); + } + // Make room for return address (or push it again) if (!cause_return) { __ push(rbx); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/stubGenerator_x86_32.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_32.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/stubGenerator_x86_32.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -2403,6 +2403,9 @@ // c_rarg3 - r vector byte array address // c_rarg4 - input length // + // Output: + // rax - input length + // address generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAES, "need AES instructions and misaligned SSE support"); __ align(CodeEntryAlignment); @@ -2483,7 +2486,7 @@ __ movdqu(Address(rvec, 0), xmm_result); // final value of r stored in rvec of CipherBlockChaining object handleSOERegisters(false /*restoring*/); - __ movl(rax, 0); // return 0 (why?) + __ movptr(rax, len_param); // return length __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); @@ -2557,6 +2560,9 @@ // c_rarg3 - r vector byte array address // c_rarg4 - input length // + // Output: + // rax - input length + // address generate_cipherBlockChaining_decryptAESCrypt() { assert(UseAES, "need AES instructions and misaligned SSE support"); @@ -2650,7 +2656,7 @@ __ movptr(rvec , rvec_param); // restore this since used in loop __ movdqu(Address(rvec, 0), xmm_temp); // final value of r stored in rvec of CipherBlockChaining object handleSOERegisters(false /*restoring*/); - __ movl(rax, 0); // return 0 (why?) + __ movptr(rax, len_param); // return length __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/stubGenerator_x86_64.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_64.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/stubGenerator_x86_64.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -3217,6 +3217,9 @@ // c_rarg3 - r vector byte array address // c_rarg4 - input length // + // Output: + // rax - input length + // address generate_cipherBlockChaining_encryptAESCrypt() { assert(UseAES, "need AES instructions and misaligned SSE support"); __ align(CodeEntryAlignment); @@ -3232,7 +3235,7 @@ #ifndef _WIN64 const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) #else - const Address len_mem(rsp, 6 * wordSize); // length is on stack on Win64 + const Address len_mem(rbp, 6 * wordSize); // length is on stack on Win64 const Register len_reg = r10; // pick the first volatile windows register #endif const Register pos = rax; @@ -3259,6 +3262,8 @@ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { __ movdqu(xmm_save(i), as_XMMRegister(i)); } +#else + __ push(len_reg); // Save #endif const XMMRegister xmm_key_shuf_mask = xmm_temp; // used temporarily to swap key bytes up front @@ -3301,8 +3306,10 @@ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { __ movdqu(as_XMMRegister(i), xmm_save(i)); } + __ movl(rax, len_mem); +#else + __ pop(rax); // return length #endif - __ movl(rax, 0); // return 0 (why?) __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); @@ -3409,6 +3416,9 @@ // c_rarg3 - r vector byte array address // c_rarg4 - input length // + // Output: + // rax - input length + // address generate_cipherBlockChaining_decryptAESCrypt_Parallel() { assert(UseAES, "need AES instructions and misaligned SSE support"); @@ -3427,7 +3437,7 @@ #ifndef _WIN64 const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) #else - const Address len_mem(rsp, 6 * wordSize); // length is on stack on Win64 + const Address len_mem(rbp, 6 * wordSize); // length is on stack on Win64 const Register len_reg = r10; // pick the first volatile windows register #endif const Register pos = rax; @@ -3448,7 +3458,10 @@ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { __ movdqu(xmm_save(i), as_XMMRegister(i)); } +#else + __ push(len_reg); // Save #endif + // the java expanded key ordering is rotated one position from what we want // so we start from 0x10 here and hit 0x00 last const XMMRegister xmm_key_shuf_mask = xmm1; // used temporarily to swap key bytes up front @@ -3554,8 +3567,10 @@ for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { __ movdqu(as_XMMRegister(i), xmm_save(i)); } + __ movl(rax, len_mem); +#else + __ pop(rax); // return length #endif - __ movl(rax, 0); // return 0 (why?) __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(0); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/templateInterpreter_x86.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/x86/vm/templateInterpreter_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,124 @@ +/* + * 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 + * 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 "ci/ciMethod.hpp" +#include "interpreter/interpreter.hpp" +#include "runtime/frame.inline.hpp" + +#ifndef CC_INTERP + +// asm based interpreter deoptimization helpers +int AbstractInterpreter::size_activation(int max_stack, + int temps, + int extra_args, + int monitors, + int callee_params, + int callee_locals, + bool is_top_frame) { + // Note: This calculation must exactly parallel the frame setup + // in AbstractInterpreterGenerator::generate_method_entry. + + // fixed size of an interpreter frame: + int overhead = frame::sender_sp_offset - + frame::interpreter_frame_initial_sp_offset; + // Our locals were accounted for by the caller (or last_frame_adjust + // on the transistion) Since the callee parameters already account + // for the callee's params we only need to account for the extra + // locals. + int size = overhead + + (callee_locals - callee_params)*Interpreter::stackElementWords + + monitors * frame::interpreter_frame_monitor_size() + + temps* Interpreter::stackElementWords + extra_args; + + return size; +} + +void AbstractInterpreter::layout_activation(Method* 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) { + // The frame interpreter_frame is guaranteed to be the right size, + // as determined by a previous call to the size_activation() method. + // It is also guaranteed to be walkable even though it is in a + // skeletal state + + int max_locals = method->max_locals() * Interpreter::stackElementWords; + int extra_locals = (method->max_locals() - method->size_of_parameters()) * + Interpreter::stackElementWords; + +#ifdef ASSERT + if (!EnableInvokeDynamic) { + // @@@ FIXME: Should we correct interpreter_frame_sender_sp in the calling sequences? + // Probably, since deoptimization doesn't work yet. + assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); + } + assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); +#endif + + interpreter_frame->interpreter_frame_set_method(method); + // NOTE the difference in using sender_sp and + // interpreter_frame_sender_sp interpreter_frame_sender_sp is + // the original sp of the caller (the unextended_sp) and + // sender_sp is fp+8/16 (32bit/64bit) XXX + intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; + +#ifdef ASSERT + if (caller->is_interpreted_frame()) { + assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement"); + } +#endif + + interpreter_frame->interpreter_frame_set_locals(locals); + BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); + BasicObjectLock* monbot = montop - moncount; + interpreter_frame->interpreter_frame_set_monitor_end(monbot); + + // Set last_sp + intptr_t* esp = (intptr_t*) monbot - + tempcount*Interpreter::stackElementWords - + popframe_extra_args; + interpreter_frame->interpreter_frame_set_last_sp(esp); + + // All frames but the initial (oldest) interpreter frame we fill in have + // a value for sender_sp that allows walking the stack but isn't + // truly correct. Correct the value here. + if (extra_locals != 0 && + interpreter_frame->sender_sp() == + interpreter_frame->interpreter_frame_sender_sp()) { + interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + + extra_locals); + } + *interpreter_frame->interpreter_frame_cache_addr() = + method->constants()->cache(); +} + +#endif // CC_INTERP diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/templateInterpreter_x86_32.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1287,7 +1287,7 @@ // reset handle block __ movptr(t, Address(thread, JavaThread::active_handles_offset())); - __ movptr(Address(t, JNIHandleBlock::top_offset_in_bytes()), NULL_WORD); + __ movl(Address(t, JNIHandleBlock::top_offset_in_bytes()), NULL_WORD); // If result was an oop then unbox and save it in the frame { Label L; @@ -1686,91 +1686,6 @@ return overhead_size + method_stack + stub_code; } -// asm based interpreter deoptimization helpers - -int AbstractInterpreter::layout_activation(Method* 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) { - // Note: This calculation must exactly parallel the frame setup - // in AbstractInterpreterGenerator::generate_method_entry. - // If 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 - // NOTE: return size is in words not bytes - - // fixed size of an interpreter frame: - int max_locals = method->max_locals() * Interpreter::stackElementWords; - int extra_locals = (method->max_locals() - method->size_of_parameters()) * - Interpreter::stackElementWords; - - int overhead = frame::sender_sp_offset - frame::interpreter_frame_initial_sp_offset; - - // Our locals were accounted for by the caller (or last_frame_adjust on the transistion) - // Since the callee parameters already account for the callee's params we only need to account for - // the extra locals. - - - int size = overhead + - ((callee_locals - callee_param_count)*Interpreter::stackElementWords) + - (moncount*frame::interpreter_frame_monitor_size()) + - tempcount*Interpreter::stackElementWords + popframe_extra_args; - - if (interpreter_frame != NULL) { -#ifdef ASSERT - if (!EnableInvokeDynamic) - // @@@ FIXME: Should we correct interpreter_frame_sender_sp in the calling sequences? - // Probably, since deoptimization doesn't work yet. - assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); - assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); -#endif - - interpreter_frame->interpreter_frame_set_method(method); - // NOTE the difference in using sender_sp and interpreter_frame_sender_sp - // interpreter_frame_sender_sp is the original sp of the caller (the unextended_sp) - // and sender_sp is fp+8 - intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; - -#ifdef ASSERT - if (caller->is_interpreted_frame()) { - assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement"); - } -#endif - - interpreter_frame->interpreter_frame_set_locals(locals); - BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); - BasicObjectLock* monbot = montop - moncount; - interpreter_frame->interpreter_frame_set_monitor_end(monbot); - - // Set last_sp - intptr_t* rsp = (intptr_t*) monbot - - tempcount*Interpreter::stackElementWords - - popframe_extra_args; - interpreter_frame->interpreter_frame_set_last_sp(rsp); - - // All frames but the initial (oldest) interpreter frame we fill in have a - // value for sender_sp that allows walking the stack but isn't - // truly correct. Correct the value here. - - if (extra_locals != 0 && - interpreter_frame->sender_sp() == interpreter_frame->interpreter_frame_sender_sp() ) { - interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + extra_locals); - } - *interpreter_frame->interpreter_frame_cache_addr() = - method->constants()->cache(); - } - return size; -} - - //------------------------------------------------------------------------------------------------------------------------ // Exceptions diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/templateInterpreter_x86_64.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1274,7 +1274,7 @@ // reset handle block __ movptr(t, Address(r15_thread, JavaThread::active_handles_offset())); - __ movptr(Address(t, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); + __ movl(Address(t, JNIHandleBlock::top_offset_in_bytes()), (int32_t)NULL_WORD); // If result is an oop unbox and store it in frame where gc will see it // and result handler will pick it up @@ -1710,87 +1710,6 @@ return (overhead_size + method_stack + stub_code); } -int AbstractInterpreter::layout_activation(Method* 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) { - // Note: This calculation must exactly parallel the frame setup - // in AbstractInterpreterGenerator::generate_method_entry. - // If 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 - - // fixed size of an interpreter frame: - int max_locals = method->max_locals() * Interpreter::stackElementWords; - int extra_locals = (method->max_locals() - method->size_of_parameters()) * - Interpreter::stackElementWords; - - int overhead = frame::sender_sp_offset - - frame::interpreter_frame_initial_sp_offset; - // Our locals were accounted for by the caller (or last_frame_adjust - // on the transistion) Since the callee parameters already account - // for the callee's params we only need to account for the extra - // locals. - int size = overhead + - (callee_locals - callee_param_count)*Interpreter::stackElementWords + - moncount * frame::interpreter_frame_monitor_size() + - tempcount* Interpreter::stackElementWords + popframe_extra_args; - if (interpreter_frame != NULL) { -#ifdef ASSERT - if (!EnableInvokeDynamic) - // @@@ FIXME: Should we correct interpreter_frame_sender_sp in the calling sequences? - // Probably, since deoptimization doesn't work yet. - assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); - assert(caller->sp() == interpreter_frame->sender_sp(), "Frame not properly walkable(2)"); -#endif - - interpreter_frame->interpreter_frame_set_method(method); - // NOTE the difference in using sender_sp and - // interpreter_frame_sender_sp interpreter_frame_sender_sp is - // the original sp of the caller (the unextended_sp) and - // sender_sp is fp+16 XXX - intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1; - -#ifdef ASSERT - if (caller->is_interpreted_frame()) { - assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement"); - } -#endif - - interpreter_frame->interpreter_frame_set_locals(locals); - BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin(); - BasicObjectLock* monbot = montop - moncount; - interpreter_frame->interpreter_frame_set_monitor_end(monbot); - - // Set last_sp - intptr_t* esp = (intptr_t*) monbot - - tempcount*Interpreter::stackElementWords - - popframe_extra_args; - interpreter_frame->interpreter_frame_set_last_sp(esp); - - // All frames but the initial (oldest) interpreter frame we fill in have - // a value for sender_sp that allows walking the stack but isn't - // truly correct. Correct the value here. - if (extra_locals != 0 && - interpreter_frame->sender_sp() == - interpreter_frame->interpreter_frame_sender_sp()) { - interpreter_frame->set_interpreter_frame_sender_sp(caller->sp() + - extra_locals); - } - *interpreter_frame->interpreter_frame_cache_addr() = - method->constants()->cache(); - } - return size; -} - //----------------------------------------------------------------------------- // Exceptions diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/vm_version_x86.cpp --- a/src/cpu/x86/vm/vm_version_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/vm_version_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -50,13 +50,18 @@ const char* VM_Version::_features_str = ""; VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, }; +// Address of instruction which causes SEGV +address VM_Version::_cpuinfo_segv_addr = 0; +// Address of instruction after the one which causes SEGV +address VM_Version::_cpuinfo_cont_addr = 0; + static BufferBlob* stub_blob; -static const int stub_size = 550; +static const int stub_size = 600; extern "C" { - typedef void (*getPsrInfo_stub_t)(void*); + typedef void (*get_cpu_info_stub_t)(void*); } -static getPsrInfo_stub_t getPsrInfo_stub = NULL; +static get_cpu_info_stub_t get_cpu_info_stub = NULL; class VM_Version_StubGenerator: public StubCodeGenerator { @@ -64,7 +69,7 @@ VM_Version_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {} - address generate_getPsrInfo() { + address generate_get_cpu_info() { // Flags to test CPU type. const uint32_t HS_EFL_AC = 0x40000; const uint32_t HS_EFL_ID = 0x200000; @@ -76,13 +81,13 @@ Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4; Label sef_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7, done; - StubCodeMark mark(this, "VM_Version", "getPsrInfo_stub"); + StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub"); # define __ _masm-> address start = __ pc(); // - // void getPsrInfo(VM_Version::CpuidInfo* cpuid_info); + // void get_cpu_info(VM_Version::CpuidInfo* cpuid_info); // // LP64: rcx and rdx are first and second argument registers on windows @@ -234,9 +239,9 @@ // Check if OS has enabled XGETBV instruction to access XCR0 // (OSXSAVE feature flag) and CPU supports AVX // - __ andl(rcx, 0x18000000); + __ andl(rcx, 0x18000000); // cpuid1 bits osxsave | avx __ cmpl(rcx, 0x18000000); - __ jccb(Assembler::notEqual, sef_cpuid); + __ jccb(Assembler::notEqual, sef_cpuid); // jump if AVX is not supported // // XCR0, XFEATURE_ENABLED_MASK register @@ -247,6 +252,53 @@ __ movl(Address(rsi, 0), rax); __ movl(Address(rsi, 4), rdx); + __ andl(rax, 0x6); // xcr0 bits sse | ymm + __ cmpl(rax, 0x6); + __ jccb(Assembler::notEqual, sef_cpuid); // jump if AVX is not supported + + // + // Some OSs have a bug when upper 128bits of YMM + // registers are not restored after a signal processing. + // Generate SEGV here (reference through NULL) + // and check upper YMM bits after it. + // + VM_Version::set_avx_cpuFeatures(); // Enable temporary to pass asserts + intx saved_useavx = UseAVX; + intx saved_usesse = UseSSE; + UseAVX = 1; + UseSSE = 2; + + // load value into all 32 bytes of ymm7 register + __ movl(rcx, VM_Version::ymm_test_value()); + + __ movdl(xmm0, rcx); + __ pshufd(xmm0, xmm0, 0x00); + __ vinsertf128h(xmm0, xmm0, xmm0); + __ vmovdqu(xmm7, xmm0); +#ifdef _LP64 + __ vmovdqu(xmm8, xmm0); + __ vmovdqu(xmm15, xmm0); +#endif + + __ xorl(rsi, rsi); + VM_Version::set_cpuinfo_segv_addr( __ pc() ); + // Generate SEGV + __ movl(rax, Address(rsi, 0)); + + VM_Version::set_cpuinfo_cont_addr( __ pc() ); + // Returns here after signal. Save xmm0 to check it later. + __ lea(rsi, Address(rbp, in_bytes(VM_Version::ymm_save_offset()))); + __ vmovdqu(Address(rsi, 0), xmm0); + __ vmovdqu(Address(rsi, 32), xmm7); +#ifdef _LP64 + __ vmovdqu(Address(rsi, 64), xmm8); + __ vmovdqu(Address(rsi, 96), xmm15); +#endif + + VM_Version::clean_cpuFeatures(); + UseAVX = saved_useavx; + UseSSE = saved_usesse; + // // cpuid(0x7) Structured Extended Features // @@ -339,6 +391,14 @@ }; +void VM_Version::get_cpu_info_wrapper() { + get_cpu_info_stub(&_cpuid_info); +} + +#ifndef CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED + #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) f() +#endif + void VM_Version::get_processor_features() { _cpu = 4; // 486 by default @@ -349,7 +409,11 @@ if (!Use486InstrsOnly) { // Get raw processor info - getPsrInfo_stub(&_cpuid_info); + + // Some platforms (like Win*) need a wrapper around here + // in order to properly handle SEGV for YMM registers test. + CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(get_cpu_info_wrapper); + assert_is_initialized(); _cpu = extended_cpu_family(); _model = extended_cpu_model(); @@ -429,7 +493,7 @@ } char buf[256]; - jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", cores_per_cpu(), threads_per_core(), cpu_family(), _model, _stepping, (supports_cmov() ? ", cmov" : ""), @@ -446,8 +510,9 @@ (supports_avx() ? ", avx" : ""), (supports_avx2() ? ", avx2" : ""), (supports_aes() ? ", aes" : ""), - (supports_clmul() ? ", clmul" : ""), + (supports_clmul() ? ", clmul" : ""), (supports_erms() ? ", erms" : ""), + (supports_rtm() ? ", rtm" : ""), (supports_mmx_ext() ? ", mmxext" : ""), (supports_3dnow_prefetch() ? ", 3dnowpref" : ""), (supports_lzcnt() ? ", lzcnt": ""), @@ -455,7 +520,9 @@ (supports_ht() ? ", ht": ""), (supports_tsc() ? ", tsc": ""), (supports_tscinv_bit() ? ", tscinvbit": ""), - (supports_tscinv() ? ", tscinv": "")); + (supports_tscinv() ? ", tscinv": ""), + (supports_bmi1() ? ", bmi1" : ""), + (supports_bmi2() ? ", bmi2" : "")); _features_str = strdup(buf); // UseSSE is set to the smaller of what hardware supports and what @@ -486,7 +553,7 @@ } } else if (UseAES) { if (!FLAG_IS_DEFAULT(UseAES)) - warning("AES instructions not available on this CPU"); + warning("AES instructions are not available on this CPU"); FLAG_SET_DEFAULT(UseAES, false); } @@ -519,10 +586,57 @@ } } else if (UseAESIntrinsics) { if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) - warning("AES intrinsics not available on this CPU"); + warning("AES intrinsics are not available on this CPU"); FLAG_SET_DEFAULT(UseAESIntrinsics, false); } + // Adjust RTM (Restricted Transactional Memory) flags + if (!supports_rtm() && UseRTMLocking) { + // Can't continue because UseRTMLocking affects UseBiasedLocking flag + // setting during arguments processing. See use_biased_locking(). + // VM_Version_init() is executed after UseBiasedLocking is used + // in Thread::allocate(). + vm_exit_during_initialization("RTM instructions are not available on this CPU"); + } + +#if INCLUDE_RTM_OPT + if (UseRTMLocking) { + if (!FLAG_IS_CMDLINE(UseRTMLocking)) { + // RTM locking should be used only for applications with + // high lock contention. For now we do not use it by default. + vm_exit_during_initialization("UseRTMLocking flag should be only set on command line"); + } + if (!is_power_of_2(RTMTotalCountIncrRate)) { + warning("RTMTotalCountIncrRate must be a power of 2, resetting it to 64"); + FLAG_SET_DEFAULT(RTMTotalCountIncrRate, 64); + } + if (RTMAbortRatio < 0 || RTMAbortRatio > 100) { + warning("RTMAbortRatio must be in the range 0 to 100, resetting it to 50"); + FLAG_SET_DEFAULT(RTMAbortRatio, 50); + } + } else { // !UseRTMLocking + if (UseRTMForStackLocks) { + if (!FLAG_IS_DEFAULT(UseRTMForStackLocks)) { + warning("UseRTMForStackLocks flag should be off when UseRTMLocking flag is off"); + } + FLAG_SET_DEFAULT(UseRTMForStackLocks, false); + } + if (UseRTMDeopt) { + FLAG_SET_DEFAULT(UseRTMDeopt, false); + } + if (PrintPreciseRTMLockingStatistics) { + FLAG_SET_DEFAULT(PrintPreciseRTMLockingStatistics, false); + } + } +#else + if (UseRTMLocking) { + // Only C2 does RTM locking optimization. + // Can't continue because UseRTMLocking affects UseBiasedLocking flag + // setting during arguments processing. See use_biased_locking(). + vm_exit_during_initialization("RTM locking optimization is not supported in this VM"); + } +#endif + #ifdef COMPILER2 if (UseFPUForSpilling) { if (UseSSE < 2) { @@ -538,14 +652,28 @@ if (MaxVectorSize > 32) { FLAG_SET_DEFAULT(MaxVectorSize, 32); } - if (MaxVectorSize > 16 && UseAVX == 0) { - // Only supported with AVX+ + if (MaxVectorSize > 16 && (UseAVX == 0 || !os_supports_avx_vectors())) { + // 32 bytes vectors (in YMM) are only supported with AVX+ FLAG_SET_DEFAULT(MaxVectorSize, 16); } if (UseSSE < 2) { - // Only supported with SSE2+ + // Vectors (in XMM) are only supported with SSE2+ FLAG_SET_DEFAULT(MaxVectorSize, 0); } +#ifdef ASSERT + if (supports_avx() && PrintMiscellaneous && Verbose && TraceNewVectors) { + tty->print_cr("State of YMM registers after signal handle:"); + int nreg = 2 LP64_ONLY(+2); + const char* ymm_name[4] = {"0", "7", "8", "15"}; + for (int i = 0; i < nreg; i++) { + tty->print("YMM%s:", ymm_name[i]); + for (int j = 7; j >=0; j--) { + tty->print(" %x", _cpuid_info.ymm_save[i*8 + j]); + } + tty->cr(); + } + } +#endif } #endif @@ -600,13 +728,6 @@ } } - // Use count leading zeros count instruction if available. - if (supports_lzcnt()) { - if (FLAG_IS_DEFAULT(UseCountLeadingZerosInstruction)) { - UseCountLeadingZerosInstruction = true; - } - } - // some defaults for AMD family 15h if ( cpu_family() == 0x15 ) { // On family 15h processors default is no sw prefetch @@ -683,14 +804,35 @@ } } } -#if defined(COMPILER2) && defined(_ALLBSD_SOURCE) - if (MaxVectorSize > 16) { - // Limit vectors size to 16 bytes on BSD until it fixes - // restoring upper 128bit of YMM registers on return - // from signal handler. - FLAG_SET_DEFAULT(MaxVectorSize, 16); + + // Use count leading zeros count instruction if available. + if (supports_lzcnt()) { + if (FLAG_IS_DEFAULT(UseCountLeadingZerosInstruction)) { + UseCountLeadingZerosInstruction = true; + } + } else if (UseCountLeadingZerosInstruction) { + warning("lzcnt instruction is not available on this CPU"); + FLAG_SET_DEFAULT(UseCountLeadingZerosInstruction, false); + } + + if (supports_bmi1()) { + if (FLAG_IS_DEFAULT(UseBMI1Instructions)) { + UseBMI1Instructions = true; } -#endif // COMPILER2 + } else if (UseBMI1Instructions) { + warning("BMI1 instructions are not available on this CPU"); + FLAG_SET_DEFAULT(UseBMI1Instructions, false); + } + + // Use count trailing zeros instruction if available + if (supports_bmi1()) { + if (FLAG_IS_DEFAULT(UseCountTrailingZerosInstruction)) { + UseCountTrailingZerosInstruction = UseBMI1Instructions; + } + } else if (UseCountTrailingZerosInstruction) { + warning("tzcnt instruction is not available on this CPU"); + FLAG_SET_DEFAULT(UseCountTrailingZerosInstruction, false); + } // Use population count instruction if available. if (supports_popcnt()) { @@ -783,13 +925,18 @@ if (PrintMiscellaneous && Verbose) { tty->print_cr("Logical CPUs per core: %u", logical_processors_per_package()); - tty->print("UseSSE=%d",UseSSE); + tty->print("UseSSE=%d", (int) UseSSE); if (UseAVX > 0) { - tty->print(" UseAVX=%d",UseAVX); + tty->print(" UseAVX=%d", (int) UseAVX); } if (UseAES) { tty->print(" UseAES=1"); } +#ifdef COMPILER2 + if (MaxVectorSize > 0) { + tty->print(" MaxVectorSize=%d", (int) MaxVectorSize); + } +#endif tty->cr(); tty->print("Allocation"); if (AllocatePrefetchStyle <= 0 || UseSSE == 0 && !supports_3dnow_prefetch()) { @@ -810,40 +957,61 @@ } } if (AllocatePrefetchLines > 1) { - tty->print_cr(" at distance %d, %d lines of %d bytes", AllocatePrefetchDistance, AllocatePrefetchLines, AllocatePrefetchStepSize); + tty->print_cr(" at distance %d, %d lines of %d bytes", (int) AllocatePrefetchDistance, (int) AllocatePrefetchLines, (int) AllocatePrefetchStepSize); } else { - tty->print_cr(" at distance %d, one line of %d bytes", AllocatePrefetchDistance, AllocatePrefetchStepSize); + tty->print_cr(" at distance %d, one line of %d bytes", (int) AllocatePrefetchDistance, (int) AllocatePrefetchStepSize); } } if (PrefetchCopyIntervalInBytes > 0) { - tty->print_cr("PrefetchCopyIntervalInBytes %d", PrefetchCopyIntervalInBytes); + tty->print_cr("PrefetchCopyIntervalInBytes %d", (int) PrefetchCopyIntervalInBytes); } if (PrefetchScanIntervalInBytes > 0) { - tty->print_cr("PrefetchScanIntervalInBytes %d", PrefetchScanIntervalInBytes); + tty->print_cr("PrefetchScanIntervalInBytes %d", (int) PrefetchScanIntervalInBytes); } if (PrefetchFieldsAhead > 0) { - tty->print_cr("PrefetchFieldsAhead %d", PrefetchFieldsAhead); + tty->print_cr("PrefetchFieldsAhead %d", (int) PrefetchFieldsAhead); } if (ContendedPaddingWidth > 0) { - tty->print_cr("ContendedPaddingWidth %d", ContendedPaddingWidth); + tty->print_cr("ContendedPaddingWidth %d", (int) ContendedPaddingWidth); } } #endif // !PRODUCT } +bool VM_Version::use_biased_locking() { +#if INCLUDE_RTM_OPT + // RTM locking is most useful when there is high lock contention and + // low data contention. With high lock contention the lock is usually + // inflated and biased locking is not suitable for that case. + // RTM locking code requires that biased locking is off. + // Note: we can't switch off UseBiasedLocking in get_processor_features() + // because it is used by Thread::allocate() which is called before + // VM_Version::initialize(). + if (UseRTMLocking && UseBiasedLocking) { + if (FLAG_IS_DEFAULT(UseBiasedLocking)) { + FLAG_SET_DEFAULT(UseBiasedLocking, false); + } else { + warning("Biased locking is not supported with RTM locking; ignoring UseBiasedLocking flag." ); + UseBiasedLocking = false; + } + } +#endif + return UseBiasedLocking; +} + void VM_Version::initialize() { ResourceMark rm; // Making this stub must be FIRST use of assembler - stub_blob = BufferBlob::create("getPsrInfo_stub", stub_size); + stub_blob = BufferBlob::create("get_cpu_info_stub", stub_size); if (stub_blob == NULL) { - vm_exit_during_initialization("Unable to allocate getPsrInfo_stub"); + vm_exit_during_initialization("Unable to allocate get_cpu_info_stub"); } CodeBuffer c(stub_blob); VM_Version_StubGenerator g(&c); - getPsrInfo_stub = CAST_TO_FN_PTR(getPsrInfo_stub_t, - g.generate_getPsrInfo()); + get_cpu_info_stub = CAST_TO_FN_PTR(get_cpu_info_stub_t, + g.generate_get_cpu_info()); get_processor_features(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/vm_version_x86.hpp --- a/src/cpu/x86/vm/vm_version_x86.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/vm_version_x86.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -142,7 +142,8 @@ struct { uint32_t LahfSahf : 1, CmpLegacy : 1, - : 4, + : 3, + lzcnt_intel : 1, lzcnt : 1, sse4a : 1, misalignsse : 1, @@ -207,7 +208,9 @@ : 2, bmi2 : 1, erms : 1, - : 22; + : 1, + rtm : 1, + : 20; } bits; }; @@ -229,6 +232,9 @@ // 0 if this instruction is not available static const char* _features_str; + static address _cpuinfo_segv_addr; // address of instruction which causes SEGV + static address _cpuinfo_cont_addr; // address of instruction after the one which causes SEGV + enum { CPU_CX8 = (1 << 0), // next bits are from cpuid 1 (EDX) CPU_CMOV = (1 << 1), @@ -252,7 +258,10 @@ CPU_AVX2 = (1 << 18), CPU_AES = (1 << 19), CPU_ERMS = (1 << 20), // enhanced 'rep movsb/stosb' instructions - CPU_CLMUL = (1 << 21) // carryless multiply for CRC + CPU_CLMUL = (1 << 21), // carryless multiply for CRC + CPU_BMI1 = (1 << 22), + CPU_BMI2 = (1 << 23), + CPU_RTM = (1 << 24) // Restricted Transactional Memory instructions } cpuFeatureFlags; enum { @@ -359,6 +368,9 @@ // extended control register XCR0 (the XFEATURE_ENABLED_MASK register) XemXcr0Eax xem_xcr0_eax; uint32_t xem_xcr0_edx; // reserved + + // Space to save ymm registers after signal handle + int ymm_save[8*4]; // Save ymm0, ymm7, ymm8, ymm15 }; // The actual cpuid info block @@ -424,6 +436,8 @@ if (_cpuid_info.sef_cpuid7_ebx.bits.avx2 != 0) result |= CPU_AVX2; } + if(_cpuid_info.sef_cpuid7_ebx.bits.bmi1 != 0) + result |= CPU_BMI1; if (_cpuid_info.std_cpuid1_edx.bits.tsc != 0) result |= CPU_TSC; if (_cpuid_info.ext_cpuid7_edx.bits.tsc_invariance != 0) @@ -434,6 +448,8 @@ result |= CPU_ERMS; if (_cpuid_info.std_cpuid1_ecx.bits.clmul != 0) result |= CPU_CLMUL; + if (_cpuid_info.sef_cpuid7_ebx.bits.rtm != 0) + result |= CPU_RTM; // AMD features. if (is_amd()) { @@ -445,10 +461,32 @@ if (_cpuid_info.ext_cpuid1_ecx.bits.sse4a != 0) result |= CPU_SSE4A; } + // Intel features. + if(is_intel()) { + if(_cpuid_info.sef_cpuid7_ebx.bits.bmi2 != 0) + result |= CPU_BMI2; + if(_cpuid_info.ext_cpuid1_ecx.bits.lzcnt_intel != 0) + result |= CPU_LZCNT; + } return result; } + static bool os_supports_avx_vectors() { + if (!supports_avx()) { + return false; + } + // Verify that OS save/restore all bits of AVX registers + // during signal processing. + int nreg = 2 LP64_ONLY(+2); + for (int i = 0; i < 8 * nreg; i++) { // 32 bytes per ymm register + if (_cpuid_info.ymm_save[i] != ymm_test_value()) { + return false; + } + } + return true; + } + static void get_processor_features(); public: @@ -465,10 +503,27 @@ static ByteSize tpl_cpuidB1_offset() { return byte_offset_of(CpuidInfo, tpl_cpuidB1_eax); } static ByteSize tpl_cpuidB2_offset() { return byte_offset_of(CpuidInfo, tpl_cpuidB2_eax); } static ByteSize xem_xcr0_offset() { return byte_offset_of(CpuidInfo, xem_xcr0_eax); } + static ByteSize ymm_save_offset() { return byte_offset_of(CpuidInfo, ymm_save); } + + // The value used to check ymm register after signal handle + static int ymm_test_value() { return 0xCAFEBABE; } + + static void get_cpu_info_wrapper(); + static void set_cpuinfo_segv_addr(address pc) { _cpuinfo_segv_addr = pc; } + static bool is_cpuinfo_segv_addr(address pc) { return _cpuinfo_segv_addr == pc; } + static void set_cpuinfo_cont_addr(address pc) { _cpuinfo_cont_addr = pc; } + static address cpuinfo_cont_addr() { return _cpuinfo_cont_addr; } + + static void clean_cpuFeatures() { _cpuFeatures = 0; } + static void set_avx_cpuFeatures() { _cpuFeatures = (CPU_SSE | CPU_SSE2 | CPU_AVX); } + // Initialization static void initialize(); + // Override Abstract_VM_Version implementation + static bool use_biased_locking(); + // Asserts static void assert_is_initialized() { assert(_cpuid_info.std_cpuid1_eax.bits.family != 0, "VM_Version not initialized"); @@ -561,7 +616,9 @@ static bool supports_aes() { return (_cpuFeatures & CPU_AES) != 0; } static bool supports_erms() { return (_cpuFeatures & CPU_ERMS) != 0; } static bool supports_clmul() { return (_cpuFeatures & CPU_CLMUL) != 0; } - + static bool supports_rtm() { return (_cpuFeatures & CPU_RTM) != 0; } + static bool supports_bmi1() { return (_cpuFeatures & CPU_BMI1) != 0; } + static bool supports_bmi2() { return (_cpuFeatures & CPU_BMI2) != 0; } // Intel features static bool is_intel_family_core() { return is_intel() && extended_cpu_family() == CPU_FAMILY_INTEL_CORE; } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/vtableStubs_x86_32.cpp --- a/src/cpu/x86/vm/vtableStubs_x86_32.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/vtableStubs_x86_32.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -118,7 +118,7 @@ if (PrintMiscellaneous && (WizardMode || Verbose)) { tty->print_cr("vtable #%d at "PTR_FORMAT"[%d] left over: %d", - vtable_index, s->entry_point(), + vtable_index, p2i(s->entry_point()), (int)(s->code_end() - s->entry_point()), (int)(s->code_end() - __ pc())); } @@ -199,7 +199,7 @@ if (PrintMiscellaneous && (WizardMode || Verbose)) { tty->print_cr("itable #%d at "PTR_FORMAT"[%d] left over: %d", - itable_index, s->entry_point(), + itable_index, p2i(s->entry_point()), (int)(s->code_end() - s->entry_point()), (int)(s->code_end() - __ pc())); } diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/vtableStubs_x86_64.cpp --- a/src/cpu/x86/vm/vtableStubs_x86_64.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/vtableStubs_x86_64.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -35,6 +35,8 @@ #include "opto/runtime.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // machine-dependent part of VtableStubs: create VtableStub of correct size and // initialize its code diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/x86.ad --- a/src/cpu/x86/vm/x86.ad Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/x86.ad Wed Oct 15 16:02:50 2014 +0200 @@ -474,7 +474,125 @@ %} + +//----------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. + +class CallStubImpl { + + //-------------------------------------------------------------- + //---< Used for optimization in Compile::shorten_branches >--- + //-------------------------------------------------------------- + + public: + // Size of call trampoline stub. + static uint size_call_trampoline() { + return 0; // no call trampolines on this platform + } + + // number of relocations needed by a call trampoline stub + static uint reloc_call_trampoline() { + return 0; // no call trampolines on this platform + } +}; + +class HandlerImpl { + + public: + + static int emit_exception_handler(CodeBuffer &cbuf); + static int emit_deopt_handler(CodeBuffer& cbuf); + + static uint size_exception_handler() { + // NativeCall instruction size is the same as NativeJump. + // exception handler starts out as jump and can be patched to + // a call be deoptimization. (4932387) + // Note that this value is also credited (in output.cpp) to + // the size of the code section. + return NativeJump::instruction_size; + } + +#ifdef _LP64 + static uint size_deopt_handler() { + // three 5 byte instructions + return 15; + } +#else + static uint size_deopt_handler() { + // NativeCall instruction size is the same as NativeJump. + // exception handler starts out as jump and can be patched to + // a call be deoptimization. (4932387) + // Note that this value is also credited (in output.cpp) to + // the size of the code section. + return 5 + NativeJump::instruction_size; // pushl(); jmp; + } +#endif +}; + +%} // end source_hpp + source %{ + +// Emit exception handler code. +// Stuff framesize into a register and call a VM stub routine. +int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf) { + + // Note that the code buffer's insts_mark is always relative to insts. + // That's why we must use the macroassembler to generate a handler. + MacroAssembler _masm(&cbuf); + address base = __ start_a_stub(size_exception_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + int offset = __ offset(); + __ jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); + assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); + __ end_a_stub(); + return offset; +} + +// Emit deopt handler code. +int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) { + + // Note that the code buffer's insts_mark is always relative to insts. + // That's why we must use the macroassembler to generate a handler. + MacroAssembler _masm(&cbuf); + address base = __ start_a_stub(size_deopt_handler()); + if (base == NULL) return 0; // CodeBuffer::expand failed + int offset = __ offset(); + +#ifdef _LP64 + address the_pc = (address) __ pc(); + Label next; + // push a "the_pc" on the stack without destroying any registers + // as they all may be live. + + // push address of "next" + __ call(next, relocInfo::none); // reloc none is fine since it is a disp32 + __ bind(next); + // adjust it so it matches "the_pc" + __ subptr(Address(rsp, 0), __ offset() - offset); +#else + InternalAddress here(__ pc()); + __ pushptr(here.addr()); +#endif + + __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); + assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); + __ end_a_stub(); + return offset; +} + + +//============================================================================= + // Float masks come from different places depending on platform. #ifdef _LP64 static address float_signmask() { return StubRoutines::x86::float_sign_mask(); } @@ -581,6 +699,12 @@ return !AlignVector; // can be changed by flag } +// x86 AES instructions are compatible with SunJCE expanded +// keys, hence we do not need to pass the original key to stubs +const bool Matcher::pass_original_key_for_aes() { + return false; +} + // Helper methods for MachSpillCopyNode::implementation(). static int vec_mov_helper(CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo, int src_hi, int dst_hi, uint ireg, outputStream* st) { diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/x86_32.ad Wed Oct 15 16:02:50 2014 +0200 @@ -487,6 +487,11 @@ return 0; // absolute addressing, no offset } +bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } +void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { // Empty encoding } @@ -507,14 +512,15 @@ void MachPrologNode::format(PhaseRegAlloc* ra_, outputStream* st) const { Compile* C = ra_->C; - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); + int bangsize = C->bang_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove wordSize for return addr which is already pushed. framesize -= wordSize; - if (C->need_stack_bang(framesize)) { + if (C->need_stack_bang(bangsize)) { framesize -= wordSize; - st->print("# stack bang"); + st->print("# stack bang (%d bytes)", bangsize); st->print("\n\t"); st->print("PUSH EBP\t# Save EBP"); if (framesize) { @@ -558,9 +564,10 @@ Compile* C = ra_->C; MacroAssembler _masm(&cbuf); - int framesize = C->frame_slots() << LogBytesPerInt; - - __ verified_entry(framesize, C->need_stack_bang(framesize), C->in_24_bit_fp_mode()); + int framesize = C->frame_size_in_bytes(); + int bangsize = C->bang_size_in_bytes(); + + __ verified_entry(framesize, C->need_stack_bang(bangsize)?bangsize:0, C->in_24_bit_fp_mode()); C->set_frame_complete(cbuf.insts_size()); @@ -584,7 +591,7 @@ #ifndef PRODUCT void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream* st ) const { Compile *C = ra_->C; - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove two words for return addr and rbp, framesize -= 2*wordSize; @@ -624,7 +631,7 @@ masm.fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); } - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove two words for return addr and rbp, framesize -= 2*wordSize; @@ -658,7 +665,7 @@ if (C->max_vector_size() > 16) size += 3; // vzeroupper if (do_polling() && C->is_method_compilation()) size += 6; - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove two words for return addr and rbp, framesize -= 2*wordSize; @@ -1292,59 +1299,6 @@ //============================================================================= -uint size_exception_handler() { - // NativeCall instruction size is the same as NativeJump. - // exception handler starts out as jump and can be patched to - // a call be deoptimization. (4932387) - // Note that this value is also credited (in output.cpp) to - // the size of the code section. - return NativeJump::instruction_size; -} - -// Emit exception handler code. Stuff framesize into a register -// and call a VM stub routine. -int emit_exception_handler(CodeBuffer& cbuf) { - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - MacroAssembler _masm(&cbuf); - address base = - __ start_a_stub(size_exception_handler()); - if (base == NULL) return 0; // CodeBuffer::expand failed - int offset = __ offset(); - __ jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - __ end_a_stub(); - return offset; -} - -uint size_deopt_handler() { - // NativeCall instruction size is the same as NativeJump. - // exception handler starts out as jump and can be patched to - // a call be deoptimization. (4932387) - // Note that this value is also credited (in output.cpp) to - // the size of the code section. - return 5 + NativeJump::instruction_size; // pushl(); jmp; -} - -// Emit deopt handler code. -int emit_deopt_handler(CodeBuffer& cbuf) { - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - MacroAssembler _masm(&cbuf); - address base = - __ start_a_stub(size_exception_handler()); - if (base == NULL) return 0; // CodeBuffer::expand failed - int offset = __ offset(); - InternalAddress here(__ pc()); - __ pushptr(here.addr()); - - __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); - assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); - __ end_a_stub(); - return offset; -} int Matcher::regnum_to_fpu_offset(int regnum) { return regnum - 32; // The FP registers are in the second chunk @@ -1389,6 +1343,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_postalloc_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 @@ -1534,19 +1491,6 @@ return EBP_REG_mask(); } -const RegMask Matcher::mathExactI_result_proj_mask() { - return EAX_REG_mask(); -} - -const RegMask Matcher::mathExactL_result_proj_mask() { - ShouldNotReachHere(); - return RegMask(); -} - -const RegMask Matcher::mathExactI_flags_proj_mask() { - return INT_FLAGS_mask(); -} - // Returns true if the high 32 bits of the value is known to be zero. bool is_operand_hi32_zero(Node* n) { int opc = n->Opcode(); @@ -2910,542 +2854,6 @@ emit_d8 (cbuf,0 ); %} - - // Because the transitions from emitted code to the runtime - // monitorenter/exit helper stubs are so slow it's critical that - // we inline both the stack-locking fast-path and the inflated fast path. - // - // See also: cmpFastLock and cmpFastUnlock. - // - // What follows is a specialized inline transliteration of the code - // in slow_enter() and slow_exit(). If we're concerned about I$ bloat - // another option would be to emit TrySlowEnter and TrySlowExit methods - // at startup-time. These methods would accept arguments as - // (rax,=Obj, rbx=Self, rcx=box, rdx=Scratch) and return success-failure - // indications in the icc.ZFlag. Fast_Lock and Fast_Unlock would simply - // marshal the arguments and emit calls to TrySlowEnter and TrySlowExit. - // In practice, however, the # of lock sites is bounded and is usually small. - // Besides the call overhead, TrySlowEnter and TrySlowExit might suffer - // if the processor uses simple bimodal branch predictors keyed by EIP - // Since the helper routines would be called from multiple synchronization - // sites. - // - // An even better approach would be write "MonitorEnter()" and "MonitorExit()" - // in java - using j.u.c and unsafe - and just bind the lock and unlock sites - // to those specialized methods. That'd give us a mostly platform-independent - // implementation that the JITs could optimize and inline at their pleasure. - // Done correctly, the only time we'd need to cross to native could would be - // to park() or unpark() threads. We'd also need a few more unsafe operators - // to (a) prevent compiler-JIT reordering of non-volatile accesses, and - // (b) explicit barriers or fence operations. - // - // TODO: - // - // * Arrange for C2 to pass "Self" into Fast_Lock and Fast_Unlock in one of the registers (scr). - // This avoids manifesting the Self pointer in the Fast_Lock and Fast_Unlock terminals. - // Given TLAB allocation, Self is usually manifested in a register, so passing it into - // the lock operators would typically be faster than reifying Self. - // - // * Ideally I'd define the primitives as: - // fast_lock (nax Obj, nax box, EAX tmp, nax scr) where box, tmp and scr are KILLED. - // fast_unlock (nax Obj, EAX box, nax tmp) where box and tmp are KILLED - // Unfortunately ADLC bugs prevent us from expressing the ideal form. - // Instead, we're stuck with a rather awkward and brittle register assignments below. - // Furthermore the register assignments are overconstrained, possibly resulting in - // sub-optimal code near the synchronization site. - // - // * Eliminate the sp-proximity tests and just use "== Self" tests instead. - // Alternately, use a better sp-proximity test. - // - // * Currently ObjectMonitor._Owner can hold either an sp value or a (THREAD *) value. - // Either one is sufficient to uniquely identify a thread. - // TODO: eliminate use of sp in _owner and use get_thread(tr) instead. - // - // * Intrinsify notify() and notifyAll() for the common cases where the - // object is locked by the calling thread but the waitlist is empty. - // avoid the expensive JNI call to JVM_Notify() and JVM_NotifyAll(). - // - // * use jccb and jmpb instead of jcc and jmp to improve code density. - // But beware of excessive branch density on AMD Opterons. - // - // * Both Fast_Lock and Fast_Unlock set the ICC.ZF to indicate success - // or failure of the fast-path. If the fast-path fails then we pass - // control to the slow-path, typically in C. In Fast_Lock and - // Fast_Unlock we often branch to DONE_LABEL, just to find that C2 - // will emit a conditional branch immediately after the node. - // So we have branches to branches and lots of ICC.ZF games. - // Instead, it might be better to have C2 pass a "FailureLabel" - // into Fast_Lock and Fast_Unlock. In the case of success, control - // will drop through the node. ICC.ZF is undefined at exit. - // In the case of failure, the node will branch directly to the - // FailureLabel - - - // obj: object to lock - // box: on-stack box address (displaced header location) - KILLED - // rax,: tmp -- KILLED - // scr: tmp -- KILLED - enc_class Fast_Lock( eRegP obj, eRegP box, eAXRegI tmp, eRegP scr ) %{ - - Register objReg = as_Register($obj$$reg); - Register boxReg = as_Register($box$$reg); - Register tmpReg = as_Register($tmp$$reg); - Register scrReg = as_Register($scr$$reg); - - // Ensure the register assignents are disjoint - guarantee (objReg != boxReg, "") ; - guarantee (objReg != tmpReg, "") ; - guarantee (objReg != scrReg, "") ; - guarantee (boxReg != tmpReg, "") ; - guarantee (boxReg != scrReg, "") ; - guarantee (tmpReg == as_Register(EAX_enc), "") ; - - MacroAssembler masm(&cbuf); - - if (_counters != NULL) { - masm.atomic_incl(ExternalAddress((address) _counters->total_entry_count_addr())); - } - if (EmitSync & 1) { - // set box->dhw = unused_mark (3) - // Force all sync thru slow-path: slow_enter() and slow_exit() - masm.movptr (Address(boxReg, 0), int32_t(markOopDesc::unused_mark())) ; - masm.cmpptr (rsp, (int32_t)0) ; - } else - if (EmitSync & 2) { - Label DONE_LABEL ; - if (UseBiasedLocking) { - // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. - masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, _counters); - } - - masm.movptr(tmpReg, Address(objReg, 0)) ; // fetch markword - masm.orptr (tmpReg, 0x1); - masm.movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(boxReg, Address(objReg, 0)); // Updates tmpReg - masm.jcc(Assembler::equal, DONE_LABEL); - // Recursive locking - masm.subptr(tmpReg, rsp); - masm.andptr(tmpReg, (int32_t) 0xFFFFF003 ); - masm.movptr(Address(boxReg, 0), tmpReg); - masm.bind(DONE_LABEL) ; - } else { - // Possible cases that we'll encounter in fast_lock - // ------------------------------------------------ - // * Inflated - // -- unlocked - // -- Locked - // = by self - // = by other - // * biased - // -- by Self - // -- by other - // * neutral - // * stack-locked - // -- by self - // = sp-proximity test hits - // = sp-proximity test generates false-negative - // -- by other - // - - Label IsInflated, DONE_LABEL, PopDone ; - - // TODO: optimize away redundant LDs of obj->mark and improve the markword triage - // order to reduce the number of conditional branches in the most common cases. - // Beware -- there's a subtle invariant that fetch of the markword - // at [FETCH], below, will never observe a biased encoding (*101b). - // If this invariant is not held we risk exclusion (safety) failure. - if (UseBiasedLocking && !UseOptoBiasInlining) { - masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, _counters); - } - - masm.movptr(tmpReg, Address(objReg, 0)) ; // [FETCH] - masm.testptr(tmpReg, 0x02) ; // Inflated v (Stack-locked or neutral) - masm.jccb (Assembler::notZero, IsInflated) ; - - // Attempt stack-locking ... - masm.orptr (tmpReg, 0x1); - masm.movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(boxReg, Address(objReg, 0)); // Updates tmpReg - if (_counters != NULL) { - masm.cond_inc32(Assembler::equal, - ExternalAddress((address)_counters->fast_path_entry_count_addr())); - } - masm.jccb (Assembler::equal, DONE_LABEL); - - // Recursive locking - masm.subptr(tmpReg, rsp); - masm.andptr(tmpReg, 0xFFFFF003 ); - masm.movptr(Address(boxReg, 0), tmpReg); - if (_counters != NULL) { - masm.cond_inc32(Assembler::equal, - ExternalAddress((address)_counters->fast_path_entry_count_addr())); - } - masm.jmp (DONE_LABEL) ; - - masm.bind (IsInflated) ; - - // The object is inflated. - // - // TODO-FIXME: eliminate the ugly use of manifest constants: - // Use markOopDesc::monitor_value instead of "2". - // use markOop::unused_mark() instead of "3". - // The tmpReg value is an objectMonitor reference ORed with - // markOopDesc::monitor_value (2). We can either convert tmpReg to an - // objectmonitor pointer by masking off the "2" bit or we can just - // use tmpReg as an objectmonitor pointer but bias the objectmonitor - // field offsets with "-2" to compensate for and annul the low-order tag bit. - // - // I use the latter as it avoids AGI stalls. - // As such, we write "mov r, [tmpReg+OFFSETOF(Owner)-2]" - // instead of "mov r, [tmpReg+OFFSETOF(Owner)]". - // - #define OFFSET_SKEWED(f) ((ObjectMonitor::f ## _offset_in_bytes())-2) - - // boxReg refers to the on-stack BasicLock in the current frame. - // We'd like to write: - // set box->_displaced_header = markOop::unused_mark(). Any non-0 value suffices. - // This is convenient but results a ST-before-CAS penalty. The following CAS suffers - // additional latency as we have another ST in the store buffer that must drain. - - if (EmitSync & 8192) { - masm.movptr(Address(boxReg, 0), 3) ; // results in ST-before-CAS penalty - masm.get_thread (scrReg) ; - masm.movptr(boxReg, tmpReg); // consider: LEA box, [tmp-2] - masm.movptr(tmpReg, NULL_WORD); // consider: xor vs mov - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - } else - if ((EmitSync & 128) == 0) { // avoid ST-before-CAS - masm.movptr(scrReg, boxReg) ; - masm.movptr(boxReg, tmpReg); // consider: LEA box, [tmp-2] - - // Using a prefetchw helps avoid later RTS->RTO upgrades and cache probes - if ((EmitSync & 2048) && VM_Version::supports_3dnow_prefetch() && os::is_MP()) { - // prefetchw [eax + Offset(_owner)-2] - masm.prefetchw(Address(rax, ObjectMonitor::owner_offset_in_bytes()-2)); - } - - if ((EmitSync & 64) == 0) { - // Optimistic form: consider XORL tmpReg,tmpReg - masm.movptr(tmpReg, NULL_WORD) ; - } else { - // Can suffer RTS->RTO upgrades on shared or cold $ lines - // Test-And-CAS instead of CAS - masm.movptr(tmpReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; // rax, = m->_owner - masm.testptr(tmpReg, tmpReg) ; // Locked ? - masm.jccb (Assembler::notZero, DONE_LABEL) ; - } - - // Appears unlocked - try to swing _owner from null to non-null. - // Ideally, I'd manifest "Self" with get_thread and then attempt - // to CAS the register containing Self into m->Owner. - // But we don't have enough registers, so instead we can either try to CAS - // rsp or the address of the box (in scr) into &m->owner. If the CAS succeeds - // we later store "Self" into m->Owner. Transiently storing a stack address - // (rsp or the address of the box) into m->owner is harmless. - // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - masm.movptr(Address(scrReg, 0), 3) ; // box->_displaced_header = 3 - masm.jccb (Assembler::notZero, DONE_LABEL) ; - masm.get_thread (scrReg) ; // beware: clobbers ICCs - masm.movptr(Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2), scrReg) ; - masm.xorptr(boxReg, boxReg) ; // set icc.ZFlag = 1 to indicate success - - // If the CAS fails we can either retry or pass control to the slow-path. - // We use the latter tactic. - // Pass the CAS result in the icc.ZFlag into DONE_LABEL - // If the CAS was successful ... - // Self has acquired the lock - // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. - // Intentional fall-through into DONE_LABEL ... - } else { - masm.movptr(Address(boxReg, 0), 3) ; // results in ST-before-CAS penalty - masm.movptr(boxReg, tmpReg) ; - - // Using a prefetchw helps avoid later RTS->RTO upgrades and cache probes - if ((EmitSync & 2048) && VM_Version::supports_3dnow_prefetch() && os::is_MP()) { - // prefetchw [eax + Offset(_owner)-2] - masm.prefetchw(Address(rax, ObjectMonitor::owner_offset_in_bytes()-2)); - } - - if ((EmitSync & 64) == 0) { - // Optimistic form - masm.xorptr (tmpReg, tmpReg) ; - } else { - // Can suffer RTS->RTO upgrades on shared or cold $ lines - masm.movptr(tmpReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; // rax, = m->_owner - masm.testptr(tmpReg, tmpReg) ; // Locked ? - masm.jccb (Assembler::notZero, DONE_LABEL) ; - } - - // Appears unlocked - try to swing _owner from null to non-null. - // Use either "Self" (in scr) or rsp as thread identity in _owner. - // Invariant: tmpReg == 0. tmpReg is EAX which is the implicit cmpxchg comparand. - masm.get_thread (scrReg) ; - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(scrReg, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - - // If the CAS fails we can either retry or pass control to the slow-path. - // We use the latter tactic. - // Pass the CAS result in the icc.ZFlag into DONE_LABEL - // If the CAS was successful ... - // Self has acquired the lock - // Invariant: m->_recursions should already be 0, so we don't need to explicitly set it. - // Intentional fall-through into DONE_LABEL ... - } - - // DONE_LABEL is a hot target - we'd really like to place it at the - // start of cache line by padding with NOPs. - // See the AMD and Intel software optimization manuals for the - // most efficient "long" NOP encodings. - // Unfortunately none of our alignment mechanisms suffice. - masm.bind(DONE_LABEL); - - // Avoid branch-to-branch on AMD processors - // This appears to be superstition. - if (EmitSync & 32) masm.nop() ; - - - // At DONE_LABEL the icc ZFlag is set as follows ... - // Fast_Unlock uses the same protocol. - // ZFlag == 1 -> Success - // ZFlag == 0 -> Failure - force control through the slow-path - } - %} - - // obj: object to unlock - // box: box address (displaced header location), killed. Must be EAX. - // rbx,: killed tmp; cannot be obj nor box. - // - // Some commentary on balanced locking: - // - // Fast_Lock and Fast_Unlock are emitted only for provably balanced lock sites. - // Methods that don't have provably balanced locking are forced to run in the - // interpreter - such methods won't be compiled to use fast_lock and fast_unlock. - // The interpreter provides two properties: - // I1: At return-time the interpreter automatically and quietly unlocks any - // objects acquired the current activation (frame). Recall that the - // interpreter maintains an on-stack list of locks currently held by - // a frame. - // I2: If a method attempts to unlock an object that is not held by the - // the frame the interpreter throws IMSX. - // - // Lets say A(), which has provably balanced locking, acquires O and then calls B(). - // B() doesn't have provably balanced locking so it runs in the interpreter. - // Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O - // is still locked by A(). - // - // The only other source of unbalanced locking would be JNI. The "Java Native Interface: - // Programmer's Guide and Specification" claims that an object locked by jni_monitorenter - // should not be unlocked by "normal" java-level locking and vice-versa. The specification - // doesn't specify what will occur if a program engages in such mixed-mode locking, however. - - enc_class Fast_Unlock( nabxRegP obj, eAXRegP box, eRegP tmp) %{ - - Register objReg = as_Register($obj$$reg); - Register boxReg = as_Register($box$$reg); - Register tmpReg = as_Register($tmp$$reg); - - guarantee (objReg != boxReg, "") ; - guarantee (objReg != tmpReg, "") ; - guarantee (boxReg != tmpReg, "") ; - guarantee (boxReg == as_Register(EAX_enc), "") ; - MacroAssembler masm(&cbuf); - - if (EmitSync & 4) { - // Disable - inhibit all inlining. Force control through the slow-path - masm.cmpptr (rsp, 0) ; - } else - if (EmitSync & 8) { - Label DONE_LABEL ; - if (UseBiasedLocking) { - masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); - } - // classic stack-locking code ... - masm.movptr(tmpReg, Address(boxReg, 0)) ; - masm.testptr(tmpReg, tmpReg) ; - masm.jcc (Assembler::zero, DONE_LABEL) ; - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses EAX which is box - masm.bind(DONE_LABEL); - } else { - Label DONE_LABEL, Stacked, CheckSucc, Inflated ; - - // Critically, the biased locking test must have precedence over - // and appear before the (box->dhw == 0) recursive stack-lock test. - if (UseBiasedLocking && !UseOptoBiasInlining) { - masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); - } - - masm.cmpptr(Address(boxReg, 0), 0) ; // Examine the displaced header - masm.movptr(tmpReg, Address(objReg, 0)) ; // Examine the object's markword - masm.jccb (Assembler::zero, DONE_LABEL) ; // 0 indicates recursive stack-lock - - masm.testptr(tmpReg, 0x02) ; // Inflated? - masm.jccb (Assembler::zero, Stacked) ; - - masm.bind (Inflated) ; - // It's inflated. - // Despite our balanced locking property we still check that m->_owner == Self - // as java routines or native JNI code called by this thread might - // have released the lock. - // Refer to the comments in synchronizer.cpp for how we might encode extra - // state in _succ so we can avoid fetching EntryList|cxq. - // - // I'd like to add more cases in fast_lock() and fast_unlock() -- - // such as recursive enter and exit -- but we have to be wary of - // I$ bloat, T$ effects and BP$ effects. - // - // If there's no contention try a 1-0 exit. That is, exit without - // a costly MEMBAR or CAS. See synchronizer.cpp for details on how - // we detect and recover from the race that the 1-0 exit admits. - // - // Conceptually Fast_Unlock() must execute a STST|LDST "release" barrier - // before it STs null into _owner, releasing the lock. Updates - // to data protected by the critical section must be visible before - // we drop the lock (and thus before any other thread could acquire - // the lock and observe the fields protected by the lock). - // IA32's memory-model is SPO, so STs are ordered with respect to - // each other and there's no need for an explicit barrier (fence). - // See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html. - - masm.get_thread (boxReg) ; - if ((EmitSync & 4096) && VM_Version::supports_3dnow_prefetch() && os::is_MP()) { - // prefetchw [ebx + Offset(_owner)-2] - masm.prefetchw(Address(rbx, ObjectMonitor::owner_offset_in_bytes()-2)); - } - - // Note that we could employ various encoding schemes to reduce - // the number of loads below (currently 4) to just 2 or 3. - // Refer to the comments in synchronizer.cpp. - // In practice the chain of fetches doesn't seem to impact performance, however. - if ((EmitSync & 65536) == 0 && (EmitSync & 256)) { - // Attempt to reduce branch density - AMD's branch predictor. - masm.xorptr(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - masm.orptr(boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; - masm.orptr(boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; - masm.orptr(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; - masm.jccb (Assembler::notZero, DONE_LABEL) ; - masm.movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), NULL_WORD) ; - masm.jmpb (DONE_LABEL) ; - } else { - masm.xorptr(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - masm.orptr(boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; - masm.jccb (Assembler::notZero, DONE_LABEL) ; - masm.movptr(boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; - masm.orptr(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; - masm.jccb (Assembler::notZero, CheckSucc) ; - masm.movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), NULL_WORD) ; - masm.jmpb (DONE_LABEL) ; - } - - // The Following code fragment (EmitSync & 65536) improves the performance of - // contended applications and contended synchronization microbenchmarks. - // Unfortunately the emission of the code - even though not executed - causes regressions - // in scimark and jetstream, evidently because of $ effects. Replacing the code - // with an equal number of never-executed NOPs results in the same regression. - // We leave it off by default. - - if ((EmitSync & 65536) != 0) { - Label LSuccess, LGoSlowPath ; - - masm.bind (CheckSucc) ; - - // Optional pre-test ... it's safe to elide this - if ((EmitSync & 16) == 0) { - masm.cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), 0) ; - masm.jccb (Assembler::zero, LGoSlowPath) ; - } - - // We have a classic Dekker-style idiom: - // ST m->_owner = 0 ; MEMBAR; LD m->_succ - // There are a number of ways to implement the barrier: - // (1) lock:andl &m->_owner, 0 - // is fast, but mask doesn't currently support the "ANDL M,IMM32" form. - // LOCK: ANDL [ebx+Offset(_Owner)-2], 0 - // Encodes as 81 31 OFF32 IMM32 or 83 63 OFF8 IMM8 - // (2) If supported, an explicit MFENCE is appealing. - // In older IA32 processors MFENCE is slower than lock:add or xchg - // particularly if the write-buffer is full as might be the case if - // if stores closely precede the fence or fence-equivalent instruction. - // In more modern implementations MFENCE appears faster, however. - // (3) In lieu of an explicit fence, use lock:addl to the top-of-stack - // The $lines underlying the top-of-stack should be in M-state. - // The locked add instruction is serializing, of course. - // (4) Use xchg, which is serializing - // mov boxReg, 0; xchgl boxReg, [tmpReg + Offset(_owner)-2] also works - // (5) ST m->_owner = 0 and then execute lock:orl &m->_succ, 0. - // The integer condition codes will tell us if succ was 0. - // Since _succ and _owner should reside in the same $line and - // we just stored into _owner, it's likely that the $line - // remains in M-state for the lock:orl. - // - // We currently use (3), although it's likely that switching to (2) - // is correct for the future. - - masm.movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), NULL_WORD) ; - if (os::is_MP()) { - if (VM_Version::supports_sse2() && 1 == FenceInstruction) { - masm.mfence(); - } else { - masm.lock () ; masm.addptr(Address(rsp, 0), 0) ; - } - } - // Ratify _succ remains non-null - masm.cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), 0) ; - masm.jccb (Assembler::notZero, LSuccess) ; - - masm.xorptr(boxReg, boxReg) ; // box is really EAX - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(rsp, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); - masm.jccb (Assembler::notEqual, LSuccess) ; - // Since we're low on registers we installed rsp as a placeholding in _owner. - // Now install Self over rsp. This is safe as we're transitioning from - // non-null to non=null - masm.get_thread (boxReg) ; - masm.movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), boxReg) ; - // Intentional fall-through into LGoSlowPath ... - - masm.bind (LGoSlowPath) ; - masm.orptr(boxReg, 1) ; // set ICC.ZF=0 to indicate failure - masm.jmpb (DONE_LABEL) ; - - masm.bind (LSuccess) ; - masm.xorptr(boxReg, boxReg) ; // set ICC.ZF=1 to indicate success - masm.jmpb (DONE_LABEL) ; - } - - masm.bind (Stacked) ; - // It's not inflated and it's not recursively stack-locked and it's not biased. - // It must be stack-locked. - // Try to reset the header to displaced header. - // The "box" value on the stack is stable, so we can reload - // and be assured we observe the same value as above. - masm.movptr(tmpReg, Address(boxReg, 0)) ; - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses EAX which is box - // Intention fall-thru into DONE_LABEL - - - // DONE_LABEL is a hot target - we'd really like to place it at the - // start of cache line by padding with NOPs. - // See the AMD and Intel software optimization manuals for the - // most efficient "long" NOP encodings. - // Unfortunately none of our alignment mechanisms suffice. - if ((EmitSync & 65536) == 0) { - masm.bind (CheckSucc) ; - } - masm.bind(DONE_LABEL); - - // Avoid branch to branch on AMD processors - if (EmitSync & 32768) { masm.nop() ; } - } - %} - - enc_class enc_pop_rdx() %{ emit_opcode(cbuf,0x5A); %} @@ -3768,7 +3176,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 @@ -5704,6 +5112,19 @@ %} instruct countTrailingZerosI(rRegI dst, rRegI src, eFlagsReg cr) %{ + predicate(UseCountTrailingZerosInstruction); + match(Set dst (CountTrailingZerosI src)); + effect(KILL cr); + + format %{ "TZCNT $dst, $src\t# count trailing zeros (int)" %} + ins_encode %{ + __ tzcntl($dst$$Register, $src$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct countTrailingZerosI_bsf(rRegI dst, rRegI src, eFlagsReg cr) %{ + predicate(!UseCountTrailingZerosInstruction); match(Set dst (CountTrailingZerosI src)); effect(KILL cr); @@ -5723,6 +5144,30 @@ %} instruct countTrailingZerosL(rRegI dst, eRegL src, eFlagsReg cr) %{ + predicate(UseCountTrailingZerosInstruction); + match(Set dst (CountTrailingZerosL src)); + effect(TEMP dst, KILL cr); + + format %{ "TZCNT $dst, $src.lo\t# count trailing zeros (long) \n\t" + "JNC done\n\t" + "TZCNT $dst, $src.hi\n\t" + "ADD $dst, 32\n" + "done:" %} + ins_encode %{ + Register Rdst = $dst$$Register; + Register Rsrc = $src$$Register; + Label done; + __ tzcntl(Rdst, Rsrc); + __ jccb(Assembler::carryClear, done); + __ tzcntl(Rdst, HIGH_FROM_LOW(Rsrc)); + __ addl(Rdst, BitsPerInt); + __ bind(done); + %} + ins_pipe(ialu_reg); +%} + +instruct countTrailingZerosL_bsf(rRegI dst, eRegL src, eFlagsReg cr) %{ + predicate(!UseCountTrailingZerosInstruction); match(Set dst (CountTrailingZerosL src)); effect(TEMP dst, KILL cr); @@ -7099,6 +6544,7 @@ instruct membar_acquire() %{ match(MemBarAcquire); + match(LoadFence); ins_cost(400); size(0); @@ -7119,6 +6565,7 @@ instruct membar_release() %{ match(MemBarRelease); + match(StoreFence); ins_cost(400); size(0); @@ -7535,44 +6982,6 @@ //----------Arithmetic Instructions-------------------------------------------- //----------Addition Instructions---------------------------------------------- -instruct addExactI_eReg(eAXRegI dst, rRegI src, eFlagsReg cr) -%{ - match(AddExactI dst src); - effect(DEF cr); - - format %{ "ADD $dst, $src\t# addExact int" %} - ins_encode %{ - __ addl($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct addExactI_eReg_imm(eAXRegI dst, immI src, eFlagsReg cr) -%{ - match(AddExactI dst src); - effect(DEF cr); - - format %{ "ADD $dst, $src\t# addExact int" %} - ins_encode %{ - __ addl($dst$$Register, $src$$constant); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct addExactI_eReg_mem(eAXRegI dst, memory src, eFlagsReg cr) -%{ - match(AddExactI dst (LoadI src)); - effect(DEF cr); - - ins_cost(125); - format %{ "ADD $dst,$src\t# addExact int" %} - ins_encode %{ - __ addl($dst$$Register, $src$$Address); - %} - ins_pipe( ialu_reg_mem ); -%} - - // Integer Addition Instructions instruct addI_eReg(rRegI dst, rRegI src, eFlagsReg cr) %{ match(Set dst (AddI dst src)); @@ -7882,43 +7291,6 @@ //----------Subtraction Instructions------------------------------------------- -instruct subExactI_eReg(eAXRegI dst, rRegI src, eFlagsReg cr) -%{ - match(SubExactI dst src); - effect(DEF cr); - - format %{ "SUB $dst, $src\t# subExact int" %} - ins_encode %{ - __ subl($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct subExactI_eReg_imm(eAXRegI dst, immI src, eFlagsReg cr) -%{ - match(SubExactI dst src); - effect(DEF cr); - - format %{ "SUB $dst, $src\t# subExact int" %} - ins_encode %{ - __ subl($dst$$Register, $src$$constant); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct subExactI_eReg_mem(eAXRegI dst, memory src, eFlagsReg cr) -%{ - match(SubExactI dst (LoadI src)); - effect(DEF cr); - - ins_cost(125); - format %{ "SUB $dst,$src\t# subExact int" %} - ins_encode %{ - __ subl($dst$$Register, $src$$Address); - %} - ins_pipe( ialu_reg_mem ); -%} - // Integer Subtraction Instructions instruct subI_eReg(rRegI dst, rRegI src, eFlagsReg cr) %{ match(Set dst (SubI dst src)); @@ -7987,17 +7359,6 @@ ins_pipe( ialu_reg ); %} -instruct negExactI_eReg(eAXRegI dst, eFlagsReg cr) %{ - match(NegExactI dst); - effect(DEF cr); - - format %{ "NEG $dst\t# negExact int"%} - ins_encode %{ - __ negl($dst$$Register); - %} - ins_pipe(ialu_reg); -%} - //----------Multiplication/Division Instructions------------------------------- // Integer Multiplication Instructions // Multiply Register @@ -8209,46 +7570,6 @@ ins_pipe( pipe_slow ); %} -instruct mulExactI_eReg(eAXRegI dst, rRegI src, eFlagsReg cr) -%{ - match(MulExactI dst src); - effect(DEF cr); - - ins_cost(300); - format %{ "IMUL $dst, $src\t# mulExact int" %} - ins_encode %{ - __ imull($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg_alu0); -%} - -instruct mulExactI_eReg_imm(eAXRegI dst, rRegI src, immI imm, eFlagsReg cr) -%{ - match(MulExactI src imm); - effect(DEF cr); - - ins_cost(300); - format %{ "IMUL $dst, $src, $imm\t# mulExact int" %} - ins_encode %{ - __ imull($dst$$Register, $src$$Register, $imm$$constant); - %} - ins_pipe(ialu_reg_reg_alu0); -%} - -instruct mulExactI_eReg_mem(eAXRegI dst, memory src, eFlagsReg cr) -%{ - match(MulExactI dst (LoadI src)); - effect(DEF cr); - - ins_cost(350); - format %{ "IMUL $dst, $src\t# mulExact int" %} - ins_encode %{ - __ imull($dst$$Register, $src$$Address); - %} - ins_pipe(ialu_reg_mem_alu0); -%} - - // Integer DIV with Register instruct divI_eReg(eAXRegI rax, eDXRegI rdx, eCXRegI div, eFlagsReg cr) %{ match(Set rax (DivI rax div)); @@ -8692,6 +8013,123 @@ ins_pipe( ialu_mem_imm ); %} +// BMI1 instructions +instruct andnI_rReg_rReg_rReg(rRegI dst, rRegI src1, rRegI src2, immI_M1 minus_1, eFlagsReg cr) %{ + match(Set dst (AndI (XorI src1 minus_1) src2)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "ANDNL $dst, $src1, $src2" %} + + ins_encode %{ + __ andnl($dst$$Register, $src1$$Register, $src2$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct andnI_rReg_rReg_mem(rRegI dst, rRegI src1, memory src2, immI_M1 minus_1, eFlagsReg cr) %{ + match(Set dst (AndI (XorI src1 minus_1) (LoadI src2) )); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "ANDNL $dst, $src1, $src2" %} + + ins_encode %{ + __ andnl($dst$$Register, $src1$$Register, $src2$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI0 imm_zero, eFlagsReg cr) %{ + match(Set dst (AndI (SubI imm_zero src) src)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "BLSIL $dst, $src" %} + + ins_encode %{ + __ blsil($dst$$Register, $src$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct blsiI_rReg_mem(rRegI dst, memory src, immI0 imm_zero, eFlagsReg cr) %{ + match(Set dst (AndI (SubI imm_zero (LoadI src) ) (LoadI src) )); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "BLSIL $dst, $src" %} + + ins_encode %{ + __ blsil($dst$$Register, $src$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsmskI_rReg_rReg(rRegI dst, rRegI src, immI_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (XorI (AddI src minus_1) src)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "BLSMSKL $dst, $src" %} + + ins_encode %{ + __ blsmskl($dst$$Register, $src$$Register); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsmskI_rReg_mem(rRegI dst, memory src, immI_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (XorI (AddI (LoadI src) minus_1) (LoadI src) )); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "BLSMSKL $dst, $src" %} + + ins_encode %{ + __ blsmskl($dst$$Register, $src$$Address); + %} + + ins_pipe(ialu_reg_mem); +%} + +instruct blsrI_rReg_rReg(rRegI dst, rRegI src, immI_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (AndI (AddI src minus_1) src) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "BLSRL $dst, $src" %} + + ins_encode %{ + __ blsrl($dst$$Register, $src$$Register); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsrI_rReg_mem(rRegI dst, memory src, immI_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (AndI (AddI (LoadI src) minus_1) (LoadI src) )); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "BLSRL $dst, $src" %} + + ins_encode %{ + __ blsrl($dst$$Register, $src$$Address); + %} + + ins_pipe(ialu_reg_mem); +%} + // Or Instructions // Or Register with Register instruct orI_eReg(rRegI dst, rRegI src, eFlagsReg cr) %{ @@ -9114,6 +8552,91 @@ instruct cadd_cmpLTMask_mem(ncxRegI p, ncxRegI q, memory y, eCXRegI tmp, eFlagsReg cr) %{ match(Set p (AddI (AndI (CmpLTMask p q) (LoadI y)) (SubI p q))); */ +//----------Overflow Math Instructions----------------------------------------- + +instruct overflowAddI_eReg(eFlagsReg cr, eAXRegI op1, rRegI op2) +%{ + match(Set cr (OverflowAddI op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "ADD $op1, $op2\t# overflow check int" %} + + ins_encode %{ + __ addl($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowAddI_rReg_imm(eFlagsReg cr, eAXRegI op1, immI op2) +%{ + match(Set cr (OverflowAddI op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "ADD $op1, $op2\t# overflow check int" %} + + ins_encode %{ + __ addl($op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowSubI_rReg(eFlagsReg cr, rRegI op1, rRegI op2) +%{ + match(Set cr (OverflowSubI op1 op2)); + + format %{ "CMP $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ cmpl($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowSubI_rReg_imm(eFlagsReg cr, rRegI op1, immI op2) +%{ + match(Set cr (OverflowSubI op1 op2)); + + format %{ "CMP $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ cmpl($op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowNegI_rReg(eFlagsReg cr, immI0 zero, eAXRegI op2) +%{ + match(Set cr (OverflowSubI zero op2)); + effect(DEF cr, USE_KILL op2); + + format %{ "NEG $op2\t# overflow check int" %} + ins_encode %{ + __ negl($op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowMulI_rReg(eFlagsReg cr, eAXRegI op1, rRegI op2) +%{ + match(Set cr (OverflowMulI op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "IMUL $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ imull($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct overflowMulI_rReg_imm(eFlagsReg cr, rRegI op1, immI op2, rRegI tmp) +%{ + match(Set cr (OverflowMulI op1 op2)); + effect(DEF cr, TEMP tmp, USE op1, USE op2); + + format %{ "IMUL $tmp, $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ imull($tmp$$Register, $op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg_alu0); +%} //----------Long Instructions------------------------------------------------ // Add Long Register with Register @@ -9229,6 +8752,210 @@ ins_pipe( ialu_reg_long_mem ); %} +// BMI1 instructions +instruct andnL_eReg_eReg_eReg(eRegL dst, eRegL src1, eRegL src2, immL_M1 minus_1, eFlagsReg cr) %{ + match(Set dst (AndL (XorL src1 minus_1) src2)); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + format %{ "ANDNL $dst.lo, $src1.lo, $src2.lo\n\t" + "ANDNL $dst.hi, $src1.hi, $src2.hi" + %} + + ins_encode %{ + Register Rdst = $dst$$Register; + Register Rsrc1 = $src1$$Register; + Register Rsrc2 = $src2$$Register; + __ andnl(Rdst, Rsrc1, Rsrc2); + __ andnl(HIGH_FROM_LOW(Rdst), HIGH_FROM_LOW(Rsrc1), HIGH_FROM_LOW(Rsrc2)); + %} + ins_pipe(ialu_reg_reg_long); +%} + +instruct andnL_eReg_eReg_mem(eRegL dst, eRegL src1, memory src2, immL_M1 minus_1, eFlagsReg cr) %{ + match(Set dst (AndL (XorL src1 minus_1) (LoadL src2) )); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + ins_cost(125); + format %{ "ANDNL $dst.lo, $src1.lo, $src2\n\t" + "ANDNL $dst.hi, $src1.hi, $src2+4" + %} + + ins_encode %{ + Register Rdst = $dst$$Register; + Register Rsrc1 = $src1$$Register; + Address src2_hi = Address::make_raw($src2$$base, $src2$$index, $src2$$scale, $src2$$disp + 4, relocInfo::none); + + __ andnl(Rdst, Rsrc1, $src2$$Address); + __ andnl(HIGH_FROM_LOW(Rdst), HIGH_FROM_LOW(Rsrc1), src2_hi); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsiL_eReg_eReg(eRegL dst, eRegL src, immL0 imm_zero, eFlagsReg cr) %{ + match(Set dst (AndL (SubL imm_zero src) src)); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + format %{ "MOVL $dst.hi, 0\n\t" + "BLSIL $dst.lo, $src.lo\n\t" + "JNZ done\n\t" + "BLSIL $dst.hi, $src.hi\n" + "done:" + %} + + ins_encode %{ + Label done; + Register Rdst = $dst$$Register; + Register Rsrc = $src$$Register; + __ movl(HIGH_FROM_LOW(Rdst), 0); + __ blsil(Rdst, Rsrc); + __ jccb(Assembler::notZero, done); + __ blsil(HIGH_FROM_LOW(Rdst), HIGH_FROM_LOW(Rsrc)); + __ bind(done); + %} + ins_pipe(ialu_reg); +%} + +instruct blsiL_eReg_mem(eRegL dst, memory src, immL0 imm_zero, eFlagsReg cr) %{ + match(Set dst (AndL (SubL imm_zero (LoadL src) ) (LoadL src) )); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + ins_cost(125); + format %{ "MOVL $dst.hi, 0\n\t" + "BLSIL $dst.lo, $src\n\t" + "JNZ done\n\t" + "BLSIL $dst.hi, $src+4\n" + "done:" + %} + + ins_encode %{ + Label done; + Register Rdst = $dst$$Register; + Address src_hi = Address::make_raw($src$$base, $src$$index, $src$$scale, $src$$disp + 4, relocInfo::none); + + __ movl(HIGH_FROM_LOW(Rdst), 0); + __ blsil(Rdst, $src$$Address); + __ jccb(Assembler::notZero, done); + __ blsil(HIGH_FROM_LOW(Rdst), src_hi); + __ bind(done); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsmskL_eReg_eReg(eRegL dst, eRegL src, immL_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (XorL (AddL src minus_1) src)); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + format %{ "MOVL $dst.hi, 0\n\t" + "BLSMSKL $dst.lo, $src.lo\n\t" + "JNC done\n\t" + "BLSMSKL $dst.hi, $src.hi\n" + "done:" + %} + + ins_encode %{ + Label done; + Register Rdst = $dst$$Register; + Register Rsrc = $src$$Register; + __ movl(HIGH_FROM_LOW(Rdst), 0); + __ blsmskl(Rdst, Rsrc); + __ jccb(Assembler::carryClear, done); + __ blsmskl(HIGH_FROM_LOW(Rdst), HIGH_FROM_LOW(Rsrc)); + __ bind(done); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsmskL_eReg_mem(eRegL dst, memory src, immL_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (XorL (AddL (LoadL src) minus_1) (LoadL src) )); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + ins_cost(125); + format %{ "MOVL $dst.hi, 0\n\t" + "BLSMSKL $dst.lo, $src\n\t" + "JNC done\n\t" + "BLSMSKL $dst.hi, $src+4\n" + "done:" + %} + + ins_encode %{ + Label done; + Register Rdst = $dst$$Register; + Address src_hi = Address::make_raw($src$$base, $src$$index, $src$$scale, $src$$disp + 4, relocInfo::none); + + __ movl(HIGH_FROM_LOW(Rdst), 0); + __ blsmskl(Rdst, $src$$Address); + __ jccb(Assembler::carryClear, done); + __ blsmskl(HIGH_FROM_LOW(Rdst), src_hi); + __ bind(done); + %} + + ins_pipe(ialu_reg_mem); +%} + +instruct blsrL_eReg_eReg(eRegL dst, eRegL src, immL_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (AndL (AddL src minus_1) src) ); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + format %{ "MOVL $dst.hi, $src.hi\n\t" + "BLSRL $dst.lo, $src.lo\n\t" + "JNC done\n\t" + "BLSRL $dst.hi, $src.hi\n" + "done:" + %} + + ins_encode %{ + Label done; + Register Rdst = $dst$$Register; + Register Rsrc = $src$$Register; + __ movl(HIGH_FROM_LOW(Rdst), HIGH_FROM_LOW(Rsrc)); + __ blsrl(Rdst, Rsrc); + __ jccb(Assembler::carryClear, done); + __ blsrl(HIGH_FROM_LOW(Rdst), HIGH_FROM_LOW(Rsrc)); + __ bind(done); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsrL_eReg_mem(eRegL dst, memory src, immL_M1 minus_1, eFlagsReg cr) +%{ + match(Set dst (AndL (AddL (LoadL src) minus_1) (LoadL src) )); + predicate(UseBMI1Instructions); + effect(KILL cr, TEMP dst); + + ins_cost(125); + format %{ "MOVL $dst.hi, $src+4\n\t" + "BLSRL $dst.lo, $src\n\t" + "JNC done\n\t" + "BLSRL $dst.hi, $src+4\n" + "done:" + %} + + ins_encode %{ + Label done; + Register Rdst = $dst$$Register; + Address src_hi = Address::make_raw($src$$base, $src$$index, $src$$scale, $src$$disp + 4, relocInfo::none); + __ movl(HIGH_FROM_LOW(Rdst), src_hi); + __ blsrl(Rdst, $src$$Address); + __ jccb(Assembler::carryClear, done); + __ blsrl(HIGH_FROM_LOW(Rdst), src_hi); + __ bind(done); + %} + + ins_pipe(ialu_reg_mem); +%} + // Or Long Register with Register instruct orl_eReg(eRegL dst, eRegL src, eFlagsReg cr) %{ match(Set dst (OrL dst src)); @@ -13147,23 +12874,44 @@ // inlined locking and unlocking - -instruct cmpFastLock( eFlagsReg cr, eRegP object, eBXRegP box, eAXRegI tmp, eRegP scr) %{ - match( Set cr (FastLock object box) ); - effect( TEMP tmp, TEMP scr, USE_KILL box ); +instruct cmpFastLockRTM(eFlagsReg cr, eRegP object, eBXRegP box, eAXRegI tmp, eDXRegI scr, rRegI cx1, rRegI cx2) %{ + predicate(Compile::current()->use_rtm()); + match(Set cr (FastLock object box)); + effect(TEMP tmp, TEMP scr, TEMP cx1, TEMP cx2, USE_KILL box); + ins_cost(300); + format %{ "FASTLOCK $object,$box\t! kills $box,$tmp,$scr,$cx1,$cx2" %} + ins_encode %{ + __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, + $scr$$Register, $cx1$$Register, $cx2$$Register, + _counters, _rtm_counters, _stack_rtm_counters, + ((Method*)(ra_->C->method()->constant_encoding()))->method_data(), + true, ra_->C->profile_rtm()); + %} + ins_pipe(pipe_slow); +%} + +instruct cmpFastLock(eFlagsReg cr, eRegP object, eBXRegP box, eAXRegI tmp, eRegP scr) %{ + predicate(!Compile::current()->use_rtm()); + match(Set cr (FastLock object box)); + effect(TEMP tmp, TEMP scr, USE_KILL box); ins_cost(300); format %{ "FASTLOCK $object,$box\t! kills $box,$tmp,$scr" %} - ins_encode( Fast_Lock(object,box,tmp,scr) ); - ins_pipe( pipe_slow ); -%} - -instruct cmpFastUnlock( eFlagsReg cr, eRegP object, eAXRegP box, eRegP tmp ) %{ - match( Set cr (FastUnlock object box) ); - effect( TEMP tmp, USE_KILL box ); + ins_encode %{ + __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, + $scr$$Register, noreg, noreg, _counters, NULL, NULL, NULL, false, false); + %} + ins_pipe(pipe_slow); +%} + +instruct cmpFastUnlock(eFlagsReg cr, eRegP object, eAXRegP box, eRegP tmp ) %{ + match(Set cr (FastUnlock object box)); + effect(TEMP tmp, USE_KILL box); ins_cost(300); format %{ "FASTUNLOCK $object,$box\t! kills $box,$tmp" %} - ins_encode( Fast_Unlock(object,box,tmp) ); - ins_pipe( pipe_slow ); + ins_encode %{ + __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register, ra_->C->use_rtm()); + %} + ins_pipe(pipe_slow); %} diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/x86/vm/x86_64.ad Wed Oct 15 16:02:50 2014 +0200 @@ -688,6 +688,11 @@ return 0; // absolute addressing, no offset } +bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } +void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { // Empty encoding } @@ -708,14 +713,15 @@ void MachPrologNode::format(PhaseRegAlloc* ra_, outputStream* st) const { Compile* C = ra_->C; - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); + int bangsize = C->bang_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove wordSize for return addr which is already pushed. framesize -= wordSize; - if (C->need_stack_bang(framesize)) { + if (C->need_stack_bang(bangsize)) { framesize -= wordSize; - st->print("# stack bang"); + st->print("# stack bang (%d bytes)", bangsize); st->print("\n\t"); st->print("pushq rbp\t# Save rbp"); if (framesize) { @@ -746,9 +752,10 @@ Compile* C = ra_->C; MacroAssembler _masm(&cbuf); - int framesize = C->frame_slots() << LogBytesPerInt; - - __ verified_entry(framesize, C->need_stack_bang(framesize), false); + int framesize = C->frame_size_in_bytes(); + int bangsize = C->bang_size_in_bytes(); + + __ verified_entry(framesize, C->need_stack_bang(bangsize)?bangsize:0, false); C->set_frame_complete(cbuf.insts_size()); @@ -781,7 +788,7 @@ st->cr(); st->print("\t"); } - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove word for return adr already pushed // and RBP @@ -817,7 +824,7 @@ __ vzeroupper(); } - int framesize = C->frame_slots() << LogBytesPerInt; + int framesize = C->frame_size_in_bytes(); assert((framesize & (StackAlignmentInBytes-1)) == 0, "frame size not aligned"); // Remove word for return adr already pushed // and RBP @@ -1434,66 +1441,9 @@ return MachNode::size(ra_); // too many variables; just compute it // the hard way } - + //============================================================================= -uint size_exception_handler() -{ - // NativeCall instruction size is the same as NativeJump. - // Note that this value is also credited (in output.cpp) to - // the size of the code section. - return NativeJump::instruction_size; -} - -// Emit exception handler code. -int emit_exception_handler(CodeBuffer& cbuf) -{ - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - MacroAssembler _masm(&cbuf); - address base = - __ start_a_stub(size_exception_handler()); - if (base == NULL) return 0; // CodeBuffer::expand failed - int offset = __ offset(); - __ jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); - assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); - __ end_a_stub(); - return offset; -} - -uint size_deopt_handler() -{ - // three 5 byte instructions - return 15; -} - -// Emit deopt handler code. -int emit_deopt_handler(CodeBuffer& cbuf) -{ - - // Note that the code buffer's insts_mark is always relative to insts. - // That's why we must use the macroassembler to generate a handler. - MacroAssembler _masm(&cbuf); - address base = - __ start_a_stub(size_deopt_handler()); - if (base == NULL) return 0; // CodeBuffer::expand failed - int offset = __ offset(); - address the_pc = (address) __ pc(); - Label next; - // push a "the_pc" on the stack without destroying any registers - // as they all may be live. - - // push address of "next" - __ call(next, relocInfo::none); // reloc none is fine since it is a disp32 - __ bind(next); - // adjust it so it matches "the_pc" - __ subptr(Address(rsp, 0), __ offset() - offset); - __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); - assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); - __ end_a_stub(); - return offset; -} int Matcher::regnum_to_fpu_offset(int regnum) { @@ -1542,6 +1492,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_postalloc_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 @@ -1649,18 +1602,6 @@ return PTR_RBP_REG_mask(); } -const RegMask Matcher::mathExactI_result_proj_mask() { - return INT_RAX_REG_mask(); -} - -const RegMask Matcher::mathExactL_result_proj_mask() { - return LONG_RAX_REG_mask(); -} - -const RegMask Matcher::mathExactI_flags_proj_mask() { - return INT_FLAGS_mask(); -} - %} //----------ENCODING BLOCK----------------------------------------------------- @@ -2591,231 +2532,6 @@ %} - // obj: object to lock - // box: box address (header location) -- killed - // tmp: rax -- killed - // scr: rbx -- killed - // - // What follows is a direct transliteration of fast_lock() and fast_unlock() - // from i486.ad. See that file for comments. - // TODO: where possible switch from movq (r, 0) to movl(r,0) and - // use the shorter encoding. (Movl clears the high-order 32-bits). - - - enc_class Fast_Lock(rRegP obj, rRegP box, rax_RegI tmp, rRegP scr) - %{ - Register objReg = as_Register((int)$obj$$reg); - Register boxReg = as_Register((int)$box$$reg); - Register tmpReg = as_Register($tmp$$reg); - Register scrReg = as_Register($scr$$reg); - MacroAssembler masm(&cbuf); - - // Verify uniqueness of register assignments -- necessary but not sufficient - assert (objReg != boxReg && objReg != tmpReg && - objReg != scrReg && tmpReg != scrReg, "invariant") ; - - if (_counters != NULL) { - masm.atomic_incl(ExternalAddress((address) _counters->total_entry_count_addr())); - } - if (EmitSync & 1) { - // Without cast to int32_t a movptr will destroy r10 which is typically obj - masm.movptr (Address(boxReg, 0), (int32_t)intptr_t(markOopDesc::unused_mark())) ; - masm.cmpptr(rsp, (int32_t)NULL_WORD) ; - } else - if (EmitSync & 2) { - Label DONE_LABEL; - if (UseBiasedLocking) { - // Note: tmpReg maps to the swap_reg argument and scrReg to the tmp_reg argument. - masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, false, DONE_LABEL, NULL, _counters); - } - // QQQ was movl... - masm.movptr(tmpReg, 0x1); - masm.orptr(tmpReg, Address(objReg, 0)); - masm.movptr(Address(boxReg, 0), tmpReg); - if (os::is_MP()) { - masm.lock(); - } - masm.cmpxchgptr(boxReg, Address(objReg, 0)); // Updates tmpReg - masm.jcc(Assembler::equal, DONE_LABEL); - - // Recursive locking - masm.subptr(tmpReg, rsp); - masm.andptr(tmpReg, 7 - os::vm_page_size()); - masm.movptr(Address(boxReg, 0), tmpReg); - - masm.bind(DONE_LABEL); - masm.nop(); // avoid branch to branch - } else { - Label DONE_LABEL, IsInflated, Egress; - - masm.movptr(tmpReg, Address(objReg, 0)) ; - masm.testl (tmpReg, 0x02) ; // inflated vs stack-locked|neutral|biased - masm.jcc (Assembler::notZero, IsInflated) ; - - // it's stack-locked, biased or neutral - // TODO: optimize markword triage order to reduce the number of - // conditional branches in the most common cases. - // Beware -- there's a subtle invariant that fetch of the markword - // at [FETCH], below, will never observe a biased encoding (*101b). - // If this invariant is not held we'll suffer exclusion (safety) failure. - - if (UseBiasedLocking && !UseOptoBiasInlining) { - masm.biased_locking_enter(boxReg, objReg, tmpReg, scrReg, true, DONE_LABEL, NULL, _counters); - masm.movptr(tmpReg, Address(objReg, 0)) ; // [FETCH] - } - - // was q will it destroy high? - masm.orl (tmpReg, 1) ; - masm.movptr(Address(boxReg, 0), tmpReg) ; - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(boxReg, Address(objReg, 0)); // Updates tmpReg - if (_counters != NULL) { - masm.cond_inc32(Assembler::equal, - ExternalAddress((address) _counters->fast_path_entry_count_addr())); - } - masm.jcc (Assembler::equal, DONE_LABEL); - - // Recursive locking - masm.subptr(tmpReg, rsp); - masm.andptr(tmpReg, 7 - os::vm_page_size()); - masm.movptr(Address(boxReg, 0), tmpReg); - if (_counters != NULL) { - masm.cond_inc32(Assembler::equal, - ExternalAddress((address) _counters->fast_path_entry_count_addr())); - } - masm.jmp (DONE_LABEL) ; - - masm.bind (IsInflated) ; - // It's inflated - - // TODO: someday avoid the ST-before-CAS penalty by - // relocating (deferring) the following ST. - // We should also think about trying a CAS without having - // fetched _owner. If the CAS is successful we may - // avoid an RTO->RTS upgrade on the $line. - // Without cast to int32_t a movptr will destroy r10 which is typically obj - masm.movptr(Address(boxReg, 0), (int32_t)intptr_t(markOopDesc::unused_mark())) ; - - masm.mov (boxReg, tmpReg) ; - masm.movptr (tmpReg, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - masm.testptr(tmpReg, tmpReg) ; - masm.jcc (Assembler::notZero, DONE_LABEL) ; - - // It's inflated and appears unlocked - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(r15_thread, Address(boxReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - // Intentional fall-through into DONE_LABEL ... - - masm.bind (DONE_LABEL) ; - masm.nop () ; // avoid jmp to jmp - } - %} - - // obj: object to unlock - // box: box address (displaced header location), killed - // RBX: killed tmp; cannot be obj nor box - enc_class Fast_Unlock(rRegP obj, rax_RegP box, rRegP tmp) - %{ - - Register objReg = as_Register($obj$$reg); - Register boxReg = as_Register($box$$reg); - Register tmpReg = as_Register($tmp$$reg); - MacroAssembler masm(&cbuf); - - if (EmitSync & 4) { - masm.cmpptr(rsp, 0) ; - } else - if (EmitSync & 8) { - Label DONE_LABEL; - if (UseBiasedLocking) { - masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); - } - - // Check whether the displaced header is 0 - //(=> recursive unlock) - masm.movptr(tmpReg, Address(boxReg, 0)); - masm.testptr(tmpReg, tmpReg); - masm.jcc(Assembler::zero, DONE_LABEL); - - // If not recursive lock, reset the header to displaced header - if (os::is_MP()) { - masm.lock(); - } - masm.cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses RAX which is box - masm.bind(DONE_LABEL); - masm.nop(); // avoid branch to branch - } else { - Label DONE_LABEL, Stacked, CheckSucc ; - - if (UseBiasedLocking && !UseOptoBiasInlining) { - masm.biased_locking_exit(objReg, tmpReg, DONE_LABEL); - } - - masm.movptr(tmpReg, Address(objReg, 0)) ; - masm.cmpptr(Address(boxReg, 0), (int32_t)NULL_WORD) ; - masm.jcc (Assembler::zero, DONE_LABEL) ; - masm.testl (tmpReg, 0x02) ; - masm.jcc (Assembler::zero, Stacked) ; - - // It's inflated - masm.movptr(boxReg, Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)) ; - masm.xorptr(boxReg, r15_thread) ; - masm.orptr (boxReg, Address (tmpReg, ObjectMonitor::recursions_offset_in_bytes()-2)) ; - masm.jcc (Assembler::notZero, DONE_LABEL) ; - masm.movptr(boxReg, Address (tmpReg, ObjectMonitor::cxq_offset_in_bytes()-2)) ; - masm.orptr (boxReg, Address (tmpReg, ObjectMonitor::EntryList_offset_in_bytes()-2)) ; - masm.jcc (Assembler::notZero, CheckSucc) ; - masm.movptr(Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), (int32_t)NULL_WORD) ; - masm.jmp (DONE_LABEL) ; - - if ((EmitSync & 65536) == 0) { - Label LSuccess, LGoSlowPath ; - masm.bind (CheckSucc) ; - masm.cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int32_t)NULL_WORD) ; - masm.jcc (Assembler::zero, LGoSlowPath) ; - - // I'd much rather use lock:andl m->_owner, 0 as it's faster than the - // the explicit ST;MEMBAR combination, but masm doesn't currently support - // "ANDQ M,IMM". Don't use MFENCE here. lock:add to TOS, xchg, etc - // are all faster when the write buffer is populated. - masm.movptr (Address (tmpReg, ObjectMonitor::owner_offset_in_bytes()-2), (int32_t)NULL_WORD) ; - if (os::is_MP()) { - masm.lock () ; masm.addl (Address(rsp, 0), 0) ; - } - masm.cmpptr(Address (tmpReg, ObjectMonitor::succ_offset_in_bytes()-2), (int32_t)NULL_WORD) ; - masm.jcc (Assembler::notZero, LSuccess) ; - - masm.movptr (boxReg, (int32_t)NULL_WORD) ; // box is really EAX - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(r15_thread, Address(tmpReg, ObjectMonitor::owner_offset_in_bytes()-2)); - masm.jcc (Assembler::notEqual, LSuccess) ; - // Intentional fall-through into slow-path - - masm.bind (LGoSlowPath) ; - masm.orl (boxReg, 1) ; // set ICC.ZF=0 to indicate failure - masm.jmp (DONE_LABEL) ; - - masm.bind (LSuccess) ; - masm.testl (boxReg, 0) ; // set ICC.ZF=1 to indicate success - masm.jmp (DONE_LABEL) ; - } - - masm.bind (Stacked) ; - masm.movptr(tmpReg, Address (boxReg, 0)) ; // re-fetch - if (os::is_MP()) { masm.lock(); } - masm.cmpxchgptr(tmpReg, Address(objReg, 0)); // Uses RAX which is box - - if (EmitSync & 65536) { - masm.bind (CheckSucc) ; - } - masm.bind(DONE_LABEL); - if (EmitSync & 32768) { - masm.nop(); // avoid branch to branch - } - } - %} - - enc_class enc_rethrow() %{ cbuf.set_insts_mark(); @@ -2953,7 +2669,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. @@ -6251,6 +5967,19 @@ %} instruct countTrailingZerosI(rRegI dst, rRegI src, rFlagsReg cr) %{ + predicate(UseCountTrailingZerosInstruction); + match(Set dst (CountTrailingZerosI src)); + effect(KILL cr); + + format %{ "tzcntl $dst, $src\t# count trailing zeros (int)" %} + ins_encode %{ + __ tzcntl($dst$$Register, $src$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct countTrailingZerosI_bsf(rRegI dst, rRegI src, rFlagsReg cr) %{ + predicate(!UseCountTrailingZerosInstruction); match(Set dst (CountTrailingZerosI src)); effect(KILL cr); @@ -6270,6 +5999,19 @@ %} instruct countTrailingZerosL(rRegI dst, rRegL src, rFlagsReg cr) %{ + predicate(UseCountTrailingZerosInstruction); + match(Set dst (CountTrailingZerosL src)); + effect(KILL cr); + + format %{ "tzcntq $dst, $src\t# count trailing zeros (long)" %} + ins_encode %{ + __ tzcntq($dst$$Register, $src$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct countTrailingZerosL_bsf(rRegI dst, rRegL src, rFlagsReg cr) %{ + predicate(!UseCountTrailingZerosInstruction); match(Set dst (CountTrailingZerosL src)); effect(KILL cr); @@ -6348,6 +6090,7 @@ instruct membar_acquire() %{ match(MemBarAcquire); + match(LoadFence); ins_cost(0); size(0); @@ -6370,6 +6113,7 @@ instruct membar_release() %{ match(MemBarRelease); + match(StoreFence); ins_cost(0); size(0); @@ -6953,82 +6697,6 @@ //----------Arithmetic Instructions-------------------------------------------- //----------Addition Instructions---------------------------------------------- -instruct addExactI_rReg(rax_RegI dst, rRegI src, rFlagsReg cr) -%{ - match(AddExactI dst src); - effect(DEF cr); - - format %{ "addl $dst, $src\t# addExact int" %} - ins_encode %{ - __ addl($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct addExactI_rReg_imm(rax_RegI dst, immI src, rFlagsReg cr) -%{ - match(AddExactI dst src); - effect(DEF cr); - - format %{ "addl $dst, $src\t# addExact int" %} - ins_encode %{ - __ addl($dst$$Register, $src$$constant); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct addExactI_rReg_mem(rax_RegI dst, memory src, rFlagsReg cr) -%{ - match(AddExactI dst (LoadI src)); - effect(DEF cr); - - ins_cost(125); // XXX - format %{ "addl $dst, $src\t# addExact int" %} - ins_encode %{ - __ addl($dst$$Register, $src$$Address); - %} - - ins_pipe(ialu_reg_mem); -%} - -instruct addExactL_rReg(rax_RegL dst, rRegL src, rFlagsReg cr) -%{ - match(AddExactL dst src); - effect(DEF cr); - - format %{ "addq $dst, $src\t# addExact long" %} - ins_encode %{ - __ addq($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct addExactL_rReg_imm(rax_RegL dst, immL32 src, rFlagsReg cr) -%{ - match(AddExactL dst src); - effect(DEF cr); - - format %{ "addq $dst, $src\t# addExact long" %} - ins_encode %{ - __ addq($dst$$Register, $src$$constant); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct addExactL_rReg_mem(rax_RegL dst, memory src, rFlagsReg cr) -%{ - match(AddExactL dst (LoadL src)); - effect(DEF cr); - - ins_cost(125); // XXX - format %{ "addq $dst, $src\t# addExact long" %} - ins_encode %{ - __ addq($dst$$Register, $src$$Address); - %} - - ins_pipe(ialu_reg_mem); -%} - instruct addI_rReg(rRegI dst, rRegI src, rFlagsReg cr) %{ match(Set dst (AddI dst src)); @@ -7641,80 +7309,6 @@ ins_pipe(ialu_mem_imm); %} -instruct subExactI_rReg(rax_RegI dst, rRegI src, rFlagsReg cr) -%{ - match(SubExactI dst src); - effect(DEF cr); - - format %{ "subl $dst, $src\t# subExact int" %} - ins_encode %{ - __ subl($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct subExactI_rReg_imm(rax_RegI dst, immI src, rFlagsReg cr) -%{ - match(SubExactI dst src); - effect(DEF cr); - - format %{ "subl $dst, $src\t# subExact int" %} - ins_encode %{ - __ subl($dst$$Register, $src$$constant); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct subExactI_rReg_mem(rax_RegI dst, memory src, rFlagsReg cr) -%{ - match(SubExactI dst (LoadI src)); - effect(DEF cr); - - ins_cost(125); - format %{ "subl $dst, $src\t# subExact int" %} - ins_encode %{ - __ subl($dst$$Register, $src$$Address); - %} - ins_pipe(ialu_reg_mem); -%} - -instruct subExactL_rReg(rax_RegL dst, rRegL src, rFlagsReg cr) -%{ - match(SubExactL dst src); - effect(DEF cr); - - format %{ "subq $dst, $src\t# subExact long" %} - ins_encode %{ - __ subq($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct subExactL_rReg_imm(rax_RegL dst, immL32 src, rFlagsReg cr) -%{ - match(SubExactL dst (LoadL src)); - effect(DEF cr); - - format %{ "subq $dst, $src\t# subExact long" %} - ins_encode %{ - __ subq($dst$$Register, $src$$constant); - %} - ins_pipe(ialu_reg_reg); -%} - -instruct subExactL_rReg_mem(rax_RegI dst, memory src, rFlagsReg cr) -%{ - match(SubExactI dst src); - effect(DEF cr); - - ins_cost(125); - format %{ "subq $dst, $src\t# subExact long" %} - ins_encode %{ - __ subq($dst$$Register, $src$$Address); - %} - ins_pipe(ialu_reg_mem); -%} - instruct subL_rReg(rRegL dst, rRegL src, rFlagsReg cr) %{ match(Set dst (SubL dst src)); @@ -7831,31 +7425,6 @@ ins_pipe(ialu_reg); %} -instruct negExactI_rReg(rax_RegI dst, rFlagsReg cr) -%{ - match(NegExactI dst); - effect(KILL cr); - - format %{ "negl $dst\t# negExact int" %} - ins_encode %{ - __ negl($dst$$Register); - %} - ins_pipe(ialu_reg); -%} - -instruct negExactL_rReg(rax_RegL dst, rFlagsReg cr) -%{ - match(NegExactL dst); - effect(KILL cr); - - format %{ "negq $dst\t# negExact long" %} - ins_encode %{ - __ negq($dst$$Register); - %} - ins_pipe(ialu_reg); -%} - - //----------Multiplication/Division Instructions------------------------------- // Integer Multiplication Instructions // Multiply Register @@ -7972,86 +7541,6 @@ ins_pipe(ialu_reg_reg_alu0); %} - -instruct mulExactI_rReg(rax_RegI dst, rRegI src, rFlagsReg cr) -%{ - match(MulExactI dst src); - effect(DEF cr); - - ins_cost(300); - format %{ "imull $dst, $src\t# mulExact int" %} - ins_encode %{ - __ imull($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg_alu0); -%} - - -instruct mulExactI_rReg_imm(rax_RegI dst, rRegI src, immI imm, rFlagsReg cr) -%{ - match(MulExactI src imm); - effect(DEF cr); - - ins_cost(300); - format %{ "imull $dst, $src, $imm\t# mulExact int" %} - ins_encode %{ - __ imull($dst$$Register, $src$$Register, $imm$$constant); - %} - ins_pipe(ialu_reg_reg_alu0); -%} - -instruct mulExactI_rReg_mem(rax_RegI dst, memory src, rFlagsReg cr) -%{ - match(MulExactI dst (LoadI src)); - effect(DEF cr); - - ins_cost(350); - format %{ "imull $dst, $src\t# mulExact int" %} - ins_encode %{ - __ imull($dst$$Register, $src$$Address); - %} - ins_pipe(ialu_reg_mem_alu0); -%} - -instruct mulExactL_rReg(rax_RegL dst, rRegL src, rFlagsReg cr) -%{ - match(MulExactL dst src); - effect(DEF cr); - - ins_cost(300); - format %{ "imulq $dst, $src\t# mulExact long" %} - ins_encode %{ - __ imulq($dst$$Register, $src$$Register); - %} - ins_pipe(ialu_reg_reg_alu0); -%} - -instruct mulExactL_rReg_imm(rax_RegL dst, rRegL src, immL32 imm, rFlagsReg cr) -%{ - match(MulExactL src imm); - effect(DEF cr); - - ins_cost(300); - format %{ "imulq $dst, $src, $imm\t# mulExact long" %} - ins_encode %{ - __ imulq($dst$$Register, $src$$Register, $imm$$constant); - %} - ins_pipe(ialu_reg_reg_alu0); -%} - -instruct mulExactL_rReg_mem(rax_RegL dst, memory src, rFlagsReg cr) -%{ - match(MulExactL dst (LoadL src)); - effect(DEF cr); - - ins_cost(350); - format %{ "imulq $dst, $src\t# mulExact long" %} - ins_encode %{ - __ imulq($dst$$Register, $src$$Address); - %} - ins_pipe(ialu_reg_mem_alu0); -%} - instruct divI_rReg(rax_RegI rax, rdx_RegI rdx, no_rax_rdx_RegI div, rFlagsReg cr) %{ @@ -9104,6 +8593,122 @@ ins_pipe(ialu_mem_imm); %} +// BMI1 instructions +instruct andnI_rReg_rReg_mem(rRegI dst, rRegI src1, memory src2, immI_M1 minus_1, rFlagsReg cr) %{ + match(Set dst (AndI (XorI src1 minus_1) (LoadI src2))); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "andnl $dst, $src1, $src2" %} + + ins_encode %{ + __ andnl($dst$$Register, $src1$$Register, $src2$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct andnI_rReg_rReg_rReg(rRegI dst, rRegI src1, rRegI src2, immI_M1 minus_1, rFlagsReg cr) %{ + match(Set dst (AndI (XorI src1 minus_1) src2)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "andnl $dst, $src1, $src2" %} + + ins_encode %{ + __ andnl($dst$$Register, $src1$$Register, $src2$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI0 imm_zero, rFlagsReg cr) %{ + match(Set dst (AndI (SubI imm_zero src) src)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "blsil $dst, $src" %} + + ins_encode %{ + __ blsil($dst$$Register, $src$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct blsiI_rReg_mem(rRegI dst, memory src, immI0 imm_zero, rFlagsReg cr) %{ + match(Set dst (AndI (SubI imm_zero (LoadI src) ) (LoadI src) )); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "blsil $dst, $src" %} + + ins_encode %{ + __ blsil($dst$$Register, $src$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsmskI_rReg_mem(rRegI dst, memory src, immI_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (XorI (AddI (LoadI src) minus_1) (LoadI src) ) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "blsmskl $dst, $src" %} + + ins_encode %{ + __ blsmskl($dst$$Register, $src$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsmskI_rReg_rReg(rRegI dst, rRegI src, immI_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (XorI (AddI src minus_1) src)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "blsmskl $dst, $src" %} + + ins_encode %{ + __ blsmskl($dst$$Register, $src$$Register); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsrI_rReg_rReg(rRegI dst, rRegI src, immI_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (AndI (AddI src minus_1) src) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "blsrl $dst, $src" %} + + ins_encode %{ + __ blsrl($dst$$Register, $src$$Register); + %} + + ins_pipe(ialu_reg_mem); +%} + +instruct blsrI_rReg_mem(rRegI dst, memory src, immI_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (AndI (AddI (LoadI src) minus_1) (LoadI src) ) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "blsrl $dst, $src" %} + + ins_encode %{ + __ blsrl($dst$$Register, $src$$Address); + %} + + ins_pipe(ialu_reg); +%} + // Or Instructions // Or Register with Register instruct orI_rReg(rRegI dst, rRegI src, rFlagsReg cr) @@ -9335,6 +8940,122 @@ ins_pipe(ialu_mem_imm); %} +// BMI1 instructions +instruct andnL_rReg_rReg_mem(rRegL dst, rRegL src1, memory src2, immL_M1 minus_1, rFlagsReg cr) %{ + match(Set dst (AndL (XorL src1 minus_1) (LoadL src2))); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "andnq $dst, $src1, $src2" %} + + ins_encode %{ + __ andnq($dst$$Register, $src1$$Register, $src2$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct andnL_rReg_rReg_rReg(rRegL dst, rRegL src1, rRegL src2, immL_M1 minus_1, rFlagsReg cr) %{ + match(Set dst (AndL (XorL src1 minus_1) src2)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "andnq $dst, $src1, $src2" %} + + ins_encode %{ + __ andnq($dst$$Register, $src1$$Register, $src2$$Register); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsiL_rReg_rReg(rRegL dst, rRegL src, immL0 imm_zero, rFlagsReg cr) %{ + match(Set dst (AndL (SubL imm_zero src) src)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "blsiq $dst, $src" %} + + ins_encode %{ + __ blsiq($dst$$Register, $src$$Register); + %} + ins_pipe(ialu_reg); +%} + +instruct blsiL_rReg_mem(rRegL dst, memory src, immL0 imm_zero, rFlagsReg cr) %{ + match(Set dst (AndL (SubL imm_zero (LoadL src) ) (LoadL src) )); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "blsiq $dst, $src" %} + + ins_encode %{ + __ blsiq($dst$$Register, $src$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsmskL_rReg_mem(rRegL dst, memory src, immL_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (XorL (AddL (LoadL src) minus_1) (LoadL src) ) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "blsmskq $dst, $src" %} + + ins_encode %{ + __ blsmskq($dst$$Register, $src$$Address); + %} + ins_pipe(ialu_reg_mem); +%} + +instruct blsmskL_rReg_rReg(rRegL dst, rRegL src, immL_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (XorL (AddL src minus_1) src)); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "blsmskq $dst, $src" %} + + ins_encode %{ + __ blsmskq($dst$$Register, $src$$Register); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsrL_rReg_rReg(rRegL dst, rRegL src, immL_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (AndL (AddL src minus_1) src) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + format %{ "blsrq $dst, $src" %} + + ins_encode %{ + __ blsrq($dst$$Register, $src$$Register); + %} + + ins_pipe(ialu_reg); +%} + +instruct blsrL_rReg_mem(rRegL dst, memory src, immL_M1 minus_1, rFlagsReg cr) +%{ + match(Set dst (AndL (AddL (LoadL src) minus_1) (LoadL src)) ); + predicate(UseBMI1Instructions); + effect(KILL cr); + + ins_cost(125); + format %{ "blsrq $dst, $src" %} + + ins_encode %{ + __ blsrq($dst$$Register, $src$$Address); + %} + + ins_pipe(ialu_reg); +%} + // Or Instructions // Or Register with Register instruct orL_rReg(rRegL dst, rRegL src, rFlagsReg cr) @@ -10660,6 +10381,174 @@ ins_pipe( pipe_slow ); %} +//----------Overflow Math Instructions----------------------------------------- + +instruct overflowAddI_rReg(rFlagsReg cr, rax_RegI op1, rRegI op2) +%{ + match(Set cr (OverflowAddI op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "addl $op1, $op2\t# overflow check int" %} + + ins_encode %{ + __ addl($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowAddI_rReg_imm(rFlagsReg cr, rax_RegI op1, immI op2) +%{ + match(Set cr (OverflowAddI op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "addl $op1, $op2\t# overflow check int" %} + + ins_encode %{ + __ addl($op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowAddL_rReg(rFlagsReg cr, rax_RegL op1, rRegL op2) +%{ + match(Set cr (OverflowAddL op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "addq $op1, $op2\t# overflow check long" %} + ins_encode %{ + __ addq($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowAddL_rReg_imm(rFlagsReg cr, rax_RegL op1, immL32 op2) +%{ + match(Set cr (OverflowAddL op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "addq $op1, $op2\t# overflow check long" %} + ins_encode %{ + __ addq($op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowSubI_rReg(rFlagsReg cr, rRegI op1, rRegI op2) +%{ + match(Set cr (OverflowSubI op1 op2)); + + format %{ "cmpl $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ cmpl($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowSubI_rReg_imm(rFlagsReg cr, rRegI op1, immI op2) +%{ + match(Set cr (OverflowSubI op1 op2)); + + format %{ "cmpl $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ cmpl($op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowSubL_rReg(rFlagsReg cr, rRegL op1, rRegL op2) +%{ + match(Set cr (OverflowSubL op1 op2)); + + format %{ "cmpq $op1, $op2\t# overflow check long" %} + ins_encode %{ + __ cmpq($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowSubL_rReg_imm(rFlagsReg cr, rRegL op1, immL32 op2) +%{ + match(Set cr (OverflowSubL op1 op2)); + + format %{ "cmpq $op1, $op2\t# overflow check long" %} + ins_encode %{ + __ cmpq($op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowNegI_rReg(rFlagsReg cr, immI0 zero, rax_RegI op2) +%{ + match(Set cr (OverflowSubI zero op2)); + effect(DEF cr, USE_KILL op2); + + format %{ "negl $op2\t# overflow check int" %} + ins_encode %{ + __ negl($op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowNegL_rReg(rFlagsReg cr, immL0 zero, rax_RegL op2) +%{ + match(Set cr (OverflowSubL zero op2)); + effect(DEF cr, USE_KILL op2); + + format %{ "negq $op2\t# overflow check long" %} + ins_encode %{ + __ negq($op2$$Register); + %} + ins_pipe(ialu_reg_reg); +%} + +instruct overflowMulI_rReg(rFlagsReg cr, rax_RegI op1, rRegI op2) +%{ + match(Set cr (OverflowMulI op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "imull $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ imull($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct overflowMulI_rReg_imm(rFlagsReg cr, rRegI op1, immI op2, rRegI tmp) +%{ + match(Set cr (OverflowMulI op1 op2)); + effect(DEF cr, TEMP tmp, USE op1, USE op2); + + format %{ "imull $tmp, $op1, $op2\t# overflow check int" %} + ins_encode %{ + __ imull($tmp$$Register, $op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct overflowMulL_rReg(rFlagsReg cr, rax_RegL op1, rRegL op2) +%{ + match(Set cr (OverflowMulL op1 op2)); + effect(DEF cr, USE_KILL op1, USE op2); + + format %{ "imulq $op1, $op2\t# overflow check long" %} + ins_encode %{ + __ imulq($op1$$Register, $op2$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct overflowMulL_rReg_imm(rFlagsReg cr, rRegL op1, immL32 op2, rRegL tmp) +%{ + match(Set cr (OverflowMulL op1 op2)); + effect(DEF cr, TEMP tmp, USE op1, USE op2); + + format %{ "imulq $tmp, $op1, $op2\t# overflow check long" %} + ins_encode %{ + __ imulq($tmp$$Register, $op1$$Register, $op2$$constant); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + //----------Control Flow Instructions------------------------------------------ // Signed compare Instructions @@ -11443,27 +11332,43 @@ // ============================================================================ // inlined locking and unlocking -instruct cmpFastLock(rFlagsReg cr, - rRegP object, rbx_RegP box, rax_RegI tmp, rRegP scr) -%{ +instruct cmpFastLockRTM(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI tmp, rdx_RegI scr, rRegI cx1, rRegI cx2) %{ + predicate(Compile::current()->use_rtm()); + match(Set cr (FastLock object box)); + effect(TEMP tmp, TEMP scr, TEMP cx1, TEMP cx2, USE_KILL box); + ins_cost(300); + format %{ "fastlock $object,$box\t! kills $box,$tmp,$scr,$cx1,$cx2" %} + ins_encode %{ + __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, + $scr$$Register, $cx1$$Register, $cx2$$Register, + _counters, _rtm_counters, _stack_rtm_counters, + ((Method*)(ra_->C->method()->constant_encoding()))->method_data(), + true, ra_->C->profile_rtm()); + %} + ins_pipe(pipe_slow); +%} + +instruct cmpFastLock(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI tmp, rRegP scr) %{ + predicate(!Compile::current()->use_rtm()); match(Set cr (FastLock object box)); effect(TEMP tmp, TEMP scr, USE_KILL box); - ins_cost(300); format %{ "fastlock $object,$box\t! kills $box,$tmp,$scr" %} - ins_encode(Fast_Lock(object, box, tmp, scr)); + ins_encode %{ + __ fast_lock($object$$Register, $box$$Register, $tmp$$Register, + $scr$$Register, noreg, noreg, _counters, NULL, NULL, NULL, false, false); + %} ins_pipe(pipe_slow); %} -instruct cmpFastUnlock(rFlagsReg cr, - rRegP object, rax_RegP box, rRegP tmp) -%{ +instruct cmpFastUnlock(rFlagsReg cr, rRegP object, rax_RegP box, rRegP tmp) %{ match(Set cr (FastUnlock object box)); effect(TEMP tmp, USE_KILL box); - ins_cost(300); format %{ "fastunlock $object,$box\t! kills $box,$tmp" %} - ins_encode(Fast_Unlock(object, box, tmp)); + ins_encode %{ + __ fast_unlock($object$$Register, $box$$Register, $tmp$$Register, ra_->C->use_rtm()); + %} ins_pipe(pipe_slow); %} diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/zero/vm/bytecodeInterpreter_zero.hpp --- a/src/cpu/zero/vm/bytecodeInterpreter_zero.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/zero/vm/bytecodeInterpreter_zero.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -132,7 +132,7 @@ #define LOCALS_ADDR(offset) ((address)locals[-(offset)]) #define LOCALS_INT(offset) (*((jint*)&locals[-(offset)])) #define LOCALS_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) -#define LOCALS_OBJECT(offset) ((oop)locals[-(offset)]) +#define LOCALS_OBJECT(offset) (cast_to_oop(locals[-(offset)])) #define LOCALS_DOUBLE(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->d) #define LOCALS_LONG(offset) (((VMJavaVal64*)&locals[-((offset) + 1)])->l) #define LOCALS_LONG_AT(offset) (((address)&locals[-((offset) + 1)])) diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/zero/vm/cppInterpreter_zero.cpp --- a/src/cpu/zero/vm/cppInterpreter_zero.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/zero/vm/cppInterpreter_zero.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -220,7 +220,7 @@ } InvocationCounter *counter = mcs->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) @@ -916,17 +916,32 @@ return (InterpreterFrame *) fp; } -int AbstractInterpreter::layout_activation(Method* 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) { +int AbstractInterpreter::size_activation(int max_stack, + int tempcount, + int extra_args, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame) { + int header_words = InterpreterFrame::header_words; + int monitor_words = moncount * frame::interpreter_frame_monitor_size(); + int stack_words = is_top_frame ? max_stack : tempcount; + int callee_extra_locals = callee_locals - callee_param_count; + + return header_words + monitor_words + stack_words + callee_extra_locals; +} + +void AbstractInterpreter::layout_activation(Method* 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) { assert(popframe_extra_args == 0, "what to do?"); assert(!is_top_frame || (!callee_locals && !callee_param_count), "top frame should have no caller"); @@ -935,39 +950,31 @@ // does (the full InterpreterFrame::build, that is, not the // one that creates empty frames for the deoptimizer). // - // If interpreter_frame is not NULL then it will be filled in. - // It's size is determined by a previous call to this method, - // so it should be correct. + // interpreter_frame will be filled in. It's size is determined by + // a previous call to the size_activation() method, // // Note that tempcount is the current size of the expression // stack. For top most frames we will allocate a full sized // expression stack and not the trimmed version that non-top // frames have. - int header_words = InterpreterFrame::header_words; int monitor_words = moncount * frame::interpreter_frame_monitor_size(); - int stack_words = is_top_frame ? method->max_stack() : tempcount; - int callee_extra_locals = callee_locals - callee_param_count; - - if (interpreter_frame) { - intptr_t *locals = interpreter_frame->fp() + method->max_locals(); - interpreterState istate = interpreter_frame->get_interpreterState(); - intptr_t *monitor_base = (intptr_t*) istate; - intptr_t *stack_base = monitor_base - monitor_words; - intptr_t *stack = stack_base - tempcount - 1; + intptr_t *locals = interpreter_frame->fp() + method->max_locals(); + interpreterState istate = interpreter_frame->get_interpreterState(); + intptr_t *monitor_base = (intptr_t*) istate; + intptr_t *stack_base = monitor_base - monitor_words; + intptr_t *stack = stack_base - tempcount - 1; - BytecodeInterpreter::layout_interpreterState(istate, - caller, - NULL, - method, - locals, - stack, - stack_base, - monitor_base, - NULL, - is_top_frame); - } - return header_words + monitor_words + stack_words + callee_extra_locals; + BytecodeInterpreter::layout_interpreterState(istate, + caller, + NULL, + method, + locals, + stack, + stack_base, + monitor_base, + NULL, + is_top_frame); } void BytecodeInterpreter::layout_interpreterState(interpreterState istate, diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/zero/vm/globalDefinitions_zero.hpp --- a/src/cpu/zero/vm/globalDefinitions_zero.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/zero/vm/globalDefinitions_zero.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -28,4 +28,10 @@ #include +// 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 = false; + #endif // CPU_ZERO_VM_GLOBALDEFINITIONS_ZERO_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/zero/vm/globals_zero.hpp --- a/src/cpu/zero/vm/globals_zero.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/zero/vm/globals_zero.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -38,11 +38,13 @@ define_pd_global(bool, NeedsDeoptSuspend, false); define_pd_global(bool, ImplicitNullChecks, true); +define_pd_global(bool, TrapBasedNullChecks, false); define_pd_global(bool, UncommonNullCast, true); 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); diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/zero/vm/sharedRuntime_zero.cpp --- a/src/cpu/zero/vm/sharedRuntime_zero.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/zero/vm/sharedRuntime_zero.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -135,6 +135,7 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, + VMRegPair *regs2, int total_args_passed) { ShouldNotCallThis(); return 0; diff -r 45d7b2c7029d -r 52b4284cb496 src/cpu/zero/vm/shark_globals_zero.hpp --- a/src/cpu/zero/vm/shark_globals_zero.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/cpu/zero/vm/shark_globals_zero.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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(uintx, NewRatio, 12 ); define_pd_global(intx, NewSizeThreadIncrease, 4*K ); define_pd_global(intx, InitialCodeCacheSize, 160*K); diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2005, 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/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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,37 @@ +/* + * 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_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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2011, 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. + * + */ + +#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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005, 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_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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005, 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_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 45d7b2c7029d -r 52b4284cb496 src/os/aix/vm/jsig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/aix/vm/jsig.c Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,233 @@ +/* + * 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. + * + */ + +/* 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,201 @@ +/* + * 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. + * + */ + +#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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1999, 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 OS_AIX_VM_MUTEX_AIX_INLINE_HPP +#define OS_AIX_VM_MUTEX_AIX_INLINE_HPP + +#include "os_aix.inline.hpp" +#include "runtime/interfaceSupport.hpp" +#include "runtime/thread.inline.hpp" + +#endif // OS_AIX_VM_MUTEX_AIX_INLINE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,58 @@ +/* + * 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. + * + */ + +// 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,144 @@ +/* + * 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 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,5256 @@ +/* + * Copyright (c) 1999, 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. + * + */ + +// 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/thread.inline.hpp" +#include "runtime/threadCritical.hpp" +#include "runtime/timer.hpp" +#include "services/attachListener.hpp" +#include "services/runtimeService.hpp" +#include "utilities/decoder.hpp" +#include "utilities/defaultStream.hpp" +#include "utilities/events.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/vmError.hpp" + +// 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() { + os::Aix::meminfo_t mi; + if (os::Aix::get_meminfo(&mi)) { + return mi.real_free; + } else { + return 0xFFFFFFFFFFFFFFFFLL; + } +} + +julong os::physical_memory() { + return Aix::physical_memory(); +} + +//////////////////////////////////////////////////////////////////////////////// +// 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); + guarantee(p != NULL, "malloc failed"); + 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() { + +#define DEFAULT_LIBPATH "/usr/lib:/lib" +#define EXTENSIONS_DIR "/lib/ext" +#define ENDORSED_DIR "/lib/endorsed" + + // Buffer that fits several sprintfs. + // Note that the space for the trailing null is provided + // by the nulls included by the sizeof operator. + const size_t bufsize = + MAX3((size_t)MAXPATHLEN, // For dll_dir & friends. + (size_t)MAXPATHLEN + sizeof(EXTENSIONS_DIR), // extensions dir + (size_t)MAXPATHLEN + sizeof(ENDORSED_DIR)); // endorsed dir + char *buf = (char *)NEW_C_HEAP_ARRAY(char, bufsize, mtInternal); + + // sysclasspath, java_home, dll_dir + { + char *pslash; + os::jvm_path(buf, bufsize); + + // 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}. + } + Arguments::set_dll_dir(buf); + + 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. + } + } + } + Arguments::set_java_home(buf); + set_boot_path('/', ':'); + } + + // 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. + // Get the user setting of LIBPATH. + const char *v = ::getenv("LIBPATH"); + const char *v_colon = ":"; + if (v == NULL) { v = ""; v_colon = ""; } + + // Concatenate user and invariant part of ld_library_path. + // That's +1 for the colon and +1 for the trailing '\0'. + char *ld_library_path = (char *)NEW_C_HEAP_ARRAY(char, strlen(v) + 1 + sizeof(DEFAULT_LIBPATH) + 1, mtInternal); + sprintf(ld_library_path, "%s%s" DEFAULT_LIBPATH, v, v_colon); + Arguments::set_library_path(ld_library_path); + FREE_C_HEAP_ARRAY(char, ld_library_path, mtInternal); + + // Extensions directories. + sprintf(buf, "%s" EXTENSIONS_DIR, Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + + // Endorsed standards default directory. + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + +#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) { + struct tms ticks; + clock_t real_ticks = times(&ticks); + + if (real_ticks == (clock_t) (-1)) { + return false; + } else { + double ticks_per_second = (double) clock_tics_per_sec; + *process_user_time = ((double) ticks.tms_utime) / ticks_per_second; + *process_system_time = ((double) ticks.tms_stime) / ticks_per_second; + *process_real_time = ((double) real_ticks) / ticks_per_second; + + return true; + } +} + + +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; +} + +bool os::dll_build_name(char* buffer, size_t buflen, + const char* pname, const char* fname) { + bool retval = false; + // Copied from libhpi + const size_t pnamelen = pname ? strlen(pname) : 0; + + // Return error on buffer overflow. + if (pnamelen + strlen(fname) + 10 > (size_t) buflen) { + *buffer = '\0'; + return retval; + } + + if (pnamelen == 0) { + snprintf(buffer, buflen, "lib%s.so", fname); + retval = true; + } 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)) { + retval = true; + 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); + retval = true; + } + return retval; +} + +// Check if addr is inside libjvm.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::get_default_process_handle() { + return (void*)::dlopen(NULL, RTLD_LAZY); +} + +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(); +} + +int os::Aix::commit_memory_impl(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 0; +} + +bool os::pd_commit_memory(char* addr, size_t size, bool exec) { + return os::Aix::commit_memory_impl(addr, size, exec) == 0; +} + +void os::pd_commit_memory_or_exit(char* addr, size_t size, bool exec, + const char* mesg) { + assert(mesg != NULL, "mesg must be specified"); + os::Aix::commit_memory_impl(addr, size, exec); +} + +int os::Aix::commit_memory_impl(char* addr, size_t size, + size_t alignment_hint, bool exec) { + return os::Aix::commit_memory_impl(addr, size, exec); +} + +bool os::pd_commit_memory(char* addr, size_t size, size_t alignment_hint, + bool exec) { + return os::Aix::commit_memory_impl(addr, size, alignment_hint, exec) == 0; +} + +void os::pd_commit_memory_or_exit(char* addr, size_t size, + size_t alignment_hint, bool exec, + const char* mesg) { + os::Aix::commit_memory_impl(addr, size, alignment_hint, exec); +} + +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 wish 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: + + // trace + if (Verbose) { + if (addr) { + 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 (StubRoutines::SafeFetch32_stub()) { + + 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) { + // Call the old signal handler + if (actp->sa_handler == SIG_DFL) { + // It's more reasonable to let jvm treat it as an unexpected exception + // instead of taking the default action. + return false; + } else if (actp->sa_handler != SIG_IGN) { + if ((actp->sa_flags & SA_NODEFER) == 0) { + // automaticlly block the signal + sigaddset(&(actp->sa_mask), sig); + } + + sa_handler_t hand = NULL; + sa_sigaction_t sa = NULL; + bool siginfo_flag_set = (actp->sa_flags & SA_SIGINFO) != 0; + // retrieve the chained handler + if (siginfo_flag_set) { + sa = actp->sa_sigaction; + } else { + hand = actp->sa_handler; + } + + if ((actp->sa_flags & SA_RESETHAND) != 0) { + actp->sa_handler = SIG_DFL; + } + + // try to honor the signal mask + sigset_t oset; + pthread_sigmask(SIG_SETMASK, &(actp->sa_mask), &oset); + + // call into the chained handler + if (siginfo_flag_set) { + (*sa)(sig, siginfo, context); + } else { + (*hand)(sig); + } + + // restore the signal mask + pthread_sigmask(SIG_SETMASK, &oset, 0); + } + // Tell jvm's signal handler the signal is taken care of. + 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) { + + st->print(PTR_FORMAT ": ", addr); + + const LoadedLibraryModule* lib = LoadedLibraries::find_for_text_address(addr); + if (lib) { + lib->print(st); + return true; + } else { + lib = LoadedLibraries::find_for_data_address(addr); + if (lib) { + lib->print(st); + return true; + } else { + st->print_cr("(outside any module)"); + } + } + + 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) { + DIR *dir = NULL; + struct dirent *ptr; + + dir = opendir(path); + if (dir == NULL) return true; + + /* Scan the directory */ + bool result = true; + char buf[sizeof(struct dirent) + MAX_PATH]; + while (result && (ptr = ::readdir(dir)) != NULL) { + if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0) { + result = false; + } + } + closedir(dir); + return result; +} + +// 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) { + int oflags = O_WRONLY | O_CREAT; + if (!rewrite_existing) { + oflags |= O_EXCL; + } + return ::open64(path, oflags, S_IREAD | S_IWRITE); +} + +// 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) { + char * argv[4] = {"sh", "-c", cmd, NULL}; + + pid_t pid = fork(); + + if (pid < 0) { + // fork failed + return -1; + + } else if (pid == 0) { + // child process + + // try to be consistent with system(), which uses "/usr/bin/sh" on AIX + execve("/usr/bin/sh", argv, environ); + + // execve failed + _exit(-1); + + } else { + // copied from J2SE ..._waitForProcessExit() in UNIXProcess_md.c; we don't + // care about the actual exit code, for now. + + int status; + + // Wait for the child process to exit. This returns immediately if + // the child has already exited. */ + while (waitpid(pid, &status, 0) < 0) { + switch (errno) { + case ECHILD: return 0; + case EINTR: break; + default: return -1; + } + } + + if (WIFEXITED(status)) { + // The child exited normally; get its exit code. + return WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + // The child exited because of a signal + // The best value to return is 0x80 + signal number, + // because that is what all Unix shells do, and because + // it allows callers to distinguish between process exit and + // process death by signal. + return 0x80 + WTERMSIG(status); + } else { + // Unknown exit code; pass it through + return status; + } + } + // Remove warning. + return -1; +} + +// 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,385 @@ +/* + * Copyright (c) 1999, 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. + * + */ + +#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(); + + static int commit_memory_impl(char* addr, size_t bytes, bool exec); + static int commit_memory_impl(char* addr, size_t bytes, + size_t alignment_hint, bool exec); + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,286 @@ +/* + * 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 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 ":"; +} + +// 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,37 @@ +/* + * 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 + * 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,1026 @@ +/* + * 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 "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 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, now check it's mode + if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) { + // the path represents a link or some non-directory file type, + // which is not what we expected. declare it insecure. + // + return false; + } + else { + // we have an existing directory, check if the permissions are safe. + // + if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) { + // the directory is open for writing and could be subjected + // to a symlnk attack. declare it insecure. + // + 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); + + DIR* subdirp = os::opendir(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)); + } + } +} + + +// remove file +// +// this method removes the file with the given file name in the +// named directory. +// +static void remove_file(const char* dirname, const char* filename) { + + size_t nbytes = strlen(dirname) + strlen(filename) + 2; + char* path = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal); + + strcpy(path, dirname); + strcat(path, "/"); + strcat(path, filename); + + remove_file(path); + + FREE_C_HEAP_ARRAY(char, path, mtInternal); +} + + +// 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) { + + // open the user temp directory + DIR* dirp = os::opendir(dirname); + + if (dirp == NULL) { + // directory doesn't exist, so there is nothing to cleanup + return; + } + + if (!is_directory_secure(dirname)) { + // the directory is not a secure directory + 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 ".." + remove_file(dirname, 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))) { + + remove_file(dirname, entry->d_name); + } + errno = 0; + } + os::closedir(dirp); + 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 result; + + RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result); + if (result == OS_ERR) { + if (PrintMiscellaneous && Verbose) { + warning("could not create file %s: %s\n", filename, strerror(errno)); + } + return -1; + } + + // save the file descriptor + int fd = result; + + // 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; + RESTARTABLE(::open(filename, oflags), result); + 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)); + } + } + + return result; +} + +// 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); + + // 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, 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); + + 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; + 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; + file_flags = O_RDONLY; + } + else if (mode == PerfMemory::PERF_MODE_RW) { +#ifdef LATER + mmap_prot = PROT_READ | PROT_WRITE; + file_flags = O_RDWR; +#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); + 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"); + } + + *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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,367 @@ +/* + * 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 "asm/assembler.hpp" +#include "loadlib_aix.hpp" +#include "porting_aix.hpp" +#include "utilities/debug.hpp" + +#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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2001, 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/threadCritical.hpp" +#include "runtime/thread.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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,42 @@ +/* + * 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_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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2003, 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 + * 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 45d7b2c7029d -r 52b4284cb496 src/os/bsd/vm/decoder_machO.cpp --- a/src/os/bsd/vm/decoder_machO.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/bsd/vm/decoder_machO.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -53,7 +53,7 @@ struct symtab_command * symt = (struct symtab_command *) mach_find_command((struct mach_header_64 *)mach_base, LC_SYMTAB); if (symt == NULL) { - DEBUG_ONLY(tty->print_cr("no symtab in mach file at 0x%lx", mach_base)); + DEBUG_ONLY(tty->print_cr("no symtab in mach file at 0x%lx", p2i(mach_base))); return false; } uint32_t off = symt->symoff; /* symbol table offset (within this mach file) */ diff -r 45d7b2c7029d -r 52b4284cb496 src/os/bsd/vm/os_bsd.cpp --- a/src/os/bsd/vm/os_bsd.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/bsd/vm/os_bsd.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -123,12 +123,19 @@ #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) #define LARGEPAGES_BIT (1 << 6) + +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + //////////////////////////////////////////////////////////////////////////////// // global variables julong os::Bsd::_physical_memory = 0; - +#ifdef __APPLE__ +mach_timebase_info_data_t os::Bsd::_timebase_info = {0, 0}; +volatile uint64_t os::Bsd::_max_abstime = 0; +#else int (*os::Bsd::_clock_gettime)(clockid_t, struct timespec *) = NULL; +#endif pthread_t os::Bsd::_main_thread; int os::Bsd::_page_size = -1; @@ -219,7 +226,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 @@ -306,9 +313,6 @@ #endif void os::init_system_properties_values() { -// char arch[12]; -// sysinfo(SI_ARCHITECTURE, arch, sizeof(arch)); - // The next steps are taken in the product version: // // Obtain the JAVA_HOME value from the location of libjvm.so. @@ -335,199 +339,205 @@ // Important note: if the location of libjvm.so changes this // code needs to be changed accordingly. - // The next few definitions allow the code to be verbatim: -#define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n), mtInternal) -#define getenv(n) ::getenv(n) - -/* - * See ld(1): - * The linker uses the following search paths to locate required - * shared libraries: - * 1: ... - * ... - * 7: The default directories, normally /lib and /usr/lib. - */ +// See ld(1): +// The linker uses the following search paths to locate required +// shared libraries: +// 1: ... +// ... +// 7: The default directories, normally /lib and /usr/lib. #ifndef DEFAULT_LIBPATH #define DEFAULT_LIBPATH "/lib:/usr/lib" #endif +// Base path of extensions installed on the system. +#define SYS_EXT_DIR "/usr/java/packages" #define EXTENSIONS_DIR "/lib/ext" #define ENDORSED_DIR "/lib/endorsed" -#define REG_DIR "/usr/java/packages" - -#ifdef __APPLE__ + +#ifndef __APPLE__ + + // Buffer that fits several sprintfs. + // Note that the space for the colon and the trailing null are provided + // by the nulls included by the sizeof operator. + const size_t bufsize = + MAX3((size_t)MAXPATHLEN, // For dll_dir & friends. + (size_t)MAXPATHLEN + sizeof(EXTENSIONS_DIR) + sizeof(SYS_EXT_DIR) + sizeof(EXTENSIONS_DIR), // extensions dir + (size_t)MAXPATHLEN + sizeof(ENDORSED_DIR)); // endorsed dir + char *buf = (char *)NEW_C_HEAP_ARRAY(char, bufsize, mtInternal); + + // sysclasspath, java_home, dll_dir + { + char *pslash; + os::jvm_path(buf, bufsize); + + // 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}. + } + Arguments::set_dll_dir(buf); + + 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. + } + } + } + Arguments::set_java_home(buf); + set_boot_path('/', ':'); + } + + // Where to look for native libraries. + // + // Note: Due to a legacy implementation, most of the library path + // is set in the launcher. This was to accomodate linking restrictions + // on legacy Bsd implementations (which are no longer supported). + // Eventually, all the library path setting will be done here. + // + // However, to prevent the proliferation of improperly built native + // libraries, the new path component /usr/java/packages is added here. + // Eventually, all the library path setting will be done here. + { + // Get the user setting of LD_LIBRARY_PATH, and prepended it. It + // should always exist (until the legacy problem cited above is + // addressed). + const char *v = ::getenv("LD_LIBRARY_PATH"); + const char *v_colon = ":"; + if (v == NULL) { v = ""; v_colon = ""; } + // That's +1 for the colon and +1 for the trailing '\0'. + char *ld_library_path = (char *)NEW_C_HEAP_ARRAY(char, + strlen(v) + 1 + + sizeof(SYS_EXT_DIR) + sizeof("/lib/") + strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH) + 1, + mtInternal); + sprintf(ld_library_path, "%s%s" SYS_EXT_DIR "/lib/%s:" DEFAULT_LIBPATH, v, v_colon, cpu_arch); + Arguments::set_library_path(ld_library_path); + FREE_C_HEAP_ARRAY(char, ld_library_path, mtInternal); + } + + // Extensions directories. + sprintf(buf, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + + // Endorsed standards default directory. + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + +#else // __APPLE__ + #define SYS_EXTENSIONS_DIR "/Library/Java/Extensions" #define SYS_EXTENSIONS_DIRS SYS_EXTENSIONS_DIR ":/Network" SYS_EXTENSIONS_DIR ":/System" SYS_EXTENSIONS_DIR ":/usr/lib/java" - const char *user_home_dir = get_home(); - // the null in SYS_EXTENSIONS_DIRS counts for the size of the colon after user_home_dir - int system_ext_size = strlen(user_home_dir) + sizeof(SYS_EXTENSIONS_DIR) + - sizeof(SYS_EXTENSIONS_DIRS); -#endif - + + const char *user_home_dir = get_home(); + // The null in SYS_EXTENSIONS_DIRS counts for the size of the colon after user_home_dir. + size_t system_ext_size = strlen(user_home_dir) + sizeof(SYS_EXTENSIONS_DIR) + + sizeof(SYS_EXTENSIONS_DIRS); + + // Buffer that fits several sprintfs. + // Note that the space for the colon and the trailing null are provided + // by the nulls included by the sizeof operator. + const size_t bufsize = + MAX3((size_t)MAXPATHLEN, // for dll_dir & friends. + (size_t)MAXPATHLEN + sizeof(EXTENSIONS_DIR) + system_ext_size, // extensions dir + (size_t)MAXPATHLEN + sizeof(ENDORSED_DIR)); // endorsed dir + char *buf = (char *)NEW_C_HEAP_ARRAY(char, bufsize, mtInternal); + + // sysclasspath, java_home, dll_dir { - /* 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); - if (dll_path == NULL) - return; - strcpy(dll_path, buf); - Arguments::set_dll_dir(dll_path); - - if (pslash != NULL) { - pslash = strrchr(buf, '/'); - if (pslash != NULL) { - *pslash = '\0'; /* get rid of / (/lib on macosx) */ -#ifndef __APPLE__ - pslash = strrchr(buf, '/'); - if (pslash != NULL) - *pslash = '\0'; /* get rid of /lib */ -#endif - } - } - - home_path = malloc(strlen(buf) + 1); - if (home_path == NULL) - return; - strcpy(home_path, buf); - Arguments::set_java_home(home_path); - - if (!set_boot_path('/', ':')) - return; + char *pslash; + os::jvm_path(buf, bufsize); + + // 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}. + } + Arguments::set_dll_dir(buf); + + if (pslash != NULL) { + pslash = strrchr(buf, '/'); + if (pslash != NULL) { + *pslash = '\0'; // Get rid of /lib. + } } - - /* - * Where to look for native libraries - * - * Note: Due to a legacy implementation, most of the library path - * is set in the launcher. This was to accomodate linking restrictions - * on legacy Bsd implementations (which are no longer supported). - * Eventually, all the library path setting will be done here. - * - * However, to prevent the proliferation of improperly built native - * libraries, the new path component /usr/java/packages is added here. - * Eventually, all the library path setting will be done here. - */ - { - char *ld_library_path; - - /* - * Construct the invariant part of ld_library_path. Note that the - * space for the colon and the trailing null are provided by the - * nulls included by the sizeof operator (so actually we allocate - * a byte more than necessary). - */ -#ifdef __APPLE__ - ld_library_path = (char *) malloc(system_ext_size); - sprintf(ld_library_path, "%s" SYS_EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS, user_home_dir); -#else - ld_library_path = (char *) malloc(sizeof(REG_DIR) + sizeof("/lib/") + - strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH)); - sprintf(ld_library_path, REG_DIR "/lib/%s:" DEFAULT_LIBPATH, cpu_arch); -#endif - - /* - * Get the user setting of LD_LIBRARY_PATH, and prepended it. It - * should always exist (until the legacy problem cited above is - * addressed). - */ -#ifdef __APPLE__ - // Prepend the default path with the JAVA_LIBRARY_PATH so that the app launcher code can specify a directory inside an app wrapper - char *l = getenv("JAVA_LIBRARY_PATH"); - if (l != NULL) { - char *t = ld_library_path; - /* That's +1 for the colon and +1 for the trailing '\0' */ - ld_library_path = (char *) malloc(strlen(l) + 1 + strlen(t) + 1); - sprintf(ld_library_path, "%s:%s", l, t); - free(t); - } - - char *v = getenv("DYLD_LIBRARY_PATH"); -#else - char *v = getenv("LD_LIBRARY_PATH"); -#endif - if (v != NULL) { - 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); - free(t); - } - -#ifdef __APPLE__ - // Apple's Java6 has "." at the beginning of java.library.path. - // OpenJDK on Windows has "." at the end of java.library.path. - // OpenJDK on Linux and Solaris don't have "." in java.library.path - // at all. To ease the transition from Apple's Java6 to OpenJDK7, - // "." is appended to the end of java.library.path. Yes, this - // could cause a change in behavior, but Apple's Java6 behavior - // can be achieved by putting "." at the beginning of the - // JAVA_LIBRARY_PATH environment variable. - { - char *t = ld_library_path; - // that's +3 for appending ":." and the trailing '\0' - ld_library_path = (char *) malloc(strlen(t) + 3); - sprintf(ld_library_path, "%s:%s", t, "."); - free(t); - } -#endif - - Arguments::set_library_path(ld_library_path); - } - - /* - * Extensions directories. - * - * Note that the space for the colon and the trailing null are provided - * by the nulls included by the sizeof operator (so actually one byte more - * than necessary is allocated). - */ - { -#ifdef __APPLE__ - char *buf = malloc(strlen(Arguments::get_java_home()) + - sizeof(EXTENSIONS_DIR) + system_ext_size); - sprintf(buf, "%s" SYS_EXTENSIONS_DIR ":%s" EXTENSIONS_DIR ":" - SYS_EXTENSIONS_DIRS, user_home_dir, Arguments::get_java_home()); -#else - char *buf = malloc(strlen(Arguments::get_java_home()) + - sizeof(EXTENSIONS_DIR) + sizeof(REG_DIR) + sizeof(EXTENSIONS_DIR)); - sprintf(buf, "%s" EXTENSIONS_DIR ":" REG_DIR EXTENSIONS_DIR, - Arguments::get_java_home()); -#endif - - Arguments::set_ext_dirs(buf); - } - - /* Endorsed standards default directory. */ - { - char * buf; - buf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); - sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); - Arguments::set_endorsed_dirs(buf); - } + Arguments::set_java_home(buf); + set_boot_path('/', ':'); } -#ifdef __APPLE__ + // Where to look for native libraries. + // + // Note: Due to a legacy implementation, most of the library path + // is set in the launcher. This was to accomodate linking restrictions + // on legacy Bsd implementations (which are no longer supported). + // Eventually, all the library path setting will be done here. + // + // However, to prevent the proliferation of improperly built native + // libraries, the new path component /usr/java/packages is added here. + // Eventually, all the library path setting will be done here. + { + // Get the user setting of LD_LIBRARY_PATH, and prepended it. It + // should always exist (until the legacy problem cited above is + // addressed). + // Prepend the default path with the JAVA_LIBRARY_PATH so that the app launcher code + // can specify a directory inside an app wrapper + const char *l = ::getenv("JAVA_LIBRARY_PATH"); + const char *l_colon = ":"; + if (l == NULL) { l = ""; l_colon = ""; } + + const char *v = ::getenv("DYLD_LIBRARY_PATH"); + const char *v_colon = ":"; + if (v == NULL) { v = ""; v_colon = ""; } + + // Apple's Java6 has "." at the beginning of java.library.path. + // OpenJDK on Windows has "." at the end of java.library.path. + // OpenJDK on Linux and Solaris don't have "." in java.library.path + // at all. To ease the transition from Apple's Java6 to OpenJDK7, + // "." is appended to the end of java.library.path. Yes, this + // could cause a change in behavior, but Apple's Java6 behavior + // can be achieved by putting "." at the beginning of the + // JAVA_LIBRARY_PATH environment variable. + char *ld_library_path = (char *)NEW_C_HEAP_ARRAY(char, + strlen(v) + 1 + strlen(l) + 1 + + system_ext_size + 3, + mtInternal); + sprintf(ld_library_path, "%s%s%s%s%s" SYS_EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS ":.", + v, v_colon, l, l_colon, user_home_dir); + Arguments::set_library_path(ld_library_path); + FREE_C_HEAP_ARRAY(char, ld_library_path, mtInternal); + } + + // Extensions directories. + // + // Note that the space for the colon and the trailing null are provided + // by the nulls included by the sizeof operator (so actually one byte more + // than necessary is allocated). + sprintf(buf, "%s" SYS_EXTENSIONS_DIR ":%s" EXTENSIONS_DIR ":" SYS_EXTENSIONS_DIRS, + user_home_dir, Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + + // Endorsed standards default directory. + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + #undef SYS_EXTENSIONS_DIR -#endif -#undef malloc -#undef getenv +#undef SYS_EXTENSIONS_DIRS + +#endif // __APPLE__ + +#undef SYS_EXT_DIR #undef EXTENSIONS_DIR #undef ENDORSED_DIR - - // Done - return; } //////////////////////////////////////////////////////////////////////////////// @@ -914,9 +924,20 @@ ////////////////////////////////////////////////////////////////////////////// // thread local storage +// Restore the thread pointer if the destructor is called. This is in case +// someone from JNI code sets up a destructor with pthread_key_create to run +// detachCurrentThread on thread death. Unless we restore the thread pointer we +// will hang or crash. When detachCurrentThread is called the key will be set +// to null and we will not be called again. If detachCurrentThread is never +// called we could loop forever depending on the pthread implementation. +static void restore_thread_pointer(void* p) { + Thread* thread = (Thread*) p; + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); +} + int os::allocate_thread_local_storage() { pthread_key_t key; - int rslt = pthread_key_create(&key, NULL); + int rslt = pthread_key_create(&key, restore_thread_pointer); assert(rslt == 0, "cannot allocate thread local storage"); return (int)key; } @@ -972,13 +993,15 @@ return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000); } +#ifndef __APPLE__ #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC (1) #endif +#endif #ifdef __APPLE__ void os::Bsd::clock_init() { - // XXXDARWIN: Investigate replacement monotonic clock + mach_timebase_info(&_timebase_info); } #else void os::Bsd::clock_init() { @@ -993,10 +1016,38 @@ #endif +#ifdef __APPLE__ + +jlong os::javaTimeNanos() { + const uint64_t tm = mach_absolute_time(); + const uint64_t now = (tm * Bsd::_timebase_info.numer) / Bsd::_timebase_info.denom; + const uint64_t prev = Bsd::_max_abstime; + if (now <= prev) { + return prev; // same or retrograde time; + } + const uint64_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&Bsd::_max_abstime, prev); + assert(obsv >= prev, "invariant"); // Monotonicity + // If the CAS succeeded then we're done and return "now". + // If the CAS failed and the observed value "obsv" is >= now then + // we should return "obsv". If the CAS failed and now > obsv > prv then + // some other thread raced this thread and installed a new value, in which case + // we could either (a) retry the entire operation, (b) retry trying to install now + // or (c) just return obsv. We use (c). No loop is required although in some cases + // we might discard a higher "now" value in deference to a slightly lower but freshly + // installed obsv value. That's entirely benign -- it admits no new orderings compared + // to (a) or (b) -- and greatly reduces coherence traffic. + // We might also condition (c) on the magnitude of the delta between obsv and now. + // Avoiding excessive CAS operations to hot RW locations is critical. + // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate + return (prev == obsv) ? now : obsv; +} + +#else // __APPLE__ + jlong os::javaTimeNanos() { if (Bsd::supports_monotonic_clock()) { struct timespec tp; - int status = Bsd::clock_gettime(CLOCK_MONOTONIC, &tp); + int status = Bsd::_clock_gettime(CLOCK_MONOTONIC, &tp); assert(status == 0, "gettime error"); jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec); return result; @@ -1009,6 +1060,8 @@ } } +#endif // __APPLE__ + void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { if (Bsd::supports_monotonic_clock()) { info_ptr->max_value = ALL_64_BITS; @@ -1553,6 +1606,17 @@ } #endif /* !__APPLE__ */ +void* os::get_default_process_handle() { +#ifdef __APPLE__ + // MacOS X needs to use RTLD_FIRST instead of RTLD_LAZY + // to avoid finding unexpected symbols on second (or later) + // loads of a library. + return (void*)::dlopen(NULL, RTLD_FIRST); +#else + return (void*)::dlopen(NULL, RTLD_LAZY); +#endif +} + // XXX: Do we need a lock around this as per Linux? void* os::dll_lookup(void* handle, const char* name) { return dlsym(handle, name); @@ -1662,58 +1726,12 @@ st->cr(); } -// Taken from /usr/include/bits/siginfo.h Supposed to be architecture specific -// but they're the same for all the bsd arch that we support -// and they're the same for solaris but there's no common place to put this. -const char *ill_names[] = { "ILL0", "ILL_ILLOPC", "ILL_ILLOPN", "ILL_ILLADR", - "ILL_ILLTRP", "ILL_PRVOPC", "ILL_PRVREG", - "ILL_COPROC", "ILL_BADSTK" }; - -const char *fpe_names[] = { "FPE0", "FPE_INTDIV", "FPE_INTOVF", "FPE_FLTDIV", - "FPE_FLTOVF", "FPE_FLTUND", "FPE_FLTRES", - "FPE_FLTINV", "FPE_FLTSUB", "FPE_FLTDEN" }; - -const char *segv_names[] = { "SEGV0", "SEGV_MAPERR", "SEGV_ACCERR" }; - -const char *bus_names[] = { "BUS0", "BUS_ADRALN", "BUS_ADRERR", "BUS_OBJERR" }; - void os::print_siginfo(outputStream* st, void* siginfo) { - st->print("siginfo:"); - - const int buflen = 100; - char buf[buflen]; - siginfo_t *si = (siginfo_t*)siginfo; - st->print("si_signo=%s: ", os::exception_name(si->si_signo, buf, buflen)); - if (si->si_errno != 0 && strerror_r(si->si_errno, buf, buflen) == 0) { - st->print("si_errno=%s", buf); - } else { - st->print("si_errno=%d", si->si_errno); - } - const int c = si->si_code; - assert(c > 0, "unexpected si_code"); - switch (si->si_signo) { - case SIGILL: - st->print(", si_code=%d (%s)", c, c > 8 ? "" : ill_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGFPE: - st->print(", si_code=%d (%s)", c, c > 9 ? "" : fpe_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGSEGV: - st->print(", si_code=%d (%s)", c, c > 2 ? "" : segv_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGBUS: - st->print(", si_code=%d (%s)", c, c > 3 ? "" : bus_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - default: - st->print(", si_code=%d", si->si_code); - // no si_addr - } - - if ((si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + const siginfo_t* si = (const siginfo_t*)siginfo; + + os::Posix::print_siginfo_brief(st, si); + + if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && UseSharedSpaces) { FileMapInfo* mapinfo = FileMapInfo::current_info(); if (mapinfo->is_in_shared_space(si->si_addr)) { @@ -2372,7 +2390,6 @@ (!FLAG_IS_DEFAULT(UseLargePages) || !FLAG_IS_DEFAULT(LargePageSizeInBytes) ); - char msg[128]; // Create a large shared memory region to attach to based on size. // Currently, size is the total size of the heap @@ -2393,8 +2410,7 @@ // coalesce into large pages. Try to reserve large pages when // the system is still "fresh". if (warn_on_failure) { - jio_snprintf(msg, sizeof(msg), "Failed to reserve shared memory (errno = %d).", errno); - warning(msg); + warning("Failed to reserve shared memory (errno = %d).", errno); } return NULL; } @@ -2411,8 +2427,7 @@ if ((intptr_t)addr == -1) { if (warn_on_failure) { - jio_snprintf(msg, sizeof(msg), "Failed to attach shared memory (errno = %d).", err); - warning(msg); + warning("Failed to attach shared memory (errno = %d).", err); } return NULL; } @@ -2622,9 +2637,21 @@ } } -int os::naked_sleep() { - // %% make the sleep time an integer flag. for now use 1 millisec. - return os::sleep(Thread::current(), 1, false); +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 @@ -3232,7 +3259,7 @@ sigAct.sa_sigaction = signalHandler; sigAct.sa_flags = SA_SIGINFO|SA_RESTART; } -#if __APPLE__ +#ifdef __APPLE__ // Needed for main thread as XNU (Mac OS X kernel) will only deliver SIGSEGV // (which starts as SIGBUS) on main thread with faulting address inside "stack+guard pages" // if the signal handler declares it will handle it on alternate stack. @@ -3389,7 +3416,8 @@ st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); } - st->print(", sa_mask[0]=" PTR32_FORMAT, *(uint32_t*)&sa.sa_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? @@ -3398,7 +3426,8 @@ sa.sa_flags = VMError::get_resetted_sigflags(sig) & SIGNIFICANT_SIGNAL_MASK; } - st->print(", sa_flags=" PTR32_FORMAT, sa.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)signalHandler) || @@ -3902,6 +3931,7 @@ return true; } +ATTRIBUTE_PRINTF(3, 0) int local_vsnprintf(char* buf, size_t count, const char* format, va_list args) { return ::vsnprintf(buf, count, format, args); } diff -r 45d7b2c7029d -r 52b4284cb496 src/os/bsd/vm/os_bsd.hpp --- a/src/os/bsd/vm/os_bsd.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/bsd/vm/os_bsd.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 *); @@ -55,7 +58,13 @@ // For signal flags diagnostics static int sigflags[MAXSIGNUM]; +#ifdef __APPLE__ + // mach_absolute_time + static mach_timebase_info_data_t _timebase_info; + static volatile uint64_t _max_abstime; +#else static int (*_clock_gettime)(clockid_t, struct timespec *); +#endif static GrowableArray* _cpu_to_node; @@ -132,11 +141,11 @@ static void clock_init(void); static inline bool supports_monotonic_clock() { +#ifdef __APPLE__ + return true; +#else return _clock_gettime != NULL; - } - - static int clock_gettime(clockid_t clock_id, struct timespec *tp) { - return _clock_gettime ? _clock_gettime(clock_id, tp) : -1; +#endif } // Stack repair handling diff -r 45d7b2c7029d -r 52b4284cb496 src/os/bsd/vm/perfMemory_bsd.cpp --- a/src/os/bsd/vm/perfMemory_bsd.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/bsd/vm/perfMemory_bsd.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -925,7 +925,7 @@ if (PerfTraceMemOps) { tty->print("mapped " SIZE_FORMAT " bytes for vmid %d at " - INTPTR_FORMAT "\n", size, vmid, (void*)mapAddress); + INTPTR_FORMAT "\n", size, vmid, p2i((void*)mapAddress)); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/os/linux/vm/decoder_linux.cpp --- a/src/os/linux/vm/decoder_linux.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/linux/vm/decoder_linux.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/os/linux/vm/os_linux.cpp --- a/src/os/linux/vm/os_linux.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/linux/vm/os_linux.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -101,6 +101,8 @@ # include # include +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // if RUSAGE_THREAD for getrusage() has not been defined, do it here. The code calling // getrusage() is prepared to handle the associated failure. #ifndef RUSAGE_THREAD @@ -109,6 +111,8 @@ #define MAX_PATH (2 * K) +#define MAX_SECS 100000000 + // for timer info max values which include all bits #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) @@ -139,7 +143,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; @@ -257,8 +261,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"; @@ -315,9 +321,6 @@ } void os::init_system_properties_values() { -// char arch[12]; -// sysinfo(SI_ARCHITECTURE, arch, sizeof(arch)); - // The next steps are taken in the product version: // // Obtain the JAVA_HOME value from the location of libjvm.so. @@ -344,140 +347,101 @@ // Important note: if the location of libjvm.so changes this // code needs to be changed accordingly. - // The next few definitions allow the code to be verbatim: -#define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n), mtInternal) -#define getenv(n) ::getenv(n) - -/* - * See ld(1): - * The linker uses the following search paths to locate required - * shared libraries: - * 1: ... - * ... - * 7: The default directories, normally /lib and /usr/lib. - */ +// See ld(1): +// The linker uses the following search paths to locate required +// shared libraries: +// 1: ... +// ... +// 7: The default directories, normally /lib and /usr/lib. #if defined(AMD64) || defined(_LP64) && (defined(SPARC) || defined(PPC) || defined(S390)) #define DEFAULT_LIBPATH "/usr/lib64:/lib64:/lib:/usr/lib" #else #define DEFAULT_LIBPATH "/lib:/usr/lib" #endif +// Base path of extensions installed on the system. +#define SYS_EXT_DIR "/usr/java/packages" #define EXTENSIONS_DIR "/lib/ext" #define ENDORSED_DIR "/lib/endorsed" -#define REG_DIR "/usr/java/packages" - + + // Buffer that fits several sprintfs. + // Note that the space for the colon and the trailing null are provided + // by the nulls included by the sizeof operator. + const size_t bufsize = + MAX3((size_t)MAXPATHLEN, // For dll_dir & friends. + (size_t)MAXPATHLEN + sizeof(EXTENSIONS_DIR) + sizeof(SYS_EXT_DIR) + sizeof(EXTENSIONS_DIR), // extensions dir + (size_t)MAXPATHLEN + sizeof(ENDORSED_DIR)); // endorsed dir + char *buf = (char *)NEW_C_HEAP_ARRAY(char, bufsize, mtInternal); + + // sysclasspath, java_home, dll_dir { - /* 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 */ + char *pslash; + os::jvm_path(buf, bufsize); + + // 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}. + } + Arguments::set_dll_dir(buf); + + 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 /{client|server|hotspot} */ - dll_path = malloc(strlen(buf) + 1); - if (dll_path == NULL) - return; - 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 */ - } + *pslash = '\0'; // Get rid of /lib. } - - home_path = malloc(strlen(buf) + 1); - if (home_path == NULL) - return; - strcpy(home_path, buf); - Arguments::set_java_home(home_path); - - if (!set_boot_path('/', ':')) - return; + } } - - /* - * Where to look for native libraries - * - * Note: Due to a legacy implementation, most of the library path - * is set in the launcher. This was to accomodate linking restrictions - * on legacy Linux implementations (which are no longer supported). - * Eventually, all the library path setting will be done here. - * - * However, to prevent the proliferation of improperly built native - * libraries, the new path component /usr/java/packages is added here. - * Eventually, all the library path setting will be done here. - */ - { - char *ld_library_path; - - /* - * Construct the invariant part of ld_library_path. Note that the - * space for the colon and the trailing null are provided by the - * nulls included by the sizeof operator (so actually we allocate - * a byte more than necessary). - */ - ld_library_path = (char *) malloc(sizeof(REG_DIR) + sizeof("/lib/") + - strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH)); - sprintf(ld_library_path, REG_DIR "/lib/%s:" DEFAULT_LIBPATH, cpu_arch); - - /* - * Get the user setting of LD_LIBRARY_PATH, and prepended it. It - * should always exist (until the legacy problem cited above is - * addressed). - */ - char *v = getenv("LD_LIBRARY_PATH"); - if (v != NULL) { - 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. - * - * Note that the space for the colon and the trailing null are provided - * by the nulls included by the sizeof operator (so actually one byte more - * than necessary is allocated). - */ - { - char *buf = malloc(strlen(Arguments::get_java_home()) + - sizeof(EXTENSIONS_DIR) + sizeof(REG_DIR) + sizeof(EXTENSIONS_DIR)); - sprintf(buf, "%s" EXTENSIONS_DIR ":" REG_DIR EXTENSIONS_DIR, - Arguments::get_java_home()); - Arguments::set_ext_dirs(buf); - } - - /* Endorsed standards default directory. */ - { - char * buf; - buf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); - sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); - Arguments::set_endorsed_dirs(buf); - } - } - -#undef malloc -#undef getenv + Arguments::set_java_home(buf); + set_boot_path('/', ':'); + } + + // Where to look for native libraries. + // + // Note: Due to a legacy implementation, most of the library path + // is set in the launcher. This was to accomodate linking restrictions + // on legacy Linux implementations (which are no longer supported). + // Eventually, all the library path setting will be done here. + // + // However, to prevent the proliferation of improperly built native + // libraries, the new path component /usr/java/packages is added here. + // Eventually, all the library path setting will be done here. + { + // Get the user setting of LD_LIBRARY_PATH, and prepended it. It + // should always exist (until the legacy problem cited above is + // addressed). + const char *v = ::getenv("LD_LIBRARY_PATH"); + const char *v_colon = ":"; + if (v == NULL) { v = ""; v_colon = ""; } + // That's +1 for the colon and +1 for the trailing '\0'. + char *ld_library_path = (char *)NEW_C_HEAP_ARRAY(char, + strlen(v) + 1 + + sizeof(SYS_EXT_DIR) + sizeof("/lib/") + strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH) + 1, + mtInternal); + sprintf(ld_library_path, "%s%s" SYS_EXT_DIR "/lib/%s:" DEFAULT_LIBPATH, v, v_colon, cpu_arch); + Arguments::set_library_path(ld_library_path); + FREE_C_HEAP_ARRAY(char, ld_library_path, mtInternal); + } + + // Extensions directories. + sprintf(buf, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + + // Endorsed standards default directory. + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + +#undef DEFAULT_LIBPATH +#undef SYS_EXT_DIR #undef EXTENSIONS_DIR #undef ENDORSED_DIR - - // Done - return; } //////////////////////////////////////////////////////////////////////////////// @@ -530,6 +494,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) { @@ -1067,9 +1034,20 @@ ////////////////////////////////////////////////////////////////////////////// // thread local storage +// Restore the thread pointer if the destructor is called. This is in case +// someone from JNI code sets up a destructor with pthread_key_create to run +// detachCurrentThread on thread death. Unless we restore the thread pointer we +// will hang or crash. When detachCurrentThread is called the key will be set +// to null and we will not be called again. If detachCurrentThread is never +// called we could loop forever depending on the pthread implementation. +static void restore_thread_pointer(void* p) { + Thread* thread = (Thread*) p; + os::thread_local_storage_at_put(ThreadLocalStorage::thread_index(), thread); +} + int os::allocate_thread_local_storage() { pthread_key_t key; - int rslt = pthread_key_create(&key, NULL); + int rslt = pthread_key_create(&key, restore_thread_pointer); assert(rslt == 0, "cannot allocate thread local storage"); return (int)key; } @@ -1953,7 +1931,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"}, @@ -2101,6 +2083,9 @@ return res; } +void* os::get_default_process_handle() { + return (void*)::dlopen(NULL, RTLD_LAZY); +} static bool _print_ascii_file(const char* filename, outputStream* st) { int fd = ::open(filename, O_RDONLY); @@ -2151,7 +2136,7 @@ // Print warning if unsafe chroot environment detected if (unsafe_chroot_detected) { st->print("WARNING!! "); - st->print_cr(unstable_chroot_error); + st->print_cr("%s", unstable_chroot_error); } os::Linux::print_libversion_info(st); @@ -2212,8 +2197,8 @@ void os::Linux::print_libversion_info(outputStream* st) { // libc, pthread st->print("libc:"); - st->print(os::Linux::glibc_version()); st->print(" "); - st->print(os::Linux::libpthread_version()); st->print(" "); + st->print("%s ", os::Linux::glibc_version()); + st->print("%s ", os::Linux::libpthread_version()); if (os::Linux::is_LinuxThreads()) { st->print("(%s stack)", os::Linux::is_floating_stack() ? "floating" : "fixed"); } @@ -2254,58 +2239,12 @@ st->cr(); } -// Taken from /usr/include/bits/siginfo.h Supposed to be architecture specific -// but they're the same for all the linux arch that we support -// and they're the same for solaris but there's no common place to put this. -const char *ill_names[] = { "ILL0", "ILL_ILLOPC", "ILL_ILLOPN", "ILL_ILLADR", - "ILL_ILLTRP", "ILL_PRVOPC", "ILL_PRVREG", - "ILL_COPROC", "ILL_BADSTK" }; - -const char *fpe_names[] = { "FPE0", "FPE_INTDIV", "FPE_INTOVF", "FPE_FLTDIV", - "FPE_FLTOVF", "FPE_FLTUND", "FPE_FLTRES", - "FPE_FLTINV", "FPE_FLTSUB", "FPE_FLTDEN" }; - -const char *segv_names[] = { "SEGV0", "SEGV_MAPERR", "SEGV_ACCERR" }; - -const char *bus_names[] = { "BUS0", "BUS_ADRALN", "BUS_ADRERR", "BUS_OBJERR" }; - void os::print_siginfo(outputStream* st, void* siginfo) { - st->print("siginfo:"); - - const int buflen = 100; - char buf[buflen]; - siginfo_t *si = (siginfo_t*)siginfo; - st->print("si_signo=%s: ", os::exception_name(si->si_signo, buf, buflen)); - if (si->si_errno != 0 && strerror_r(si->si_errno, buf, buflen) == 0) { - st->print("si_errno=%s", buf); - } else { - st->print("si_errno=%d", si->si_errno); - } - const int c = si->si_code; - assert(c > 0, "unexpected si_code"); - switch (si->si_signo) { - case SIGILL: - st->print(", si_code=%d (%s)", c, c > 8 ? "" : ill_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGFPE: - st->print(", si_code=%d (%s)", c, c > 9 ? "" : fpe_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGSEGV: - st->print(", si_code=%d (%s)", c, c > 2 ? "" : segv_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGBUS: - st->print(", si_code=%d (%s)", c, c > 3 ? "" : bus_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - default: - st->print(", si_code=%d", si->si_code); - // no si_addr - } - - if ((si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + const siginfo_t* si = (const siginfo_t*)siginfo; + + os::Posix::print_siginfo_brief(st, si); + + if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && UseSharedSpaces) { FileMapInfo* mapinfo = FileMapInfo::current_info(); if (mapinfo->is_in_shared_space(si->si_addr)) { @@ -2335,6 +2274,9 @@ print_signal_handler(st, SHUTDOWN2_SIGNAL , buf, buflen); print_signal_handler(st, SHUTDOWN3_SIGNAL , buf, buflen); print_signal_handler(st, BREAK_SIGNAL, buf, buflen); +#if defined(PPC64) + print_signal_handler(st, SIGTRAP, buf, buflen); +#endif } static char saved_jvm_path[MAXPATHLEN] = {0}; @@ -2466,7 +2408,6 @@ sem_t _semaphore; }; - Semaphore::Semaphore() { sem_init(&_semaphore, 0, 0); } @@ -2488,8 +2429,22 @@ } bool Semaphore::timedwait(unsigned int sec, int nsec) { + struct timespec ts; - unpackTime(&ts, false, (sec * NANOSECS_PER_SEC) + nsec); + // Semaphore's are always associated with CLOCK_REALTIME + os::Linux::clock_gettime(CLOCK_REALTIME, &ts); + // see unpackTime for discussion on overflow checking + if (sec >= MAX_SECS) { + ts.tv_sec += MAX_SECS; + ts.tv_nsec = 0; + } else { + ts.tv_sec += sec; + ts.tv_nsec += nsec; + if (ts.tv_nsec >= NANOSECS_PER_SEC) { + ts.tv_nsec -= NANOSECS_PER_SEC; + ++ts.tv_sec; // note: this must be <= max_secs + } + } while (1) { int result = sem_timedwait(&_semaphore, &ts); @@ -2994,7 +2949,9 @@ unsigned char vec[1]; unsigned imin = 1, imax = pages + 1, imid; - int mincore_return_value; + int mincore_return_value = 0; + + assert(imin <= imax, "Unexpected page size"); while (imin < imax) { imid = (imax + imin) / 2; @@ -3458,7 +3415,7 @@ // the system is still "fresh". if (warn_on_failure) { jio_snprintf(msg, sizeof(msg), "Failed to reserve shared memory (errno = %d).", errno); - warning(msg); + warning("%s", msg); } return NULL; } @@ -3476,7 +3433,7 @@ if ((intptr_t)addr == -1) { if (warn_on_failure) { jio_snprintf(msg, sizeof(msg), "Failed to attach shared memory (errno = %d).", err); - warning(msg); + warning("%s", msg); } return NULL; } @@ -3496,7 +3453,7 @@ char msg[128]; jio_snprintf(msg, sizeof(msg), "Failed to reserve large pages memory req_addr: " PTR_FORMAT " bytes: " SIZE_FORMAT " (errno = %d).", req_addr, bytes, error); - warning(msg); + warning("%s", msg); } } @@ -3866,9 +3823,33 @@ } } -int os::naked_sleep() { - // %% make the sleep time an integer flag. for now use 1 millisec. - return os::sleep(Thread::current(), 1, false); +// +// Short sleep, direct OS call. +// +// Note: certain versions of Linux CFS scheduler (since 2.6.23) do not guarantee +// sched_yield(2) will actually give up the CPU: +// +// * Alone on this pariticular CPU, keeps running. +// * Before the introduction of "skip_buddy" with "compat_yield" disabled +// (pre 2.6.39). +// +// So calling this with 0 is an alternative. +// +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 @@ -4465,6 +4446,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) { @@ -4558,7 +4542,8 @@ st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); } - st->print(", sa_mask[0]=" PTR32_FORMAT, *(uint32_t*)&sa.sa_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? @@ -4567,7 +4552,8 @@ sa.sa_flags = VMError::get_resetted_sigflags(sig) & SIGNIFICANT_SIGNAL_MASK; } - st->print(", sa_flags=" PTR32_FORMAT, sa.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)signalHandler) || @@ -4605,7 +4591,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 @@ -4927,7 +4915,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"); } } @@ -4938,8 +4926,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) { @@ -5410,6 +5397,8 @@ // -1 on error. // +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { static bool proc_task_unchecked = true; static const char *proc_stat_path = "/proc/%d/stat"; @@ -5471,6 +5460,7 @@ return (jlong)user_time * (1000000000 / clock_tics_per_sec); } } +PRAGMA_DIAG_POP 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 @@ -5784,7 +5774,6 @@ * 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. diff -r 45d7b2c7029d -r 52b4284cb496 src/os/linux/vm/os_linux.hpp --- a/src/os/linux/vm/os_linux.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/linux/vm/os_linux.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/os/linux/vm/perfMemory_linux.cpp --- a/src/os/linux/vm/perfMemory_linux.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/linux/vm/perfMemory_linux.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -891,8 +891,16 @@ 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"); + fd = open_sharedmem_file(rfilename, file_flags, THREAD); + + if (fd == OS_ERR) { + return; + } + + if (HAS_PENDING_EXCEPTION) { + ::close(fd); + return; + } if (*sizep == 0) { size = sharedmem_filesize(fd, CHECK); @@ -923,7 +931,7 @@ if (PerfTraceMemOps) { tty->print("mapped " SIZE_FORMAT " bytes for vmid %d at " - INTPTR_FORMAT "\n", size, vmid, (void*)mapAddress); + INTPTR_FORMAT "\n", size, vmid, p2i((void*)mapAddress)); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/os/posix/vm/os_posix.cpp --- a/src/os/posix/vm/os_posix.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/posix/vm/os_posix.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,38 +1,48 @@ /* -* 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 -* 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. -* -*/ +* Copyright (c) 1999, 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 + * 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/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 +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +// 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) { @@ -158,8 +168,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 +#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"); @@ -183,10 +193,10 @@ st->print("uname:"); struct utsname name; uname(&name); - st->print(name.sysname); st->print(" "); - st->print(name.release); st->print(" "); - st->print(name.version); st->print(" "); - st->print(name.machine); + st->print("%s ", name.sysname); + st->print("%s ", name.release); + st->print("%s ", name.version); + st->print("%s", name.machine); st->cr(); } @@ -262,10 +272,6 @@ return ::fdopen(fd, mode); } -void* os::get_default_process_handle() { - return (void*)::dlopen(NULL, RTLD_LAZY); -} - // Builds a platform dependent Agent_OnLoad_ function name // which is used to find statically linked in agents. // Parameters: @@ -311,6 +317,483 @@ return agent_entry_name; } +// 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("%s", 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("%s", 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"); } diff -r 45d7b2c7029d -r 52b4284cb496 src/os/posix/vm/os_posix.hpp --- a/src/os/posix/vm/os_posix.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/posix/vm/os_posix.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/os/solaris/vm/os_solaris.cpp --- a/src/os/solaris/vm/os_solaris.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/solaris/vm/os_solaris.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -415,11 +415,7 @@ static hrtime_t first_hrtime = 0; static const hrtime_t hrtime_hz = 1000*1000*1000; -const int LOCK_BUSY = 1; -const int LOCK_FREE = 0; -const int LOCK_INVALID = -1; static volatile hrtime_t max_hrtime = 0; -static volatile int max_hrtime_lock = LOCK_FREE; // Update counter with LSB as lock-in-progress void os::Solaris::initialize_system_info() { @@ -648,9 +644,6 @@ void os::init_system_properties_values() { - char arch[12]; - sysinfo(SI_ARCHITECTURE, arch, sizeof(arch)); - // The next steps are taken in the product version: // // Obtain the JAVA_HOME value from the location of libjvm.so. @@ -677,218 +670,174 @@ // Important note: if the location of libjvm.so changes this // code needs to be changed accordingly. - // The next few definitions allow the code to be verbatim: -#define malloc(n) (char*)NEW_C_HEAP_ARRAY(char, (n), mtInternal) -#define free(p) FREE_C_HEAP_ARRAY(char, p, mtInternal) -#define getenv(n) ::getenv(n) - +// Base path of extensions installed on the system. +#define SYS_EXT_DIR "/usr/jdk/packages" #define EXTENSIONS_DIR "/lib/ext" #define ENDORSED_DIR "/lib/endorsed" -#define COMMON_DIR "/usr/jdk/packages" - + + char cpu_arch[12]; + // Buffer that fits several sprintfs. + // Note that the space for the colon and the trailing null are provided + // by the nulls included by the sizeof operator. + const size_t bufsize = + MAX4((size_t)MAXPATHLEN, // For dll_dir & friends. + sizeof(SYS_EXT_DIR) + sizeof("/lib/") + strlen(cpu_arch), // invariant ld_library_path + (size_t)MAXPATHLEN + sizeof(EXTENSIONS_DIR) + sizeof(SYS_EXT_DIR) + sizeof(EXTENSIONS_DIR), // extensions dir + (size_t)MAXPATHLEN + sizeof(ENDORSED_DIR)); // endorsed dir + char *buf = (char *)NEW_C_HEAP_ARRAY(char, bufsize, mtInternal); + + // sysclasspath, java_home, dll_dir { - /* 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); - if (dll_path == NULL) - return; - 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); - if (home_path == NULL) - return; - strcpy(home_path, buf); - Arguments::set_java_home(home_path); - - if (!set_boot_path('/', ':')) - return; + char *pslash; + os::jvm_path(buf, bufsize); + + // 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}. } - - /* - * Where to look for native libraries - */ - { - // Use dlinfo() to determine the correct java.library.path. - // - // If we're launched by the Java launcher, and the user - // does not set java.library.path explicitly on the commandline, - // the Java launcher sets LD_LIBRARY_PATH for us and unsets - // LD_LIBRARY_PATH_32 and LD_LIBRARY_PATH_64. In this case - // dlinfo returns LD_LIBRARY_PATH + crle settings (including - // /usr/lib), which is exactly what we want. - // - // If the user does set java.library.path, it completely - // overwrites this setting, and always has. - // - // If we're not launched by the Java launcher, we may - // get here with any/all of the LD_LIBRARY_PATH[_32|64] - // settings. Again, dlinfo does exactly what we want. - - Dl_serinfo _info, *info = &_info; - Dl_serpath *path; - char* library_path; - char *common_path; - int i; - - // determine search path count and required buffer size - if (dlinfo(RTLD_SELF, RTLD_DI_SERINFOSIZE, (void *)info) == -1) { - vm_exit_during_initialization("dlinfo SERINFOSIZE request", dlerror()); - } - - // allocate new buffer and initialize - info = (Dl_serinfo*)malloc(_info.dls_size); - if (info == NULL) { - vm_exit_out_of_memory(_info.dls_size, OOM_MALLOC_ERROR, - "init_system_properties_values info"); - } - info->dls_size = _info.dls_size; - info->dls_cnt = _info.dls_cnt; - - // obtain search path information - if (dlinfo(RTLD_SELF, RTLD_DI_SERINFO, (void *)info) == -1) { - free(info); - vm_exit_during_initialization("dlinfo SERINFO request", dlerror()); + Arguments::set_dll_dir(buf); + + 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. + } } - - path = &info->dls_serpath[0]; - - // Note: Due to a legacy implementation, most of the library path - // is set in the launcher. This was to accomodate linking restrictions - // on legacy Solaris implementations (which are no longer supported). - // Eventually, all the library path setting will be done here. - // - // However, to prevent the proliferation of improperly built native - // libraries, the new path component /usr/jdk/packages is added here. - - // Determine the actual CPU architecture. - char cpu_arch[12]; - sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof(cpu_arch)); + } + Arguments::set_java_home(buf); + set_boot_path('/', ':'); + } + + // Where to look for native libraries. + { + // Use dlinfo() to determine the correct java.library.path. + // + // If we're launched by the Java launcher, and the user + // does not set java.library.path explicitly on the commandline, + // the Java launcher sets LD_LIBRARY_PATH for us and unsets + // LD_LIBRARY_PATH_32 and LD_LIBRARY_PATH_64. In this case + // dlinfo returns LD_LIBRARY_PATH + crle settings (including + // /usr/lib), which is exactly what we want. + // + // If the user does set java.library.path, it completely + // overwrites this setting, and always has. + // + // If we're not launched by the Java launcher, we may + // get here with any/all of the LD_LIBRARY_PATH[_32|64] + // settings. Again, dlinfo does exactly what we want. + + Dl_serinfo info_sz, *info = &info_sz; + Dl_serpath *path; + char *library_path; + char *common_path = buf; + + // Determine search path count and required buffer size. + if (dlinfo(RTLD_SELF, RTLD_DI_SERINFOSIZE, (void *)info) == -1) { + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + vm_exit_during_initialization("dlinfo SERINFOSIZE request", dlerror()); + } + + // Allocate new buffer and initialize. + info = (Dl_serinfo*)NEW_C_HEAP_ARRAY(char, info_sz.dls_size, mtInternal); + info->dls_size = info_sz.dls_size; + info->dls_cnt = info_sz.dls_cnt; + + // Obtain search path information. + if (dlinfo(RTLD_SELF, RTLD_DI_SERINFO, (void *)info) == -1) { + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + FREE_C_HEAP_ARRAY(char, info, mtInternal); + vm_exit_during_initialization("dlinfo SERINFO request", dlerror()); + } + + path = &info->dls_serpath[0]; + + // Note: Due to a legacy implementation, most of the library path + // is set in the launcher. This was to accomodate linking restrictions + // on legacy Solaris implementations (which are no longer supported). + // Eventually, all the library path setting will be done here. + // + // However, to prevent the proliferation of improperly built native + // libraries, the new path component /usr/jdk/packages is added here. + + // Determine the actual CPU architecture. + sysinfo(SI_ARCHITECTURE, cpu_arch, sizeof(cpu_arch)); #ifdef _LP64 - // If we are a 64-bit vm, perform the following translations: - // sparc -> sparcv9 - // i386 -> amd64 - if (strcmp(cpu_arch, "sparc") == 0) - strcat(cpu_arch, "v9"); - else if (strcmp(cpu_arch, "i386") == 0) - strcpy(cpu_arch, "amd64"); + // If we are a 64-bit vm, perform the following translations: + // sparc -> sparcv9 + // i386 -> amd64 + if (strcmp(cpu_arch, "sparc") == 0) { + strcat(cpu_arch, "v9"); + } else if (strcmp(cpu_arch, "i386") == 0) { + strcpy(cpu_arch, "amd64"); + } #endif - // Construct the invariant part of ld_library_path. Note that the - // space for the colon and the trailing null are provided by the - // nulls included by the sizeof operator. - size_t bufsize = sizeof(COMMON_DIR) + sizeof("/lib/") + strlen(cpu_arch); - common_path = malloc(bufsize); - if (common_path == NULL) { - free(info); - vm_exit_out_of_memory(bufsize, OOM_MALLOC_ERROR, - "init_system_properties_values common_path"); - } - sprintf(common_path, COMMON_DIR "/lib/%s", cpu_arch); - - // struct size is more than sufficient for the path components obtained - // through the dlinfo() call, so only add additional space for the path - // components explicitly added here. - bufsize = info->dls_size + strlen(common_path); - library_path = malloc(bufsize); - if (library_path == NULL) { - free(info); - free(common_path); - vm_exit_out_of_memory(bufsize, OOM_MALLOC_ERROR, - "init_system_properties_values library_path"); + // Construct the invariant part of ld_library_path. + sprintf(common_path, SYS_EXT_DIR "/lib/%s", cpu_arch); + + // Struct size is more than sufficient for the path components obtained + // through the dlinfo() call, so only add additional space for the path + // components explicitly added here. + size_t library_path_size = info->dls_size + strlen(common_path); + library_path = (char *)NEW_C_HEAP_ARRAY(char, library_path_size, mtInternal); + library_path[0] = '\0'; + + // Construct the desired Java library path from the linker's library + // search path. + // + // For compatibility, it is optimal that we insert the additional path + // components specific to the Java VM after those components specified + // in LD_LIBRARY_PATH (if any) but before those added by the ld.so + // infrastructure. + if (info->dls_cnt == 0) { // Not sure this can happen, but allow for it. + strcpy(library_path, common_path); + } else { + int inserted = 0; + int i; + for (i = 0; i < info->dls_cnt; i++, path++) { + uint_t flags = path->dls_flags & LA_SER_MASK; + if (((flags & LA_SER_LIBPATH) == 0) && !inserted) { + strcat(library_path, common_path); + strcat(library_path, os::path_separator()); + inserted = 1; + } + strcat(library_path, path->dls_name); + strcat(library_path, os::path_separator()); } - library_path[0] = '\0'; - - // Construct the desired Java library path from the linker's library - // search path. - // - // For compatibility, it is optimal that we insert the additional path - // components specific to the Java VM after those components specified - // in LD_LIBRARY_PATH (if any) but before those added by the ld.so - // infrastructure. - if (info->dls_cnt == 0) { // Not sure this can happen, but allow for it - strcpy(library_path, common_path); - } else { - int inserted = 0; - for (i = 0; i < info->dls_cnt; i++, path++) { - uint_t flags = path->dls_flags & LA_SER_MASK; - if (((flags & LA_SER_LIBPATH) == 0) && !inserted) { - strcat(library_path, common_path); - strcat(library_path, os::path_separator()); - inserted = 1; - } - strcat(library_path, path->dls_name); - strcat(library_path, os::path_separator()); - } - // eliminate trailing path separator - library_path[strlen(library_path)-1] = '\0'; - } - - // happens before argument parsing - can't use a trace flag - // tty->print_raw("init_system_properties_values: native lib path: "); - // tty->print_raw_cr(library_path); - - // callee copies into its own buffer - Arguments::set_library_path(library_path); - - free(common_path); - free(library_path); - free(info); + // Eliminate trailing path separator. + library_path[strlen(library_path)-1] = '\0'; } - /* - * Extensions directories. - * - * Note that the space for the colon and the trailing null are provided - * by the nulls included by the sizeof operator (so actually one byte more - * than necessary is allocated). - */ - { - char *buf = (char *) malloc(strlen(Arguments::get_java_home()) + - sizeof(EXTENSIONS_DIR) + sizeof(COMMON_DIR) + - sizeof(EXTENSIONS_DIR)); - sprintf(buf, "%s" EXTENSIONS_DIR ":" COMMON_DIR EXTENSIONS_DIR, - Arguments::get_java_home()); - Arguments::set_ext_dirs(buf); - } - - /* Endorsed standards default directory. */ - { - char * buf = malloc(strlen(Arguments::get_java_home()) + sizeof(ENDORSED_DIR)); - sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); - Arguments::set_endorsed_dirs(buf); - } - } - -#undef malloc -#undef free -#undef getenv + // happens before argument parsing - can't use a trace flag + // tty->print_raw("init_system_properties_values: native lib path: "); + // tty->print_raw_cr(library_path); + + // Callee copies into its own buffer. + Arguments::set_library_path(library_path); + + FREE_C_HEAP_ARRAY(char, library_path, mtInternal); + FREE_C_HEAP_ARRAY(char, info, mtInternal); + } + + // Extensions directories. + sprintf(buf, "%s" EXTENSIONS_DIR ":" SYS_EXT_DIR EXTENSIONS_DIR, Arguments::get_java_home()); + Arguments::set_ext_dirs(buf); + + // Endorsed standards default directory. + sprintf(buf, "%s" ENDORSED_DIR, Arguments::get_java_home()); + Arguments::set_endorsed_dirs(buf); + + FREE_C_HEAP_ARRAY(char, buf, mtInternal); + +#undef SYS_EXT_DIR #undef EXTENSIONS_DIR #undef ENDORSED_DIR -#undef COMMON_DIR - } void os::breakpoint() { @@ -1581,58 +1530,31 @@ } -// gethrtime can move backwards if read from one cpu and then a different cpu -// getTimeNanos is guaranteed to not move backward on Solaris -// local spinloop created as faster for a CAS on an int than -// a CAS on a 64bit jlong. Also Atomic::cmpxchg for jlong is not -// supported on sparc v8 or pre supports_cx8 intel boxes. -// oldgetTimeNanos for systems which do not support CAS on 64bit jlong -// i.e. sparc v8 and pre supports_cx8 (i486) intel boxes -inline hrtime_t oldgetTimeNanos() { - int gotlock = LOCK_INVALID; - hrtime_t newtime = gethrtime(); - - for (;;) { -// grab lock for max_hrtime - int curlock = max_hrtime_lock; - if (curlock & LOCK_BUSY) continue; - if (gotlock = Atomic::cmpxchg(LOCK_BUSY, &max_hrtime_lock, LOCK_FREE) != LOCK_FREE) continue; - if (newtime > max_hrtime) { - max_hrtime = newtime; - } else { - newtime = max_hrtime; - } - // release lock - max_hrtime_lock = LOCK_FREE; - return newtime; - } -} -// gethrtime can move backwards if read from one cpu and then a different cpu -// getTimeNanos is guaranteed to not move backward on Solaris +// gethrtime() should be monotonic according to the documentation, +// but some virtualized platforms are known to break this guarantee. +// getTimeNanos() must be guaranteed not to move backwards, so we +// are forced to add a check here. inline hrtime_t getTimeNanos() { - if (VM_Version::supports_cx8()) { - const hrtime_t now = gethrtime(); - // Use atomic long load since 32-bit x86 uses 2 registers to keep long. - const hrtime_t prev = Atomic::load((volatile jlong*)&max_hrtime); - if (now <= prev) return prev; // same or retrograde time; - const hrtime_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&max_hrtime, prev); - assert(obsv >= prev, "invariant"); // Monotonicity - // If the CAS succeeded then we're done and return "now". - // If the CAS failed and the observed value "obs" is >= now then - // we should return "obs". If the CAS failed and now > obs > prv then - // some other thread raced this thread and installed a new value, in which case - // we could either (a) retry the entire operation, (b) retry trying to install now - // or (c) just return obs. We use (c). No loop is required although in some cases - // we might discard a higher "now" value in deference to a slightly lower but freshly - // installed obs value. That's entirely benign -- it admits no new orderings compared - // to (a) or (b) -- and greatly reduces coherence traffic. - // We might also condition (c) on the magnitude of the delta between obs and now. - // Avoiding excessive CAS operations to hot RW locations is critical. - // See http://blogs.sun.com/dave/entry/cas_and_cache_trivia_invalidate - return (prev == obsv) ? now : obsv ; - } else { - return oldgetTimeNanos(); - } + const hrtime_t now = gethrtime(); + const hrtime_t prev = max_hrtime; + if (now <= prev) { + return prev; // same or retrograde time; + } + const hrtime_t obsv = Atomic::cmpxchg(now, (volatile jlong*)&max_hrtime, prev); + assert(obsv >= prev, "invariant"); // Monotonicity + // If the CAS succeeded then we're done and return "now". + // If the CAS failed and the observed value "obsv" is >= now then + // we should return "obsv". If the CAS failed and now > obsv > prv then + // some other thread raced this thread and installed a new value, in which case + // we could either (a) retry the entire operation, (b) retry trying to install now + // or (c) just return obsv. We use (c). No loop is required although in some cases + // we might discard a higher "now" value in deference to a slightly lower but freshly + // installed obsv value. That's entirely benign -- it admits no new orderings compared + // to (a) or (b) -- and greatly reduces coherence traffic. + // We might also condition (c) on the magnitude of the delta between obsv and now. + // Avoiding excessive CAS operations to hot RW locations is critical. + // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate + return (prev == obsv) ? now : obsv; } // Time since start-up in seconds to a fine granularity. @@ -2143,6 +2065,10 @@ return dlsym(handle, name); } +void* os::get_default_process_handle() { + return (void*)::dlopen(NULL, RTLD_LAZY); +} + int os::stat(const char *path, struct stat *sbuf) { char pathbuf[MAX_PATH]; if (strlen(path) > MAX_PATH - 1) { @@ -2225,8 +2151,8 @@ st->cr(); status = true; } - ::close(fd); } + ::close(fd); } return status; } @@ -2244,58 +2170,12 @@ (void) check_addr0(st); } -// Taken from /usr/include/sys/machsig.h Supposed to be architecture specific -// but they're the same for all the solaris architectures that we support. -const char *ill_names[] = { "ILL0", "ILL_ILLOPC", "ILL_ILLOPN", "ILL_ILLADR", - "ILL_ILLTRP", "ILL_PRVOPC", "ILL_PRVREG", - "ILL_COPROC", "ILL_BADSTK" }; - -const char *fpe_names[] = { "FPE0", "FPE_INTDIV", "FPE_INTOVF", "FPE_FLTDIV", - "FPE_FLTOVF", "FPE_FLTUND", "FPE_FLTRES", - "FPE_FLTINV", "FPE_FLTSUB" }; - -const char *segv_names[] = { "SEGV0", "SEGV_MAPERR", "SEGV_ACCERR" }; - -const char *bus_names[] = { "BUS0", "BUS_ADRALN", "BUS_ADRERR", "BUS_OBJERR" }; - void os::print_siginfo(outputStream* st, void* siginfo) { - st->print("siginfo:"); - - const int buflen = 100; - char buf[buflen]; - siginfo_t *si = (siginfo_t*)siginfo; - st->print("si_signo=%s: ", os::exception_name(si->si_signo, buf, buflen)); - char *err = strerror(si->si_errno); - if (si->si_errno != 0 && err != NULL) { - st->print("si_errno=%s", err); - } else { - st->print("si_errno=%d", si->si_errno); - } - const int c = si->si_code; - assert(c > 0, "unexpected si_code"); - switch (si->si_signo) { - case SIGILL: - st->print(", si_code=%d (%s)", c, c > 8 ? "" : ill_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGFPE: - st->print(", si_code=%d (%s)", c, c > 9 ? "" : fpe_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGSEGV: - st->print(", si_code=%d (%s)", c, c > 2 ? "" : segv_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - case SIGBUS: - st->print(", si_code=%d (%s)", c, c > 3 ? "" : bus_names[c]); - st->print(", si_addr=" PTR_FORMAT, si->si_addr); - break; - default: - st->print(", si_code=%d", si->si_code); - // no si_addr - } - - if ((si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && + const siginfo_t* si = (const siginfo_t*)siginfo; + + os::Posix::print_siginfo_brief(st, si); + + if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && UseSharedSpaces) { FileMapInfo* mapinfo = FileMapInfo::current_info(); if (mapinfo->is_in_shared_space(si->si_addr)) { @@ -2365,7 +2245,8 @@ st->print("[%s]", get_signal_handler_name(handler, buf, buflen)); } - st->print(", sa_mask[0]=" PTR32_FORMAT, *(uint32_t*)&sa.sa_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? @@ -2374,7 +2255,8 @@ sa.sa_flags = VMError::get_resetted_sigflags(sig); } - st->print(", sa_flags=" PTR32_FORMAT, sa.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, signalHandler) || @@ -3005,7 +2887,7 @@ char *os::scan_pages(char *start, char* end, page_info* page_expected, page_info* page_found) { const uint_t info_types[] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE }; const size_t types = sizeof(info_types) / sizeof(info_types[0]); - uint64_t addrs[MAX_MEMINFO_CNT], outdata[types * MAX_MEMINFO_CNT]; + uint64_t addrs[MAX_MEMINFO_CNT], outdata[types * MAX_MEMINFO_CNT + 1]; uint_t validity[MAX_MEMINFO_CNT]; size_t page_size = MAX2((size_t)os::vm_page_size(), page_expected->size); @@ -3044,7 +2926,7 @@ } } - if (i != addrs_count) { + if (i < addrs_count) { if ((validity[i] & 2) != 0) { page_found->lgrp_id = outdata[types * i]; } else { @@ -3534,9 +3416,14 @@ return os_sleep(millis, interruptible); } -int os::naked_sleep() { - // %% make the sleep time an integer flag. for now use 1 millisec. - return os_sleep(1, false); +void os::naked_short_sleep(jlong ms) { + assert(ms < 1000, "Un-interruptable sleep, short time use only"); + + // usleep is deprecated and removed from POSIX, in favour of nanosleep, but + // Solaris requires -lrt for this. + usleep((ms * 1000)); + + return; } // Sleep forever; naked call to OS-specific sleep; use with CAUTION diff -r 45d7b2c7029d -r 52b4284cb496 src/os/solaris/vm/os_solaris.hpp --- a/src/os/solaris/vm/os_solaris.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/solaris/vm/os_solaris.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/os/solaris/vm/perfMemory_solaris.cpp --- a/src/os/solaris/vm/perfMemory_solaris.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/solaris/vm/perfMemory_solaris.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -431,10 +431,12 @@ RESTARTABLE(::read(fd, addr, remaining), result); if (result == OS_ERR) { + ::close(fd); THROW_MSG_0(vmSymbols::java_io_IOException(), "Read error"); + } else { + remaining-=result; + addr+=result; } - remaining-=result; - addr+=result; } ::close(fd); @@ -906,8 +908,16 @@ 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"); + fd = open_sharedmem_file(rfilename, file_flags, THREAD); + + if (fd == OS_ERR) { + return; + } + + if (HAS_PENDING_EXCEPTION) { + ::close(fd); + return; + } if (*sizep == 0) { size = sharedmem_filesize(fd, CHECK); diff -r 45d7b2c7029d -r 52b4284cb496 src/os/windows/vm/os_windows.cpp --- a/src/os/windows/vm/os_windows.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/windows/vm/os_windows.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -2440,6 +2440,12 @@ } } + if ((exception_code == EXCEPTION_ACCESS_VIOLATION) && + VM_Version::is_cpuinfo_segv_addr(pc)) { + // Verify that OS save/restore AVX registers. + return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr()); + } + if (t != NULL && t->is_Java_thread()) { JavaThread* thread = (JavaThread*) t; bool in_java = thread->thread_state() == _thread_in_Java; @@ -2713,7 +2719,6 @@ } #endif -#ifndef PRODUCT void os::win32::call_test_func_with_wrapper(void (*funcPtr)(void)) { // Install a win32 structured exception handler around the test // function call so the VM can generate an error dump if needed. @@ -2724,7 +2729,6 @@ // Nothing to do. } } -#endif // Virtual Memory @@ -3499,6 +3503,16 @@ return result; } +// +// Short sleep, direct OS call. +// +// ms = 0, means allow others (if any) to run. +// +void os::naked_short_sleep(jlong ms) { + assert(ms < 1000, "Un-interruptable sleep, short time use only"); + Sleep(ms); +} + // Sleep forever; naked call to OS-specific sleep; use with CAUTION void os::infinite_sleep() { while (true) { // sleep forever ... @@ -3626,13 +3640,14 @@ "possibility of dangling Thread pointer"); OSThread* osthread = thread->osthread(); - bool interrupted = osthread->interrupted(); // There is no synchronization between the setting of the interrupt // and it being cleared here. It is critical - see 6535709 - that // we only clear the interrupt state, and reset the interrupt event, // if we are going to report that we were indeed interrupted - else // an interrupt can be "lost", leading to spurious wakeups or lost wakeups - // depending on the timing + // depending on the timing. By checking thread interrupt event to see + // if the thread gets real interrupt thus prevent spurious wakeup. + bool interrupted = osthread->interrupted() && (WaitForSingleObject(osthread->interrupt_event(), 0) == WAIT_OBJECT_0); if (interrupted && clear_interrupted) { osthread->set_interrupted(false); ResetEvent(osthread->interrupt_event()); diff -r 45d7b2c7029d -r 52b4284cb496 src/os/windows/vm/os_windows.hpp --- a/src/os/windows/vm/os_windows.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/windows/vm/os_windows.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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; @@ -94,9 +97,7 @@ static address fast_jni_accessor_wrapper(BasicType); #endif -#ifndef PRODUCT static void call_test_func_with_wrapper(void (*funcPtr)(void)); -#endif // filter function to ignore faults on serializations page static LONG WINAPI serialize_fault_filter(struct _EXCEPTION_POINTERS* e); diff -r 45d7b2c7029d -r 52b4284cb496 src/os/windows/vm/os_windows.inline.hpp --- a/src/os/windows/vm/os_windows.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os/windows/vm/os_windows.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -107,9 +107,7 @@ return ::close(fd); } -#ifndef PRODUCT - #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) \ - os::win32::call_test_func_with_wrapper(f) -#endif +#define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) \ + os::win32::call_test_func_with_wrapper(f) #endif // OS_WINDOWS_VM_OS_WINDOWS_INLINE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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_OJDKPPC_VM_ATOMIC_AIX_PPC_INLINE_HPP +#define OS_CPU_AIX_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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,147 @@ +/* + * 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" + +// 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,565 @@ +/* + * 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. + * + */ + +// 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/thread.inline.hpp" +#include "runtime/timer.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) { } + +// 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; + } + + // 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; + } + + // 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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,55 @@ +/* + * 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) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) + + +#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) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) + +#endif // OS_CPU_AIX_OJDKPPC_VM_VMSTRUCTS_AIX_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp --- a/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -276,6 +276,8 @@ # endif #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + address os::current_stack_pointer() { #if defined(__clang__) || defined(__llvm__) register void *esp; @@ -492,6 +494,11 @@ } } + if ((sig == SIGSEGV || sig == SIGBUS) && VM_Version::is_cpuinfo_segv_addr(pc)) { + // Verify that OS save/restore AVX registers. + stub = VM_Version::cpuinfo_cont_addr(); + } + // We test if stub is already set (by the stack overflow code // above) so it is not overwritten by the code that follows. This // check is not required on other platforms, because on other diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/bsd_zero/vm/os_bsd_zero.hpp --- a/src/os_cpu/bsd_zero/vm/os_bsd_zero.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/bsd_zero/vm/os_bsd_zero.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,614 @@ +/* + * 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 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/thread.inline.hpp" +#include "runtime/timer.hpp" +#include "utilities/events.hpp" +#include "utilities/vmError.hpp" + +// 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) { } + +// 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; + } + + // 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()) { + 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, OOM_MMAP_ERROR, "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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,39 @@ +/* + * 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" + +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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,55 @@ +/* + * 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) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) + + +#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) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) + +#endif // OS_CPU_LINUX_PPC_VM_VMSTRUCTS_LINUX_PPC_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/linux_sparc/vm/atomic_linux_sparc.inline.hpp --- a/src/os_cpu/linux_sparc/vm/atomic_linux_sparc.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/linux_sparc/vm/atomic_linux_sparc.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -78,12 +78,12 @@ __asm__ volatile( "1: \n\t" " ldx [%2], %%o2\n\t" - " add %0, %%o2, %%o3\n\t" + " add %1, %%o2, %%o3\n\t" " casx [%2], %%o2, %%o3\n\t" " cmp %%o2, %%o3\n\t" " bne %%xcc, 1b\n\t" " nop\n\t" - " add %0, %%o2, %0\n\t" + " add %1, %%o2, %0\n\t" : "=r" (rv) : "r" (add_value), "r" (dest) : "memory", "o2", "o3"); diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp --- a/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -302,29 +302,30 @@ if (context == NULL) return; ucontext_t *uc = (ucontext_t*)context; + sigcontext* sc = (sigcontext*)context; intptr_t *sp = (intptr_t *)os::Linux::ucontext_get_sp(uc); st->print_cr("Register to memory mapping:"); st->cr(); // this is only for the "general purpose" registers - st->print("G1="); print_location(st, SIG_REGS(sc).u_regs[CON__G1]); - st->print("G2="); print_location(st, SIG_REGS(sc).u_regs[CON__G2]); - st->print("G3="); print_location(st, SIG_REGS(sc).u_regs[CON__G3]); - st->print("G4="); print_location(st, SIG_REGS(sc).u_regs[CON__G4]); - st->print("G5="); print_location(st, SIG_REGS(sc).u_regs[CON__G5]); - st->print("G6="); print_location(st, SIG_REGS(sc).u_regs[CON__G6]); - st->print("G7="); print_location(st, SIG_REGS(sc).u_regs[CON__G7]); + st->print("G1="); print_location(st, SIG_REGS(sc).u_regs[CON_G1]); + st->print("G2="); print_location(st, SIG_REGS(sc).u_regs[CON_G2]); + st->print("G3="); print_location(st, SIG_REGS(sc).u_regs[CON_G3]); + st->print("G4="); print_location(st, SIG_REGS(sc).u_regs[CON_G4]); + st->print("G5="); print_location(st, SIG_REGS(sc).u_regs[CON_G5]); + st->print("G6="); print_location(st, SIG_REGS(sc).u_regs[CON_G6]); + st->print("G7="); print_location(st, SIG_REGS(sc).u_regs[CON_G7]); st->cr(); - st->print("O0="); print_location(st, SIG_REGS(sc).u_regs[CON__O0]); - st->print("O1="); print_location(st, SIG_REGS(sc).u_regs[CON__O1]); - st->print("O2="); print_location(st, SIG_REGS(sc).u_regs[CON__O2]); - st->print("O3="); print_location(st, SIG_REGS(sc).u_regs[CON__O3]); - st->print("O4="); print_location(st, SIG_REGS(sc).u_regs[CON__O4]); - st->print("O5="); print_location(st, SIG_REGS(sc).u_regs[CON__O5]); - st->print("O6="); print_location(st, SIG_REGS(sc).u_regs[CON__O6]); - st->print("O7="); print_location(st, SIG_REGS(sc).u_regs[CON__O7]); + st->print("O0="); print_location(st, SIG_REGS(sc).u_regs[CON_O0]); + st->print("O1="); print_location(st, SIG_REGS(sc).u_regs[CON_O1]); + st->print("O2="); print_location(st, SIG_REGS(sc).u_regs[CON_O2]); + st->print("O3="); print_location(st, SIG_REGS(sc).u_regs[CON_O3]); + st->print("O4="); print_location(st, SIG_REGS(sc).u_regs[CON_O4]); + st->print("O5="); print_location(st, SIG_REGS(sc).u_regs[CON_O5]); + st->print("O6="); print_location(st, SIG_REGS(sc).u_regs[CON_O6]); + st->print("O7="); print_location(st, SIG_REGS(sc).u_regs[CON_O7]); st->cr(); st->print("L0="); print_location(st, sp[L0->sp_offset_in_saved_window()]); @@ -516,7 +517,7 @@ if (nativeInstruction_at(*pc)->is_ic_miss_trap()) { #ifdef ASSERT #ifdef TIERED - CodeBlob* cb = CodeCache::find_blob_unsafe(pc); + CodeBlob* cb = CodeCache::find_blob_unsafe(*pc); assert(cb->is_compiled_by_c2(), "Wrong compiler"); #endif // TIERED #endif // ASSERT diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/linux_x86/vm/os_linux_x86.cpp --- a/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -88,6 +88,8 @@ #define SPELL_REG_FP "ebp" #endif // AMD64 +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + address os::current_stack_pointer() { #ifdef SPARC_WORKS register void *esp; @@ -337,6 +339,11 @@ } } + if ((sig == SIGSEGV) && VM_Version::is_cpuinfo_segv_addr(pc)) { + // Verify that OS save/restore AVX registers. + stub = VM_Version::cpuinfo_cont_addr(); + } + 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 diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/linux_zero/vm/os_linux_zero.hpp --- a/src/os_cpu/linux_zero/vm/os_linux_zero.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/linux_zero/vm/os_linux_zero.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp --- a/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -75,13 +75,19 @@ do_sysinfo(SI_ARCHITECTURE_64, "sparcv9", &features, generic_v9_m); // Extract valid instruction set extensions. - uint_t av; - uint_t avn = os::Solaris::getisax(&av, 1); - assert(avn == 1, "should only return one av"); + uint_t avs[2]; + uint_t avn = os::Solaris::getisax(avs, 2); + assert(avn <= 2, "should return two or less av's"); + uint_t av = avs[0]; #ifndef PRODUCT - if (PrintMiscellaneous && Verbose) - tty->print_cr("getisax(2) returned: " PTR32_FORMAT, av); + if (PrintMiscellaneous && Verbose) { + tty->print("getisax(2) returned: " PTR32_FORMAT, av); + if (avn > 1) { + tty->print(", " PTR32_FORMAT, avs[1]); + } + tty->cr(); + } #endif if (av & AV_SPARC_MUL32) features |= hardware_mul32_m; @@ -91,6 +97,13 @@ if (av & AV_SPARC_POPC) features |= hardware_popc_m; if (av & AV_SPARC_VIS) features |= vis1_instructions_m; if (av & AV_SPARC_VIS2) features |= vis2_instructions_m; + if (avn > 1) { + uint_t av2 = avs[1]; +#ifndef AV2_SPARC_SPARC5 +#define AV2_SPARC_SPARC5 0x00000008 /* The 29 new fp and sub instructions */ +#endif + if (av2 & AV2_SPARC_SPARC5) features |= sparc5_instructions_m; + } // Next values are not defined before Solaris 10 // but Solaris 8 is used for jdk6 update builds. @@ -119,6 +132,11 @@ #endif if (av & AV_SPARC_CBCOND) features |= cbcond_instructions_m; +#ifndef AV_SPARC_AES +#define AV_SPARC_AES 0x00020000 /* aes instrs supported */ +#endif + if (av & AV_SPARC_AES) features |= aes_instructions_m; + } else { // getisax(2) failed, use the old legacy code. #ifndef PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp --- a/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -459,6 +459,11 @@ } } + if ((sig == SIGSEGV) && VM_Version::is_cpuinfo_segv_addr(pc)) { + // Verify that OS save/restore AVX registers. + stub = VM_Version::cpuinfo_cont_addr(); + } + if (thread->thread_state() == _thread_in_vm) { if (sig == SIGBUS && info->si_code == BUS_OBJERR && thread->doing_unsafe_access()) { stub = StubRoutines::handler_for_unsafe_access(); @@ -475,9 +480,11 @@ // 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()) { - stub = StubRoutines::handler_for_unsafe_access(); + if (cb != NULL) { + nmethod* nm = cb->is_nmethod() ? (nmethod*)cb : NULL; + if (nm != NULL && nm->has_unsafe_access()) { + stub = StubRoutines::handler_for_unsafe_access(); + } } } else @@ -724,6 +731,7 @@ err.report_and_die(); ShouldNotReachHere(); + return false; } void os::print_context(outputStream *st, void *context) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/tools/hsdis/Makefile --- a/src/share/tools/hsdis/Makefile Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/tools/hsdis/Makefile Wed Oct 15 16:02:50 2014 +0200 @@ -27,6 +27,7 @@ # Default arch; it is changed below as needed. ARCH = i386 OS = $(shell uname) +AR = ar ## OS = SunOS ## ifeq ($(OS),SunOS) @@ -73,6 +74,7 @@ ifdef LP64 CFLAGS/sparcv9 += -m64 CFLAGS/amd64 += -m64 +CFLAGS/ppc64 += -m64 else ARCH=$(ARCH1:amd64=i386) CFLAGS/i386 += -m32 @@ -88,8 +90,20 @@ DLDFLAGS += -shared LDFLAGS += -ldl OUTFLAGS += -o $@ -## OS = Windows ## -else # !SunOS, !Linux => Darwin or Windows +else +## OS = AIX ## +ifeq ($(OS),AIX) +OS = aix +ARCH = ppc64 +CC = xlc_r +CFLAGS += -DAIX -g -qpic=large -q64 +CFLAGS/ppc64 += -q64 +AR = ar -X64 +DLDFLAGS += -qmkshrobj -lz +OUTFLAGS += -o $@ +LIB_EXT = .so +else +## OS = Darwin ## ifeq ($(OS),Darwin) CPU = $(shell uname -m) ARCH1=$(CPU:x86_64=amd64) @@ -113,7 +127,8 @@ DLDFLAGS += -lz LDFLAGS += -ldl OUTFLAGS += -o $@ -else #Windows +else +## OS = Windows ## OS = windows CC = gcc CFLAGS += /nologo /MD /W3 /WX /O2 /Fo$(@:.dll=.obj) /Gi- @@ -123,6 +138,7 @@ OUTFLAGS += /link /out:$@ LIB_EXT = .dll endif # Darwin +endif # AIX endif # Linux endif # SunOS @@ -176,7 +192,7 @@ if [ ! -f $@ ]; then cd $(TARGET_DIR); make all-opcodes; fi $(TARGET_DIR)/Makefile: - (cd $(TARGET_DIR); CC=$(CC) CFLAGS="$(CFLAGS)" $(BINUTILSDIR)/configure --disable-nls $(CONFIGURE_ARGS)) + (cd $(TARGET_DIR); CC=$(CC) CFLAGS="$(CFLAGS)" AR="$(AR)" $(BINUTILSDIR)/configure --disable-nls $(CONFIGURE_ARGS)) $(TARGET): $(SOURCE) $(LIBS) $(LIBRARIES) $(TARGET_DIR) $(CC) $(OUTFLAGS) $(CPPFLAGS) $(CFLAGS) $(SOURCE) $(DLDFLAGS) $(LIBRARIES) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/tools/hsdis/README --- a/src/share/tools/hsdis/README Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/tools/hsdis/README Wed Oct 15 16:02:50 2014 +0200 @@ -1,4 +1,4 @@ -Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2008, 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 @@ -54,6 +54,17 @@ disassembler library only. If you build demo it will build a demo program that attempts to exercise the library. +With recent version of binutils (i.e. binutils-2.23.2) you may get the +following build error: + +WARNING: `makeinfo' is missing on your system. You should only need it if + you modified a `.texi' or `.texinfo' file, or any other file + ... + +This is because of "Bug 15345 - binutils-2.23.2 tarball doesn't build +without makeinfo" [2]. The easiest way to work around this problem is +by doing a "touch $BINUTILS/bfd/doc/bfd.info". + Windows In theory this should be buildable on Windows but getting a working @@ -101,3 +112,13 @@ If the product mode of the JVM does not accept -XX:+PrintAssembly, you do not have a version new enough to use the hsdis plugin. + +* Wiki + +More information can be found in the OpenJDK HotSpot Wiki [1]. + + +Resources: + +[1] https://wiki.openjdk.java.net/display/HotSpot/PrintAssembly +[2] http://sourceware.org/bugzilla/show_bug.cgi?id=15345 diff -r 45d7b2c7029d -r 52b4284cb496 src/share/tools/hsdis/hsdis.c --- a/src/share/tools/hsdis/hsdis.c Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/tools/hsdis/hsdis.c Wed Oct 15 16:02:50 2014 +0200 @@ -307,7 +307,8 @@ app_data->printf_stream, app_data->printf_callback, native_bfd, - app_data->insn_options); + /* On PowerPC we get warnings, if we pass empty options */ + (caller_options == NULL) ? NULL : app_data->insn_options); /* Finish linking together the various callback blocks. */ app_data->dinfo.application_data = (void*) app_data; @@ -459,6 +460,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/adlparse.cpp --- a/src/share/vm/adlc/adlparse.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/adlparse.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -219,19 +219,21 @@ else if (!strcmp(ident, "encode")) { parse_err(SYNERR, "Instructions specify ins_encode, not encode\n"); } - else if (!strcmp(ident, "ins_encode")) ins_encode_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); - else if (!strcmp(ident, "expand")) instr->_exprule = expand_parse(instr); - else if (!strcmp(ident, "rewrite")) instr->_rewrule = rewrite_parse(); + else if (!strcmp(ident, "ins_encode")) ins_encode_parse(*instr); + // Parse late expand keyword. + else if (!strcmp(ident, "postalloc_expand")) postalloc_expand_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); + else if (!strcmp(ident, "expand")) instr->_exprule = expand_parse(instr); + else if (!strcmp(ident, "rewrite")) instr->_rewrule = rewrite_parse(); else if (!strcmp(ident, "constraint")) { parse_err(SYNERR, "Instructions do not specify a constraint\n"); } else if (!strcmp(ident, "construct")) { parse_err(SYNERR, "Instructions do not specify a construct\n"); } - else if (!strcmp(ident, "format")) instr->_format = format_parse(); + else if (!strcmp(ident, "format")) instr->_format = format_parse(); else if (!strcmp(ident, "interface")) { parse_err(SYNERR, "Instructions do not specify an interface\n"); } @@ -240,13 +242,14 @@ // Check identifier to see if it is the name of an attribute const Form *form = _globalNames[ident]; AttributeForm *attr = form ? form->is_attribute() : NULL; - if( attr && (attr->_atype == INS_ATTR) ) { + if (attr && (attr->_atype == INS_ATTR)) { // Insert the new attribute into the linked list. Attribute *temp = attr_parse(ident); temp->_next = instr->_attribs; instr->_attribs = temp; } else { - parse_err(SYNERR, "expected one of:\n predicate, match, encode, or the name of an instruction attribute at %s\n", ident); + parse_err(SYNERR, "expected one of:\n predicate, match, encode, or the name of" + " an instruction attribute at %s\n", ident); } } skipws(); @@ -258,13 +261,17 @@ } // Check for "Set" form of chain rule adjust_set_rule(instr); - if (_AD._pipeline ) { - if( instr->expands() ) { - 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); + if (_AD._pipeline) { + // No pipe required for late expand. + if (instr->expands() || instr->postalloc_expands()) { + 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 { - if( !instr->_ins_pipe ) + if (!instr->_ins_pipe) { parse_err(WARN, "No ins_pipe specified for instruction \"%s\"\n", instr->_ident); + } } } // Add instruction to tail of instruction list @@ -2779,11 +2786,13 @@ encoding->add_parameter(opForm->_ident, param); } - // Define a MacroAssembler instance for use by the encoding. The - // name is chosen to match the __ idiom used for assembly in other - // parts of hotspot and assumes the existence of the standard - // #define __ _masm. - encoding->add_code(" MacroAssembler _masm(&cbuf);\n"); + if (!inst._is_postalloc_expand) { + // Define a MacroAssembler instance for use by the encoding. The + // name is chosen to match the __ idiom used for assembly in other + // parts of hotspot and assumes the existence of the standard + // #define __ _masm. + encoding->add_code(" MacroAssembler _masm(&cbuf);\n"); + } // Parse the following %{ }% block ins_encode_parse_block_impl(inst, encoding, ec_name); @@ -2854,10 +2863,14 @@ // Check if this instruct is a MachConstantNode. if (strcmp(rep_var, "constanttablebase") == 0) { // This instruct is a MachConstantNode. - inst.set_is_mach_constant(true); + inst.set_needs_constant_base(true); + if (strncmp("MachCall", inst.mach_base_class(_globalNames), strlen("MachCall")) != 0 ) { + 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; } } @@ -2955,18 +2968,34 @@ while (_curchar != ')') { char *param = get_ident_or_literal_constant("encoding operand"); if ( param != NULL ) { - // Found a parameter: - // 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 - 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; + + // Check if this instruct is a MachConstantNode. + if (strcmp(param, "constanttablebase") == 0) { + // This instruct is a MachConstantNode. + inst.set_needs_constant_base(true); + if (strncmp("MachCall", inst.mach_base_class(_globalNames), strlen("MachCall")) != 0 ) { + 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 { + // Found a parameter: + // 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 + 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); @@ -3050,6 +3079,160 @@ inst._insencode = encrule; } +//------------------------------postalloc_expand_parse--------------------------- +// Encode rules have the form +// postalloc_expand( 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::postalloc_expand_parse(InstructForm& inst) { + inst._is_postalloc_expand = true; + + // Parse encode class name. + skipws(); // Skip whitespace. + if (_curchar != '(') { + // Check for postalloc_expand %{ form + if ((_curchar == '%') && (*(_ptr+1) == '{')) { + next_char(); // Skip '%' + next_char(); // Skip '{' + + // Parse the block form of postalloc_expand + ins_encode_parse_block(inst); + return; + } + + parse_err(SYNERR, "missing '(' in postalloc_expand 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 postalloc_expand class name after 'postalloc_expand('.\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_needs_constant_base(true); + if (strncmp("MachCall", inst.mach_base_class(_globalNames), strlen("MachCall")) != 0 ) { + 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 postalloc_expand %s.\n", ec_name); + return; + } + if (_curchar != ')') { + parse_err(SYNERR, "Expected ')' after postalloc_expand 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 postalloc_expand %s.\n", ec_name); + return; + } + } // Done parsing postalloc_expand method and their parameters. + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' at end of postalloc_expand description.\n"); + return; + } + next_char(); // Move past ')'. + skipws(); // Skip leading whitespace. + + if (_curchar != ';') { + parse_err(SYNERR, "Missing ';' at end of postalloc_expand.\n"); + return; + } + next_char(); // Move past ';'. + skipws(); // Be friendly to oper_parse(). + + // Debug Stuff. + if (_AD._adl_debug > 1) fprintf(stderr, "Instruction postalloc_expand: %s\n", ec_name); + + // Set encode class of this instruction. + inst._insencode = encrule; +} + //------------------------------constant_parse--------------------------------- // Parse a constant expression. @@ -3835,13 +4018,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 != '%') @@ -3855,12 +4036,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; @@ -3895,6 +4094,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 @@ -3918,7 +4118,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/adlparse.hpp --- a/src/share/vm/adlc/adlparse.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/adlparse.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 postalloc expand rule. + void postalloc_expand_parse(InstructForm &inst); void constant_parse(InstructForm& inst); void constant_parse_expression(EncClass* encoding, char* ec_name); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/archDesc.cpp --- a/src/share/vm/adlc/archDesc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/archDesc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -172,7 +172,8 @@ _internalOps(cmpstr,hashstr, Form::arena), _internalMatch(cmpstr,hashstr, Form::arena), _chainRules(cmpstr,hashstr, Form::arena), - _cisc_spill_operand(NULL) { + _cisc_spill_operand(NULL), + _needs_clone_jvms(false) { // Initialize the opcode to MatchList table with NULLs for( int i=0; i<_last_opcode; ++i ) { @@ -1192,15 +1193,12 @@ || strcmp(idealName,"CmpF") == 0 || strcmp(idealName,"FastLock") == 0 || strcmp(idealName,"FastUnlock") == 0 - || strcmp(idealName,"AddExactI") == 0 - || strcmp(idealName,"AddExactL") == 0 - || strcmp(idealName,"SubExactI") == 0 - || strcmp(idealName,"SubExactL") == 0 - || strcmp(idealName,"MulExactI") == 0 - || strcmp(idealName,"MulExactL") == 0 - || strcmp(idealName,"NegExactI") == 0 - || strcmp(idealName,"NegExactL") == 0 - || strcmp(idealName,"FlagsProj") == 0 + || strcmp(idealName,"OverflowAddI") == 0 + || strcmp(idealName,"OverflowAddL") == 0 + || strcmp(idealName,"OverflowSubI") == 0 + || strcmp(idealName,"OverflowSubL") == 0 + || strcmp(idealName,"OverflowMulI") == 0 + || strcmp(idealName,"OverflowMulL") == 0 || strcmp(idealName,"Bool") == 0 || strcmp(idealName,"Binary") == 0 ) { // Removed ConI from the must_clone list. CPUs that cannot use diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/archDesc.hpp --- a/src/share/vm/adlc/archDesc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/archDesc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -121,6 +121,12 @@ // to access [stack_pointer + offset] OperandForm *_cisc_spill_operand; + // If a Call node uses $constanttablebase, it gets MachConstantBaseNode + // by the matcher and the matcher will modify the jvms. If so, jvm states + // always have to be cloned when a node is cloned. Adlc generates + // Compile::needs_clone_jvms() accordingly. + bool _needs_clone_jvms; + // Methods for outputting the DFA void gen_match(FILE *fp, MatchList &mlist, ProductionState &status, Dict &operands_chained_from); void chain_rule(FILE *fp, const char *indent, const char *ideal, @@ -289,6 +295,7 @@ void addPreHeaderBlocks(FILE *fp_hpp); void addHeaderBlocks(FILE *fp_hpp); void addSourceBlocks(FILE *fp_cpp); + void generate_needs_clone_jvms(FILE *fp_cpp); void generate_adlc_verification(FILE *fp_cpp); // output declaration of class State @@ -311,6 +318,8 @@ void defineEvalConstant(FILE *fp, InstructForm &node); // Generator for Emit methods for instructions void defineEmit (FILE *fp, InstructForm &node); + // Generator for postalloc_expand methods for instructions. + void define_postalloc_expand(FILE *fp, InstructForm &node); // Define a MachOper encode method void define_oper_interface(FILE *fp, OperandForm &oper, FormDict &globals, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/formssel.cpp --- a/src/share/vm/adlc/formssel.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/formssel.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -32,31 +32,33 @@ _localNames(cmpstr, hashstr, Form::arena), _effects(cmpstr, hashstr, Form::arena), _is_mach_constant(false), + _needs_constant_base(false), _has_call(false) { _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; - _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill + _matrule = NULL; + _insencode = NULL; + _constant = NULL; + _is_postalloc_expand = 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; - _is_cisc_alternate = false; - _is_short_branch = false; - _short_branch_form = NULL; - _alignment = 1; + _cisc_reg_mask_name = NULL; + _is_cisc_alternate = false; + _is_short_branch = false; + _short_branch_form = NULL; + _alignment = 1; } InstructForm::InstructForm(const char *id, InstructForm *instr, MatchRule *rule) @@ -64,31 +66,33 @@ _localNames(instr->_localNames), _effects(instr->_effects), _is_mach_constant(false), + _needs_constant_base(false), _has_call(false) { _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; - _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill - _cisc_spill_alternate = NULL; // possible cisc replacement - _cisc_reg_mask_name = NULL; - _is_cisc_alternate = false; - _is_short_branch = false; - _short_branch_form = NULL; - _alignment = 1; + _matrule = rule; + _insencode = instr->_insencode; + _constant = instr->_constant; + _is_postalloc_expand = instr->_is_postalloc_expand; + _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; + _is_cisc_alternate = false; + _is_short_branch = false; + _short_branch_form = NULL; + _alignment = 1; // Copy parameters const char *name; instr->_parameters.reset(); @@ -157,6 +161,11 @@ return ( _exprule != NULL ); } +// This instruction has a late expand rule? +bool InstructForm::postalloc_expands() const { + return _is_postalloc_expand; +} + // This instruction has a peephole rule? Peephole *InstructForm::peepholes() const { return _peephole; @@ -639,6 +648,8 @@ if( strcmp(_matrule->_opType,"MemBarReleaseLock") == 0 ) return true; if( strcmp(_matrule->_opType,"MemBarAcquireLock") == 0 ) return true; if( strcmp(_matrule->_opType,"MemBarStoreStore") == 0 ) return true; + if( strcmp(_matrule->_opType,"StoreFence") == 0 ) return true; + if( strcmp(_matrule->_opType,"LoadFence") == 0 ) return true; return false; } @@ -649,6 +660,7 @@ int USE_of_memory = 0; int DEF_of_memory = 0; const char* last_memory_DEF = NULL; // to test DEF/USE pairing in asserts + const char* last_memory_USE = NULL; Component *unique = NULL; Component *comp = NULL; ComponentList &components = (ComponentList &)_components; @@ -670,7 +682,16 @@ assert(0 == strcmp(last_memory_DEF, comp->_name), "every memory DEF is followed by a USE of the same name"); last_memory_DEF = NULL; } - USE_of_memory++; + // Handles same memory being used multiple times in the case of BMI1 instructions. + if (last_memory_USE != NULL) { + if (strcmp(comp->_name, last_memory_USE) != 0) { + USE_of_memory++; + } + } else { + USE_of_memory++; + } + last_memory_USE = comp->_name; + if (DEF_of_memory == 0) // defs take precedence unique = comp; } else { @@ -1269,11 +1290,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; } @@ -1301,7 +1322,7 @@ OperandForm* oper = form->is_operand(); if (oper != NULL && oper->is_bound_register()) { const RegDef* first = oper->get_RegClass()->find_first_elem(); - fprintf(fp, " st->print(\"%s\");\n", first->_regname); + fprintf(fp, " st->print_raw(\"%s\");\n", first->_regname); } else { globalAD->syntax_err(_linenum, "In %s can't find format for %s %s", _ident, opc->_ident, rep_var); } @@ -2509,7 +2530,7 @@ case Form::idealP: fprintf(fp," if (_c%d) _c%d->dump_on(st);\n", const_index, const_index); break; case Form::idealNKlass: case Form::idealN: fprintf(fp," if (_c%d) _c%d->dump_on(st);\n", const_index, const_index); break; - case Form::idealL: fprintf(fp," st->print(\"#%%lld\", _c%d);\n", const_index); break; + case Form::idealL: fprintf(fp," st->print(\"#\" INT64_FORMAT, (int64_t)_c%d);\n", const_index); break; case Form::idealF: fprintf(fp," st->print(\"#%%f\", _c%d);\n", const_index); break; case Form::idealD: fprintf(fp," st->print(\"#%%f\", _c%d);\n", const_index); break; default: @@ -4045,13 +4066,15 @@ bool MatchRule::is_ideal_membar() const { if( !_opType ) return false; return - !strcmp(_opType,"MemBarAcquire" ) || - !strcmp(_opType,"MemBarRelease" ) || + !strcmp(_opType,"MemBarAcquire") || + !strcmp(_opType,"MemBarRelease") || !strcmp(_opType,"MemBarAcquireLock") || !strcmp(_opType,"MemBarReleaseLock") || - !strcmp(_opType,"MemBarVolatile" ) || - !strcmp(_opType,"MemBarCPUOrder" ) || - !strcmp(_opType,"MemBarStoreStore" ); + !strcmp(_opType,"LoadFence" ) || + !strcmp(_opType,"StoreFence") || + !strcmp(_opType,"MemBarVolatile") || + !strcmp(_opType,"MemBarCPUOrder") || + !strcmp(_opType,"MemBarStoreStore"); } bool MatchRule::is_ideal_loadPC() const { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/formssel.hpp --- a/src/share/vm/adlc/formssel.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/formssel.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -83,35 +83,37 @@ const char *_cisc_reg_mask_name; InstructForm *_short_branch_form; bool _is_short_branch; - bool _is_mach_constant; // true if Node is a MachConstantNode + bool _is_mach_constant; // True if Node is a MachConstantNode. + bool _needs_constant_base; // True if Node needs the mach_constant_base input. uint _alignment; public: // Public Data - const char *_ident; // Name of this instruction - NameList _parameters; // Locally defined names - FormDict _localNames; // Table of operands & their types - MatchRule *_matrule; // Matching rule for this instruction - Opcode *_opcode; // Encoding of the opcode for instruction - char *_size; // Size of instruction - InsEncode *_insencode; // Encoding class instruction belongs to - InsEncode *_constant; // Encoding class constant value belongs to - Attribute *_attribs; // List of Attribute rules - Predicate *_predicate; // Predicate test for this instruction - FormDict _effects; // Dictionary of effect rules - ExpandRule *_exprule; // Expand rule for this instruction - RewriteRule *_rewrule; // Rewrite rule for this instruction - FormatRule *_format; // Format for assembly generation - Peephole *_peephole; // List of peephole rules for instruction - const char *_ins_pipe; // Instruction Scheduling description class + const char *_ident; // Name of this instruction + NameList _parameters; // Locally defined names + FormDict _localNames; // Table of operands & their types + MatchRule *_matrule; // Matching rule for this instruction + Opcode *_opcode; // Encoding of the opcode for instruction + char *_size; // Size of instruction + InsEncode *_insencode; // Encoding class instruction belongs to + InsEncode *_constant; // Encoding class constant value belongs to + bool _is_postalloc_expand; // 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 + ExpandRule *_exprule; // Expand rule for this instruction + RewriteRule *_rewrule; // Rewrite rule for this instruction + FormatRule *_format; // Format for assembly generation + Peephole *_peephole; // List of peephole rules for instruction + const char *_ins_pipe; // Instruction Scheduling description class - uint *_uniq_idx; // Indexes of unique operands - uint _uniq_idx_length; // Length of _uniq_idx array - uint _num_uniq; // Number of unique operands - ComponentList _components; // List of Components matches MachNode's - // operand structure + uint *_uniq_idx; // Indexes of unique operands + uint _uniq_idx_length; // Length of _uniq_idx array + uint _num_uniq; // Number of unique operands + ComponentList _components; // List of Components matches MachNode's + // operand structure - bool _has_call; // contain a call and caller save registers should be saved? + bool _has_call; // contain a call and caller save registers should be saved? // Public Methods InstructForm(const char *id, bool ideal_only = false); @@ -133,6 +135,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 postalloc_expands() const; // Return this instruction's first peephole rule, or NULL virtual Peephole *peepholes() const; // Add a peephole rule to this instruction @@ -259,6 +263,8 @@ bool is_mach_constant() const { return _is_mach_constant; } void set_is_mach_constant(bool x) { _is_mach_constant = x; } + bool needs_constant_base() const { return _needs_constant_base; } + void set_needs_constant_base(bool x) { _needs_constant_base = x; } InstructForm *short_branch_form() { return _short_branch_form; } bool has_short_branch_form() { return _short_branch_form != NULL; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/main.cpp --- a/src/share/vm/adlc/main.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/main.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -243,6 +243,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"); @@ -267,6 +272,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: @@ -300,6 +306,7 @@ AD.buildInstructMatchCheck(AD._CPP_file._fp); // .cpp // define methods for machine dependent frame management AD.buildFrameMethods(AD._CPP_file._fp); // .cpp + AD.generate_needs_clone_jvms(AD._CPP_file._fp); // do this last: AD.addPreprocessorChecks(AD._CPP_file._fp); // .cpp diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/output_c.cpp --- a/src/share/vm/adlc/output_c.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/output_c.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1570,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); @@ -1592,6 +1599,8 @@ if( node->is_ideal_fastlock() && new_inst->is_ideal_fastlock() ) { fprintf(fp, " ((MachFastLockNode*)n%d)->_counters = _counters;\n",cnt); + fprintf(fp, " ((MachFastLockNode*)n%d)->_rtm_counters = _rtm_counters;\n",cnt); + fprintf(fp, " ((MachFastLockNode*)n%d)->_stack_rtm_counters = _stack_rtm_counters;\n",cnt); } // Fill in the bottom_type where requested @@ -1628,6 +1637,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); @@ -1825,18 +1841,26 @@ // If the node is a MachConstantNode, insert the MachConstantBaseNode edge. // NOTE: this edge must be the last input (see MachConstantNode::mach_constant_base_node_input). - if (node->is_mach_constant()) { - fprintf(fp," add_req(C->mach_constant_base_node());\n"); + // There are nodes that don't use $constantablebase, but still require that it + // is an input to the node. Example: divF_reg_immN, Repl32B_imm on x86_64. + if (node->is_mach_constant() || node->needs_constant_base()) { + if (node->is_ideal_call() != Form::invalid_type && + node->is_ideal_call() != Form::JAVA_LEAF) { + fprintf(fp, " // MachConstantBaseNode added in matcher.\n"); + _needs_clone_jvms = true; + } else { + fprintf(fp, " add_req(C->mach_constant_base_node());\n"); + } } - fprintf(fp,"\n"); - if( node->expands() ) { - fprintf(fp," return result;\n"); + fprintf(fp, "\n"); + if (node->expands()) { + fprintf(fp, " return result;\n"); } else { - fprintf(fp," return this;\n"); + fprintf(fp, " return this;\n"); } - fprintf(fp,"}\n"); - fprintf(fp,"\n"); + fprintf(fp, "}\n"); + fprintf(fp, "\n"); } @@ -1938,9 +1962,9 @@ else if ((strcmp(rep_var, "constanttablebase") == 0) || (strcmp(rep_var, "constantoffset") == 0) || (strcmp(rep_var, "constantaddress") == 0)) { - if (!_inst.is_mach_constant()) { + if (!(_inst.is_mach_constant() || _inst.needs_constant_base())) { _AD.syntax_err(_encoding._linenum, - "Replacement variable %s not allowed in instruct %s (only in MachConstantNode).\n", + "Replacement variable %s not allowed in instruct %s (only in MachConstantNode or MachCall).\n", rep_var, _encoding._name); } } @@ -2103,16 +2127,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 @@ -2294,6 +2323,7 @@ #if defined(IA32) || defined(AMD64) if (strcmp(rep_var,"$XMMRegister") == 0) return "as_XMMRegister"; #endif + if (strcmp(rep_var,"$CondRegister") == 0) return "as_ConditionRegister"; return NULL; } @@ -2488,7 +2518,113 @@ fprintf(fp, " return (VerifyOops ? MachNode::size(ra_) : %s);\n", inst._size); // (3) and (4) - fprintf(fp,"}\n"); + fprintf(fp,"}\n\n"); +} + +// Emit postalloc expand function. +void ArchDesc::define_postalloc_expand(FILE *fp, InstructForm &inst) { + InsEncode *ins_encode = inst._insencode; + + // Output instruction's postalloc_expand prototype. + fprintf(fp, "void %sNode::postalloc_expand(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, "Postalloc 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 postalloc 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 postalloc 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, "Postalloc expand may only have one encoding."); } // defineEmit ----------------------------------------------------------------- @@ -2841,7 +2977,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 @@ -2891,6 +3027,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"); @@ -3055,6 +3193,7 @@ if( instr->expands() || instr->needs_projections() || instr->has_temps() || instr->is_mach_constant() || + instr->needs_constant_base() || instr->_matrule != NULL && instr->num_opnds() != instr->num_unique_opnds() ) defineExpand(_CPP_EXPAND_file._fp, instr); @@ -3142,7 +3281,15 @@ // Ensure this is a machine-world instruction if ( instr->ideal_only() ) continue; - if (instr->_insencode) defineEmit (fp, *instr); + if (instr->_insencode) { + if (instr->postalloc_expands()) { + // 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. + define_postalloc_expand(fp, *instr); + } else { + defineEmit(fp, *instr); + } + } if (instr->is_mach_constant()) defineEvalConstant(fp, *instr); if (instr->_size) defineSize (fp, *instr); @@ -3503,6 +3650,11 @@ return callconv; } +void ArchDesc::generate_needs_clone_jvms(FILE *fp_cpp) { + fprintf(fp_cpp, "bool Compile::needs_clone_jvms() { return %s; }\n\n", + _needs_clone_jvms ? "true" : "false"); +} + //---------------------------generate_assertion_checks------------------- void ArchDesc::generate_adlc_verification(FILE *fp_cpp) { fprintf(fp_cpp, "\n"); @@ -3819,8 +3971,10 @@ } // Fill in the bottom_type where requested - if ( inst->captures_bottom_type(_globalNames) ) { - fprintf(fp_cpp, "%s node->_bottom_type = _leaf->bottom_type();\n", indent); + if (inst->captures_bottom_type(_globalNames)) { + if (strncmp("MachCall", inst->mach_base_class(_globalNames), strlen("MachCall"))) { + fprintf(fp_cpp, "%s node->_bottom_type = _leaf->bottom_type();\n", indent); + } } if( inst->is_ideal_if() ) { fprintf(fp_cpp, "%s node->_prob = _leaf->as_If()->_prob;\n", indent); @@ -3828,6 +3982,8 @@ } if( inst->is_ideal_fastlock() ) { fprintf(fp_cpp, "%s node->_counters = _leaf->as_FastLock()->counters();\n", indent); + fprintf(fp_cpp, "%s node->_rtm_counters = _leaf->as_FastLock()->rtm_counters();\n", indent); + fprintf(fp_cpp, "%s node->_stack_rtm_counters = _leaf->as_FastLock()->stack_rtm_counters();\n", indent); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/adlc/output_h.cpp --- a/src/share/vm/adlc/output_h.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/adlc/output_h.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -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) { @@ -382,14 +386,14 @@ static void defineCCodeDump(OperandForm* oper, FILE *fp, int i) { assert(oper != NULL, "what"); CondInterface* cond = oper->_interface->is_CondInterface(); - fprintf(fp, " if( _c%d == BoolTest::eq ) st->print(\"%s\");\n",i,cond->_equal_format); - fprintf(fp, " else if( _c%d == BoolTest::ne ) st->print(\"%s\");\n",i,cond->_not_equal_format); - fprintf(fp, " else if( _c%d == BoolTest::le ) st->print(\"%s\");\n",i,cond->_less_equal_format); - fprintf(fp, " else if( _c%d == BoolTest::ge ) st->print(\"%s\");\n",i,cond->_greater_equal_format); - fprintf(fp, " else if( _c%d == BoolTest::lt ) st->print(\"%s\");\n",i,cond->_less_format); - fprintf(fp, " else if( _c%d == BoolTest::gt ) st->print(\"%s\");\n",i,cond->_greater_format); - fprintf(fp, " else if( _c%d == BoolTest::overflow ) st->print(\"%s\");\n",i,cond->_overflow_format); - fprintf(fp, " else if( _c%d == BoolTest::no_overflow ) st->print(\"%s\");\n",i,cond->_no_overflow_format); + fprintf(fp, " if( _c%d == BoolTest::eq ) st->print_raw(\"%s\");\n",i,cond->_equal_format); + fprintf(fp, " else if( _c%d == BoolTest::ne ) st->print_raw(\"%s\");\n",i,cond->_not_equal_format); + fprintf(fp, " else if( _c%d == BoolTest::le ) st->print_raw(\"%s\");\n",i,cond->_less_equal_format); + fprintf(fp, " else if( _c%d == BoolTest::ge ) st->print_raw(\"%s\");\n",i,cond->_greater_equal_format); + fprintf(fp, " else if( _c%d == BoolTest::lt ) st->print_raw(\"%s\");\n",i,cond->_less_format); + fprintf(fp, " else if( _c%d == BoolTest::gt ) st->print_raw(\"%s\");\n",i,cond->_greater_format); + fprintf(fp, " else if( _c%d == BoolTest::overflow ) st->print_raw(\"%s\");\n",i,cond->_overflow_format); + fprintf(fp, " else if( _c%d == BoolTest::no_overflow ) st->print_raw(\"%s\");\n",i,cond->_no_overflow_format); } // Output code that dumps constant values, increment "i" if type is constant @@ -412,8 +416,8 @@ ++i; } else if (!strcmp(ideal_type, "ConL")) { - fprintf(fp," st->print(\"#\" INT64_FORMAT, _c%d);\n", i); - fprintf(fp," st->print(\"/\" PTR64_FORMAT, _c%d);\n", i); + fprintf(fp," st->print(\"#\" INT64_FORMAT, (int64_t)_c%d);\n", i); + fprintf(fp," st->print(\"/\" PTR64_FORMAT, (uint64_t)_c%d);\n", i); ++i; } else if (!strcmp(ideal_type, "ConF")) { @@ -425,7 +429,7 @@ else if (!strcmp(ideal_type, "ConD")) { fprintf(fp," st->print(\"#%%f\", _c%d);\n", i); fprintf(fp," jlong _c%dl = JavaValue(_c%d).get_jlong();\n", i, i); - fprintf(fp," st->print(\"/\" PTR64_FORMAT, _c%dl);\n", i); + fprintf(fp," st->print(\"/\" PTR64_FORMAT, (uint64_t)_c%dl);\n", i); ++i; } else if (!strcmp(ideal_type, "Bool")) { @@ -467,7 +471,7 @@ if ( string != NameList::_signal ) { // Normal string // Pass through to st->print - fprintf(fp," st->print(\"%s\");\n", string); + fprintf(fp," st->print_raw(\"%s\");\n", string); } else { // Replacement variable const char *rep_var = oper._format->_rep_vars.iter(); @@ -538,7 +542,7 @@ if ( string != NameList::_signal ) { // Normal string // Pass through to st->print - fprintf(fp," st->print(\"%s\");\n", string); + fprintf(fp," st->print_raw(\"%s\");\n", string); } else { // Replacement variable const char *rep_var = oper._format->_rep_vars.iter(); @@ -665,7 +669,7 @@ } else if( string == NameList::_signal2 ) // Raw program text fputs(inst._format->_strings.iter(), fp); else - fprintf(fp,"st->print(\"%s\");\n", string); + fprintf(fp,"st->print_raw(\"%s\");\n", string); } // Done with all format strings } // Done generating the user-defined portion of the format @@ -692,13 +696,13 @@ default: assert(0,"ShouldNotReachHere"); } - fprintf(fp, " st->print_cr(\"\");\n" ); + fprintf(fp, " st->cr();\n" ); fprintf(fp, " if (_jvms) _jvms->format(ra, this, st); else st->print_cr(\" No JVM State Info\");\n" ); fprintf(fp, " st->print(\" # \");\n" ); fprintf(fp, " if( _jvms && _oop_map ) _oop_map->print_on(st);\n"); } else if(inst.is_ideal_safepoint()) { - fprintf(fp, " st->print(\"\");\n" ); + fprintf(fp, " st->print_raw(\"\");\n" ); fprintf(fp, " if (_jvms) _jvms->format(ra, this, st); else st->print_cr(\" No JVM State Info\");\n" ); fprintf(fp, " st->print(\" # \");\n" ); fprintf(fp, " if( _jvms && _oop_map ) _oop_map->print_on(st);\n"); @@ -1551,7 +1555,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"); @@ -1596,16 +1613,20 @@ // Each instruction attribute results in a virtual call of same name. // The ins_cost is not handled here. Attribute *attr = instr->_attribs; - bool avoid_back_to_back = false; + Attribute *avoid_back_to_back_attr = NULL; while (attr != NULL) { - if (strcmp(attr->_ident,"ins_cost") && - strcmp(attr->_ident,"ins_short_branch")) { - fprintf(fp," int %s() const { return %s; }\n", - attr->_ident, attr->_val); + if (strcmp (attr->_ident, "ins_is_TrapBasedCheckNode") == 0) { + fprintf(fp, " virtual bool is_TrapBasedCheckNode() const { return %s; }\n", attr->_val); + } else if (strcmp (attr->_ident, "ins_cost") != 0 && + strncmp(attr->_ident, "ins_field_", 10) != 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") != 0) { + 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_avoid_back_to_back") == 0) { + avoid_back_to_back_attr = attr; + } attr = (Attribute *)attr->_next; } @@ -1619,7 +1640,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->postalloc_expands()) { + fprintf(fp," virtual bool requires_postalloc_expand() const { return true; }\n"); + fprintf(fp," virtual void postalloc_expand(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 @@ -1636,6 +1662,19 @@ instr->ideal_Opcode(_globalNames) ); } + if (instr->needs_constant_base() && + !instr->is_mach_constant()) { // These inherit the funcion from MachConstantNode. + fprintf(fp," virtual uint mach_constant_base_node_input() const { "); + if (instr->is_ideal_call() != Form::invalid_type && + instr->is_ideal_call() != Form::JAVA_LEAF) { + // MachConstantBase goes behind arguments, but before jvms. + fprintf(fp,"assert(tf() && tf()->domain(), \"\"); return tf()->domain()->cnt();"); + } else { + fprintf(fp,"return req()-1;"); + } + fprintf(fp," }\n"); + } + // Allow machine-independent optimization, invert the sense of the IF test if( instr->is_ideal_if() ) { fprintf(fp," virtual void negate() { \n"); @@ -1759,11 +1798,11 @@ } // flag: if this instruction should not be generated back to back. - if ( avoid_back_to_back ) { - if ( node_flags_set ) { - fprintf(fp," | Flag_avoid_back_to_back"); + if (avoid_back_to_back_attr != NULL) { + if (node_flags_set) { + fprintf(fp," | (%s)", avoid_back_to_back_attr->_val); } else { - fprintf(fp,"init_flags(Flag_avoid_back_to_back"); + fprintf(fp,"init_flags((%s)", avoid_back_to_back_attr->_val); node_flags_set = true; } } @@ -1804,6 +1843,7 @@ if( instr->expands() || instr->needs_projections() || instr->has_temps() || instr->is_mach_constant() || + instr->needs_constant_base() || instr->_matrule != NULL && instr->num_opnds() != instr->num_unique_opnds() ) { fprintf(fp," virtual MachNode *Expand(State *state, Node_List &proj_list, Node* mem);\n"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/asm/assembler.hpp --- a/src/share/vm/asm/assembler.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/asm/assembler.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -204,10 +204,11 @@ CodeSection* _code_section; // section within the code buffer OopRecorder* _oop_recorder; // support for relocInfo::oop_type + public: // Code emission & accessing address addr_at(int pos) const { return code_section()->start() + 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, pc()); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/asm/codeBuffer.cpp --- a/src/share/vm/asm/codeBuffer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/asm/codeBuffer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -987,7 +987,7 @@ for (csize_t step; ptr < end(); ptr += step) { step = end() - ptr; if (step > jintSize * 4) step = jintSize * 4; - tty->print(PTR_FORMAT ": ", ptr); + tty->print(INTPTR_FORMAT ": ", p2i(ptr)); while (step > 0) { tty->print(" " PTR32_FORMAT, *(jint*)ptr); ptr += jintSize; @@ -1154,10 +1154,10 @@ void CodeSection::print(const char* name) { csize_t locs_size = locs_end() - locs_start(); tty->print_cr(" %7s.code = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d)%s", - name, start(), end(), limit(), size(), capacity(), + name, p2i(start()), p2i(end()), p2i(limit()), size(), capacity(), is_frozen()? " [frozen]": ""); tty->print_cr(" %7s.locs = " PTR_FORMAT " : " PTR_FORMAT " : " PTR_FORMAT " (%d of %d) point=%d", - name, locs_start(), locs_end(), locs_limit(), locs_size, locs_capacity(), locs_point_off()); + name, p2i(locs_start()), p2i(locs_end()), p2i(locs_limit()), locs_size, locs_capacity(), locs_point_off()); if (PrintRelocations) { RelocIterator iter(this); iter.print(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/asm/codeBuffer.hpp --- a/src/share/vm/asm/codeBuffer.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/asm/codeBuffer.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -173,7 +173,7 @@ bool allocates(address pc) const { return pc >= _start && pc < _limit; } bool allocates2(address pc) const { return pc >= _start && pc <= _limit; } - void set_end(address pc) { assert(allocates2(pc), err_msg("not in CodeBuffer memory: " PTR_FORMAT " <= " PTR_FORMAT " <= " PTR_FORMAT, _start, pc, _limit)); _end = pc; } + void set_end(address pc) { assert(allocates2(pc), err_msg("not in CodeBuffer memory: " PTR_FORMAT " <= " PTR_FORMAT " <= " INTPTR_FORMAT, p2i(_start), p2i(pc), p2i(_limit))); _end = pc; } void set_mark(address pc) { assert(contains2(pc), "not in codeBuffer"); _mark = pc; } void set_mark_off(int offset) { assert(contains2(offset+_start),"not in codeBuffer"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/asm/register.hpp --- a/src/share/vm/asm/register.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/asm/register.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -118,8 +118,8 @@ ) { assert( a != b, - err_msg_res("registers must be different: a=%d, b=%d", - a, b) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT "", + p2i(a), p2i(b)) ); } @@ -132,8 +132,9 @@ assert( a != b && a != c && b != c, - err_msg_res("registers must be different: a=%d, b=%d, c=%d", - a, b, c) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c)) ); } @@ -148,8 +149,9 @@ a != b && a != c && a != d && b != c && b != d && c != d, - err_msg_res("registers must be different: a=%d, b=%d, c=%d, d=%d", - a, b, c, d) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d)) ); } @@ -166,8 +168,9 @@ && b != c && b != d && b != e && c != d && c != e && d != e, - err_msg_res("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d", - a, b, c, d, e) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e)) ); } @@ -186,8 +189,10 @@ && c != d && c != e && c != f && d != e && d != f && e != f, - err_msg_res("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d", - a, b, c, d, e, f) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f)) ); } @@ -208,8 +213,10 @@ && d != e && d != f && d != g && e != f && e != g && f != g, - err_msg_res("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d", - a, b, c, d, e, f, g) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g)) ); } @@ -232,8 +239,10 @@ && e != f && e != g && e != h && f != g && f != h && g != h, - err_msg_res("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d", - a, b, c, d, e, f, g, h) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h)) ); } @@ -258,8 +267,11 @@ && f != g && f != h && f != i && g != h && g != i && h != i, - err_msg_res("registers must be different: a=%d, b=%d, c=%d, d=%d, e=%d, f=%d, g=%d, h=%d, i=%d", - a, b, c, d, e, f, g, h, i) + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT + ", i=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i)) ); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_CFGPrinter.cpp --- a/src/share/vm/c1/c1_CFGPrinter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_CFGPrinter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -50,7 +50,7 @@ void inc_indent(); void dec_indent(); - void print(const char* format, ...); + void print(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); void print_begin(const char* tag); void print_end(const char* tag); @@ -161,7 +161,7 @@ print("name \"%s\"", method_name(_compilation->method(), true)); print("method \"%s\"", method_name(_compilation->method())); - print("date "INT64_FORMAT, os::javaTimeMillis()); + print("date "INT64_FORMAT, (int64_t) os::javaTimeMillis()); print_end("compilation"); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_Compilation.cpp --- a/src/share/vm/c1/c1_Compilation.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_Compilation.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -546,6 +546,7 @@ , _code(buffer_blob) , _has_access_indexed(false) , _current_instruction(NULL) +, _interpreter_frame_size(0) #ifndef PRODUCT , _last_instruction_printed(NULL) #endif // PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_Compilation.hpp --- a/src/share/vm/c1/c1_Compilation.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_Compilation.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -88,6 +88,7 @@ CodeOffsets _offsets; CodeBuffer _code; bool _has_access_indexed; + int _interpreter_frame_size; // Stack space needed in case of a deoptimization // compilation helpers void initialize(); @@ -259,6 +260,21 @@ } ciKlass* cha_exact_type(ciType* type); + + // Dump inlining replay data to the stream. + void dump_inline_data(outputStream* out) { /* do nothing now */ } + + // How much stack space would the interpreter need in case of a + // deoptimization (worst case) + void update_interpreter_frame_size(int size) { + if (_interpreter_frame_size < size) { + _interpreter_frame_size = size; + } + } + + int interpreter_frame_size() const { + return _interpreter_frame_size; + } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_FrameMap.cpp --- a/src/share/vm/c1/c1_FrameMap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_FrameMap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_GraphBuilder.cpp --- a/src/share/vm/c1/c1_GraphBuilder.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_GraphBuilder.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1569,6 +1569,7 @@ default: constant = new Constant(as_ValueType(field_val)); } + // Stable static fields are checked for non-default values in ciField::initialize_from(). } if (constant != NULL) { push(type, append(constant)); @@ -1610,6 +1611,10 @@ default: constant = new Constant(as_ValueType(field_val)); } + if (FoldStableValues && field->is_stable() && field_val.is_null_or_zero()) { + // Stable field with default value can't be constant. + constant = NULL; + } } else { // For CallSite objects treat the target field as a compile time constant. if (const_oop->is_call_site()) { @@ -1697,6 +1702,15 @@ return NULL; } +void GraphBuilder::check_args_for_profiling(Values* obj_args, int expected) { +#ifdef ASSERT + bool ignored_will_link; + ciSignature* declared_signature = NULL; + ciMethod* real_target = method()->get_method_at_bci(bci(), ignored_will_link, &declared_signature); + assert(expected == obj_args->length() || real_target->is_method_handle_intrinsic(), "missed on arg?"); +#endif +} + // Collect arguments that we want to profile in a list Values* GraphBuilder::collect_args_for_profiling(Values* args, ciMethod* target, bool may_have_receiver) { int start = 0; @@ -1705,13 +1719,14 @@ return NULL; } int s = obj_args->size(); - for (int i = start, j = 0; j < s; i++) { + // if called through method handle invoke, some arguments may have been popped + for (int i = start, j = 0; j < s && i < args->length(); i++) { if (args->at(i)->type()->is_object_kind()) { obj_args->push(args->at(i)); j++; } } - assert(s == obj_args->length(), "missed on arg?"); + check_args_for_profiling(obj_args, s); return obj_args; } @@ -1983,7 +1998,13 @@ if (!UseInlineCaches && is_loaded && code == Bytecodes::_invokevirtual && !target->can_be_statically_bound()) { // Find a vtable index if one is available - vtable_index = target->resolve_vtable_index(calling_klass, callee_holder); + // For arrays, callee_holder is Object. Resolving the call with + // Object would allow an illegal call to finalize() on an + // array. We use holder instead: illegal calls to finalize() won't + // be compiled as vtable calls (IC call resolution will catch the + // illegal call) and the few legal calls on array types won't be + // either. + vtable_index = target->resolve_vtable_index(calling_klass, holder); } #endif @@ -3769,11 +3790,14 @@ } // now perform tests that are based on flag settings - if (callee->force_inline()) { - if (inline_level() > MaxForceInlineLevel) INLINE_BAILOUT("MaxForceInlineLevel"); - print_inlining(callee, "force inline by annotation"); - } else if (callee->should_inline()) { - print_inlining(callee, "force inline by CompileOracle"); + if (callee->force_inline() || callee->should_inline()) { + if (inline_level() > MaxForceInlineLevel ) INLINE_BAILOUT("MaxForceInlineLevel"); + if (recursive_inline_level(callee) > MaxRecursiveInlineLevel) INLINE_BAILOUT("recursive inlining too deep"); + + const char* msg = ""; + if (callee->force_inline()) msg = "force inline by annotation"; + if (callee->should_inline()) msg = "force inline by CompileOracle"; + print_inlining(callee, msg); } else { // use heuristic controls on inlining if (inline_level() > MaxInlineLevel ) INLINE_BAILOUT("inlining too deep"); @@ -3842,14 +3866,7 @@ j++; } } -#ifdef ASSERT - { - bool ignored_will_link; - ciSignature* declared_signature = NULL; - ciMethod* real_target = method()->get_method_at_bci(bci(), ignored_will_link, &declared_signature); - assert(s == obj_args->length() || real_target->is_method_handle_intrinsic(), "missed on arg?"); - } -#endif + check_args_for_profiling(obj_args, s); } profile_call(callee, recv, holder_known ? callee->holder() : NULL, obj_args, true); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_GraphBuilder.hpp --- a/src/share/vm/c1/c1_GraphBuilder.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_GraphBuilder.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -392,6 +392,7 @@ Values* args_list_for_profiling(ciMethod* target, int& start, bool may_have_receiver); Values* collect_args_for_profiling(Values* args, ciMethod* target, bool may_have_receiver); + void check_args_for_profiling(Values* obj_args, int expected); public: NOT_PRODUCT(void print_stats();) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_IR.cpp --- a/src/share/vm/c1/c1_IR.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_IR.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -226,8 +226,38 @@ _oop_map->set_oop(name); } +// Mirror the stack size calculation in the deopt code +// How much stack space would we need at this point in the program in +// case of deoptimization? +int CodeEmitInfo::interpreter_frame_size() const { + ValueStack* state = _stack; + int size = 0; + int callee_parameters = 0; + int callee_locals = 0; + int extra_args = state->scope()->method()->max_stack() - state->stack_size(); + while (state != NULL) { + int locks = state->locks_size(); + int temps = state->stack_size(); + bool is_top_frame = (state == _stack); + ciMethod* method = state->scope()->method(); + int frame_size = BytesPerWord * Interpreter::size_activation(method->max_stack(), + temps + callee_parameters, + extra_args, + locks, + callee_parameters, + callee_locals, + is_top_frame); + size += frame_size; + + callee_parameters = method->size_of_parameters(); + callee_locals = method->max_locals(); + extra_args = 0; + state = state->caller_state(); + } + return size + Deoptimization::last_frame_adjust(0, callee_locals) * BytesPerWord; +} // Implementation of IR diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_IR.hpp --- a/src/share/vm/c1/c1_IR.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_IR.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -281,6 +281,8 @@ bool is_method_handle_invoke() const { return _is_method_handle_invoke; } void set_is_method_handle_invoke(bool x) { _is_method_handle_invoke = x; } + + int interpreter_frame_size() const; }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_InstructionPrinter.cpp --- a/src/share/vm/c1/c1_InstructionPrinter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_InstructionPrinter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -134,23 +134,23 @@ if (value->is_null_object()) { output()->print("null"); } else if (!value->is_loaded()) { - output()->print("", value); + output()->print("", p2i(value)); } else { - output()->print(""); } } else if (type->as_InstanceConstant() != NULL) { ciInstance* value = type->as_InstanceConstant()->value(); if (value->is_loaded()) { - output()->print(""); } else { - output()->print("", value); + output()->print("", p2i(value)); } } else if (type->as_ArrayConstant() != NULL) { - output()->print("", type->as_ArrayConstant()->value()->constant_encoding()); + output()->print("", p2i(type->as_ArrayConstant()->value()->constant_encoding())); } else if (type->as_ClassConstant() != NULL) { ciInstanceKlass* klass = type->as_ClassConstant()->value(); if (!klass->is_loaded()) { @@ -268,7 +268,7 @@ void InstructionPrinter::print_unsafe_op(UnsafeOp* op, const char* name) { - output()->print(name); + output()->print("%s", name); output()->print(".("); } @@ -479,7 +479,7 @@ if (x->declared_type()->is_klass()) print_klass(x->declared_type()->as_klass()); else - output()->print(type2name(x->declared_type()->basic_type())); + output()->print("%s", type2name(x->declared_type()->basic_type())); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_LIR.cpp --- a/src/share/vm/c1/c1_LIR.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_LIR.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -1083,7 +1083,7 @@ void LIR_OpArrayCopy::emit_code(LIR_Assembler* masm) { masm->emit_arraycopy(this); - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } void LIR_OpUpdateCRC32::emit_code(LIR_Assembler* masm) { @@ -1100,20 +1100,20 @@ void LIR_OpAllocObj::emit_code(LIR_Assembler* masm) { masm->emit_alloc_obj(this); - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } void LIR_OpBranch::emit_code(LIR_Assembler* masm) { masm->emit_opBranch(this); if (stub()) { - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } } void LIR_OpConvert::emit_code(LIR_Assembler* masm) { masm->emit_opConvert(this); if (stub() != NULL) { - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } } @@ -1123,13 +1123,13 @@ void LIR_OpAllocArray::emit_code(LIR_Assembler* masm) { masm->emit_alloc_array(this); - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } void LIR_OpTypeCheck::emit_code(LIR_Assembler* masm) { masm->emit_opTypeCheck(this); if (stub()) { - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } } @@ -1144,7 +1144,7 @@ void LIR_OpLock::emit_code(LIR_Assembler* masm) { masm->emit_lock(this); if (stub()) { - masm->emit_code_stub(stub()); + masm->append_code_stub(stub()); } } @@ -1563,15 +1563,15 @@ } else if (is_virtual()) { out->print("R%d", vreg_number()); } else if (is_single_cpu()) { - out->print(as_register()->name()); + out->print("%s", as_register()->name()); } else if (is_double_cpu()) { - out->print(as_register_hi()->name()); - out->print(as_register_lo()->name()); + out->print("%s", as_register_hi()->name()); + out->print("%s", as_register_lo()->name()); #if defined(X86) } else if (is_single_xmm()) { - out->print(as_xmm_float_reg()->name()); + out->print("%s", as_xmm_float_reg()->name()); } else if (is_double_xmm()) { - out->print(as_xmm_double_reg()->name()); + out->print("%s", as_xmm_double_reg()->name()); } else if (is_single_fpu()) { out->print("fpu%d", fpu_regnr()); } else if (is_double_fpu()) { @@ -1583,9 +1583,9 @@ out->print("d%d", fpu_regnrLo() >> 1); #else } else if (is_single_fpu()) { - out->print(as_float_reg()->name()); + out->print("%s", as_float_reg()->name()); } else if (is_double_fpu()) { - out->print(as_double_reg()->name()); + out->print("%s", as_double_reg()->name()); #endif } else if (is_illegal()) { @@ -1611,9 +1611,9 @@ case T_LONG: out->print("lng:" JLONG_FORMAT, as_jlong()); break; case T_FLOAT: out->print("flt:%f", as_jfloat()); break; case T_DOUBLE: out->print("dbl:%f", as_jdouble()); break; - case T_OBJECT: out->print("obj:0x%x", as_jobject()); break; - case T_METADATA: out->print("metadata:0x%x", as_metadata());break; - default: out->print("%3d:0x%x",type(), as_jdouble()); break; + case T_OBJECT: out->print("obj:" INTPTR_FORMAT, p2i(as_jobject())); break; + case T_METADATA: out->print("metadata:" INTPTR_FORMAT, p2i(as_metadata()));break; + default: out->print("%3d:0x" UINT64_FORMAT_X, type(), (uint64_t)as_jlong()); break; } } @@ -1629,7 +1629,7 @@ case times_8: out->print(" * 8"); break; } } - out->print(" Disp: %d", _disp); + out->print(" Disp: " INTX_FORMAT, _disp); } // debug output of block header without InstructionPrinter @@ -1703,7 +1703,7 @@ } else { out->print(" "); } - out->print(name()); out->print(" "); + out->print("%s ", name()); print_instr(out); if (info() != NULL) out->print(" [bci:%d]", info()->stack()->bci()); #ifdef ASSERT @@ -1833,7 +1833,7 @@ // LIR_OpJavaCall void LIR_OpJavaCall::print_instr(outputStream* out) const { out->print("call: "); - out->print("[addr: 0x%x]", address()); + out->print("[addr: " INTPTR_FORMAT "]", p2i(address())); if (receiver()->is_valid()) { out->print(" [recv: "); receiver()->print(out); out->print("]"); } @@ -1844,7 +1844,7 @@ // LIR_OpLabel void LIR_OpLabel::print_instr(outputStream* out) const { - out->print("[label:0x%x]", _label); + out->print("[label:" INTPTR_FORMAT "]", p2i(_label)); } // LIR_OpArrayCopy @@ -1911,7 +1911,7 @@ // LIR_Op1 void LIR_OpRTCall::print_instr(outputStream* out) const { intx a = (intx)addr(); - out->print(Runtime1::name_for_address(addr())); + out->print("%s", Runtime1::name_for_address(addr())); out->print(" "); tmp()->print(out); } @@ -1934,10 +1934,10 @@ } else if (stub() != NULL) { out->print("["); stub()->print_name(out); - out->print(": 0x%x]", stub()); + out->print(": " INTPTR_FORMAT "]", p2i(stub())); if (stub()->info() != NULL) out->print(" [bci:%d]", stub()->info()->stack()->bci()); } else { - out->print("[label:0x%x] ", label()); + out->print("[label:" INTPTR_FORMAT "] ", p2i(label())); } if (ublock() != NULL) { out->print("unordered: [B%d] ", ublock()->block_id()); @@ -2004,7 +2004,7 @@ tmp4()->print(out); out->print(" "); out->print("[hdr:%d]", header_size()); out->print(" "); out->print("[obj:%d]", object_size()); out->print(" "); - out->print("[lbl:0x%x]", stub()->entry()); + out->print("[lbl:" INTPTR_FORMAT "]", p2i(stub()->entry())); } void LIR_OpRoundFP::print_instr(outputStream* out) const { @@ -2037,7 +2037,7 @@ tmp3()->print(out); out->print(" "); tmp4()->print(out); out->print(" "); out->print("[type:0x%x]", type()); out->print(" "); - out->print("[label:0x%x]", stub()->entry()); + out->print("[label:" INTPTR_FORMAT "]", p2i(stub()->entry())); } @@ -2074,7 +2074,7 @@ if (_scratch->is_valid()) { _scratch->print(out); out->print(" "); } - out->print("[lbl:0x%x]", stub()->entry()); + out->print("[lbl:" INTPTR_FORMAT "]", p2i(stub()->entry())); } #ifdef ASSERT @@ -2082,7 +2082,7 @@ print_condition(out, condition()); out->print(" "); in_opr1()->print(out); out->print(" "); in_opr2()->print(out); out->print(", \""); - out->print(msg()); out->print("\""); + out->print("%s", msg()); out->print("\""); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_LIR.hpp --- a/src/share/vm/c1/c1_LIR.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_LIR.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1127,6 +1127,7 @@ virtual void print_instr(outputStream* out) const = 0; virtual void print_on(outputStream* st) const PRODUCT_RETURN; + virtual bool is_patching() { return false; } virtual LIR_OpCall* as_OpCall() { return NULL; } virtual LIR_OpJavaCall* as_OpJavaCall() { return NULL; } virtual LIR_OpLabel* as_OpLabel() { return NULL; } @@ -1387,6 +1388,7 @@ return (LIR_MoveKind)_flags; } + virtual bool is_patching() { return _patch != lir_patch_none; } virtual void emit_code(LIR_Assembler* masm); virtual LIR_Op1* as_Op1() { return this; } virtual const char * name() const PRODUCT_RETURN0; @@ -1619,6 +1621,7 @@ int profiled_bci() const { return _profiled_bci; } bool should_profile() const { return _should_profile; } + virtual bool is_patching() { return _info_for_patch != NULL; } virtual void emit_code(LIR_Assembler* masm); virtual LIR_OpTypeCheck* as_OpTypeCheck() { return this; } void print_instr(outputStream* out) const PRODUCT_RETURN; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_LIRAssembler.cpp --- a/src/share/vm/c1/c1_LIRAssembler.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_LIRAssembler.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -58,7 +58,7 @@ _masm->nop(); } patch->install(_masm, patch_code, obj, info); - append_patching_stub(patch); + append_code_stub(patch); #ifdef ASSERT Bytecodes::Code code = info->scope()->method()->java_code_at_bci(info->stack()->bci()); @@ -131,11 +131,6 @@ } -void LIR_Assembler::append_patching_stub(PatchingStub* stub) { - _slow_case_stubs->append(stub); -} - - void LIR_Assembler::check_codespace() { CodeSection* cs = _masm->code_section(); if (cs->remaining() < (int)(NOT_LP64(1*K)LP64_ONLY(2*K))) { @@ -144,7 +139,7 @@ } -void LIR_Assembler::emit_code_stub(CodeStub* stub) { +void LIR_Assembler::append_code_stub(CodeStub* stub) { _slow_case_stubs->append(stub); } @@ -190,6 +185,13 @@ return _masm->pc(); } +// To bang the stack of this compiled method we use the stack size +// that the interpreter would need in case of a deoptimization. This +// removes the need to bang the stack in the deoptimization blob which +// in turn simplifies stack overflow handling. +int LIR_Assembler::bang_size_in_bytes() const { + return MAX2(initial_frame_size_in_bytes(), _compilation->interpreter_frame_size()); +} void LIR_Assembler::emit_exception_entries(ExceptionInfoList* info_list) { for (int i = 0; i < info_list->length(); i++) { @@ -435,7 +437,7 @@ void LIR_Assembler::add_debug_info_for_null_check(int pc_offset, CodeEmitInfo* cinfo) { ImplicitNullCheckStub* stub = new ImplicitNullCheckStub(pc_offset, cinfo); - emit_code_stub(stub); + append_code_stub(stub); } void LIR_Assembler::add_debug_info_for_div0_here(CodeEmitInfo* info) { @@ -444,7 +446,7 @@ void LIR_Assembler::add_debug_info_for_div0(int pc_offset, CodeEmitInfo* cinfo) { DivByZeroStub* stub = new DivByZeroStub(pc_offset, cinfo); - emit_code_stub(stub); + append_code_stub(stub); } void LIR_Assembler::emit_rtcall(LIR_OpRTCall* op) { @@ -797,7 +799,7 @@ void LIR_Assembler::build_frame() { - _masm->build_frame(initial_frame_size_in_bytes()); + _masm->build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes()); } @@ -858,9 +860,7 @@ void LIR_Assembler::verify_oop_map(CodeEmitInfo* info) { #ifndef PRODUCT - if (VerifyOopMaps || VerifyOops) { - bool v = VerifyOops; - VerifyOops = true; + if (VerifyOops) { OopMapStream s(info->oop_map()); while (!s.is_done()) { OopMapValue v = s.current(); @@ -883,7 +883,6 @@ s.next(); } - VerifyOops = v; } #endif } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_LIRAssembler.hpp --- a/src/share/vm/c1/c1_LIRAssembler.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_LIRAssembler.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -132,7 +132,8 @@ int code_offset() const; address pc() const; - int initial_frame_size_in_bytes(); + int initial_frame_size_in_bytes() const; + int bang_size_in_bytes() const; // test for constants which can be encoded directly in instructions static bool is_small_constant(LIR_Opr opr); @@ -143,7 +144,7 @@ // stubs void emit_slow_case_stubs(); void emit_static_call_stub(); - void emit_code_stub(CodeStub* op); + void append_code_stub(CodeStub* op); void add_call_info_here(CodeEmitInfo* info) { add_call_info(code_offset(), info); } // code patterns diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_LIRGenerator.cpp --- a/src/share/vm/c1/c1_LIRGenerator.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_LIRGenerator.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -2634,8 +2634,10 @@ // LIR_Assembler::emit_profile_type() from emitting useless code profiled_k = ciTypeEntries::with_status(result, profiled_k); } - if (exact_signature_k != NULL && exact_klass != exact_signature_k) { - assert(exact_klass == NULL, "obj and signature disagree?"); + // exact_klass and exact_signature_k can be both non NULL but + // different if exact_klass is loaded after the ciObject for + // exact_signature_k is created. + if (exact_klass == NULL && exact_signature_k != NULL && exact_klass != exact_signature_k) { // sometimes the type of the signature is better than the best type // the compiler has exact_klass = exact_signature_k; @@ -2646,8 +2648,7 @@ if (improved_klass == NULL) { improved_klass = comp->cha_exact_type(callee_signature_k); } - if (improved_klass != NULL && exact_klass != improved_klass) { - assert(exact_klass == NULL, "obj and signature disagree?"); + if (exact_klass == NULL && improved_klass != NULL && exact_klass != improved_klass) { exact_klass = exact_signature_k; } } @@ -3186,8 +3187,8 @@ #ifdef ASSERT Bytecodes::Code code = x->method()->raw_code_at_bci(x->bci_of_invoke()); int n = x->nb_profiled_args(); - assert(MethodData::profile_parameters() && x->inlined() && - ((code == Bytecodes::_invokedynamic && n <= 1) || (code == Bytecodes::_invokehandle && n <= 2)), + assert(MethodData::profile_parameters() && (MethodData::profile_arguments_jsr292_only() || + (x->inlined() && ((code == Bytecodes::_invokedynamic && n <= 1) || (code == Bytecodes::_invokehandle && n <= 2)))), "only at JSR292 bytecodes"); #endif } @@ -3288,7 +3289,10 @@ ciSignature* signature_at_call = NULL; x->method()->get_method_at_bci(bci, ignored_will_link, &signature_at_call); - ciKlass* exact = profile_type(md, 0, md->byte_offset_of_slot(data, ret->type_offset()), + // The offset within the MDO of the entry to update may be too large + // to be used in load/store instructions on some platforms. So have + // profile_type() compute the address of the profile in a register. + ciKlass* exact = profile_type(md, md->byte_offset_of_slot(data, ret->type_offset()), 0, ret->type(), x->ret(), mdp, !x->needs_null_check(), signature_at_call->return_type()->as_klass(), diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_LinearScan.cpp --- a/src/share/vm/c1/c1_LinearScan.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_LinearScan.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -2382,16 +2382,6 @@ int arg_count = frame_map()->oop_map_arg_count(); OopMap* map = new OopMap(frame_size, arg_count); - // Check if this is a patch site. - bool is_patch_info = false; - if (op->code() == lir_move) { - assert(!is_call_site, "move must not be a call site"); - assert(op->as_Op1() != NULL, "move must be LIR_Op1"); - LIR_Op1* move = (LIR_Op1*)op; - - is_patch_info = move->patch_code() != lir_patch_none; - } - // Iterate through active intervals for (Interval* interval = iw->active_first(fixedKind); interval != Interval::end(); interval = interval->next()) { int assigned_reg = interval->assigned_reg(); @@ -2406,7 +2396,7 @@ // moves, any intervals which end at this instruction are included // in the oop map since we may safepoint while doing the patch // before we've consumed the inputs. - if (is_patch_info || op->id() < interval->current_to()) { + if (op->is_patching() || op->id() < interval->current_to()) { // caller-save registers must not be included into oop-maps at calls assert(!is_call_site || assigned_reg >= nof_regs || !is_caller_save(assigned_reg), "interval is in a caller-save register at a call -> register will be overwritten"); @@ -2451,6 +2441,9 @@ CodeEmitInfo* info = visitor.info_at(i); OopMap* oop_map = first_oop_map; + // compute worst case interpreter size in case of a deoptimization + _compilation->update_interpreter_frame_size(info->interpreter_frame_size()); + if (info->stack()->locks_size() != first_info->stack()->locks_size()) { // this info has a different number of locks then the precomputed oop map // (possible for lock and unlock instructions) -> compute oop map with diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_MacroAssembler.hpp --- a/src/share/vm/c1/c1_MacroAssembler.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_MacroAssembler.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -39,7 +39,7 @@ void explicit_null_check(Register base); void inline_cache_check(Register receiver, Register iCache); - void build_frame(int frame_size_in_bytes); + void build_frame(int frame_size_in_bytes, int bang_size_in_bytes); void remove_frame(int frame_size_in_bytes); void unverified_entry(Register receiver, Register ic_klass); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_RangeCheckElimination.cpp --- a/src/share/vm/c1/c1_RangeCheckElimination.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_RangeCheckElimination.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -62,10 +62,10 @@ _optimistic = ir->compilation()->is_optimistic(); TRACE_RANGE_CHECK_ELIMINATION( - tty->print_cr(""); + tty->cr(); tty->print_cr("Range check elimination"); ir->method()->print_name(tty); - tty->print_cr(""); + tty->cr(); ); TRACE_RANGE_CHECK_ELIMINATION( @@ -1024,7 +1024,7 @@ tty->print("i%d", phi->id()); tty->print(": "); bound->print(); - tty->print_cr(""); + tty->cr(); ); } }); @@ -1039,7 +1039,7 @@ tty->print("i%d", instr->id()); tty->print(": "); bound->print(); - tty->print_cr(""); + tty->cr(); ); } } @@ -1400,7 +1400,7 @@ // print void RangeCheckEliminator::Bound::print() { - tty->print(""); + tty->print("%s", ""); if (this->_lower_instr || this->_lower != min_jint) { if (this->_lower_instr) { tty->print("i%d", this->_lower_instr->id()); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_Runtime1.cpp --- a/src/share/vm/c1/c1_Runtime1.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_Runtime1.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -532,8 +532,8 @@ if (TraceExceptions) { ttyLocker ttyl; ResourceMark rm; - tty->print_cr("Exception <%s> (0x%x) thrown in compiled method <%s> at PC " PTR_FORMAT " for thread 0x%x", - exception->print_value_string(), (address)exception(), nm->method()->print_value_string(), pc, thread); + tty->print_cr("Exception <%s> (" INTPTR_FORMAT ") thrown in compiled method <%s> at PC " INTPTR_FORMAT " for thread " INTPTR_FORMAT "", + exception->print_value_string(), p2i((address)exception()), nm->method()->print_value_string(), p2i(pc), p2i(thread)); } // for AbortVMOnException flag NOT_PRODUCT(Exceptions::debug_check_abort(exception)); @@ -563,7 +563,7 @@ ttyLocker ttyl; ResourceMark rm; tty->print_cr("Thread " PTR_FORMAT " continuing at PC " PTR_FORMAT " for exception thrown at PC " PTR_FORMAT, - thread, continuation, pc); + p2i(thread), p2i(continuation), p2i(pc)); } return continuation; @@ -970,8 +970,8 @@ address copy_buff = stub_location - *byte_skip - *byte_count; address being_initialized_entry = stub_location - *being_initialized_entry_offset; if (TracePatching) { - tty->print_cr(" Patching %s at bci %d at address 0x%x (%s)", Bytecodes::name(code), bci, - instr_pc, (stub_id == Runtime1::access_field_patching_id) ? "field" : "klass"); + tty->print_cr(" Patching %s at bci %d at address " INTPTR_FORMAT " (%s)", Bytecodes::name(code), bci, + p2i(instr_pc), (stub_id == Runtime1::access_field_patching_id) ? "field" : "klass"); nmethod* caller_code = CodeCache::find_nmethod(caller_frame.pc()); assert(caller_code != NULL, "nmethod not found"); @@ -1430,7 +1430,7 @@ methodHandle inlinee = methodHandle(vfst.method()); inlinee->print_short_name(&ss1); m->print_short_name(&ss2); - tty->print_cr("Predicate failed trap in method %s at bci %d inlined in %s at pc %x", ss1.as_string(), vfst.bci(), ss2.as_string(), caller_frame.pc()); + tty->print_cr("Predicate failed trap in method %s at bci %d inlined in %s at pc " INTPTR_FORMAT, ss1.as_string(), vfst.bci(), ss2.as_string(), p2i(caller_frame.pc())); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_ValueType.hpp --- a/src/share/vm/c1/c1_ValueType.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_ValueType.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -175,7 +175,7 @@ ValueType* join(ValueType* y) const; // debugging - void print(outputStream* s = tty) { s->print(name()); } + void print(outputStream* s = tty) { s->print("%s", name()); } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/c1/c1_globals.hpp --- a/src/share/vm/c1/c1_globals.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/c1/c1_globals.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -269,9 +272,6 @@ develop(bool, PrintNotLoaded, false, \ "Prints where classes are not loaded during code generation") \ \ - notproduct(bool, VerifyOopMaps, false, \ - "Adds oopmap verification code to the generated code") \ - \ develop(bool, PrintLIR, false, \ "print low-level IR") \ \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/bcEscapeAnalyzer.cpp --- a/src/share/vm/ci/bcEscapeAnalyzer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/bcEscapeAnalyzer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -158,6 +158,9 @@ void BCEscapeAnalyzer::set_method_escape(ArgumentMap vars) { clear_bits(vars, _arg_local); + if (vars.contains_allocated()) { + _allocated_escapes = true; + } } void BCEscapeAnalyzer::set_global_escape(ArgumentMap vars, bool merge) { @@ -1287,10 +1290,10 @@ tty->print_cr("class of method is not initialized."); else if (_level > MaxBCEAEstimateLevel) tty->print_cr("level (%d) exceeds MaxBCEAEstimateLevel (%d).", - _level, MaxBCEAEstimateLevel); + _level, (int) MaxBCEAEstimateLevel); else if (method()->code_size() > MaxBCEAEstimateSize) - tty->print_cr("code size (%d) exceeds MaxBCEAEstimateSize.", - method()->code_size(), MaxBCEAEstimateSize); + tty->print_cr("code size (%d) exceeds MaxBCEAEstimateSize (%d).", + method()->code_size(), (int) MaxBCEAEstimateSize); else ShouldNotReachHere(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciClassList.hpp --- a/src/share/vm/ci/ciClassList.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciClassList.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -103,6 +103,7 @@ friend class ciMethodType; \ friend class ciReceiverTypeData; \ friend class ciTypeEntries; \ +friend class ciSpeculativeTrapData; \ friend class ciSymbol; \ friend class ciArray; \ friend class ciObjArray; \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciConstant.cpp --- a/src/share/vm/ci/ciConstant.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciConstant.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -48,7 +48,7 @@ tty->print("%d", _value._int); break; case T_LONG: - tty->print(INT64_FORMAT, _value._long); + tty->print(INT64_FORMAT, (int64_t)(_value._long)); break; case T_FLOAT: tty->print("%f", _value._float); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciEnv.cpp --- a/src/share/vm/ci/ciEnv.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciEnv.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -926,7 +926,8 @@ AbstractCompiler* compiler, int comp_level, bool has_unsafe_access, - bool has_wide_vectors) { + bool has_wide_vectors, + RTMState rtm_state) { VM_ENTRY_MARK; nmethod* nm = NULL; { @@ -973,6 +974,15 @@ methodHandle method(THREAD, target->get_Method()); +#if INCLUDE_RTM_OPT + if (!failing() && (rtm_state != NoRTM) && + (method()->method_data() != NULL) && + (method()->method_data()->rtm_state() != rtm_state)) { + // Preemptive decompile if rtm state was changed. + record_failure("RTM state change invalidated rtm code"); + } +#endif + if (failing()) { // While not a true deoptimization, it is a preemptive decompile. MethodData* mdo = method()->method_data(); @@ -999,13 +1009,15 @@ frame_words, oop_map_set, handler_table, inc_table, compiler, comp_level); - // Free codeBlobs code_buffer->free_blob(); if (nm != NULL) { nm->set_has_unsafe_access(has_unsafe_access); nm->set_has_wide_vectors(has_wide_vectors); +#if INCLUDE_RTM_OPT + nm->set_rtm_state(rtm_state); +#endif // Record successful registration. // (Put nm into the task handle *before* publishing to the Java heap.) @@ -1147,6 +1159,33 @@ // Don't change thread state and acquire any locks. // Safe to call from VM error reporter. + +void ciEnv::dump_compile_data(outputStream* out) { + CompileTask* task = this->task(); + Method* method = task->method(); + int entry_bci = task->osr_bci(); + int comp_level = task->comp_level(); + out->print("compile %s %s %s %d %d", + method->klass_name()->as_quoted_ascii(), + method->name()->as_quoted_ascii(), + method->signature()->as_quoted_ascii(), + entry_bci, comp_level); + if (compiler_data() != NULL) { + if (is_c2_compile(comp_level)) { // C2 or Shark +#ifdef COMPILER2 + // Dump C2 inlining data. + ((Compile*)compiler_data())->dump_inline_data(out); +#endif + } else if (is_c1_compile(comp_level)) { // C1 +#ifdef COMPILER1 + // Dump C1 inlining data. + ((Compilation*)compiler_data())->dump_inline_data(out); +#endif + } + } + out->cr(); +} + void ciEnv::dump_replay_data_unsafe(outputStream* out) { ResourceMark rm; #if INCLUDE_JVMTI @@ -1160,16 +1199,7 @@ for (int i = 0; i < objects->length(); i++) { objects->at(i)->dump_replay_data(out); } - CompileTask* task = this->task(); - Method* method = task->method(); - int entry_bci = task->osr_bci(); - int comp_level = task->comp_level(); - // Klass holder = method->method_holder(); - out->print_cr("compile %s %s %s %d %d", - method->klass_name()->as_quoted_ascii(), - method->name()->as_quoted_ascii(), - method->signature()->as_quoted_ascii(), - entry_bci, comp_level); + dump_compile_data(out); out->flush(); } @@ -1179,3 +1209,44 @@ dump_replay_data_unsafe(out); ) } + +void ciEnv::dump_replay_data(int compile_id) { + static char buffer[O_BUFLEN]; + int ret = jio_snprintf(buffer, O_BUFLEN, "replay_pid%p_compid%d.log", os::current_process_id(), compile_id); + if (ret > 0) { + int fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + FILE* replay_data_file = os::open(fd, "w"); + if (replay_data_file != NULL) { + fileStream replay_data_stream(replay_data_file, /*need_close=*/true); + dump_replay_data(&replay_data_stream); + tty->print_cr("# Compiler replay data is saved as: %s", buffer); + } else { + tty->print_cr("# Can't open file to dump replay data."); + } + } + } +} + +void ciEnv::dump_inline_data(int compile_id) { + static char buffer[O_BUFLEN]; + int ret = jio_snprintf(buffer, O_BUFLEN, "inline_pid%p_compid%d.log", os::current_process_id(), compile_id); + if (ret > 0) { + int fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd != -1) { + FILE* inline_data_file = os::open(fd, "w"); + if (inline_data_file != NULL) { + fileStream replay_data_stream(inline_data_file, /*need_close=*/true); + GUARDED_VM_ENTRY( + MutexLocker ml(Compile_lock); + dump_compile_data(&replay_data_stream); + ) + replay_data_stream.flush(); + tty->print("# Compiler inline data is saved as: "); + tty->print_cr("%s", buffer); + } else { + tty->print_cr("# Can't open file to dump inline data."); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciEnv.hpp --- a/src/share/vm/ci/ciEnv.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciEnv.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -363,7 +363,8 @@ AbstractCompiler* compiler, int comp_level, bool has_unsafe_access, - bool has_wide_vectors); + bool has_wide_vectors, + RTMState rtm_state = NoRTM); // Access to certain well known ciObjects. @@ -451,8 +452,11 @@ void metadata_do(void f(Metadata*)) { _factory->metadata_do(f); } // Dump the compilation replay data for the ciEnv to the stream. + void dump_replay_data(int compile_id); + void dump_inline_data(int compile_id); void dump_replay_data(outputStream* out); void dump_replay_data_unsafe(outputStream* out); + void dump_compile_data(outputStream* out); }; #endif // SHARE_VM_CI_CIENV_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciField.cpp --- a/src/share/vm/ci/ciField.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciField.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -138,6 +138,17 @@ return; } + // Access check based on declared_holder. canonical_holder should not be used + // to check access because it can erroneously succeed. If this check fails, + // propagate the declared holder to will_link() which in turn will bail out + // compilation for this field access. + if (!Reflection::verify_field_access(klass->get_Klass(), declared_holder->get_Klass(), canonical_holder, field_desc.access_flags(), true)) { + _holder = declared_holder; + _offset = -1; + _is_constant = false; + return; + } + assert(canonical_holder == field_desc.field_holder(), "just checking"); initialize_from(&field_desc); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciInstanceKlass.cpp --- a/src/share/vm/ci/ciInstanceKlass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciInstanceKlass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -292,7 +292,7 @@ // Implementation of the print method. void ciInstanceKlass::print_impl(outputStream* st) { ciKlass::print_impl(st); - GUARDED_VM_ENTRY(st->print(" loader=0x%x", (address)loader());) + GUARDED_VM_ENTRY(st->print(" loader=" INTPTR_FORMAT, p2i((address)loader()));) if (is_loaded()) { st->print(" loaded=true initialized=%s finalized=%s subklass=%s size=%d flags=", bool_to_str(is_initialized()), @@ -618,7 +618,7 @@ case T_SHORT: _out->print_cr("%d", mirror->short_field(fd->offset())); break; case T_CHAR: _out->print_cr("%d", mirror->char_field(fd->offset())); break; case T_INT: _out->print_cr("%d", mirror->int_field(fd->offset())); break; - case T_LONG: _out->print_cr(INT64_FORMAT, mirror->long_field(fd->offset())); break; + case T_LONG: _out->print_cr(INT64_FORMAT, (int64_t)(mirror->long_field(fd->offset()))); break; case T_FLOAT: { float f = mirror->float_field(fd->offset()); _out->print_cr("%d", *(int*)&f); @@ -626,7 +626,7 @@ } case T_DOUBLE: { double d = mirror->double_field(fd->offset()); - _out->print_cr(INT64_FORMAT, *(jlong*)&d); + _out->print_cr(INT64_FORMAT, *(int64_t*)&d); break; } case T_ARRAY: { @@ -656,7 +656,7 @@ _out->print_cr("\""); } else { const char* klass_name = value->klass()->name()->as_quoted_ascii(); - _out->print_cr(klass_name); + _out->print_cr("%s", klass_name); } } else { ShouldNotReachHere(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciMetadata.cpp --- a/src/share/vm/ci/ciMetadata.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciMetadata.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -38,7 +38,7 @@ void ciMetadata::print(outputStream* st) { st->print("<%s", type_string()); GUARDED_VM_ENTRY(print_impl(st);) - st->print(" ident=%d address=0x%x>", ident(), (address)this); + st->print(" ident=%d address=" INTPTR_FORMAT ">", ident(), p2i((address)this)); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciMethod.cpp --- a/src/share/vm/ci/ciMethod.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciMethod.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -80,6 +80,7 @@ _code_size = h_m()->code_size(); _intrinsic_id = h_m()->intrinsic_id(); _handler_count = h_m()->exception_table_length(); + _size_of_parameters = h_m()->size_of_parameters(); _uses_monitors = h_m()->access_flags().has_monitor_bytecodes(); _balanced_monitors = !_uses_monitors || h_m()->access_flags().is_monitor_matching(); _is_c1_compilable = !h_m()->is_not_c1_compilable(); @@ -1362,15 +1363,21 @@ #undef FETCH_FLAG_FROM_VM +void ciMethod::dump_name_as_ascii(outputStream* st) { + Method* method = get_Method(); + st->print("%s %s %s", + method->klass_name()->as_quoted_ascii(), + method->name()->as_quoted_ascii(), + method->signature()->as_quoted_ascii()); +} + void ciMethod::dump_replay_data(outputStream* st) { ResourceMark rm; Method* method = get_Method(); MethodCounters* mcs = method->method_counters(); - Klass* holder = method->method_holder(); - st->print_cr("ciMethod %s %s %s %d %d %d %d %d", - holder->name()->as_quoted_ascii(), - method->name()->as_quoted_ascii(), - method->signature()->as_quoted_ascii(), + st->print("ciMethod "); + dump_name_as_ascii(st); + st->print_cr(" %d %d %d %d %d", mcs == NULL ? 0 : mcs->invocation_counter()->raw_counter(), mcs == NULL ? 0 : mcs->backedge_counter()->raw_counter(), interpreter_invocation_count(), diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciMethod.hpp --- a/src/share/vm/ci/ciMethod.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciMethod.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -71,6 +71,7 @@ int _interpreter_invocation_count; int _interpreter_throwout_count; int _instructions_size; + int _size_of_parameters; bool _uses_monitors; bool _balanced_monitors; @@ -166,6 +167,7 @@ int exception_table_length() const { check_is_loaded(); return _handler_count; } int interpreter_invocation_count() const { check_is_loaded(); return _interpreter_invocation_count; } int interpreter_throwout_count() const { check_is_loaded(); return _interpreter_throwout_count; } + int size_of_parameters() const { check_is_loaded(); return _size_of_parameters; } // Code size for inlining decisions. int code_size_for_inlining(); @@ -241,7 +243,6 @@ ciField* get_field_at_bci( int bci, bool &will_link); ciMethod* get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature); - // Given a certain calling environment, find the monomorphic target // for the call. Return NULL if the call is not monomorphic in // its calling environment. @@ -310,10 +311,13 @@ bool is_accessor () const; bool is_initializer () const; bool can_be_statically_bound() const { return _can_be_statically_bound; } - void dump_replay_data(outputStream* st); bool is_boxing_method() const; bool is_unboxing_method() const; + // Replay data methods + void dump_name_as_ascii(outputStream* st); + void dump_replay_data(outputStream* st); + // Print the bytecodes of this method. void print_codes_on(outputStream* st); void print_codes() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciMethodData.cpp --- a/src/share/vm/ci/ciMethodData.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciMethodData.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -78,6 +78,36 @@ _parameters = NULL; } +void ciMethodData::load_extra_data() { + MethodData* mdo = get_MethodData(); + + // speculative trap entries also hold a pointer to a Method so need to be translated + DataLayout* dp_src = mdo->extra_data_base(); + DataLayout* end_src = mdo->extra_data_limit(); + DataLayout* dp_dst = extra_data_base(); + for (;; dp_src = MethodData::next_extra(dp_src), dp_dst = MethodData::next_extra(dp_dst)) { + assert(dp_src < end_src, "moved past end of extra data"); + // New traps in the MDO can be added as we translate the copy so + // look at the entries in the copy. + switch(dp_dst->tag()) { + case DataLayout::speculative_trap_data_tag: { + ciSpeculativeTrapData* data_dst = new ciSpeculativeTrapData(dp_dst); + SpeculativeTrapData* data_src = new SpeculativeTrapData(dp_src); + data_dst->translate_from(data_src); + break; + } + case DataLayout::bit_data_tag: + break; + case DataLayout::no_tag: + case DataLayout::arg_info_data_tag: + // An empty slot or ArgInfoData entry marks the end of the trap data + return; + default: + fatal(err_msg("bad tag = %d", dp_dst->tag())); + } + } +} + void ciMethodData::load_data() { MethodData* mdo = get_MethodData(); if (mdo == NULL) { @@ -116,6 +146,8 @@ parameters->translate_from(mdo->parameters_type_data()); } + load_extra_data(); + // Note: Extra data are all BitData, and do not need translation. _current_mileage = MethodData::mileage_of(mdo->method()); _invocation_counter = mdo->invocation_count(); @@ -156,6 +188,12 @@ set_type(translate_klass(k)); } +void ciSpeculativeTrapData::translate_from(const ProfileData* data) { + Method* m = data->as_SpeculativeTrapData()->method(); + ciMethod* ci_m = CURRENT_ENV->get_method(m); + set_method(ci_m); +} + // Get the data at an arbitrary (sort of) data index. ciProfileData* ciMethodData::data_at(int data_index) { if (out_of_bounds(data_index)) { @@ -203,32 +241,64 @@ return next; } -// Translate a bci to its corresponding data, or NULL. -ciProfileData* ciMethodData::bci_to_data(int bci) { - ciProfileData* data = data_before(bci); - for ( ; is_valid(data); data = next_data(data)) { - if (data->bci() == bci) { - set_hint_di(dp_to_di(data->dp())); - return data; - } else if (data->bci() > bci) { - break; - } - } +ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots) { // bci_to_extra_data(bci) ... DataLayout* dp = data_layout_at(data_size()); DataLayout* end = data_layout_at(data_size() + extra_data_size()); - for (; dp < end; dp = MethodData::next_extra(dp)) { - if (dp->tag() == DataLayout::no_tag) { + two_free_slots = false; + for (;dp < end; dp = MethodData::next_extra(dp)) { + switch(dp->tag()) { + case DataLayout::no_tag: _saw_free_extra_data = true; // observed an empty slot (common case) + two_free_slots = (MethodData::next_extra(dp)->tag() == DataLayout::no_tag); return NULL; + case DataLayout::arg_info_data_tag: + return NULL; // ArgInfoData is at the end of extra data section. + case DataLayout::bit_data_tag: + if (m == NULL && dp->bci() == bci) { + return new ciBitData(dp); + } + break; + case DataLayout::speculative_trap_data_tag: { + ciSpeculativeTrapData* data = new ciSpeculativeTrapData(dp); + // data->method() might be null if the MDO is snapshotted + // concurrently with a trap + if (m != NULL && data->method() == m && dp->bci() == bci) { + return data; + } + break; + } + default: + fatal(err_msg("bad tag = %d", dp->tag())); } - if (dp->tag() == DataLayout::arg_info_data_tag) { - break; // ArgInfoData is at the end of extra data section. + } + return NULL; +} + +// Translate a bci to its corresponding data, or NULL. +ciProfileData* ciMethodData::bci_to_data(int bci, ciMethod* m) { + // If m is not NULL we look for a SpeculativeTrapData entry + if (m == NULL) { + ciProfileData* data = data_before(bci); + for ( ; is_valid(data); data = next_data(data)) { + if (data->bci() == bci) { + set_hint_di(dp_to_di(data->dp())); + return data; + } else if (data->bci() > bci) { + break; + } } - if (dp->bci() == bci) { - assert(dp->tag() == DataLayout::bit_data_tag, "sane"); - return new ciBitData(dp); - } + } + bool two_free_slots = false; + ciProfileData* result = bci_to_extra_data(bci, m, two_free_slots); + if (result != NULL) { + return result; + } + if (m != NULL && !two_free_slots) { + // We were looking for a SpeculativeTrapData entry we didn't + // find. Room is not available for more SpeculativeTrapData + // entries, look in the non SpeculativeTrapData entries. + return bci_to_data(bci, NULL); } return NULL; } @@ -487,7 +557,7 @@ if (round == 0) { count++; } else { - out->print(" %d %s", dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t), k->name()->as_quoted_ascii()); + out->print(" %d %s", (int)(dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t)), k->name()->as_quoted_ascii()); } } } @@ -499,7 +569,7 @@ if (round == 0) { count++; } else { - out->print(" %d %s", dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t), k->name()->as_quoted_ascii()); + out->print(" %d %s", (int)(dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t)), k->name()->as_quoted_ascii()); } } } @@ -525,18 +595,25 @@ st->print_cr("--- Extra data:"); DataLayout* dp = data_layout_at(data_size()); DataLayout* end = data_layout_at(data_size() + extra_data_size()); - for (; dp < end; dp = MethodData::next_extra(dp)) { - if (dp->tag() == DataLayout::no_tag) continue; - if (dp->tag() == DataLayout::bit_data_tag) { + for (;; dp = MethodData::next_extra(dp)) { + assert(dp < end, "moved past end of extra data"); + switch (dp->tag()) { + case DataLayout::no_tag: + continue; + case DataLayout::bit_data_tag: data = new BitData(dp); - } else { - assert(dp->tag() == DataLayout::arg_info_data_tag, "must be BitData or ArgInfo"); + break; + case DataLayout::arg_info_data_tag: data = new ciArgInfoData(dp); dp = end; // ArgInfoData is at the end of extra data section. + break; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); } st->print("%d", dp_to_di(data->dp())); st->fill_to(6); data->print_data_on(st); + if (dp >= end) return; } } @@ -569,8 +646,8 @@ st->cr(); } -void ciCallTypeData::print_data_on(outputStream* st) const { - print_shared(st, "ciCallTypeData"); +void ciCallTypeData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "ciCallTypeData", extra); if (has_arguments()) { tab(st, true); st->print("argument types"); @@ -599,18 +676,18 @@ } } -void ciReceiverTypeData::print_data_on(outputStream* st) const { - print_shared(st, "ciReceiverTypeData"); +void ciReceiverTypeData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "ciReceiverTypeData", extra); print_receiver_data_on(st); } -void ciVirtualCallData::print_data_on(outputStream* st) const { - print_shared(st, "ciVirtualCallData"); +void ciVirtualCallData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "ciVirtualCallData", extra); rtd_super()->print_receiver_data_on(st); } -void ciVirtualCallTypeData::print_data_on(outputStream* st) const { - print_shared(st, "ciVirtualCallTypeData"); +void ciVirtualCallTypeData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "ciVirtualCallTypeData", extra); rtd_super()->print_receiver_data_on(st); if (has_arguments()) { tab(st, true); @@ -624,8 +701,15 @@ } } -void ciParametersTypeData::print_data_on(outputStream* st) const { - st->print_cr("Parametertypes"); +void ciParametersTypeData::print_data_on(outputStream* st, const char* extra) const { + st->print_cr("ciParametersTypeData"); parameters()->print_data_on(st); } + +void ciSpeculativeTrapData::print_data_on(outputStream* st, const char* extra) const { + st->print_cr("ciSpeculativeTrapData"); + tab(st); + method()->print_short_name(st); + st->cr(); +} #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciMethodData.hpp --- a/src/share/vm/ci/ciMethodData.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciMethodData.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -31,6 +31,7 @@ #include "ci/ciUtilities.hpp" #include "oops/methodData.hpp" #include "oops/oop.inline.hpp" +#include "runtime/deoptimization.hpp" class ciBitData; class ciCounterData; @@ -44,6 +45,7 @@ class ciCallTypeData; class ciVirtualCallTypeData; class ciParametersTypeData; +class ciSpeculativeTrapData;; typedef ProfileData ciProfileData; @@ -173,7 +175,7 @@ } #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra) const; #endif }; @@ -200,7 +202,7 @@ } void translate_receiver_data_from(const ProfileData* data); #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra) const; void print_receiver_data_on(outputStream* st) const; #endif }; @@ -225,7 +227,7 @@ rtd_super()->translate_receiver_data_from(data); } #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra) const; #endif }; @@ -287,7 +289,7 @@ } #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra) const; #endif }; @@ -336,7 +338,26 @@ } #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra) const; +#endif +}; + +class ciSpeculativeTrapData : public SpeculativeTrapData { +public: + ciSpeculativeTrapData(DataLayout* layout) : SpeculativeTrapData(layout) {} + + virtual void translate_from(const ProfileData* data); + + ciMethod* method() const { + return (ciMethod*)intptr_at(method_offset); + } + + void set_method(ciMethod* m) { + set_intptr_at(method_offset, (intptr_t)m); + } + +#ifndef PRODUCT + void print_data_on(outputStream* st, const char* extra) const; #endif }; @@ -436,6 +457,16 @@ ciArgInfoData *arg_info() const; + address data_base() const { + return (address) _data; + } + DataLayout* limit_data_position() const { + return (DataLayout*)((address)data_base() + _data_size); + } + + void load_extra_data(); + ciProfileData* bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots); + public: bool is_method_data() const { return true; } @@ -447,6 +478,18 @@ int invocation_count() { return _invocation_counter; } int backedge_count() { return _backedge_counter; } + +#if INCLUDE_RTM_OPT + // return cached value + int rtm_state() { + if (is_empty()) { + return NoRTM; + } else { + return get_MethodData()->rtm_state(); + } + } +#endif + // Transfer information about the method to MethodData*. // would_profile means we would like to profile this method, // meaning it's not trivial. @@ -475,9 +518,11 @@ ciProfileData* next_data(ciProfileData* current); bool is_valid(ciProfileData* current) { return current != NULL; } - // Get the data at an arbitrary bci, or NULL if there is none. - ciProfileData* bci_to_data(int bci); - ciProfileData* bci_to_extra_data(int bci, bool create_if_missing); + DataLayout* extra_data_base() const { return limit_data_position(); } + + // Get the data at an arbitrary bci, or NULL if there is none. If m + // is not NULL look for a SpeculativeTrapData if any first. + ciProfileData* bci_to_data(int bci, ciMethod* m = NULL); uint overflow_trap_count() const { return _orig.overflow_trap_count(); @@ -496,12 +541,13 @@ // Helpful query functions that decode trap_state. int has_trap_at(ciProfileData* data, int reason); - int has_trap_at(int bci, int reason) { - return has_trap_at(bci_to_data(bci), reason); + int has_trap_at(int bci, ciMethod* m, int reason) { + assert((m != NULL) == Deoptimization::reason_is_speculate(reason), "inconsistent method/reason"); + return has_trap_at(bci_to_data(bci, m), reason); } int trap_recompiled_at(ciProfileData* data); - int trap_recompiled_at(int bci) { - return trap_recompiled_at(bci_to_data(bci)); + int trap_recompiled_at(int bci, ciMethod* m) { + return trap_recompiled_at(bci_to_data(bci, m)); } void clear_escape_info(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciObject.cpp --- a/src/share/vm/ci/ciObject.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciObject.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -214,9 +214,9 @@ void ciObject::print(outputStream* st) { st->print("<%s", type_string()); GUARDED_VM_ENTRY(print_impl(st);) - st->print(" ident=%d %s address=0x%x>", ident(), + st->print(" ident=%d %s address=" INTPTR_FORMAT ">", ident(), is_scavengable() ? "SCAVENGABLE" : "", - (address)this); + p2i((address)this)); } // ------------------------------------------------------------------ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciReplay.cpp --- a/src/share/vm/ci/ciReplay.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciReplay.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -24,6 +24,8 @@ #include "precompiled.hpp" #include "ci/ciMethodData.hpp" #include "ci/ciReplay.hpp" +#include "ci/ciSymbol.hpp" +#include "ci/ciKlass.hpp" #include "ci/ciUtilities.hpp" #include "compiler/compileBroker.hpp" #include "memory/allocation.inline.hpp" @@ -37,74 +39,107 @@ // ciReplay typedef struct _ciMethodDataRecord { - const char* klass; - const char* method; - const char* signature; - int state; - int current_mileage; - intptr_t* data; - int data_length; - char* orig_data; - int orig_data_length; - int oops_length; - jobject* oops_handles; - int* oops_offsets; + const char* _klass_name; + const char* _method_name; + const char* _signature; + + int _state; + int _current_mileage; + + intptr_t* _data; + char* _orig_data; + jobject* _oops_handles; + int* _oops_offsets; + int _data_length; + int _orig_data_length; + int _oops_length; } ciMethodDataRecord; typedef struct _ciMethodRecord { - const char* klass; - const char* method; - const char* signature; - int instructions_size; - int interpreter_invocation_count; - int interpreter_throwout_count; - int invocation_counter; - int backedge_counter; + const char* _klass_name; + const char* _method_name; + const char* _signature; + + int _instructions_size; + int _interpreter_invocation_count; + int _interpreter_throwout_count; + int _invocation_counter; + int _backedge_counter; } ciMethodRecord; -class CompileReplay; +typedef struct _ciInlineRecord { + const char* _klass_name; + const char* _method_name; + const char* _signature; + + int _inline_depth; + int _inline_bci; +} ciInlineRecord; + +class CompileReplay; static CompileReplay* replay_state; class CompileReplay : public StackObj { private: - FILE* stream; - Thread* thread; - Handle protection_domain; - Handle loader; + FILE* _stream; + Thread* _thread; + Handle _protection_domain; + Handle _loader; - GrowableArray ci_method_records; - GrowableArray ci_method_data_records; + GrowableArray _ci_method_records; + GrowableArray _ci_method_data_records; + + // Use pointer because we may need to return inline records + // without destroying them. + GrowableArray* _ci_inline_records; const char* _error_message; - char* bufptr; - char* buffer; - int buffer_length; - int buffer_end; - int line_no; + char* _bufptr; + char* _buffer; + int _buffer_length; + int _buffer_pos; + + // "compile" data + ciKlass* _iklass; + Method* _imethod; + int _entry_bci; + int _comp_level; public: CompileReplay(const char* filename, TRAPS) { - thread = THREAD; - loader = Handle(thread, SystemDictionary::java_system_loader()); - stream = fopen(filename, "rt"); - if (stream == NULL) { + _thread = THREAD; + _loader = Handle(_thread, SystemDictionary::java_system_loader()); + _protection_domain = Handle(); + + _stream = fopen(filename, "rt"); + if (_stream == NULL) { fprintf(stderr, "ERROR: Can't open replay file %s\n", filename); } - buffer_length = 32; - buffer = NEW_RESOURCE_ARRAY(char, buffer_length); + + _ci_inline_records = NULL; _error_message = NULL; + _buffer_length = 32; + _buffer = NEW_RESOURCE_ARRAY(char, _buffer_length); + _bufptr = _buffer; + _buffer_pos = 0; + + _imethod = NULL; + _iklass = NULL; + _entry_bci = 0; + _comp_level = 0; + test(); } ~CompileReplay() { - if (stream != NULL) fclose(stream); + if (_stream != NULL) fclose(_stream); } void test() { - strcpy(buffer, "1 2 foo 4 bar 0x9 \"this is it\""); - bufptr = buffer; + strcpy(_buffer, "1 2 foo 4 bar 0x9 \"this is it\""); + _bufptr = _buffer; assert(parse_int("test") == 1, "what"); assert(parse_int("test") == 2, "what"); assert(strcmp(parse_string(), "foo") == 0, "what"); @@ -115,18 +150,18 @@ } bool had_error() { - return _error_message != NULL || thread->has_pending_exception(); + return _error_message != NULL || _thread->has_pending_exception(); } bool can_replay() { - return !(stream == NULL || had_error()); + return !(_stream == NULL || had_error()); } void report_error(const char* msg) { _error_message = msg; - // Restore the buffer contents for error reporting - for (int i = 0; i < buffer_end; i++) { - if (buffer[i] == '\0') buffer[i] = ' '; + // Restore the _buffer contents for error reporting + for (int i = 0; i < _buffer_pos; i++) { + if (_buffer[i] == '\0') _buffer[i] = ' '; } } @@ -137,10 +172,10 @@ int v = 0; int read; - if (sscanf(bufptr, "%i%n", &v, &read) != 1) { + if (sscanf(_bufptr, "%i%n", &v, &read) != 1) { report_error(label); } else { - bufptr += read; + _bufptr += read; } return v; } @@ -152,31 +187,31 @@ intptr_t v = 0; int read; - if (sscanf(bufptr, INTPTR_FORMAT "%n", &v, &read) != 1) { + if (sscanf(_bufptr, INTPTR_FORMAT "%n", &v, &read) != 1) { report_error(label); } else { - bufptr += read; + _bufptr += read; } return v; } void skip_ws() { // Skip any leading whitespace - while (*bufptr == ' ' || *bufptr == '\t') { - bufptr++; + while (*_bufptr == ' ' || *_bufptr == '\t') { + _bufptr++; } } char* scan_and_terminate(char delim) { - char* str = bufptr; - while (*bufptr != delim && *bufptr != '\0') { - bufptr++; + char* str = _bufptr; + while (*_bufptr != delim && *_bufptr != '\0') { + _bufptr++; } - if (*bufptr != '\0') { - *bufptr++ = '\0'; + if (*_bufptr != '\0') { + *_bufptr++ = '\0'; } - if (bufptr == str) { + if (_bufptr == str) { // nothing here return NULL; } @@ -195,8 +230,8 @@ skip_ws(); - if (*bufptr == '"') { - bufptr++; + if (*_bufptr == '"') { + _bufptr++; return scan_and_terminate('"'); } else { return scan_and_terminate(' '); @@ -273,7 +308,12 @@ const char* str = parse_escaped_string(); Symbol* klass_name = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL); if (klass_name != NULL) { - Klass* k = SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, THREAD); + Klass* k = NULL; + if (_iklass != NULL) { + k = (Klass*)_iklass->find_klass(ciSymbol::make(klass_name->as_C_string()))->constant_encoding(); + } else { + k = SystemDictionary::resolve_or_fail(klass_name, _loader, _protection_domain, true, THREAD); + } if (HAS_PENDING_EXCEPTION) { oop throwable = PENDING_EXCEPTION; java_lang_Throwable::print(throwable, tty); @@ -289,7 +329,7 @@ // Lookup a klass Klass* resolve_klass(const char* klass, TRAPS) { Symbol* klass_name = SymbolTable::lookup(klass, (int)strlen(klass), CHECK_NULL); - return SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, CHECK_NULL); + return SystemDictionary::resolve_or_fail(klass_name, _loader, _protection_domain, true, CHECK_NULL); } // Parse the standard tuple of @@ -304,40 +344,49 @@ return m; } + int get_line(int c) { + while(c != EOF) { + if (_buffer_pos + 1 >= _buffer_length) { + int new_length = _buffer_length * 2; + // Next call will throw error in case of OOM. + _buffer = REALLOC_RESOURCE_ARRAY(char, _buffer, _buffer_length, new_length); + _buffer_length = new_length; + } + if (c == '\n') { + c = getc(_stream); // get next char + break; + } else if (c == '\r') { + // skip LF + } else { + _buffer[_buffer_pos++] = c; + } + c = getc(_stream); + } + // null terminate it, reset the pointer + _buffer[_buffer_pos] = '\0'; // NL or EOF + _buffer_pos = 0; + _bufptr = _buffer; + return c; + } + // Process each line of the replay file executing each command until // the file ends. void process(TRAPS) { - line_no = 1; - int pos = 0; - int c = getc(stream); + int line_no = 1; + int c = getc(_stream); while(c != EOF) { - if (pos + 1 >= buffer_length) { - int newl = buffer_length * 2; - char* newb = NEW_RESOURCE_ARRAY(char, newl); - memcpy(newb, buffer, pos); - buffer = newb; - buffer_length = newl; - } - if (c == '\n') { - // null terminate it, reset the pointer and process the line - buffer[pos] = '\0'; - buffer_end = pos++; - bufptr = buffer; - process_command(CHECK); - if (had_error()) { - tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); - tty->print_cr("%s", buffer); + c = get_line(c); + process_command(THREAD); + if (had_error()) { + tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); + if (ReplayIgnoreInitErrors) { + CLEAR_PENDING_EXCEPTION; + _error_message = NULL; + } else { return; } - pos = 0; - buffer_end = 0; - line_no++; - } else if (c == '\r') { - // skip LF - } else { - buffer[pos++] = c; } - c = getc(stream); + line_no++; } } @@ -396,7 +445,37 @@ return true; } - // compile + // compile inline ... + void* process_inline(ciMethod* imethod, Method* m, int entry_bci, int comp_level, TRAPS) { + _imethod = m; + _iklass = imethod->holder(); + _entry_bci = entry_bci; + _comp_level = comp_level; + int line_no = 1; + int c = getc(_stream); + while(c != EOF) { + c = get_line(c); + // Expecting only lines with "compile" command in inline replay file. + char* cmd = parse_string(); + if (cmd == NULL || strcmp("compile", cmd) != 0) { + return NULL; + } + process_compile(CHECK_NULL); + if (had_error()) { + tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); + tty->print_cr("%s", _buffer); + return NULL; + } + if (_ci_inline_records != NULL && _ci_inline_records->length() > 0) { + // Found inlining record for the requested method. + return _ci_inline_records; + } + line_no++; + } + return NULL; + } + + // compile inline ... void process_compile(TRAPS) { Method* method = parse_method(CHECK); if (had_error()) return; @@ -410,6 +489,43 @@ if (!is_valid_comp_level(comp_level)) { return; } + if (_imethod != NULL) { + // Replay Inlining + if (entry_bci != _entry_bci || comp_level != _comp_level) { + return; + } + const char* iklass_name = _imethod->method_holder()->name()->as_utf8(); + const char* imethod_name = _imethod->name()->as_utf8(); + const char* isignature = _imethod->signature()->as_utf8(); + const char* klass_name = method->method_holder()->name()->as_utf8(); + const char* method_name = method->name()->as_utf8(); + const char* signature = method->signature()->as_utf8(); + if (strcmp(iklass_name, klass_name) != 0 || + strcmp(imethod_name, method_name) != 0 || + strcmp(isignature, signature) != 0) { + return; + } + } + int inline_count = 0; + if (parse_tag_and_count("inline", inline_count)) { + // Record inlining data + _ci_inline_records = new GrowableArray(); + for (int i = 0; i < inline_count; i++) { + int depth = parse_int("inline_depth"); + int bci = parse_int("inline_bci"); + if (had_error()) { + break; + } + Method* inl_method = parse_method(CHECK); + if (had_error()) { + break; + } + new_ciInlineRecord(inl_method, bci, depth); + } + } + if (_imethod != NULL) { + return; // Replay Inlining + } Klass* k = method->method_holder(); ((InstanceKlass*)k)->initialize(THREAD); if (HAS_PENDING_EXCEPTION) { @@ -442,21 +558,25 @@ Method* method = parse_method(CHECK); if (had_error()) return; ciMethodRecord* rec = new_ciMethod(method); - rec->invocation_counter = parse_int("invocation_counter"); - rec->backedge_counter = parse_int("backedge_counter"); - rec->interpreter_invocation_count = parse_int("interpreter_invocation_count"); - rec->interpreter_throwout_count = parse_int("interpreter_throwout_count"); - rec->instructions_size = parse_int("instructions_size"); + rec->_invocation_counter = parse_int("invocation_counter"); + rec->_backedge_counter = parse_int("backedge_counter"); + rec->_interpreter_invocation_count = parse_int("interpreter_invocation_count"); + rec->_interpreter_throwout_count = parse_int("interpreter_throwout_count"); + rec->_instructions_size = parse_int("instructions_size"); } // ciMethodData orig # # ... data # # ... oops void process_ciMethodData(TRAPS) { Method* method = parse_method(CHECK); if (had_error()) return; - /* jsut copied from Method, to build interpret data*/ + /* just copied from Method, to build interpret data*/ if (InstanceRefKlass::owns_pending_list_lock((JavaThread*)THREAD)) { return; } + // To be properly initialized, some profiling in the MDO needs the + // method to be rewritten (number of arguments at a call for + // instance) + method->method_holder()->link_class(CHECK); // methodOopDesc::build_interpreter_method_data(method, CHECK); { // Grab a lock here to prevent multiple @@ -471,32 +591,32 @@ // collect and record all the needed information for later ciMethodDataRecord* rec = new_ciMethodData(method); - rec->state = parse_int("state"); - rec->current_mileage = parse_int("current_mileage"); + rec->_state = parse_int("state"); + rec->_current_mileage = parse_int("current_mileage"); - rec->orig_data = parse_data("orig", rec->orig_data_length); - if (rec->orig_data == NULL) { + rec->_orig_data = parse_data("orig", rec->_orig_data_length); + if (rec->_orig_data == NULL) { return; } - rec->data = parse_intptr_data("data", rec->data_length); - if (rec->data == NULL) { + rec->_data = parse_intptr_data("data", rec->_data_length); + if (rec->_data == NULL) { return; } - if (!parse_tag_and_count("oops", rec->oops_length)) { + if (!parse_tag_and_count("oops", rec->_oops_length)) { return; } - rec->oops_handles = NEW_RESOURCE_ARRAY(jobject, rec->oops_length); - rec->oops_offsets = NEW_RESOURCE_ARRAY(int, rec->oops_length); - for (int i = 0; i < rec->oops_length; i++) { + rec->_oops_handles = NEW_RESOURCE_ARRAY(jobject, rec->_oops_length); + rec->_oops_offsets = NEW_RESOURCE_ARRAY(int, rec->_oops_length); + for (int i = 0; i < rec->_oops_length; i++) { int offset = parse_int("offset"); if (had_error()) { return; } Klass* k = parse_klass(CHECK); - rec->oops_offsets[i] = offset; + rec->_oops_offsets[i] = offset; KlassHandle *kh = NEW_C_HEAP_OBJ(KlassHandle, mtCompiler); ::new ((void*)kh) KlassHandle(THREAD, k); - rec->oops_handles[i] = (jobject)kh; + rec->_oops_handles[i] = (jobject)kh; } } @@ -570,6 +690,9 @@ case JVM_CONSTANT_Utf8: case JVM_CONSTANT_Integer: case JVM_CONSTANT_Float: + case JVM_CONSTANT_MethodHandle: + case JVM_CONSTANT_MethodType: + case JVM_CONSTANT_InvokeDynamic: if (tag != cp->tag_at(i).value()) { report_error("tag mismatch: wrong class files?"); return; @@ -729,10 +852,10 @@ // Create and initialize a record for a ciMethod ciMethodRecord* new_ciMethod(Method* method) { ciMethodRecord* rec = NEW_RESOURCE_OBJ(ciMethodRecord); - rec->klass = method->method_holder()->name()->as_utf8(); - rec->method = method->name()->as_utf8(); - rec->signature = method->signature()->as_utf8(); - ci_method_records.append(rec); + rec->_klass_name = method->method_holder()->name()->as_utf8(); + rec->_method_name = method->name()->as_utf8(); + rec->_signature = method->signature()->as_utf8(); + _ci_method_records.append(rec); return rec; } @@ -741,11 +864,11 @@ const char* klass_name = method->method_holder()->name()->as_utf8(); const char* method_name = method->name()->as_utf8(); const char* signature = method->signature()->as_utf8(); - for (int i = 0; i < ci_method_records.length(); i++) { - ciMethodRecord* rec = ci_method_records.at(i); - if (strcmp(rec->klass, klass_name) == 0 && - strcmp(rec->method, method_name) == 0 && - strcmp(rec->signature, signature) == 0) { + for (int i = 0; i < _ci_method_records.length(); i++) { + ciMethodRecord* rec = _ci_method_records.at(i); + if (strcmp(rec->_klass_name, klass_name) == 0 && + strcmp(rec->_method_name, method_name) == 0 && + strcmp(rec->_signature, signature) == 0) { return rec; } } @@ -755,10 +878,10 @@ // Create and initialize a record for a ciMethodData ciMethodDataRecord* new_ciMethodData(Method* method) { ciMethodDataRecord* rec = NEW_RESOURCE_OBJ(ciMethodDataRecord); - rec->klass = method->method_holder()->name()->as_utf8(); - rec->method = method->name()->as_utf8(); - rec->signature = method->signature()->as_utf8(); - ci_method_data_records.append(rec); + rec->_klass_name = method->method_holder()->name()->as_utf8(); + rec->_method_name = method->name()->as_utf8(); + rec->_signature = method->signature()->as_utf8(); + _ci_method_data_records.append(rec); return rec; } @@ -767,25 +890,65 @@ const char* klass_name = method->method_holder()->name()->as_utf8(); const char* method_name = method->name()->as_utf8(); const char* signature = method->signature()->as_utf8(); - for (int i = 0; i < ci_method_data_records.length(); i++) { - ciMethodDataRecord* rec = ci_method_data_records.at(i); - if (strcmp(rec->klass, klass_name) == 0 && - strcmp(rec->method, method_name) == 0 && - strcmp(rec->signature, signature) == 0) { + for (int i = 0; i < _ci_method_data_records.length(); i++) { + ciMethodDataRecord* rec = _ci_method_data_records.at(i); + if (strcmp(rec->_klass_name, klass_name) == 0 && + strcmp(rec->_method_name, method_name) == 0 && + strcmp(rec->_signature, signature) == 0) { return rec; } } return NULL; } + // Create and initialize a record for a ciInlineRecord + ciInlineRecord* new_ciInlineRecord(Method* method, int bci, int depth) { + ciInlineRecord* rec = NEW_RESOURCE_OBJ(ciInlineRecord); + rec->_klass_name = method->method_holder()->name()->as_utf8(); + rec->_method_name = method->name()->as_utf8(); + rec->_signature = method->signature()->as_utf8(); + rec->_inline_bci = bci; + rec->_inline_depth = depth; + _ci_inline_records->append(rec); + return rec; + } + + // Lookup inlining data for a ciMethod + ciInlineRecord* find_ciInlineRecord(Method* method, int bci, int depth) { + if (_ci_inline_records != NULL) { + return find_ciInlineRecord(_ci_inline_records, method, bci, depth); + } + return NULL; + } + + static ciInlineRecord* find_ciInlineRecord(GrowableArray* records, + Method* method, int bci, int depth) { + if (records != NULL) { + const char* klass_name = method->method_holder()->name()->as_utf8(); + const char* method_name = method->name()->as_utf8(); + const char* signature = method->signature()->as_utf8(); + for (int i = 0; i < records->length(); i++) { + ciInlineRecord* rec = records->at(i); + if ((rec->_inline_bci == bci) && + (rec->_inline_depth == depth) && + (strcmp(rec->_klass_name, klass_name) == 0) && + (strcmp(rec->_method_name, method_name) == 0) && + (strcmp(rec->_signature, signature) == 0)) { + return rec; + } + } + } + return NULL; + } + const char* error_message() { return _error_message; } void reset() { _error_message = NULL; - ci_method_records.clear(); - ci_method_data_records.clear(); + _ci_method_records.clear(); + _ci_method_data_records.clear(); } // Take an ascii string contain \u#### escapes and convert it to utf8 @@ -845,6 +1008,37 @@ vm_exit(exit_code); } +void* ciReplay::load_inline_data(ciMethod* method, int entry_bci, int comp_level) { + if (FLAG_IS_DEFAULT(InlineDataFile)) { + tty->print_cr("ERROR: no inline replay data file specified (use -XX:InlineDataFile=inline_pid12345.txt)."); + return NULL; + } + + VM_ENTRY_MARK; + // Load and parse the replay data + CompileReplay rp(InlineDataFile, THREAD); + if (!rp.can_replay()) { + tty->print_cr("ciReplay: !rp.can_replay()"); + return NULL; + } + void* data = rp.process_inline(method, method->get_Method(), entry_bci, comp_level, THREAD); + if (HAS_PENDING_EXCEPTION) { + oop throwable = PENDING_EXCEPTION; + CLEAR_PENDING_EXCEPTION; + java_lang_Throwable::print(throwable, tty); + tty->cr(); + java_lang_Throwable::print_stack_trace(throwable, tty); + tty->cr(); + return NULL; + } + + if (rp.had_error()) { + tty->print_cr("ciReplay: Failed on %s", rp.error_message()); + return NULL; + } + return data; +} + int ciReplay::replay_impl(TRAPS) { HandleMark hm; ResourceMark rm; @@ -890,7 +1084,6 @@ return exit_code; } - void ciReplay::initialize(ciMethodData* m) { if (replay_state == NULL) { return; @@ -909,28 +1102,28 @@ method->print_name(tty); tty->cr(); } else { - m->_state = rec->state; - m->_current_mileage = rec->current_mileage; - if (rec->data_length != 0) { - assert(m->_data_size == rec->data_length * (int)sizeof(rec->data[0]), "must agree"); + m->_state = rec->_state; + m->_current_mileage = rec->_current_mileage; + if (rec->_data_length != 0) { + assert(m->_data_size == rec->_data_length * (int)sizeof(rec->_data[0]), "must agree"); // Write the correct ciObjects back into the profile data ciEnv* env = ciEnv::current(); - for (int i = 0; i < rec->oops_length; i++) { - KlassHandle *h = (KlassHandle *)rec->oops_handles[i]; - *(ciMetadata**)(rec->data + rec->oops_offsets[i]) = + for (int i = 0; i < rec->_oops_length; i++) { + KlassHandle *h = (KlassHandle *)rec->_oops_handles[i]; + *(ciMetadata**)(rec->_data + rec->_oops_offsets[i]) = env->get_metadata((*h)()); } // Copy the updated profile data into place as intptr_ts #ifdef _LP64 - Copy::conjoint_jlongs_atomic((jlong *)rec->data, (jlong *)m->_data, rec->data_length); + Copy::conjoint_jlongs_atomic((jlong *)rec->_data, (jlong *)m->_data, rec->_data_length); #else - Copy::conjoint_jints_atomic((jint *)rec->data, (jint *)m->_data, rec->data_length); + Copy::conjoint_jints_atomic((jint *)rec->_data, (jint *)m->_data, rec->_data_length); #endif } // copy in the original header - Copy::conjoint_jbytes(rec->orig_data, (char*)&m->_orig, rec->orig_data_length); + Copy::conjoint_jbytes(rec->_orig_data, (char*)&m->_orig, rec->_orig_data_length); } } @@ -939,12 +1132,38 @@ if (replay_state == NULL) { return false; } - VM_ENTRY_MARK; // ciMethod without a record shouldn't be inlined. return replay_state->find_ciMethodRecord(method->get_Method()) == NULL; } +bool ciReplay::should_inline(void* data, ciMethod* method, int bci, int inline_depth) { + if (data != NULL) { + GrowableArray* records = (GrowableArray*)data; + VM_ENTRY_MARK; + // Inline record are ordered by bci and depth. + return CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth) != NULL; + } else if (replay_state != NULL) { + VM_ENTRY_MARK; + // Inline record are ordered by bci and depth. + return replay_state->find_ciInlineRecord(method->get_Method(), bci, inline_depth) != NULL; + } + return false; +} + +bool ciReplay::should_not_inline(void* data, ciMethod* method, int bci, int inline_depth) { + if (data != NULL) { + GrowableArray* records = (GrowableArray*)data; + VM_ENTRY_MARK; + // Inline record are ordered by bci and depth. + return CompileReplay::find_ciInlineRecord(records, method->get_Method(), bci, inline_depth) == NULL; + } else if (replay_state != NULL) { + VM_ENTRY_MARK; + // Inline record are ordered by bci and depth. + return replay_state->find_ciInlineRecord(method->get_Method(), bci, inline_depth) == NULL; + } + return false; +} void ciReplay::initialize(ciMethod* m) { if (replay_state == NULL) { @@ -965,14 +1184,14 @@ tty->cr(); } else { EXCEPTION_CONTEXT; - // m->_instructions_size = rec->instructions_size; + // m->_instructions_size = rec->_instructions_size; m->_instructions_size = -1; - m->_interpreter_invocation_count = rec->interpreter_invocation_count; - m->_interpreter_throwout_count = rec->interpreter_throwout_count; + m->_interpreter_invocation_count = rec->_interpreter_invocation_count; + m->_interpreter_throwout_count = rec->_interpreter_throwout_count; MethodCounters* mcs = method->get_method_counters(CHECK_AND_CLEAR); guarantee(mcs != NULL, "method counters allocation failed"); - mcs->invocation_counter()->_counter = rec->invocation_counter; - mcs->backedge_counter()->_counter = rec->backedge_counter; + mcs->invocation_counter()->_counter = rec->_invocation_counter; + mcs->backedge_counter()->_counter = rec->_backedge_counter; } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciReplay.hpp --- a/src/share/vm/ci/ciReplay.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciReplay.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -29,6 +29,73 @@ // ciReplay +// +// Replay compilation of a java method by using an information in replay file. +// Replay inlining decisions during compilation by using an information in inline file. +// +// NOTE: these replay functions only exist in debug version of VM. +// +// Replay compilation. +// ------------------- +// +// Replay data file replay.txt can be created by Serviceability Agent +// from a core file, see agent/doc/cireplay.html +// +// $ java -cp /lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB +// hsdb> attach /bin/java ./core +// hsdb> threads +// t@10 Service Thread +// t@9 C2 CompilerThread0 +// t@8 Signal Dispatcher +// t@7 Finalizer +// t@6 Reference Handler +// t@2 main +// hsdb> dumpreplaydata t@9 > replay.txt +// hsdb> quit +// +// (Note: SA could be also used to extract app.jar and boot.jar files +// from core file to replay compilation if only core file is available) +// +// Replay data file replay_pid%p.log is also created when VM crashes +// in Compiler thread during compilation. It is controlled by +// DumpReplayDataOnError flag which is ON by default. +// +// Replay file replay_pid%p_compid%d.log can be created +// for the specified java method during normal execution using +// CompileCommand option DumpReplay: +// +// -XX:CompileCommand=option,Benchmark::test,DumpReplay +// +// In this case the file name has additional compilation id "_compid%d" +// because the method could be compiled several times. +// +// To replay compilation the replay file should be specified: +// +// -XX:+ReplayCompiles -XX:ReplayDataFile=replay_pid2133.log +// +// VM thread reads data from the file immediately after VM initialization +// and puts the compilation task on compile queue. After that it goes into +// wait state (BackgroundCompilation flag is set to false) since there is no +// a program to execute. VM exits when the compilation is finished. +// +// +// Replay inlining. +// ---------------- +// +// Replay inlining file inline_pid%p_compid%d.log is created for +// a specific java method during normal execution of a java program +// using CompileCommand option DumpInline: +// +// -XX:CompileCommand=option,Benchmark::test,DumpInline +// +// To replay inlining the replay file and the method should be specified: +// +// -XX:CompileCommand=option,Benchmark::test,ReplayInline -XX:InlineDataFile=inline_pid3244_compid6.log +// +// The difference from replay compilation is that replay inlining +// is performed during normal java program execution. +// + class ciReplay { CI_PACKAGE_ACCESS @@ -37,7 +104,11 @@ static int replay_impl(TRAPS); public: + // Replay specified compilation and exit VM. static void replay(TRAPS); + // Load inlining decisions from file and use them + // during compilation of specified method. + static void* load_inline_data(ciMethod* method, int entry_bci, int comp_level); // These are used by the CI to fill in the cached data from the // replay file when replaying compiles. @@ -48,6 +119,8 @@ static bool is_loaded(Klass* klass); static bool should_not_inline(ciMethod* method); + static bool should_inline(void* data, ciMethod* method, int bci, int inline_depth); + static bool should_not_inline(void* data, ciMethod* method, int bci, int inline_depth); #endif }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciSignature.cpp --- a/src/share/vm/ci/ciSignature.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciSignature.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -148,5 +148,5 @@ print_signature(); tty->print(" accessing_klass="); _accessing_klass->print(); - tty->print(" address=0x%x>", (address)this); + tty->print(" address=" INTPTR_FORMAT ">", p2i((address)this)); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/ci/ciType.cpp --- a/src/share/vm/ci/ciType.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/ci/ciType.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -87,7 +87,7 @@ // Print the name of this type void ciType::print_name_on(outputStream* st) { ResourceMark rm; - st->print(name()); + st->print("%s", name()); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/altHashing.cpp --- a/src/share/vm/classfile/altHashing.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/altHashing.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -39,18 +39,18 @@ } // Seed value used for each alternative hash calculated. -jint AltHashing::compute_seed() { +juint AltHashing::compute_seed() { jlong nanos = os::javaTimeNanos(); jlong now = os::javaTimeMillis(); - jint SEED_MATERIAL[8] = { - (jint) object_hash(SystemDictionary::String_klass()), - (jint) object_hash(SystemDictionary::System_klass()), - (jint) os::random(), // current thread isn't a java thread - (jint) (((julong)nanos) >> 32), - (jint) nanos, - (jint) (((julong)now) >> 32), - (jint) now, - (jint) (os::javaTimeNanos() >> 2) + int SEED_MATERIAL[8] = { + (int) object_hash(SystemDictionary::String_klass()), + (int) object_hash(SystemDictionary::System_klass()), + (int) os::random(), // current thread isn't a java thread + (int) (((julong)nanos) >> 32), + (int) nanos, + (int) (((julong)now) >> 32), + (int) now, + (int) (os::javaTimeNanos() >> 2) }; return murmur3_32(SEED_MATERIAL, 8); @@ -58,14 +58,14 @@ // Murmur3 hashing for Symbol -jint AltHashing::murmur3_32(jint seed, const jbyte* data, int len) { - jint h1 = seed; +juint AltHashing::murmur3_32(juint seed, const jbyte* data, int len) { + juint h1 = seed; int count = len; int offset = 0; // body while (count >= 4) { - jint k1 = (data[offset] & 0x0FF) + juint k1 = (data[offset] & 0x0FF) | (data[offset + 1] & 0x0FF) << 8 | (data[offset + 2] & 0x0FF) << 16 | data[offset + 3] << 24; @@ -85,7 +85,7 @@ // tail if (count > 0) { - jint k1 = 0; + juint k1 = 0; switch (count) { case 3: @@ -109,18 +109,18 @@ h1 ^= len; // finalization mix force all bits of a hash block to avalanche - h1 ^= ((unsigned int)h1) >> 16; + h1 ^= h1 >> 16; h1 *= 0x85ebca6b; - h1 ^= ((unsigned int)h1) >> 13; + h1 ^= h1 >> 13; h1 *= 0xc2b2ae35; - h1 ^= ((unsigned int)h1) >> 16; + h1 ^= h1 >> 16; return h1; } // Murmur3 hashing for Strings -jint AltHashing::murmur3_32(jint seed, const jchar* data, int len) { - jint h1 = seed; +juint AltHashing::murmur3_32(juint seed, const jchar* data, int len) { + juint h1 = seed; int off = 0; int count = len; @@ -129,7 +129,7 @@ while (count >= 2) { jchar d1 = data[off++] & 0xFFFF; jchar d2 = data[off++]; - jint k1 = (d1 | d2 << 16); + juint k1 = (d1 | d2 << 16); count -= 2; @@ -145,7 +145,7 @@ // tail if (count > 0) { - int k1 = data[off]; + juint k1 = (juint)data[off]; k1 *= 0xcc9e2d51; k1 = Integer_rotateLeft(k1, 15); @@ -157,25 +157,25 @@ h1 ^= len * 2; // (Character.SIZE / Byte.SIZE); // finalization mix force all bits of a hash block to avalanche - h1 ^= ((unsigned int)h1) >> 16; + h1 ^= h1 >> 16; h1 *= 0x85ebca6b; - h1 ^= ((unsigned int)h1) >> 13; + h1 ^= h1 >> 13; h1 *= 0xc2b2ae35; - h1 ^= ((unsigned int)h1) >> 16; + h1 ^= h1 >> 16; return h1; } // Hash used for the seed. -jint AltHashing::murmur3_32(jint seed, const int* data, int len) { - jint h1 = seed; +juint AltHashing::murmur3_32(juint seed, const int* data, int len) { + juint h1 = seed; int off = 0; int end = len; // body while (off < end) { - jint k1 = data[off++]; + juint k1 = (juint)data[off++]; k1 *= 0xcc9e2d51; k1 = Integer_rotateLeft(k1, 15); @@ -193,26 +193,26 @@ h1 ^= len * 4; // (Integer.SIZE / Byte.SIZE); // finalization mix force all bits of a hash block to avalanche - h1 ^= ((juint)h1) >> 16; + h1 ^= h1 >> 16; h1 *= 0x85ebca6b; - h1 ^= ((juint)h1) >> 13; + h1 ^= h1 >> 13; h1 *= 0xc2b2ae35; - h1 ^= ((juint)h1) >> 16; + h1 ^= h1 >> 16; return h1; } -jint AltHashing::murmur3_32(const int* data, int len) { +juint AltHashing::murmur3_32(const int* data, int len) { return murmur3_32(0, data, len); } #ifndef PRODUCT // Overloaded versions for internal test. -jint AltHashing::murmur3_32(const jbyte* data, int len) { +juint AltHashing::murmur3_32(const jbyte* data, int len) { return murmur3_32(0, data, len); } -jint AltHashing::murmur3_32(const jchar* data, int len) { +juint AltHashing::murmur3_32(const jchar* data, int len) { return murmur3_32(0, data, len); } @@ -251,11 +251,11 @@ // Hash subranges {}, {0}, {0,1}, {0,1,2}, ..., {0,...,255} for (int i = 0; i < 256; i++) { - jint hash = murmur3_32(256 - i, vector, i); + juint hash = murmur3_32(256 - i, vector, i); hashes[i * 4] = (jbyte) hash; - hashes[i * 4 + 1] = (jbyte) (((juint)hash) >> 8); - hashes[i * 4 + 2] = (jbyte) (((juint)hash) >> 16); - hashes[i * 4 + 3] = (jbyte) (((juint)hash) >> 24); + hashes[i * 4 + 1] = (jbyte)(hash >> 8); + hashes[i * 4 + 2] = (jbyte)(hash >> 16); + hashes[i * 4 + 3] = (jbyte)(hash >> 24); } // hash to get const result. @@ -269,7 +269,7 @@ } void AltHashing::testEquivalentHashes() { - jint jbytes, jchars, ints; + juint jbytes, jchars, ints; // printf("testEquivalentHashes\n"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/altHashing.hpp --- a/src/share/vm/classfile/altHashing.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/altHashing.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -39,24 +39,24 @@ class AltHashing : AllStatic { // utility function copied from java/lang/Integer - static jint Integer_rotateLeft(jint i, int distance) { - return (i << distance) | (((juint)i) >> (32-distance)); + static juint Integer_rotateLeft(juint i, int distance) { + return (i << distance) | (i >> (32-distance)); } - static jint murmur3_32(const int* data, int len); - static jint murmur3_32(jint seed, const int* data, int len); + static juint murmur3_32(const int* data, int len); + static juint murmur3_32(juint seed, const int* data, int len); #ifndef PRODUCT // Hashing functions used for internal testing - static jint murmur3_32(const jbyte* data, int len); - static jint murmur3_32(const jchar* data, int len); + static juint murmur3_32(const jbyte* data, int len); + static juint murmur3_32(const jchar* data, int len); static void testMurmur3_32_ByteArray(); static void testEquivalentHashes(); #endif // PRODUCT public: - static jint compute_seed(); - static jint murmur3_32(jint seed, const jbyte* data, int len); - static jint murmur3_32(jint seed, const jchar* data, int len); + static juint compute_seed(); + static juint murmur3_32(juint seed, const jbyte* data, int len); + static juint murmur3_32(juint seed, const jchar* data, int len); NOT_PRODUCT(static void test_alt_hash();) }; #endif // SHARE_VM_CLASSFILE_ALTHASHING_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/classFileError.cpp --- a/src/share/vm/classfile/classFileError.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/classFileError.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -29,6 +29,9 @@ // Keep these in a separate file to prevent inlining +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED + void ClassFileParser::classfile_parse_error(const char* msg, TRAPS) { ResourceMark rm(THREAD); Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_ClassFormatError(), @@ -53,6 +56,8 @@ msg, index, name, _class_name->as_C_string()); } +PRAGMA_DIAG_POP + void StackMapStream::stackmap_format_error(const char* msg, TRAPS) { ResourceMark rm(THREAD); Exceptions::fthrow( diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/classFileParser.cpp --- a/src/share/vm/classfile/classFileParser.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/classFileParser.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -2799,6 +2799,11 @@ "Short length on BootstrapMethods in class file %s", CHECK); + guarantee_property(attribute_byte_length >= sizeof(u2), + "Invalid BootstrapMethods attribute length %u in class file %s", + attribute_byte_length, + CHECK); + // The attribute contains a counted array of counted tuples of shorts, // represending bootstrap specifiers: // length*{bootstrap_method_index, argument_count*{argument_index}} @@ -3768,18 +3773,24 @@ Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_UnsupportedClassVersionError(), - "Unsupported major.minor version %u.%u", + "Unsupported class file version %u.%u, " + "this version of the Java Runtime only recognizes class file versions up to %u.%u", major_version, - minor_version); + minor_version, + JAVA_MAX_SUPPORTED_VERSION, + JAVA_MAX_SUPPORTED_MINOR_VERSION); } else { ResourceMark rm(THREAD); Exceptions::fthrow( THREAD_AND_LOCATION, vmSymbols::java_lang_UnsupportedClassVersionError(), - "%s : Unsupported major.minor version %u.%u", + "%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), " + "this version of the Java Runtime only recognizes class file versions up to %u.%u", name->as_C_string(), major_version, - minor_version); + minor_version, + JAVA_MAX_SUPPORTED_VERSION, + JAVA_MAX_SUPPORTED_MINOR_VERSION); } return nullHandle; } @@ -4033,6 +4044,11 @@ this_klass->set_major_version(major_version); this_klass->set_has_default_methods(has_default_methods); + if (!host_klass.is_null()) { + assert (this_klass->is_anonymous(), "should be the same"); + this_klass->set_host_klass(host_klass()); + } + // Set up Method*::intrinsic_id as soon as we know the names of methods. // (We used to do this lazily, but now we query it in Rewriter, // which is eagerly done for every method, so we might as well do it now, @@ -4192,8 +4208,12 @@ clear_class_metadata(); - // deallocate the klass if already created. - MetadataFactory::free_metadata(_loader_data, _klass); + // deallocate the klass if already created. Don't directly deallocate, but add + // to the deallocate list so that the klass is removed from the CLD::_klasses list + // at a safepoint. + if (_klass != NULL) { + _loader_data->add_to_deallocate_list(_klass); + } _klass = NULL; } @@ -4522,8 +4542,8 @@ break; // didn't find any match; get out } - if (super_m->is_final() && - // matching method in super is final + if (super_m->is_final() && !super_m->is_static() && + // matching method in super is final, and not static (Reflection::verify_field_access(this_klass(), super_m->method_holder(), super_m->method_holder(), diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/classFileParser.hpp --- a/src/share/vm/classfile/classFileParser.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/classFileParser.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -313,7 +313,9 @@ if (!b) { classfile_parse_error(msg, CHECK); } } - inline void assert_property(bool b, const char* msg, TRAPS) { +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +inline void assert_property(bool b, const char* msg, TRAPS) { #ifdef ASSERT if (!b) { ResourceMark rm(THREAD); @@ -330,6 +332,7 @@ } #endif } +PRAGMA_DIAG_POP inline void check_property(bool property, const char* msg, int index, TRAPS) { if (_need_verify) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/classLoader.cpp --- a/src/share/vm/classfile/classLoader.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/classLoader.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -340,7 +343,7 @@ tty->print("[Meta index for %s=", entry->name()); for (int i = 0; i < meta_packages.length(); i++) { if (i > 0) tty->print(" "); - tty->print(meta_packages.at(i)); + tty->print("%s", meta_packages.at(i)); } tty->print_cr("]"); } @@ -1296,7 +1299,7 @@ e = e->next(); } jlong end = os::javaTimeMillis(); - tty->print_cr("CompileTheWorld : Done (%d classes, %d methods, %d ms)", + tty->print_cr("CompileTheWorld : Done (%d classes, %d methods, " JLONG_FORMAT " ms)", _compile_the_world_class_counter, _compile_the_world_method_counter, (end - start)); { // Print statistics as if before normal exit: diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/classLoaderData.cpp --- a/src/share/vm/classfile/classLoaderData.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/classLoaderData.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -269,10 +269,10 @@ ResourceMark rm; tty->print_cr("[TraceClassLoaderData] Adding k: " PTR_FORMAT " %s to CLD: " PTR_FORMAT " loader: " PTR_FORMAT " %s", - k, + p2i(k), k->external_name(), - k->class_loader_data(), - (void *)k->class_loader(), + p2i(k->class_loader_data()), + p2i((void *)k->class_loader()), loader_name()); } } @@ -307,11 +307,11 @@ if (TraceClassLoaderData) { ResourceMark rm; - tty->print("[ClassLoaderData: unload loader data "PTR_FORMAT, this); - tty->print(" for instance "PTR_FORMAT" of %s", (void *)class_loader(), + tty->print("[ClassLoaderData: unload loader data " INTPTR_FORMAT, p2i(this)); + tty->print(" for instance " INTPTR_FORMAT " of %s", p2i((void *)class_loader()), loader_name()); if (is_anonymous()) { - tty->print(" for anonymous class "PTR_FORMAT " ", _klasses); + tty->print(" for anonymous class " INTPTR_FORMAT " ", p2i(_klasses)); } tty->print_cr("]"); } @@ -469,14 +469,14 @@ void ClassLoaderData::dump(outputStream * const out) { ResourceMark rm; out->print("ClassLoaderData CLD: "PTR_FORMAT", loader: "PTR_FORMAT", loader_klass: "PTR_FORMAT" %s {", - this, (void *)class_loader(), - class_loader() != NULL ? class_loader()->klass() : NULL, loader_name()); + p2i(this), p2i((void *)class_loader()), + p2i(class_loader() != NULL ? class_loader()->klass() : NULL), loader_name()); if (claimed()) out->print(" claimed "); if (is_unloading()) out->print(" unloading "); - out->print(" handles " INTPTR_FORMAT, handles()); + out->print(" handles " INTPTR_FORMAT, p2i(handles())); out->cr(); if (metaspace_or_null() != NULL) { - out->print_cr("metaspace: " PTR_FORMAT, metaspace_or_null()); + out->print_cr("metaspace: " INTPTR_FORMAT, p2i(metaspace_or_null())); metaspace_or_null()->dump(out); } else { out->print_cr("metaspace: NULL"); @@ -520,12 +520,22 @@ } } +bool ClassLoaderData::contains_klass(Klass* klass) { + for (Klass* k = _klasses; k != NULL; k = k->next_link()) { + if (k == klass) return true; + } + return false; +} + // GC root of class loader data created. ClassLoaderData* ClassLoaderDataGraph::_head = NULL; ClassLoaderData* ClassLoaderDataGraph::_unloading = NULL; +ClassLoaderData* ClassLoaderDataGraph::_saved_unloading = NULL; ClassLoaderData* ClassLoaderDataGraph::_saved_head = NULL; +bool ClassLoaderDataGraph::_should_purge = false; + // Add a new class loader data node to the list. Assign the newly created // ClassLoaderData into the java/lang/ClassLoader object as a hidden field ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool is_anonymous, TRAPS) { @@ -563,8 +573,8 @@ if (TraceClassLoaderData) { ResourceMark rm; tty->print("[ClassLoaderData: "); - tty->print("create class loader data "PTR_FORMAT, cld); - tty->print(" for instance "PTR_FORMAT" of %s", (void *)cld->class_loader(), + tty->print("create class loader data " INTPTR_FORMAT, p2i(cld)); + tty->print(" for instance " INTPTR_FORMAT " of %s", p2i((void *)cld->class_loader()), cld->loader_name()); tty->print_cr("]"); } @@ -619,7 +629,9 @@ void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); - for (ClassLoaderData* cld = _unloading; cld != NULL; cld = cld->next()) { + // Only walk the head until any clds not purged from prior unloading + // (CMS doesn't purge right away). + for (ClassLoaderData* cld = _unloading; cld != _saved_unloading; cld = cld->next()) { cld->classes_do(f); } } @@ -649,37 +661,6 @@ } #ifndef PRODUCT -// for debugging and hsfind(x) -bool ClassLoaderDataGraph::contains(address x) { - // I think we need the _metaspace_lock taken here because the class loader - // data graph could be changing while we are walking it (new entries added, - // new entries being unloaded, etc). - if (DumpSharedSpaces) { - // There are only two metaspaces to worry about. - ClassLoaderData* ncld = ClassLoaderData::the_null_class_loader_data(); - return (ncld->ro_metaspace()->contains(x) || ncld->rw_metaspace()->contains(x)); - } - - if (UseSharedSpaces && MetaspaceShared::is_in_shared_space(x)) { - return true; - } - - for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) { - if (cld->metaspace_or_null() != NULL && cld->metaspace_or_null()->contains(x)) { - return true; - } - } - - // Could also be on an unloading list which is okay, ie. still allocated - // for a little while. - for (ClassLoaderData* ucld = _unloading; ucld != NULL; ucld = ucld->next()) { - if (ucld->metaspace_or_null() != NULL && ucld->metaspace_or_null()->contains(x)) { - return true; - } - } - return false; -} - bool ClassLoaderDataGraph::contains_loader_data(ClassLoaderData* loader_data) { for (ClassLoaderData* data = _head; data != NULL; data = data->next()) { if (loader_data == data) { @@ -698,6 +679,11 @@ ClassLoaderData* data = _head; ClassLoaderData* prev = NULL; bool seen_dead_loader = false; + + // Save previous _unloading pointer for CMS which may add to unloading list before + // purging and we don't want to rewalk the previously unloaded class loader data. + _saved_unloading = _unloading; + // mark metadata seen on the stack and code cache so we can delete // unneeded entries. bool has_redefined_a_class = JvmtiExport::has_redefined_a_class(); @@ -737,6 +723,7 @@ } void ClassLoaderDataGraph::purge() { + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); ClassLoaderData* list = _unloading; _unloading = NULL; ClassLoaderData* next = list; @@ -825,7 +812,7 @@ if (class_loader() == NULL) { out->print("NULL class_loader"); } else { - out->print("class loader "PTR_FORMAT, this); + out->print("class loader " INTPTR_FORMAT, p2i(this)); class_loader()->print_value_on(out); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/classLoaderData.hpp --- a/src/share/vm/classfile/classLoaderData.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/classLoaderData.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -66,6 +66,8 @@ static ClassLoaderData* _unloading; // CMS support. static ClassLoaderData* _saved_head; + static ClassLoaderData* _saved_unloading; + static bool _should_purge; static ClassLoaderData* add(Handle class_loader, bool anonymous, TRAPS); static void post_class_unload_events(void); @@ -86,13 +88,21 @@ static void remember_new_clds(bool remember) { _saved_head = (remember ? _head : NULL); } static GrowableArray* new_clds(); + static void set_should_purge(bool b) { _should_purge = b; } + static void purge_if_needed() { + // Only purge the CLDG for CMS if concurrent sweep is complete. + if (_should_purge) { + purge(); + // reset for next time. + set_should_purge(false); + } + } + static void dump_on(outputStream * const out) PRODUCT_RETURN; static void dump() { dump_on(tty); } static void verify(); #ifndef PRODUCT - // expensive test for pointer in metaspace for debugging - static bool contains(address x); static bool contains_loader_data(ClassLoaderData* loader_data); #endif @@ -260,6 +270,7 @@ jobject add_handle(Handle h); void add_class(Klass* k); void remove_class(Klass* k); + bool contains_klass(Klass* k); void record_dependency(Klass* to, TRAPS); void init_dependencies(TRAPS); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/defaultMethods.cpp --- a/src/share/vm/classfile/defaultMethods.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/defaultMethods.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -390,20 +390,6 @@ Symbol* get_exception_message() { return _exception_message; } Symbol* get_exception_name() { return _exception_name; } - // Return true if the specified klass has a static method that matches - // the name and signature of the target method. - bool has_matching_static(InstanceKlass* root) { - if (_members.length() > 0) { - Pair entry = _members.at(0); - Method* impl = root->find_method(entry.first->name(), - entry.first->signature()); - if ((impl != NULL) && impl->is_static()) { - return true; - } - } - return false; - } - // Either sets the target or the exception error message void determine_target(InstanceKlass* root, TRAPS) { if (has_target() || throws_exception()) { @@ -433,26 +419,24 @@ // If the root klass has a static method with matching name and signature // then do not generate an overpass method because it will hide the // static method during resolution. - if (!has_matching_static(root)) { - if (qualified_methods.length() == 0) { - _exception_message = generate_no_defaults_message(CHECK); - } else { - assert(root != NULL, "Null root class"); - _exception_message = generate_method_message(root->name(), qualified_methods.at(0), CHECK); - } - _exception_name = vmSymbols::java_lang_AbstractMethodError(); + if (qualified_methods.length() == 0) { + _exception_message = generate_no_defaults_message(CHECK); + } else { + assert(root != NULL, "Null root class"); + _exception_message = generate_method_message(root->name(), qualified_methods.at(0), CHECK); } + _exception_name = vmSymbols::java_lang_AbstractMethodError(); // If only one qualified method is default, select that } else if (num_defaults == 1) { _selected_target = qualified_methods.at(default_index); - } else if (num_defaults > 1 && !has_matching_static(root)) { + } else if (num_defaults > 1) { _exception_message = generate_conflicts_message(&qualified_methods,CHECK); _exception_name = vmSymbols::java_lang_IncompatibleClassChangeError(); if (TraceDefaultMethods) { _exception_message->print_value_on(tty); - tty->print_cr(""); + tty->cr(); } } } @@ -479,7 +463,7 @@ if (_members.at(i).second == DISQUALIFIED) { str->print(" (disqualified)"); } - str->print_cr(""); + str->cr(); } if (_selected_target != NULL) { @@ -496,7 +480,7 @@ if (!method_holder->is_interface()) { tty->print(" : in superclass"); } - str->print_cr(""); + str->cr(); } void print_exception(outputStream* str, int indent) { @@ -704,7 +688,7 @@ for (int i = 0; i < slots->length(); ++i) { tty->indent(); slots->at(i)->print_on(tty); - tty->print_cr(""); + tty->cr(); } } #endif // ndef PRODUCT @@ -844,7 +828,7 @@ streamIndentor si(tty, 2); tty->indent().print("Looking for default methods for slot "); slot->print_on(tty); - tty->print_cr(""); + tty->cr(); } #endif // ndef PRODUCT @@ -962,7 +946,7 @@ if (TraceDefaultMethods) { tty->print("for slot: "); slot->print_on(tty); - tty->print_cr(""); + tty->cr(); if (method->has_target()) { method->print_selected(tty, 1); } else if (method->throws_exception()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/dictionary.cpp --- a/src/share/vm/classfile/dictionary.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/dictionary.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -30,6 +30,7 @@ #include "prims/jvmtiRedefineClassesTrace.hpp" #include "utilities/hashtable.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC DictionaryEntry* Dictionary::_current_class_entry = NULL; int Dictionary::_current_class_index = 0; @@ -707,7 +708,7 @@ loader_data->class_loader() == NULL || loader_data->class_loader()->is_instance(), "checking type of class_loader"); - e->verify(/*check_dictionary*/false); + e->verify(); probe->verify_protection_domain_set(); element_count++; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/dictionary.hpp --- a/src/share/vm/classfile/dictionary.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/dictionary.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -379,7 +379,7 @@ } if (method_type() != NULL) { if (printed) st->print(" and "); - st->print(INTPTR_FORMAT, (void *)method_type()); + st->print(INTPTR_FORMAT, p2i((void *)method_type())); printed = true; } st->print_cr(printed ? "" : "(empty)"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/javaClasses.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -51,6 +51,8 @@ #include "runtime/vframe.hpp" #include "utilities/preserveException.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #define INJECTED_FIELD_COMPUTE_OFFSET(klass, name, signature, may_be_java) \ klass::_##name##_offset = JavaClasses::compute_injected_offset(JavaClasses::klass##_##name##_enum); @@ -482,8 +484,8 @@ } } -static void initialize_static_field(fieldDescriptor* fd, TRAPS) { - Handle mirror (THREAD, fd->field_holder()->java_mirror()); + +static void initialize_static_field(fieldDescriptor* fd, Handle mirror, TRAPS) { assert(mirror.not_null() && fd->is_static(), "just checking"); if (fd->has_initial_value()) { BasicType t = fd->field_type(); @@ -550,21 +552,51 @@ create_mirror(k, Handle(NULL), CHECK); } -oop java_lang_Class::create_mirror(KlassHandle k, Handle protection_domain, TRAPS) { +void java_lang_Class::initialize_mirror_fields(KlassHandle k, + Handle mirror, + Handle protection_domain, + TRAPS) { + // Allocate a simple java object for a lock. + // This needs to be a java object because during class initialization + // it can be held across a java call. + typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK); + set_init_lock(mirror(), r); + + // Set protection domain also +#ifdef GRAAL + if (k->class_loader() == SystemDictionary::graal_loader()) { + // Same protection domain as for classes loaded by the boot loader + protection_domain = Handle(); + } +#endif + set_protection_domain(mirror(), protection_domain()); + + // Initialize static fields + InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, mirror, CHECK); +} + +void java_lang_Class::create_mirror(KlassHandle k, Handle protection_domain, TRAPS) { assert(k->java_mirror() == NULL, "should only assign mirror once"); // Use this moment of initialization to cache modifier_flags also, // to support Class.getModifiers(). Instance classes recalculate // the cached flags after the class file is parsed, but before the // class is put into the system dictionary. - int computed_modifiers = k->compute_modifier_flags(CHECK_0); + int computed_modifiers = k->compute_modifier_flags(CHECK); k->set_modifier_flags(computed_modifiers); // Class_klass has to be loaded because it is used to allocate // the mirror. if (SystemDictionary::Class_klass_loaded()) { // Allocate mirror (java.lang.Class instance) - Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK_0); + Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK); + + // Setup indirection from mirror->klass + if (!k.is_null()) { + java_lang_Class::set_klass(mirror(), k()); + } InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); + assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); + java_lang_Class::set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror())); // It might also have a component mirror. This mirror must already exist. @@ -577,35 +609,32 @@ assert(k->oop_is_objArray(), "Must be"); Klass* element_klass = ObjArrayKlass::cast(k())->element_klass(); assert(element_klass != NULL, "Must have an element klass"); - comp_mirror = element_klass->java_mirror(); + comp_mirror = element_klass->java_mirror(); } assert(comp_mirror.not_null(), "must have a mirror"); - // Two-way link between the array klass and its component mirror: + // Two-way link between the array klass and its component mirror: ArrayKlass::cast(k())->set_component_mirror(comp_mirror()); set_array_klass(comp_mirror(), k()); } else { assert(k->oop_is_instance(), "Must be"); - // Allocate a simple java object for a lock. - // This needs to be a java object because during class initialization - // it can be held across a java call. - typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_NULL); - set_init_lock(mirror(), r); - - // Set protection domain also -#ifdef GRAAL - if (k->class_loader() == SystemDictionary::graal_loader()) { - // Same protection domain as for classes loaded by the boot loader - protection_domain = Handle(); + initialize_mirror_fields(k, mirror, protection_domain, THREAD); + if (HAS_PENDING_EXCEPTION) { + // If any of the fields throws an exception like OOM remove the klass field + // from the mirror so GC doesn't follow it after the klass has been deallocated. + // This mirror looks like a primitive type, which logically it is because it + // it represents no class. + java_lang_Class::set_klass(mirror(), NULL); + return; } -#endif - set_protection_domain(mirror(), protection_domain()); - - // Initialize static fields - InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, CHECK_NULL); } - return mirror(); + + // Setup indirection from klass->mirror last + // after any exceptions can happen during allocations. + if (!k.is_null()) { + k->set_java_mirror(mirror()); + } } else { if (fixup_mirror_list() == NULL) { GrowableArray* list = @@ -613,12 +642,10 @@ set_fixup_mirror_list(list); } fixup_mirror_list()->push(k()); - return NULL; } } - int java_lang_Class::oop_size(oop java_class) { assert(_oop_size_offset != 0, "must be set"); return java_class->int_field(_oop_size_offset); @@ -680,6 +707,7 @@ return java_class; } + Klass* java_lang_Class::as_Klass(oop java_class) { //%note memory_2 assert(java_lang_Class::is_instance(java_class), "must be a Class object"); @@ -1461,7 +1489,7 @@ while (h_throwable.not_null()) { objArrayHandle result (THREAD, objArrayOop(backtrace(h_throwable()))); if (result.is_null()) { - st->print_cr(no_stack_trace_message()); + st->print_raw_cr(no_stack_trace_message()); return; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/javaClasses.hpp --- a/src/share/vm/classfile/javaClasses.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/javaClasses.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -61,10 +61,6 @@ static Handle basic_create(int length, TRAPS); - static void set_value( oop string, typeArrayOop buffer) { - assert(initialized, "Must be initialized"); - string->obj_field_put(value_offset, (oop)buffer); - } static void set_offset(oop string, int offset) { assert(initialized, "Must be initialized"); if (offset_offset > 0) { @@ -122,12 +118,26 @@ return hash_offset; } + static void set_value(oop string, typeArrayOop buffer) { + assert(initialized && (value_offset > 0), "Must be initialized"); + string->obj_field_put(value_offset, (oop)buffer); + } + static void set_hash(oop string, unsigned int hash) { + assert(initialized && (hash_offset > 0), "Must be initialized"); + string->int_field_put(hash_offset, hash); + } + // Accessors static typeArrayOop value(oop java_string) { assert(initialized && (value_offset > 0), "Must be initialized"); assert(is_instance(java_string), "must be java_string"); return (typeArrayOop) java_string->obj_field(value_offset); } + static unsigned int hash(oop java_string) { + assert(initialized && (hash_offset > 0), "Must be initialized"); + assert(is_instance(java_string), "must be java_string"); + return java_string->int_field(hash_offset); + } static int offset(oop java_string) { assert(initialized, "Must be initialized"); assert(is_instance(java_string), "must be java_string"); @@ -236,11 +246,12 @@ static void set_init_lock(oop java_class, oop init_lock); static void set_protection_domain(oop java_class, oop protection_domain); + static void initialize_mirror_fields(KlassHandle k, Handle mirror, Handle protection_domain, TRAPS); public: static void compute_offsets(); // Instance creation - static oop create_mirror(KlassHandle k, Handle protection_domain, TRAPS); + static void create_mirror(KlassHandle k, Handle protection_domain, TRAPS); static void fixup_mirror(KlassHandle k, TRAPS); static oop create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS); // Conversion diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/symbolTable.cpp --- a/src/share/vm/classfile/symbolTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/symbolTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -35,9 +35,17 @@ #include "oops/oop.inline2.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/hashtable.inline.hpp" +#if INCLUDE_ALL_GCS +#include "gc_implementation/g1/g1StringDedup.hpp" +#endif + +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // -------------------------------------------------------------------------- +// the number of buckets a thread claims +const int ClaimChunkSize = 32; + SymbolTable* SymbolTable::_the_table = NULL; // Static arena for symbols that are not deallocated Arena* SymbolTable::_arena = NULL; @@ -83,16 +91,12 @@ } } -int SymbolTable::symbols_removed = 0; -int SymbolTable::symbols_counted = 0; +int SymbolTable::_symbols_removed = 0; +int SymbolTable::_symbols_counted = 0; +volatile int SymbolTable::_parallel_claimed_idx = 0; -// Remove unreferenced symbols from the symbol table -// This is done late during GC. -void SymbolTable::unlink() { - int removed = 0; - int total = 0; - size_t memory_total = 0; - for (int i = 0; i < the_table()->table_size(); ++i) { +void SymbolTable::buckets_unlink(int start_idx, int end_idx, int* processed, int* removed, size_t* memory_total) { + for (int i = start_idx; i < end_idx; ++i) { HashtableEntry** p = the_table()->bucket_addr(i); HashtableEntry* entry = the_table()->bucket(i); while (entry != NULL) { @@ -104,14 +108,14 @@ break; } Symbol* s = entry->literal(); - memory_total += s->size(); - total++; + (*memory_total) += s->size(); + (*processed)++; assert(s != NULL, "just checking"); // If reference count is zero, remove. if (s->refcount() == 0) { assert(!entry->is_shared(), "shared entries should be kept live"); delete s; - removed++; + (*removed)++; *p = entry->next(); the_table()->free_entry(entry); } else { @@ -121,12 +125,45 @@ entry = (HashtableEntry*)HashtableEntry::make_ptr(*p); } } - symbols_removed += removed; - symbols_counted += total; +} + +// Remove unreferenced symbols from the symbol table +// This is done late during GC. +void SymbolTable::unlink(int* processed, int* removed) { + size_t memory_total = 0; + buckets_unlink(0, the_table()->table_size(), processed, removed, &memory_total); + _symbols_removed += *removed; + _symbols_counted += *processed; // Exclude printing for normal PrintGCDetails because people parse // this output. if (PrintGCDetails && Verbose && WizardMode) { - gclog_or_tty->print(" [Symbols=%d size=" SIZE_FORMAT "K] ", total, + gclog_or_tty->print(" [Symbols=%d size=" SIZE_FORMAT "K] ", *processed, + (memory_total*HeapWordSize)/1024); + } +} + +void SymbolTable::possibly_parallel_unlink(int* processed, int* removed) { + const int limit = the_table()->table_size(); + + size_t memory_total = 0; + + for (;;) { + // Grab next set of buckets to scan + int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize; + if (start_idx >= limit) { + // End of table + break; + } + + int end_idx = MIN2(limit, start_idx + ClaimChunkSize); + buckets_unlink(start_idx, end_idx, processed, removed, &memory_total); + } + Atomic::add(*processed, &_symbols_counted); + Atomic::add(*removed, &_symbols_removed); + // Exclude printing for normal PrintGCDetails because people parse + // this output. + if (PrintGCDetails && Verbose && WizardMode) { + gclog_or_tty->print(" [Symbols: scanned=%d removed=%d size=" SIZE_FORMAT "K] ", *processed, *removed, (memory_total*HeapWordSize)/1024); } } @@ -494,11 +531,11 @@ tty->print_cr("Total number of symbols %5d", count); tty->print_cr("Total size in memory %5dK", (memory_total*HeapWordSize)/1024); - tty->print_cr("Total counted %5d", symbols_counted); - tty->print_cr("Total removed %5d", symbols_removed); - if (symbols_counted > 0) { + tty->print_cr("Total counted %5d", _symbols_counted); + tty->print_cr("Total removed %5d", _symbols_removed); + if (_symbols_counted > 0) { tty->print_cr("Percent removed %3.2f", - ((float)symbols_removed/(float)symbols_counted)* 100); + ((float)_symbols_removed/(float)_symbols_counted)* 100); } tty->print_cr("Reference counts %5d", Symbol::_total_count); tty->print_cr("Symbol arena size %5d used %5d", @@ -696,6 +733,15 @@ string = java_lang_String::create_from_unicode(name, len, CHECK_NULL); } +#if INCLUDE_ALL_GCS + if (G1StringDedup::is_enabled()) { + // Deduplicate the string before it is interned. Note that we should never + // deduplicate a string after it has been interned. Doing so will counteract + // compiler optimizations done on e.g. interned string literals. + G1StringDedup::deduplicate(string()); + } +#endif + // Grab the StringTable_lock before getting the_table() because it could // change at safepoint. MutexLocker ml(StringTable_lock, THREAD); @@ -739,39 +785,38 @@ return result; } -void StringTable::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { +void StringTable::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int* processed, int* removed) { + buckets_unlink_or_oops_do(is_alive, f, 0, the_table()->table_size(), processed, removed); +} + +void StringTable::possibly_parallel_unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int* processed, int* removed) { // Readers of the table are unlocked, so we should only be removing // entries at a safepoint. assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - for (int i = 0; i < the_table()->table_size(); ++i) { - HashtableEntry** p = the_table()->bucket_addr(i); - HashtableEntry* entry = the_table()->bucket(i); - while (entry != NULL) { - assert(!entry->is_shared(), "CDS not used for the StringTable"); + const int limit = the_table()->table_size(); - if (is_alive->do_object_b(entry->literal())) { - if (f != NULL) { - f->do_oop((oop*)entry->literal_addr()); - } - p = entry->next_addr(); - } else { - *p = entry->next(); - the_table()->free_entry(entry); - } - entry = *p; + for (;;) { + // Grab next set of buckets to scan + int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize; + if (start_idx >= limit) { + // End of table + break; } + + int end_idx = MIN2(limit, start_idx + ClaimChunkSize); + buckets_unlink_or_oops_do(is_alive, f, start_idx, end_idx, processed, removed); } } -void StringTable::buckets_do(OopClosure* f, int start_idx, int end_idx) { +void StringTable::buckets_oops_do(OopClosure* f, int start_idx, int end_idx) { const int limit = the_table()->table_size(); assert(0 <= start_idx && start_idx <= limit, - err_msg("start_idx (" INT32_FORMAT ") oob?", start_idx)); + err_msg("start_idx (" INT32_FORMAT ") is out of bounds", start_idx)); assert(0 <= end_idx && end_idx <= limit, - err_msg("end_idx (" INT32_FORMAT ") oob?", end_idx)); + err_msg("end_idx (" INT32_FORMAT ") is out of bounds", end_idx)); assert(start_idx <= end_idx, - err_msg("Ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT, + err_msg("Index ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT, start_idx, end_idx)); for (int i = start_idx; i < end_idx; i += 1) { @@ -786,12 +831,44 @@ } } +void StringTable::buckets_unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int start_idx, int end_idx, int* processed, int* removed) { + const int limit = the_table()->table_size(); + + assert(0 <= start_idx && start_idx <= limit, + err_msg("start_idx (" INT32_FORMAT ") is out of bounds", start_idx)); + assert(0 <= end_idx && end_idx <= limit, + err_msg("end_idx (" INT32_FORMAT ") is out of bounds", end_idx)); + assert(start_idx <= end_idx, + err_msg("Index ordering: start_idx=" INT32_FORMAT", end_idx=" INT32_FORMAT, + start_idx, end_idx)); + + for (int i = start_idx; i < end_idx; ++i) { + HashtableEntry** p = the_table()->bucket_addr(i); + HashtableEntry* entry = the_table()->bucket(i); + while (entry != NULL) { + assert(!entry->is_shared(), "CDS not used for the StringTable"); + + if (is_alive->do_object_b(entry->literal())) { + if (f != NULL) { + f->do_oop((oop*)entry->literal_addr()); + } + p = entry->next_addr(); + } else { + *p = entry->next(); + the_table()->free_entry(entry); + (*removed)++; + } + (*processed)++; + entry = *p; + } + } +} + void StringTable::oops_do(OopClosure* f) { - buckets_do(f, 0, the_table()->table_size()); + buckets_oops_do(f, 0, the_table()->table_size()); } void StringTable::possibly_parallel_oops_do(OopClosure* f) { - const int ClaimChunkSize = 32; const int limit = the_table()->table_size(); for (;;) { @@ -803,7 +880,7 @@ } int end_idx = MIN2(limit, start_idx + ClaimChunkSize); - buckets_do(f, start_idx, end_idx); + buckets_oops_do(f, start_idx, end_idx); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/symbolTable.hpp --- a/src/share/vm/classfile/symbolTable.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/symbolTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -86,8 +86,8 @@ static bool _needs_rehashing; // For statistics - static int symbols_removed; - static int symbols_counted; + static int _symbols_removed; + static int _symbols_counted; Symbol* allocate_symbol(const u1* name, int len, bool c_heap, TRAPS); // Assumes no characters larger than 0x7F @@ -121,6 +121,11 @@ static Arena* arena() { return _arena; } // called for statistics static void initialize_symbols(int arena_alloc_size = 0); + + static volatile int _parallel_claimed_idx; + + // Release any dead symbols + static void buckets_unlink(int start_idx, int end_idx, int* processed, int* removed, size_t* memory_total); public: enum { symbol_alloc_batch_size = 8, @@ -177,7 +182,14 @@ unsigned int* hashValues, TRAPS); // Release any dead symbols - static void unlink(); + static void unlink() { + int processed = 0; + int removed = 0; + unlink(&processed, &removed); + } + static void unlink(int* processed, int* removed); + // Release any dead symbols, possibly parallel version + static void possibly_parallel_unlink(int* processed, int* removed); // iterate over symbols static void symbols_do(SymbolClosure *cl); @@ -235,6 +247,9 @@ // Rehash the symbol table if it gets out of balance static void rehash_table(); static bool needs_rehashing() { return _needs_rehashing; } + // Parallel chunked scanning + static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; } + static int parallel_claimed_index() { return _parallel_claimed_idx; } }; class StringTable : public Hashtable { @@ -258,7 +273,10 @@ // Apply the give oop closure to the entries to the buckets // in the range [start_idx, end_idx). - static void buckets_do(OopClosure* f, int start_idx, int end_idx); + static void buckets_oops_do(OopClosure* f, int start_idx, int end_idx); + // Unlink or apply the give oop closure to the entries to the buckets + // in the range [start_idx, end_idx). + static void buckets_unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int start_idx, int end_idx, int* processed, int* removed); StringTable() : Hashtable((int)StringTableSize, sizeof (HashtableEntry)) {} @@ -280,15 +298,28 @@ // GC support // Delete pointers to otherwise-unreachable objects. - static void unlink_or_oops_do(BoolObjectClosure* cl, OopClosure* f); + static void unlink_or_oops_do(BoolObjectClosure* cl, OopClosure* f) { + int processed = 0; + int removed = 0; + unlink_or_oops_do(cl, f, &processed, &removed); + } static void unlink(BoolObjectClosure* cl) { - unlink_or_oops_do(cl, NULL); + int processed = 0; + int removed = 0; + unlink_or_oops_do(cl, NULL, &processed, &removed); } - + static void unlink_or_oops_do(BoolObjectClosure* cl, OopClosure* f, int* processed, int* removed); + static void unlink(BoolObjectClosure* cl, int* processed, int* removed) { + unlink_or_oops_do(cl, NULL, processed, removed); + } // Serially invoke "f->do_oop" on the locations of all oops in the table. static void oops_do(OopClosure* f); - // Possibly parallel version of the above + // Possibly parallel versions of the above + static void possibly_parallel_unlink_or_oops_do(BoolObjectClosure* cl, OopClosure* f, int* processed, int* removed); + static void possibly_parallel_unlink(BoolObjectClosure* cl, int* processed, int* removed) { + possibly_parallel_unlink_or_oops_do(cl, NULL, processed, removed); + } static void possibly_parallel_oops_do(OopClosure* f); // Hashing algorithm, used as the hash value used by the @@ -349,5 +380,6 @@ // Parallel chunked scanning static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; } + static int parallel_claimed_index() { return _parallel_claimed_idx; } }; #endif // SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/systemDictionary.cpp --- a/src/share/vm/classfile/systemDictionary.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/systemDictionary.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -837,47 +837,6 @@ } } // load_instance_class loop - if (HAS_PENDING_EXCEPTION) { - // An exception, such as OOM could have happened at various places inside - // load_instance_class. We might have partially initialized a shared class - // and need to clean it up. - if (class_loader.is_null()) { - // In some cases k may be null. Let's find the shared class again. - instanceKlassHandle ik(THREAD, find_shared_class(name)); - if (ik.not_null()) { - if (ik->class_loader_data() == NULL) { - // We didn't go as far as Klass::restore_unshareable_info(), - // so nothing to clean up. - } else { - Klass *kk; - { - MutexLocker mu(SystemDictionary_lock, THREAD); - kk = find_class(d_index, d_hash, name, ik->class_loader_data()); - } - if (kk != NULL) { - // No clean up is needed if the shared class has been entered - // into system dictionary, as load_shared_class() won't be called - // again. - } else { - // This must be done outside of the SystemDictionary_lock to - // avoid deadlock. - // - // Note that Klass::restore_unshareable_info (called via - // load_instance_class above) is also called outside - // of SystemDictionary_lock. Other threads are blocked from - // loading this class because they are waiting on the - // SystemDictionary_lock until this thread removes - // the placeholder below. - // - // This need to be re-thought when parallel-capable non-boot - // classloaders are supported by CDS (today they're not). - clean_up_shared_class(ik, class_loader, THREAD); - } - } - } - } - } - if (load_instance_added == true) { // clean up placeholder entries for LOAD_INSTANCE success or error // This brackets the SystemDictionary updates for both defining @@ -1049,7 +1008,6 @@ if (host_klass.not_null() && k.not_null()) { assert(EnableInvokeDynamic, ""); - k->set_host_klass(host_klass()); // If it's anonymous, initialize it now, since nobody else will. { @@ -1283,19 +1241,6 @@ return ik; } -void SystemDictionary::clean_up_shared_class(instanceKlassHandle ik, Handle class_loader, TRAPS) { - // Updating methods must be done under a lock so multiple - // threads don't update these in parallel - // Shared classes are all currently loaded by the bootstrap - // classloader, so this will never cause a deadlock on - // a custom class loader lock. - { - Handle lockObject = compute_loader_lock_object(class_loader, THREAD); - check_loader_lock_contention(lockObject, THREAD); - ObjectLocker ol(lockObject, THREAD, true); - ik->remove_unshareable_info(); - } -} instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) { instanceKlassHandle nh = instanceKlassHandle(); // null Handle @@ -2362,7 +2307,7 @@ oop appendix = appendix_box->obj_at(0); if (TraceMethodHandles) { #ifndef PRODUCT - tty->print("Linked method="INTPTR_FORMAT": ", m); + tty->print("Linked method=" INTPTR_FORMAT ": ", p2i(m)); m->print(); if (appendix != NULL) { tty->print("appendix = "); appendix->print(); } tty->cr(); @@ -2690,23 +2635,6 @@ constraints()->verify(dictionary(), placeholders()); } - -void SystemDictionary::verify_obj_klass_present(Symbol* class_name, - ClassLoaderData* loader_data) { - GCMutexLocker mu(SystemDictionary_lock); - Symbol* name; - - Klass* probe = find_class(class_name, loader_data); - if (probe == NULL) { - probe = SystemDictionary::find_shared_class(class_name); - if (probe == NULL) { - name = find_placeholder(class_name, loader_data); - } - } - guarantee(probe != NULL || name != NULL, - "Loaded klasses should be in SystemDictionary"); -} - // utility function for class load event void SystemDictionary::post_class_load_event(const Ticks& start_time, instanceKlassHandle k, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/systemDictionary.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -448,10 +448,6 @@ static bool is_internal_format(Symbol* class_name); #endif - // Verify class is in dictionary - static void verify_obj_klass_present(Symbol* class_name, - ClassLoaderData* loader_data); - // Initialization static void initialize(TRAPS); @@ -703,7 +699,6 @@ Handle class_loader, TRAPS); static instanceKlassHandle load_shared_class(instanceKlassHandle ik, Handle class_loader, TRAPS); - static void clean_up_shared_class(instanceKlassHandle ik, Handle class_loader, TRAPS); static instanceKlassHandle load_instance_class(Symbol* class_name, Handle class_loader, TRAPS); static Handle compute_loader_lock_object(Handle class_loader, TRAPS); static void check_loader_lock_contention(Handle loader_lock, TRAPS); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/verificationType.hpp --- a/src/share/vm/classfile/verificationType.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/verificationType.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -299,7 +299,7 @@ int dimensions() const { assert(is_array(), "Must be an array"); int index = 0; - while (name()->byte_at(index++) == '['); + while (name()->byte_at(index) == '[') index++; return index; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/verifier.cpp --- a/src/share/vm/classfile/verifier.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/verifier.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -364,7 +364,7 @@ void ErrorContext::details(outputStream* ss, const Method* method) const { if (is_valid()) { - ss->print_cr(""); + ss->cr(); ss->print_cr("Exception Details:"); location_details(ss, method); reason_details(ss); @@ -379,7 +379,7 @@ streamIndentor si(ss); ss->indent().print_cr("Reason:"); streamIndentor si2(ss); - ss->indent().print(""); + ss->indent().print("%s", ""); switch (_fault) { case INVALID_BYTECODE: ss->print("Error exists in the bytecode"); @@ -432,7 +432,7 @@ ShouldNotReachHere(); ss->print_cr("Unknown"); } - ss->print_cr(""); + ss->cr(); } void ErrorContext::location_details(outputStream* ss, const Method* method) const { @@ -507,7 +507,7 @@ for (u2 i = 0; i < sm_table->number_of_entries(); ++i) { ss->indent(); sm_frame->print_on(ss, current_offset); - ss->print_cr(""); + ss->cr(); current_offset += sm_frame->offset_delta(); sm_frame = sm_frame->next(); } @@ -579,7 +579,8 @@ tty->print_cr("Verifying method %s", m->name_and_sig_as_C_string()); } - const char* bad_type_msg = "Bad type on operand stack in %s"; +// For clang, the only good constant format string is a literal constant format string. +#define bad_type_msg "Bad type on operand stack in %s" int32_t max_stack = m->verifier_max_stack(); int32_t max_locals = m->max_locals(); @@ -1679,6 +1680,8 @@ } } +#undef bad_type_message + char* ClassVerifier::generate_code_data(methodHandle m, u4 code_length, TRAPS) { char* code_data = NEW_RESOURCE_ARRAY(char, code_length); memset(code_data, 0, sizeof(char) * code_length); @@ -1946,7 +1949,7 @@ InstanceKlass* target_instance = InstanceKlass::cast(target_class); fieldDescriptor fd; if (is_method) { - Method* m = target_instance->uncached_lookup_method(field_name, field_sig); + Method* m = target_instance->uncached_lookup_method(field_name, field_sig, Klass::normal); if (m != NULL && m->is_protected()) { if (!this_class->is_same_class_package(m->method_holder())) { return true; @@ -2306,7 +2309,7 @@ ref_class_type.name(), CHECK_VERIFY(this)); Method* m = InstanceKlass::cast(ref_klass)->uncached_lookup_method( vmSymbols::object_initializer_name(), - cp->signature_ref_at(bcs->get_index_u2())); + cp->signature_ref_at(bcs->get_index_u2()), Klass::normal); // Do nothing if method is not found. Let resolution detect the error. if (m != NULL) { instanceKlassHandle mh(THREAD, m->method_holder()); @@ -2391,11 +2394,12 @@ if (opcode == Bytecodes::_invokedynamic) { if (!EnableInvokeDynamic || _klass->major_version() < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { - class_format_error( - (!EnableInvokeDynamic ? - "invokedynamic instructions not enabled in this JVM" : - "invokedynamic instructions not supported by this class file version"), - _klass->external_name()); + if (!EnableInvokeDynamic) { + class_format_error("invokedynamic instructions not enabled in this JVM"); + } else { + class_format_error("invokedynamic instructions not supported by this class file version (%d), class %s", + _klass->major_version(), _klass->external_name()); + } return; } } else { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/verifier.hpp --- a/src/share/vm/classfile/verifier.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/verifier.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -378,15 +378,15 @@ bool has_error() const { return result() != NULL; } char* exception_message() { stringStream ss; - ss.print(_message); + ss.print("%s", _message); _error_context.details(&ss, _method()); return ss.as_string(); } // Called when verify or class format errors are encountered. // May throw an exception based upon the mode. - void verify_error(ErrorContext ctx, const char* fmt, ...); - void class_format_error(const char* fmt, ...); + void verify_error(ErrorContext ctx, const char* fmt, ...) ATTRIBUTE_PRINTF(3, 4); + void class_format_error(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3); Klass* load_class(Symbol* name, TRAPS); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/vmSymbols.cpp --- a/src/share/vm/classfile/vmSymbols.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/vmSymbols.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -533,7 +533,7 @@ xtty->begin_elem("intrinsic_misdeclared actual='%s' declared='%s'", actual_name, declared_name); xtty->method(mh); - xtty->end_elem(""); + xtty->end_elem("%s", ""); } if (PrintMiscellaneous && (WizardMode || Verbose)) { tty->print_cr("*** misidentified method; %s(%d) should be %s(%d):", diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/classfile/vmSymbols.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -726,9 +726,9 @@ do_intrinsic(_addExactI, java_lang_Math, addExact_name, int2_int_signature, F_S) \ do_intrinsic(_addExactL, java_lang_Math, addExact_name, long2_long_signature, F_S) \ do_intrinsic(_decrementExactI, java_lang_Math, decrementExact_name, int_int_signature, F_S) \ - do_intrinsic(_decrementExactL, java_lang_Math, decrementExact_name, long2_long_signature, F_S) \ + do_intrinsic(_decrementExactL, java_lang_Math, decrementExact_name, long_long_signature, F_S) \ do_intrinsic(_incrementExactI, java_lang_Math, incrementExact_name, int_int_signature, F_S) \ - do_intrinsic(_incrementExactL, java_lang_Math, incrementExact_name, long2_long_signature, F_S) \ + do_intrinsic(_incrementExactL, java_lang_Math, incrementExact_name, long_long_signature, F_S) \ do_intrinsic(_multiplyExactI, java_lang_Math, multiplyExact_name, int2_int_signature, F_S) \ do_intrinsic(_multiplyExactL, java_lang_Math, multiplyExact_name, long2_long_signature, F_S) \ do_intrinsic(_negateExactI, java_lang_Math, negateExact_name, int_int_signature, F_S) \ @@ -846,7 +846,7 @@ /* java/lang/ref/Reference */ \ do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ \ - /* support for com.sum.crypto.provider.AESCrypt and some of its callers */ \ + /* support for com.sun.crypto.provider.AESCrypt and some of its callers */ \ do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AESCrypt") \ do_intrinsic(_aescrypt_encryptBlock, com_sun_crypto_provider_aescrypt, encryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ do_intrinsic(_aescrypt_decryptBlock, com_sun_crypto_provider_aescrypt, decryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ @@ -859,7 +859,7 @@ do_intrinsic(_cipherBlockChaining_decryptAESCrypt, com_sun_crypto_provider_cipherBlockChaining, decrypt_name, byteArray_int_int_byteArray_int_signature, F_R) \ do_name( encrypt_name, "encrypt") \ do_name( decrypt_name, "decrypt") \ - do_signature(byteArray_int_int_byteArray_int_signature, "([BII[BI)V") \ + do_signature(byteArray_int_int_byteArray_int_signature, "([BII[BI)I") \ \ /* support for java.util.zip */ \ do_class(java_util_zip_CRC32, "java/util/zip/CRC32") \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/codeBlob.cpp --- a/src/share/vm/code/codeBlob.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/codeBlob.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -530,7 +530,7 @@ } void CodeBlob::print_on(outputStream* st) const { - st->print_cr("[CodeBlob (" INTPTR_FORMAT ")]", this); + st->print_cr("[CodeBlob (" INTPTR_FORMAT ")]", p2i(this)); st->print_cr("Framesize: %d", _frame_size); } @@ -548,7 +548,7 @@ } void BufferBlob::print_value_on(outputStream* st) const { - st->print_cr("BufferBlob (" INTPTR_FORMAT ") used for %s", this, name()); + st->print_cr("BufferBlob (" INTPTR_FORMAT ") used for %s", p2i(this), name()); } void RuntimeStub::verify() { @@ -558,13 +558,13 @@ void RuntimeStub::print_on(outputStream* st) const { ttyLocker ttyl; CodeBlob::print_on(st); - st->print("Runtime Stub (" INTPTR_FORMAT "): ", this); - st->print_cr(name()); + st->print("Runtime Stub (" INTPTR_FORMAT "): ", p2i(this)); + st->print_cr("%s", name()); Disassembler::decode((CodeBlob*)this, st); } void RuntimeStub::print_value_on(outputStream* st) const { - st->print("RuntimeStub (" INTPTR_FORMAT "): ", this); st->print(name()); + st->print("RuntimeStub (" INTPTR_FORMAT "): ", p2i(this)); st->print("%s", name()); } void SingletonBlob::verify() { @@ -574,12 +574,12 @@ void SingletonBlob::print_on(outputStream* st) const { ttyLocker ttyl; CodeBlob::print_on(st); - st->print_cr(name()); + st->print_cr("%s", name()); Disassembler::decode((CodeBlob*)this, st); } void SingletonBlob::print_value_on(outputStream* st) const { - st->print_cr(name()); + st->print_cr("%s", name()); } void DeoptimizationBlob::print_value_on(outputStream* st) const { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/codeCache.cpp --- a/src/share/vm/code/codeCache.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/codeCache.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -81,10 +81,10 @@ bool is_empty() { return count == 0; } void print(const char* title) { - tty->print_cr(" #%d %s = %dK (hdr %d%%, loc %d%%, code %d%%, stub %d%%, [oops %d%%, data %d%%, pcs %d%%])", + tty->print_cr(" #%d %s = %dK (hdr %d%%, loc %d%%, code %d%%, stub %d%%, [oops %d%%, metadata %d%%, data %d%%, pcs %d%%])", count, title, - total() / K, + (int)(total() / K), header_size * 100 / total_size, relocation_size * 100 / total_size, code_size * 100 / total_size, @@ -191,7 +191,7 @@ } if (PrintCodeCacheExtension) { ResourceMark rm; - tty->print_cr("code cache extended to [" INTPTR_FORMAT ", " INTPTR_FORMAT "] (%d bytes)", + tty->print_cr("code cache extended to [" INTPTR_FORMAT ", " INTPTR_FORMAT "] (" SSIZE_FORMAT " bytes)", (intptr_t)_heap->low_boundary(), (intptr_t)_heap->high(), (address)_heap->high() - (address)_heap->low_boundary()); } @@ -496,7 +496,7 @@ if (CompiledIC::is_icholder_call_site(iter.virtual_call_reloc())) { CompiledIC *ic = CompiledIC_at(iter.reloc()); if (TraceCompiledIC) { - tty->print("noticed icholder " INTPTR_FORMAT " ", ic->cached_icholder()); + tty->print("noticed icholder " INTPTR_FORMAT " ", p2i(ic->cached_icholder())); ic->print(); } assert(ic->cached_icholder() != NULL, "must be non-NULL"); @@ -775,7 +775,7 @@ if (PrintCodeCache2) { // Need to add a new flag ResourceMark rm; if (size == 0) size = cb->size(); - tty->print_cr("CodeCache %s: addr: " INTPTR_FORMAT ", size: 0x%x", event, cb, size); + tty->print_cr("CodeCache %s: addr: " INTPTR_FORMAT ", size: 0x%x", event, p2i(cb), size); } } @@ -900,7 +900,7 @@ tty->print_cr("CodeCache:"); - tty->print_cr("nmethod dependency checking time %f", dependentCheckTime.seconds(), + tty->print_cr("nmethod dependency checking time %f, per dependent %f", dependentCheckTime.seconds(), dependentCheckTime.seconds() / dependentCheckCount); if (!live.is_empty()) { @@ -947,9 +947,9 @@ if (detailed) { st->print_cr(" bounds [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT "]", - _heap->low_boundary(), - _heap->high(), - _heap->high_boundary()); + p2i(_heap->low_boundary()), + p2i(_heap->high()), + p2i(_heap->high_boundary())); st->print_cr(" total_blobs=" UINT32_FORMAT " nmethods=" UINT32_FORMAT " adapters=" UINT32_FORMAT, nof_blobs(), nof_nmethods(), nof_adapters()); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/compiledIC.cpp --- a/src/share/vm/code/compiledIC.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/compiledIC.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -88,9 +88,9 @@ if (TraceCompiledIC) { tty->print(" "); print_compiled_ic(); - tty->print(" changing destination to " INTPTR_FORMAT, entry_point); + tty->print(" changing destination to " INTPTR_FORMAT, p2i(entry_point)); if (!is_optimized()) { - tty->print(" changing cached %s to " INTPTR_FORMAT, is_icholder ? "icholder" : "metadata", (address)cache); + tty->print(" changing cached %s to " INTPTR_FORMAT, is_icholder ? "icholder" : "metadata", p2i((address)cache)); } if (is_icstub) { tty->print(" (icstub)"); @@ -195,7 +195,7 @@ if (TraceICs) { ResourceMark rm; tty->print_cr ("IC@" INTPTR_FORMAT ": to megamorphic %s entry: " INTPTR_FORMAT, - instruction_address(), call_info->selected_method()->print_value_string(), entry); + p2i(instruction_address()), call_info->selected_method()->print_value_string(), p2i(entry)); } // We can't check this anymore. With lazy deopt we could have already @@ -274,7 +274,7 @@ void CompiledIC::set_to_clean() { assert(SafepointSynchronize::is_at_safepoint() || CompiledIC_lock->is_locked() , "MT-unsafe call"); if (TraceInlineCacheClearing || TraceICs) { - tty->print_cr("IC@" INTPTR_FORMAT ": set to clean", instruction_address()); + tty->print_cr("IC@" INTPTR_FORMAT ": set to clean", p2i(instruction_address())); print(); } @@ -356,7 +356,7 @@ if (TraceICs) { ResourceMark rm(thread); tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter: %s", - instruction_address(), + p2i(instruction_address()), method->print_value_string()); } } else { @@ -364,7 +364,7 @@ InlineCacheBuffer::create_transition_stub(this, info.claim_cached_icholder(), info.entry()); if (TraceICs) { ResourceMark rm(thread); - tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter via icholder ", instruction_address()); + tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter via icholder ", p2i(instruction_address())); } } } else { @@ -394,7 +394,7 @@ ResourceMark rm(thread); assert(info.cached_metadata() == NULL || info.cached_metadata()->is_klass(), "must be"); tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to compiled (rcvr klass) %s: %s", - instruction_address(), + p2i(instruction_address()), ((Klass*)info.cached_metadata())->print_value_string(), (safe) ? "" : "via stub"); } @@ -532,8 +532,8 @@ if (TraceICs) { ResourceMark rm; tty->print_cr("CompiledStaticCall@" INTPTR_FORMAT ": set_to_compiled " INTPTR_FORMAT, - instruction_address(), - info.entry()); + p2i(instruction_address()), + p2i(info.entry())); } // Call to compiled code assert (CodeCache::contains(info.entry()), "wrong entry point"); @@ -602,11 +602,11 @@ void CompiledIC::print_compiled_ic() { tty->print("Inline cache at " INTPTR_FORMAT ", calling %s " INTPTR_FORMAT " cached_value " INTPTR_FORMAT, - instruction_address(), is_call_to_interpreted() ? "interpreted " : "", ic_destination(), is_optimized() ? NULL : cached_value()); + p2i(instruction_address()), is_call_to_interpreted() ? "interpreted " : "", p2i(ic_destination()), p2i(is_optimized() ? NULL : cached_value())); } void CompiledStaticCall::print() { - tty->print("static call at " INTPTR_FORMAT " -> ", instruction_address()); + tty->print("static call at " INTPTR_FORMAT " -> ", p2i(instruction_address())); if (is_clean()) { tty->print("clean"); } else if (is_call_to_compiled()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/compressedStream.cpp --- a/src/share/vm/code/compressedStream.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/compressedStream.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -212,6 +212,8 @@ return h ^ l; } +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_IGNORED // Someone needs to deal with this. void test_compressed_stream(int trace) { CompressedWriteStream bytes(stretch_limit * 100); jint n; @@ -275,6 +277,7 @@ guarantee(length == length2, "bad length"); guarantee(fails == 0, "test failures"); } +PRAGMA_DIAG_POP #if defined(_MSC_VER) &&_MSC_VER >=1400 && !defined(_WIN64) #pragma warning(default: 4748) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/debugInfo.cpp --- a/src/share/vm/code/debugInfo.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/debugInfo.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -28,7 +28,9 @@ #include "code/nmethod.hpp" #include "runtime/handles.inline.hpp" -// Comstructors +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +// Constructors DebugInfoWriteStream::DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size) : CompressedWriteStream(initial_size) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/debugInfo.hpp --- a/src/share/vm/code/debugInfo.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/debugInfo.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -273,8 +273,8 @@ } Method* read_method() { Method* o = (Method*)(code()->metadata_at(read_int())); - assert(o == NULL || - o->is_metaspace_object(), "meta data only"); + // is_metadata() is a faster check than is_metaspace_object() + assert(o == NULL || o->is_metadata(), "meta data only"); return o; } ScopeValue* read_object_value(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/dependencies.cpp --- a/src/share/vm/code/dependencies.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/dependencies.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -867,8 +867,6 @@ } else { o = _deps->oop_recorder()->metadata_at(i); } - assert(o == NULL || o->is_metaspace_object(), - err_msg("Should be metadata " PTR_FORMAT, o)); return o; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/exceptionHandlerTable.cpp --- a/src/share/vm/code/exceptionHandlerTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/exceptionHandlerTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -27,6 +27,8 @@ #include "code/nmethod.hpp" #include "memory/allocation.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void ExceptionHandlerTable::add_entry(HandlerTableEntry entry) { _nesting.check(); if (_length >= _size) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/icBuffer.cpp --- a/src/share/vm/code/icBuffer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/icBuffer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -39,6 +39,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/stubRoutines.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC DEF_STUB_INTERFACE(ICStub); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/nmethod.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -49,6 +49,8 @@ #include "graal/graalJavaAccess.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef DTRACE_ENABLED // Only bother with this argument setup if dtrace is available @@ -543,6 +545,9 @@ _scavenge_root_link = NULL; _scavenge_root_state = 0; _compiler = NULL; +#if INCLUDE_RTM_OPT + _rtm_state = NoRTM; +#endif #ifdef GRAAL _graal_installed_code = NULL; _speculation_log = NULL; @@ -692,7 +697,7 @@ InstanceKlass::cast(klass)->add_dependent_nmethod(nm); } if (nm != NULL) note_java_nmethod(nm); - if (PrintAssembly) { + if (PrintAssembly || CompilerOracle::has_option_string(method, "PrintAssembly")) { Disassembler::decode(nm); } } @@ -842,7 +847,11 @@ _hotness_counter = NMethodSweeper::hotness_counter_reset_val(); code_buffer->copy_values_to(this); - debug_only(verify_scavenge_root_oops()); + if (ScavengeRootsInCode && detect_scavenge_root_oops()) { + CodeCache::add_scavenge_root_nmethod(this); + Universe::heap()->register_nmethod(this); + } + DEBUG_ONLY(verify_scavenge_root_oops();) CodeCache::commit(this); } @@ -1044,13 +1053,13 @@ LOG_OFFSET(xtty, consts); LOG_OFFSET(xtty, insts); LOG_OFFSET(xtty, stub); - LOG_OFFSET(xtty, oops); - LOG_OFFSET(xtty, metadata); LOG_OFFSET(xtty, scopes_data); LOG_OFFSET(xtty, scopes_pcs); LOG_OFFSET(xtty, dependencies); LOG_OFFSET(xtty, handler_table); LOG_OFFSET(xtty, nul_chk_table); + LOG_OFFSET(xtty, oops); + LOG_OFFSET(xtty, metadata); xtty->method(method()); xtty->stamp(); @@ -1447,8 +1456,8 @@ // The caller can be calling the method statically or through an inline // cache call. if (!is_osr_method() && !is_not_entrant()) { - address stub = SharedRuntime::get_handle_wrong_method_stub(); - NativeJump::patch_verified_entry(entry_point(), verified_entry_point(), stub); + NativeJump::patch_verified_entry(entry_point(), verified_entry_point(), + SharedRuntime::get_handle_wrong_method_stub()); } if (is_in_use()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/nmethod.hpp --- a/src/share/vm/code/nmethod.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/nmethod.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -200,6 +200,12 @@ jbyte _scavenge_root_state; +#if INCLUDE_RTM_OPT + // RTM state at compile time. Used during deoptimization to decide + // whether to restart collecting RTM locking abort statistic again. + RTMState _rtm_state; +#endif + // Nmethod Flushing lock. If non-zero, then the nmethod is not removed // and is not made into a zombie. However, once the nmethod is made into // a zombie, it will be locked one final time if CompiledMethodUnload @@ -432,6 +438,12 @@ bool is_zombie() const { return _state == zombie; } bool is_unloaded() const { return _state == unloaded; } +#if INCLUDE_RTM_OPT + // rtm state accessing and manipulating + RTMState rtm_state() const { return _rtm_state; } + void set_rtm_state(RTMState state) { _rtm_state = state; } +#endif + // Make the nmethod non entrant. The nmethod will continue to be // alive. It is used when an uncommon trap happens. Returns true // if this thread changed the state of the nmethod or false if diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/pcDesc.cpp --- a/src/share/vm/code/pcDesc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/pcDesc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -29,6 +29,8 @@ #include "code/scopeDesc.hpp" #include "memory/resourceArea.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + PcDesc::PcDesc(int pc_offset, int scope_decode_offset, int obj_decode_offset) { _pc_offset = pc_offset; _scope_decode_offset = scope_decode_offset; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/relocInfo.cpp --- a/src/share/vm/code/relocInfo.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/relocInfo.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -31,6 +31,7 @@ #include "runtime/stubCodeGenerator.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC const RelocationHolder RelocationHolder::none; // its type is relocInfo::none @@ -582,6 +583,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(); @@ -811,6 +824,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. @@ -975,6 +1007,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/relocInfo.hpp --- a/src/share/vm/code/relocInfo.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/relocInfo.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -265,8 +265,8 @@ poll_type = 10, // polling instruction for safepoints poll_return_type = 11, // polling instruction for safepoints at return metadata_type = 12, // metadata that used to be oops - yet_unused_type_1 = 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 }; @@ -306,6 +306,7 @@ visitor(poll) \ visitor(poll_return) \ visitor(section_word) \ + visitor(trampoline_stub) \ public: @@ -464,7 +465,7 @@ return relocInfo(relocInfo::none, relocInfo::offset_limit() - relocInfo::offset_unit); } -inline relocInfo prefix_relocInfo(int datalen) { +inline relocInfo prefix_relocInfo(int datalen = 0) { assert(relocInfo::fits_into_immediate(datalen), "datalen in limits"); return relocInfo(relocInfo::data_prefix_tag, relocInfo::RAW_BITS, relocInfo::datalen_tag | datalen); } @@ -1155,6 +1156,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/scopeDesc.cpp --- a/src/share/vm/code/scopeDesc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/scopeDesc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -30,6 +30,7 @@ #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool rethrow_exception, bool return_oop) { _code = code; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/stubs.hpp --- a/src/share/vm/code/stubs.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/stubs.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/vmreg.hpp --- a/src/share/vm/code/vmreg.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/vmreg.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -47,8 +47,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 @@ -70,7 +73,7 @@ // friend class Location; private: enum { - BAD = -1 + BAD_REG = -1 }; @@ -83,7 +86,7 @@ public: - static VMReg as_VMReg(int val, bool bad_ok = false) { assert(val > BAD || bad_ok, "invalid"); return (VMReg) (intptr_t) val; } + static VMReg as_VMReg(int val, bool bad_ok = false) { assert(val > BAD_REG || bad_ok, "invalid"); return (VMReg) (intptr_t) val; } const char* name() { if (is_reg()) { @@ -95,8 +98,8 @@ return "STACKED REG"; } } - static VMReg Bad() { return (VMReg) (intptr_t) BAD; } - bool is_valid() const { return ((intptr_t) this) != BAD; } + static VMReg Bad() { return (VMReg) (intptr_t) BAD_REG; } + bool is_valid() const { return ((intptr_t) this) != BAD_REG; } bool is_stack() const { return (intptr_t) this >= (intptr_t) stack0; } bool is_reg() const { return is_valid() && !is_stack(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/vtableStubs.cpp --- a/src/share/vm/code/vtableStubs.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/vtableStubs.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -39,6 +39,8 @@ #include "opto/matcher.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // ----------------------------------------------------------------------------------------- // Implementation of VtableStub @@ -55,6 +57,9 @@ const int chunk_factor = 32; if (_chunk == NULL || _chunk + real_size > _chunk_end) { const int bytes = chunk_factor * real_size + pd_code_alignment(); + + // There is a dependency on the name of the blob in src/share/vm/prims/jvmtiCodeBlobEvents.cpp + // If changing the name, update the other file accordingly. BufferBlob* blob = BufferBlob::create("vtable chunks", bytes); if (blob == NULL) { return NULL; @@ -62,12 +67,6 @@ _chunk = blob->content_begin(); _chunk_end = _chunk + bytes; Forte::register_stub("vtable stub", _chunk, _chunk_end); - // Notify JVMTI about this stub. The event will be recorded by the enclosing - // JvmtiDynamicCodeEventCollector and posted when this thread has released - // all locks. - if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated_while_holding_locks("vtable stub", _chunk, _chunk_end); - } align_chunk(); } assert(_chunk + real_size <= _chunk_end, "bad allocation"); @@ -130,6 +129,13 @@ is_vtable_stub? "vtbl": "itbl", vtable_index, VtableStub::receiver_location()); Disassembler::decode(s->code_begin(), s->code_end()); } + // Notify JVMTI about this stub. The event will be recorded by the enclosing + // JvmtiDynamicCodeEventCollector and posted when this thread has released + // all locks. + if (JvmtiExport::should_post_dynamic_code_generated()) { + JvmtiExport::post_dynamic_code_generated_while_holding_locks(is_vtable_stub? "vtable stub": "itable stub", + s->code_begin(), s->code_end()); + } } return s->entry_point(); } @@ -195,6 +201,14 @@ VtableStubs::initialize(); } +void VtableStubs::vtable_stub_do(void f(VtableStub*)) { + for (int i = 0; i < N; i++) { + for (VtableStub* s = _table[i]; s != NULL; s = s->next()) { + f(s); + } + } +} + //----------------------------------------------------------------------------------------------------- // Non-product code diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/code/vtableStubs.hpp --- a/src/share/vm/code/vtableStubs.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/code/vtableStubs.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -131,6 +131,7 @@ static VtableStub* stub_containing(address pc); // stub containing pc or NULL static int number_of_vtable_stubs() { return _number_of_vtable_stubs; } static void initialize(); + static void vtable_stub_do(void f(VtableStub*)); // iterates over all vtable stubs }; #endif // SHARE_VM_CODE_VTABLESTUBS_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/compileBroker.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -139,9 +139,9 @@ // The installed compiler(s) AbstractCompiler* CompileBroker::_compilers[2]; -// These counters are used for assigning id's to each compilation -uint CompileBroker::_compilation_id = 0; -uint CompileBroker::_osr_compilation_id = 0; +// These counters are used to assign an unique ID to each compilation. +volatile jint CompileBroker::_compilation_id = 0; +volatile jint CompileBroker::_osr_compilation_id = 0; // Debugging information int CompileBroker::_last_compile_type = no_compile; @@ -213,7 +213,7 @@ void log_nmethod(JavaThread* thread, nmethod* nm) { log(thread, "nmethod %d%s " INTPTR_FORMAT " code ["INTPTR_FORMAT ", " INTPTR_FORMAT "]", nm->compile_id(), nm->is_osr_method() ? "%" : "", - nm, nm->code_begin(), nm->code_end()); + p2i(nm), p2i(nm->code_begin()), p2i(nm->code_end())); } void log_failure(JavaThread* thread, CompileTask* task, const char* reason, const char* retry_message) { @@ -1181,7 +1181,7 @@ // We now know that this compilation is not pending, complete, // or prohibited. Assign a compile_id to this compilation // and check to see if it is in our [Start..Stop) range. - uint compile_id = assign_compile_id(method, osr_bci); + int compile_id = assign_compile_id(method, osr_bci); if (compile_id == 0) { // The compilation falls outside the allowed range. return; @@ -1353,18 +1353,12 @@ // do the compilation if (method->is_native()) { if (!PreferInterpreterNativeStubs || method->is_method_handle_intrinsic()) { - // Acquire our lock. - int compile_id; - { - MutexLocker locker(MethodCompileQueue_lock, THREAD); - compile_id = assign_compile_id(method, standard_entry_bci); - } // To properly handle the appendix argument for out-of-line calls we are using a small trampoline that // pops off the appendix argument and jumps to the target (see gen_special_dispatch in SharedRuntime). // // Since normal compiled-to-compiled calls are not able to handle such a thing we MUST generate an adapter // in this case. If we can't generate one and use it we can not execute the out-of-line method handle calls. - (void) AdapterHandlerLibrary::create_native_wrapper(method, compile_id); + AdapterHandlerLibrary::create_native_wrapper(method); } else { return NULL; } @@ -1467,27 +1461,28 @@ return false; } - -// ------------------------------------------------------------------ -// CompileBroker::assign_compile_id -// -// Assign a serialized id number to this compilation request. If the -// number falls out of the allowed range, return a 0. OSR -// compilations may be numbered separately from regular compilations -// if certain debugging flags are used. -uint CompileBroker::assign_compile_id(methodHandle method, int osr_bci) { - assert(MethodCompileQueue_lock->owner() == Thread::current(), - "must hold the compilation queue lock"); +/** + * Generate serialized IDs for compilation requests. If certain debugging flags are used + * and the ID is not within the specified range, the method is not compiled and 0 is returned. + * The function also allows to generate separate compilation IDs for OSR compilations. + */ +int CompileBroker::assign_compile_id(methodHandle method, int osr_bci) { +#ifdef ASSERT bool is_osr = (osr_bci != standard_entry_bci); - uint id; - if (CICountOSR && is_osr) { - id = ++_osr_compilation_id; - if ((uint)CIStartOSR <= id && id < (uint)CIStopOSR) { + int id; + if (method->is_native()) { + assert(!is_osr, "can't be osr"); + // Adapters, native wrappers and method handle intrinsics + // should be generated always. + return Atomic::add(1, &_compilation_id); + } else if (CICountOSR && is_osr) { + id = Atomic::add(1, &_osr_compilation_id); + if (CIStartOSR <= id && id < CIStopOSR) { return id; } } else { - id = ++_compilation_id; - if ((uint)CIStart <= id && id < (uint)CIStop) { + id = Atomic::add(1, &_compilation_id); + if (CIStart <= id && id < CIStop) { return id; } } @@ -1495,6 +1490,11 @@ // Method was not in the appropriate compilation range. method->set_not_compilable_quietly(); return 0; +#else + // CICountOSR is a develop flag and set to 'false' by default. In a product built, + // only _compilation_id is incremented. + return Atomic::add(1, &_compilation_id); +#endif } @@ -1849,7 +1849,7 @@ if (xtty != NULL) { ttyLocker ttyl; // Record any per thread log files - xtty->elem("thread_logfile thread='%d' filename='%s'", thread_id, file_name); + xtty->elem("thread_logfile thread='" INTX_FORMAT "' filename='%s'", thread_id, file_name); } return; } @@ -1880,7 +1880,7 @@ if (_should_block) { #ifndef PRODUCT if (PrintCompilation && (Verbose || WizardMode)) - tty->print_cr("compiler thread " INTPTR_FORMAT " poll detects block request", Thread::current()); + tty->print_cr("compiler thread " INTPTR_FORMAT " poll detects block request", p2i(Thread::current())); #endif ThreadInVMfromNative tivfn(JavaThread::current()); } @@ -1897,7 +1897,7 @@ CodeCache::print_summary(&s, detailed); } ttyLocker ttyl; - tty->print(s.as_string()); + tty->print("%s", s.as_string()); } void CompileBroker::post_compile(CompilerThread* thread, CompileTask* task, EventCompilation& event, bool success, ciEnv* ci_env) { @@ -2125,7 +2125,7 @@ // Lock to prevent tearing ttyLocker ttyl; xtty->begin_elem("code_cache_full"); - xtty->print(s.as_string()); + xtty->print("%s", s.as_string()); xtty->stamp(); xtty->end_elem(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/compileBroker.hpp --- a/src/share/vm/compiler/compileBroker.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/compileBroker.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -247,6 +247,8 @@ // Compile type Information for print_last_compile() and CompilerCounters enum { no_compile, normal_compile, osr_compile, native_compile }; + static int assign_compile_id (methodHandle method, int osr_bci); + private: static bool _initialized; @@ -259,9 +261,8 @@ static AbstractCompiler* _compilers[2]; // These counters are used for assigning id's to each compilation - static uint _compilation_id; - static uint _osr_compilation_id; - static uint _native_compilation_id; + static volatile jint _compilation_id; + static volatile jint _osr_compilation_id; static int _last_compile_type; static int _last_compile_level; @@ -322,7 +323,6 @@ static void init_compiler_threads(int c1_compiler_count, int c2_compiler_count); static bool compilation_is_complete (methodHandle method, int osr_bci, int comp_level); static bool compilation_is_prohibited(methodHandle method, int osr_bci, int comp_level); - static uint assign_compile_id (methodHandle method, int osr_bci); static bool is_compile_blocking (methodHandle method, int osr_bci); static void preload_classes (methodHandle method, TRAPS); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/compileLog.cpp --- a/src/share/vm/compiler/compileLog.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/compileLog.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -294,7 +294,7 @@ // Print about successful method inlining. void CompileLog::inline_success(const char* reason) { begin_elem("inline_success reason='"); - text(reason); + text("%s", reason); end_elem("'"); } @@ -304,7 +304,7 @@ // Print about failed method inlining. void CompileLog::inline_fail(const char* reason) { begin_elem("inline_fail reason='"); - text(reason); + text("%s", reason); end_elem("'"); } @@ -330,5 +330,5 @@ void CompileLog::code_cache_state() { begin_elem("code_cache"); CodeCache::log_state(this); - end_elem(""); + end_elem("%s", ""); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/compileLog.hpp --- a/src/share/vm/compiler/compileLog.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/compileLog.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -54,7 +54,7 @@ static CompileLog* _first; // head of static chain - void va_tag(bool push, const char* format, va_list ap); + void va_tag(bool push, const char* format, va_list ap) ATTRIBUTE_PRINTF(3, 0); public: CompileLog(const char* file_name, FILE* fp, intx thread_id); @@ -68,7 +68,7 @@ // or reset, context string will be silently ignored stringStream* context() { return &_context; } void clear_context() { context()->reset(); } - void set_context(const char* format, ...); + void set_context(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); void name(ciSymbol* s); // name='s' void name(Symbol* s) { xmlStream::name(s); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/compilerOracle.cpp --- a/src/share/vm/compiler/compilerOracle.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/compilerOracle.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -539,7 +539,7 @@ tty->print_cr("CompilerOracle: unrecognized line"); tty->print_cr(" \"%s\"", original_line); if (error_msg != NULL) { - tty->print_cr(error_msg); + tty->print_cr("%s", error_msg); } } } @@ -661,7 +661,7 @@ char method_sep = have_colon ? ':' : '.'; if (Verbose) { - tty->print_cr(line); + tty->print_cr("%s", line); } ResourceMark rm; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/disassembler.cpp --- a/src/share/vm/compiler/disassembler.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/disassembler.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -51,6 +51,8 @@ #include "shark/sharkEntry.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void* Disassembler::_library = NULL; bool Disassembler::_tried_to_load_library = false; @@ -411,6 +413,7 @@ return env->handle_event(event, (address) arg); } +ATTRIBUTE_PRINTF(2, 3) static int printf_to_env(void* env_pv, const char* format, ...) { decode_env* env = (decode_env*) env_pv; outputStream* st = env->output(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/disassembler.hpp --- a/src/share/vm/compiler/disassembler.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/disassembler.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/methodLiveness.cpp --- a/src/share/vm/compiler/methodLiveness.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/methodLiveness.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -32,6 +32,8 @@ #include "memory/allocation.inline.hpp" #include "utilities/bitMap.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // The MethodLiveness class performs a simple liveness analysis on a method // in order to decide which locals are live (that is, will be used again) at // a particular bytecode index (bci). diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/compiler/oopMap.cpp --- a/src/share/vm/compiler/oopMap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/compiler/oopMap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -640,8 +640,8 @@ tty->print_cr( "Add derived pointer@" INTPTR_FORMAT " - Derived: " INTPTR_FORMAT - " Base: " INTPTR_FORMAT " (@" INTPTR_FORMAT ") (Offset: %d)", - derived_loc, (address)*derived_loc, (address)*base_loc, base_loc, offset + " Base: " INTPTR_FORMAT " (@" INTPTR_FORMAT ") (Offset: " INTX_FORMAT ")", + p2i(derived_loc), p2i((address)*derived_loc), p2i((address)*base_loc), p2i(base_loc), offset ); } // Set derived oop location to point to base. @@ -668,8 +668,8 @@ if (TraceDerivedPointers) { tty->print_cr("Updating derived pointer@" INTPTR_FORMAT - " - Derived: " INTPTR_FORMAT " Base: " INTPTR_FORMAT " (Offset: %d)", - derived_loc, (address)*derived_loc, (address)base, offset); + " - Derived: " INTPTR_FORMAT " Base: " INTPTR_FORMAT " (Offset: " INTX_FORMAT ")", + p2i(derived_loc), p2i((address)*derived_loc), p2i((address)base), offset); } // Delete entry diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -122,7 +122,7 @@ template void AdaptiveFreeList::return_chunk_at_tail(Chunk* chunk) { - return_chunk_at_tail(chunk, true); + AdaptiveFreeList::return_chunk_at_tail(chunk, true); } template @@ -157,7 +157,7 @@ " split_deaths(" SIZE_FORMAT ")" " coal_deaths(" SIZE_FORMAT ")" " + count(" SSIZE_FORMAT ")", - this, size(), _allocation_stats.prev_sweep(), _allocation_stats.split_births(), + p2i(this), size(), _allocation_stats.prev_sweep(), _allocation_stats.split_births(), _allocation_stats.split_births(), _allocation_stats.split_deaths(), _allocation_stats.coal_deaths(), count())); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -427,7 +427,7 @@ void LinearAllocBlock::print_on(outputStream* st) const { st->print_cr(" LinearAllocBlock: ptr = " PTR_FORMAT ", word_size = " SIZE_FORMAT ", refillsize = " SIZE_FORMAT ", allocation_size_limit = " SIZE_FORMAT, - _ptr, _word_size, _refillSize, _allocation_size_limit); + p2i(_ptr), _word_size, _refillSize, _allocation_size_limit); } void CompactibleFreeListSpace::print_on(outputStream* st) const { @@ -458,7 +458,7 @@ for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL; fc = fc->next()) { gclog_or_tty->print_cr("\t[" PTR_FORMAT "," PTR_FORMAT ") %s", - fc, (HeapWord*)fc + i, + p2i(fc), p2i((HeapWord*)fc + i), fc->cantCoalesce() ? "\t CC" : ""); } } @@ -502,7 +502,7 @@ if (_sp->block_is_obj(addr)) { const bool dead = _post_remark && !_live_bit_map->isMarked(addr); _st->print_cr(PTR_FORMAT ": %s object of size " SIZE_FORMAT "%s", - addr, + p2i(addr), dead ? "dead" : "live", sz, (!dead && CMSPrintObjectsInDump) ? ":" : "."); @@ -512,7 +512,7 @@ } } else { // free block _st->print_cr(PTR_FORMAT ": free block of size " SIZE_FORMAT "%s", - addr, sz, CMSPrintChunksInDump ? ":" : "."); + p2i(addr), sz, CMSPrintChunksInDump ? ":" : "."); if (CMSPrintChunksInDump) { ((FreeChunk*)addr)->print_on(_st); _st->print_cr("--------------------------------------"); @@ -564,11 +564,11 @@ "--------------------------------\n"); size_t total_size = totalSizeInIndexedFreeLists(); size_t free_blocks = numFreeBlocksInIndexedFreeLists(); - gclog_or_tty->print("Total Free Space: %d\n", total_size); - gclog_or_tty->print("Max Chunk Size: %d\n", maxChunkSizeInIndexedFreeLists()); - gclog_or_tty->print("Number of Blocks: %d\n", free_blocks); + gclog_or_tty->print("Total Free Space: " SIZE_FORMAT "\n", total_size); + gclog_or_tty->print("Max Chunk Size: " SIZE_FORMAT "\n", maxChunkSizeInIndexedFreeLists()); + gclog_or_tty->print("Number of Blocks: " SIZE_FORMAT "\n", free_blocks); if (free_blocks != 0) { - gclog_or_tty->print("Av. Block Size: %d\n", total_size/free_blocks); + gclog_or_tty->print("Av. Block Size: " SIZE_FORMAT "\n", total_size/free_blocks); } } @@ -997,6 +997,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)) { @@ -1010,6 +1017,12 @@ assert(k->is_klass(), "Should really be klass oop."); oop o = (oop)p; 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); res = adjustObjectSize(res); assert(res != 0, "Block size should not be 0"); @@ -1040,6 +1053,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"); @@ -1055,6 +1075,12 @@ assert(k->is_klass(), "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); res = adjustObjectSize(res); assert(res != 0, "Block size should not be 0"); @@ -1704,8 +1730,8 @@ _dictionary->return_chunk(chunk); #ifndef PRODUCT if (CMSCollector::abstract_state() != CMSCollector::Sweeping) { - TreeChunk* tc = TreeChunk::as_TreeChunk(chunk); - TreeList* tl = tc->list(); + TreeChunk >* tc = TreeChunk >::as_TreeChunk(chunk); + TreeList >* tl = tc->list(); tl->verify_stats(); } #endif // PRODUCT @@ -1985,7 +2011,7 @@ assert(ur.contains(urasm), err_msg(" Error at save_marks(): [" PTR_FORMAT "," PTR_FORMAT ")" " should contain [" PTR_FORMAT "," PTR_FORMAT ")", - ur.start(), ur.end(), urasm.start(), urasm.end())); + p2i(ur.start()), p2i(ur.end()), p2i(urasm.start()), p2i(urasm.end()))); #endif // inform allocator that promotions should be tracked. assert(_promoInfo.noPromotions(), "_promoInfo inconsistency"); @@ -2155,7 +2181,7 @@ for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { AdaptiveFreeList* fl = &_indexedFreeList[i]; if (PrintFLSStatistics > 1) { - gclog_or_tty->print("size[%d] : ", i); + gclog_or_tty->print("size[" SIZE_FORMAT "] : ", i); } fl->compute_desired(inter_sweep_current, inter_sweep_estimate, intra_sweep_estimate); fl->set_coal_desired((ssize_t)((double)fl->desired() * CMSSmallCoalSurplusPercent)); @@ -2208,7 +2234,7 @@ if (PrintFLSStatistics > 0) { HeapWord* largestAddr = (HeapWord*) dictionary()->find_largest_dict(); gclog_or_tty->print_cr("CMS: Large block " PTR_FORMAT, - largestAddr); + p2i(largestAddr)); } setFLSurplus(); setFLHints(); @@ -2357,8 +2383,8 @@ gclog_or_tty->print_cr( " Current: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n" " Previous: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n", - addr, res, was_obj ?"true":"false", was_live ?"true":"false", - _last_addr, _last_size, _last_was_obj?"true":"false", _last_was_live?"true":"false"); + p2i(addr), res, was_obj ?"true":"false", was_live ?"true":"false", + p2i(_last_addr), _last_size, _last_was_obj?"true":"false", _last_was_live?"true":"false"); _sp->print_on(gclog_or_tty); guarantee(false, "Seppuku!"); } @@ -2515,10 +2541,10 @@ #ifndef PRODUCT void CompactibleFreeListSpace::check_free_list_consistency() const { - assert((TreeChunk::min_size() <= IndexSetSize), + assert((TreeChunk >::min_size() <= IndexSetSize), "Some sizes can't be allocated without recourse to" " linear allocation buffers"); - assert((TreeChunk::min_size()*HeapWordSize == sizeof(TreeChunk)), + assert((TreeChunk >::min_size()*HeapWordSize == sizeof(TreeChunk >)), "else MIN_TREE_CHUNK_SIZE is wrong"); assert(IndexSetStart != 0, "IndexSetStart not initialized"); assert(IndexSetStride != 0, "IndexSetStride not initialized"); @@ -2686,7 +2712,7 @@ _global_num_workers[i] = 0; _global_num_blocks[i] = 0; if (PrintOldPLAB) { - gclog_or_tty->print_cr("[%d]: %d", i, (size_t)_blocks_to_claim[i].average()); + gclog_or_tty->print_cr("[" SIZE_FORMAT "]: " SIZE_FORMAT, i, (size_t)_blocks_to_claim[i].average()); } } } @@ -2725,7 +2751,7 @@ } } if (PrintOldPLAB) { - gclog_or_tty->print_cr("%d[%d]: %d/%d/%d", + gclog_or_tty->print_cr("%d[" SIZE_FORMAT "]: " SIZE_FORMAT "/" SIZE_FORMAT "/" SIZE_FORMAT, tid, i, num_retire, _num_blocks[i], (size_t)_blocks_to_claim[i].average()); } // Reset stats for next round diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -404,7 +404,7 @@ if (CMSTraceSweeper) { gclog_or_tty->print_cr(">>>>> Saving sweep limit " PTR_FORMAT " for space [" PTR_FORMAT "," PTR_FORMAT ") <<<<<<", - _sweep_limit, bottom(), end()); + p2i(_sweep_limit), p2i(bottom()), p2i(end())); } } NOT_PRODUCT( diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -63,6 +63,8 @@ #include "services/memoryService.hpp" #include "services/runtimeService.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // statics CMSCollector* ConcurrentMarkSweepGeneration::_collector = NULL; bool CMSCollector::_full_gc_requested = false; @@ -308,8 +310,7 @@ _cmsGen->refs_discovery_is_mt(), // mt discovery (int) MAX2(ConcGCThreads, ParallelGCThreads), // mt discovery degree _cmsGen->refs_discovery_is_atomic(), // discovery is not atomic - &_is_alive_closure, // closure for liveness info - false); // next field updates do not need write barrier + &_is_alive_closure); // closure for liveness info // Initialize the _ref_processor field of CMSGen _cmsGen->set_ref_processor(_ref_processor); @@ -1181,7 +1182,7 @@ gclog_or_tty->print(" icms alloc limits: " PTR_FORMAT "," PTR_FORMAT " (" SIZE_FORMAT "%%," SIZE_FORMAT "%%) ", - _icms_start_limit, _icms_stop_limit, + p2i(_icms_start_limit), p2i(_icms_stop_limit), percent_of_space(eden, _icms_start_limit), percent_of_space(eden, _icms_stop_limit)); if (Verbose) { @@ -1209,7 +1210,7 @@ gclog_or_tty->print_cr(" start limit top=" PTR_FORMAT ", new limit=" PTR_FORMAT " (" SIZE_FORMAT "%%)", - top, _icms_stop_limit, + p2i(top), p2i(_icms_stop_limit), percent_of_space(space, _icms_stop_limit)); } ConcurrentMarkSweepThread::start_icms(); @@ -1226,7 +1227,7 @@ gclog_or_tty->print_cr(" +stop limit top=" PTR_FORMAT ", new limit=" PTR_FORMAT " (" SIZE_FORMAT "%%)", - top, space->end(), + p2i(top), p2i(space->end()), percent_of_space(space, space->end())); } ConcurrentMarkSweepThread::stop_icms(); @@ -1501,7 +1502,7 @@ if (PrintCMSInitiationStatistics && stats().valid()) { gclog_or_tty->print("CMSCollector shouldConcurrentCollect: "); gclog_or_tty->stamp(); - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); stats().print_on(gclog_or_tty); gclog_or_tty->print_cr("time_until_cms_gen_full %3.7f", stats().time_until_cms_gen_full()); @@ -2496,7 +2497,8 @@ } void CMSCollector::report_heap_summary(GCWhen::Type when) { - _gc_tracer_cm->report_gc_heap_summary(when, _last_heap_summary, _last_metaspace_summary); + _gc_tracer_cm->report_gc_heap_summary(when, _last_heap_summary); + _gc_tracer_cm->report_metaspace_summary(when, _last_metaspace_summary); } void CMSCollector::collect_in_foreground(bool clear_all_soft_refs, GCCause::Cause cause) { @@ -3576,7 +3578,7 @@ _collector->cmsGen()->short_name(), _phase, _collector->timerValue(), _wallclock.seconds()); if (_print_cr) { - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); } if (PrintCMSStatistics != 0) { gclog_or_tty->print_cr(" (CMS-concurrent-%s yielded %d times)", _phase, @@ -6362,7 +6364,9 @@ verify_overflow_empty(); if (should_unload_classes()) { - ClassLoaderDataGraph::purge(); + // Delay purge to the beginning of the next safepoint. Metaspace::contains + // requires that the virtual spaces are stable and not deleted. + ClassLoaderDataGraph::set_should_purge(true); } _intra_sweep_timer.stop(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -111,7 +111,7 @@ // From this time Thread::current() should be working. assert(this == Thread::current(), "just checking"); if (BindCMSThreadToCPU && !os::bind_to_processor(CPUForCMSThread)) { - warning("Couldn't bind CMS thread to processor %u", CPUForCMSThread); + warning("Couldn't bind CMS thread to processor " UINTX_FORMAT, CPUForCMSThread); } // Wait until Universe::is_fully_initialized() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -241,7 +241,7 @@ jio_snprintf(buf, sizeof(buf), " [%.3f: CMSThread %s] ", ts.seconds(), desc); buf[sizeof(buf) - 1] = '\0'; - gclog_or_tty->print(buf); + gclog_or_tty->print("%s", buf); } } @@ -273,7 +273,7 @@ inline void tick() { _ticks++; if (CMSLoopWarn && _ticks % _threshold == 0) { - warning("%s has looped %d times %s", _src, _ticks, _msg); + warning("%s has looped " INTX_FORMAT " times %s", _src, _ticks, _msg); } } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -27,6 +27,8 @@ #include "memory/freeBlockDictionary.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifndef PRODUCT #define baadbabeHeapWord badHeapWordVal diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -28,6 +28,8 @@ #include "oops/markOop.inline.hpp" #include "oops/oop.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ///////////////////////////////////////////////////////////////////////// //// PromotionInfo ///////////////////////////////////////////////////////////////////////// diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -34,6 +34,7 @@ #include "runtime/os.hpp" #include "utilities/dtrace.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC #ifndef USDT2 HS_DTRACE_PROBE_DECL(hs_private, cms__initmark__begin); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/bufferingOopClosure.hpp --- a/src/share/vm/gc_implementation/g1/bufferingOopClosure.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/bufferingOopClosure.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -98,116 +98,4 @@ _closure_app_seconds(0.0) { } }; -class BufferingOopsInGenClosure: public OopsInGenClosure { - BufferingOopClosure _boc; - OopsInGenClosure* _oc; - protected: - template inline void do_oop_work(T* p) { - assert(generation()->is_in_reserved((void*)p), "Must be in!"); - _boc.do_oop(p); - } - public: - BufferingOopsInGenClosure(OopsInGenClosure *oc) : - _boc(oc), _oc(oc) {} - - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } - - void done() { - _boc.done(); - } - - double closure_app_seconds () { - return _boc.closure_app_seconds(); - } - - void set_generation(Generation* gen) { - OopsInGenClosure::set_generation(gen); - _oc->set_generation(gen); - } - - void reset_generation() { - // Make sure we finish the current work with the current generation. - _boc.done(); - OopsInGenClosure::reset_generation(); - _oc->reset_generation(); - } - -}; - - -class BufferingOopsInHeapRegionClosure: public OopsInHeapRegionClosure { -private: - enum PrivateConstants { - BufferLength = 1024 - }; - - StarTask _buffer[BufferLength]; - StarTask* _buffer_top; - StarTask* _buffer_curr; - - HeapRegion* _hr_buffer[BufferLength]; - HeapRegion** _hr_curr; - - OopsInHeapRegionClosure* _oc; - double _closure_app_seconds; - - void process_buffer () { - - assert((_hr_curr - _hr_buffer) == (_buffer_curr - _buffer), - "the two lengths should be the same"); - - double start = os::elapsedTime(); - HeapRegion** hr_curr = _hr_buffer; - HeapRegion* hr_prev = NULL; - for (StarTask* curr = _buffer; curr < _buffer_curr; ++curr) { - HeapRegion* region = *hr_curr; - if (region != hr_prev) { - _oc->set_region(region); - hr_prev = region; - } - if (curr->is_narrow()) { - assert(UseCompressedOops, "Error"); - _oc->do_oop((narrowOop*)(*curr)); - } else { - _oc->do_oop((oop*)(*curr)); - } - ++hr_curr; - } - _buffer_curr = _buffer; - _hr_curr = _hr_buffer; - _closure_app_seconds += (os::elapsedTime() - start); - } - -public: - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop( oop* p) { do_oop_work(p); } - - template void do_oop_work(T* p) { - if (_buffer_curr == _buffer_top) { - assert(_hr_curr > _hr_buffer, "_hr_curr should be consistent with _buffer_curr"); - process_buffer(); - } - StarTask new_ref(p); - *_buffer_curr = new_ref; - ++_buffer_curr; - *_hr_curr = _from; - ++_hr_curr; - } - void done () { - if (_buffer_curr > _buffer) { - assert(_hr_curr > _hr_buffer, "_hr_curr should be consistent with _buffer_curr"); - process_buffer(); - } - } - double closure_app_seconds () { - return _closure_app_seconds; - } - BufferingOopsInHeapRegionClosure (OopsInHeapRegionClosure *oc) : - _oc(oc), - _buffer_curr(_buffer), _buffer_top(_buffer + BufferLength), - _hr_curr(_hr_buffer), - _closure_app_seconds(0.0) { } -}; - #endif // SHARE_VM_GC_IMPLEMENTATION_G1_BUFFERINGOOPCLOSURE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp --- a/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -57,10 +57,10 @@ _threads = NEW_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _n_threads, mtGC); - int worker_id_offset = (int)DirtyCardQueueSet::num_par_ids(); + uint worker_id_offset = DirtyCardQueueSet::num_par_ids(); ConcurrentG1RefineThread *next = NULL; - for (int i = _n_threads - 1; i >= 0; i--) { + for (uint i = _n_threads - 1; i != UINT_MAX; i--) { ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(this, next, worker_id_offset, i); assert(t != NULL, "Conc refine should have been created"); if (t->osthread() == NULL) { @@ -87,7 +87,7 @@ void ConcurrentG1Refine::stop() { if (_threads != NULL) { - for (int i = 0; i < _n_threads; i++) { + for (uint i = 0; i < _n_threads; i++) { _threads[i]->stop(); } } @@ -96,7 +96,7 @@ void ConcurrentG1Refine::reinitialize_threads() { reset_threshold_step(); if (_threads != NULL) { - for (int i = 0; i < _n_threads; i++) { + for (uint i = 0; i < _n_threads; i++) { _threads[i]->initialize(); } } @@ -104,7 +104,7 @@ ConcurrentG1Refine::~ConcurrentG1Refine() { if (_threads != NULL) { - for (int i = 0; i < _n_threads; i++) { + for (uint i = 0; i < _n_threads; i++) { delete _threads[i]; } FREE_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _threads, mtGC); @@ -113,7 +113,7 @@ void ConcurrentG1Refine::threads_do(ThreadClosure *tc) { if (_threads != NULL) { - for (int i = 0; i < _n_threads; i++) { + for (uint i = 0; i < _n_threads; i++) { tc->do_thread(_threads[i]); } } @@ -121,20 +121,20 @@ void ConcurrentG1Refine::worker_threads_do(ThreadClosure * tc) { if (_threads != NULL) { - for (int i = 0; i < worker_thread_num(); i++) { + for (uint i = 0; i < worker_thread_num(); i++) { tc->do_thread(_threads[i]); } } } -int ConcurrentG1Refine::thread_num() { - int n_threads = (G1ConcRefinementThreads > 0) ? G1ConcRefinementThreads +uint ConcurrentG1Refine::thread_num() { + uint n_threads = (G1ConcRefinementThreads > 0) ? G1ConcRefinementThreads : ParallelGCThreads; - return MAX2(n_threads, 1); + return MAX2(n_threads, 1); } void ConcurrentG1Refine::print_worker_threads_on(outputStream* st) const { - for (int i = 0; i < _n_threads; ++i) { + for (uint i = 0; i < _n_threads; ++i) { _threads[i]->print_on(st); st->cr(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp --- a/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -39,8 +39,8 @@ class ConcurrentG1Refine: public CHeapObj { ConcurrentG1RefineThread** _threads; - int _n_threads; - int _n_worker_threads; + uint _n_threads; + uint _n_worker_threads; /* * The value of the update buffer queue length falls into one of 3 zones: * green, yellow, red. If the value is in [0, green) nothing is @@ -88,7 +88,7 @@ // The RS sampling thread ConcurrentG1RefineThread * sampling_thread() const; - static int thread_num(); + static uint thread_num(); void print_worker_threads_on(outputStream* st) const; @@ -100,8 +100,8 @@ int yellow_zone() const { return _yellow_zone; } int red_zone() const { return _red_zone; } - int total_thread_num() const { return _n_threads; } - int worker_thread_num() const { return _n_worker_threads; } + uint total_thread_num() const { return _n_threads; } + uint worker_thread_num() const { return _n_worker_threads; } int thread_threshold_step() const { return _thread_threshold_step; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp --- a/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -33,7 +33,7 @@ ConcurrentG1RefineThread:: ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread *next, - int worker_id_offset, int worker_id) : + uint worker_id_offset, uint worker_id) : ConcurrentGCThread(), _worker_id_offset(worker_id_offset), _worker_id(worker_id), diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp --- a/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -38,8 +38,8 @@ double _vtime_start; // Initial virtual time. double _vtime_accum; // Initial virtual time. - int _worker_id; - int _worker_id_offset; + uint _worker_id; + uint _worker_id_offset; // The refinement threads collection is linked list. A predecessor can activate a successor // when the number of the rset update buffer crosses a certain threshold. A successor @@ -71,7 +71,7 @@ virtual void run(); // Constructor ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread* next, - int worker_id_offset, int worker_id); + uint worker_id_offset, uint worker_id); void initialize(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentMark.cpp --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -537,7 +537,7 @@ if (verbose_low()) { gclog_or_tty->print_cr("[global] init, heap start = "PTR_FORMAT", " - "heap end = "PTR_FORMAT, _heap_start, _heap_end); + "heap end = " INTPTR_FORMAT, p2i(_heap_start), p2i(_heap_end)); } if (!_markBitMap1.allocate(heap_rs)) { @@ -567,8 +567,8 @@ _root_regions.init(_g1h, this); if (ConcGCThreads > ParallelGCThreads) { - warning("Can't have more ConcGCThreads (" UINT32_FORMAT ") " - "than ParallelGCThreads (" UINT32_FORMAT ").", + warning("Can't have more ConcGCThreads (" UINTX_FORMAT ") " + "than ParallelGCThreads (" UINTX_FORMAT ").", ConcGCThreads, ParallelGCThreads); return; } @@ -651,7 +651,7 @@ if (!(mark_stack_size >= 1 && mark_stack_size <= MarkStackSizeMax)) { warning("Invalid value calculated for MarkStackSize (" UINTX_FORMAT "): " "must be between " UINTX_FORMAT " and " UINTX_FORMAT, - mark_stack_size, 1, MarkStackSizeMax); + mark_stack_size, (uintx) 1, MarkStackSizeMax); return; } FLAG_SET_ERGO(uintx, MarkStackSize, mark_stack_size); @@ -662,7 +662,7 @@ if (!(MarkStackSize >= 1 && MarkStackSize <= MarkStackSizeMax)) { warning("Invalid value specified for MarkStackSize (" UINTX_FORMAT "): " "must be between " UINTX_FORMAT " and " UINTX_FORMAT, - MarkStackSize, 1, MarkStackSizeMax); + MarkStackSize, (uintx) 1, MarkStackSizeMax); return; } } else if (FLAG_IS_CMDLINE(MarkStackSizeMax)) { @@ -819,9 +819,9 @@ // false before we start remark. At this point we should also be // in a STW phase. assert(!concurrent_marking_in_progress(), "invariant"); - assert(_finger == _heap_end, + assert(out_of_regions(), err_msg("only way to get here: _finger: "PTR_FORMAT", _heap_end: "PTR_FORMAT, - _finger, _heap_end)); + p2i(_finger), p2i(_heap_end))); update_g1_committed(true); } } @@ -978,7 +978,9 @@ if (concurrent()) { ConcurrentGCThread::stsLeave(); } - _first_overflow_barrier_sync.enter(); + + bool barrier_aborted = !_first_overflow_barrier_sync.enter(); + if (concurrent()) { ConcurrentGCThread::stsJoin(); } @@ -986,7 +988,17 @@ // more work if (verbose_low()) { - gclog_or_tty->print_cr("[%u] leaving first barrier", worker_id); + if (barrier_aborted) { + gclog_or_tty->print_cr("[%u] aborted first barrier", worker_id); + } else { + gclog_or_tty->print_cr("[%u] leaving first barrier", worker_id); + } + } + + if (barrier_aborted) { + // If the barrier aborted we ignore the overflow condition and + // just abort the whole marking phase as quickly as possible. + return; } // If we're executing the concurrent phase of marking, reset the marking @@ -1026,14 +1038,20 @@ if (concurrent()) { ConcurrentGCThread::stsLeave(); } - _second_overflow_barrier_sync.enter(); + + bool barrier_aborted = !_second_overflow_barrier_sync.enter(); + if (concurrent()) { ConcurrentGCThread::stsJoin(); } // at this point everything should be re-initialized and ready to go if (verbose_low()) { - gclog_or_tty->print_cr("[%u] leaving second barrier", worker_id); + if (barrier_aborted) { + gclog_or_tty->print_cr("[%u] aborted second barrier", worker_id); + } else { + gclog_or_tty->print_cr("[%u] leaving second barrier", worker_id); + } } } @@ -1422,7 +1440,7 @@ assert(start <= hr->end() && start <= ntams && ntams <= hr->end(), err_msg("Preconditions not met - " "start: "PTR_FORMAT", ntams: "PTR_FORMAT", end: "PTR_FORMAT, - start, ntams, hr->end())); + p2i(start), p2i(ntams), p2i(hr->end()))); // Find the first marked object at or after "start". start = _bm->getNextMarkedWordAddress(start, ntams); @@ -1607,7 +1625,7 @@ if (failures > 0 && _verbose) { gclog_or_tty->print_cr("Region " HR_FORMAT ", ntams: " PTR_FORMAT ", " "marked_bytes: calc/actual " SIZE_FORMAT "/" SIZE_FORMAT, - HR_FORMAT_PARAMS(hr), hr->next_top_at_mark_start(), + HR_FORMAT_PARAMS(hr), p2i(hr->next_top_at_mark_start()), _calc_cl.region_marked_bytes(), hr->next_marked_bytes()); } @@ -1619,7 +1637,6 @@ } }; - class G1ParVerifyFinalCountTask: public AbstractGangTask { protected: G1CollectedHeap* _g1h; @@ -1805,34 +1822,32 @@ class G1NoteEndOfConcMarkClosure : public HeapRegionClosure { G1CollectedHeap* _g1; - int _worker_num; size_t _max_live_bytes; uint _regions_claimed; size_t _freed_bytes; FreeRegionList* _local_cleanup_list; - OldRegionSet* _old_proxy_set; - HumongousRegionSet* _humongous_proxy_set; + HeapRegionSetCount _old_regions_removed; + HeapRegionSetCount _humongous_regions_removed; HRRSCleanupTask* _hrrs_cleanup_task; double _claimed_region_time; double _max_region_time; public: G1NoteEndOfConcMarkClosure(G1CollectedHeap* g1, - int worker_num, FreeRegionList* local_cleanup_list, - OldRegionSet* old_proxy_set, - HumongousRegionSet* humongous_proxy_set, HRRSCleanupTask* hrrs_cleanup_task) : - _g1(g1), _worker_num(worker_num), + _g1(g1), _max_live_bytes(0), _regions_claimed(0), _freed_bytes(0), _claimed_region_time(0.0), _max_region_time(0.0), _local_cleanup_list(local_cleanup_list), - _old_proxy_set(old_proxy_set), - _humongous_proxy_set(humongous_proxy_set), + _old_regions_removed(), + _humongous_regions_removed(), _hrrs_cleanup_task(hrrs_cleanup_task) { } size_t freed_bytes() { return _freed_bytes; } + const HeapRegionSetCount& old_regions_removed() { return _old_regions_removed; } + const HeapRegionSetCount& humongous_regions_removed() { return _humongous_regions_removed; } bool doHeapRegion(HeapRegion *hr) { if (hr->continuesHumongous()) { @@ -1845,13 +1860,22 @@ _regions_claimed++; hr->note_end_of_marking(); _max_live_bytes += hr->max_live_bytes(); - _g1->free_region_if_empty(hr, - &_freed_bytes, - _local_cleanup_list, - _old_proxy_set, - _humongous_proxy_set, - _hrrs_cleanup_task, - true /* par */); + + if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young()) { + _freed_bytes += hr->used(); + hr->set_containing_set(NULL); + if (hr->isHumongous()) { + assert(hr->startsHumongous(), "we should only see starts humongous"); + _humongous_regions_removed.increment(1u, hr->capacity()); + _g1->free_humongous_region(hr, _local_cleanup_list, true); + } else { + _old_regions_removed.increment(1u, hr->capacity()); + _g1->free_region(hr, _local_cleanup_list, true); + } + } else { + hr->rem_set()->do_cleanup_work(_hrrs_cleanup_task); + } + double region_time = (os::elapsedTime() - start); _claimed_region_time += region_time; if (region_time > _max_region_time) { @@ -1884,12 +1908,8 @@ void work(uint worker_id) { double start = os::elapsedTime(); FreeRegionList local_cleanup_list("Local Cleanup List"); - OldRegionSet old_proxy_set("Local Cleanup Old Proxy Set"); - HumongousRegionSet humongous_proxy_set("Local Cleanup Humongous Proxy Set"); HRRSCleanupTask hrrs_cleanup_task; - G1NoteEndOfConcMarkClosure g1_note_end(_g1h, worker_id, &local_cleanup_list, - &old_proxy_set, - &humongous_proxy_set, + G1NoteEndOfConcMarkClosure g1_note_end(_g1h, &local_cleanup_list, &hrrs_cleanup_task); if (G1CollectedHeap::use_parallel_gc_threads()) { _g1h->heap_region_par_iterate_chunked(&g1_note_end, worker_id, @@ -1901,13 +1921,10 @@ assert(g1_note_end.complete(), "Shouldn't have yielded!"); // Now update the lists - _g1h->update_sets_after_freeing_regions(g1_note_end.freed_bytes(), - NULL /* free_list */, - &old_proxy_set, - &humongous_proxy_set, - true /* par */); + _g1h->remove_from_old_sets(g1_note_end.old_regions_removed(), g1_note_end.humongous_regions_removed()); { MutexLockerEx x(ParGCRareEvent_lock, Mutex::_no_safepoint_check_flag); + _g1h->decrement_summary_bytes(g1_note_end.freed_bytes()); _max_live_bytes += g1_note_end.max_live_bytes(); _freed_bytes += g1_note_end.freed_bytes(); @@ -1921,14 +1938,14 @@ G1HRPrinter* hr_printer = _g1h->hr_printer(); if (hr_printer->is_active()) { - HeapRegionLinkedListIterator iter(&local_cleanup_list); + FreeRegionListIterator iter(&local_cleanup_list); while (iter.more_available()) { HeapRegion* hr = iter.get_next(); hr_printer->cleanup(hr); } } - _cleanup_list->add_as_tail(&local_cleanup_list); + _cleanup_list->add_ordered(&local_cleanup_list); assert(local_cleanup_list.is_empty(), "post-condition"); HeapRegionRemSet::finish_cleanup_task(&hrrs_cleanup_task); @@ -1972,7 +1989,6 @@ return; } - HRSPhaseSetter x(HRSPhaseCleanup); g1h->verify_region_sets_optional(); if (VerifyDuringGC) { @@ -2158,9 +2174,9 @@ // so it's not necessary to take any locks while (!_cleanup_list.is_empty()) { HeapRegion* hr = _cleanup_list.remove_head(); - assert(hr != NULL, "the list was not empty"); + assert(hr != NULL, "Got NULL from a non-empty list"); hr->par_clear(); - tmp_free_list.add_as_tail(hr); + tmp_free_list.add_ordered(hr); // Instead of adding one region at a time to the secondary_free_list, // we accumulate them in the local list and move them a few at a @@ -2180,7 +2196,7 @@ { MutexLockerEx x(SecondaryFreeList_lock, Mutex::_no_safepoint_check_flag); - g1h->secondary_free_list_add_as_tail(&tmp_free_list); + g1h->secondary_free_list_add(&tmp_free_list); SecondaryFreeList_lock->notify_all(); } @@ -2239,7 +2255,7 @@ if (_cm->verbose_high()) { gclog_or_tty->print_cr("\t[%u] we're looking at location " "*"PTR_FORMAT" = "PTR_FORMAT, - _task->worker_id(), p, (void*) obj); + _task->worker_id(), p2i(p), p2i((void*) obj)); } _task->deal_with_reference(obj); @@ -2529,10 +2545,14 @@ assert(!rp->discovery_enabled(), "Post condition"); } - // Now clean up stale oops in StringTable - StringTable::unlink(&g1_is_alive); - // Clean up unreferenced symbols in symbol table. - SymbolTable::unlink(); + if (has_overflown()) { + // We can not trust g1_is_alive if the marking stack overflowed + return; + } + + g1h->unlink_string_and_symbol_table(&g1_is_alive, + /* process_strings */ false, // currently strings are always roots + /* process_symbols */ true); } void ConcurrentMark::swapMarkBitMaps() { @@ -2670,7 +2690,7 @@ } _out->print_cr(" "PTR_FORMAT": "PTR_FORMAT"%s%s", - p, (void*) obj, str, str2); + p2i(p), p2i((void*) obj), str, str2); } }; @@ -2697,7 +2717,7 @@ if (print_it) { _out->print_cr(" "PTR_FORMAT"%s", - (void *)o, (over_tams) ? " >" : (marked) ? " M" : ""); + p2i((void *)o), (over_tams) ? " >" : (marked) ? " M" : ""); PrintReachableOopClosure oopCl(_out, _vo, _all); o->oop_iterate_no_header(&oopCl); } @@ -2718,14 +2738,14 @@ HeapWord* t = hr->top(); HeapWord* p = _g1h->top_at_mark_start(hr, _vo); _out->print_cr("** ["PTR_FORMAT", "PTR_FORMAT"] top: "PTR_FORMAT" " - "TAMS: "PTR_FORMAT, b, e, t, p); + "TAMS: " PTR_FORMAT, p2i(b), p2i(e), p2i(t), p2i(p)); _out->cr(); HeapWord* from = b; HeapWord* to = t; if (to > from) { - _out->print_cr("Objects in ["PTR_FORMAT", "PTR_FORMAT"]", from, to); + _out->print_cr("Objects in [" PTR_FORMAT ", " PTR_FORMAT "]", p2i(from), p2i(to)); _out->cr(); PrintReachableObjectClosure ocl(_out, _vo, _all, hr); hr->object_iterate_mem_careful(MemRegion(from, to), &ocl); @@ -2841,7 +2861,7 @@ gclog_or_tty->print_cr("[%u] curr_region = "PTR_FORMAT" " "["PTR_FORMAT", "PTR_FORMAT"), " "limit = "PTR_FORMAT, - worker_id, curr_region, bottom, end, limit); + worker_id, p2i(curr_region), p2i(bottom), p2i(end), p2i(limit)); } // Is the gap between reading the finger and doing the CAS too long? @@ -2855,13 +2875,13 @@ if (verbose_low()) { gclog_or_tty->print_cr("[%u] we were successful with region = " - PTR_FORMAT, worker_id, curr_region); + PTR_FORMAT, worker_id, p2i(curr_region)); } if (limit > bottom) { if (verbose_low()) { gclog_or_tty->print_cr("[%u] region "PTR_FORMAT" is not empty, " - "returning it ", worker_id, curr_region); + "returning it ", worker_id, p2i(curr_region)); } return curr_region; } else { @@ -2869,7 +2889,7 @@ "the region limit should be at bottom"); if (verbose_low()) { gclog_or_tty->print_cr("[%u] region "PTR_FORMAT" is empty, " - "returning NULL", worker_id, curr_region); + "returning NULL", worker_id, p2i(curr_region)); } // we return NULL and the caller should try calling // claim_region() again. @@ -2881,7 +2901,7 @@ gclog_or_tty->print_cr("[%u] somebody else moved the finger, " "global finger = "PTR_FORMAT", " "our finger = "PTR_FORMAT, - worker_id, _finger, finger); + worker_id, p2i(_finger), p2i(finger)); } // read it again @@ -2920,7 +2940,7 @@ void do_object_work(oop obj) { guarantee(!_g1h->obj_in_cs(obj), err_msg("obj: "PTR_FORMAT" in CSet, phase: %s, info: %d", - (void*) obj, phase_str(), _info)); + p2i((void*) obj), phase_str(), _info)); } public: @@ -2999,7 +3019,7 @@ HeapRegion* global_hr = _g1h->heap_region_containing_raw(global_finger); guarantee(global_finger == global_hr->bottom(), err_msg("global finger: "PTR_FORMAT" region: "HR_FORMAT, - global_finger, HR_FORMAT_PARAMS(global_hr))); + p2i(global_finger), HR_FORMAT_PARAMS(global_hr))); } // Verify the task fingers @@ -3013,7 +3033,7 @@ guarantee(task_finger == task_hr->bottom() || !task_hr->in_collection_set(), err_msg("task finger: "PTR_FORMAT" region: "HR_FORMAT, - task_finger, HR_FORMAT_PARAMS(task_hr))); + p2i(task_finger), HR_FORMAT_PARAMS(task_hr))); } } } @@ -3057,7 +3077,7 @@ err_msg("Preconditions not met - " "start: "PTR_FORMAT", limit: "PTR_FORMAT", " "top: "PTR_FORMAT", end: "PTR_FORMAT, - start, limit, hr->top(), hr->end())); + p2i(start), p2i(limit), p2i(hr->top()), p2i(hr->end()))); assert(hr->next_marked_bytes() == 0, "Precondition"); @@ -3230,6 +3250,8 @@ for (uint i = 0; i < _max_worker_id; ++i) { _tasks[i]->clear_region_fields(); } + _first_overflow_barrier_sync.abort(); + _second_overflow_barrier_sync.abort(); _has_aborted = true; SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); @@ -3293,7 +3315,7 @@ void ConcurrentMark::print_on_error(outputStream* st) const { st->print_cr("Marking Bits (Prev, Next): (CMBitMap*) " PTR_FORMAT ", (CMBitMap*) " PTR_FORMAT, - _prevMarkBitMap, _nextMarkBitMap); + p2i(_prevMarkBitMap), p2i(_nextMarkBitMap)); _prevMarkBitMap->print_on_error(st, " Prev Bits: "); _nextMarkBitMap->print_on_error(st, " Next Bits: "); } @@ -3330,11 +3352,11 @@ // for debugging purposes void ConcurrentMark::print_finger() { gclog_or_tty->print_cr("heap ["PTR_FORMAT", "PTR_FORMAT"), global finger = "PTR_FORMAT, - _heap_start, _heap_end, _finger); + p2i(_heap_start), p2i(_heap_end), p2i(_finger)); for (uint i = 0; i < _max_worker_id; ++i) { - gclog_or_tty->print(" %u: "PTR_FORMAT, i, _tasks[i]->finger()); + gclog_or_tty->print(" %u: " PTR_FORMAT, i, p2i(_tasks[i]->finger())); } - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); } #endif @@ -3343,7 +3365,7 @@ if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] we're scanning object "PTR_FORMAT, - _worker_id, (void*) obj); + _worker_id, p2i((void*) obj)); } size_t obj_size = obj->size(); @@ -3423,7 +3445,7 @@ if (_cm->verbose_low()) { gclog_or_tty->print_cr("[%u] setting up for region "PTR_FORMAT, - _worker_id, hr); + _worker_id, p2i(hr)); } _curr_region = hr; @@ -3440,7 +3462,7 @@ if (_cm->verbose_low()) { gclog_or_tty->print_cr("[%u] found an empty region " "["PTR_FORMAT", "PTR_FORMAT")", - _worker_id, bottom, limit); + _worker_id, p2i(bottom), p2i(limit)); } // The region was collected underneath our feet. // We set the finger to bottom to ensure that the bitmap @@ -3472,7 +3494,7 @@ assert(_curr_region != NULL, "invariant"); if (_cm->verbose_low()) { gclog_or_tty->print_cr("[%u] giving up region "PTR_FORMAT, - _worker_id, _curr_region); + _worker_id, p2i(_curr_region)); } clear_region_fields(); } @@ -3752,7 +3774,7 @@ if (_task_queue->size() > target_size) { if (_cm->verbose_high()) { - gclog_or_tty->print_cr("[%u] draining local queue, target size = %d", + gclog_or_tty->print_cr("[%u] draining local queue, target size = " SIZE_FORMAT, _worker_id, target_size); } @@ -3763,7 +3785,7 @@ if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] popped "PTR_FORMAT, _worker_id, - (void*) obj); + p2i((void*) obj)); } assert(_g1h->is_in_g1_reserved((HeapWord*) obj), "invariant" ); @@ -3808,7 +3830,7 @@ if (_cm->mark_stack_size() > target_size) { if (_cm->verbose_low()) { - gclog_or_tty->print_cr("[%u] draining global_stack, target size %d", + gclog_or_tty->print_cr("[%u] draining global_stack, target size " SIZE_FORMAT, _worker_id, target_size); } @@ -3818,7 +3840,7 @@ } if (_cm->verbose_low()) { - gclog_or_tty->print_cr("[%u] drained global stack, size = %d", + gclog_or_tty->print_cr("[%u] drained global stack, size = " SIZE_FORMAT, _worker_id, _cm->mark_stack_size()); } } @@ -4148,7 +4170,7 @@ gclog_or_tty->print_cr("[%u] we're scanning part " "["PTR_FORMAT", "PTR_FORMAT") " "of region "HR_FORMAT, - _worker_id, _finger, _region_limit, + _worker_id, p2i(_finger), p2i(_region_limit), HR_FORMAT_PARAMS(_curr_region)); } @@ -4235,7 +4257,7 @@ if (_cm->verbose_low()) { gclog_or_tty->print_cr("[%u] we successfully claimed " "region "PTR_FORMAT, - _worker_id, claimed_region); + _worker_id, p2i(claimed_region)); } setup_for_region(claimed_region); @@ -4296,7 +4318,7 @@ if (_cm->try_stealing(_worker_id, &_hash_seed, obj)) { if (_cm->verbose_medium()) { gclog_or_tty->print_cr("[%u] stolen "PTR_FORMAT" successfully", - _worker_id, (void*) obj); + _worker_id, p2i((void*) obj)); } statsOnly( ++_steals ); @@ -4544,8 +4566,8 @@ G1PPRL_SUM_ADDR_FORMAT("committed") G1PPRL_SUM_ADDR_FORMAT("reserved") G1PPRL_SUM_BYTE_FORMAT("region-size"), - g1_committed.start(), g1_committed.end(), - g1_reserved.start(), g1_reserved.end(), + p2i(g1_committed.start()), p2i(g1_committed.end()), + p2i(g1_reserved.start()), p2i(g1_reserved.end()), HeapRegion::GrainBytes); _out->print_cr(G1PPRL_LINE_PREFIX); _out->print_cr(G1PPRL_LINE_PREFIX @@ -4662,7 +4684,7 @@ G1PPRL_DOUBLE_FORMAT G1PPRL_BYTE_FORMAT G1PPRL_BYTE_FORMAT, - type, bottom, end, + type, p2i(bottom), p2i(end), used_bytes, prev_live_bytes, next_live_bytes, gc_eff, remset_bytes, strong_code_roots_bytes); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentMark.hpp --- a/src/share/vm/gc_implementation/g1/concurrentMark.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -25,7 +25,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTMARK_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_CONCURRENTMARK_HPP -#include "gc_implementation/g1/heapRegionSets.hpp" +#include "gc_implementation/g1/heapRegionSet.hpp" #include "utilities/taskqueue.hpp" class G1CollectedHeap; @@ -542,8 +542,12 @@ // frequently. HeapRegion* claim_region(uint worker_id); - // It determines whether we've run out of regions to scan. - bool out_of_regions() { return _finger == _heap_end; } + // It determines whether we've run out of regions to scan. Note that + // the finger can point past the heap end in case the heap was expanded + // to satisfy an allocation without doing a GC. This is fine, because all + // objects in those regions will be considered live anyway because of + // SATB guarantees (i.e. their TAMS will be equal to bottom). + bool out_of_regions() { return _finger >= _heap_end; } // Returns the task with the given id CMTask* task(int id) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp --- a/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -277,7 +277,7 @@ assert(_nextMarkBitMap->isMarked(objAddr), "invariant"); if (_cm->verbose_high()) { - gclog_or_tty->print_cr("[%u] pushing "PTR_FORMAT, _worker_id, (void*) obj); + gclog_or_tty->print_cr("[%u] pushing " PTR_FORMAT, _worker_id, p2i((void*) obj)); } if (!_task_queue->push(obj)) { @@ -317,7 +317,7 @@ inline void CMTask::deal_with_reference(oop obj) { if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] we're dealing with reference = "PTR_FORMAT, - _worker_id, (void*) obj); + _worker_id, p2i((void*) obj)); } ++_refs_reached; @@ -334,7 +334,7 @@ if (!hr->obj_allocated_since_next_marking(obj)) { if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] "PTR_FORMAT" is not considered marked", - _worker_id, (void*) obj); + _worker_id, p2i((void*) obj)); } // we need to mark it first @@ -349,7 +349,7 @@ if (_finger != NULL && objAddr < _finger) { if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] below the local finger ("PTR_FORMAT"), " - "pushing it", _worker_id, _finger); + "pushing it", _worker_id, p2i(_finger)); } push(obj); } else if (_curr_region != NULL && objAddr < _region_limit) { @@ -367,7 +367,7 @@ if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] below the global finger " "("PTR_FORMAT"), pushing it", - _worker_id, global_finger); + _worker_id, p2i(global_finger)); } push(obj); } else { @@ -382,7 +382,7 @@ if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] below the global finger " "("PTR_FORMAT"), pushing it", - _worker_id, global_finger); + _worker_id, p2i(global_finger)); } push(obj); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp --- a/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -89,6 +89,10 @@ while (!_should_terminate) { // wait until started is set. sleepBeforeNextCycle(); + if (_should_terminate) { + break; + } + { ResourceMark rm; HandleMark hm; @@ -303,11 +307,21 @@ } void ConcurrentMarkThread::stop() { - // it is ok to take late safepoints here, if needed - MutexLockerEx mu(Terminator_lock); - _should_terminate = true; - while (!_has_terminated) { - Terminator_lock->wait(); + { + MutexLockerEx ml(Terminator_lock); + _should_terminate = true; + } + + { + MutexLockerEx ml(CGC_lock, Mutex::_no_safepoint_check_flag); + CGC_lock->notify_all(); + } + + { + MutexLockerEx ml(Terminator_lock); + while (!_has_terminated) { + Terminator_lock->wait(); + } } } @@ -327,11 +341,14 @@ assert(!in_progress(), "should have been cleared"); MutexLockerEx x(CGC_lock, Mutex::_no_safepoint_check_flag); - while (!started()) { + while (!started() && !_should_terminate) { CGC_lock->wait(Mutex::_no_safepoint_check_flag); } - set_in_progress(); - clear_started(); + + if (started()) { + set_in_progress(); + clear_started(); + } } // Note: As is the case with CMS - this method, although exported diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc_implementation/g1/dirtyCardQueue.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" #include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" @@ -33,12 +34,12 @@ bool DirtyCardQueue::apply_closure(CardTableEntryClosure* cl, bool consume, - size_t worker_i) { + uint worker_i) { bool res = true; if (_buf != NULL) { res = apply_closure_to_buffer(cl, _buf, _index, _sz, consume, - (int) worker_i); + worker_i); if (res && consume) _index = _sz; } return res; @@ -48,7 +49,7 @@ void** buf, size_t index, size_t sz, bool consume, - int worker_i) { + uint worker_i) { if (cl == NULL) return true; for (size_t i = index; i < sz; i += oopSize) { int ind = byte_index_to_index((int)i); @@ -78,8 +79,8 @@ } // Determines how many mutator threads can process the buffers in parallel. -size_t DirtyCardQueueSet::num_par_ids() { - return os::processor_count(); +uint DirtyCardQueueSet::num_par_ids() { + return (uint)os::processor_count(); } void DirtyCardQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock, @@ -102,7 +103,7 @@ } void DirtyCardQueueSet::iterate_closure_all_threads(bool consume, - size_t worker_i) { + uint worker_i) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); for(JavaThread* t = Threads::first(); t; t = t->next()) { bool b = t->dirty_card_queue().apply_closure(_closure, consume); @@ -125,11 +126,11 @@ // We get the the number of any par_id that this thread // might have already claimed. - int worker_i = thread->get_claimed_par_id(); + uint worker_i = thread->get_claimed_par_id(); - // If worker_i is not -1 then the thread has already claimed + // If worker_i is not UINT_MAX then the thread has already claimed // a par_id. We make note of it using the already_claimed value - if (worker_i != -1) { + if (worker_i != UINT_MAX) { already_claimed = true; } else { @@ -141,7 +142,7 @@ } bool b = false; - if (worker_i != -1) { + if (worker_i != UINT_MAX) { b = DirtyCardQueue::apply_closure_to_buffer(_closure, buf, 0, _sz, true, worker_i); if (b) Atomic::inc(&_processed_buffers_mut); @@ -153,8 +154,8 @@ // we release the id _free_ids->release_par_id(worker_i); - // and set the claimed_id in the thread to -1 - thread->set_claimed_par_id(-1); + // and set the claimed_id in the thread to UINT_MAX + thread->set_claimed_par_id(UINT_MAX); } } return b; @@ -185,7 +186,7 @@ bool DirtyCardQueueSet:: apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl, - int worker_i, + uint worker_i, BufferNode* nd) { if (nd != NULL) { void **buf = BufferNode::make_buffer_from_node(nd); @@ -207,7 +208,7 @@ } bool DirtyCardQueueSet::apply_closure_to_completed_buffer(CardTableEntryClosure* cl, - int worker_i, + uint worker_i, int stop_at, bool during_pause) { assert(!during_pause || stop_at == 0, "Should not leave any completed buffers during a pause"); @@ -217,7 +218,7 @@ return res; } -bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i, +bool DirtyCardQueueSet::apply_closure_to_completed_buffer(uint worker_i, int stop_at, bool during_pause) { return apply_closure_to_completed_buffer(_closure, worker_i, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -36,7 +36,7 @@ public: // Process the card whose card table entry is "card_ptr". If returns // "false", terminate the iteration early. - virtual bool do_card_ptr(jbyte* card_ptr, int worker_i = 0) = 0; + virtual bool do_card_ptr(jbyte* card_ptr, uint worker_i = 0) = 0; }; // A ptrQueue whose elements are "oops", pointers to object heads. @@ -53,7 +53,7 @@ // deletes processed entries from logs. bool apply_closure(CardTableEntryClosure* cl, bool consume = true, - size_t worker_i = 0); + uint worker_i = 0); // Apply the closure to all elements of "buf", down to "index" // (inclusive.) If returns "false", then a closure application returned @@ -63,7 +63,7 @@ static bool apply_closure_to_buffer(CardTableEntryClosure* cl, void** buf, size_t index, size_t sz, bool consume = true, - int worker_i = 0); + uint worker_i = 0); void **get_buf() { return _buf;} void set_buf(void **buf) {_buf = buf;} size_t get_index() { return _index;} @@ -98,7 +98,7 @@ // The number of parallel ids that can be claimed to allow collector or // mutator threads to do card-processing work. - static size_t num_par_ids(); + static uint num_par_ids(); static void handle_zero_index_for_thread(JavaThread* t); @@ -115,7 +115,7 @@ // change in the future.) If "consume" is true, processed entries are // discarded. void iterate_closure_all_threads(bool consume = true, - size_t worker_i = 0); + uint worker_i = 0); // If there exists some completed buffer, pop it, then apply the // registered closure to all its elements, nulling out those elements @@ -124,7 +124,7 @@ // but is only partially completed before a "yield" happens, the // partially completed buffer (with its processed elements set to NULL) // is returned to the completed buffer set, and this call returns false. - bool apply_closure_to_completed_buffer(int worker_i = 0, + bool apply_closure_to_completed_buffer(uint worker_i = 0, int stop_at = 0, bool during_pause = false); @@ -136,13 +136,13 @@ // partially completed buffer (with its processed elements set to NULL) // is returned to the completed buffer set, and this call returns false. bool apply_closure_to_completed_buffer(CardTableEntryClosure* cl, - int worker_i = 0, + uint worker_i = 0, int stop_at = 0, bool during_pause = false); // Helper routine for the above. bool apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl, - int worker_i, + uint worker_i, BufferNode* nd); BufferNode* get_completed_buffer(int stop_at); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1AllocRegion.cpp --- a/src/share/vm/gc_implementation/g1/g1AllocRegion.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1AllocRegion.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -142,7 +142,7 @@ void G1AllocRegion::fill_in_ext_msg(ar_ext_msg* msg, const char* message) { msg->append("[%s] %s c: %u b: %s r: "PTR_FORMAT" u: "SIZE_FORMAT, _name, message, _count, BOOL_TO_STR(_bot_updates), - _alloc_region, _used_bytes_before); + p2i(_alloc_region), _used_bytes_before); } void G1AllocRegion::init() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1AllocRegion.hpp --- a/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -193,7 +193,7 @@ class ar_ext_msg : public err_msg { public: - ar_ext_msg(G1AllocRegion* alloc_region, const char *message) : err_msg("") { + ar_ext_msg(G1AllocRegion* alloc_region, const char *message) : err_msg("%s", "") { alloc_region->fill_in_ext_msg(this, message); } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1BiasedArray.cpp --- a/src/share/vm/gc_implementation/g1/g1BiasedArray.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1BiasedArray.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,6 +24,14 @@ #include "precompiled.hpp" #include "gc_implementation/g1/g1BiasedArray.hpp" +#include "memory/padded.inline.hpp" + +// Allocate a new array, generic version. +address G1BiasedMappedArrayBase::create_new_base_array(size_t length, size_t elem_size) { + assert(length > 0, "just checking"); + assert(elem_size > 0, "just checking"); + return PaddedPrimitiveArray::create_unfreeable(length * elem_size); +} #ifndef PRODUCT void G1BiasedMappedArrayBase::verify_index(idx_t index) const { @@ -57,7 +65,7 @@ REGION_SIZE_IN_WORDS * HeapWordSize); // Check address calculation (bounds) assert(array.bottom_address_mapped() == fake_heap, - err_msg("bottom mapped address should be "PTR_FORMAT", but is "PTR_FORMAT, fake_heap, array.bottom_address_mapped())); + err_msg("bottom mapped address should be " PTR_FORMAT ", but is " PTR_FORMAT, p2i(fake_heap), p2i(array.bottom_address_mapped()))); assert(array.end_address_mapped() == (fake_heap + REGION_SIZE_IN_WORDS * NUM_REGIONS), "must be"); int* bottom = array.address_mapped_to(fake_heap); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1BiasedArray.hpp --- a/src/share/vm/gc_implementation/g1/g1BiasedArray.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1BiasedArray.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -25,8 +25,8 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1BIASEDARRAY_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_G1BIASEDARRAY_HPP +#include "memory/allocation.hpp" #include "utilities/debug.hpp" -#include "memory/allocation.inline.hpp" // Implements the common base functionality for arrays that contain provisions // for accessing its elements using a biased index. @@ -48,18 +48,14 @@ _bias(0), _shift_by(0) { } // Allocate a new array, generic version. - static address create_new_base_array(size_t length, size_t elem_size) { - assert(length > 0, "just checking"); - assert(elem_size > 0, "just checking"); - return NEW_C_HEAP_ARRAY(u_char, length * elem_size, mtGC); - } + static address create_new_base_array(size_t length, size_t elem_size); // Initialize the members of this class. The biased start address of this array // is the bias (in elements) multiplied by the element size. void initialize_base(address base, size_t length, size_t bias, size_t elem_size, uint shift_by) { assert(base != NULL, "just checking"); assert(length > 0, "just checking"); - assert(shift_by < sizeof(uintptr_t) * 8, err_msg("Shifting by %zd, larger than word size?", shift_by)); + assert(shift_by < sizeof(uintptr_t) * 8, err_msg("Shifting by " SSIZE_FORMAT ", larger than word size?", (size_t) shift_by)); _base = base; _length = length; _biased_base = base - (bias * elem_size); @@ -74,11 +70,11 @@ assert(is_power_of_2(mapping_granularity_in_bytes), err_msg("mapping granularity must be power of 2, is %zd", mapping_granularity_in_bytes)); assert((uintptr_t)bottom % mapping_granularity_in_bytes == 0, - err_msg("bottom mapping area address must be a multiple of mapping granularity %zd, is "PTR_FORMAT, - mapping_granularity_in_bytes, bottom)); + err_msg("bottom mapping area address must be a multiple of mapping granularity %zd, is " PTR_FORMAT, + mapping_granularity_in_bytes, p2i(bottom))); assert((uintptr_t)end % mapping_granularity_in_bytes == 0, - err_msg("end mapping area address must be a multiple of mapping granularity %zd, is "PTR_FORMAT, - mapping_granularity_in_bytes, end)); + err_msg("end mapping area address must be a multiple of mapping granularity %zd, is " PTR_FORMAT, + mapping_granularity_in_bytes, p2i(end))); size_t num_target_elems = (end - bottom) / (mapping_granularity_in_bytes / HeapWordSize); idx_t bias = (uintptr_t)bottom / mapping_granularity_in_bytes; address base = create_new_base_array(num_target_elems, target_elem_size_in_bytes); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp --- a/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -29,6 +29,8 @@ #include "runtime/java.hpp" #include "services/memTracker.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ////////////////////////////////////////////////////////////////////// // G1BlockOffsetSharedArray ////////////////////////////////////////////////////////////////////// diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp --- a/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -80,7 +80,7 @@ virtual void set_bottom(HeapWord* new_bottom) { assert(new_bottom <= _end, err_msg("new_bottom (" PTR_FORMAT ") > _end (" PTR_FORMAT ")", - new_bottom, _end)); + p2i(new_bottom), p2i(_end))); _bottom = new_bottom; resize(pointer_delta(_end, _bottom)); } @@ -146,7 +146,7 @@ void check_offset(size_t offset, const char* msg) const { assert(offset <= N_words, err_msg("%s - " - "offset: " UINT32_FORMAT", N_words: " UINT32_FORMAT, + "offset: " SIZE_FORMAT ", N_words: " UINT32_FORMAT, msg, offset, N_words)); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp --- a/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -50,7 +50,7 @@ assert(pc >= (char*)_reserved.start() && pc < (char*)_reserved.end(), err_msg("p (" PTR_FORMAT ") not in reserved [" PTR_FORMAT ", " PTR_FORMAT ")", - p, (char*)_reserved.start(), (char*)_reserved.end())); + p2i(p), p2i(_reserved.start()), p2i(_reserved.end()))); size_t delta = pointer_delta(pc, _reserved.start(), sizeof(char)); size_t result = delta >> LogN; check_index(result, "bad index from address"); @@ -65,7 +65,7 @@ err_msg("bad address from index result " PTR_FORMAT " _reserved.start() " PTR_FORMAT " _reserved.end() " PTR_FORMAT, - result, _reserved.start(), _reserved.end())); + p2i(result), p2i(_reserved.start()), p2i(_reserved.end()))); return result; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CardCounts.cpp --- a/src/share/vm/gc_implementation/g1/g1CardCounts.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CardCounts.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -31,6 +31,8 @@ #include "services/memTracker.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void G1CardCounts::clear_range(size_t from_card_num, size_t to_card_num) { if (has_count_table()) { assert(from_card_num >= 0 && from_card_num < _committed_max_card_num, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CardCounts.hpp --- a/src/share/vm/gc_implementation/g1/g1CardCounts.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CardCounts.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -77,10 +77,10 @@ err_msg("Invalid card pointer: " "card_ptr: " PTR_FORMAT ", " "_ct_bot: " PTR_FORMAT, - card_ptr, _ct_bot)); + p2i(card_ptr), p2i(_ct_bot))); size_t card_num = pointer_delta(card_ptr, _ct_bot, sizeof(jbyte)); assert(card_num >= 0 && card_num < _committed_max_card_num, - err_msg("card pointer out of range: " PTR_FORMAT, card_ptr)); + err_msg("card pointer out of range: " PTR_FORMAT, p2i(card_ptr))); return card_num; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,319 @@ +/* + * Copyright (c) 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 + * 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/nmethod.hpp" +#include "gc_implementation/g1/g1CodeCacheRemSet.hpp" +#include "memory/iterator.hpp" + +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL) { + _top = bottom(); +} + +void G1CodeRootChunk::reset() { + _next = _prev = NULL; + _top = bottom(); +} + +void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) { + nmethod** cur = bottom(); + while (cur != _top) { + cl->do_code_blob(*cur); + cur++; + } +} + +FreeList G1CodeRootSet::_free_list; +size_t G1CodeRootSet::_num_chunks_handed_out = 0; + +G1CodeRootChunk* G1CodeRootSet::new_chunk() { + G1CodeRootChunk* result = _free_list.get_chunk_at_head(); + if (result == NULL) { + result = new G1CodeRootChunk(); + } + G1CodeRootSet::_num_chunks_handed_out++; + result->reset(); + return result; +} + +void G1CodeRootSet::free_chunk(G1CodeRootChunk* chunk) { + _free_list.return_chunk_at_head(chunk); + G1CodeRootSet::_num_chunks_handed_out--; +} + +void G1CodeRootSet::free_all_chunks(FreeList* list) { + G1CodeRootSet::_num_chunks_handed_out -= list->count(); + _free_list.prepend(list); +} + +void G1CodeRootSet::purge_chunks(size_t keep_ratio) { + size_t keep = G1CodeRootSet::_num_chunks_handed_out * keep_ratio / 100; + + if (keep >= (size_t)_free_list.count()) { + return; + } + + FreeList temp; + temp.initialize(); + temp.set_size(G1CodeRootChunk::word_size()); + + _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp); + + G1CodeRootChunk* cur = temp.get_chunk_at_head(); + while (cur != NULL) { + delete cur; + cur = temp.get_chunk_at_head(); + } +} + +size_t G1CodeRootSet::static_mem_size() { + return sizeof(_free_list) + sizeof(_num_chunks_handed_out); +} + +size_t G1CodeRootSet::fl_mem_size() { + return _free_list.count() * _free_list.size(); +} + +void G1CodeRootSet::initialize() { + _free_list.initialize(); + _free_list.set_size(G1CodeRootChunk::word_size()); +} + +G1CodeRootSet::G1CodeRootSet() : _list(), _length(0) { + _list.initialize(); + _list.set_size(G1CodeRootChunk::word_size()); +} + +G1CodeRootSet::~G1CodeRootSet() { + clear(); +} + +void G1CodeRootSet::add(nmethod* method) { + if (!contains(method)) { + // Try to add the nmethod. If there is not enough space, get a new chunk. + if (_list.head() == NULL || _list.head()->is_full()) { + G1CodeRootChunk* cur = new_chunk(); + _list.return_chunk_at_head(cur); + } + bool result = _list.head()->add(method); + guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method)); + _length++; + } +} + +void G1CodeRootSet::remove(nmethod* method) { + G1CodeRootChunk* found = find(method); + if (found != NULL) { + bool result = found->remove(method); + guarantee(result, err_msg("could not find nmethod "PTR_FORMAT" during removal although we previously found it", method)); + // eventually free completely emptied chunk + if (found->is_empty()) { + _list.remove_chunk(found); + free(found); + } + _length--; + } + assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method)); +} + +nmethod* G1CodeRootSet::pop() { + do { + G1CodeRootChunk* cur = _list.head(); + if (cur == NULL) { + assert(_length == 0, "when there are no chunks, there should be no elements"); + return NULL; + } + nmethod* result = cur->pop(); + if (result != NULL) { + _length--; + return result; + } else { + free(_list.get_chunk_at_head()); + } + } while (true); +} + +G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) { + G1CodeRootChunk* cur = _list.head(); + while (cur != NULL) { + if (cur->contains(method)) { + return cur; + } + cur = (G1CodeRootChunk*)cur->next(); + } + return NULL; +} + +void G1CodeRootSet::free(G1CodeRootChunk* chunk) { + free_chunk(chunk); +} + +bool G1CodeRootSet::contains(nmethod* method) { + return find(method) != NULL; +} + +void G1CodeRootSet::clear() { + free_all_chunks(&_list); + _length = 0; +} + +void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const { + G1CodeRootChunk* cur = _list.head(); + while (cur != NULL) { + cur->nmethods_do(blk); + cur = (G1CodeRootChunk*)cur->next(); + } +} + +size_t G1CodeRootSet::mem_size() { + return sizeof(this) + _list.count() * _list.size(); +} + +#ifndef PRODUCT + +void G1CodeRootSet::test() { + initialize(); + + assert(_free_list.count() == 0, "Free List must be empty"); + assert(_num_chunks_handed_out == 0, "No elements must have been handed out yet"); + + // The number of chunks that we allocate for purge testing. + size_t const num_chunks = 10; + { + G1CodeRootSet set1; + assert(set1.is_empty(), "Code root set must be initially empty but is not."); + + set1.add((nmethod*)1); + assert(_num_chunks_handed_out == 1, + err_msg("Must have allocated and handed out one chunk, but handed out " + SIZE_FORMAT" chunks", _num_chunks_handed_out)); + assert(set1.length() == 1, err_msg("Added exactly one element, but set contains " + SIZE_FORMAT" elements", set1.length())); + + // G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which + // we cannot access. + for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) { + set1.add((nmethod*)1); + } + assert(_num_chunks_handed_out == 1, + err_msg("Duplicate detection must have prevented allocation of further " + "chunks but contains "SIZE_FORMAT, _num_chunks_handed_out)); + assert(set1.length() == 1, + err_msg("Duplicate detection should not have increased the set size but " + "is "SIZE_FORMAT, set1.length())); + + size_t num_total_after_add = G1CodeRootChunk::word_size() + 1; + for (size_t i = 0; i < num_total_after_add - 1; i++) { + set1.add((nmethod*)(2 + i)); + } + assert(_num_chunks_handed_out > 1, + "After adding more code roots, more than one chunks should have been handed out"); + assert(set1.length() == num_total_after_add, + err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they " + "need to be in the set, but there are only "SIZE_FORMAT, + num_total_after_add, set1.length())); + + size_t num_popped = 0; + while (set1.pop() != NULL) { + num_popped++; + } + assert(num_popped == num_total_after_add, + err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" " + "were added", num_popped, num_total_after_add)); + assert(_num_chunks_handed_out == 0, + err_msg("After popping all elements, all chunks must have been returned " + "but are still "SIZE_FORMAT, _num_chunks_handed_out)); + + purge_chunks(0); + assert(_free_list.count() == 0, + err_msg("After purging everything, the free list must be empty but still " + "contains "SIZE_FORMAT" chunks", _free_list.count())); + + // Add some more handed out chunks. + size_t i = 0; + while (_num_chunks_handed_out < num_chunks) { + set1.add((nmethod*)i); + i++; + } + + { + // Generate chunks on the free list. + G1CodeRootSet set2; + size_t i = 0; + while (_num_chunks_handed_out < num_chunks * 2) { + set2.add((nmethod*)i); + i++; + } + // Exit of the scope of the set2 object will call the destructor that generates + // num_chunks elements on the free list. + } + + assert(_num_chunks_handed_out == num_chunks, + err_msg("Deletion of the second set must have resulted in giving back " + "those, but there is still "SIZE_FORMAT" handed out, expecting " + SIZE_FORMAT, _num_chunks_handed_out, num_chunks)); + assert((size_t)_free_list.count() == num_chunks, + err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list " + "but there are only "SIZE_FORMAT, num_chunks, _free_list.count())); + + size_t const test_percentage = 50; + purge_chunks(test_percentage); + assert(_num_chunks_handed_out == num_chunks, + err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT, + _num_chunks_handed_out)); + assert((size_t)_free_list.count() == (ssize_t)(num_chunks * test_percentage / 100), + err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks" + "but there are "SSIZE_FORMAT, test_percentage, num_chunks, + _free_list.count())); + // Purge the remainder of the chunks on the free list. + purge_chunks(0); + assert(_free_list.count() == 0, "Free List must be empty"); + assert(_num_chunks_handed_out == num_chunks, + err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set " + "but there are "SIZE_FORMAT, num_chunks, _num_chunks_handed_out)); + + // Exit of the scope of the set1 object will call the destructor that generates + // num_chunks additional elements on the free list. + } + + assert(_num_chunks_handed_out == 0, + err_msg("Deletion of the only set must have resulted in no chunks handed " + "out, but there is still "SIZE_FORMAT" handed out", _num_chunks_handed_out)); + assert((size_t)_free_list.count() == num_chunks, + err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list " + "but there are only "SSIZE_FORMAT, num_chunks, _free_list.count())); + + // Restore initial state. + purge_chunks(0); + assert(_free_list.count() == 0, "Free List must be empty"); + assert(_num_chunks_handed_out == 0, "No elements must have been handed out yet"); +} + +void TestCodeCacheRemSet_test() { + G1CodeRootSet::test(); +} +#endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 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 + * 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_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP + +#include "memory/allocation.hpp" +#include "memory/freeList.hpp" +#include "runtime/globals.hpp" + +class CodeBlobClosure; + +class G1CodeRootChunk : public CHeapObj { + private: + static const int NUM_ENTRIES = 32; + public: + G1CodeRootChunk* _next; + G1CodeRootChunk* _prev; + + nmethod** _top; + + nmethod* _data[NUM_ENTRIES]; + + nmethod** bottom() const { + return (nmethod**) &(_data[0]); + } + + nmethod** end() const { + return (nmethod**) &(_data[NUM_ENTRIES]); + } + + public: + G1CodeRootChunk(); + ~G1CodeRootChunk() {} + + static size_t word_size() { return (size_t)(align_size_up_(sizeof(G1CodeRootChunk), HeapWordSize) / HeapWordSize); } + + // FreeList "interface" methods + + G1CodeRootChunk* next() const { return _next; } + G1CodeRootChunk* prev() const { return _prev; } + void set_next(G1CodeRootChunk* v) { _next = v; assert(v != this, "Boom");} + void set_prev(G1CodeRootChunk* v) { _prev = v; assert(v != this, "Boom");} + void clear_next() { set_next(NULL); } + void clear_prev() { set_prev(NULL); } + + size_t size() const { return word_size(); } + + void link_next(G1CodeRootChunk* ptr) { set_next(ptr); } + void link_prev(G1CodeRootChunk* ptr) { set_prev(ptr); } + void link_after(G1CodeRootChunk* ptr) { + link_next(ptr); + if (ptr != NULL) ptr->link_prev((G1CodeRootChunk*)this); + } + + bool is_free() { return true; } + + // New G1CodeRootChunk routines + + void reset(); + + bool is_empty() const { + return _top == bottom(); + } + + bool is_full() const { + return _top == (nmethod**)end(); + } + + bool contains(nmethod* method) { + nmethod** cur = bottom(); + while (cur != _top) { + if (*cur == method) return true; + cur++; + } + return false; + } + + bool add(nmethod* method) { + if (is_full()) return false; + *_top = method; + _top++; + return true; + } + + bool remove(nmethod* method) { + nmethod** cur = bottom(); + while (cur != _top) { + if (*cur == method) { + memmove(cur, cur + 1, (_top - (cur + 1)) * sizeof(nmethod**)); + _top--; + return true; + } + cur++; + } + return false; + } + + void nmethods_do(CodeBlobClosure* blk); + + nmethod* pop() { + if (is_empty()) { + return NULL; + } + _top--; + return *_top; + } +}; + +// Implements storage for a set of code roots. +// All methods that modify the set are not thread-safe except if otherwise noted. +class G1CodeRootSet VALUE_OBJ_CLASS_SPEC { + private: + // Global free chunk list management + static FreeList _free_list; + // Total number of chunks handed out + static size_t _num_chunks_handed_out; + + static G1CodeRootChunk* new_chunk(); + static void free_chunk(G1CodeRootChunk* chunk); + // Free all elements of the given list. + static void free_all_chunks(FreeList* list); + + // Return the chunk that contains the given nmethod, NULL otherwise. + // Scans the list of chunks backwards, as this method is used to add new + // entries, which are typically added in bulk for a single nmethod. + G1CodeRootChunk* find(nmethod* method); + void free(G1CodeRootChunk* chunk); + + size_t _length; + FreeList _list; + + public: + G1CodeRootSet(); + ~G1CodeRootSet(); + + static void initialize(); + static void purge_chunks(size_t keep_ratio); + + static size_t static_mem_size(); + static size_t fl_mem_size(); + + // Search for the code blob from the recently allocated ones to find duplicates more quickly, as this + // method is likely to be repeatedly called with the same nmethod. + void add(nmethod* method); + + void remove(nmethod* method); + nmethod* pop(); + + bool contains(nmethod* method); + + void clear(); + + void nmethods_do(CodeBlobClosure* blk) const; + + bool is_empty() { return length() == 0; } + + // Length in elements + size_t length() const { return _length; } + + // Memory size in bytes taken by this set. + size_t mem_size(); + + static void test() PRODUCT_RETURN; +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -22,6 +22,10 @@ * */ +#if !defined(__clang_major__) && defined(__GNUC__) +#define ATTRIBUTE_PRINTF(x,y) // FIXME, formats are a mess. +#endif + #include "precompiled.hpp" #include "code/codeCache.hpp" #include "code/icBuffer.hpp" @@ -39,6 +43,7 @@ #include "gc_implementation/g1/g1MarkSweep.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" +#include "gc_implementation/g1/g1StringDedup.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" #include "gc_implementation/g1/heapRegion.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" @@ -50,8 +55,8 @@ #include "gc_implementation/shared/gcTraceTime.hpp" #include "gc_implementation/shared/isGCActiveMark.hpp" #include "memory/gcLocker.inline.hpp" -#include "memory/genOopClosures.inline.hpp" #include "memory/generationSpec.hpp" +#include "memory/iterator.hpp" #include "memory/referenceProcessor.hpp" #include "oops/oop.inline.hpp" #include "oops/oop.pcgc.inline.hpp" @@ -101,7 +106,7 @@ ConcurrentG1Refine* cg1r) : _sts(sts), _g1rs(g1rs), _cg1r(cg1r), _concurrent(true) {} - bool do_card_ptr(jbyte* card_ptr, int worker_i) { + bool do_card_ptr(jbyte* card_ptr, uint worker_i) { bool oops_into_cset = _g1rs->refine_card(card_ptr, worker_i, false); // This path is executed by the concurrent refine or mutator threads, // concurrently, and so we do not care if card_ptr contains references @@ -130,7 +135,7 @@ { for (int i = 0; i < 256; i++) _histo[i] = 0; } - bool do_card_ptr(jbyte* card_ptr, int worker_i) { + bool do_card_ptr(jbyte* card_ptr, uint worker_i) { if (_g1h->is_in_reserved(_ctbs->addr_for(card_ptr))) { _calls++; unsigned char* ujb = (unsigned char*)card_ptr; @@ -159,7 +164,7 @@ RedirtyLoggedCardTableEntryClosure() : _calls(0), _g1h(G1CollectedHeap::heap()), _ctbs(_g1h->g1_barrier_set()) {} - bool do_card_ptr(jbyte* card_ptr, int worker_i) { + bool do_card_ptr(jbyte* card_ptr, uint worker_i) { if (_g1h->is_in_reserved(_ctbs->addr_for(card_ptr))) { _calls++; *card_ptr = 0; @@ -169,14 +174,6 @@ int calls() { return _calls; } }; -class RedirtyLoggedCardTableEntryFastClosure : public CardTableEntryClosure { -public: - bool do_card_ptr(jbyte* card_ptr, int worker_i) { - *card_ptr = CardTableModRefBS::dirty_card_val(); - return true; - } -}; - YoungList::YoungList(G1CollectedHeap* g1h) : _g1h(g1h), _head(NULL), _length(0), _last_sampled_rs_lengths(0), _survivor_head(NULL), _survivor_tail(NULL), _survivor_length(0) { @@ -379,7 +376,7 @@ } } - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); } void G1CollectedHeap::push_dirty_cards_region(HeapRegion* hr) @@ -435,11 +432,6 @@ return hr; } -void G1CollectedHeap::stop_conc_gc_threads() { - _cg1r->stop(); - _cmThread->stop(); -} - #ifdef ASSERT // A region is added to the collection set as it is retired // so an address p can point to a region which will be in the @@ -524,7 +516,7 @@ // Private methods. HeapRegion* -G1CollectedHeap::new_region_try_secondary_free_list() { +G1CollectedHeap::new_region_try_secondary_free_list(bool is_old) { MutexLockerEx x(SecondaryFreeList_lock, Mutex::_no_safepoint_check_flag); while (!_secondary_free_list.is_empty() || free_regions_coming()) { if (!_secondary_free_list.is_empty()) { @@ -540,7 +532,7 @@ assert(!_free_list.is_empty(), "if the secondary_free_list was not " "empty we should have moved at least one entry to the free_list"); - HeapRegion* res = _free_list.remove_head(); + HeapRegion* res = _free_list.remove_region(is_old); if (G1ConcRegionFreeingVerbose) { gclog_or_tty->print_cr("G1ConcRegionFreeing [region alloc] : " "allocated "HR_FORMAT" from secondary_free_list", @@ -562,7 +554,7 @@ return NULL; } -HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool do_expand) { +HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool is_old, bool do_expand) { assert(!isHumongous(word_size) || word_size <= HeapRegion::GrainWords, "the only time we use this to allocate a humongous region is " "when we are allocating a single humongous region"); @@ -574,19 +566,21 @@ gclog_or_tty->print_cr("G1ConcRegionFreeing [region alloc] : " "forced to look at the secondary_free_list"); } - res = new_region_try_secondary_free_list(); + res = new_region_try_secondary_free_list(is_old); if (res != NULL) { return res; } } } - res = _free_list.remove_head_or_null(); + + res = _free_list.remove_region(is_old); + if (res == NULL) { if (G1ConcRegionFreeingVerbose) { gclog_or_tty->print_cr("G1ConcRegionFreeing [region alloc] : " "res == NULL, trying the secondary_free_list"); } - res = new_region_try_secondary_free_list(); + res = new_region_try_secondary_free_list(is_old); } if (res == NULL && do_expand && _expand_heap_after_alloc_failure) { // Currently, only attempts to allocate GC alloc regions set @@ -603,12 +597,9 @@ if (expand(word_size * HeapWordSize)) { // Given that expand() succeeded in expanding the heap, and we // always expand the heap by an amount aligned to the heap - // region size, the free list should in theory not be empty. So - // it would probably be OK to use remove_head(). But the extra - // check for NULL is unlikely to be a performance issue here (we - // just expanded the heap!) so let's just be conservative and - // use remove_head_or_null(). - res = _free_list.remove_head_or_null(); + // region size, the free list should in theory not be empty. + // In either case remove_region() will check for NULL. + res = _free_list.remove_region(is_old); } else { _expand_heap_after_alloc_failure = false; } @@ -626,7 +617,7 @@ // Only one region to allocate, no need to go through the slower // path. The caller will attempt the expansion if this fails, so // let's not try to expand here too. - HeapRegion* hr = new_region(word_size, false /* do_expand */); + HeapRegion* hr = new_region(word_size, true /* is_old */, false /* do_expand */); if (hr != NULL) { first = hr->hrs_index(); } else { @@ -1296,9 +1287,8 @@ print_heap_before_gc(); trace_heap_before_gc(gc_tracer); - size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes(); - - HRSPhaseSetter x(HRSPhaseFullGC); + size_t metadata_prev_used = MetaspaceAux::used_bytes(); + verify_region_sets_optional(); const bool do_clear_all_soft_refs = clear_all_soft_refs || @@ -1575,8 +1565,6 @@ void G1CollectedHeap:: resize_if_necessary_after_full_collection(size_t word_size) { - assert(MinHeapFreeRatio <= MaxHeapFreeRatio, "sanity check"); - // Include the current allocation, if any, and bytes that will be // pre-allocated to support collections, as "used". const size_t used_after_gc = used(); @@ -1930,10 +1918,10 @@ _g1mm(NULL), _refine_cte_cl(NULL), _full_collection(false), - _free_list("Master Free List"), - _secondary_free_list("Secondary Free List"), - _old_set("Old Set"), - _humongous_set("Master Humongous Set"), + _free_list("Master Free List", new MasterFreeRegionListMtSafeChecker()), + _secondary_free_list("Secondary Free List", new SecondaryFreeRegionListMtSafeChecker()), + _old_set("Old Set", false /* humongous */, new OldRegionSetMtSafeChecker()), + _humongous_set("Master Humongous Set", true /* humongous */, new HumongousRegionSetMtSafeChecker()), _free_regions_coming(false), _young_list(new YoungList(this)), _gc_time_stamp(0), @@ -1965,7 +1953,7 @@ int n_queues = MAX2((int)ParallelGCThreads, 1); _task_queues = new RefToScanQueueSet(n_queues); - int n_rem_sets = HeapRegionRemSet::num_par_rem_sets(); + uint n_rem_sets = HeapRegionRemSet::num_par_rem_sets(); assert(n_rem_sets > 0, "Invariant."); _worker_cset_start_region = NEW_C_HEAP_ARRAY(HeapRegion*, n_queues, mtGC); @@ -2081,7 +2069,7 @@ guarantee(HeapRegion::CardsPerRegion < max_cards_per_region, "too many cards per region"); - HeapRegionSet::set_unrealistically_long_length(max_regions() + 1); + FreeRegionList::set_unrealistically_long_length(max_regions() + 1); _bot_shared = new G1BlockOffsetSharedArray(_reserved, heap_word_size(init_byte_size)); @@ -2184,9 +2172,22 @@ // values in the heap have been properly initialized. _g1mm = new G1MonitoringSupport(this); + G1StringDedup::initialize(); + return JNI_OK; } +void G1CollectedHeap::stop() { + // Stop all concurrent threads. We do this to make sure these threads + // do not continue to execute and access resources (e.g. gclog_or_tty) + // that are destroyed during shutdown. + _cg1r->stop(); + _cmThread->stop(); + if (G1StringDedup::is_enabled()) { + G1StringDedup::stop(); + } +} + size_t G1CollectedHeap::conservative_max_heap_alignment() { return HeapRegion::max_region_size(); } @@ -2243,12 +2244,9 @@ // degree of mt discovery false, // Reference discovery is not atomic - &_is_alive_closure_cm, + &_is_alive_closure_cm); // is alive closure // (for efficiency/performance) - true); - // Setting next fields of discovered - // lists requires a barrier. // STW ref processor _ref_processor_stw = @@ -2263,12 +2261,9 @@ // degree of mt discovery true, // Reference discovery is atomic - &_is_alive_closure_stw, + &_is_alive_closure_stw); // is alive closure // (for efficiency/performance) - false); - // Setting next fields of discovered - // lists requires a barrier. } size_t G1CollectedHeap::capacity() const { @@ -2323,7 +2318,7 @@ void G1CollectedHeap::iterate_dirty_card_closure(CardTableEntryClosure* cl, DirtyCardQueue* into_cset_dcq, bool concurrent, - int worker_i) { + uint worker_i) { // Clean cards in the hot card cache G1HotCardCache* hot_card_cache = _cg1r->hot_card_cache(); hot_card_cache->drain(worker_i, g1_rem_set(), into_cset_dcq); @@ -2371,8 +2366,12 @@ }; size_t G1CollectedHeap::recalculate_used() const { + double recalculate_used_start = os::elapsedTime(); + SumUsedClosure blk; heap_region_iterate(&blk); + + g1_policy()->phase_times()->record_evac_fail_recalc_used_time((os::elapsedTime() - recalculate_used_start) * 1000.0); return blk.result(); } @@ -2877,7 +2876,7 @@ // Given the id of a worker, obtain or calculate a suitable // starting region for iterating over the current collection set. -HeapRegion* G1CollectedHeap::start_cset_region_for_worker(int worker_i) { +HeapRegion* G1CollectedHeap::start_cset_region_for_worker(uint worker_i) { assert(get_gc_time_stamp() > 0, "should have been updated by now"); HeapRegion* result = NULL; @@ -3025,7 +3024,17 @@ } size_t G1CollectedHeap::tlab_capacity(Thread* ignored) const { - return HeapRegion::GrainBytes; + return (_g1_policy->young_list_target_length() - young_list()->survivor_length()) * HeapRegion::GrainBytes; +} + +size_t G1CollectedHeap::tlab_used(Thread* ignored) const { + return young_list()->eden_used_bytes(); +} + +// For G1 TLABs should not contain humongous objects, so the maximum TLAB size +// must be smaller than the humongous object limit. +size_t G1CollectedHeap::max_tlab_size() const { + return align_size_down(_humongous_object_threshold_in_words - 1, MinObjAlignment); } size_t G1CollectedHeap::unsafe_max_tlab_alloc(Thread* ignored) const { @@ -3037,11 +3046,11 @@ // humongous objects. HeapRegion* hr = _mutator_alloc_region.get(); - size_t max_tlab_size = _humongous_object_threshold_in_words * wordSize; + size_t max_tlab = max_tlab_size() * wordSize; if (hr == NULL) { - return max_tlab_size; + return max_tlab; } else { - return MIN2(MAX2(hr->free(), (size_t) MinTLABSize), max_tlab_size); + return MIN2(MAX2(hr->free(), (size_t) MinTLABSize), max_tlab); } } @@ -3106,11 +3115,7 @@ return NULL; // keep some compilers happy } -// TODO: VerifyRootsClosure extends OopsInGenClosure so that we can -// pass it as the perm_blk to SharedHeap::process_strong_roots. -// When process_strong_roots stop calling perm_blk->younger_refs_iterate -// we can change this closure to extend the simpler OopClosure. -class VerifyRootsClosure: public OopsInGenClosure { +class VerifyRootsClosure: public OopClosure { private: G1CollectedHeap* _g1h; VerifyOption _vo; @@ -3146,7 +3151,7 @@ void do_oop(narrowOop* p) { do_oop_nv(p); } }; -class G1VerifyCodeRootOopClosure: public OopsInGenClosure { +class G1VerifyCodeRootOopClosure: public OopClosure { G1CollectedHeap* _g1h; OopClosure* _root_cl; nmethod* _nm; @@ -3486,13 +3491,18 @@ if (!silent) gclog_or_tty->print("RemSet "); rem_set()->verify(); + if (G1StringDedup::is_enabled()) { + if (!silent) gclog_or_tty->print("StrDedup "); + G1StringDedup::verify(); + } + if (failures) { gclog_or_tty->print_cr("Heap:"); // It helps to have the per-region information in the output to // help us track down what went wrong. This is why we call // print_extended_on() instead of print_on(). print_extended_on(gclog_or_tty); - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); #ifndef PRODUCT if (VerifyDuringGC && G1VerifyDuringGCPrintReachable) { concurrent_mark()->print_reachable("at-verification-failure", @@ -3503,8 +3513,13 @@ } guarantee(!failures, "there should not have been any failures"); } else { - if (!silent) - gclog_or_tty->print("(SKIPPING roots, heapRegionSets, heapRegions, remset) "); + if (!silent) { + gclog_or_tty->print("(SKIPPING Roots, HeapRegionSets, HeapRegions, RemSet"); + if (G1StringDedup::is_enabled()) { + gclog_or_tty->print(", StrDedup"); + } + gclog_or_tty->print(") "); + } } } @@ -3546,6 +3561,29 @@ } }; +bool G1CollectedHeap::is_obj_dead_cond(const oop obj, + const HeapRegion* hr, + const VerifyOption vo) const { + switch (vo) { + case VerifyOption_G1UsePrevMarking: return is_obj_dead(obj, hr); + case VerifyOption_G1UseNextMarking: return is_obj_ill(obj, hr); + case VerifyOption_G1UseMarkWord: return !obj->is_gc_marked(); + default: ShouldNotReachHere(); + } + return false; // keep some compilers happy +} + +bool G1CollectedHeap::is_obj_dead_cond(const oop obj, + const VerifyOption vo) const { + switch (vo) { + case VerifyOption_G1UsePrevMarking: return is_obj_dead(obj); + case VerifyOption_G1UseNextMarking: return is_obj_ill(obj); + case VerifyOption_G1UseMarkWord: return !obj->is_gc_marked(); + default: ShouldNotReachHere(); + } + return false; // keep some compilers happy +} + void G1CollectedHeap::print_on(outputStream* st) const { st->print(" %-20s", "garbage-first heap"); st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", @@ -3597,6 +3635,9 @@ st->cr(); _cm->print_worker_threads_on(st); _cg1r->print_worker_threads_on(st); + if (G1StringDedup::is_enabled()) { + G1StringDedup::print_worker_threads_on(st); + } } void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const { @@ -3605,6 +3646,9 @@ } tc->do_thread(_cmThread); _cg1r->threads_do(tc); + if (G1StringDedup::is_enabled()) { + G1StringDedup::threads_do(tc); + } } void G1CollectedHeap::print_tracing_info() const { @@ -3652,7 +3696,7 @@ PrintRSetsClosure(const char* msg) : _msg(msg), _occupied_sum(0) { gclog_or_tty->cr(); gclog_or_tty->print_cr("========================================"); - gclog_or_tty->print_cr(msg); + gclog_or_tty->print_cr("%s", msg); gclog_or_tty->cr(); } @@ -3684,6 +3728,7 @@ // always_do_update_barrier = false; assert(InlineCacheBuffer::is_empty(), "should have cleaned up ICBuffer"); // Fill TLAB's and such + accumulate_statistics_all_tlabs(); ensure_parsability(true); if (G1SummarizeRSetStats && (G1SummarizeRSetStatsPeriod > 0) && @@ -3708,6 +3753,8 @@ "derived pointer present")); // always_do_update_barrier = true; + resize_all_tlabs(); + // We have just completed a GC. Update the soft reference // policy with the new heap occupancy Universe::update_heap_info_at_gc(); @@ -3908,7 +3955,6 @@ print_heap_before_gc(); trace_heap_before_gc(_gc_tracer_stw); - HRSPhaseSetter x(HRSPhaseEvacuation); verify_region_sets_optional(); verify_dirty_young_regions(); @@ -4407,6 +4453,8 @@ void G1CollectedHeap::remove_self_forwarding_pointers() { assert(check_cset_heap_region_claim_values(HeapRegion::InitialClaimValue), "sanity"); + double remove_self_forwards_start = os::elapsedTime(); + G1ParRemoveSelfForwardPtrsTask rsfp_task(this); if (G1CollectedHeap::use_parallel_gc_threads()) { @@ -4434,6 +4482,8 @@ } _objs_with_preserved_marks.clear(true); _preserved_marks_of_objs.clear(true); + + g1_policy()->phase_times()->record_evac_fail_remove_self_forwards((os::elapsedTime() - remove_self_forwards_start) * 1000.0); } void G1CollectedHeap::push_on_evac_failure_scan_stack(oop obj) { @@ -4555,7 +4605,7 @@ G1ParGCAllocBuffer::G1ParGCAllocBuffer(size_t gclab_word_size) : ParGCAllocBuffer(gclab_word_size), _retired(false) { } -G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num) +G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num, ReferenceProcessor* rp) : _g1h(g1h), _refs(g1h->task_queue(queue_num)), _dcq(&g1h->dirty_card_queue_set()), @@ -4565,7 +4615,7 @@ _term_attempts(0), _surviving_alloc_buffer(g1h->desired_plab_sz(GCAllocForSurvived)), _tenured_alloc_buffer(g1h->desired_plab_sz(GCAllocForTenured)), - _age_table(false), + _age_table(false), _scanner(g1h, this, rp), _strong_roots_time(0), _term_time(0), _alloc_buffer_waste(0), _undo_waste(0) { // we allocate G1YoungSurvRateNumRegions plus one entries, since @@ -4655,9 +4705,7 @@ #endif // ASSERT void G1ParScanThreadState::trim_queue() { - assert(_evac_cl != NULL, "not set"); assert(_evac_failure_cl != NULL, "not set"); - assert(_partial_scan_cl != NULL, "not set"); StarTask ref; do { @@ -4674,14 +4722,10 @@ G1ParClosureSuper::G1ParClosureSuper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : - _g1(g1), _g1_rem(_g1->g1_rem_set()), _cm(_g1->concurrent_mark()), - _par_scan_state(par_scan_state), - _worker_id(par_scan_state->queue_num()), - _during_initial_mark(_g1->g1_policy()->during_initial_mark_pause()), - _mark_in_progress(_g1->mark_in_progress()) { } - -template -void G1ParCopyClosure::mark_object(oop obj) { + _g1(g1), _par_scan_state(par_scan_state), + _worker_id(par_scan_state->queue_num()) { } + +void G1ParCopyHelper::mark_object(oop obj) { #ifdef ASSERT HeapRegion* hr = _g1->heap_region_containing(obj); assert(hr != NULL, "sanity"); @@ -4692,9 +4736,7 @@ _cm->grayRoot(obj, (size_t) obj->size(), _worker_id); } -template -void G1ParCopyClosure - ::mark_forwarded_object(oop from_obj, oop to_obj) { +void G1ParCopyHelper::mark_forwarded_object(oop from_obj, oop to_obj) { #ifdef ASSERT assert(from_obj->is_forwarded(), "from obj should be forwarded"); assert(from_obj->forwardee() == to_obj, "to obj should be the forwardee"); @@ -4716,27 +4758,25 @@ _cm->grayRoot(to_obj, (size_t) from_obj->size(), _worker_id); } -template -oop G1ParCopyClosure - ::copy_to_survivor_space(oop old) { +oop G1ParScanThreadState::copy_to_survivor_space(oop const old) { size_t word_sz = old->size(); - HeapRegion* from_region = _g1->heap_region_containing_raw(old); + HeapRegion* from_region = _g1h->heap_region_containing_raw(old); // +1 to make the -1 indexes valid... int young_index = from_region->young_index_in_cset()+1; assert( (from_region->is_young() && young_index > 0) || (!from_region->is_young() && young_index == 0), "invariant" ); - G1CollectorPolicy* g1p = _g1->g1_policy(); + G1CollectorPolicy* g1p = _g1h->g1_policy(); markOop m = old->mark(); int age = m->has_displaced_mark_helper() ? m->displaced_mark_helper()->age() : m->age(); GCAllocPurpose alloc_purpose = g1p->evacuation_destination(from_region, age, word_sz); - HeapWord* obj_ptr = _par_scan_state->allocate(alloc_purpose, word_sz); + HeapWord* obj_ptr = allocate(alloc_purpose, word_sz); #ifndef PRODUCT // Should this evacuation fail? - if (_g1->evacuation_should_fail()) { + if (_g1h->evacuation_should_fail()) { if (obj_ptr != NULL) { - _par_scan_state->undo_allocation(alloc_purpose, obj_ptr, word_sz); + undo_allocation(alloc_purpose, obj_ptr, word_sz); obj_ptr = NULL; } } @@ -4745,7 +4785,7 @@ if (obj_ptr == NULL) { // This will either forward-to-self, or detect that someone else has // installed a forwarding pointer. - return _g1->handle_evacuation_failure_par(_par_scan_state, old); + return _g1h->handle_evacuation_failure_par(this, old); } oop obj = oop(obj_ptr); @@ -4756,6 +4796,12 @@ oop forward_ptr = old->forward_to_atomic(obj); if (forward_ptr == NULL) { Copy::aligned_disjoint_words((HeapWord*) old, obj_ptr, word_sz); + + // alloc_purpose is just a hint to allocate() above, recheck the type of region + // we actually allocated from and update alloc_purpose accordingly + HeapRegion* to_region = _g1h->heap_region_containing_raw(obj_ptr); + alloc_purpose = to_region->is_young() ? GCAllocForSurvived : GCAllocForTenured; + if (g1p->track_object_age(alloc_purpose)) { // We could simply do obj->incr_age(). However, this causes a // performance issue. obj->incr_age() will first check whether @@ -4778,12 +4824,19 @@ m = m->incr_age(); obj->set_mark(m); } - _par_scan_state->age_table()->add(obj, word_sz); + age_table()->add(obj, word_sz); } else { obj->set_mark(m); } - size_t* surv_young_words = _par_scan_state->surviving_young_words(); + if (G1StringDedup::is_enabled()) { + G1StringDedup::enqueue_from_evacuation(from_region->is_young(), + to_region->is_young(), + queue_num(), + obj); + } + + size_t* surv_young_words = surviving_young_words(); surv_young_words[young_index] += word_sz; if (obj->is_objArray() && arrayOop(obj)->length() >= ParGCArrayScanChunk) { @@ -4792,15 +4845,15 @@ // length field of the from-space object. arrayOop(obj)->set_length(0); oop* old_p = set_partial_array_mask(old); - _par_scan_state->push_on_queue(old_p); + push_on_queue(old_p); } else { // No point in using the slower heap_region_containing() method, // given that we know obj is in the heap. - _scanner.set_region(_g1->heap_region_containing_raw(obj)); + _scanner.set_region(_g1h->heap_region_containing_raw(obj)); obj->oop_iterate_backwards(&_scanner); } } else { - _par_scan_state->undo_allocation(alloc_purpose, obj_ptr, word_sz); + undo_allocation(alloc_purpose, obj_ptr, word_sz); obj = forward_ptr; } return obj; @@ -4813,23 +4866,25 @@ } } -template +template template -void G1ParCopyClosure -::do_oop_work(T* p) { - oop obj = oopDesc::load_decode_heap_oop(p); - assert(barrier != G1BarrierRS || obj != NULL, - "Precondition: G1BarrierRS implies obj is non-NULL"); +void G1ParCopyClosure::do_oop_work(T* p) { + T heap_oop = oopDesc::load_heap_oop(p); + + if (oopDesc::is_null(heap_oop)) { + return; + } + + oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); assert(_worker_id == _par_scan_state->queue_num(), "sanity"); - // here the null check is implicit in the cset_fast_test() test if (_g1->in_cset_fast_test(obj)) { oop forwardee; if (obj->is_forwarded()) { forwardee = obj->forwardee(); } else { - forwardee = copy_to_survivor_space(obj); + forwardee = _par_scan_state->copy_to_survivor_space(obj); } assert(forwardee != NULL, "forwardee should not be NULL"); oopDesc::encode_store_heap_oop(p, forwardee); @@ -4839,81 +4894,25 @@ mark_forwarded_object(obj, forwardee); } - // When scanning the RS, we only care about objs in CS. - if (barrier == G1BarrierRS) { - _par_scan_state->update_rs(_from, p, _worker_id); - } else if (barrier == G1BarrierKlass) { + if (barrier == G1BarrierKlass) { do_klass_barrier(p, forwardee); } } else { // The object is not in collection set. If we're a root scanning // closure during an initial mark pause (i.e. do_mark_object will // be true) then attempt to mark the object. - if (do_mark_object && _g1->is_in_g1_reserved(obj)) { + if (do_mark_object) { mark_object(obj); } } - if (barrier == G1BarrierEvac && obj != NULL) { + if (barrier == G1BarrierEvac) { _par_scan_state->update_rs(_from, p, _worker_id); } - - if (do_gen_barrier && obj != NULL) { - par_do_barrier(p); - } -} - -template void G1ParCopyClosure::do_oop_work(oop* p); -template void G1ParCopyClosure::do_oop_work(narrowOop* p); - -template void G1ParScanPartialArrayClosure::do_oop_nv(T* p) { - assert(has_partial_array_mask(p), "invariant"); - oop from_obj = clear_partial_array_mask(p); - - assert(Universe::heap()->is_in_reserved(from_obj), "must be in heap."); - assert(from_obj->is_objArray(), "must be obj array"); - objArrayOop from_obj_array = objArrayOop(from_obj); - // The from-space object contains the real length. - int length = from_obj_array->length(); - - assert(from_obj->is_forwarded(), "must be forwarded"); - oop to_obj = from_obj->forwardee(); - assert(from_obj != to_obj, "should not be chunking self-forwarded objects"); - objArrayOop to_obj_array = objArrayOop(to_obj); - // We keep track of the next start index in the length field of the - // to-space object. - int next_index = to_obj_array->length(); - assert(0 <= next_index && next_index < length, - err_msg("invariant, next index: %d, length: %d", next_index, length)); - - int start = next_index; - int end = length; - int remainder = end - start; - // We'll try not to push a range that's smaller than ParGCArrayScanChunk. - if (remainder > 2 * ParGCArrayScanChunk) { - end = start + ParGCArrayScanChunk; - to_obj_array->set_length(end); - // Push the remainder before we process the range in case another - // worker has run out of things to do and can steal it. - oop* from_obj_p = set_partial_array_mask(from_obj); - _par_scan_state->push_on_queue(from_obj_p); - } else { - assert(length == end, "sanity"); - // We'll process the final range for this object. Restore the length - // so that the heap remains parsable in case of evacuation failure. - to_obj_array->set_length(end); - } - _scanner.set_region(_g1->heap_region_containing_raw(to_obj)); - // Process indexes [start,end). It will also process the header - // along with the first chunk (i.e., the chunk with start == 0). - // Note that at this point the length field of to_obj_array is not - // correct given that we are using it to keep track of the next - // start index. oop_iterate_range() (thankfully!) ignores the length - // field and only relies on the start / end parameters. It does - // however return the size of the object which will be incorrect. So - // we have to ignore it even if we wanted to use it. - to_obj_array->oop_iterate_range(&_scanner, start, end); -} +} + +template void G1ParCopyClosure::do_oop_work(oop* p); +template void G1ParCopyClosure::do_oop_work(narrowOop* p); class G1ParEvacuateFollowersClosure : public VoidClosure { protected: @@ -5055,14 +5054,10 @@ ReferenceProcessor* rp = _g1h->ref_processor_stw(); - G1ParScanThreadState pss(_g1h, worker_id); - G1ParScanHeapEvacClosure scan_evac_cl(_g1h, &pss, rp); + G1ParScanThreadState pss(_g1h, worker_id, rp); G1ParScanHeapEvacFailureClosure evac_failure_cl(_g1h, &pss, rp); - G1ParScanPartialArrayClosure partial_scan_cl(_g1h, &pss, rp); - - pss.set_evac_closure(&scan_evac_cl); + pss.set_evac_failure_closure(&evac_failure_cl); - pss.set_partial_scan_closure(&partial_scan_cl); G1ParScanExtRootClosure only_scan_root_cl(_g1h, &pss, rp); G1ParScanMetadataClosure only_scan_metadata_cl(_g1h, &pss, rp); @@ -5140,7 +5135,7 @@ OopClosure* scan_non_heap_roots, OopsInHeapRegionClosure* scan_rs, G1KlassScanClosure* scan_klasses, - int worker_i) { + uint worker_i) { // First scan the strong roots double ext_roots_start = os::elapsedTime(); @@ -5222,6 +5217,129 @@ SharedHeap::process_weak_roots(root_closure, &roots_in_blobs); } +class G1StringSymbolTableUnlinkTask : public AbstractGangTask { +private: + BoolObjectClosure* _is_alive; + int _initial_string_table_size; + int _initial_symbol_table_size; + + bool _process_strings; + int _strings_processed; + int _strings_removed; + + bool _process_symbols; + int _symbols_processed; + int _symbols_removed; + + bool _do_in_parallel; +public: + G1StringSymbolTableUnlinkTask(BoolObjectClosure* is_alive, bool process_strings, bool process_symbols) : + AbstractGangTask("Par String/Symbol table unlink"), _is_alive(is_alive), + _do_in_parallel(G1CollectedHeap::use_parallel_gc_threads()), + _process_strings(process_strings), _strings_processed(0), _strings_removed(0), + _process_symbols(process_symbols), _symbols_processed(0), _symbols_removed(0) { + + _initial_string_table_size = StringTable::the_table()->table_size(); + _initial_symbol_table_size = SymbolTable::the_table()->table_size(); + if (process_strings) { + StringTable::clear_parallel_claimed_index(); + } + if (process_symbols) { + SymbolTable::clear_parallel_claimed_index(); + } + } + + ~G1StringSymbolTableUnlinkTask() { + guarantee(!_process_strings || !_do_in_parallel || StringTable::parallel_claimed_index() >= _initial_string_table_size, + err_msg("claim value "INT32_FORMAT" after unlink less than initial string table size "INT32_FORMAT, + StringTable::parallel_claimed_index(), _initial_string_table_size)); + guarantee(!_process_symbols || !_do_in_parallel || SymbolTable::parallel_claimed_index() >= _initial_symbol_table_size, + err_msg("claim value "INT32_FORMAT" after unlink less than initial symbol table size "INT32_FORMAT, + SymbolTable::parallel_claimed_index(), _initial_symbol_table_size)); + } + + void work(uint worker_id) { + if (_do_in_parallel) { + int strings_processed = 0; + int strings_removed = 0; + int symbols_processed = 0; + int symbols_removed = 0; + if (_process_strings) { + StringTable::possibly_parallel_unlink(_is_alive, &strings_processed, &strings_removed); + Atomic::add(strings_processed, &_strings_processed); + Atomic::add(strings_removed, &_strings_removed); + } + if (_process_symbols) { + SymbolTable::possibly_parallel_unlink(&symbols_processed, &symbols_removed); + Atomic::add(symbols_processed, &_symbols_processed); + Atomic::add(symbols_removed, &_symbols_removed); + } + } else { + if (_process_strings) { + StringTable::unlink(_is_alive, &_strings_processed, &_strings_removed); + } + if (_process_symbols) { + SymbolTable::unlink(&_symbols_processed, &_symbols_removed); + } + } + } + + size_t strings_processed() const { return (size_t)_strings_processed; } + size_t strings_removed() const { return (size_t)_strings_removed; } + + size_t symbols_processed() const { return (size_t)_symbols_processed; } + size_t symbols_removed() const { return (size_t)_symbols_removed; } +}; + +void G1CollectedHeap::unlink_string_and_symbol_table(BoolObjectClosure* is_alive, + bool process_strings, bool process_symbols) { + uint n_workers = (G1CollectedHeap::use_parallel_gc_threads() ? + _g1h->workers()->active_workers() : 1); + + G1StringSymbolTableUnlinkTask g1_unlink_task(is_alive, process_strings, process_symbols); + if (G1CollectedHeap::use_parallel_gc_threads()) { + set_par_threads(n_workers); + workers()->run_task(&g1_unlink_task); + set_par_threads(0); + } else { + g1_unlink_task.work(0); + } + if (G1TraceStringSymbolTableScrubbing) { + gclog_or_tty->print_cr("Cleaned string and symbol table, " + "strings: "SIZE_FORMAT" processed, "SIZE_FORMAT" removed, " + "symbols: "SIZE_FORMAT" processed, "SIZE_FORMAT" removed", + g1_unlink_task.strings_processed(), g1_unlink_task.strings_removed(), + g1_unlink_task.symbols_processed(), g1_unlink_task.symbols_removed()); + } + + if (G1StringDedup::is_enabled()) { + G1StringDedup::unlink(is_alive); + } +} + +class RedirtyLoggedCardTableEntryFastClosure : public CardTableEntryClosure { +public: + bool do_card_ptr(jbyte* card_ptr, uint worker_i) { + *card_ptr = CardTableModRefBS::dirty_card_val(); + return true; + } +}; + +void G1CollectedHeap::redirty_logged_cards() { + guarantee(G1DeferredRSUpdate, "Must only be called when using deferred RS updates."); + double redirty_logged_cards_start = os::elapsedTime(); + + RedirtyLoggedCardTableEntryFastClosure redirty; + dirty_card_queue_set().set_closure(&redirty); + dirty_card_queue_set().apply_closure_to_all_completed_buffers(); + + DirtyCardQueueSet& dcq = JavaThread::dirty_card_queue_set(); + dcq.merge_bufferlists(&dirty_card_queue_set()); + assert(dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed"); + + g1_policy()->phase_times()->record_redirty_logged_cards_time_ms((os::elapsedTime() - redirty_logged_cards_start) * 1000.0); +} + // Weak Reference Processing support // An always "is_alive" closure that is used to preserve referents. @@ -5312,7 +5430,7 @@ if (_g1h->is_in_g1_reserved(p)) { _par_scan_state->push_on_queue(p); } else { - assert(!ClassLoaderDataGraph::contains((address)p), + assert(!Metaspace::contains((const void*)p), err_msg("Otherwise need to call _copy_metadata_obj_cl->do_oop(p) " PTR_FORMAT, p)); _copy_non_heap_obj_cl->do_oop(p); @@ -5402,15 +5520,10 @@ G1STWIsAliveClosure is_alive(_g1h); - G1ParScanThreadState pss(_g1h, worker_id); - - G1ParScanHeapEvacClosure scan_evac_cl(_g1h, &pss, NULL); + G1ParScanThreadState pss(_g1h, worker_id, NULL); G1ParScanHeapEvacFailureClosure evac_failure_cl(_g1h, &pss, NULL); - G1ParScanPartialArrayClosure partial_scan_cl(_g1h, &pss, NULL); - - pss.set_evac_closure(&scan_evac_cl); + pss.set_evac_failure_closure(&evac_failure_cl); - pss.set_partial_scan_closure(&partial_scan_cl); G1ParScanExtRootClosure only_copy_non_heap_cl(_g1h, &pss, NULL); G1ParScanMetadataClosure only_copy_metadata_cl(_g1h, &pss, NULL); @@ -5514,14 +5627,10 @@ ResourceMark rm; HandleMark hm; - G1ParScanThreadState pss(_g1h, worker_id); - G1ParScanHeapEvacClosure scan_evac_cl(_g1h, &pss, NULL); + G1ParScanThreadState pss(_g1h, worker_id, NULL); G1ParScanHeapEvacFailureClosure evac_failure_cl(_g1h, &pss, NULL); - G1ParScanPartialArrayClosure partial_scan_cl(_g1h, &pss, NULL); - - pss.set_evac_closure(&scan_evac_cl); + pss.set_evac_failure_closure(&evac_failure_cl); - pss.set_partial_scan_closure(&partial_scan_cl); assert(pss.refs()->is_empty(), "both queue and overflow should be empty"); @@ -5640,18 +5749,14 @@ // JNI refs. // Use only a single queue for this PSS. - G1ParScanThreadState pss(this, 0); + G1ParScanThreadState pss(this, 0, NULL); // We do not embed a reference processor in the copying/scanning // closures while we're actually processing the discovered // reference objects. - G1ParScanHeapEvacClosure scan_evac_cl(this, &pss, NULL); G1ParScanHeapEvacFailureClosure evac_failure_cl(this, &pss, NULL); - G1ParScanPartialArrayClosure partial_scan_cl(this, &pss, NULL); - - pss.set_evac_closure(&scan_evac_cl); + pss.set_evac_failure_closure(&evac_failure_cl); - pss.set_partial_scan_closure(&partial_scan_cl); assert(pss.refs()->is_empty(), "pre-condition"); @@ -5833,6 +5938,9 @@ G1STWIsAliveClosure is_alive(this); G1KeepAliveClosure keep_alive(this); JNIHandles::weak_oops_do(&is_alive, &keep_alive); + if (G1StringDedup::is_enabled()) { + G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive); + } } release_gc_alloc_regions(n_workers, evacuation_info); @@ -5850,6 +5958,8 @@ // strong code roots for a particular heap region. migrate_strong_code_roots(); + purge_code_root_memory(); + if (g1_policy()->during_initial_mark_pause()) { // Reset the claim values set during marking the strong code roots reset_heap_region_claim_values(); @@ -5876,41 +5986,15 @@ enqueue_discovered_references(n_workers); if (G1DeferredRSUpdate) { - RedirtyLoggedCardTableEntryFastClosure redirty; - dirty_card_queue_set().set_closure(&redirty); - dirty_card_queue_set().apply_closure_to_all_completed_buffers(); - - DirtyCardQueueSet& dcq = JavaThread::dirty_card_queue_set(); - dcq.merge_bufferlists(&dirty_card_queue_set()); - assert(dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed"); + redirty_logged_cards(); } COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); } -void G1CollectedHeap::free_region_if_empty(HeapRegion* hr, - size_t* pre_used, - FreeRegionList* free_list, - OldRegionSet* old_proxy_set, - HumongousRegionSet* humongous_proxy_set, - HRRSCleanupTask* hrrs_cleanup_task, - bool par) { - if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young()) { - if (hr->isHumongous()) { - assert(hr->startsHumongous(), "we should only see starts humongous"); - free_humongous_region(hr, pre_used, free_list, humongous_proxy_set, par); - } else { - _old_set.remove_with_proxy(hr, old_proxy_set); - free_region(hr, pre_used, free_list, par); - } - } else { - hr->rem_set()->do_cleanup_work(hrrs_cleanup_task); - } -} - void G1CollectedHeap::free_region(HeapRegion* hr, - size_t* pre_used, FreeRegionList* free_list, - bool par) { + bool par, + bool locked) { assert(!hr->isHumongous(), "this is only for non-humongous regions"); assert(!hr->is_empty(), "the region should not be empty"); assert(free_list != NULL, "pre-condition"); @@ -5921,70 +6005,56 @@ if (!hr->is_young()) { _cg1r->hot_card_cache()->reset_card_counts(hr); } - *pre_used += hr->used(); - hr->hr_clear(par, true /* clear_space */); - free_list->add_as_head(hr); + hr->hr_clear(par, true /* clear_space */, locked /* locked */); + free_list->add_ordered(hr); } void G1CollectedHeap::free_humongous_region(HeapRegion* hr, - size_t* pre_used, FreeRegionList* free_list, - HumongousRegionSet* humongous_proxy_set, bool par) { assert(hr->startsHumongous(), "this is only for starts humongous regions"); assert(free_list != NULL, "pre-condition"); - assert(humongous_proxy_set != NULL, "pre-condition"); - - size_t hr_used = hr->used(); + size_t hr_capacity = hr->capacity(); - size_t hr_pre_used = 0; - _humongous_set.remove_with_proxy(hr, humongous_proxy_set); // We need to read this before we make the region non-humongous, // otherwise the information will be gone. uint last_index = hr->last_hc_index(); hr->set_notHumongous(); - free_region(hr, &hr_pre_used, free_list, par); + free_region(hr, free_list, par); uint i = hr->hrs_index() + 1; while (i < last_index) { HeapRegion* curr_hr = region_at(i); assert(curr_hr->continuesHumongous(), "invariant"); curr_hr->set_notHumongous(); - free_region(curr_hr, &hr_pre_used, free_list, par); + free_region(curr_hr, free_list, par); i += 1; } - assert(hr_pre_used == hr_used, - err_msg("hr_pre_used: "SIZE_FORMAT" and hr_used: "SIZE_FORMAT" " - "should be the same", hr_pre_used, hr_used)); - *pre_used += hr_pre_used; -} - -void G1CollectedHeap::update_sets_after_freeing_regions(size_t pre_used, - FreeRegionList* free_list, - OldRegionSet* old_proxy_set, - HumongousRegionSet* humongous_proxy_set, - bool par) { - if (pre_used > 0) { - Mutex* lock = (par) ? ParGCRareEvent_lock : NULL; - MutexLockerEx x(lock, Mutex::_no_safepoint_check_flag); - assert(_summary_bytes_used >= pre_used, - err_msg("invariant: _summary_bytes_used: "SIZE_FORMAT" " - "should be >= pre_used: "SIZE_FORMAT, - _summary_bytes_used, pre_used)); - _summary_bytes_used -= pre_used; - } - if (free_list != NULL && !free_list->is_empty()) { +} + +void G1CollectedHeap::remove_from_old_sets(const HeapRegionSetCount& old_regions_removed, + const HeapRegionSetCount& humongous_regions_removed) { + if (old_regions_removed.length() > 0 || humongous_regions_removed.length() > 0) { + MutexLockerEx x(OldSets_lock, Mutex::_no_safepoint_check_flag); + _old_set.bulk_remove(old_regions_removed); + _humongous_set.bulk_remove(humongous_regions_removed); + } + +} + +void G1CollectedHeap::prepend_to_freelist(FreeRegionList* list) { + assert(list != NULL, "list can't be null"); + if (!list->is_empty()) { MutexLockerEx x(FreeList_lock, Mutex::_no_safepoint_check_flag); - _free_list.add_as_head(free_list); - } - if (old_proxy_set != NULL && !old_proxy_set->is_empty()) { - MutexLockerEx x(OldSets_lock, Mutex::_no_safepoint_check_flag); - _old_set.update_from_proxy(old_proxy_set); - } - if (humongous_proxy_set != NULL && !humongous_proxy_set->is_empty()) { - MutexLockerEx x(OldSets_lock, Mutex::_no_safepoint_check_flag); - _humongous_set.update_from_proxy(humongous_proxy_set); - } + _free_list.add_ordered(list); + } +} + +void G1CollectedHeap::decrement_summary_bytes(size_t bytes) { + assert(_summary_bytes_used >= bytes, + err_msg("invariant: _summary_bytes_used: "SIZE_FORMAT" should be >= bytes: "SIZE_FORMAT, + _summary_bytes_used, bytes)); + _summary_bytes_used -= bytes; } class G1ParCleanupCTTask : public AbstractGangTask { @@ -6144,7 +6214,7 @@ } } - rs_lengths += cur->rem_set()->occupied(); + rs_lengths += cur->rem_set()->occupied_locked(); HeapRegion* next = cur->next_in_collection_set(); assert(cur->in_collection_set(), "bad CS"); @@ -6177,7 +6247,8 @@ // And the region is empty. assert(!used_mr.is_empty(), "Should not have empty regions in a CS."); - free_region(cur, &pre_used, &local_free_list, false /* par */); + pre_used += cur->used(); + free_region(cur, &local_free_list, false /* par */, true /* locked */); } else { cur->uninstall_surv_rate_group(); if (cur->is_young()) { @@ -6205,10 +6276,8 @@ young_time_ms += elapsed_ms; } - update_sets_after_freeing_regions(pre_used, &local_free_list, - NULL /* old_proxy_set */, - NULL /* humongous_proxy_set */, - false /* par */); + prepend_to_freelist(&local_free_list); + decrement_summary_bytes(pre_used); policy->phase_times()->record_young_free_cset_time_ms(young_time_ms); policy->phase_times()->record_non_young_free_cset_time_ms(non_young_time_ms); } @@ -6320,10 +6389,10 @@ class TearDownRegionSetsClosure : public HeapRegionClosure { private: - OldRegionSet *_old_set; + HeapRegionSet *_old_set; public: - TearDownRegionSetsClosure(OldRegionSet* old_set) : _old_set(old_set) { } + TearDownRegionSetsClosure(HeapRegionSet* old_set) : _old_set(old_set) { } bool doHeapRegion(HeapRegion* r) { if (r->is_empty()) { @@ -6352,9 +6421,10 @@ TearDownRegionSetsClosure cl(&_old_set); heap_region_iterate(&cl); - // Need to do this after the heap iteration to be able to - // recognize the young regions and ignore them during the iteration. - _young_list->empty_list(); + // Note that emptying the _young_list is postponed and instead done as + // the first step when rebuilding the regions sets again. The reason for + // this is that during a full GC string deduplication needs to know if + // a collected region was young or old when the full GC was initiated. } _free_list.remove_all(); } @@ -6362,13 +6432,13 @@ class RebuildRegionSetsClosure : public HeapRegionClosure { private: bool _free_list_only; - OldRegionSet* _old_set; + HeapRegionSet* _old_set; FreeRegionList* _free_list; size_t _total_used; public: RebuildRegionSetsClosure(bool free_list_only, - OldRegionSet* old_set, FreeRegionList* free_list) : + HeapRegionSet* old_set, FreeRegionList* free_list) : _free_list_only(free_list_only), _old_set(old_set), _free_list(free_list), _total_used(0) { assert(_free_list->is_empty(), "pre-condition"); @@ -6408,6 +6478,10 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) { assert_at_safepoint(true /* should_be_vm_thread */); + if (!free_list_only) { + _young_list->empty_list(); + } + RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_free_list); heap_region_iterate(&cl); @@ -6443,6 +6517,7 @@ bool young_list_full = g1_policy()->is_young_list_full(); if (force || !young_list_full) { HeapRegion* new_alloc_region = new_region(word_size, + false /* is_old */, false /* do_expand */); if (new_alloc_region != NULL) { set_region_short_lived_locked(new_alloc_region); @@ -6501,14 +6576,16 @@ assert(FreeList_lock->owned_by_self(), "pre-condition"); if (count < g1_policy()->max_regions(ap)) { + bool survivor = (ap == GCAllocForSurvived); HeapRegion* new_alloc_region = new_region(word_size, + !survivor, true /* do_expand */); if (new_alloc_region != NULL) { // We really only need to do this for old regions given that we // should never scan survivors. But it doesn't hurt to do it // for survivors too. new_alloc_region->set_saved_mark(); - if (ap == GCAllocForSurvived) { + if (survivor) { new_alloc_region->set_survivor(); _hr_printer.alloc(new_alloc_region, G1HRPrinter::Survivor); } else { @@ -6565,23 +6642,22 @@ class VerifyRegionListsClosure : public HeapRegionClosure { private: - FreeRegionList* _free_list; - OldRegionSet* _old_set; - HumongousRegionSet* _humongous_set; - uint _region_count; + HeapRegionSet* _old_set; + HeapRegionSet* _humongous_set; + FreeRegionList* _free_list; public: - VerifyRegionListsClosure(OldRegionSet* old_set, - HumongousRegionSet* humongous_set, + HeapRegionSetCount _old_count; + HeapRegionSetCount _humongous_count; + HeapRegionSetCount _free_count; + + VerifyRegionListsClosure(HeapRegionSet* old_set, + HeapRegionSet* humongous_set, FreeRegionList* free_list) : - _old_set(old_set), _humongous_set(humongous_set), - _free_list(free_list), _region_count(0) { } - - uint region_count() { return _region_count; } + _old_set(old_set), _humongous_set(humongous_set), _free_list(free_list), + _old_count(), _humongous_count(), _free_count(){ } bool doHeapRegion(HeapRegion* hr) { - _region_count += 1; - if (hr->continuesHumongous()) { return false; } @@ -6589,14 +6665,31 @@ if (hr->is_young()) { // TODO } else if (hr->startsHumongous()) { - _humongous_set->verify_next_region(hr); + assert(hr->containing_set() == _humongous_set, err_msg("Heap region %u is starts humongous but not in humongous set.", hr->region_num())); + _humongous_count.increment(1u, hr->capacity()); } else if (hr->is_empty()) { - _free_list->verify_next_region(hr); + assert(hr->containing_set() == _free_list, err_msg("Heap region %u is empty but not on the free list.", hr->region_num())); + _free_count.increment(1u, hr->capacity()); } else { - _old_set->verify_next_region(hr); + assert(hr->containing_set() == _old_set, err_msg("Heap region %u is old but not in the old set.", hr->region_num())); + _old_count.increment(1u, hr->capacity()); } return false; } + + void verify_counts(HeapRegionSet* old_set, HeapRegionSet* humongous_set, FreeRegionList* free_list) { + guarantee(old_set->length() == _old_count.length(), err_msg("Old set count mismatch. Expected %u, actual %u.", old_set->length(), _old_count.length())); + guarantee(old_set->total_capacity_bytes() == _old_count.capacity(), err_msg("Old set capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, + old_set->total_capacity_bytes(), _old_count.capacity())); + + guarantee(humongous_set->length() == _humongous_count.length(), err_msg("Hum set count mismatch. Expected %u, actual %u.", humongous_set->length(), _humongous_count.length())); + guarantee(humongous_set->total_capacity_bytes() == _humongous_count.capacity(), err_msg("Hum set capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, + humongous_set->total_capacity_bytes(), _humongous_count.capacity())); + + guarantee(free_list->length() == _free_count.length(), err_msg("Free list count mismatch. Expected %u, actual %u.", free_list->length(), _free_count.length())); + guarantee(free_list->total_capacity_bytes() == _free_count.capacity(), err_msg("Free list capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, + free_list->total_capacity_bytes(), _free_count.capacity())); + } }; HeapRegion* G1CollectedHeap::new_heap_region(uint hrs_index, @@ -6612,16 +6705,14 @@ assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */); // First, check the explicit lists. - _free_list.verify(); + _free_list.verify_list(); { // Given that a concurrent operation might be adding regions to // the secondary free list we have to take the lock before // verifying it. MutexLockerEx x(SecondaryFreeList_lock, Mutex::_no_safepoint_check_flag); - _secondary_free_list.verify(); - } - _old_set.verify(); - _humongous_set.verify(); + _secondary_free_list.verify_list(); + } // If a concurrent region freeing operation is in progress it will // be difficult to correctly attributed any free regions we come @@ -6644,16 +6735,10 @@ // Finally, make sure that the region accounting in the lists is // consistent with what we see in the heap. - _old_set.verify_start(); - _humongous_set.verify_start(); - _free_list.verify_start(); VerifyRegionListsClosure cl(&_old_set, &_humongous_set, &_free_list); heap_region_iterate(&cl); - - _old_set.verify_end(); - _humongous_set.verify_end(); - _free_list.verify_end(); + cl.verify_counts(&_old_set, &_humongous_set, &_free_list); } // Optimized nmethod scanning @@ -6754,6 +6839,13 @@ g1_policy()->phase_times()->record_strong_code_root_migration_time(migration_time_ms); } +void G1CollectedHeap::purge_code_root_memory() { + double purge_start = os::elapsedTime(); + G1CodeRootSet::purge_chunks(G1CodeRootsChunkCacheKeepPercent); + double purge_time_ms = (os::elapsedTime() - purge_start) * 1000.0; + g1_policy()->phase_times()->record_strong_code_root_purge_time(purge_time_ms); +} + // Mark all the code roots that point into regions *not* in the // collection set. // @@ -6824,7 +6916,7 @@ // Code roots should never be attached to a continuation of a humongous region assert(hrrs->strong_code_roots_list_length() == 0, err_msg("code roots should never be attached to continuations of humongous region "HR_FORMAT - " starting at "HR_FORMAT", but has "INT32_FORMAT, + " starting at "HR_FORMAT", but has "SIZE_FORMAT, HR_FORMAT_PARAMS(hr), HR_FORMAT_PARAMS(hr->humongous_start_region()), hrrs->strong_code_roots_list_length())); return false; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -34,7 +34,7 @@ #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" #include "gc_implementation/g1/heapRegionSeq.hpp" -#include "gc_implementation/g1/heapRegionSets.hpp" +#include "gc_implementation/g1/heapRegionSet.hpp" #include "gc_implementation/shared/hSpaceCounters.hpp" #include "gc_implementation/shared/parGCAllocBuffer.hpp" #include "memory/barrierSet.hpp" @@ -209,7 +209,7 @@ friend class OldGCAllocRegion; // Closures used in implementation. - template + template friend class G1ParCopyClosure; friend class G1IsAliveClosure; friend class G1EvacuateFollowersClosure; @@ -243,18 +243,18 @@ MemRegion _g1_committed; // The master free list. It will satisfy all new region allocations. - MasterFreeRegionList _free_list; + FreeRegionList _free_list; // The secondary free list which contains regions that have been // freed up during the cleanup process. This will be appended to the // master free list when appropriate. - SecondaryFreeRegionList _secondary_free_list; + FreeRegionList _secondary_free_list; // It keeps track of the old regions. - MasterOldRegionSet _old_set; + HeapRegionSet _old_set; // It keeps track of the humongous regions. - MasterHumongousRegionSet _humongous_set; + HeapRegionSet _humongous_set; // The number of regions we could create by expansion. uint _expansion_regions; @@ -497,13 +497,14 @@ // check whether there's anything available on the // secondary_free_list and/or wait for more regions to appear on // that list, if _free_regions_coming is set. - HeapRegion* new_region_try_secondary_free_list(); + HeapRegion* new_region_try_secondary_free_list(bool is_old); // Try to allocate a single non-humongous HeapRegion sufficient for // an allocation of the given word_size. If do_expand is true, // attempt to expand the heap if necessary to satisfy the allocation - // request. - HeapRegion* new_region(size_t word_size, bool do_expand); + // request. If the region is to be used as an old region or for a + // humongous object, set is_old to true. If not, to false. + HeapRegion* new_region(size_t word_size, bool is_old, bool do_expand); // Attempt to satisfy a humongous allocation request of the given // size by finding a contiguous set of free regions of num_regions @@ -606,6 +607,11 @@ // may not be a humongous - it must fit into a single heap region. HeapWord* par_allocate_during_gc(GCAllocPurpose purpose, size_t word_size); + HeapWord* allocate_during_gc_slow(GCAllocPurpose purpose, + HeapRegion* alloc_region, + bool par, + size_t word_size); + // Ensure that no further allocations can happen in "r", bearing in mind // that parallel threads might be attempting allocations. void par_allocate_remaining_space(HeapRegion* r); @@ -703,24 +709,9 @@ } // This is a fast test on whether a reference points into the - // collection set or not. It does not assume that the reference - // points into the heap; if it doesn't, it will return false. - bool in_cset_fast_test(oop obj) { - assert(_in_cset_fast_test != NULL, "sanity"); - if (_g1_committed.contains((HeapWord*) obj)) { - // no need to subtract the bottom of the heap from obj, - // _in_cset_fast_test is biased - uintx index = cast_from_oop(obj) >> HeapRegion::LogOfHRGrainBytes; - bool ret = _in_cset_fast_test[index]; - // let's make sure the result is consistent with what the slower - // test returns - assert( ret || !obj_in_cs(obj), "sanity"); - assert(!ret || obj_in_cs(obj), "sanity"); - return ret; - } else { - return false; - } - } + // collection set or not. Assume that the reference + // points into the heap. + inline bool in_cset_fast_test(oop obj); void clear_cset_fast_test() { assert(_in_cset_fast_test_base != NULL, "sanity"); @@ -760,6 +751,29 @@ G1HRPrinter* hr_printer() { return &_hr_printer; } + // Frees a non-humongous region by initializing its contents and + // adding it to the free list that's passed as a parameter (this is + // usually a local list which will be appended to the master free + // list later). The used bytes of freed regions are accumulated in + // pre_used. If par is true, the region's RSet will not be freed + // up. The assumption is that this will be done later. + // The locked parameter indicates if the caller has already taken + // care of proper synchronization. This may allow some optimizations. + void free_region(HeapRegion* hr, + FreeRegionList* free_list, + bool par, + bool locked = false); + + // Frees a humongous region by collapsing it into individual regions + // and calling free_region() for each of them. The freed regions + // will be added to the free list that's passed as a parameter (this + // is usually a local list which will be appended to the master free + // list later). The used bytes of freed regions are accumulated in + // pre_used. If par is true, the region's RSet will not be freed + // up. The assumption is that this will be done later. + void free_humongous_region(HeapRegion* hr, + FreeRegionList* free_list, + bool par); protected: // Shrink the garbage-first heap by at most the given size (in bytes!). @@ -836,37 +850,13 @@ OopClosure* scan_non_heap_roots, OopsInHeapRegionClosure* scan_rs, G1KlassScanClosure* scan_klasses, - int worker_i); + uint worker_i); // Apply "blk" to all the weak roots of the system. These include // JNI weak roots, the code cache, system dictionary, symbol table, // string table, and referents of reachable weak refs. void g1_process_weak_roots(OopClosure* root_closure); - // Frees a non-humongous region by initializing its contents and - // adding it to the free list that's passed as a parameter (this is - // usually a local list which will be appended to the master free - // list later). The used bytes of freed regions are accumulated in - // pre_used. If par is true, the region's RSet will not be freed - // up. The assumption is that this will be done later. - void free_region(HeapRegion* hr, - size_t* pre_used, - FreeRegionList* free_list, - bool par); - - // Frees a humongous region by collapsing it into individual regions - // and calling free_region() for each of them. The freed regions - // will be added to the free list that's passed as a parameter (this - // is usually a local list which will be appended to the master free - // list later). The used bytes of freed regions are accumulated in - // pre_used. If par is true, the region's RSet will not be freed - // up. The assumption is that this will be done later. - void free_humongous_region(HeapRegion* hr, - size_t* pre_used, - FreeRegionList* free_list, - HumongousRegionSet* humongous_proxy_set, - bool par); - // Notifies all the necessary spaces that the committed space has // been updated (either expanded or shrunk). It should be called // after _g1_storage is updated. @@ -1097,6 +1087,8 @@ // specified by the policy object. jint initialize(); + virtual void stop(); + // Return the (conservative) maximum heap alignment for any G1 heap static size_t conservative_max_heap_alignment(); @@ -1159,7 +1151,7 @@ void iterate_dirty_card_closure(CardTableEntryClosure* cl, DirtyCardQueue* into_cset_dcq, - bool concurrent, int worker_i); + bool concurrent, uint worker_i); // The shared block offset table array. G1BlockOffsetSharedArray* bot_shared() const { return _bot_shared; } @@ -1245,21 +1237,17 @@ bool is_on_master_free_list(HeapRegion* hr) { return hr->containing_set() == &_free_list; } - - bool is_in_humongous_set(HeapRegion* hr) { - return hr->containing_set() == &_humongous_set; - } #endif // ASSERT // Wrapper for the region list operations that can be called from // methods outside this class. - void secondary_free_list_add_as_tail(FreeRegionList* list) { - _secondary_free_list.add_as_tail(list); + void secondary_free_list_add(FreeRegionList* list) { + _secondary_free_list.add_ordered(list); } void append_secondary_free_list() { - _free_list.add_as_head(&_secondary_free_list); + _free_list.add_ordered(&_secondary_free_list); } void append_secondary_free_list_if_not_empty_with_lock() { @@ -1271,9 +1259,7 @@ } } - void old_set_remove(HeapRegion* hr) { - _old_set.remove(hr); - } + inline void old_set_remove(HeapRegion* hr); size_t non_young_capacity_bytes() { return _old_set.total_capacity_bytes() + _humongous_set.total_capacity_bytes(); @@ -1301,27 +1287,9 @@ // True iff an evacuation has failed in the most-recent collection. bool evacuation_failed() { return _evacuation_failed; } - // It will free a region if it has allocated objects in it that are - // all dead. It calls either free_region() or - // free_humongous_region() depending on the type of the region that - // is passed to it. - void free_region_if_empty(HeapRegion* hr, - size_t* pre_used, - FreeRegionList* free_list, - OldRegionSet* old_proxy_set, - HumongousRegionSet* humongous_proxy_set, - HRRSCleanupTask* hrrs_cleanup_task, - bool par); - - // It appends the free list to the master free list and updates the - // master humongous list according to the contents of the proxy - // list. It also adjusts the total used bytes according to pre_used - // (if par is true, it will do so by taking the ParGCRareEvent_lock). - void update_sets_after_freeing_regions(size_t pre_used, - FreeRegionList* free_list, - OldRegionSet* old_proxy_set, - HumongousRegionSet* humongous_proxy_set, - bool par); + void remove_from_old_sets(const HeapRegionSetCount& old_regions_removed, const HeapRegionSetCount& humongous_regions_removed); + void prepend_to_freelist(FreeRegionList* list); + void decrement_summary_bytes(size_t bytes); // Returns "TRUE" iff "p" points into the committed areas of the heap. virtual bool is_in(const void* p) const; @@ -1382,7 +1350,7 @@ void heap_region_iterate(HeapRegionClosure* blk) const; // Return the region with the given index. It assumes the index is valid. - HeapRegion* region_at(uint index) const { return _hrs.at(index); } + inline HeapRegion* region_at(uint index) const; // Divide the heap region sequence into "chunks" of some size (the number // of regions divided by the number of parallel threads times some @@ -1423,7 +1391,7 @@ // Given the id of a worker, obtain or calculate a suitable // starting region for iterating over the current collection set. - HeapRegion* start_cset_region_for_worker(int worker_i); + HeapRegion* start_cset_region_for_worker(uint worker_i); // This is a convenience method that is used by the // HeapRegionIterator classes to calculate the starting region for @@ -1484,9 +1452,11 @@ // Section on thread-local allocation buffers (TLABs) // See CollectedHeap for semantics. - virtual bool supports_tlab_allocation() const; - virtual size_t tlab_capacity(Thread* thr) const; - virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; + bool supports_tlab_allocation() const; + size_t tlab_capacity(Thread* ignored) const; + size_t tlab_used(Thread* ignored) const; + size_t max_tlab_size() const; + size_t unsafe_max_tlab_alloc(Thread* ignored) const; // Can a compiler initialize a new object without store barriers? // This permission only extends from the creation of a new object @@ -1509,10 +1479,7 @@ return true; } - bool is_in_young(const oop obj) { - HeapRegion* hr = heap_region_containing(obj); - return hr != NULL && hr->is_young(); - } + inline bool is_in_young(const oop obj); #ifdef ASSERT virtual bool is_in_partial_collection(const void* p); @@ -1525,9 +1492,7 @@ // pre-value that needs to be remembered; for the remembered-set // update logging post-barrier, we don't maintain remembered set // information for young gen objects. - virtual bool can_elide_initializing_store_barrier(oop new_obj) { - return is_in_young(new_obj); - } + virtual inline bool can_elide_initializing_store_barrier(oop new_obj); // Returns "true" iff the given word_size is "very large". static bool isHumongous(size_t word_size) { @@ -1571,7 +1536,7 @@ void set_region_short_lived_locked(HeapRegion* hr); // add appropriate methods for any other surv rate groups - YoungList* young_list() { return _young_list; } + YoungList* young_list() const { return _young_list; } // debugging bool check_young_list_well_formed() { @@ -1621,23 +1586,9 @@ // Added if it is NULL it isn't dead. - bool is_obj_dead(const oop obj) const { - const HeapRegion* hr = heap_region_containing(obj); - if (hr == NULL) { - if (obj == NULL) return false; - else return true; - } - else return is_obj_dead(obj, hr); - } + inline bool is_obj_dead(const oop obj) const; - bool is_obj_ill(const oop obj) const { - const HeapRegion* hr = heap_region_containing(obj); - if (hr == NULL) { - if (obj == NULL) return false; - else return true; - } - else return is_obj_ill(obj, hr); - } + inline bool is_obj_ill(const oop obj) const; bool allocated_since_marking(oop obj, HeapRegion* hr, VerifyOption vo); HeapWord* top_at_mark_start(HeapRegion* hr, VerifyOption vo); @@ -1674,6 +1625,9 @@ // that were not successfullly evacuated are not migrated. void migrate_strong_code_roots(); + // Free up superfluous code root memory. + void purge_code_root_memory(); + // During an initial mark pause, mark all the code roots that // point into regions *not* in the collection set. void mark_strong_code_roots(uint worker_id); @@ -1682,6 +1636,12 @@ // after a full GC void rebuild_strong_code_roots(); + // Delete entries for dead interned string and clean up unreferenced symbols + // in symbol table, possibly in parallel. + void unlink_string_and_symbol_table(BoolObjectClosure* is_alive, bool unlink_strings = true, bool unlink_symbols = true); + + // Redirty logged cards in the refinement queue. + void redirty_logged_cards(); // Verification // The following is just to alert the verification code @@ -1722,26 +1682,10 @@ bool is_obj_dead_cond(const oop obj, const HeapRegion* hr, - const VerifyOption vo) const { - switch (vo) { - case VerifyOption_G1UsePrevMarking: return is_obj_dead(obj, hr); - case VerifyOption_G1UseNextMarking: return is_obj_ill(obj, hr); - case VerifyOption_G1UseMarkWord: return !obj->is_gc_marked(); - default: ShouldNotReachHere(); - } - return false; // keep some compilers happy - } + const VerifyOption vo) const; bool is_obj_dead_cond(const oop obj, - const VerifyOption vo) const { - switch (vo) { - case VerifyOption_G1UsePrevMarking: return is_obj_dead(obj); - case VerifyOption_G1UseNextMarking: return is_obj_ill(obj); - case VerifyOption_G1UseMarkWord: return !obj->is_gc_marked(); - default: ShouldNotReachHere(); - } - return false; // keep some compilers happy - } + const VerifyOption vo) const; // Printing @@ -1760,8 +1704,6 @@ void print_all_rsets() PRODUCT_RETURN; public: - void stop_conc_gc_threads(); - size_t pending_card_num(); size_t cards_scanned(); @@ -1787,95 +1729,6 @@ ParGCAllocBuffer::retire(end_of_gc, retain); _retired = true; } - - bool is_retired() { - return _retired; - } -}; - -class G1ParGCAllocBufferContainer { -protected: - static int const _priority_max = 2; - G1ParGCAllocBuffer* _priority_buffer[_priority_max]; - -public: - G1ParGCAllocBufferContainer(size_t gclab_word_size) { - for (int pr = 0; pr < _priority_max; ++pr) { - _priority_buffer[pr] = new G1ParGCAllocBuffer(gclab_word_size); - } - } - - ~G1ParGCAllocBufferContainer() { - for (int pr = 0; pr < _priority_max; ++pr) { - assert(_priority_buffer[pr]->is_retired(), "alloc buffers should all retire at this point."); - delete _priority_buffer[pr]; - } - } - - HeapWord* allocate(size_t word_sz) { - HeapWord* obj; - for (int pr = 0; pr < _priority_max; ++pr) { - obj = _priority_buffer[pr]->allocate(word_sz); - if (obj != NULL) return obj; - } - return obj; - } - - bool contains(void* addr) { - for (int pr = 0; pr < _priority_max; ++pr) { - if (_priority_buffer[pr]->contains(addr)) return true; - } - return false; - } - - void undo_allocation(HeapWord* obj, size_t word_sz) { - bool finish_undo; - for (int pr = 0; pr < _priority_max; ++pr) { - if (_priority_buffer[pr]->contains(obj)) { - _priority_buffer[pr]->undo_allocation(obj, word_sz); - finish_undo = true; - } - } - if (!finish_undo) ShouldNotReachHere(); - } - - size_t words_remaining() { - size_t result = 0; - for (int pr = 0; pr < _priority_max; ++pr) { - result += _priority_buffer[pr]->words_remaining(); - } - return result; - } - - size_t words_remaining_in_retired_buffer() { - G1ParGCAllocBuffer* retired = _priority_buffer[0]; - return retired->words_remaining(); - } - - void flush_stats_and_retire(PLABStats* stats, bool end_of_gc, bool retain) { - for (int pr = 0; pr < _priority_max; ++pr) { - _priority_buffer[pr]->flush_stats_and_retire(stats, end_of_gc, retain); - } - } - - void update(bool end_of_gc, bool retain, HeapWord* buf, size_t word_sz) { - G1ParGCAllocBuffer* retired_and_set = _priority_buffer[0]; - retired_and_set->retire(end_of_gc, retain); - retired_and_set->set_buf(buf); - retired_and_set->set_word_size(word_sz); - adjust_priority_order(); - } - -private: - void adjust_priority_order() { - G1ParGCAllocBuffer* retired_and_set = _priority_buffer[0]; - - int last = _priority_max - 1; - for (int pr = 0; pr < last; ++pr) { - _priority_buffer[pr] = _priority_buffer[pr + 1]; - } - _priority_buffer[last] = retired_and_set; - } }; class G1ParScanThreadState : public StackObj { @@ -1886,17 +1739,17 @@ G1SATBCardTableModRefBS* _ct_bs; G1RemSet* _g1_rem; - G1ParGCAllocBufferContainer _surviving_alloc_buffer; - G1ParGCAllocBufferContainer _tenured_alloc_buffer; - G1ParGCAllocBufferContainer* _alloc_buffers[GCAllocPurposeCount]; + G1ParGCAllocBuffer _surviving_alloc_buffer; + G1ParGCAllocBuffer _tenured_alloc_buffer; + G1ParGCAllocBuffer* _alloc_buffers[GCAllocPurposeCount]; ageTable _age_table; + G1ParScanClosure _scanner; + size_t _alloc_buffer_waste; size_t _undo_waste; OopsInHeapRegionClosure* _evac_failure_cl; - G1ParScanHeapEvacClosure* _evac_cl; - G1ParScanPartialArrayClosure* _partial_scan_cl; int _hash_seed; uint _queue_num; @@ -1924,11 +1777,7 @@ DirtyCardQueue& dirty_card_queue() { return _dcq; } G1SATBCardTableModRefBS* ctbs() { return _ct_bs; } - template void immediate_rs_update(HeapRegion* from, T* p, int tid) { - if (!from->is_survivor()) { - _g1_rem->par_write_ref(from, p, tid); - } - } + template inline void immediate_rs_update(HeapRegion* from, T* p, int tid); template void deferred_rs_update(HeapRegion* from, T* p, int tid) { // If the new value of the field points to the same region or @@ -1943,7 +1792,7 @@ } public: - G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num); + G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num, ReferenceProcessor* rp); ~G1ParScanThreadState() { FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_base, mtGC); @@ -1952,7 +1801,7 @@ RefToScanQueue* refs() { return _refs; } ageTable* age_table() { return &_age_table; } - G1ParGCAllocBufferContainer* alloc_buffer(GCAllocPurpose purpose) { + G1ParGCAllocBuffer* alloc_buffer(GCAllocPurpose purpose) { return _alloc_buffers[purpose]; } @@ -1970,25 +1819,21 @@ refs()->push(ref); } - template void update_rs(HeapRegion* from, T* p, int tid) { - if (G1DeferredRSUpdate) { - deferred_rs_update(from, p, tid); - } else { - immediate_rs_update(from, p, tid); - } - } + template inline void update_rs(HeapRegion* from, T* p, int tid); HeapWord* allocate_slow(GCAllocPurpose purpose, size_t word_sz) { HeapWord* obj = NULL; size_t gclab_word_size = _g1h->desired_plab_sz(purpose); if (word_sz * 100 < gclab_word_size * ParallelGCBufferWastePct) { - G1ParGCAllocBufferContainer* alloc_buf = alloc_buffer(purpose); + G1ParGCAllocBuffer* alloc_buf = alloc_buffer(purpose); + add_to_alloc_buffer_waste(alloc_buf->words_remaining()); + alloc_buf->retire(false /* end_of_gc */, false /* retain */); HeapWord* buf = _g1h->par_allocate_during_gc(purpose, gclab_word_size); if (buf == NULL) return NULL; // Let caller handle allocation failure. - - add_to_alloc_buffer_waste(alloc_buf->words_remaining_in_retired_buffer()); - alloc_buf->update(false /* end_of_gc */, false /* retain */, buf, gclab_word_size); + // Otherwise. + alloc_buf->set_word_size(gclab_word_size); + alloc_buf->set_buf(buf); obj = alloc_buf->allocate(word_sz); assert(obj != NULL, "buffer was definitely big enough..."); @@ -2022,14 +1867,6 @@ return _evac_failure_cl; } - void set_evac_closure(G1ParScanHeapEvacClosure* evac_cl) { - _evac_cl = evac_cl; - } - - void set_partial_scan_closure(G1ParScanPartialArrayClosure* partial_scan_cl) { - _partial_scan_cl = partial_scan_cl; - } - int* hash_seed() { return &_hash_seed; } uint queue_num() { return _queue_num; } @@ -2077,29 +1914,70 @@ false /* retain */); } } +private: + #define G1_PARTIAL_ARRAY_MASK 0x2 - template void deal_with_reference(T* ref_to_scan) { - if (has_partial_array_mask(ref_to_scan)) { - _partial_scan_cl->do_oop_nv(ref_to_scan); - } else { - // Note: we can use "raw" versions of "region_containing" because - // "obj_to_scan" is definitely in the heap, and is not in a - // humongous region. - HeapRegion* r = _g1h->heap_region_containing_raw(ref_to_scan); - _evac_cl->set_region(r); - _evac_cl->do_oop_nv(ref_to_scan); - } + inline bool has_partial_array_mask(oop* ref) const { + return ((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) == G1_PARTIAL_ARRAY_MASK; + } + + // We never encode partial array oops as narrowOop*, so return false immediately. + // This allows the compiler to create optimized code when popping references from + // the work queue. + inline bool has_partial_array_mask(narrowOop* ref) const { + assert(((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) != G1_PARTIAL_ARRAY_MASK, "Partial array oop reference encoded as narrowOop*"); + return false; + } + + // Only implement set_partial_array_mask() for regular oops, not for narrowOops. + // We always encode partial arrays as regular oop, to allow the + // specialization for has_partial_array_mask() for narrowOops above. + // This means that unintentional use of this method with narrowOops are caught + // by the compiler. + inline oop* set_partial_array_mask(oop obj) const { + assert(((uintptr_t)(void *)obj & G1_PARTIAL_ARRAY_MASK) == 0, "Information loss!"); + return (oop*) ((uintptr_t)(void *)obj | G1_PARTIAL_ARRAY_MASK); + } + + inline oop clear_partial_array_mask(oop* ref) const { + return cast_to_oop((intptr_t)ref & ~G1_PARTIAL_ARRAY_MASK); } - void deal_with_reference(StarTask ref) { - assert(verify_task(ref), "sanity"); - if (ref.is_narrow()) { - deal_with_reference((narrowOop*)ref); - } else { - deal_with_reference((oop*)ref); + inline void do_oop_partial_array(oop* p); + + // This method is applied to the fields of the objects that have just been copied. + template void do_oop_evac(T* p, HeapRegion* from) { + assert(!oopDesc::is_null(oopDesc::load_decode_heap_oop(p)), + "Reference should not be NULL here as such are never pushed to the task queue."); + oop obj = oopDesc::load_decode_heap_oop_not_null(p); + + // Although we never intentionally push references outside of the collection + // set, due to (benign) races in the claim mechanism during RSet scanning more + // than one thread might claim the same card. So the same card may be + // processed multiple times. So redo this check. + if (_g1h->in_cset_fast_test(obj)) { + oop forwardee; + if (obj->is_forwarded()) { + forwardee = obj->forwardee(); + } else { + forwardee = copy_to_survivor_space(obj); + } + assert(forwardee != NULL, "forwardee should not be NULL"); + oopDesc::encode_store_heap_oop(p, forwardee); } + + assert(obj != NULL, "Must be"); + update_rs(from, p, queue_num()); } +public: + oop copy_to_survivor_space(oop const obj); + + template inline void deal_with_reference(T* ref_to_scan); + + inline void deal_with_reference(StarTask ref); + +public: void trim_queue(); }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -29,12 +29,17 @@ #include "gc_implementation/g1/g1CollectedHeap.hpp" #include "gc_implementation/g1/g1AllocRegion.inline.hpp" #include "gc_implementation/g1/g1CollectorPolicy.hpp" +#include "gc_implementation/g1/g1RemSet.inline.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" #include "gc_implementation/g1/heapRegionSeq.inline.hpp" #include "utilities/taskqueue.hpp" // Inline functions for G1CollectedHeap +// Return the region with the given index. It assumes the index is valid. +inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrs.at(index); } + template inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const { @@ -54,6 +59,10 @@ return res; } +inline void G1CollectedHeap::old_set_remove(HeapRegion* hr) { + _old_set.remove(hr); +} + inline bool G1CollectedHeap::obj_in_cs(oop obj) { HeapRegion* r = _hrs.addr_to_region((HeapWord*) obj); return r != NULL && r->in_collection_set(); @@ -150,6 +159,24 @@ return _cm->nextMarkBitMap()->isMarked((HeapWord *)obj); } + +// This is a fast test on whether a reference points into the +// collection set or not. Assume that the reference +// points into the heap. +inline bool G1CollectedHeap::in_cset_fast_test(oop obj) { + assert(_in_cset_fast_test != NULL, "sanity"); + assert(_g1_committed.contains((HeapWord*) obj), err_msg("Given reference outside of heap, is "PTR_FORMAT, p2i((HeapWord*)obj))); + // no need to subtract the bottom of the heap from obj, + // _in_cset_fast_test is biased + uintx index = cast_from_oop(obj) >> HeapRegion::LogOfHRGrainBytes; + bool ret = _in_cset_fast_test[index]; + // let's make sure the result is consistent with what the slower + // test returns + assert( ret || !obj_in_cs(obj), "sanity"); + assert(!ret || obj_in_cs(obj), "sanity"); + return ret; +} + #ifndef PRODUCT // Support for G1EvacuationFailureALot @@ -223,4 +250,121 @@ } #endif // #ifndef PRODUCT +inline bool G1CollectedHeap::is_in_young(const oop obj) { + HeapRegion* hr = heap_region_containing(obj); + return hr != NULL && hr->is_young(); +} + +// We don't need barriers for initializing stores to objects +// in the young gen: for the SATB pre-barrier, there is no +// pre-value that needs to be remembered; for the remembered-set +// update logging post-barrier, we don't maintain remembered set +// information for young gen objects. +inline bool G1CollectedHeap::can_elide_initializing_store_barrier(oop new_obj) { + return is_in_young(new_obj); +} + +inline bool G1CollectedHeap::is_obj_dead(const oop obj) const { + const HeapRegion* hr = heap_region_containing(obj); + if (hr == NULL) { + if (obj == NULL) return false; + else return true; + } + else return is_obj_dead(obj, hr); +} + +inline bool G1CollectedHeap::is_obj_ill(const oop obj) const { + const HeapRegion* hr = heap_region_containing(obj); + if (hr == NULL) { + if (obj == NULL) return false; + else return true; + } + else return is_obj_ill(obj, hr); +} + +template inline void G1ParScanThreadState::immediate_rs_update(HeapRegion* from, T* p, int tid) { + if (!from->is_survivor()) { + _g1_rem->par_write_ref(from, p, tid); + } +} + +template void G1ParScanThreadState::update_rs(HeapRegion* from, T* p, int tid) { + if (G1DeferredRSUpdate) { + deferred_rs_update(from, p, tid); + } else { + immediate_rs_update(from, p, tid); + } +} + + +inline void G1ParScanThreadState::do_oop_partial_array(oop* p) { + assert(has_partial_array_mask(p), "invariant"); + oop from_obj = clear_partial_array_mask(p); + + assert(Universe::heap()->is_in_reserved(from_obj), "must be in heap."); + assert(from_obj->is_objArray(), "must be obj array"); + objArrayOop from_obj_array = objArrayOop(from_obj); + // The from-space object contains the real length. + int length = from_obj_array->length(); + + assert(from_obj->is_forwarded(), "must be forwarded"); + oop to_obj = from_obj->forwardee(); + assert(from_obj != to_obj, "should not be chunking self-forwarded objects"); + objArrayOop to_obj_array = objArrayOop(to_obj); + // We keep track of the next start index in the length field of the + // to-space object. + int next_index = to_obj_array->length(); + assert(0 <= next_index && next_index < length, + err_msg("invariant, next index: %d, length: %d", next_index, length)); + + int start = next_index; + int end = length; + int remainder = end - start; + // We'll try not to push a range that's smaller than ParGCArrayScanChunk. + if (remainder > 2 * ParGCArrayScanChunk) { + end = start + ParGCArrayScanChunk; + to_obj_array->set_length(end); + // Push the remainder before we process the range in case another + // worker has run out of things to do and can steal it. + oop* from_obj_p = set_partial_array_mask(from_obj); + push_on_queue(from_obj_p); + } else { + assert(length == end, "sanity"); + // We'll process the final range for this object. Restore the length + // so that the heap remains parsable in case of evacuation failure. + to_obj_array->set_length(end); + } + _scanner.set_region(_g1h->heap_region_containing_raw(to_obj)); + // Process indexes [start,end). It will also process the header + // along with the first chunk (i.e., the chunk with start == 0). + // Note that at this point the length field of to_obj_array is not + // correct given that we are using it to keep track of the next + // start index. oop_iterate_range() (thankfully!) ignores the length + // field and only relies on the start / end parameters. It does + // however return the size of the object which will be incorrect. So + // we have to ignore it even if we wanted to use it. + to_obj_array->oop_iterate_range(&_scanner, start, end); +} + +template inline void G1ParScanThreadState::deal_with_reference(T* ref_to_scan) { + if (!has_partial_array_mask(ref_to_scan)) { + // Note: we can use "raw" versions of "region_containing" because + // "obj_to_scan" is definitely in the heap, and is not in a + // humongous region. + HeapRegion* r = _g1h->heap_region_containing_raw(ref_to_scan); + do_oop_evac(ref_to_scan, r); + } else { + do_oop_partial_array((oop*)ref_to_scan); + } +} + +inline void G1ParScanThreadState::deal_with_reference(StarTask ref) { + assert(verify_task(ref), "sanity"); + if (ref.is_narrow()) { + deal_with_reference((narrowOop*)ref); + } else { + deal_with_reference((oop*)ref); + } +} + #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTEDHEAP_INLINE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -22,6 +22,10 @@ * */ +#ifndef __clang_major__ +#define ATTRIBUTE_PRINTF(x,y) // FIXME, formats are a mess. +#endif + #include "precompiled.hpp" #include "gc_implementation/g1/concurrentG1Refine.hpp" #include "gc_implementation/g1/concurrentMark.hpp" @@ -965,7 +969,7 @@ #ifndef PRODUCT if (G1YoungSurvRateVerbose) { - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); _short_lived_surv_rate_group->print(); // do that for any other surv rate groups too } @@ -1204,7 +1208,7 @@ (_young_list_target_length * HeapRegion::GrainBytes) - _survivor_used_bytes_before_gc; if (full) { - _metaspace_used_bytes_before_gc = MetaspaceAux::allocated_used_bytes(); + _metaspace_used_bytes_before_gc = MetaspaceAux::used_bytes(); } } @@ -2222,11 +2226,11 @@ gclog_or_tty->print_cr("ALL PAUSES"); print_summary_sd(" Total", &_total); - gclog_or_tty->print_cr(""); - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); + gclog_or_tty->cr(); gclog_or_tty->print_cr(" Young GC Pauses: %8d", _young_pause_num); gclog_or_tty->print_cr(" Mixed GC Pauses: %8d", _mixed_pause_num); - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); gclog_or_tty->print_cr("EVACUATION PAUSES"); @@ -2246,7 +2250,7 @@ print_summary(" Clear CT", &_clear_ct); print_summary(" Other", &_other); } - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); gclog_or_tty->print_cr("MISC"); print_summary_sd(" Stop World", &_all_stop_world_times_ms); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -820,6 +820,8 @@ // do that for any other surv rate groups } + size_t young_list_target_length() const { return _young_list_target_length; } + bool is_young_list_full() { uint young_list_length = _g1->young_list()->length(); uint young_list_target_length = _young_list_target_length; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -27,6 +27,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1GCPhaseTimes.hpp" #include "gc_implementation/g1/g1Log.hpp" +#include "gc_implementation/g1/g1StringDedup.hpp" // Helper class for avoiding interleaved logging class LineBuffer: public StackObj { @@ -38,7 +39,7 @@ int _indent_level; int _cur; - void vappend(const char* format, va_list ap) { + void vappend(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0) { int res = vsnprintf(&_buffer[_cur], BUFFER_LEN - _cur, format, ap); if (res != -1) { _cur += res; @@ -62,14 +63,14 @@ } #endif - void append(const char* format, ...) { + void append(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { va_list ap; va_start(ap, format); vappend(format, ap); va_end(ap); } - void append_and_print_cr(const char* format, ...) { + void append_and_print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { va_list ap; va_start(ap, format); vappend(format, ap); @@ -79,6 +80,8 @@ } }; +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED template void WorkerDataArray::print(int level, const char* title) { if (_length == 1) { @@ -108,7 +111,7 @@ } if (G1Log::finest()) { - buf.append_and_print_cr(""); + buf.append_and_print_cr("%s", ""); } double avg = (double)sum / (double)_length; @@ -128,6 +131,7 @@ } buf.append_and_print_cr("]"); } +PRAGMA_DIAG_POP #ifndef PRODUCT @@ -168,7 +172,9 @@ _last_termination_attempts(_max_gc_threads, SIZE_FORMAT), _last_gc_worker_end_times_ms(_max_gc_threads, "%.1lf", false), _last_gc_worker_times_ms(_max_gc_threads, "%.1lf"), - _last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf") + _last_gc_worker_other_times_ms(_max_gc_threads, "%.1lf"), + _cur_string_dedup_queue_fixup_worker_times_ms(_max_gc_threads, "%.1lf"), + _cur_string_dedup_table_fixup_worker_times_ms(_max_gc_threads, "%.1lf") { assert(max_gc_threads > 0, "Must have some GC threads"); } @@ -229,12 +235,22 @@ _last_gc_worker_other_times_ms.verify(); } +void G1GCPhaseTimes::note_string_dedup_fixup_start() { + _cur_string_dedup_queue_fixup_worker_times_ms.reset(); + _cur_string_dedup_table_fixup_worker_times_ms.reset(); +} + +void G1GCPhaseTimes::note_string_dedup_fixup_end() { + _cur_string_dedup_queue_fixup_worker_times_ms.verify(); + _cur_string_dedup_table_fixup_worker_times_ms.verify(); +} + void G1GCPhaseTimes::print_stats(int level, const char* str, double value) { LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value); } -void G1GCPhaseTimes::print_stats(int level, const char* str, double value, int workers) { - LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: %d]", str, value, workers); +void G1GCPhaseTimes::print_stats(int level, const char* str, double value, uint workers) { + LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: " UINT32_FORMAT "]", str, value, workers); } double G1GCPhaseTimes::accounted_time_ms() { @@ -250,6 +266,14 @@ // Strong code root migration time misc_time_ms += _cur_strong_code_root_migration_time_ms; + // Strong code root purge time + misc_time_ms += _cur_strong_code_root_purge_time_ms; + + if (G1StringDedup::is_enabled()) { + // String dedup fixup time + misc_time_ms += _cur_string_dedup_fixup_time_ms; + } + // Subtract the time taken to clean the card table from the // current value of "other time" misc_time_ms += _cur_clear_ct_time_ms; @@ -299,20 +323,43 @@ } print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms); print_stats(1, "Code Root Migration", _cur_strong_code_root_migration_time_ms); + print_stats(1, "Code Root Purge", _cur_strong_code_root_purge_time_ms); + if (G1StringDedup::is_enabled()) { + print_stats(1, "String Dedup Fixup", _cur_string_dedup_fixup_time_ms, _active_gc_threads); + _cur_string_dedup_queue_fixup_worker_times_ms.print(2, "Queue Fixup (ms)"); + _cur_string_dedup_table_fixup_worker_times_ms.print(2, "Table Fixup (ms)"); + } print_stats(1, "Clear CT", _cur_clear_ct_time_ms); double misc_time_ms = pause_time_sec * MILLIUNITS - accounted_time_ms(); print_stats(1, "Other", misc_time_ms); if (_cur_verify_before_time_ms > 0.0) { print_stats(2, "Verify Before", _cur_verify_before_time_ms); } + if (G1CollectedHeap::heap()->evacuation_failed()) { + double evac_fail_handling = _cur_evac_fail_recalc_used + _cur_evac_fail_remove_self_forwards + + _cur_evac_fail_restore_remsets; + print_stats(2, "Evacuation Failure", evac_fail_handling); + if (G1Log::finest()) { + print_stats(3, "Recalculate Used", _cur_evac_fail_recalc_used); + print_stats(3, "Remove Self Forwards", _cur_evac_fail_remove_self_forwards); + print_stats(3, "Restore RemSet", _cur_evac_fail_restore_remsets); + } + } print_stats(2, "Choose CSet", (_recorded_young_cset_choice_time_ms + _recorded_non_young_cset_choice_time_ms)); print_stats(2, "Ref Proc", _cur_ref_proc_time_ms); print_stats(2, "Ref Enq", _cur_ref_enq_time_ms); + if (G1DeferredRSUpdate) { + print_stats(2, "Redirty Cards", _recorded_redirty_logged_cards_time_ms); + } print_stats(2, "Free CSet", (_recorded_young_free_cset_time_ms + _recorded_non_young_free_cset_time_ms)); + if (G1Log::finest()) { + print_stats(3, "Young Free CSet", _recorded_young_free_cset_time_ms); + print_stats(3, "Non-Young Free CSet", _recorded_non_young_free_cset_time_ms); + } if (_cur_verify_after_time_ms > 0.0) { print_stats(2, "Verify After", _cur_verify_after_time_ms); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp --- a/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -131,6 +131,15 @@ double _cur_collection_par_time_ms; double _cur_collection_code_root_fixup_time_ms; double _cur_strong_code_root_migration_time_ms; + double _cur_strong_code_root_purge_time_ms; + + double _cur_evac_fail_recalc_used; + double _cur_evac_fail_restore_remsets; + double _cur_evac_fail_remove_self_forwards; + + double _cur_string_dedup_fixup_time_ms; + WorkerDataArray _cur_string_dedup_queue_fixup_worker_times_ms; + WorkerDataArray _cur_string_dedup_table_fixup_worker_times_ms; double _cur_clear_ct_time_ms; double _cur_ref_proc_time_ms; @@ -142,6 +151,8 @@ double _recorded_young_cset_choice_time_ms; double _recorded_non_young_cset_choice_time_ms; + double _recorded_redirty_logged_cards_time_ms; + double _recorded_young_free_cset_time_ms; double _recorded_non_young_free_cset_time_ms; @@ -150,7 +161,7 @@ // Helper methods for detailed logging void print_stats(int level, const char* str, double value); - void print_stats(int level, const char* str, double value, int workers); + void print_stats(int level, const char* str, double value, uint workers); public: G1GCPhaseTimes(uint max_gc_threads); @@ -223,6 +234,37 @@ _cur_strong_code_root_migration_time_ms = ms; } + void record_strong_code_root_purge_time(double ms) { + _cur_strong_code_root_purge_time_ms = ms; + } + + void record_evac_fail_recalc_used_time(double ms) { + _cur_evac_fail_recalc_used = ms; + } + + void record_evac_fail_restore_remsets(double ms) { + _cur_evac_fail_restore_remsets = ms; + } + + void record_evac_fail_remove_self_forwards(double ms) { + _cur_evac_fail_remove_self_forwards = ms; + } + + void note_string_dedup_fixup_start(); + void note_string_dedup_fixup_end(); + + void record_string_dedup_fixup_time(double ms) { + _cur_string_dedup_fixup_time_ms = ms; + } + + void record_string_dedup_queue_fixup_worker_time(uint worker_id, double ms) { + _cur_string_dedup_queue_fixup_worker_times_ms.set(worker_id, ms); + } + + void record_string_dedup_table_fixup_worker_time(uint worker_id, double ms) { + _cur_string_dedup_table_fixup_worker_times_ms.set(worker_id, ms); + } + void record_ref_proc_time(double ms) { _cur_ref_proc_time_ms = ms; } @@ -251,6 +293,10 @@ _recorded_non_young_cset_choice_time_ms = time_ms; } + void record_redirty_logged_cards_time_ms(double time_ms) { + _recorded_redirty_logged_cards_time_ms = time_ms; + } + void record_cur_collection_start_sec(double time_ms) { _cur_collection_start_sec = time_ms; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1HRPrinter.cpp --- a/src/share/vm/gc_implementation/g1/g1HRPrinter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1HRPrinter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -27,6 +27,8 @@ #include "gc_implementation/g1/heapRegion.hpp" #include "utilities/ostream.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + const char* G1HRPrinter::action_name(ActionType action) { switch(action) { case Alloc: return "ALLOC"; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1HotCardCache.cpp --- a/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -44,9 +44,9 @@ _hot_cache_idx = 0; // For refining the cards in the hot cache in parallel - int n_workers = (ParallelGCThreads > 0 ? + uint n_workers = (ParallelGCThreads > 0 ? _g1h->workers()->total_workers() : 1); - _hot_cache_par_chunk_size = MAX2(1, _hot_cache_size / n_workers); + _hot_cache_par_chunk_size = MAX2(1, _hot_cache_size / (int)n_workers); _hot_cache_par_claimed_idx = 0; _card_counts.initialize(); @@ -89,7 +89,7 @@ return res; } -void G1HotCardCache::drain(int worker_i, +void G1HotCardCache::drain(uint worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq) { if (!default_use_cache()) { @@ -122,8 +122,8 @@ // RSet updating while within an evacuation pause. // In this case worker_i should be the id of a GC worker thread assert(SafepointSynchronize::is_at_safepoint(), "Should be at a safepoint"); - assert(worker_i < (int) (ParallelGCThreads == 0 ? 1 : ParallelGCThreads), - err_msg("incorrect worker id: "INT32_FORMAT, worker_i)); + assert(worker_i < (ParallelGCThreads == 0 ? 1 : ParallelGCThreads), + err_msg("incorrect worker id: "UINT32_FORMAT, worker_i)); into_cset_dcq->enqueue(card_ptr); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1HotCardCache.hpp --- a/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -99,7 +99,7 @@ // Refine the cards that have delayed as a result of // being in the cache. - void drain(int worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq); + void drain(uint worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq); // Set up for parallel processing of the cards in the hot cache void reset_hot_cache_claimed_index() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1MarkSweep.cpp --- a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -31,6 +31,7 @@ #include "code/icBuffer.hpp" #include "gc_implementation/g1/g1Log.hpp" #include "gc_implementation/g1/g1MarkSweep.hpp" +#include "gc_implementation/g1/g1StringDedup.hpp" #include "gc_implementation/shared/gcHeapSummary.hpp" #include "gc_implementation/shared/gcTimer.hpp" #include "gc_implementation/shared/gcTrace.hpp" @@ -163,11 +164,8 @@ // Prune dead klasses from subklass/sibling/implementor lists. Klass::clean_weak_klass_links(&GenMarkSweep::is_alive); - // Delete entries for dead interned strings. - StringTable::unlink(&GenMarkSweep::is_alive); - - // Clean up unreferenced symbols in symbol table. - SymbolTable::unlink(); + // Delete entries for dead interned string and clean up unreferenced symbols in symbol table. + G1CollectedHeap::heap()->unlink_string_and_symbol_table(&GenMarkSweep::is_alive); if (VerifyDuringGC) { HandleMark hm; // handle scope @@ -199,17 +197,19 @@ G1CollectedHeap* _g1h; ModRefBarrierSet* _mrbs; CompactPoint _cp; - HumongousRegionSet _humongous_proxy_set; + HeapRegionSetCount _humongous_regions_removed; void free_humongous_region(HeapRegion* hr) { HeapWord* end = hr->end(); - size_t dummy_pre_used; FreeRegionList dummy_free_list("Dummy Free List for G1MarkSweep"); assert(hr->startsHumongous(), "Only the start of a humongous region should be freed."); - _g1h->free_humongous_region(hr, &dummy_pre_used, &dummy_free_list, - &_humongous_proxy_set, false /* par */); + + hr->set_containing_set(NULL); + _humongous_regions_removed.increment(1u, hr->capacity()); + + _g1h->free_humongous_region(hr, &dummy_free_list, false /* par */); hr->prepare_for_compaction(&_cp); // Also clear the part of the card table that will be unused after // compaction. @@ -222,16 +222,13 @@ : _g1h(G1CollectedHeap::heap()), _mrbs(_g1h->g1_barrier_set()), _cp(NULL, cs, cs->initialize_threshold()), - _humongous_proxy_set("G1MarkSweep Humongous Proxy Set") { } + _humongous_regions_removed() { } void update_sets() { // We'll recalculate total used bytes and recreate the free list // at the end of the GC, so no point in updating those values here. - _g1h->update_sets_after_freeing_regions(0, /* pre_used */ - NULL, /* free_list */ - NULL, /* old_proxy_set */ - &_humongous_proxy_set, - false /* par */); + HeapRegionSetCount empty_set; + _g1h->remove_from_old_sets(empty_set, _humongous_regions_removed); } bool doHeapRegion(HeapRegion* hr) { @@ -324,6 +321,10 @@ // have been cleared if they pointed to non-surviving objects.) g1h->g1_process_weak_roots(&GenMarkSweep::adjust_pointer_closure); + if (G1StringDedup::is_enabled()) { + G1StringDedup::oops_do(&GenMarkSweep::adjust_pointer_closure); + } + GenMarkSweep::adjust_marks(); G1AdjustPointersClosure blk; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1OopClosures.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 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 + * 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 "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1OopClosures.inline.hpp" + +G1ParCopyHelper::G1ParCopyHelper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : + G1ParClosureSuper(g1, par_scan_state), _scanned_klass(NULL), + _cm(_g1->concurrent_mark()) {} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1OopClosures.hpp --- a/src/share/vm/gc_implementation/g1/g1OopClosures.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -38,7 +38,7 @@ // A class that scans oops in a given heap region (much as OopsInGenClosure // scans oops in a generation.) -class OopsInHeapRegionClosure: public OopsInGenClosure { +class OopsInHeapRegionClosure: public ExtendedOopClosure { protected: HeapRegion* _from; public: @@ -48,12 +48,8 @@ class G1ParClosureSuper : public OopsInHeapRegionClosure { protected: G1CollectedHeap* _g1; - G1RemSet* _g1_rem; - ConcurrentMark* _cm; G1ParScanThreadState* _par_scan_state; uint _worker_id; - bool _during_initial_mark; - bool _mark_in_progress; public: G1ParClosureSuper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state); bool apply_to_weak_ref_discovered_field() { return true; } @@ -84,59 +80,12 @@ virtual void do_oop(narrowOop* p) { do_oop_nv(p); } }; -#define G1_PARTIAL_ARRAY_MASK 0x2 - -template inline bool has_partial_array_mask(T* ref) { - return ((uintptr_t)ref & G1_PARTIAL_ARRAY_MASK) == G1_PARTIAL_ARRAY_MASK; -} - -template inline T* set_partial_array_mask(T obj) { - assert(((uintptr_t)(void *)obj & G1_PARTIAL_ARRAY_MASK) == 0, "Information loss!"); - return (T*) ((uintptr_t)(void *)obj | G1_PARTIAL_ARRAY_MASK); -} - -template inline oop clear_partial_array_mask(T* ref) { - return cast_to_oop((intptr_t)ref & ~G1_PARTIAL_ARRAY_MASK); -} - -class G1ParScanPartialArrayClosure : public G1ParClosureSuper { - G1ParScanClosure _scanner; - -public: - G1ParScanPartialArrayClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state, ReferenceProcessor* rp) : - G1ParClosureSuper(g1, par_scan_state), _scanner(g1, par_scan_state, rp) - { - assert(_ref_processor == NULL, "sanity"); - } - - G1ParScanClosure* scanner() { - return &_scanner; - } - - template void do_oop_nv(T* p); - virtual void do_oop(oop* p) { do_oop_nv(p); } - virtual void do_oop(narrowOop* p) { do_oop_nv(p); } -}; - // Add back base class for metadata class G1ParCopyHelper : public G1ParClosureSuper { +protected: Klass* _scanned_klass; - - public: - G1ParCopyHelper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : - _scanned_klass(NULL), - G1ParClosureSuper(g1, par_scan_state) {} + ConcurrentMark* _cm; - void set_scanned_klass(Klass* k) { _scanned_klass = k; } - template void do_klass_barrier(T* p, oop new_obj); -}; - -template -class G1ParCopyClosure : public G1ParCopyHelper { - G1ParScanClosure _scanner; - template void do_oop_work(T* p); - -protected: // Mark the object if it's not already marked. This is used to mark // objects pointed to by roots that are guaranteed not to move // during the GC (i.e., non-CSet objects). It is MT-safe. @@ -146,50 +95,41 @@ // objects pointed to by roots that have been forwarded during a // GC. It is MT-safe. void mark_forwarded_object(oop from_obj, oop to_obj); + public: + G1ParCopyHelper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state); - oop copy_to_survivor_space(oop obj); + void set_scanned_klass(Klass* k) { _scanned_klass = k; } + template void do_klass_barrier(T* p, oop new_obj); +}; + +template +class G1ParCopyClosure : public G1ParCopyHelper { +private: + template void do_oop_work(T* p); public: G1ParCopyClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state, ReferenceProcessor* rp) : - _scanner(g1, par_scan_state, rp), G1ParCopyHelper(g1, par_scan_state) { assert(_ref_processor == NULL, "sanity"); } - G1ParScanClosure* scanner() { return &_scanner; } - - template void do_oop_nv(T* p) { - do_oop_work(p); - } + template void do_oop_nv(T* p) { do_oop_work(p); } virtual void do_oop(oop* p) { do_oop_nv(p); } virtual void do_oop(narrowOop* p) { do_oop_nv(p); } }; -typedef G1ParCopyClosure G1ParScanExtRootClosure; -typedef G1ParCopyClosure G1ParScanMetadataClosure; +typedef G1ParCopyClosure G1ParScanExtRootClosure; +typedef G1ParCopyClosure G1ParScanMetadataClosure; -typedef G1ParCopyClosure G1ParScanAndMarkExtRootClosure; -typedef G1ParCopyClosure G1ParScanAndMarkClosure; -typedef G1ParCopyClosure G1ParScanAndMarkMetadataClosure; - -// The following closure types are no longer used but are retained -// for historical reasons: -// typedef G1ParCopyClosure G1ParScanHeapRSClosure; -// typedef G1ParCopyClosure G1ParScanAndMarkHeapRSClosure; - -// The following closure type is defined in g1_specialized_oop_closures.hpp: -// -// typedef G1ParCopyClosure G1ParScanHeapEvacClosure; +typedef G1ParCopyClosure G1ParScanAndMarkExtRootClosure; +typedef G1ParCopyClosure G1ParScanAndMarkMetadataClosure; // We use a separate closure to handle references during evacuation // failure processing. -// We could have used another instance of G1ParScanHeapEvacClosure -// (since that closure no longer assumes that the references it -// handles point into the collection set). -typedef G1ParCopyClosure G1ParScanHeapEvacFailureClosure; +typedef G1ParCopyClosure G1ParScanHeapEvacFailureClosure; class FilterIntoCSClosure: public ExtendedOopClosure { G1CollectedHeap* _g1; @@ -294,14 +234,14 @@ HeapRegion* _from; OopsInHeapRegionClosure* _push_ref_cl; bool _record_refs_into_cset; - int _worker_i; + uint _worker_i; public: G1UpdateRSOrPushRefOopClosure(G1CollectedHeap* g1h, G1RemSet* rs, OopsInHeapRegionClosure* push_ref_cl, bool record_refs_into_cset, - int worker_i = 0); + uint worker_i = 0); void set_from(HeapRegion* from) { assert(from != NULL, "from region must be non-NULL"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp --- a/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -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" /* @@ -82,7 +83,7 @@ _par_scan_state->push_on_queue(p); } else { - _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); + _par_scan_state->update_rs(_from, p, _worker_id); } } } @@ -113,7 +114,7 @@ if (_cm->verbose_high()) { gclog_or_tty->print_cr("[%u] we're looking at location " "*"PTR_FORMAT" = "PTR_FORMAT, - _task->worker_id(), p, (void*) obj); + _task->worker_id(), p2i(p), p2i((void*) obj)); } _task->deal_with_reference(obj); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1RemSet.cpp --- a/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -39,6 +39,8 @@ #include "oops/oop.inline.hpp" #include "utilities/intHisto.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #define CARD_REPEAT_HISTO 0 #if CARD_REPEAT_HISTO @@ -114,14 +116,14 @@ G1SATBCardTableModRefBS *_ct_bs; double _strong_code_root_scan_time_sec; - int _worker_i; + uint _worker_i; int _block_size; bool _try_claimed; public: ScanRSClosure(OopsInHeapRegionClosure* oc, CodeBlobToOopClosure* code_root_cl, - int worker_i) : + uint worker_i) : _oc(oc), _code_root_cl(code_root_cl), _strong_code_root_scan_time_sec(0.0), @@ -163,7 +165,7 @@ void printCard(HeapRegion* card_region, size_t card_index, HeapWord* card_start) { - gclog_or_tty->print_cr("T %d Region [" PTR_FORMAT ", " PTR_FORMAT ") " + gclog_or_tty->print_cr("T " UINT32_FORMAT " Region [" PTR_FORMAT ", " PTR_FORMAT ") " "RS names card %p: " "[" PTR_FORMAT ", " PTR_FORMAT ")", _worker_i, @@ -242,7 +244,7 @@ void G1RemSet::scanRS(OopsInHeapRegionClosure* oc, CodeBlobToOopClosure* code_root_cl, - int worker_i) { + uint worker_i) { double rs_time_start = os::elapsedTime(); HeapRegion *startRegion = _g1->start_cset_region_for_worker(worker_i); @@ -275,13 +277,13 @@ DirtyCardQueue* into_cset_dcq) : _g1rs(g1h->g1_rem_set()), _into_cset_dcq(into_cset_dcq) {} - bool do_card_ptr(jbyte* card_ptr, int worker_i) { + bool do_card_ptr(jbyte* card_ptr, uint worker_i) { // The only time we care about recording cards that // contain references that point into the collection set // is during RSet updating within an evacuation pause. // In this case worker_i should be the id of a GC worker thread. assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause"); - assert(worker_i < (int) (ParallelGCThreads == 0 ? 1 : ParallelGCThreads), "should be a GC worker"); + assert(worker_i < (ParallelGCThreads == 0 ? 1 : ParallelGCThreads), "should be a GC worker"); if (_g1rs->refine_card(card_ptr, worker_i, true)) { // 'card_ptr' contains references that point into the collection @@ -296,7 +298,7 @@ } }; -void G1RemSet::updateRS(DirtyCardQueue* into_cset_dcq, int worker_i) { +void G1RemSet::updateRS(DirtyCardQueue* into_cset_dcq, uint worker_i) { double start = os::elapsedTime(); // Apply the given closure to all remaining log entries. RefineRecordRefsIntoCSCardTableEntryClosure into_cset_update_rs_cl(_g1, into_cset_dcq); @@ -321,14 +323,14 @@ void G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc, CodeBlobToOopClosure* code_root_cl, - int worker_i) { + uint worker_i) { #if CARD_REPEAT_HISTO ct_freq_update_histo_and_reset(); #endif // We cache the value of 'oc' closure into the appropriate slot in the // _cset_rs_update_cl for this worker - assert(worker_i < (int)n_workers(), "sanity"); + assert(worker_i < n_workers(), "sanity"); _cset_rs_update_cl[worker_i] = oc; // A DirtyCardQueue that is used to hold cards containing references @@ -400,7 +402,7 @@ _g1(g1), _ct_bs(bs) { } - bool do_card_ptr(jbyte* card_ptr, int worker_i) { + bool do_card_ptr(jbyte* card_ptr, uint worker_i) { // Construct the region representing the card. HeapWord* start = _ct_bs->addr_for(card_ptr); // And find the region containing it. @@ -463,8 +465,9 @@ int into_cset_n_buffers = into_cset_dcqs.completed_buffers_num(); if (_g1->evacuation_failed()) { + double restore_remembered_set_start = os::elapsedTime(); + // Restore remembered sets for the regions pointing into the collection set. - if (G1DeferredRSUpdate) { // If deferred RS updates are enabled then we just need to transfer // the completed buffers from (a) the DirtyCardQueueSet used to hold @@ -483,6 +486,8 @@ } assert(n_completed_buffers == into_cset_n_buffers, "missed some buffers"); } + + _g1->g1_policy()->phase_times()->record_evac_fail_restore_remsets((os::elapsedTime() - restore_remembered_set_start) * 1000.0); } // Free any completed buffers in the DirtyCardQueueSet used to hold cards @@ -541,7 +546,7 @@ G1RemSet* rs, OopsInHeapRegionClosure* push_ref_cl, bool record_refs_into_cset, - int worker_i) : + uint worker_i) : _g1(g1h), _g1_rem_set(rs), _from(NULL), _record_refs_into_cset(record_refs_into_cset), _push_ref_cl(push_ref_cl), _worker_i(worker_i) { } @@ -550,7 +555,7 @@ // into the collection set, if we're checking for such references; // false otherwise. -bool G1RemSet::refine_card(jbyte* card_ptr, int worker_i, +bool G1RemSet::refine_card(jbyte* card_ptr, uint worker_i, bool check_for_refs_into_cset) { // If the card is no longer dirty, nothing to do. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1RemSet.hpp --- a/src/share/vm/gc_implementation/g1/g1RemSet.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -97,7 +97,7 @@ // In the sequential case this param will be ignored. void oops_into_collection_set_do(OopsInHeapRegionClosure* blk, CodeBlobToOopClosure* code_root_cl, - int worker_i); + uint worker_i); // Prepare for and cleanup after an oops_into_collection_set_do // call. Must call each of these once before and after (in sequential @@ -109,9 +109,9 @@ void scanRS(OopsInHeapRegionClosure* oc, CodeBlobToOopClosure* code_root_cl, - int worker_i); + uint worker_i); - void updateRS(DirtyCardQueue* into_cset_dcq, int worker_i); + void updateRS(DirtyCardQueue* into_cset_dcq, uint worker_i); CardTableModRefBS* ct_bs() { return _ct_bs; } size_t cardsScanned() { return _total_cards_scanned; } @@ -138,7 +138,7 @@ // if the given card contains oops that have references into the // current collection set. virtual bool refine_card(jbyte* card_ptr, - int worker_i, + uint worker_i, bool check_for_refs_into_cset); // Print accumulated summary info from the start of the VM. @@ -171,12 +171,12 @@ class UpdateRSOopClosure: public ExtendedOopClosure { HeapRegion* _from; G1RemSet* _rs; - int _worker_i; + uint _worker_i; template void do_oop_work(T* p); public: - UpdateRSOopClosure(G1RemSet* rs, int worker_i = 0) : + UpdateRSOopClosure(G1RemSet* rs, uint worker_i = 0) : _from(NULL), _rs(rs), _worker_i(worker_i) {} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp --- a/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -95,7 +95,15 @@ jbyte *const first = byte_for(mr.start()); jbyte *const last = byte_after(mr.last()); - memset(first, g1_young_gen, last - first); + // Below we may use an explicit loop instead of memset() because on + // certain platforms memset() can give concurrent readers phantom zeros. + if (UseMemSetInBOT) { + memset(first, g1_young_gen, last - first); + } else { + for (jbyte* i = first; i < last; i++) { + *i = g1_young_gen; + } + } } #ifndef PRODUCT @@ -115,7 +123,8 @@ 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; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp --- a/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -151,7 +151,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedup.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedup.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 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 + * 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/javaClasses.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1GCPhaseTimes.hpp" +#include "gc_implementation/g1/g1StringDedup.hpp" +#include "gc_implementation/g1/g1StringDedupQueue.hpp" +#include "gc_implementation/g1/g1StringDedupStat.hpp" +#include "gc_implementation/g1/g1StringDedupTable.hpp" +#include "gc_implementation/g1/g1StringDedupThread.hpp" + +bool G1StringDedup::_enabled = false; + +void G1StringDedup::initialize() { + assert(UseG1GC, "String deduplication only available with G1"); + if (UseStringDeduplication) { + _enabled = true; + G1StringDedupQueue::create(); + G1StringDedupTable::create(); + G1StringDedupThread::create(); + } +} + +void G1StringDedup::stop() { + assert(is_enabled(), "String deduplication not enabled"); + G1StringDedupThread::stop(); +} + +bool G1StringDedup::is_candidate_from_mark(oop obj) { + if (java_lang_String::is_instance(obj)) { + bool from_young = G1CollectedHeap::heap()->heap_region_containing_raw(obj)->is_young(); + if (from_young && obj->age() < StringDeduplicationAgeThreshold) { + // Candidate found. String is being evacuated from young to old but has not + // reached the deduplication age threshold, i.e. has not previously been a + // candidate during its life in the young generation. + return true; + } + } + + // Not a candidate + return false; +} + +void G1StringDedup::enqueue_from_mark(oop java_string) { + assert(is_enabled(), "String deduplication not enabled"); + if (is_candidate_from_mark(java_string)) { + G1StringDedupQueue::push(0 /* worker_id */, java_string); + } +} + +bool G1StringDedup::is_candidate_from_evacuation(bool from_young, bool to_young, oop obj) { + if (from_young && java_lang_String::is_instance(obj)) { + if (to_young && obj->age() == StringDeduplicationAgeThreshold) { + // Candidate found. String is being evacuated from young to young and just + // reached the deduplication age threshold. + return true; + } + if (!to_young && obj->age() < StringDeduplicationAgeThreshold) { + // Candidate found. String is being evacuated from young to old but has not + // reached the deduplication age threshold, i.e. has not previously been a + // candidate during its life in the young generation. + return true; + } + } + + // Not a candidate + return false; +} + +void G1StringDedup::enqueue_from_evacuation(bool from_young, bool to_young, uint worker_id, oop java_string) { + assert(is_enabled(), "String deduplication not enabled"); + if (is_candidate_from_evacuation(from_young, to_young, java_string)) { + G1StringDedupQueue::push(worker_id, java_string); + } +} + +void G1StringDedup::deduplicate(oop java_string) { + assert(is_enabled(), "String deduplication not enabled"); + G1StringDedupStat dummy; // Statistics from this path is never used + G1StringDedupTable::deduplicate(java_string, dummy); +} + +void G1StringDedup::oops_do(OopClosure* keep_alive) { + assert(is_enabled(), "String deduplication not enabled"); + unlink_or_oops_do(NULL, keep_alive); +} + +void G1StringDedup::unlink(BoolObjectClosure* is_alive) { + assert(is_enabled(), "String deduplication not enabled"); + // Don't allow a potential resize or rehash during unlink, as the unlink + // operation itself might remove enough entries to invalidate such a decision. + unlink_or_oops_do(is_alive, NULL, false /* allow_resize_and_rehash */); +} + +// +// Task for parallel unlink_or_oops_do() operation on the deduplication queue +// and table. +// +class G1StringDedupUnlinkOrOopsDoTask : public AbstractGangTask { +private: + G1StringDedupUnlinkOrOopsDoClosure _cl; + +public: + G1StringDedupUnlinkOrOopsDoTask(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool allow_resize_and_rehash) : + AbstractGangTask("G1StringDedupUnlinkOrOopsDoTask"), + _cl(is_alive, keep_alive, allow_resize_and_rehash) { + } + + virtual void work(uint worker_id) { + double queue_fixup_start = os::elapsedTime(); + G1StringDedupQueue::unlink_or_oops_do(&_cl); + + double table_fixup_start = os::elapsedTime(); + G1StringDedupTable::unlink_or_oops_do(&_cl, worker_id); + + double queue_fixup_time_ms = (table_fixup_start - queue_fixup_start) * 1000.0; + double table_fixup_time_ms = (os::elapsedTime() - table_fixup_start) * 1000.0; + G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy(); + g1p->phase_times()->record_string_dedup_queue_fixup_worker_time(worker_id, queue_fixup_time_ms); + g1p->phase_times()->record_string_dedup_table_fixup_worker_time(worker_id, table_fixup_time_ms); + } +}; + +void G1StringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool allow_resize_and_rehash) { + assert(is_enabled(), "String deduplication not enabled"); + G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy(); + g1p->phase_times()->note_string_dedup_fixup_start(); + double fixup_start = os::elapsedTime(); + + G1StringDedupUnlinkOrOopsDoTask task(is_alive, keep_alive, allow_resize_and_rehash); + if (G1CollectedHeap::use_parallel_gc_threads()) { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + g1h->set_par_threads(); + g1h->workers()->run_task(&task); + g1h->set_par_threads(0); + } else { + task.work(0); + } + + double fixup_time_ms = (os::elapsedTime() - fixup_start) * 1000.0; + g1p->phase_times()->record_string_dedup_fixup_time(fixup_time_ms); + g1p->phase_times()->note_string_dedup_fixup_end(); +} + +void G1StringDedup::threads_do(ThreadClosure* tc) { + assert(is_enabled(), "String deduplication not enabled"); + tc->do_thread(G1StringDedupThread::thread()); +} + +void G1StringDedup::print_worker_threads_on(outputStream* st) { + assert(is_enabled(), "String deduplication not enabled"); + G1StringDedupThread::thread()->print_on(st); + st->cr(); +} + +void G1StringDedup::verify() { + assert(is_enabled(), "String deduplication not enabled"); + G1StringDedupQueue::verify(); + G1StringDedupTable::verify(); +} + +G1StringDedupUnlinkOrOopsDoClosure::G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool allow_resize_and_rehash) : + _is_alive(is_alive), + _keep_alive(keep_alive), + _resized_table(NULL), + _rehashed_table(NULL), + _next_queue(0), + _next_bucket(0) { + if (allow_resize_and_rehash) { + // If both resize and rehash is needed, only do resize. Rehash of + // the table will eventually happen if the situation persists. + _resized_table = G1StringDedupTable::prepare_resize(); + if (!is_resizing()) { + _rehashed_table = G1StringDedupTable::prepare_rehash(); + } + } +} + +G1StringDedupUnlinkOrOopsDoClosure::~G1StringDedupUnlinkOrOopsDoClosure() { + assert(!is_resizing() || !is_rehashing(), "Can not both resize and rehash"); + if (is_resizing()) { + G1StringDedupTable::finish_resize(_resized_table); + } else if (is_rehashing()) { + G1StringDedupTable::finish_rehash(_rehashed_table); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedup.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedup.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,206 @@ +/* + * Copyright (c) 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 + * 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_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP + +// +// String Deduplication +// +// String deduplication aims to reduce the heap live-set by deduplicating identical +// instances of String so that they share the same backing character array. +// +// The deduplication process is divided in two main parts, 1) finding the objects to +// deduplicate, and 2) deduplicating those objects. The first part is done as part of +// a normal GC cycle when objects are marked or evacuated. At this time a check is +// applied on each object to check if it is a candidate for deduplication. If so, the +// object is placed on the deduplication queue for later processing. The second part, +// processing the objects on the deduplication queue, is a concurrent phase which +// starts right after the stop-the-wold marking/evacuation phase. This phase is +// executed by the deduplication thread, which pulls deduplication candidates of the +// deduplication queue and tries to deduplicate them. +// +// A deduplication hashtable is used to keep track of all unique character arrays +// used by String objects. When deduplicating, a lookup is made in this table to see +// if there is already an identical character array somewhere on the heap. If so, the +// String object is adjusted to point to that character array, releasing the reference +// to the original array allowing it to eventually be garbage collected. If the lookup +// fails the character array is instead inserted into the hashtable so that this array +// can be shared at some point in the future. +// +// Candidate selection +// +// An object is considered a deduplication candidate if all of the following +// statements are true: +// +// - The object is an instance of java.lang.String +// +// - The object is being evacuated from a young heap region +// +// - The object is being evacuated to a young/survivor heap region and the +// object's age is equal to the deduplication age threshold +// +// or +// +// The object is being evacuated to an old heap region and the object's age is +// less than the deduplication age threshold +// +// Once an string object has been promoted to an old region, or its age is higher +// than the deduplication age threshold, is will never become a candidate again. +// This approach avoids making the same object a candidate more than once. +// +// Interned strings are a bit special. They are explicitly deduplicated just before +// being inserted into the StringTable (to avoid counteracting C2 optimizations done +// on string literals), then they also become deduplication candidates if they reach +// the deduplication age threshold or are evacuated to an old heap region. The second +// attempt to deduplicate such strings will be in vain, but we have no fast way of +// filtering them out. This has not shown to be a problem, as the number of interned +// strings is usually dwarfed by the number of normal (non-interned) strings. +// +// For additional information on string deduplication, please see JEP 192, +// http://openjdk.java.net/jeps/192 +// + +#include "memory/allocation.hpp" +#include "oops/oop.hpp" + +class OopClosure; +class BoolObjectClosure; +class ThreadClosure; +class outputStream; +class G1StringDedupTable; + +// +// Main interface for interacting with string deduplication. +// +class G1StringDedup : public AllStatic { +private: + // Single state for checking if both G1 and string deduplication is enabled. + static bool _enabled; + + // Candidate selection policies, returns true if the given object is + // candidate for string deduplication. + static bool is_candidate_from_mark(oop obj); + static bool is_candidate_from_evacuation(bool from_young, bool to_young, oop obj); + +public: + // Returns true if both G1 and string deduplication is enabled. + static bool is_enabled() { + return _enabled; + } + + // Initialize string deduplication. + static void initialize(); + + // Stop the deduplication thread. + static void stop(); + + // Immediately deduplicates the given String object, bypassing the + // the deduplication queue. + static void deduplicate(oop java_string); + + // Enqueues a deduplication candidate for later processing by the deduplication + // thread. Before enqueuing, these functions apply the appropriate candidate + // selection policy to filters out non-candidates. + static void enqueue_from_mark(oop java_string); + static void enqueue_from_evacuation(bool from_young, bool to_young, + unsigned int queue, oop java_string); + + static void oops_do(OopClosure* keep_alive); + static void unlink(BoolObjectClosure* is_alive); + static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, + bool allow_resize_and_rehash = true); + + static void threads_do(ThreadClosure* tc); + static void print_worker_threads_on(outputStream* st); + static void verify(); +}; + +// +// This closure encapsulates the state and the closures needed when scanning +// the deduplication queue and table during the unlink_or_oops_do() operation. +// A single instance of this closure is created and then shared by all worker +// threads participating in the scan. The _next_queue and _next_bucket fields +// provide a simple mechanism for GC workers to claim exclusive access to a +// queue or a table partition. +// +class G1StringDedupUnlinkOrOopsDoClosure : public StackObj { +private: + BoolObjectClosure* _is_alive; + OopClosure* _keep_alive; + G1StringDedupTable* _resized_table; + G1StringDedupTable* _rehashed_table; + size_t _next_queue; + size_t _next_bucket; + +public: + G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + bool allow_resize_and_rehash); + ~G1StringDedupUnlinkOrOopsDoClosure(); + + bool is_resizing() { + return _resized_table != NULL; + } + + G1StringDedupTable* resized_table() { + return _resized_table; + } + + bool is_rehashing() { + return _rehashed_table != NULL; + } + + // Atomically claims the next available queue for exclusive access by + // the current thread. Returns the queue number of the claimed queue. + size_t claim_queue() { + return (size_t)Atomic::add_ptr(1, &_next_queue) - 1; + } + + // Atomically claims the next available table partition for exclusive + // access by the current thread. Returns the table bucket number where + // the claimed partition starts. + size_t claim_table_partition(size_t partition_size) { + return (size_t)Atomic::add_ptr(partition_size, &_next_bucket) - partition_size; + } + + // Applies and returns the result from the is_alive closure, or + // returns true if no such closure was provided. + bool is_alive(oop o) { + if (_is_alive != NULL) { + return _is_alive->do_object_b(o); + } + return true; + } + + // Applies the keep_alive closure, or does nothing if no such + // closure was provided. + void keep_alive(oop* p) { + if (_keep_alive != NULL) { + _keep_alive->do_oop(p); + } + } +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUP_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupQueue.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupQueue.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 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 + * 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/javaClasses.hpp" +#include "gc_implementation/g1/g1StringDedupQueue.hpp" +#include "memory/gcLocker.hpp" +#include "runtime/mutexLocker.hpp" +#include "utilities/stack.inline.hpp" + +G1StringDedupQueue* G1StringDedupQueue::_queue = NULL; +const size_t G1StringDedupQueue::_max_size = 1000000; // Max number of elements per queue +const size_t G1StringDedupQueue::_max_cache_size = 0; // Max cache size per queue + +G1StringDedupQueue::G1StringDedupQueue() : + _cursor(0), + _cancel(false), + _empty(true), + _dropped(0) { + _nqueues = MAX2(ParallelGCThreads, (size_t)1); + _queues = NEW_C_HEAP_ARRAY(G1StringDedupWorkerQueue, _nqueues, mtGC); + for (size_t i = 0; i < _nqueues; i++) { + new (_queues + i) G1StringDedupWorkerQueue(G1StringDedupWorkerQueue::default_segment_size(), _max_cache_size, _max_size); + } +} + +G1StringDedupQueue::~G1StringDedupQueue() { + ShouldNotReachHere(); +} + +void G1StringDedupQueue::create() { + assert(_queue == NULL, "One string deduplication queue allowed"); + _queue = new G1StringDedupQueue(); +} + +void G1StringDedupQueue::wait() { + MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); + while (_queue->_empty && !_queue->_cancel) { + ml.wait(Mutex::_no_safepoint_check_flag); + } +} + +void G1StringDedupQueue::cancel_wait() { + MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); + _queue->_cancel = true; + ml.notify(); +} + +void G1StringDedupQueue::push(uint worker_id, oop java_string) { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); + assert(worker_id < _queue->_nqueues, "Invalid queue"); + + // Push and notify waiter + G1StringDedupWorkerQueue& worker_queue = _queue->_queues[worker_id]; + if (!worker_queue.is_full()) { + worker_queue.push(java_string); + if (_queue->_empty) { + MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); + if (_queue->_empty) { + // Mark non-empty and notify waiter + _queue->_empty = false; + ml.notify(); + } + } + } else { + // Queue is full, drop the string and update the statistics + Atomic::inc_ptr(&_queue->_dropped); + } +} + +oop G1StringDedupQueue::pop() { + assert(!SafepointSynchronize::is_at_safepoint(), "Must not be at safepoint"); + No_Safepoint_Verifier nsv; + + // Try all queues before giving up + for (size_t tries = 0; tries < _queue->_nqueues; tries++) { + // The cursor indicates where we left of last time + G1StringDedupWorkerQueue* queue = &_queue->_queues[_queue->_cursor]; + while (!queue->is_empty()) { + oop obj = queue->pop(); + // The oop we pop can be NULL if it was marked + // dead. Just ignore those and pop the next oop. + if (obj != NULL) { + return obj; + } + } + + // Try next queue + _queue->_cursor = (_queue->_cursor + 1) % _queue->_nqueues; + } + + // Mark empty + _queue->_empty = true; + + return NULL; +} + +void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl) { + // A worker thread first claims a queue, which ensures exclusive + // access to that queue, then continues to process it. + for (;;) { + // Grab next queue to scan + size_t queue = cl->claim_queue(); + if (queue >= _queue->_nqueues) { + // End of queues + break; + } + + // Scan the queue + unlink_or_oops_do(cl, queue); + } +} + +void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) { + assert(queue < _queue->_nqueues, "Invalid queue"); + StackIterator iter(_queue->_queues[queue]); + while (!iter.is_empty()) { + oop* p = iter.next_addr(); + if (*p != NULL) { + if (cl->is_alive(*p)) { + cl->keep_alive(p); + } else { + // Clear dead reference + *p = NULL; + } + } + } +} + +void G1StringDedupQueue::print_statistics(outputStream* st) { + st->print_cr( + " [Queue]\n" + " [Dropped: "UINTX_FORMAT"]", _queue->_dropped); +} + +void G1StringDedupQueue::verify() { + for (size_t i = 0; i < _queue->_nqueues; i++) { + StackIterator iter(_queue->_queues[i]); + while (!iter.is_empty()) { + oop obj = iter.next(); + if (obj != NULL) { + guarantee(Universe::heap()->is_in_reserved(obj), "Object must be on the heap"); + guarantee(!obj->is_forwarded(), "Object must not be forwarded"); + guarantee(java_lang_String::is_instance(obj), "Object must be a String"); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupQueue.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupQueue.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 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 + * 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_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP + +#include "memory/allocation.hpp" +#include "oops/oop.hpp" +#include "utilities/stack.hpp" + +class G1StringDedupUnlinkOrOopsDoClosure; + +// +// The deduplication queue acts as the communication channel between the stop-the-world +// mark/evacuation phase and the concurrent deduplication phase. Deduplication candidates +// found during mark/evacuation are placed on this queue for later processing in the +// deduplication thread. A queue entry is an oop pointing to a String object (as opposed +// to entries in the deduplication hashtable which points to character arrays). +// +// While users of the queue treat it as a single queue, it is implemented as a set of +// queues, one queue per GC worker thread, to allow lock-free and cache-friendly enqueue +// operations by the GC workers. +// +// The oops in the queue are treated as weak pointers, meaning the objects they point to +// can become unreachable and pruned (cleared) before being popped by the deduplication +// thread. +// +// Pushing to the queue is thread safe (this relies on each thread using a unique worker +// id), but only allowed during a safepoint. Popping from the queue is NOT thread safe +// and can only be done by the deduplication thread outside a safepoint. +// +// The StringDedupQueue_lock is only used for blocking and waking up the deduplication +// thread in case the queue is empty or becomes non-empty, respectively. This lock does +// not otherwise protect the queue content. +// +class G1StringDedupQueue : public CHeapObj { +private: + typedef Stack G1StringDedupWorkerQueue; + + static G1StringDedupQueue* _queue; + static const size_t _max_size; + static const size_t _max_cache_size; + + G1StringDedupWorkerQueue* _queues; + size_t _nqueues; + size_t _cursor; + bool _cancel; + volatile bool _empty; + + // Statistics counter, only used for logging. + uintx _dropped; + + G1StringDedupQueue(); + ~G1StringDedupQueue(); + + static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue); + +public: + static void create(); + + // Blocks and waits for the queue to become non-empty. + static void wait(); + + // Wakes up any thread blocked waiting for the queue to become non-empty. + static void cancel_wait(); + + // Pushes a deduplication candidate onto a specific GC worker queue. + static void push(uint worker_id, oop java_string); + + // Pops a deduplication candidate from any queue, returns NULL if + // all queues are empty. + static oop pop(); + + static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl); + + static void print_statistics(outputStream* st); + static void verify(); +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPQUEUE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupStat.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupStat.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 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 + * 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 "gc_implementation/g1/g1StringDedupStat.hpp" + +G1StringDedupStat::G1StringDedupStat() : + _inspected(0), + _skipped(0), + _hashed(0), + _known(0), + _new(0), + _new_bytes(0), + _deduped(0), + _deduped_bytes(0), + _deduped_young(0), + _deduped_young_bytes(0), + _deduped_old(0), + _deduped_old_bytes(0), + _idle(0), + _exec(0), + _block(0), + _start(0.0), + _idle_elapsed(0.0), + _exec_elapsed(0.0), + _block_elapsed(0.0) { +} + +void G1StringDedupStat::add(const G1StringDedupStat& stat) { + _inspected += stat._inspected; + _skipped += stat._skipped; + _hashed += stat._hashed; + _known += stat._known; + _new += stat._new; + _new_bytes += stat._new_bytes; + _deduped += stat._deduped; + _deduped_bytes += stat._deduped_bytes; + _deduped_young += stat._deduped_young; + _deduped_young_bytes += stat._deduped_young_bytes; + _deduped_old += stat._deduped_old; + _deduped_old_bytes += stat._deduped_old_bytes; + _idle += stat._idle; + _exec += stat._exec; + _block += stat._block; + _idle_elapsed += stat._idle_elapsed; + _exec_elapsed += stat._exec_elapsed; + _block_elapsed += stat._block_elapsed; +} + +void G1StringDedupStat::print_summary(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) { + double total_deduped_bytes_percent = 0.0; + + if (total_stat._new_bytes > 0) { + // Avoid division by zero + total_deduped_bytes_percent = (double)total_stat._deduped_bytes / (double)total_stat._new_bytes * 100.0; + } + + st->date_stamp(PrintGCDateStamps); + st->stamp(PrintGCTimeStamps); + st->print_cr( + "[GC concurrent-string-deduplication, " + G1_STRDEDUP_BYTES_FORMAT_NS"->"G1_STRDEDUP_BYTES_FORMAT_NS"("G1_STRDEDUP_BYTES_FORMAT_NS"), avg " + G1_STRDEDUP_PERCENT_FORMAT_NS", "G1_STRDEDUP_TIME_FORMAT"]", + G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes), + G1_STRDEDUP_BYTES_PARAM(last_stat._new_bytes - last_stat._deduped_bytes), + G1_STRDEDUP_BYTES_PARAM(last_stat._deduped_bytes), + total_deduped_bytes_percent, + last_stat._exec_elapsed); +} + +void G1StringDedupStat::print_statistics(outputStream* st, const G1StringDedupStat& stat, bool total) { + double young_percent = 0.0; + double old_percent = 0.0; + double skipped_percent = 0.0; + double hashed_percent = 0.0; + double known_percent = 0.0; + double new_percent = 0.0; + double deduped_percent = 0.0; + double deduped_bytes_percent = 0.0; + double deduped_young_percent = 0.0; + double deduped_young_bytes_percent = 0.0; + double deduped_old_percent = 0.0; + double deduped_old_bytes_percent = 0.0; + + if (stat._inspected > 0) { + // Avoid division by zero + skipped_percent = (double)stat._skipped / (double)stat._inspected * 100.0; + hashed_percent = (double)stat._hashed / (double)stat._inspected * 100.0; + known_percent = (double)stat._known / (double)stat._inspected * 100.0; + new_percent = (double)stat._new / (double)stat._inspected * 100.0; + } + + if (stat._new > 0) { + // Avoid division by zero + deduped_percent = (double)stat._deduped / (double)stat._new * 100.0; + } + + if (stat._deduped > 0) { + // Avoid division by zero + deduped_young_percent = (double)stat._deduped_young / (double)stat._deduped * 100.0; + deduped_old_percent = (double)stat._deduped_old / (double)stat._deduped * 100.0; + } + + if (stat._new_bytes > 0) { + // Avoid division by zero + deduped_bytes_percent = (double)stat._deduped_bytes / (double)stat._new_bytes * 100.0; + } + + if (stat._deduped_bytes > 0) { + // Avoid division by zero + deduped_young_bytes_percent = (double)stat._deduped_young_bytes / (double)stat._deduped_bytes * 100.0; + deduped_old_bytes_percent = (double)stat._deduped_old_bytes / (double)stat._deduped_bytes * 100.0; + } + + if (total) { + st->print_cr( + " [Total Exec: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT", Idle: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT", Blocked: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT"]", + stat._exec, stat._exec_elapsed, stat._idle, stat._idle_elapsed, stat._block, stat._block_elapsed); + } else { + st->print_cr( + " [Last Exec: "G1_STRDEDUP_TIME_FORMAT", Idle: "G1_STRDEDUP_TIME_FORMAT", Blocked: "UINTX_FORMAT"/"G1_STRDEDUP_TIME_FORMAT"]", + stat._exec_elapsed, stat._idle_elapsed, stat._block, stat._block_elapsed); + } + st->print_cr( + " [Inspected: "G1_STRDEDUP_OBJECTS_FORMAT"]\n" + " [Skipped: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n" + " [Hashed: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n" + " [Known: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n" + " [New: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"]\n" + " [Deduplicated: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n" + " [Young: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]\n" + " [Old: "G1_STRDEDUP_OBJECTS_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT") "G1_STRDEDUP_BYTES_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT")]", + stat._inspected, + stat._skipped, skipped_percent, + stat._hashed, hashed_percent, + stat._known, known_percent, + stat._new, new_percent, G1_STRDEDUP_BYTES_PARAM(stat._new_bytes), + stat._deduped, deduped_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_bytes), deduped_bytes_percent, + stat._deduped_young, deduped_young_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_young_bytes), deduped_young_bytes_percent, + stat._deduped_old, deduped_old_percent, G1_STRDEDUP_BYTES_PARAM(stat._deduped_old_bytes), deduped_old_bytes_percent); +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupStat.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupStat.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 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 + * 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_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP + +#include "memory/allocation.hpp" +#include "runtime/os.hpp" + +// Macros for GC log output formating +#define G1_STRDEDUP_OBJECTS_FORMAT UINTX_FORMAT_W(12) +#define G1_STRDEDUP_TIME_FORMAT "%1.7lf secs" +#define G1_STRDEDUP_PERCENT_FORMAT "%5.1lf%%" +#define G1_STRDEDUP_PERCENT_FORMAT_NS "%.1lf%%" +#define G1_STRDEDUP_BYTES_FORMAT "%8.1lf%s" +#define G1_STRDEDUP_BYTES_FORMAT_NS "%.1lf%s" +#define G1_STRDEDUP_BYTES_PARAM(bytes) byte_size_in_proper_unit((double)(bytes)), proper_unit_for_byte_size((bytes)) + +// +// Statistics gathered by the deduplication thread. +// +class G1StringDedupStat : public StackObj { +private: + // Counters + uintx _inspected; + uintx _skipped; + uintx _hashed; + uintx _known; + uintx _new; + uintx _new_bytes; + uintx _deduped; + uintx _deduped_bytes; + uintx _deduped_young; + uintx _deduped_young_bytes; + uintx _deduped_old; + uintx _deduped_old_bytes; + uintx _idle; + uintx _exec; + uintx _block; + + // Time spent by the deduplication thread in different phases + double _start; + double _idle_elapsed; + double _exec_elapsed; + double _block_elapsed; + +public: + G1StringDedupStat(); + + void inc_inspected() { + _inspected++; + } + + void inc_skipped() { + _skipped++; + } + + void inc_hashed() { + _hashed++; + } + + void inc_known() { + _known++; + } + + void inc_new(uintx bytes) { + _new++; + _new_bytes += bytes; + } + + void inc_deduped_young(uintx bytes) { + _deduped++; + _deduped_bytes += bytes; + _deduped_young++; + _deduped_young_bytes += bytes; + } + + void inc_deduped_old(uintx bytes) { + _deduped++; + _deduped_bytes += bytes; + _deduped_old++; + _deduped_old_bytes += bytes; + } + + void mark_idle() { + _start = os::elapsedTime(); + _idle++; + } + + void mark_exec() { + double now = os::elapsedTime(); + _idle_elapsed = now - _start; + _start = now; + _exec++; + } + + void mark_block() { + double now = os::elapsedTime(); + _exec_elapsed += now - _start; + _start = now; + _block++; + } + + void mark_unblock() { + double now = os::elapsedTime(); + _block_elapsed += now - _start; + _start = now; + } + + void mark_done() { + double now = os::elapsedTime(); + _exec_elapsed += now - _start; + } + + void add(const G1StringDedupStat& stat); + + static void print_summary(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat); + static void print_statistics(outputStream* st, const G1StringDedupStat& stat, bool total); +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPSTAT_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,569 @@ +/* + * Copyright (c) 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 + * 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/altHashing.hpp" +#include "classfile/javaClasses.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/g1StringDedupTable.hpp" +#include "memory/gcLocker.hpp" +#include "memory/padded.inline.hpp" +#include "oops/typeArrayOop.hpp" +#include "runtime/mutexLocker.hpp" + +// +// Freelist in the deduplication table entry cache. Links table +// entries together using their _next fields. +// +class G1StringDedupEntryFreeList : public CHeapObj { +private: + G1StringDedupEntry* _list; + size_t _length; + +public: + G1StringDedupEntryFreeList() : + _list(NULL), + _length(0) { + } + + void add(G1StringDedupEntry* entry) { + entry->set_next(_list); + _list = entry; + _length++; + } + + G1StringDedupEntry* remove() { + G1StringDedupEntry* entry = _list; + if (entry != NULL) { + _list = entry->next(); + _length--; + } + return entry; + } + + size_t length() { + return _length; + } +}; + +// +// Cache of deduplication table entries. This cache provides fast allocation and +// reuse of table entries to lower the pressure on the underlying allocator. +// But more importantly, it provides fast/deferred freeing of table entries. This +// is important because freeing of table entries is done during stop-the-world +// phases and it is not uncommon for large number of entries to be freed at once. +// Tables entries that are freed during these phases are placed onto a freelist in +// the cache. The deduplication thread, which executes in a concurrent phase, will +// later reuse or free the underlying memory for these entries. +// +// The cache allows for single-threaded allocations and multi-threaded frees. +// Allocations are synchronized by StringDedupTable_lock as part of a table +// modification. +// +class G1StringDedupEntryCache : public CHeapObj { +private: + // One freelist per GC worker to allow lock less freeing of + // entries while doing a parallel scan of the table. Using + // PaddedEnd to avoid false sharing. + PaddedEnd* _lists; + size_t _nlists; + +public: + G1StringDedupEntryCache(); + ~G1StringDedupEntryCache(); + + // Get a table entry from the cache freelist, or allocate a new + // entry if the cache is empty. + G1StringDedupEntry* alloc(); + + // Insert a table entry into the cache freelist. + void free(G1StringDedupEntry* entry, uint worker_id); + + // Returns current number of entries in the cache. + size_t size(); + + // If the cache has grown above the given max size, trim it down + // and deallocate the memory occupied by trimmed of entries. + void trim(size_t max_size); +}; + +G1StringDedupEntryCache::G1StringDedupEntryCache() { + _nlists = MAX2(ParallelGCThreads, (size_t)1); + _lists = PaddedArray::create_unfreeable((uint)_nlists); +} + +G1StringDedupEntryCache::~G1StringDedupEntryCache() { + ShouldNotReachHere(); +} + +G1StringDedupEntry* G1StringDedupEntryCache::alloc() { + for (size_t i = 0; i < _nlists; i++) { + G1StringDedupEntry* entry = _lists[i].remove(); + if (entry != NULL) { + return entry; + } + } + return new G1StringDedupEntry(); +} + +void G1StringDedupEntryCache::free(G1StringDedupEntry* entry, uint worker_id) { + assert(entry->obj() != NULL, "Double free"); + assert(worker_id < _nlists, "Invalid worker id"); + entry->set_obj(NULL); + entry->set_hash(0); + _lists[worker_id].add(entry); +} + +size_t G1StringDedupEntryCache::size() { + size_t size = 0; + for (size_t i = 0; i < _nlists; i++) { + size += _lists[i].length(); + } + return size; +} + +void G1StringDedupEntryCache::trim(size_t max_size) { + size_t cache_size = 0; + for (size_t i = 0; i < _nlists; i++) { + G1StringDedupEntryFreeList* list = &_lists[i]; + cache_size += list->length(); + while (cache_size > max_size) { + G1StringDedupEntry* entry = list->remove(); + assert(entry != NULL, "Should not be null"); + cache_size--; + delete entry; + } + } +} + +G1StringDedupTable* G1StringDedupTable::_table = NULL; +G1StringDedupEntryCache* G1StringDedupTable::_entry_cache = NULL; + +const size_t G1StringDedupTable::_min_size = (1 << 10); // 1024 +const size_t G1StringDedupTable::_max_size = (1 << 24); // 16777216 +const double G1StringDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load +const double G1StringDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load +const double G1StringDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size +const uintx G1StringDedupTable::_rehash_multiple = 60; // Hash bucket has 60 times more collisions than expected +const uintx G1StringDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor); + +uintx G1StringDedupTable::_entries_added = 0; +uintx G1StringDedupTable::_entries_removed = 0; +uintx G1StringDedupTable::_resize_count = 0; +uintx G1StringDedupTable::_rehash_count = 0; + +G1StringDedupTable::G1StringDedupTable(size_t size, jint hash_seed) : + _size(size), + _entries(0), + _grow_threshold((uintx)(size * _grow_load_factor)), + _shrink_threshold((uintx)(size * _shrink_load_factor)), + _rehash_needed(false), + _hash_seed(hash_seed) { + assert(is_power_of_2(size), "Table size must be a power of 2"); + _buckets = NEW_C_HEAP_ARRAY(G1StringDedupEntry*, _size, mtGC); + memset(_buckets, 0, _size * sizeof(G1StringDedupEntry*)); +} + +G1StringDedupTable::~G1StringDedupTable() { + FREE_C_HEAP_ARRAY(G1StringDedupEntry*, _buckets, mtGC); +} + +void G1StringDedupTable::create() { + assert(_table == NULL, "One string deduplication table allowed"); + _entry_cache = new G1StringDedupEntryCache(); + _table = new G1StringDedupTable(_min_size); +} + +void G1StringDedupTable::add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list) { + G1StringDedupEntry* entry = _entry_cache->alloc(); + entry->set_obj(value); + entry->set_hash(hash); + entry->set_next(*list); + *list = entry; + _entries++; +} + +void G1StringDedupTable::remove(G1StringDedupEntry** pentry, uint worker_id) { + G1StringDedupEntry* entry = *pentry; + *pentry = entry->next(); + _entry_cache->free(entry, worker_id); +} + +void G1StringDedupTable::transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest) { + G1StringDedupEntry* entry = *pentry; + *pentry = entry->next(); + unsigned int hash = entry->hash(); + size_t index = dest->hash_to_index(hash); + G1StringDedupEntry** list = dest->bucket(index); + entry->set_next(*list); + *list = entry; +} + +bool G1StringDedupTable::equals(typeArrayOop value1, typeArrayOop value2) { + return (value1 == value2 || + (value1->length() == value2->length() && + (!memcmp(value1->base(T_CHAR), + value2->base(T_CHAR), + value1->length() * sizeof(jchar))))); +} + +typeArrayOop G1StringDedupTable::lookup(typeArrayOop value, unsigned int hash, + G1StringDedupEntry** list, uintx &count) { + for (G1StringDedupEntry* entry = *list; entry != NULL; entry = entry->next()) { + if (entry->hash() == hash) { + typeArrayOop existing_value = entry->obj(); + if (equals(value, existing_value)) { + // Match found + return existing_value; + } + } + count++; + } + + // Not found + return NULL; +} + +typeArrayOop G1StringDedupTable::lookup_or_add_inner(typeArrayOop value, unsigned int hash) { + size_t index = hash_to_index(hash); + G1StringDedupEntry** list = bucket(index); + uintx count = 0; + + // Lookup in list + typeArrayOop existing_value = lookup(value, hash, list, count); + + // Check if rehash is needed + if (count > _rehash_threshold) { + _rehash_needed = true; + } + + if (existing_value == NULL) { + // Not found, add new entry + add(value, hash, list); + + // Update statistics + _entries_added++; + } + + return existing_value; +} + +unsigned int G1StringDedupTable::hash_code(typeArrayOop value) { + unsigned int hash; + int length = value->length(); + const jchar* data = (jchar*)value->base(T_CHAR); + + if (use_java_hash()) { + hash = java_lang_String::hash_code(data, length); + } else { + hash = AltHashing::murmur3_32(_table->_hash_seed, data, length); + } + + return hash; +} + +void G1StringDedupTable::deduplicate(oop java_string, G1StringDedupStat& stat) { + assert(java_lang_String::is_instance(java_string), "Must be a string"); + No_Safepoint_Verifier nsv; + + stat.inc_inspected(); + + typeArrayOop value = java_lang_String::value(java_string); + if (value == NULL) { + // String has no value + stat.inc_skipped(); + return; + } + + unsigned int hash = 0; + + if (use_java_hash()) { + // Get hash code from cache + hash = java_lang_String::hash(java_string); + } + + if (hash == 0) { + // Compute hash + hash = hash_code(value); + stat.inc_hashed(); + } + + if (use_java_hash() && hash != 0) { + // Store hash code in cache + java_lang_String::set_hash(java_string, hash); + } + + typeArrayOop existing_value = lookup_or_add(value, hash); + if (existing_value == value) { + // Same value, already known + stat.inc_known(); + return; + } + + // Get size of value array + uintx size_in_bytes = value->size() * HeapWordSize; + stat.inc_new(size_in_bytes); + + if (existing_value != NULL) { + // Enqueue the reference to make sure it is kept alive. Concurrent mark might + // otherwise declare it dead if there are no other strong references to this object. + G1SATBCardTableModRefBS::enqueue(existing_value); + + // Existing value found, deduplicate string + java_lang_String::set_value(java_string, existing_value); + + if (G1CollectedHeap::heap()->is_in_young(value)) { + stat.inc_deduped_young(size_in_bytes); + } else { + stat.inc_deduped_old(size_in_bytes); + } + } +} + +G1StringDedupTable* G1StringDedupTable::prepare_resize() { + size_t size = _table->_size; + + // Check if the hashtable needs to be resized + if (_table->_entries > _table->_grow_threshold) { + // Grow table, double the size + size *= 2; + if (size > _max_size) { + // Too big, don't resize + return NULL; + } + } else if (_table->_entries < _table->_shrink_threshold) { + // Shrink table, half the size + size /= 2; + if (size < _min_size) { + // Too small, don't resize + return NULL; + } + } else if (StringDeduplicationResizeALot) { + // Force grow + size *= 2; + if (size > _max_size) { + // Too big, force shrink instead + size /= 4; + } + } else { + // Resize not needed + return NULL; + } + + // Update statistics + _resize_count++; + + // Allocate the new table. The new table will be populated by workers + // calling unlink_or_oops_do() and finally installed by finish_resize(). + return new G1StringDedupTable(size, _table->_hash_seed); +} + +void G1StringDedupTable::finish_resize(G1StringDedupTable* resized_table) { + assert(resized_table != NULL, "Invalid table"); + + resized_table->_entries = _table->_entries; + + // Free old table + delete _table; + + // Install new table + _table = resized_table; +} + +void G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id) { + // The table is divided into partitions to allow lock-less parallel processing by + // multiple worker threads. A worker thread first claims a partition, which ensures + // exclusive access to that part of the table, then continues to process it. To allow + // shrinking of the table in parallel we also need to make sure that the same worker + // thread processes all partitions where entries will hash to the same destination + // partition. Since the table size is always a power of two and we always shrink by + // dividing the table in half, we know that for a given partition there is only one + // other partition whoes entries will hash to the same destination partition. That + // other partition is always the sibling partition in the second half of the table. + // For example, if the table is divided into 8 partitions, the sibling of partition 0 + // is partition 4, the sibling of partition 1 is partition 5, etc. + size_t table_half = _table->_size / 2; + + // Let each partition be one page worth of buckets + size_t partition_size = MIN2(table_half, os::vm_page_size() / sizeof(G1StringDedupEntry*)); + assert(table_half % partition_size == 0, "Invalid partition size"); + + // Number of entries removed during the scan + uintx removed = 0; + + for (;;) { + // Grab next partition to scan + size_t partition_begin = cl->claim_table_partition(partition_size); + size_t partition_end = partition_begin + partition_size; + if (partition_begin >= table_half) { + // End of table + break; + } + + // Scan the partition followed by the sibling partition in the second half of the table + removed += unlink_or_oops_do(cl, partition_begin, partition_end, worker_id); + removed += unlink_or_oops_do(cl, table_half + partition_begin, table_half + partition_end, worker_id); + } + + // Delayed update avoid contention on the table lock + if (removed > 0) { + MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); + _table->_entries -= removed; + _entries_removed += removed; + } +} + +uintx G1StringDedupTable::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, + size_t partition_begin, + size_t partition_end, + uint worker_id) { + uintx removed = 0; + for (size_t bucket = partition_begin; bucket < partition_end; bucket++) { + G1StringDedupEntry** entry = _table->bucket(bucket); + while (*entry != NULL) { + oop* p = (oop*)(*entry)->obj_addr(); + if (cl->is_alive(*p)) { + cl->keep_alive(p); + if (cl->is_resizing()) { + // We are resizing the table, transfer entry to the new table + _table->transfer(entry, cl->resized_table()); + } else { + if (cl->is_rehashing()) { + // We are rehashing the table, rehash the entry but keep it + // in the table. We can't transfer entries into the new table + // at this point since we don't have exclusive access to all + // destination partitions. finish_rehash() will do a single + // threaded transfer of all entries. + typeArrayOop value = (typeArrayOop)*p; + unsigned int hash = hash_code(value); + (*entry)->set_hash(hash); + } + + // Move to next entry + entry = (*entry)->next_addr(); + } + } else { + // Not alive, remove entry from table + _table->remove(entry, worker_id); + removed++; + } + } + } + + return removed; +} + +G1StringDedupTable* G1StringDedupTable::prepare_rehash() { + if (!_table->_rehash_needed && !StringDeduplicationRehashALot) { + // Rehash not needed + return NULL; + } + + // Update statistics + _rehash_count++; + + // Compute new hash seed + _table->_hash_seed = AltHashing::compute_seed(); + + // Allocate the new table, same size and hash seed + return new G1StringDedupTable(_table->_size, _table->_hash_seed); +} + +void G1StringDedupTable::finish_rehash(G1StringDedupTable* rehashed_table) { + assert(rehashed_table != NULL, "Invalid table"); + + // Move all newly rehashed entries into the correct buckets in the new table + for (size_t bucket = 0; bucket < _table->_size; bucket++) { + G1StringDedupEntry** entry = _table->bucket(bucket); + while (*entry != NULL) { + _table->transfer(entry, rehashed_table); + } + } + + rehashed_table->_entries = _table->_entries; + + // Free old table + delete _table; + + // Install new table + _table = rehashed_table; +} + +void G1StringDedupTable::verify() { + for (size_t bucket = 0; bucket < _table->_size; bucket++) { + // Verify entries + G1StringDedupEntry** entry = _table->bucket(bucket); + while (*entry != NULL) { + typeArrayOop value = (*entry)->obj(); + guarantee(value != NULL, "Object must not be NULL"); + guarantee(Universe::heap()->is_in_reserved(value), "Object must be on the heap"); + guarantee(!value->is_forwarded(), "Object must not be forwarded"); + guarantee(value->is_typeArray(), "Object must be a typeArrayOop"); + unsigned int hash = hash_code(value); + guarantee((*entry)->hash() == hash, "Table entry has inorrect hash"); + guarantee(_table->hash_to_index(hash) == bucket, "Table entry has incorrect index"); + entry = (*entry)->next_addr(); + } + + // Verify that we do not have entries with identical oops or identical arrays. + // We only need to compare entries in the same bucket. If the same oop or an + // identical array has been inserted more than once into different/incorrect + // buckets the verification step above will catch that. + G1StringDedupEntry** entry1 = _table->bucket(bucket); + while (*entry1 != NULL) { + typeArrayOop value1 = (*entry1)->obj(); + G1StringDedupEntry** entry2 = (*entry1)->next_addr(); + while (*entry2 != NULL) { + typeArrayOop value2 = (*entry2)->obj(); + guarantee(!equals(value1, value2), "Table entries must not have identical arrays"); + entry2 = (*entry2)->next_addr(); + } + entry1 = (*entry1)->next_addr(); + } + } +} + +void G1StringDedupTable::trim_entry_cache() { + MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); + size_t max_cache_size = (size_t)(_table->_size * _max_cache_factor); + _entry_cache->trim(max_cache_size); +} + +void G1StringDedupTable::print_statistics(outputStream* st) { + st->print_cr( + " [Table]\n" + " [Memory Usage: "G1_STRDEDUP_BYTES_FORMAT_NS"]\n" + " [Size: "SIZE_FORMAT", Min: "SIZE_FORMAT", Max: "SIZE_FORMAT"]\n" + " [Entries: "UINTX_FORMAT", Load: "G1_STRDEDUP_PERCENT_FORMAT_NS", Cached: " UINTX_FORMAT ", Added: "UINTX_FORMAT", Removed: "UINTX_FORMAT"]\n" + " [Resize Count: "UINTX_FORMAT", Shrink Threshold: "UINTX_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT_NS"), Grow Threshold: "UINTX_FORMAT"("G1_STRDEDUP_PERCENT_FORMAT_NS")]\n" + " [Rehash Count: "UINTX_FORMAT", Rehash Threshold: "UINTX_FORMAT", Hash Seed: 0x%x]\n" + " [Age Threshold: "UINTX_FORMAT"]", + G1_STRDEDUP_BYTES_PARAM(_table->_size * sizeof(G1StringDedupEntry*) + (_table->_entries + _entry_cache->size()) * sizeof(G1StringDedupEntry)), + _table->_size, _min_size, _max_size, + _table->_entries, (double)_table->_entries / (double)_table->_size * 100.0, _entry_cache->size(), _entries_added, _entries_removed, + _resize_count, _table->_shrink_threshold, _shrink_load_factor * 100.0, _table->_grow_threshold, _grow_load_factor * 100.0, + _rehash_count, _rehash_threshold, _table->_hash_seed, + StringDeduplicationAgeThreshold); +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,230 @@ +/* + * Copyright (c) 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 + * 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_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP + +#include "gc_implementation/g1/g1StringDedupStat.hpp" +#include "runtime/mutexLocker.hpp" + +class G1StringDedupEntryCache; + +// +// Table entry in the deduplication hashtable. Points weakly to the +// character array. Can be chained in a linked list in case of hash +// collisions or when placed in a freelist in the entry cache. +// +class G1StringDedupEntry : public CHeapObj { +private: + G1StringDedupEntry* _next; + unsigned int _hash; + typeArrayOop _obj; + +public: + G1StringDedupEntry() : + _next(NULL), + _hash(0), + _obj(NULL) { + } + + G1StringDedupEntry* next() { + return _next; + } + + G1StringDedupEntry** next_addr() { + return &_next; + } + + void set_next(G1StringDedupEntry* next) { + _next = next; + } + + unsigned int hash() { + return _hash; + } + + void set_hash(unsigned int hash) { + _hash = hash; + } + + typeArrayOop obj() { + return _obj; + } + + typeArrayOop* obj_addr() { + return &_obj; + } + + void set_obj(typeArrayOop obj) { + _obj = obj; + } +}; + +// +// The deduplication hashtable keeps track of all unique character arrays used +// by String objects. Each table entry weakly points to an character array, allowing +// otherwise unreachable character arrays to be declared dead and pruned from the +// table. +// +// The table is dynamically resized to accommodate the current number of table entries. +// The table has hash buckets with chains for hash collision. If the average chain +// length goes above or below given thresholds the table grows or shrinks accordingly. +// +// The table is also dynamically rehashed (using a new hash seed) if it becomes severely +// unbalanced, i.e., a hash chain is significantly longer than average. +// +// All access to the table is protected by the StringDedupTable_lock, except under +// safepoints in which case GC workers are allowed to access a table partitions they +// have claimed without first acquiring the lock. Note however, that this applies only +// the table partition (i.e. a range of elements in _buckets), not other parts of the +// table such as the _entries field, statistics counters, etc. +// +class G1StringDedupTable : public CHeapObj { +private: + // The currently active hashtable instance. Only modified when + // the table is resizes or rehashed. + static G1StringDedupTable* _table; + + // Cache for reuse and fast alloc/free of table entries. + static G1StringDedupEntryCache* _entry_cache; + + G1StringDedupEntry** _buckets; + size_t _size; + uintx _entries; + uintx _shrink_threshold; + uintx _grow_threshold; + bool _rehash_needed; + + // The hash seed also dictates which hash function to use. A + // zero hash seed means we will use the Java compatible hash + // function (which doesn't use a seed), and a non-zero hash + // seed means we use the murmur3 hash function. + jint _hash_seed; + + // Constants governing table resize/rehash/cache. + static const size_t _min_size; + static const size_t _max_size; + static const double _grow_load_factor; + static const double _shrink_load_factor; + static const uintx _rehash_multiple; + static const uintx _rehash_threshold; + static const double _max_cache_factor; + + // Table statistics, only used for logging. + static uintx _entries_added; + static uintx _entries_removed; + static uintx _resize_count; + static uintx _rehash_count; + + G1StringDedupTable(size_t size, jint hash_seed = 0); + ~G1StringDedupTable(); + + // Returns the hash bucket at the given index. + G1StringDedupEntry** bucket(size_t index) { + return _buckets + index; + } + + // Returns the hash bucket index for the given hash code. + size_t hash_to_index(unsigned int hash) { + return (size_t)hash & (_size - 1); + } + + // Adds a new table entry to the given hash bucket. + void add(typeArrayOop value, unsigned int hash, G1StringDedupEntry** list); + + // Removes the given table entry from the table. + void remove(G1StringDedupEntry** pentry, uint worker_id); + + // Transfers a table entry from the current table to the destination table. + void transfer(G1StringDedupEntry** pentry, G1StringDedupTable* dest); + + // Returns an existing character array in the given hash bucket, or NULL + // if no matching character array exists. + typeArrayOop lookup(typeArrayOop value, unsigned int hash, + G1StringDedupEntry** list, uintx &count); + + // Returns an existing character array in the table, or inserts a new + // table entry if no matching character array exists. + typeArrayOop lookup_or_add_inner(typeArrayOop value, unsigned int hash); + + // Thread safe lookup or add of table entry + static typeArrayOop lookup_or_add(typeArrayOop value, unsigned int hash) { + // Protect the table from concurrent access. Also note that this lock + // acts as a fence for _table, which could have been replaced by a new + // instance if the table was resized or rehashed. + MutexLockerEx ml(StringDedupTable_lock, Mutex::_no_safepoint_check_flag); + return _table->lookup_or_add_inner(value, hash); + } + + // Returns true if the hashtable is currently using a Java compatible + // hash function. + static bool use_java_hash() { + return _table->_hash_seed == 0; + } + + static bool equals(typeArrayOop value1, typeArrayOop value2); + + // Computes the hash code for the given character array, using the + // currently active hash function and hash seed. + static unsigned int hash_code(typeArrayOop value); + + static uintx unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, + size_t partition_begin, + size_t partition_end, + uint worker_id); + +public: + static void create(); + + // Deduplicates the given String object, or adds its backing + // character array to the deduplication hashtable. + static void deduplicate(oop java_string, G1StringDedupStat& stat); + + // If a table resize is needed, returns a newly allocated empty + // hashtable of the proper size. + static G1StringDedupTable* prepare_resize(); + + // Installs a newly resized table as the currently active table + // and deletes the previously active table. + static void finish_resize(G1StringDedupTable* resized_table); + + // If a table rehash is needed, returns a newly allocated empty + // hashtable and updates the hash seed. + static G1StringDedupTable* prepare_rehash(); + + // Transfers rehashed entries from the currently active table into + // the new table. Installs the new table as the currently active table + // and deletes the previously active table. + static void finish_rehash(G1StringDedupTable* rehashed_table); + + // If the table entry cache has grown too large, trim it down according to policy + static void trim_entry_cache(); + + static void unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, uint worker_id); + + static void print_statistics(outputStream* st); + static void verify(); +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTABLE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 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 + * 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 "gc_implementation/g1/g1Log.hpp" +#include "gc_implementation/g1/g1StringDedup.hpp" +#include "gc_implementation/g1/g1StringDedupTable.hpp" +#include "gc_implementation/g1/g1StringDedupThread.hpp" +#include "gc_implementation/g1/g1StringDedupQueue.hpp" + +G1StringDedupThread* G1StringDedupThread::_thread = NULL; + +G1StringDedupThread::G1StringDedupThread() : + ConcurrentGCThread() { + set_name("String Deduplication Thread"); + create_and_start(); +} + +G1StringDedupThread::~G1StringDedupThread() { + ShouldNotReachHere(); +} + +void G1StringDedupThread::create() { + assert(G1StringDedup::is_enabled(), "String deduplication not enabled"); + assert(_thread == NULL, "One string deduplication thread allowed"); + _thread = new G1StringDedupThread(); +} + +G1StringDedupThread* G1StringDedupThread::thread() { + assert(G1StringDedup::is_enabled(), "String deduplication not enabled"); + assert(_thread != NULL, "String deduplication thread not created"); + return _thread; +} + +void G1StringDedupThread::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +void G1StringDedupThread::run() { + G1StringDedupStat total_stat; + + initialize_in_thread(); + wait_for_universe_init(); + + // Main loop + for (;;) { + G1StringDedupStat stat; + + stat.mark_idle(); + + // Wait for the queue to become non-empty + G1StringDedupQueue::wait(); + if (_should_terminate) { + break; + } + + // Include this thread in safepoints + stsJoin(); + + stat.mark_exec(); + + // Process the queue + for (;;) { + oop java_string = G1StringDedupQueue::pop(); + if (java_string == NULL) { + break; + } + + G1StringDedupTable::deduplicate(java_string, stat); + + // Safepoint this thread if needed + if (stsShouldYield()) { + stat.mark_block(); + stsYield(NULL); + stat.mark_unblock(); + } + } + + G1StringDedupTable::trim_entry_cache(); + + stat.mark_done(); + + // Print statistics + total_stat.add(stat); + print(gclog_or_tty, stat, total_stat); + + // Exclude this thread from safepoints + stsLeave(); + } + + terminate(); +} + +void G1StringDedupThread::stop() { + { + MonitorLockerEx ml(Terminator_lock); + _thread->_should_terminate = true; + } + + G1StringDedupQueue::cancel_wait(); + + { + MonitorLockerEx ml(Terminator_lock); + while (!_thread->_has_terminated) { + ml.wait(); + } + } +} + +void G1StringDedupThread::print(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat) { + if (G1Log::fine() || PrintStringDeduplicationStatistics) { + G1StringDedupStat::print_summary(st, last_stat, total_stat); + if (PrintStringDeduplicationStatistics) { + G1StringDedupStat::print_statistics(st, last_stat, false); + G1StringDedupStat::print_statistics(st, total_stat, true); + G1StringDedupTable::print_statistics(st); + G1StringDedupQueue::print_statistics(st); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1StringDedupThread.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/g1/g1StringDedupThread.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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 + * 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_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP + +#include "gc_implementation/g1/g1StringDedupStat.hpp" +#include "gc_implementation/shared/concurrentGCThread.hpp" + +// +// The deduplication thread is where the actual deduplication occurs. It waits for +// deduplication candidates to appear on the deduplication queue, removes them from +// the queue and tries to deduplicate them. It uses the deduplication hashtable to +// find identical, already existing, character arrays on the heap. The thread runs +// concurrently with the Java application but participates in safepoints to allow +// the GC to adjust and unlink oops from the deduplication queue and table. +// +class G1StringDedupThread: public ConcurrentGCThread { +private: + static G1StringDedupThread* _thread; + + G1StringDedupThread(); + ~G1StringDedupThread(); + + void print(outputStream* st, const G1StringDedupStat& last_stat, const G1StringDedupStat& total_stat); + +public: + static void create(); + static void stop(); + + static G1StringDedupThread* thread(); + + virtual void run(); + virtual void print_on(outputStream* st) const; +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1STRINGDEDUPTHREAD_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1_globals.hpp --- a/src/share/vm/gc_implementation/g1/g1_globals.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1_globals.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -71,6 +71,9 @@ diagnostic(bool, G1TraceConcRefinement, false, \ "Trace G1 concurrent refinement") \ \ + experimental(bool, G1TraceStringSymbolTableScrubbing, false, \ + "Trace information string and symbol table scrubbing.") \ + \ product(double, G1ConcMarkStepDurationMillis, 10.0, \ "Target duration of individual concurrent marking steps " \ "in milliseconds.") \ @@ -282,6 +285,10 @@ product(uintx, G1MixedGCCountTarget, 8, \ "The target number of mixed GCs after a marking cycle.") \ \ + experimental(uintx, G1CodeRootsChunkCacheKeepPercent, 10, \ + "The amount of code root chunks that should be kept at most " \ + "as percentage of already allocated.") \ + \ experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \ "An upper bound for the number of old CSet regions expressed " \ "as a percentage of the heap size.") \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp --- a/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -33,19 +33,16 @@ // Forward declarations. enum G1Barrier { G1BarrierNone, - G1BarrierRS, G1BarrierEvac, G1BarrierKlass }; -template +template class G1ParCopyClosure; class G1ParScanClosure; class G1ParPushHeapRSClosure; -typedef G1ParCopyClosure G1ParScanHeapEvacClosure; - class FilterIntoCSClosure; class FilterOutOfRegionClosure; class G1CMOopClosure; @@ -62,7 +59,6 @@ #endif #define FURTHER_SPECIALIZED_OOP_OOP_ITERATE_CLOSURES(f) \ - f(G1ParScanHeapEvacClosure,_nv) \ f(G1ParScanClosure,_nv) \ f(G1ParPushHeapRSClosure,_nv) \ f(FilterIntoCSClosure,_nv) \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegion.cpp --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -34,6 +34,8 @@ #include "memory/iterator.hpp" #include "oops/oop.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + int HeapRegion::LogOfHRGrainBytes = 0; int HeapRegion::LogOfHRGrainWords = 0; size_t HeapRegion::GrainBytes = 0; @@ -205,7 +207,7 @@ init_top_at_mark_start(); } -void HeapRegion::hr_clear(bool par, bool clear_space) { +void HeapRegion::hr_clear(bool par, bool clear_space, bool locked) { assert(_humongous_type == NotHumongous, "we should have already filtered out humongous regions"); assert(_humongous_start_region == NULL, @@ -223,7 +225,11 @@ if (!par) { // If this is parallel, this will be done later. HeapRegionRemSet* hrrs = rem_set(); - hrrs->clear(); + if (locked) { + hrrs->clear_locked(); + } else { + hrrs->clear(); + } _claimed = InitialClaimValue; } zero_marked_bytes(); @@ -352,7 +358,7 @@ _claimed(InitialClaimValue), _evacuation_failed(false), _prev_marked_bytes(0), _next_marked_bytes(0), _gc_efficiency(0.0), _young_type(NotYoung), _next_young_region(NULL), - _next_dirty_cards_region(NULL), _next(NULL), _pending_removal(false), + _next_dirty_cards_region(NULL), _next(NULL), _prev(NULL), _pending_removal(false), #ifdef ASSERT _containing_set(NULL), #endif // ASSERT @@ -710,14 +716,14 @@ } HeapRegionRemSet* hrrs = rem_set(); - int strong_code_roots_length = hrrs->strong_code_roots_list_length(); + size_t strong_code_roots_length = hrrs->strong_code_roots_list_length(); // if this region is empty then there should be no entries // on its strong code root list if (is_empty()) { if (strong_code_roots_length > 0) { gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] is empty " - "but has "INT32_FORMAT" code root entries", + "but has "SIZE_FORMAT" code root entries", bottom(), end(), strong_code_roots_length); *failures = true; } @@ -727,7 +733,7 @@ if (continuesHumongous()) { if (strong_code_roots_length > 0) { gclog_or_tty->print_cr("region "HR_FORMAT" is a continuation of a humongous " - "region but has "INT32_FORMAT" code root entries", + "region but has "SIZE_FORMAT" code root entries", HR_FORMAT_PARAMS(this), strong_code_roots_length); *failures = true; } @@ -825,7 +831,7 @@ Mutex::_no_safepoint_check_flag); if (!_failures) { - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); gclog_or_tty->print_cr("----------"); } if (!_g1h->is_in_closed_subset(obj)) { @@ -880,7 +886,7 @@ Mutex::_no_safepoint_check_flag); if (!_failures) { - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); gclog_or_tty->print_cr("----------"); } gclog_or_tty->print_cr("Missing rem set entry:"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegion.hpp --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -61,7 +61,7 @@ (_hr_)->startsHumongous() ? "HS" : \ (_hr_)->continuesHumongous() ? "HC" : \ !(_hr_)->is_empty() ? "O" : "F", \ - (_hr_)->bottom(), (_hr_)->top(), (_hr_)->end() + p2i((_hr_)->bottom()), p2i((_hr_)->top()), p2i((_hr_)->end()) // sentinel value for hrs_index #define G1_NULL_HRS_INDEX ((uint) -1) @@ -271,6 +271,7 @@ // Fields used by the HeapRegionSetBase class and subclasses. HeapRegion* _next; + HeapRegion* _prev; #ifdef ASSERT HeapRegionSetBase* _containing_set; #endif // ASSERT @@ -531,11 +532,13 @@ // Methods used by the HeapRegionSetBase class and subclasses. - // Getter and setter for the next field used to link regions into + // Getter and setter for the next and prev fields used to link regions into // linked lists. HeapRegion* next() { return _next; } + HeapRegion* prev() { return _prev; } void set_next(HeapRegion* next) { _next = next; } + void set_prev(HeapRegion* prev) { _prev = prev; } // Every region added to a set is tagged with a reference to that // set. This is used for doing consistency checking to make sure that @@ -547,7 +550,7 @@ (containing_set != NULL && _containing_set == NULL), err_msg("containing_set: "PTR_FORMAT" " "_containing_set: "PTR_FORMAT, - containing_set, _containing_set)); + p2i(containing_set), p2i(_containing_set))); _containing_set = containing_set; } @@ -596,7 +599,7 @@ void save_marks(); // Reset HR stuff to default values. - void hr_clear(bool par, bool clear_space); + void hr_clear(bool par, bool clear_space, bool locked = false); void par_clear(); // Get the start of the unmarked area in this region. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -29,12 +29,15 @@ #include "gc_implementation/g1/heapRegionRemSet.hpp" #include "gc_implementation/g1/heapRegionSeq.inline.hpp" #include "memory/allocation.hpp" +#include "memory/padded.inline.hpp" #include "memory/space.inline.hpp" #include "oops/oop.inline.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + class PerRegionTable: public CHeapObj { friend class OtherRegionsTable; friend class HeapRegionRemSetIterator; @@ -259,10 +262,9 @@ size_t OtherRegionsTable::_fine_eviction_stride = 0; size_t OtherRegionsTable::_fine_eviction_sample_size = 0; -OtherRegionsTable::OtherRegionsTable(HeapRegion* hr) : +OtherRegionsTable::OtherRegionsTable(HeapRegion* hr, Mutex* m) : _g1h(G1CollectedHeap::heap()), - _m(Mutex::leaf, "An OtherRegionsTable lock", true), - _hr(hr), + _hr(hr), _m(m), _coarse_map(G1CollectedHeap::heap()->max_regions(), false /* in-resource-area */), _fine_grain_regions(NULL), @@ -358,46 +360,66 @@ "just checking"); } -int** OtherRegionsTable::_from_card_cache = NULL; -size_t OtherRegionsTable::_from_card_cache_max_regions = 0; -size_t OtherRegionsTable::_from_card_cache_mem_size = 0; +int** FromCardCache::_cache = NULL; +uint FromCardCache::_max_regions = 0; +size_t FromCardCache::_static_mem_size = 0; -void OtherRegionsTable::init_from_card_cache(size_t max_regions) { - _from_card_cache_max_regions = max_regions; +void FromCardCache::initialize(uint n_par_rs, uint max_num_regions) { + guarantee(_cache == NULL, "Should not call this multiple times"); - int n_par_rs = HeapRegionRemSet::num_par_rem_sets(); - _from_card_cache = NEW_C_HEAP_ARRAY(int*, n_par_rs, mtGC); - for (int i = 0; i < n_par_rs; i++) { - _from_card_cache[i] = NEW_C_HEAP_ARRAY(int, max_regions, mtGC); - for (size_t j = 0; j < max_regions; j++) { - _from_card_cache[i][j] = -1; // An invalid value. + _max_regions = max_num_regions; + _cache = Padded2DArray::create_unfreeable(n_par_rs, + _max_regions, + &_static_mem_size); + + for (uint i = 0; i < n_par_rs; i++) { + for (uint j = 0; j < _max_regions; j++) { + set(i, j, InvalidCard); } } - _from_card_cache_mem_size = n_par_rs * max_regions * sizeof(int); } -void OtherRegionsTable::shrink_from_card_cache(size_t new_n_regs) { - for (int i = 0; i < HeapRegionRemSet::num_par_rem_sets(); i++) { - assert(new_n_regs <= _from_card_cache_max_regions, "Must be within max."); - for (size_t j = new_n_regs; j < _from_card_cache_max_regions; j++) { - _from_card_cache[i][j] = -1; // An invalid value. +void FromCardCache::shrink(uint new_num_regions) { + for (uint i = 0; i < HeapRegionRemSet::num_par_rem_sets(); i++) { + assert(new_num_regions <= _max_regions, "Must be within max."); + for (uint j = new_num_regions; j < _max_regions; j++) { + set(i, j, InvalidCard); } } } #ifndef PRODUCT -void OtherRegionsTable::print_from_card_cache() { - for (int i = 0; i < HeapRegionRemSet::num_par_rem_sets(); i++) { - for (size_t j = 0; j < _from_card_cache_max_regions; j++) { - gclog_or_tty->print_cr("_from_card_cache[%d][%d] = %d.", - i, j, _from_card_cache[i][j]); +void FromCardCache::print(outputStream* out) { + for (uint i = 0; i < HeapRegionRemSet::num_par_rem_sets(); i++) { + for (uint j = 0; j < _max_regions; j++) { + out->print_cr("_from_card_cache["UINT32_FORMAT"]["UINT32_FORMAT"] = "INT32_FORMAT".", + i, j, at(i, j)); } } } #endif +void FromCardCache::clear(uint region_idx) { + uint num_par_remsets = HeapRegionRemSet::num_par_rem_sets(); + for (uint i = 0; i < num_par_remsets; i++) { + set(i, region_idx, InvalidCard); + } +} + +void OtherRegionsTable::init_from_card_cache(uint max_regions) { + FromCardCache::initialize(HeapRegionRemSet::num_par_rem_sets(), max_regions); +} + +void OtherRegionsTable::shrink_from_card_cache(uint new_num_regions) { + FromCardCache::shrink(new_num_regions); +} + +void OtherRegionsTable::print_from_card_cache() { + FromCardCache::print(); +} + void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { - size_t cur_hrs_ind = (size_t) hr()->hrs_index(); + uint cur_hrs_ind = hr()->hrs_index(); if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("ORT::add_reference_work(" PTR_FORMAT "->" PTR_FORMAT ").", @@ -410,19 +432,17 @@ int from_card = (int)(uintptr_t(from) >> CardTableModRefBS::card_shift); if (G1TraceHeapRegionRememberedSet) { - gclog_or_tty->print_cr("Table for [" PTR_FORMAT "...): card %d (cache = %d)", + gclog_or_tty->print_cr("Table for [" PTR_FORMAT "...): card %d (cache = "INT32_FORMAT")", hr()->bottom(), from_card, - _from_card_cache[tid][cur_hrs_ind]); + FromCardCache::at((uint)tid, cur_hrs_ind)); } - if (from_card == _from_card_cache[tid][cur_hrs_ind]) { + if (FromCardCache::contains_or_replace((uint)tid, cur_hrs_ind, from_card)) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr(" from-card cache hit."); } assert(contains_reference(from), "We just added it!"); return; - } else { - _from_card_cache[tid][cur_hrs_ind] = from_card; } // Note that this may be a continued H region. @@ -442,7 +462,7 @@ size_t ind = from_hrs_ind & _mod_max_fine_entries_mask; PerRegionTable* prt = find_region_table(ind, from_hr); if (prt == NULL) { - MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); // Confirm that it's really not there... prt = find_region_table(ind, from_hr); if (prt == NULL) { @@ -544,7 +564,7 @@ jint OtherRegionsTable::_n_coarsenings = 0; PerRegionTable* OtherRegionsTable::delete_region_table() { - assert(_m.owned_by_self(), "Precondition"); + assert(_m->owned_by_self(), "Precondition"); assert(_n_fine_entries == _max_fine_entries, "Precondition"); PerRegionTable* max = NULL; jint max_occ = 0; @@ -676,8 +696,6 @@ size_t OtherRegionsTable::occupied() const { - // Cast away const in this case. - MutexLockerEx x((Mutex*)&_m, Mutex::_no_safepoint_check_flag); size_t sum = occ_fine(); sum += occ_sparse(); sum += occ_coarse(); @@ -707,8 +725,6 @@ } size_t OtherRegionsTable::mem_size() const { - // Cast away const in this case. - MutexLockerEx x((Mutex*)&_m, Mutex::_no_safepoint_check_flag); size_t sum = 0; // all PRTs are of the same size so it is sufficient to query only one of them. if (_first_all_fine_prts != NULL) { @@ -724,7 +740,7 @@ } size_t OtherRegionsTable::static_mem_size() { - return _from_card_cache_mem_size; + return FromCardCache::static_mem_size(); } size_t OtherRegionsTable::fl_mem_size() { @@ -732,14 +748,10 @@ } void OtherRegionsTable::clear_fcc() { - size_t hrs_idx = hr()->hrs_index(); - for (int i = 0; i < HeapRegionRemSet::num_par_rem_sets(); i++) { - _from_card_cache[i][hrs_idx] = -1; - } + FromCardCache::clear(hr()->hrs_index()); } void OtherRegionsTable::clear() { - MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); // if there are no entries, skip this step if (_first_all_fine_prts != NULL) { guarantee(_first_all_fine_prts != NULL && _last_all_fine_prts != NULL, "just checking"); @@ -759,7 +771,7 @@ } void OtherRegionsTable::clear_incoming_entry(HeapRegion* from_hr) { - MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); + MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); size_t hrs_ind = (size_t) from_hr->hrs_index(); size_t ind = hrs_ind & _mod_max_fine_entries_mask; if (del_single_region_table(ind, from_hr)) { @@ -768,15 +780,15 @@ _coarse_map.par_at_put(hrs_ind, 0); } // Check to see if any of the fcc entries come from here. - size_t hr_ind = (size_t) hr()->hrs_index(); - for (int tid = 0; tid < HeapRegionRemSet::num_par_rem_sets(); tid++) { - int fcc_ent = _from_card_cache[tid][hr_ind]; - if (fcc_ent != -1) { + uint hr_ind = hr()->hrs_index(); + for (uint tid = 0; tid < HeapRegionRemSet::num_par_rem_sets(); tid++) { + int fcc_ent = FromCardCache::at(tid, hr_ind); + if (fcc_ent != FromCardCache::InvalidCard) { HeapWord* card_addr = (HeapWord*) (uintptr_t(fcc_ent) << CardTableModRefBS::card_shift); if (hr()->is_in_reserved(card_addr)) { // Clear the from card cache. - _from_card_cache[tid][hr_ind] = -1; + FromCardCache::set(tid, hr_ind, FromCardCache::InvalidCard); } } } @@ -805,7 +817,7 @@ bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const { // Cast away const in this case. - MutexLockerEx x((Mutex*)&_m, Mutex::_no_safepoint_check_flag); + MutexLockerEx x((Mutex*)_m, Mutex::_no_safepoint_check_flag); return contains_reference_locked(from); } @@ -832,8 +844,6 @@ "Must be in range."); return _sparse_table.contains_card(hr_ind, card_index); } - - } void @@ -844,13 +854,15 @@ // Determines how many threads can add records to an rset in parallel. // This can be done by either mutator threads together with the // concurrent refinement threads or GC threads. -int HeapRegionRemSet::num_par_rem_sets() { - return (int)MAX2(DirtyCardQueueSet::num_par_ids() + ConcurrentG1Refine::thread_num(), ParallelGCThreads); +uint HeapRegionRemSet::num_par_rem_sets() { + return MAX2(DirtyCardQueueSet::num_par_ids() + ConcurrentG1Refine::thread_num(), (uint)ParallelGCThreads); } HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, HeapRegion* hr) - : _bosa(bosa), _strong_code_roots_list(NULL), _other_regions(hr) { + : _bosa(bosa), + _m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #"UINT32_FORMAT, hr->hrs_index()), true), + _code_roots(), _other_regions(hr, &_m) { reset_for_par_iteration(); } @@ -883,7 +895,7 @@ } #ifndef PRODUCT -void HeapRegionRemSet::print() const { +void HeapRegionRemSet::print() { HeapRegionRemSetIterator iter(this); size_t card_index; while (iter.has_next(card_index)) { @@ -909,14 +921,14 @@ } void HeapRegionRemSet::clear() { - if (_strong_code_roots_list != NULL) { - delete _strong_code_roots_list; - } - _strong_code_roots_list = new (ResourceObj::C_HEAP, mtGC) - GrowableArray(10, 0, NULL, true); + MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); + clear_locked(); +} +void HeapRegionRemSet::clear_locked() { + _code_roots.clear(); _other_regions.clear(); - assert(occupied() == 0, "Should be clear."); + assert(occupied_locked() == 0, "Should be clear."); reset_for_par_iteration(); } @@ -932,27 +944,18 @@ _other_regions.scrub(ctbs, region_bm, card_bm); } - // Code roots support void HeapRegionRemSet::add_strong_code_root(nmethod* nm) { assert(nm != NULL, "sanity"); - // Search for the code blob from the RHS to avoid - // duplicate entries as much as possible - if (_strong_code_roots_list->find_from_end(nm) < 0) { - // Code blob isn't already in the list - _strong_code_roots_list->push(nm); - } + _code_roots.add(nm); } void HeapRegionRemSet::remove_strong_code_root(nmethod* nm) { assert(nm != NULL, "sanity"); - int idx = _strong_code_roots_list->find(nm); - if (idx >= 0) { - _strong_code_roots_list->remove_at(idx); - } + _code_roots.remove(nm); // Check that there were no duplicates - guarantee(_strong_code_roots_list->find(nm) < 0, "duplicate entry found"); + guarantee(!_code_roots.contains(nm), "duplicate entry found"); } class NMethodMigrationOopClosure : public OopClosure { @@ -1014,8 +1017,8 @@ GrowableArray to_be_retained(10); G1CollectedHeap* g1h = G1CollectedHeap::heap(); - while (_strong_code_roots_list->is_nonempty()) { - nmethod *nm = _strong_code_roots_list->pop(); + while (!_code_roots.is_empty()) { + nmethod *nm = _code_roots.pop(); if (nm != NULL) { NMethodMigrationOopClosure oop_cl(g1h, hr(), nm); nm->oops_do(&oop_cl); @@ -1038,20 +1041,16 @@ } void HeapRegionRemSet::strong_code_roots_do(CodeBlobClosure* blk) const { - for (int i = 0; i < _strong_code_roots_list->length(); i += 1) { - nmethod* nm = _strong_code_roots_list->at(i); - blk->do_code_blob(nm); - } + _code_roots.nmethods_do(blk); } size_t HeapRegionRemSet::strong_code_roots_mem_size() { - return sizeof(GrowableArray) + - _strong_code_roots_list->max_length() * sizeof(nmethod*); + return _code_roots.mem_size(); } //-------------------- Iteration -------------------- -HeapRegionRemSetIterator:: HeapRegionRemSetIterator(const HeapRegionRemSet* hrrs) : +HeapRegionRemSetIterator:: HeapRegionRemSetIterator(HeapRegionRemSet* hrrs) : _hrrs(hrrs), _g1h(G1CollectedHeap::heap()), _coarse_map(&hrrs->_other_regions._coarse_map), @@ -1247,7 +1246,7 @@ while (cur_evnt < _n_recorded_events && i == cur_evnt_ind) { gclog_or_tty->print("Event: "); print_event(gclog_or_tty, cur_evnt_kind); - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); cur_evnt++; if (cur_evnt < MaxRecordedEvents) { cur_evnt_kind = _recorded_events[cur_evnt]; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp --- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -25,6 +25,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONREMSET_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONREMSET_HPP +#include "gc_implementation/g1/g1CodeCacheRemSet.hpp" #include "gc_implementation/g1/sparsePRT.hpp" // Remembered set for a heap region. Represent a set of "cards" that @@ -44,6 +45,54 @@ class HRRSCleanupTask : public SparsePRTCleanupTask { }; +// The FromCardCache remembers the most recently processed card on the heap on +// a per-region and per-thread basis. +class FromCardCache : public AllStatic { + private: + // Array of card indices. Indexed by thread X and heap region to minimize + // thread contention. + static int** _cache; + static uint _max_regions; + static size_t _static_mem_size; + + public: + enum { + InvalidCard = -1 // Card value of an invalid card, i.e. a card index not otherwise used. + }; + + static void clear(uint region_idx); + + // Returns true if the given card is in the cache at the given location, or + // replaces the card at that location and returns false. + static bool contains_or_replace(uint worker_id, uint region_idx, int card) { + int card_in_cache = at(worker_id, region_idx); + if (card_in_cache == card) { + return true; + } else { + set(worker_id, region_idx, card); + return false; + } + } + + static int at(uint worker_id, uint region_idx) { + return _cache[worker_id][region_idx]; + } + + static void set(uint worker_id, uint region_idx, int val) { + _cache[worker_id][region_idx] = val; + } + + static void initialize(uint n_par_rs, uint max_num_regions); + + static void shrink(uint new_num_regions); + + static void print(outputStream* out = gclog_or_tty) PRODUCT_RETURN; + + static size_t static_mem_size() { + return _static_mem_size; + } +}; + // The "_coarse_map" is a bitmap with one bit for each region, where set // bits indicate that the corresponding region may contain some pointer // into the owning region. @@ -72,7 +121,7 @@ friend class HeapRegionRemSetIterator; G1CollectedHeap* _g1h; - Mutex _m; + Mutex* _m; HeapRegion* _hr; // These are protected by "_m". @@ -118,18 +167,13 @@ // false. bool del_single_region_table(size_t ind, HeapRegion* hr); - // Indexed by thread X heap region, to minimize thread contention. - static int** _from_card_cache; - static size_t _from_card_cache_max_regions; - static size_t _from_card_cache_mem_size; - // link/add the given fine grain remembered set into the "all" list void link_to_all(PerRegionTable * prt); // unlink/remove the given fine grain remembered set into the "all" list void unlink_from_all(PerRegionTable * prt); public: - OtherRegionsTable(HeapRegion* hr); + OtherRegionsTable(HeapRegion* hr, Mutex* m); HeapRegion* hr() const { return _hr; } @@ -141,7 +185,6 @@ // objects. void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); - // Not const because it takes a lock. size_t occupied() const; size_t occ_fine() const; size_t occ_coarse() const; @@ -170,11 +213,11 @@ // Declare the heap size (in # of regions) to the OtherRegionsTable. // (Uses it to initialize from_card_cache). - static void init_from_card_cache(size_t max_regions); + static void init_from_card_cache(uint max_regions); // Declares that only regions i s.t. 0 <= i < new_n_regs are in use. // Make sure any entries for higher regions are invalid. - static void shrink_from_card_cache(size_t new_n_regs); + static void shrink_from_card_cache(uint new_num_regions); static void print_from_card_cache(); }; @@ -192,9 +235,11 @@ G1BlockOffsetSharedArray* _bosa; G1BlockOffsetSharedArray* bosa() const { return _bosa; } - // A list of code blobs (nmethods) whose code contains pointers into + // A set of code blobs (nmethods) whose code contains pointers into // the region that owns this RSet. - GrowableArray* _strong_code_roots_list; + G1CodeRootSet _code_roots; + + Mutex _m; OtherRegionsTable _other_regions; @@ -218,17 +263,20 @@ static void print_event(outputStream* str, Event evnt); public: - HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, - HeapRegion* hr); + HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, HeapRegion* hr); - static int num_par_rem_sets(); + static uint num_par_rem_sets(); static void setup_remset_size(); HeapRegion* hr() const { return _other_regions.hr(); } - size_t occupied() const { + size_t occupied() { + MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); + return occupied_locked(); + } + size_t occupied_locked() { return _other_regions.occupied(); } size_t occ_fine() const { @@ -260,6 +308,7 @@ // The region is being reclaimed; clear its remset, and any mention of // entries for this region in other remsets. void clear(); + void clear_locked(); // Attempt to claim the region. Returns true iff this call caused an // atomic transition from Unclaimed to Claimed. @@ -289,6 +338,7 @@ // The actual # of bytes this hr_remset takes up. // Note also includes the strong code root set. size_t mem_size() { + MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); return _other_regions.mem_size() // This correction is necessary because the above includes the second // part. @@ -299,13 +349,13 @@ // Returns the memory occupancy of all static data structures associated // with remembered sets. static size_t static_mem_size() { - return OtherRegionsTable::static_mem_size(); + return OtherRegionsTable::static_mem_size() + G1CodeRootSet::static_mem_size(); } // Returns the memory occupancy of all free_list data structures associated // with remembered sets. static size_t fl_mem_size() { - return OtherRegionsTable::fl_mem_size(); + return OtherRegionsTable::fl_mem_size() + G1CodeRootSet::fl_mem_size(); } bool contains_reference(OopOrNarrowOopStar from) const { @@ -328,21 +378,21 @@ void strong_code_roots_do(CodeBlobClosure* blk) const; // Returns the number of elements in the strong code roots list - int strong_code_roots_list_length() { - return _strong_code_roots_list->length(); + size_t strong_code_roots_list_length() { + return _code_roots.length(); } // Returns true if the strong code roots contains the given // nmethod. bool strong_code_roots_list_contains(nmethod* nm) { - return _strong_code_roots_list->contains(nm); + return _code_roots.contains(nm); } // Returns the amount of memory, in bytes, currently // consumed by the strong code roots. size_t strong_code_roots_mem_size(); - void print() const; + void print() PRODUCT_RETURN; // Called during a stop-world phase to perform any deferred cleanups. static void cleanup(); @@ -350,12 +400,13 @@ // Declare the heap size (in # of regions) to the HeapRegionRemSet(s). // (Uses it to initialize from_card_cache). static void init_heap(uint max_regions) { - OtherRegionsTable::init_from_card_cache((size_t) max_regions); + G1CodeRootSet::initialize(); + OtherRegionsTable::init_from_card_cache(max_regions); } // Declares that only regions i s.t. 0 <= i < new_n_regs are in use. static void shrink_heap(uint new_n_regs) { - OtherRegionsTable::shrink_from_card_cache((size_t) new_n_regs); + OtherRegionsTable::shrink_from_card_cache(new_n_regs); } #ifndef PRODUCT @@ -384,7 +435,7 @@ class HeapRegionRemSetIterator : public StackObj { // The region RSet over which we're iterating. - const HeapRegionRemSet* _hrrs; + HeapRegionRemSet* _hrrs; // Local caching of HRRS fields. const BitMap* _coarse_map; @@ -441,7 +492,7 @@ public: // We require an iterator to be initialized before use, so the // constructor does little. - HeapRegionRemSetIterator(const HeapRegionRemSet* hrrs); + HeapRegionRemSetIterator(HeapRegionRemSet* hrrs); // If there remains one or more cards to be yielded, returns true and // sets "card_index" to one of those cards (which is then considered diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSeq.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "gc_implementation/g1/heapRegion.hpp" #include "gc_implementation/g1/heapRegionSeq.inline.hpp" -#include "gc_implementation/g1/heapRegionSets.hpp" +#include "gc_implementation/g1/heapRegionSet.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "memory/allocation.hpp" @@ -233,7 +233,7 @@ guarantee(hr != NULL, err_msg("invariant: i: %u", i)); guarantee(hr->bottom() == prev_end, err_msg("invariant i: %u "HR_FORMAT" prev_end: "PTR_FORMAT, - i, HR_FORMAT_PARAMS(hr), prev_end)); + i, HR_FORMAT_PARAMS(hr), p2i(prev_end))); guarantee(hr->hrs_index() == i, err_msg("invariant: i: %u hrs_index(): %u", i, hr->hrs_index())); if (i < length()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp --- a/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -37,7 +37,7 @@ inline HeapRegion* HeapRegionSeq::addr_to_region(HeapWord* addr) const { if (addr != NULL && addr < heap_end()) { assert(addr >= heap_bottom(), - err_msg("addr: "PTR_FORMAT" bottom: "PTR_FORMAT, addr, heap_bottom())); + err_msg("addr: " PTR_FORMAT " bottom: " PTR_FORMAT, p2i(addr), p2i(heap_bottom()))); return addr_to_region_unsafe(addr); } return NULL; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSet.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionSet.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionSet.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -23,171 +23,62 @@ */ #include "precompiled.hpp" +#include "gc_implementation/g1/heapRegionRemSet.hpp" #include "gc_implementation/g1/heapRegionSet.inline.hpp" -uint HeapRegionSetBase::_unrealistically_long_length = 0; -HRSPhase HeapRegionSetBase::_phase = HRSPhaseNone; - -//////////////////// HeapRegionSetBase //////////////////// +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC -void HeapRegionSetBase::set_unrealistically_long_length(uint len) { - guarantee(_unrealistically_long_length == 0, "should only be set once"); - _unrealistically_long_length = len; -} +uint FreeRegionList::_unrealistically_long_length = 0; void HeapRegionSetBase::fill_in_ext_msg(hrs_ext_msg* msg, const char* message) { - msg->append("[%s] %s ln: %u rn: %u cy: "SIZE_FORMAT" ud: "SIZE_FORMAT, - name(), message, length(), region_num(), - total_capacity_bytes(), total_used_bytes()); + msg->append("[%s] %s ln: %u cy: "SIZE_FORMAT, + name(), message, length(), total_capacity_bytes()); fill_in_ext_msg_extra(msg); } -bool HeapRegionSetBase::verify_region(HeapRegion* hr, - HeapRegionSetBase* expected_containing_set) { - const char* error_message = NULL; - - if (!regions_humongous()) { - if (hr->isHumongous()) { - error_message = "the region should not be humongous"; - } - } else { - if (!hr->isHumongous() || !hr->startsHumongous()) { - error_message = "the region should be 'starts humongous'"; - } - } - - if (!regions_empty()) { - if (hr->is_empty()) { - error_message = "the region should not be empty"; - } - } else { - if (!hr->is_empty()) { - error_message = "the region should be empty"; - } - } - -#ifdef ASSERT - // The _containing_set field is only available when ASSERT is defined. - if (hr->containing_set() != expected_containing_set) { - error_message = "inconsistent containing set found"; - } -#endif // ASSERT - - const char* extra_error_message = verify_region_extra(hr); - if (extra_error_message != NULL) { - error_message = extra_error_message; - } - - if (error_message != NULL) { - outputStream* out = tty; - out->cr(); - out->print_cr("## [%s] %s", name(), error_message); - out->print_cr("## Offending Region: "PTR_FORMAT, hr); - out->print_cr(" "HR_FORMAT, HR_FORMAT_PARAMS(hr)); -#ifdef ASSERT - out->print_cr(" containing set: "PTR_FORMAT, hr->containing_set()); -#endif // ASSERT - out->print_cr("## Offending Region Set: "PTR_FORMAT, this); - print_on(out); - return false; - } else { - return true; - } +#ifndef PRODUCT +void HeapRegionSetBase::verify_region(HeapRegion* hr) { + assert(hr->containing_set() == this, err_msg("Inconsistent containing set for %u", hr->hrs_index())); + assert(!hr->is_young(), err_msg("Adding young region %u", hr->hrs_index())); // currently we don't use these sets for young regions + assert(hr->isHumongous() == regions_humongous(), err_msg("Wrong humongous state for region %u and set %s", hr->hrs_index(), name())); + assert(hr->is_empty() == regions_empty(), err_msg("Wrong empty state for region %u and set %s", hr->hrs_index(), name())); + assert(hr->rem_set()->verify_ready_for_par_iteration(), err_msg("Wrong iteration state %u", hr->hrs_index())); } +#endif void HeapRegionSetBase::verify() { // It's important that we also observe the MT safety protocol even // for the verification calls. If we do verification without the // appropriate locks and the set changes underneath our feet // verification might fail and send us on a wild goose chase. - hrs_assert_mt_safety_ok(this); - - guarantee(( is_empty() && length() == 0 && region_num() == 0 && - total_used_bytes() == 0 && total_capacity_bytes() == 0) || - (!is_empty() && length() >= 0 && region_num() >= 0 && - total_used_bytes() >= 0 && total_capacity_bytes() >= 0), - hrs_ext_msg(this, "invariant")); + check_mt_safety(); - guarantee((!regions_humongous() && region_num() == length()) || - ( regions_humongous() && region_num() >= length()), - hrs_ext_msg(this, "invariant")); - - guarantee(!regions_empty() || total_used_bytes() == 0, - hrs_ext_msg(this, "invariant")); - - guarantee(total_used_bytes() <= total_capacity_bytes(), + guarantee(( is_empty() && length() == 0 && total_capacity_bytes() == 0) || + (!is_empty() && length() >= 0 && total_capacity_bytes() >= 0), hrs_ext_msg(this, "invariant")); } void HeapRegionSetBase::verify_start() { // See comment in verify() about MT safety and verification. - hrs_assert_mt_safety_ok(this); + check_mt_safety(); assert(!_verify_in_progress, hrs_ext_msg(this, "verification should not be in progress")); // Do the basic verification first before we do the checks over the regions. HeapRegionSetBase::verify(); - _calc_length = 0; - _calc_region_num = 0; - _calc_total_capacity_bytes = 0; - _calc_total_used_bytes = 0; _verify_in_progress = true; } -void HeapRegionSetBase::verify_next_region(HeapRegion* hr) { - // See comment in verify() about MT safety and verification. - hrs_assert_mt_safety_ok(this); - assert(_verify_in_progress, - hrs_ext_msg(this, "verification should be in progress")); - - guarantee(verify_region(hr, this), hrs_ext_msg(this, "region verification")); - - _calc_length += 1; - _calc_region_num += hr->region_num(); - _calc_total_capacity_bytes += hr->capacity(); - _calc_total_used_bytes += hr->used(); -} - void HeapRegionSetBase::verify_end() { // See comment in verify() about MT safety and verification. - hrs_assert_mt_safety_ok(this); + check_mt_safety(); assert(_verify_in_progress, hrs_ext_msg(this, "verification should be in progress")); - guarantee(length() == _calc_length, - hrs_err_msg("[%s] length: %u should be == calc length: %u", - name(), length(), _calc_length)); - - guarantee(region_num() == _calc_region_num, - hrs_err_msg("[%s] region num: %u should be == calc region num: %u", - name(), region_num(), _calc_region_num)); - - guarantee(total_capacity_bytes() == _calc_total_capacity_bytes, - hrs_err_msg("[%s] capacity bytes: "SIZE_FORMAT" should be == " - "calc capacity bytes: "SIZE_FORMAT, - name(), - total_capacity_bytes(), _calc_total_capacity_bytes)); - - guarantee(total_used_bytes() == _calc_total_used_bytes, - hrs_err_msg("[%s] used bytes: "SIZE_FORMAT" should be == " - "calc used bytes: "SIZE_FORMAT, - name(), total_used_bytes(), _calc_total_used_bytes)); - _verify_in_progress = false; } -void HeapRegionSetBase::clear_phase() { - assert(_phase != HRSPhaseNone, "pre-condition"); - _phase = HRSPhaseNone; -} - -void HeapRegionSetBase::set_phase(HRSPhase phase) { - assert(_phase == HRSPhaseNone, "pre-condition"); - assert(phase != HRSPhaseNone, "pre-condition"); - _phase = phase; -} - void HeapRegionSetBase::print_on(outputStream* out, bool print_contents) { out->cr(); out->print_cr("Set: %s ("PTR_FORMAT")", name(), this); @@ -196,76 +87,38 @@ out->print_cr(" empty : %s", BOOL_TO_STR(regions_empty())); out->print_cr(" Attributes"); out->print_cr(" length : %14u", length()); - out->print_cr(" region num : %14u", region_num()); out->print_cr(" total capacity : "SIZE_FORMAT_W(14)" bytes", total_capacity_bytes()); - out->print_cr(" total used : "SIZE_FORMAT_W(14)" bytes", - total_used_bytes()); -} - -void HeapRegionSetBase::clear() { - _length = 0; - _region_num = 0; - _total_used_bytes = 0; } -HeapRegionSetBase::HeapRegionSetBase(const char* name) +HeapRegionSetBase::HeapRegionSetBase(const char* name, bool humongous, bool empty, HRSMtSafeChecker* mt_safety_checker) : _name(name), _verify_in_progress(false), - _calc_length(0), _calc_region_num(0), - _calc_total_capacity_bytes(0), _calc_total_used_bytes(0) { } - -//////////////////// HeapRegionSet //////////////////// - -void HeapRegionSet::update_from_proxy(HeapRegionSet* proxy_set) { - hrs_assert_mt_safety_ok(this); - hrs_assert_mt_safety_ok(proxy_set); - hrs_assert_sets_match(this, proxy_set); - - verify_optional(); - proxy_set->verify_optional(); - - if (proxy_set->is_empty()) return; + _is_humongous(humongous), _is_empty(empty), _mt_safety_checker(mt_safety_checker), + _count() +{ } - assert(proxy_set->length() <= _length, - hrs_err_msg("[%s] proxy set length: %u should be <= length: %u", - name(), proxy_set->length(), _length)); - _length -= proxy_set->length(); - - assert(proxy_set->region_num() <= _region_num, - hrs_err_msg("[%s] proxy set region num: %u should be <= region num: %u", - name(), proxy_set->region_num(), _region_num)); - _region_num -= proxy_set->region_num(); - - assert(proxy_set->total_used_bytes() <= _total_used_bytes, - hrs_err_msg("[%s] proxy set used bytes: "SIZE_FORMAT" " - "should be <= used bytes: "SIZE_FORMAT, - name(), proxy_set->total_used_bytes(), - _total_used_bytes)); - _total_used_bytes -= proxy_set->total_used_bytes(); - - proxy_set->clear(); - - verify_optional(); - proxy_set->verify_optional(); +void FreeRegionList::set_unrealistically_long_length(uint len) { + guarantee(_unrealistically_long_length == 0, "should only be set once"); + _unrealistically_long_length = len; } -//////////////////// HeapRegionLinkedList //////////////////// - -void HeapRegionLinkedList::fill_in_ext_msg_extra(hrs_ext_msg* msg) { +void FreeRegionList::fill_in_ext_msg_extra(hrs_ext_msg* msg) { msg->append(" hd: "PTR_FORMAT" tl: "PTR_FORMAT, head(), tail()); } -void HeapRegionLinkedList::add_as_head(HeapRegionLinkedList* from_list) { - hrs_assert_mt_safety_ok(this); - hrs_assert_mt_safety_ok(from_list); +void FreeRegionList::add_as_head_or_tail(FreeRegionList* from_list, bool as_head) { + check_mt_safety(); + from_list->check_mt_safety(); verify_optional(); from_list->verify_optional(); - if (from_list->is_empty()) return; + if (from_list->is_empty()) { + return; + } #ifdef ASSERT - HeapRegionLinkedListIterator iter(from_list); + FreeRegionListIterator iter(from_list); while (iter.more_available()) { HeapRegion* hr = iter.get_next(); // In set_containing_set() we check that we either set the value @@ -276,35 +129,75 @@ } #endif // ASSERT - if (_head != NULL) { - assert(length() > 0 && _tail != NULL, hrs_ext_msg(this, "invariant")); - from_list->_tail->set_next(_head); - } else { + if (_head == NULL) { assert(length() == 0 && _tail == NULL, hrs_ext_msg(this, "invariant")); + _head = from_list->_head; _tail = from_list->_tail; + } else { + assert(length() > 0 && _tail != NULL, hrs_ext_msg(this, "invariant")); + if (as_head) { + from_list->_tail->set_next(_head); + _head->set_prev(from_list->_tail); + _head = from_list->_head; + } else { + _tail->set_next(from_list->_head); + from_list->_head->set_prev(_tail); + _tail = from_list->_tail; + } } - _head = from_list->_head; - _length += from_list->length(); - _region_num += from_list->region_num(); - _total_used_bytes += from_list->total_used_bytes(); + _count.increment(from_list->length(), from_list->total_capacity_bytes()); from_list->clear(); verify_optional(); from_list->verify_optional(); } -void HeapRegionLinkedList::add_as_tail(HeapRegionLinkedList* from_list) { - hrs_assert_mt_safety_ok(this); - hrs_assert_mt_safety_ok(from_list); +void FreeRegionList::add_as_head(FreeRegionList* from_list) { + add_as_head_or_tail(from_list, true /* as_head */); +} + +void FreeRegionList::add_as_tail(FreeRegionList* from_list) { + add_as_head_or_tail(from_list, false /* as_head */); +} + +void FreeRegionList::remove_all() { + check_mt_safety(); + verify_optional(); + + HeapRegion* curr = _head; + while (curr != NULL) { + verify_region(curr); + + HeapRegion* next = curr->next(); + curr->set_next(NULL); + curr->set_prev(NULL); + curr->set_containing_set(NULL); + curr = next; + } + clear(); + + verify_optional(); +} + +void FreeRegionList::add_ordered(FreeRegionList* from_list) { + check_mt_safety(); + from_list->check_mt_safety(); verify_optional(); from_list->verify_optional(); - if (from_list->is_empty()) return; + if (from_list->is_empty()) { + return; + } -#ifdef ASSERT - HeapRegionLinkedListIterator iter(from_list); + if (is_empty()) { + add_as_head(from_list); + return; + } + + #ifdef ASSERT + FreeRegionListIterator iter(from_list); while (iter.more_available()) { HeapRegion* hr = iter.get_next(); // In set_containing_set() we check that we either set the value @@ -313,46 +206,50 @@ hr->set_containing_set(NULL); hr->set_containing_set(this); } -#endif // ASSERT + #endif // ASSERT + + HeapRegion* curr_to = _head; + HeapRegion* curr_from = from_list->_head; + + while (curr_from != NULL) { + while (curr_to != NULL && curr_to->hrs_index() < curr_from->hrs_index()) { + curr_to = curr_to->next(); + } + + if (curr_to == NULL) { + // The rest of the from list should be added as tail + _tail->set_next(curr_from); + curr_from->set_prev(_tail); + curr_from = NULL; + } else { + HeapRegion* next_from = curr_from->next(); - if (_tail != NULL) { - assert(length() > 0 && _head != NULL, hrs_ext_msg(this, "invariant")); - _tail->set_next(from_list->_head); - } else { - assert(length() == 0 && _head == NULL, hrs_ext_msg(this, "invariant")); - _head = from_list->_head; + curr_from->set_next(curr_to); + curr_from->set_prev(curr_to->prev()); + if (curr_to->prev() == NULL) { + _head = curr_from; + } else { + curr_to->prev()->set_next(curr_from); + } + curr_to->set_prev(curr_from); + + curr_from = next_from; + } } - _tail = from_list->_tail; - _length += from_list->length(); - _region_num += from_list->region_num(); - _total_used_bytes += from_list->total_used_bytes(); + if (_tail->hrs_index() < from_list->_tail->hrs_index()) { + _tail = from_list->_tail; + } + + _count.increment(from_list->length(), from_list->total_capacity_bytes()); from_list->clear(); verify_optional(); from_list->verify_optional(); } -void HeapRegionLinkedList::remove_all() { - hrs_assert_mt_safety_ok(this); - verify_optional(); - - HeapRegion* curr = _head; - while (curr != NULL) { - hrs_assert_region_ok(this, curr, this); - - HeapRegion* next = curr->next(); - curr->set_next(NULL); - curr->set_containing_set(NULL); - curr = next; - } - clear(); - - verify_optional(); -} - -void HeapRegionLinkedList::remove_all_pending(uint target_count) { - hrs_assert_mt_safety_ok(this); +void FreeRegionList::remove_all_pending(uint target_count) { + check_mt_safety(); assert(target_count > 1, hrs_ext_msg(this, "pre-condition")); assert(!is_empty(), hrs_ext_msg(this, "pre-condition")); @@ -360,11 +257,11 @@ DEBUG_ONLY(uint old_length = length();) HeapRegion* curr = _head; - HeapRegion* prev = NULL; uint count = 0; while (curr != NULL) { - hrs_assert_region_ok(this, curr, this); + verify_region(curr); HeapRegion* next = curr->next(); + HeapRegion* prev = curr->prev(); if (curr->pending_removal()) { assert(count < target_count, @@ -384,10 +281,15 @@ _tail = prev; } else { assert(_tail != curr, hrs_ext_msg(this, "invariant")); + next->set_prev(prev); + } + if (_last = curr) { + _last = NULL; } curr->set_next(NULL); - remove_internal(curr); + curr->set_prev(NULL); + remove(curr); curr->set_pending_removal(false); count += 1; @@ -397,8 +299,6 @@ // carry on iterating to make sure there are not more regions // tagged with pending removal. DEBUG_ONLY(if (count == target_count) break;) - } else { - prev = curr; } curr = next; } @@ -414,46 +314,27 @@ verify_optional(); } -void HeapRegionLinkedList::verify() { +void FreeRegionList::verify() { // See comment in HeapRegionSetBase::verify() about MT safety and // verification. - hrs_assert_mt_safety_ok(this); + check_mt_safety(); // This will also do the basic verification too. verify_start(); - HeapRegion* curr = _head; - HeapRegion* prev1 = NULL; - HeapRegion* prev0 = NULL; - uint count = 0; - while (curr != NULL) { - verify_next_region(curr); - - count += 1; - guarantee(count < _unrealistically_long_length, - hrs_err_msg("[%s] the calculated length: %u " - "seems very long, is there maybe a cycle? " - "curr: "PTR_FORMAT" prev0: "PTR_FORMAT" " - "prev1: "PTR_FORMAT" length: %u", - name(), count, curr, prev0, prev1, length())); - - prev1 = prev0; - prev0 = curr; - curr = curr->next(); - } - - guarantee(_tail == prev0, hrs_ext_msg(this, "post-condition")); + verify_list(); verify_end(); } -void HeapRegionLinkedList::clear() { - HeapRegionSetBase::clear(); +void FreeRegionList::clear() { + _count = HeapRegionSetCount(); _head = NULL; _tail = NULL; + _last = NULL; } -void HeapRegionLinkedList::print_on(outputStream* out, bool print_contents) { +void FreeRegionList::print_on(outputStream* out, bool print_contents) { HeapRegionSetBase::print_on(out, print_contents); out->print_cr(" Linking"); out->print_cr(" head : "PTR_FORMAT, _head); @@ -461,10 +342,124 @@ if (print_contents) { out->print_cr(" Contents"); - HeapRegionLinkedListIterator iter(this); + FreeRegionListIterator iter(this); while (iter.more_available()) { HeapRegion* hr = iter.get_next(); hr->print_on(out); } } } + +void FreeRegionList::verify_list() { + HeapRegion* curr = head(); + HeapRegion* prev1 = NULL; + HeapRegion* prev0 = NULL; + uint count = 0; + size_t capacity = 0; + uint last_index = 0; + + guarantee(_head == NULL || _head->prev() == NULL, "_head should not have a prev"); + while (curr != NULL) { + verify_region(curr); + + count++; + guarantee(count < _unrealistically_long_length, + hrs_err_msg("[%s] the calculated length: %u seems very long, is there maybe a cycle? curr: "PTR_FORMAT" prev0: "PTR_FORMAT" " "prev1: "PTR_FORMAT" length: %u", name(), count, curr, prev0, prev1, length())); + + if (curr->next() != NULL) { + guarantee(curr->next()->prev() == curr, "Next or prev pointers messed up"); + } + guarantee(curr->hrs_index() == 0 || curr->hrs_index() > last_index, "List should be sorted"); + last_index = curr->hrs_index(); + + capacity += curr->capacity(); + + prev1 = prev0; + prev0 = curr; + curr = curr->next(); + } + + guarantee(tail() == prev0, err_msg("Expected %s to end with %u but it ended with %u.", name(), tail()->hrs_index(), prev0->hrs_index())); + guarantee(_tail == NULL || _tail->next() == NULL, "_tail should not have a next"); + guarantee(length() == count, err_msg("%s count mismatch. Expected %u, actual %u.", name(), length(), count)); + guarantee(total_capacity_bytes() == capacity, err_msg("%s capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, + name(), total_capacity_bytes(), capacity)); +} + +// Note on the check_mt_safety() methods below: +// +// Verification of the "master" heap region sets / lists that are +// maintained by G1CollectedHeap is always done during a STW pause and +// by the VM thread at the start / end of the pause. The standard +// verification methods all assert check_mt_safety(). This is +// important as it ensures that verification is done without +// concurrent updates taking place at the same time. It follows, that, +// for the "master" heap region sets / lists, the check_mt_safety() +// method should include the VM thread / STW case. + +void MasterFreeRegionListMtSafeChecker::check() { + // Master Free List MT safety protocol: + // (a) If we're at a safepoint, operations on the master free list + // should be invoked by either the VM thread (which will serialize + // them) or by the GC workers while holding the + // FreeList_lock. + // (b) If we're not at a safepoint, operations on the master free + // list should be invoked while holding the Heap_lock. + + if (SafepointSynchronize::is_at_safepoint()) { + guarantee(Thread::current()->is_VM_thread() || + FreeList_lock->owned_by_self(), "master free list MT safety protocol at a safepoint"); + } else { + guarantee(Heap_lock->owned_by_self(), "master free list MT safety protocol outside a safepoint"); + } +} + +void SecondaryFreeRegionListMtSafeChecker::check() { + // Secondary Free List MT safety protocol: + // Operations on the secondary free list should always be invoked + // while holding the SecondaryFreeList_lock. + + guarantee(SecondaryFreeList_lock->owned_by_self(), "secondary free list MT safety protocol"); +} + +void OldRegionSetMtSafeChecker::check() { + // Master Old Set MT safety protocol: + // (a) If we're at a safepoint, operations on the master old set + // should be invoked: + // - by the VM thread (which will serialize them), or + // - by the GC workers while holding the FreeList_lock, if we're + // at a safepoint for an evacuation pause (this lock is taken + // anyway when an GC alloc region is retired so that a new one + // is allocated from the free list), or + // - by the GC workers while holding the OldSets_lock, if we're at a + // safepoint for a cleanup pause. + // (b) If we're not at a safepoint, operations on the master old set + // should be invoked while holding the Heap_lock. + + if (SafepointSynchronize::is_at_safepoint()) { + guarantee(Thread::current()->is_VM_thread() + || FreeList_lock->owned_by_self() || OldSets_lock->owned_by_self(), + "master old set MT safety protocol at a safepoint"); + } else { + guarantee(Heap_lock->owned_by_self(), "master old set MT safety protocol outside a safepoint"); + } +} + +void HumongousRegionSetMtSafeChecker::check() { + // Humongous Set MT safety protocol: + // (a) If we're at a safepoint, operations on the master humongous + // set should be invoked by either the VM thread (which will + // serialize them) or by the GC workers while holding the + // OldSets_lock. + // (b) If we're not at a safepoint, operations on the master + // humongous set should be invoked while holding the Heap_lock. + + if (SafepointSynchronize::is_at_safepoint()) { + guarantee(Thread::current()->is_VM_thread() || + OldSets_lock->owned_by_self(), + "master humongous set MT safety protocol at a safepoint"); + } else { + guarantee(Heap_lock->owned_by_self(), + "master humongous set MT safety protocol outside a safepoint"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSet.hpp --- a/src/share/vm/gc_implementation/g1/heapRegionSet.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionSet.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -38,135 +38,108 @@ #define HEAP_REGION_SET_FORCE_VERIFY defined(ASSERT) #endif // HEAP_REGION_SET_FORCE_VERIFY -//////////////////// HeapRegionSetBase //////////////////// +class hrs_ext_msg; + +class HRSMtSafeChecker : public CHeapObj { +public: + virtual void check() = 0; +}; + +class MasterFreeRegionListMtSafeChecker : public HRSMtSafeChecker { public: void check(); }; +class SecondaryFreeRegionListMtSafeChecker : public HRSMtSafeChecker { public: void check(); }; +class HumongousRegionSetMtSafeChecker : public HRSMtSafeChecker { public: void check(); }; +class OldRegionSetMtSafeChecker : public HRSMtSafeChecker { public: void check(); }; + +class HeapRegionSetCount VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + uint _length; + size_t _capacity; + +public: + HeapRegionSetCount() : _length(0), _capacity(0) { } + + const uint length() const { return _length; } + const size_t capacity() const { return _capacity; } + + void increment(uint length_to_add, size_t capacity_to_add) { + _length += length_to_add; + _capacity += capacity_to_add; + } + + void decrement(const uint length_to_remove, const size_t capacity_to_remove) { + _length -= length_to_remove; + _capacity -= capacity_to_remove; + } +}; // Base class for all the classes that represent heap region sets. It // contains the basic attributes that each set needs to maintain // (e.g., length, region num, used bytes sum) plus any shared // functionality (e.g., verification). -class hrs_ext_msg; - -typedef enum { - HRSPhaseNone, - HRSPhaseEvacuation, - HRSPhaseCleanup, - HRSPhaseFullGC -} HRSPhase; - -class HRSPhaseSetter; - class HeapRegionSetBase VALUE_OBJ_CLASS_SPEC { - friend class hrs_ext_msg; - friend class HRSPhaseSetter; friend class VMStructs; +private: + bool _is_humongous; + bool _is_empty; + HRSMtSafeChecker* _mt_safety_checker; protected: - static uint _unrealistically_long_length; - // The number of regions added to the set. If the set contains // only humongous regions, this reflects only 'starts humongous' // regions and does not include 'continues humongous' ones. - uint _length; - - // The total number of regions represented by the set. If the set - // does not contain humongous regions, this should be the same as - // _length. If the set contains only humongous regions, this will - // include the 'continues humongous' regions. - uint _region_num; - - // We don't keep track of the total capacity explicitly, we instead - // recalculate it based on _region_num and the heap region size. - - // The sum of used bytes in the all the regions in the set. - size_t _total_used_bytes; + HeapRegionSetCount _count; const char* _name; - bool _verify_in_progress; - uint _calc_length; - uint _calc_region_num; - size_t _calc_total_capacity_bytes; - size_t _calc_total_used_bytes; - - // This is here so that it can be used in the subclasses to assert - // something different depending on which phase the GC is in. This - // can be particularly helpful in the check_mt_safety() methods. - static HRSPhase _phase; - - // Only used by HRSPhaseSetter. - static void clear_phase(); - static void set_phase(HRSPhase phase); + bool _verify_in_progress; // verify_region() is used to ensure that the contents of a region - // added to / removed from a set are consistent. Different sets - // make different assumptions about the regions added to them. So - // each set can override verify_region_extra(), which is called - // from verify_region(), and do any extra verification it needs to - // perform in that. - virtual const char* verify_region_extra(HeapRegion* hr) { return NULL; } - bool verify_region(HeapRegion* hr, - HeapRegionSetBase* expected_containing_set); + // added to / removed from a set are consistent. + void verify_region(HeapRegion* hr) PRODUCT_RETURN; // Indicates whether all regions in the set should be humongous or // not. Only used during verification. - virtual bool regions_humongous() = 0; + bool regions_humongous() { return _is_humongous; } // Indicates whether all regions in the set should be empty or // not. Only used during verification. - virtual bool regions_empty() = 0; + bool regions_empty() { return _is_empty; } + + void check_mt_safety() { + if (_mt_safety_checker != NULL) { + _mt_safety_checker->check(); + } + } + + virtual void fill_in_ext_msg_extra(hrs_ext_msg* msg) { } + + HeapRegionSetBase(const char* name, bool humongous, bool empty, HRSMtSafeChecker* mt_safety_checker); + +public: + const char* name() { return _name; } - // Subclasses can optionally override this to do MT safety protocol - // checks. It is called in an assert from all methods that perform - // updates on the set (and subclasses should also call it too). - virtual bool check_mt_safety() { return true; } + uint length() { return _count.length(); } + + bool is_empty() { return _count.length() == 0; } + + size_t total_capacity_bytes() { + return _count.capacity(); + } + + // It updates the fields of the set to reflect hr being added to + // the set and tags the region appropriately. + inline void add(HeapRegion* hr); + + // It updates the fields of the set to reflect hr being removed + // from the set and tags the region appropriately. + inline void remove(HeapRegion* hr); // fill_in_ext_msg() writes the the values of the set's attributes // in the custom err_msg (hrs_ext_msg). fill_in_ext_msg_extra() // allows subclasses to append further information. - virtual void fill_in_ext_msg_extra(hrs_ext_msg* msg) { } void fill_in_ext_msg(hrs_ext_msg* msg, const char* message); - // It updates the fields of the set to reflect hr being added to - // the set. - inline void update_for_addition(HeapRegion* hr); - - // It updates the fields of the set to reflect hr being added to - // the set and tags the region appropriately. - inline void add_internal(HeapRegion* hr); - - // It updates the fields of the set to reflect hr being removed - // from the set. - inline void update_for_removal(HeapRegion* hr); - - // It updates the fields of the set to reflect hr being removed - // from the set and tags the region appropriately. - inline void remove_internal(HeapRegion* hr); - - // It clears all the fields of the sets. Note: it will not iterate - // over the set and remove regions from it. It assumes that the - // caller has already done so. It will literally just clear the fields. - virtual void clear(); - - HeapRegionSetBase(const char* name); - -public: - static void set_unrealistically_long_length(uint len); - - const char* name() { return _name; } - - uint length() { return _length; } - - bool is_empty() { return _length == 0; } - - uint region_num() { return _region_num; } - - size_t total_capacity_bytes() { - return (size_t) region_num() << HeapRegion::LogOfHRGrainBytes; - } - - size_t total_used_bytes() { return _total_used_bytes; } - virtual void verify(); void verify_start(); void verify_next_region(HeapRegion* hr); @@ -187,40 +160,13 @@ // assert/guarantee-specific message it also prints out the values of // the fields of the associated set. This can be very helpful in // diagnosing failures. - class hrs_ext_msg : public hrs_err_msg { public: - hrs_ext_msg(HeapRegionSetBase* set, const char* message) : hrs_err_msg("") { + hrs_ext_msg(HeapRegionSetBase* set, const char* message) : hrs_err_msg("%s","") { set->fill_in_ext_msg(this, message); } }; -class HRSPhaseSetter { -public: - HRSPhaseSetter(HRSPhase phase) { - HeapRegionSetBase::set_phase(phase); - } - ~HRSPhaseSetter() { - HeapRegionSetBase::clear_phase(); - } -}; - -// These two macros are provided for convenience, to keep the uses of -// these two asserts a bit more concise. - -#define hrs_assert_mt_safety_ok(_set_) \ - do { \ - assert((_set_)->check_mt_safety(), hrs_ext_msg((_set_), "MT safety")); \ - } while (0) - -#define hrs_assert_region_ok(_set_, _hr_, _expected_) \ - do { \ - assert((_set_)->verify_region((_hr_), (_expected_)), \ - hrs_ext_msg((_set_), "region verification")); \ - } while (0) - -//////////////////// HeapRegionSet //////////////////// - #define hrs_assert_sets_match(_set1_, _set2_) \ do { \ assert(((_set1_)->regions_humongous() == \ @@ -236,63 +182,41 @@ // the same interface (namely, the HeapRegionSetBase API). class HeapRegionSet : public HeapRegionSetBase { -protected: - virtual const char* verify_region_extra(HeapRegion* hr) { - if (hr->next() != NULL) { - return "next() should always be NULL as we do not link the regions"; - } - - return HeapRegionSetBase::verify_region_extra(hr); - } - - HeapRegionSet(const char* name) : HeapRegionSetBase(name) { - clear(); - } - public: - // It adds hr to the set. The region should not be a member of - // another set. - inline void add(HeapRegion* hr); + HeapRegionSet(const char* name, bool humongous, HRSMtSafeChecker* mt_safety_checker): + HeapRegionSetBase(name, humongous, false /* empty */, mt_safety_checker) { } - // It removes hr from the set. The region should be a member of - // this set. - inline void remove(HeapRegion* hr); - - // It removes a region from the set. Instead of updating the fields - // of the set to reflect this removal, it accumulates the updates - // in proxy_set. The idea is that proxy_set is thread-local to - // avoid multiple threads updating the fields of the set - // concurrently and having to synchronize. The method - // update_from_proxy() will update the fields of the set from the - // proxy_set. - inline void remove_with_proxy(HeapRegion* hr, HeapRegionSet* proxy_set); - - // After multiple calls to remove_with_proxy() the updates to the - // fields of the set are accumulated in proxy_set. This call - // updates the fields of the set from proxy_set. - void update_from_proxy(HeapRegionSet* proxy_set); + void bulk_remove(const HeapRegionSetCount& removed) { + _count.decrement(removed.length(), removed.capacity()); + } }; -//////////////////// HeapRegionLinkedList //////////////////// - -// A set that links all the regions added to it in a singly-linked +// A set that links all the regions added to it in a doubly-linked // list. We should try to avoid doing operations that iterate over // such lists in performance critical paths. Typically we should -// add / remove one region at a time or concatenate two lists. All -// those operations are done in constant time. +// add / remove one region at a time or concatenate two lists. There are +// two ways to treat your lists, ordered and un-ordered. All un-ordered +// operations are done in constant time. To keep a list ordered only use +// add_ordered() to add elements to the list. If a list is not ordered +// from start, there is no way to sort it later. -class HeapRegionLinkedListIterator; +class FreeRegionListIterator; -class HeapRegionLinkedList : public HeapRegionSetBase { - friend class HeapRegionLinkedListIterator; +class FreeRegionList : public HeapRegionSetBase { + friend class FreeRegionListIterator; private: HeapRegion* _head; HeapRegion* _tail; - // These are provided for use by the friend classes. - HeapRegion* head() { return _head; } - HeapRegion* tail() { return _tail; } + // _last is used to keep track of where we added an element the last + // time in ordered lists. It helps to improve performance when adding + // several ordered items in a row. + HeapRegion* _last; + + static uint _unrealistically_long_length; + + void add_as_head_or_tail(FreeRegionList* from_list, bool as_head); protected: virtual void fill_in_ext_msg_extra(hrs_ext_msg* msg); @@ -300,11 +224,24 @@ // See the comment for HeapRegionSetBase::clear() virtual void clear(); - HeapRegionLinkedList(const char* name) : HeapRegionSetBase(name) { +public: + FreeRegionList(const char* name, HRSMtSafeChecker* mt_safety_checker = NULL): + HeapRegionSetBase(name, false /* humongous */, true /* empty */, mt_safety_checker) { clear(); } -public: + void verify_list(); + + HeapRegion* head() { return _head; } + HeapRegion* tail() { return _tail; } + + static void set_unrealistically_long_length(uint len); + + // Add hr to the list. The region should not be a member of another set. + // Assumes that the list is ordered and will preserve that order. The order + // is determined by hrs_index. + inline void add_ordered(HeapRegion* hr); + // It adds hr to the list as the new head. The region should not be // a member of another set. inline void add_as_head(HeapRegion* hr); @@ -320,15 +257,29 @@ // Convenience method. inline HeapRegion* remove_head_or_null(); + // Removes and returns the last element (_tail) of the list. It assumes + // that the list isn't empty so that it can return a non-NULL value. + inline HeapRegion* remove_tail(); + + // Convenience method + inline HeapRegion* remove_tail_or_null(); + + // Removes from head or tail based on the given argument. + inline HeapRegion* remove_region(bool from_head); + + // Merge two ordered lists. The result is also ordered. The order is + // determined by hrs_index. + void add_ordered(FreeRegionList* from_list); + // It moves the regions from from_list to this list and empties // from_list. The new regions will appear in the same order as they // were in from_list and be linked in the beginning of this list. - void add_as_head(HeapRegionLinkedList* from_list); + void add_as_head(FreeRegionList* from_list); // It moves the regions from from_list to this list and empties // from_list. The new regions will appear in the same order as they // were in from_list and be linked in the end of this list. - void add_as_tail(HeapRegionLinkedList* from_list); + void add_as_tail(FreeRegionList* from_list); // It empties the list by removing all regions from it. void remove_all(); @@ -346,15 +297,13 @@ virtual void print_on(outputStream* out, bool print_contents = false); }; -//////////////////// HeapRegionLinkedListIterator //////////////////// - // Iterator class that provides a convenient way to iterate over the // regions of a HeapRegionLinkedList instance. -class HeapRegionLinkedListIterator : public StackObj { +class FreeRegionListIterator : public StackObj { private: - HeapRegionLinkedList* _list; - HeapRegion* _curr; + FreeRegionList* _list; + HeapRegion* _curr; public: bool more_available() { @@ -369,13 +318,12 @@ // do the "cycle" check. HeapRegion* hr = _curr; - assert(_list->verify_region(hr, _list), "region verification"); + _list->verify_region(hr); _curr = hr->next(); return hr; } - HeapRegionLinkedListIterator(HeapRegionLinkedList* list) - : _curr(NULL), _list(list) { + FreeRegionListIterator(FreeRegionList* list) : _curr(NULL), _list(list) { _curr = list->head(); } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp --- a/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -27,116 +27,110 @@ #include "gc_implementation/g1/heapRegionSet.hpp" -//////////////////// HeapRegionSetBase //////////////////// - -inline void HeapRegionSetBase::update_for_addition(HeapRegion* hr) { - // Assumes the caller has already verified the region. - - _length += 1; - _region_num += hr->region_num(); - _total_used_bytes += hr->used(); -} - -inline void HeapRegionSetBase::add_internal(HeapRegion* hr) { - hrs_assert_region_ok(this, hr, NULL); - assert(hr->next() == NULL, hrs_ext_msg(this, "should not already be linked")); - - update_for_addition(hr); - hr->set_containing_set(this); -} +inline void HeapRegionSetBase::add(HeapRegion* hr) { + check_mt_safety(); + assert(hr->containing_set() == NULL, hrs_ext_msg(this, "should not already have a containing set %u")); + assert(hr->next() == NULL && hr->prev() == NULL, hrs_ext_msg(this, "should not already be linked")); -inline void HeapRegionSetBase::update_for_removal(HeapRegion* hr) { - // Assumes the caller has already verified the region. - assert(_length > 0, hrs_ext_msg(this, "pre-condition")); - _length -= 1; - - uint region_num_diff = hr->region_num(); - assert(region_num_diff <= _region_num, - hrs_err_msg("[%s] region's region num: %u " - "should be <= region num: %u", - name(), region_num_diff, _region_num)); - _region_num -= region_num_diff; - - size_t used_bytes = hr->used(); - assert(used_bytes <= _total_used_bytes, - hrs_err_msg("[%s] region's used bytes: "SIZE_FORMAT" " - "should be <= used bytes: "SIZE_FORMAT, - name(), used_bytes, _total_used_bytes)); - _total_used_bytes -= used_bytes; + _count.increment(1u, hr->capacity()); + hr->set_containing_set(this); + verify_region(hr); } -inline void HeapRegionSetBase::remove_internal(HeapRegion* hr) { - hrs_assert_region_ok(this, hr, this); - assert(hr->next() == NULL, hrs_ext_msg(this, "should already be unlinked")); +inline void HeapRegionSetBase::remove(HeapRegion* hr) { + check_mt_safety(); + verify_region(hr); + assert(hr->next() == NULL && hr->prev() == NULL, hrs_ext_msg(this, "should already be unlinked")); hr->set_containing_set(NULL); - update_for_removal(hr); -} - -//////////////////// HeapRegionSet //////////////////// - -inline void HeapRegionSet::add(HeapRegion* hr) { - hrs_assert_mt_safety_ok(this); - // add_internal() will verify the region. - add_internal(hr); + assert(_count.length() > 0, hrs_ext_msg(this, "pre-condition")); + _count.decrement(1u, hr->capacity()); } -inline void HeapRegionSet::remove(HeapRegion* hr) { - hrs_assert_mt_safety_ok(this); - // remove_internal() will verify the region. - remove_internal(hr); -} - -inline void HeapRegionSet::remove_with_proxy(HeapRegion* hr, - HeapRegionSet* proxy_set) { - // No need to fo the MT safety check here given that this method - // does not update the contents of the set but instead accumulates - // the changes in proxy_set which is assumed to be thread-local. - hrs_assert_sets_match(this, proxy_set); - hrs_assert_region_ok(this, hr, this); - - hr->set_containing_set(NULL); - proxy_set->update_for_addition(hr); -} - -//////////////////// HeapRegionLinkedList //////////////////// - -inline void HeapRegionLinkedList::add_as_head(HeapRegion* hr) { - hrs_assert_mt_safety_ok(this); +inline void FreeRegionList::add_ordered(HeapRegion* hr) { + check_mt_safety(); assert((length() == 0 && _head == NULL && _tail == NULL) || (length() > 0 && _head != NULL && _tail != NULL), hrs_ext_msg(this, "invariant")); - // add_internal() will verify the region. - add_internal(hr); + // add() will verify the region and check mt safety. + add(hr); + + // Now link the region + if (_head != NULL) { + HeapRegion* curr; + + if (_last != NULL && _last->hrs_index() < hr->hrs_index()) { + curr = _last; + } else { + curr = _head; + } + + // Find first entry with a Region Index larger than entry to insert. + while (curr != NULL && curr->hrs_index() < hr->hrs_index()) { + curr = curr->next(); + } + + hr->set_next(curr); + + if (curr == NULL) { + // Adding at the end + hr->set_prev(_tail); + _tail->set_next(hr); + _tail = hr; + } else if (curr->prev() == NULL) { + // Adding at the beginning + hr->set_prev(NULL); + _head = hr; + curr->set_prev(hr); + } else { + hr->set_prev(curr->prev()); + hr->prev()->set_next(hr); + curr->set_prev(hr); + } + } else { + // The list was empty + _tail = hr; + _head = hr; + } + _last = hr; +} + +inline void FreeRegionList::add_as_head(HeapRegion* hr) { + assert((length() == 0 && _head == NULL && _tail == NULL) || + (length() > 0 && _head != NULL && _tail != NULL), + hrs_ext_msg(this, "invariant")); + // add() will verify the region and check mt safety. + add(hr); // Now link the region. if (_head != NULL) { hr->set_next(_head); + _head->set_prev(hr); } else { _tail = hr; } _head = hr; } -inline void HeapRegionLinkedList::add_as_tail(HeapRegion* hr) { - hrs_assert_mt_safety_ok(this); +inline void FreeRegionList::add_as_tail(HeapRegion* hr) { + check_mt_safety(); assert((length() == 0 && _head == NULL && _tail == NULL) || (length() > 0 && _head != NULL && _tail != NULL), hrs_ext_msg(this, "invariant")); - // add_internal() will verify the region. - add_internal(hr); + // add() will verify the region and check mt safety. + add(hr); // Now link the region. if (_tail != NULL) { _tail->set_next(hr); + hr->set_prev(_tail); } else { _head = hr; } _tail = hr; } -inline HeapRegion* HeapRegionLinkedList::remove_head() { - hrs_assert_mt_safety_ok(this); +inline HeapRegion* FreeRegionList::remove_head() { assert(!is_empty(), hrs_ext_msg(this, "the list should not be empty")); assert(length() > 0 && _head != NULL && _tail != NULL, hrs_ext_msg(this, "invariant")); @@ -146,17 +140,22 @@ _head = hr->next(); if (_head == NULL) { _tail = NULL; + } else { + _head->set_prev(NULL); } hr->set_next(NULL); - // remove_internal() will verify the region. - remove_internal(hr); + if (_last == hr) { + _last = NULL; + } + + // remove() will verify the region and check mt safety. + remove(hr); return hr; } -inline HeapRegion* HeapRegionLinkedList::remove_head_or_null() { - hrs_assert_mt_safety_ok(this); - +inline HeapRegion* FreeRegionList::remove_head_or_null() { + check_mt_safety(); if (!is_empty()) { return remove_head(); } else { @@ -164,4 +163,47 @@ } } +inline HeapRegion* FreeRegionList::remove_tail() { + assert(!is_empty(), hrs_ext_msg(this, "The list should not be empty")); + assert(length() > 0 && _head != NULL && _tail != NULL, + hrs_ext_msg(this, "invariant")); + + // We need to unlink it first + HeapRegion* hr = _tail; + + _tail = hr->prev(); + if (_tail == NULL) { + _head = NULL; + } else { + _tail->set_next(NULL); + } + hr->set_prev(NULL); + + if (_last == hr) { + _last = NULL; + } + + // remove() will verify the region and check mt safety. + remove(hr); + return hr; +} + +inline HeapRegion* FreeRegionList::remove_tail_or_null() { + check_mt_safety(); + + if (!is_empty()) { + return remove_tail(); + } else { + return NULL; + } +} + +inline HeapRegion* FreeRegionList::remove_region(bool from_head) { + if (from_head) { + return remove_head_or_null(); + } else { + return remove_tail_or_null(); + } +} + #endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSET_INLINE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSets.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionSets.cpp Thu Oct 16 10:21:29 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,175 +0,0 @@ -/* - * Copyright (c) 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. - * - */ - -#include "precompiled.hpp" -#include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSets.hpp" - -// Note on the check_mt_safety() methods below: -// -// Verification of the "master" heap region sets / lists that are -// maintained by G1CollectedHeap is always done during a STW pause and -// by the VM thread at the start / end of the pause. The standard -// verification methods all assert check_mt_safety(). This is -// important as it ensures that verification is done without -// concurrent updates taking place at the same time. It follows, that, -// for the "master" heap region sets / lists, the check_mt_safety() -// method should include the VM thread / STW case. - -//////////////////// FreeRegionList //////////////////// - -const char* FreeRegionList::verify_region_extra(HeapRegion* hr) { - if (hr->is_young()) { - return "the region should not be young"; - } - // The superclass will check that the region is empty and - // not humongous. - return HeapRegionLinkedList::verify_region_extra(hr); -} - -//////////////////// MasterFreeRegionList //////////////////// - -const char* MasterFreeRegionList::verify_region_extra(HeapRegion* hr) { - // We should reset the RSet for parallel iteration before we add it - // to the master free list so that it is ready when the region is - // re-allocated. - if (!hr->rem_set()->verify_ready_for_par_iteration()) { - return "the region's RSet should be ready for parallel iteration"; - } - return FreeRegionList::verify_region_extra(hr); -} - -bool MasterFreeRegionList::check_mt_safety() { - // Master Free List MT safety protocol: - // (a) If we're at a safepoint, operations on the master free list - // should be invoked by either the VM thread (which will serialize - // them) or by the GC workers while holding the - // FreeList_lock. - // (b) If we're not at a safepoint, operations on the master free - // list should be invoked while holding the Heap_lock. - - if (SafepointSynchronize::is_at_safepoint()) { - guarantee(Thread::current()->is_VM_thread() || - FreeList_lock->owned_by_self(), - hrs_ext_msg(this, "master free list MT safety protocol " - "at a safepoint")); - } else { - guarantee(Heap_lock->owned_by_self(), - hrs_ext_msg(this, "master free list MT safety protocol " - "outside a safepoint")); - } - - return FreeRegionList::check_mt_safety(); -} - -//////////////////// SecondaryFreeRegionList //////////////////// - -bool SecondaryFreeRegionList::check_mt_safety() { - // Secondary Free List MT safety protocol: - // Operations on the secondary free list should always be invoked - // while holding the SecondaryFreeList_lock. - - guarantee(SecondaryFreeList_lock->owned_by_self(), - hrs_ext_msg(this, "secondary free list MT safety protocol")); - - return FreeRegionList::check_mt_safety(); -} - -//////////////////// OldRegionSet //////////////////// - -const char* OldRegionSet::verify_region_extra(HeapRegion* hr) { - if (hr->is_young()) { - return "the region should not be young"; - } - // The superclass will check that the region is not empty and not - // humongous. - return HeapRegionSet::verify_region_extra(hr); -} - -//////////////////// MasterOldRegionSet //////////////////// - -bool MasterOldRegionSet::check_mt_safety() { - // Master Old Set MT safety protocol: - // (a) If we're at a safepoint, operations on the master old set - // should be invoked: - // - by the VM thread (which will serialize them), or - // - by the GC workers while holding the FreeList_lock, if we're - // at a safepoint for an evacuation pause (this lock is taken - // anyway when an GC alloc region is retired so that a new one - // is allocated from the free list), or - // - by the GC workers while holding the OldSets_lock, if we're at a - // safepoint for a cleanup pause. - // (b) If we're not at a safepoint, operations on the master old set - // should be invoked while holding the Heap_lock. - - if (SafepointSynchronize::is_at_safepoint()) { - guarantee(Thread::current()->is_VM_thread() || - _phase == HRSPhaseEvacuation && FreeList_lock->owned_by_self() || - _phase == HRSPhaseCleanup && OldSets_lock->owned_by_self(), - hrs_ext_msg(this, "master old set MT safety protocol " - "at a safepoint")); - } else { - guarantee(Heap_lock->owned_by_self(), - hrs_ext_msg(this, "master old set MT safety protocol " - "outside a safepoint")); - } - - return OldRegionSet::check_mt_safety(); -} - -//////////////////// HumongousRegionSet //////////////////// - -const char* HumongousRegionSet::verify_region_extra(HeapRegion* hr) { - if (hr->is_young()) { - return "the region should not be young"; - } - // The superclass will check that the region is not empty and - // humongous. - return HeapRegionSet::verify_region_extra(hr); -} - -//////////////////// MasterHumongousRegionSet //////////////////// - -bool MasterHumongousRegionSet::check_mt_safety() { - // Master Humongous Set MT safety protocol: - // (a) If we're at a safepoint, operations on the master humongous - // set should be invoked by either the VM thread (which will - // serialize them) or by the GC workers while holding the - // OldSets_lock. - // (b) If we're not at a safepoint, operations on the master - // humongous set should be invoked while holding the Heap_lock. - - if (SafepointSynchronize::is_at_safepoint()) { - guarantee(Thread::current()->is_VM_thread() || - OldSets_lock->owned_by_self(), - hrs_ext_msg(this, "master humongous set MT safety protocol " - "at a safepoint")); - } else { - guarantee(Heap_lock->owned_by_self(), - hrs_ext_msg(this, "master humongous set MT safety protocol " - "outside a safepoint")); - } - - return HumongousRegionSet::check_mt_safety(); -} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/heapRegionSets.hpp --- a/src/share/vm/gc_implementation/g1/heapRegionSets.hpp Thu Oct 16 10:21:29 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/* - * Copyright (c) 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. - * - */ - -#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSETS_HPP -#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSETS_HPP - -#include "gc_implementation/g1/heapRegionSet.inline.hpp" - -//////////////////// FreeRegionList //////////////////// - -class FreeRegionList : public HeapRegionLinkedList { -protected: - virtual const char* verify_region_extra(HeapRegion* hr); - - virtual bool regions_humongous() { return false; } - virtual bool regions_empty() { return true; } - -public: - FreeRegionList(const char* name) : HeapRegionLinkedList(name) { } -}; - -//////////////////// MasterFreeRegionList //////////////////// - -class MasterFreeRegionList : public FreeRegionList { -protected: - virtual const char* verify_region_extra(HeapRegion* hr); - virtual bool check_mt_safety(); - -public: - MasterFreeRegionList(const char* name) : FreeRegionList(name) { } -}; - -//////////////////// SecondaryFreeRegionList //////////////////// - -class SecondaryFreeRegionList : public FreeRegionList { -protected: - virtual bool check_mt_safety(); - -public: - SecondaryFreeRegionList(const char* name) : FreeRegionList(name) { } -}; - -//////////////////// OldRegionSet //////////////////// - -class OldRegionSet : public HeapRegionSet { -protected: - virtual const char* verify_region_extra(HeapRegion* hr); - - virtual bool regions_humongous() { return false; } - virtual bool regions_empty() { return false; } - -public: - OldRegionSet(const char* name) : HeapRegionSet(name) { } -}; - -//////////////////// MasterOldRegionSet //////////////////// - -class MasterOldRegionSet : public OldRegionSet { -private: -protected: - virtual bool check_mt_safety(); - -public: - MasterOldRegionSet(const char* name) : OldRegionSet(name) { } -}; - -//////////////////// HumongousRegionSet //////////////////// - -class HumongousRegionSet : public HeapRegionSet { -protected: - virtual const char* verify_region_extra(HeapRegion* hr); - - virtual bool regions_humongous() { return true; } - virtual bool regions_empty() { return false; } - -public: - HumongousRegionSet(const char* name) : HeapRegionSet(name) { } -}; - -//////////////////// MasterHumongousRegionSet //////////////////// - -class MasterHumongousRegionSet : public HumongousRegionSet { -protected: - virtual bool check_mt_safety(); - -public: - MasterHumongousRegionSet(const char* name) : HumongousRegionSet(name) { } -}; - -#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSETS_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/satbQueue.cpp --- a/src/share/vm/gc_implementation/g1/satbQueue.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/satbQueue.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -32,6 +32,8 @@ #include "runtime/thread.hpp" #include "runtime/vmThread.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void ObjPtrQueue::flush() { // The buffer might contain refs into the CSet. We have to filter it // first before we flush it, otherwise we might end up with an @@ -219,58 +221,52 @@ } #ifdef ASSERT -void SATBMarkQueueSet::dump_active_values(JavaThread* first, - bool expected_active) { - gclog_or_tty->print_cr("SATB queue active values for Java Threads"); - gclog_or_tty->print_cr(" SATB queue set: active is %s", - (is_active()) ? "TRUE" : "FALSE"); - gclog_or_tty->print_cr(" expected_active is %s", - (expected_active) ? "TRUE" : "FALSE"); - for (JavaThread* t = first; t; t = t->next()) { - bool active = t->satb_mark_queue().is_active(); - gclog_or_tty->print_cr(" thread %s, active is %s", - t->name(), (active) ? "TRUE" : "FALSE"); +void SATBMarkQueueSet::dump_active_states(bool expected_active) { + gclog_or_tty->print_cr("Expected SATB active state: %s", + expected_active ? "ACTIVE" : "INACTIVE"); + gclog_or_tty->print_cr("Actual SATB active states:"); + gclog_or_tty->print_cr(" Queue set: %s", is_active() ? "ACTIVE" : "INACTIVE"); + for (JavaThread* t = Threads::first(); t; t = t->next()) { + gclog_or_tty->print_cr(" Thread \"%s\" queue: %s", t->name(), + t->satb_mark_queue().is_active() ? "ACTIVE" : "INACTIVE"); + } + gclog_or_tty->print_cr(" Shared queue: %s", + shared_satb_queue()->is_active() ? "ACTIVE" : "INACTIVE"); +} + +void SATBMarkQueueSet::verify_active_states(bool expected_active) { + // Verify queue set state + if (is_active() != expected_active) { + dump_active_states(expected_active); + guarantee(false, "SATB queue set has an unexpected active state"); + } + + // Verify thread queue states + for (JavaThread* t = Threads::first(); t; t = t->next()) { + if (t->satb_mark_queue().is_active() != expected_active) { + dump_active_states(expected_active); + guarantee(false, "Thread SATB queue has an unexpected active state"); + } + } + + // Verify shared queue state + if (shared_satb_queue()->is_active() != expected_active) { + dump_active_states(expected_active); + guarantee(false, "Shared SATB queue has an unexpected active state"); } } #endif // ASSERT -void SATBMarkQueueSet::set_active_all_threads(bool b, - bool expected_active) { +void SATBMarkQueueSet::set_active_all_threads(bool active, bool expected_active) { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); - JavaThread* first = Threads::first(); - #ifdef ASSERT - if (_all_active != expected_active) { - dump_active_values(first, expected_active); - - // I leave this here as a guarantee, instead of an assert, so - // that it will still be compiled in if we choose to uncomment - // the #ifdef ASSERT in a product build. The whole block is - // within an #ifdef ASSERT so the guarantee will not be compiled - // in a product build anyway. - guarantee(false, - "SATB queue set has an unexpected active value"); - } + verify_active_states(expected_active); #endif // ASSERT - _all_active = b; - - for (JavaThread* t = first; t; t = t->next()) { -#ifdef ASSERT - bool active = t->satb_mark_queue().is_active(); - if (active != expected_active) { - dump_active_values(first, expected_active); - - // I leave this here as a guarantee, instead of an assert, so - // that it will still be compiled in if we choose to uncomment - // the #ifdef ASSERT in a product build. The whole block is - // within an #ifdef ASSERT so the guarantee will not be compiled - // in a product build anyway. - guarantee(false, - "thread has an unexpected active value in its SATB queue"); - } -#endif // ASSERT - t->satb_mark_queue().set_active(b); + _all_active = active; + for (JavaThread* t = Threads::first(); t; t = t->next()) { + t->satb_mark_queue().set_active(active); } + shared_satb_queue()->set_active(active); } void SATBMarkQueueSet::filter_thread_buffers() { @@ -296,7 +292,7 @@ shared_satb_queue()->apply_closure_and_empty(_closure); } -void SATBMarkQueueSet::par_iterate_closure_all_threads(int worker) { +void SATBMarkQueueSet::par_iterate_closure_all_threads(uint worker) { SharedHeap* sh = SharedHeap::heap(); int parity = sh->strong_roots_parity(); @@ -321,7 +317,7 @@ } bool SATBMarkQueueSet::apply_closure_to_completed_buffer_work(bool par, - int worker) { + uint worker) { BufferNode* nd = NULL; { MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/satbQueue.hpp --- a/src/share/vm/gc_implementation/g1/satbQueue.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/satbQueue.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -84,10 +84,11 @@ // Utility function to support sequential and parallel versions. If // "par" is true, then "worker" is the par thread id; if "false", worker // is ignored. - bool apply_closure_to_completed_buffer_work(bool par, int worker); + bool apply_closure_to_completed_buffer_work(bool par, uint worker); #ifdef ASSERT - void dump_active_values(JavaThread* first, bool expected_active); + void dump_active_states(bool expected_active); + void verify_active_states(bool expected_active); #endif // ASSERT public: @@ -99,11 +100,11 @@ static void handle_zero_index_for_thread(JavaThread* t); - // Apply "set_active(b)" to all Java threads' SATB queues. It should be + // Apply "set_active(active)" to all SATB queues in the set. It should be // called only with the world stopped. The method will assert that the // SATB queues of all threads it visits, as well as the SATB queue // set itself, has an active value same as expected_active. - void set_active_all_threads(bool b, bool expected_active); + void set_active_all_threads(bool active, bool expected_active); // Filter all the currently-active SATB buffers. void filter_thread_buffers(); @@ -123,7 +124,7 @@ // be called serially and at a safepoint. void iterate_closure_all_threads(); // Parallel version of the above. - void par_iterate_closure_all_threads(int worker); + void par_iterate_closure_all_threads(uint worker); // If there exists some completed buffer, pop it, then apply the // registered closure to all its elements, and return true. If no @@ -132,7 +133,7 @@ return apply_closure_to_completed_buffer_work(false, 0); } // Parallel version of the above. - bool par_apply_closure_to_completed_buffer(int worker) { + bool par_apply_closure_to_completed_buffer(uint worker) { return apply_closure_to_completed_buffer_work(true, worker); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/sparsePRT.hpp --- a/src/share/vm/gc_implementation/g1/sparsePRT.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/sparsePRT.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -25,7 +25,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_SPARSEPRT_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_SPARSEPRT_HPP -#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/g1CollectedHeap.hpp" #include "gc_implementation/g1/heapRegion.hpp" #include "memory/allocation.hpp" #include "memory/cardTableModRefBS.hpp" diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/survRateGroup.cpp --- a/src/share/vm/gc_implementation/g1/survRateGroup.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/survRateGroup.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -29,6 +29,8 @@ #include "gc_implementation/g1/survRateGroup.hpp" #include "memory/allocation.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + SurvRateGroup::SurvRateGroup(G1CollectorPolicy* g1p, const char* name, size_t summary_surv_rates_len) : @@ -202,7 +204,7 @@ if (length == 0) return; - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); gclog_or_tty->print_cr("%s Rate Summary (for up to age %d)", _name, length-1); gclog_or_tty->print_cr(" age range survival rate (avg) samples (avg)"); gclog_or_tty->print_cr(" ---------------------------------------------------------"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/g1/vmStructs_g1.hpp --- a/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -57,9 +57,10 @@ nonstatic_field(G1MonitoringSupport, _old_committed, size_t) \ nonstatic_field(G1MonitoringSupport, _old_used, size_t) \ \ - nonstatic_field(HeapRegionSetBase, _length, uint) \ - nonstatic_field(HeapRegionSetBase, _region_num, uint) \ - nonstatic_field(HeapRegionSetBase, _total_used_bytes, size_t) \ + nonstatic_field(HeapRegionSetBase, _count, HeapRegionSetCount) \ + \ + nonstatic_field(HeapRegionSetCount, _length, uint) \ + nonstatic_field(HeapRegionSetCount, _capacity, size_t) \ #define VM_TYPES_G1(declare_type, declare_toplevel_type) \ @@ -72,6 +73,7 @@ declare_type(HeapRegion, ContiguousSpace) \ declare_toplevel_type(HeapRegionSeq) \ declare_toplevel_type(HeapRegionSetBase) \ + declare_toplevel_type(HeapRegionSetCount) \ declare_toplevel_type(G1MonitoringSupport) \ \ declare_toplevel_type(G1CollectedHeap*) \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parNew/asParNewGeneration.cpp --- a/src/share/vm/gc_implementation/parNew/asParNewGeneration.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parNew/asParNewGeneration.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -259,22 +259,22 @@ requested_eden_size, requested_survivor_size); gclog_or_tty->print_cr(" eden: [" PTR_FORMAT ".." PTR_FORMAT ") " SIZE_FORMAT, - eden()->bottom(), - eden()->end(), + p2i(eden()->bottom()), + p2i(eden()->end()), pointer_delta(eden()->end(), eden()->bottom(), sizeof(char))); gclog_or_tty->print_cr(" from: [" PTR_FORMAT ".." PTR_FORMAT ") " SIZE_FORMAT, - from()->bottom(), - from()->end(), + p2i(from()->bottom()), + p2i(from()->end()), pointer_delta(from()->end(), from()->bottom(), sizeof(char))); gclog_or_tty->print_cr(" to: [" PTR_FORMAT ".." PTR_FORMAT ") " SIZE_FORMAT, - to()->bottom(), - to()->end(), + p2i(to()->bottom()), + p2i(to()->end()), pointer_delta( to()->end(), to()->bottom(), sizeof(char))); @@ -382,18 +382,18 @@ if (PrintAdaptiveSizePolicy && Verbose) { gclog_or_tty->print_cr(" [eden_start .. eden_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - eden_start, - eden_end, + p2i(eden_start), + p2i(eden_end), pointer_delta(eden_end, eden_start, sizeof(char))); gclog_or_tty->print_cr(" [from_start .. from_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - from_start, - from_end, + p2i(from_start), + p2i(from_end), pointer_delta(from_end, from_start, sizeof(char))); gclog_or_tty->print_cr(" [ to_start .. to_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - to_start, - to_end, + p2i(to_start), + p2i(to_end), pointer_delta( to_end, to_start, sizeof(char))); } } else { @@ -473,18 +473,18 @@ if (PrintAdaptiveSizePolicy && Verbose) { gclog_or_tty->print_cr(" [eden_start .. eden_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - eden_start, - eden_end, + p2i(eden_start), + p2i(eden_end), pointer_delta(eden_end, eden_start, sizeof(char))); gclog_or_tty->print_cr(" [ to_start .. to_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - to_start, - to_end, + p2i(to_start), + p2i(to_end), pointer_delta( to_end, to_start, sizeof(char))); gclog_or_tty->print_cr(" [from_start .. from_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - from_start, - from_end, + p2i(from_start), + p2i(from_end), pointer_delta(from_end, from_start, sizeof(char))); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp --- a/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -35,6 +35,8 @@ #include "runtime/virtualspace.hpp" #include "runtime/vmThread.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr, OopsInGenClosure* cl, CardTableRS* ct, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parNew/parNewGeneration.cpp --- a/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -55,6 +55,8 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/workgroup.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable:4355 ) // 'this' : used in base member initializer list @@ -1636,8 +1638,7 @@ refs_discovery_is_mt(), // mt discovery (int) ParallelGCThreads, // mt discovery degree refs_discovery_is_atomic(), // atomic_discovery - NULL, // is_alive_non_header - false); // write barrier for next field updates + NULL); // is_alive_non_header } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp --- a/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -79,12 +79,12 @@ if ((HeapWord*)obj < _boundary) { #ifndef PRODUCT if (_g->to()->is_in_reserved(obj)) { - tty->print_cr("Scanning field (" PTR_FORMAT ") twice?", p); + tty->print_cr("Scanning field (" PTR_FORMAT ") twice?", p2i(p)); GenCollectedHeap* gch = (GenCollectedHeap*)Universe::heap(); Space* sp = gch->space_containing(p); oop obj = oop(sp->block_start(p)); assert((HeapWord*)obj < (HeapWord*)p, "Error"); - tty->print_cr("Object: " PTR_FORMAT, (void *)obj); + tty->print_cr("Object: " PTR_FORMAT, p2i((void *)obj)); tty->print_cr("-------"); obj->print(); tty->print_cr("-----"); @@ -110,7 +110,7 @@ if (TraceScavenge) { gclog_or_tty->print_cr("{%s %s ( " PTR_FORMAT " ) " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", "forwarded ", - new_obj->klass()->internal_name(), p, (void *)obj, (void *)new_obj, new_obj->size()); + new_obj->klass()->internal_name(), p2i(p), p2i((void *)obj), p2i((void *)new_obj), new_obj->size()); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/adjoiningGenerations.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -143,7 +143,7 @@ if (TraceAdaptiveGCBoundary) { gclog_or_tty->print_cr("Before expansion of old gen with boundary move"); - gclog_or_tty->print_cr(" Requested change: 0x%x Attempted change: 0x%x", + gclog_or_tty->print_cr(" Requested change: " SIZE_FORMAT_HEX " Attempted change: " SIZE_FORMAT_HEX, expand_in_bytes, change_in_bytes); if (!PrintHeapAtGC) { Universe::print_on(gclog_or_tty); @@ -201,7 +201,7 @@ if (TraceAdaptiveGCBoundary) { gclog_or_tty->print_cr("Before expansion of young gen with boundary move"); - gclog_or_tty->print_cr(" Requested change: 0x%x Attempted change: 0x%x", + gclog_or_tty->print_cr(" Requested change: " SIZE_FORMAT_HEX " Attempted change: " SIZE_FORMAT_HEX, expand_in_bytes, change_in_bytes); if (!PrintHeapAtGC) { Universe::print_on(gclog_or_tty); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/asPSOldGen.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -127,22 +127,22 @@ size_t result_aligned = align_size_down(result, gen_alignment); if (PrintAdaptiveSizePolicy && Verbose) { gclog_or_tty->print_cr("\nASPSOldGen::available_for_contraction:" - " %d K / 0x%x", result_aligned/K, result_aligned); - gclog_or_tty->print_cr(" reserved().byte_size() %d K / 0x%x ", - reserved().byte_size()/K, reserved().byte_size()); + " " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX, (result_aligned/K), result_aligned); + gclog_or_tty->print_cr(" reserved().byte_size() " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX " ", + (reserved().byte_size()/K), reserved().byte_size()); size_t working_promoted = (size_t) policy->avg_promoted()->padded_average(); - gclog_or_tty->print_cr(" padded promoted %d K / 0x%x", - working_promoted/K, working_promoted); - gclog_or_tty->print_cr(" used %d K / 0x%x", - used_in_bytes()/K, used_in_bytes()); - gclog_or_tty->print_cr(" min_gen_size() %d K / 0x%x", - min_gen_size()/K, min_gen_size()); - gclog_or_tty->print_cr(" max_contraction %d K / 0x%x", - max_contraction/K, max_contraction); - gclog_or_tty->print_cr(" without alignment %d K / 0x%x", - policy->promo_increment(max_contraction)/K, + gclog_or_tty->print_cr(" padded promoted " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX, + (working_promoted/K), working_promoted); + gclog_or_tty->print_cr(" used " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX, + (used_in_bytes()/K), used_in_bytes()); + gclog_or_tty->print_cr(" min_gen_size() " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX, + (min_gen_size()/K), min_gen_size()); + gclog_or_tty->print_cr(" max_contraction " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX, + (max_contraction/K), max_contraction); + gclog_or_tty->print_cr(" without alignment " SSIZE_FORMAT " K / " SIZE_FORMAT_HEX, + (policy->promo_increment(max_contraction)/K), policy->promo_increment(max_contraction)); - gclog_or_tty->print_cr(" alignment 0x%x", gen_alignment); + gclog_or_tty->print_cr(" alignment " SIZE_FORMAT_HEX, gen_alignment); } assert(result_aligned <= max_contraction, "arithmetic is wrong"); return result_aligned; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/asPSYoungGen.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -112,11 +112,11 @@ size_t result = policy->eden_increment_aligned_down(max_contraction); size_t result_aligned = align_size_down(result, gen_alignment); if (PrintAdaptiveSizePolicy && Verbose) { - gclog_or_tty->print_cr("ASPSYoungGen::available_for_contraction: %d K", + gclog_or_tty->print_cr("ASPSYoungGen::available_for_contraction: " SIZE_FORMAT " K", result_aligned/K); - gclog_or_tty->print_cr(" max_contraction %d K", max_contraction/K); - gclog_or_tty->print_cr(" eden_avail %d K", eden_avail/K); - gclog_or_tty->print_cr(" gen_avail %d K", gen_avail/K); + gclog_or_tty->print_cr(" max_contraction " SIZE_FORMAT " K", max_contraction/K); + gclog_or_tty->print_cr(" eden_avail " SIZE_FORMAT " K", eden_avail/K); + gclog_or_tty->print_cr(" gen_avail " SIZE_FORMAT " K", gen_avail/K); } return result_aligned; } @@ -252,22 +252,22 @@ requested_eden_size, requested_survivor_size); gclog_or_tty->print_cr(" eden: [" PTR_FORMAT ".." PTR_FORMAT ") " SIZE_FORMAT, - eden_space()->bottom(), - eden_space()->end(), + p2i(eden_space()->bottom()), + p2i(eden_space()->end()), pointer_delta(eden_space()->end(), eden_space()->bottom(), sizeof(char))); gclog_or_tty->print_cr(" from: [" PTR_FORMAT ".." PTR_FORMAT ") " SIZE_FORMAT, - from_space()->bottom(), - from_space()->end(), + p2i(from_space()->bottom()), + p2i(from_space()->end()), pointer_delta(from_space()->end(), from_space()->bottom(), sizeof(char))); gclog_or_tty->print_cr(" to: [" PTR_FORMAT ".." PTR_FORMAT ") " SIZE_FORMAT, - to_space()->bottom(), - to_space()->end(), + p2i(to_space()->bottom()), + p2i(to_space()->end()), pointer_delta( to_space()->end(), to_space()->bottom(), sizeof(char))); @@ -373,18 +373,18 @@ if (PrintAdaptiveSizePolicy && Verbose) { gclog_or_tty->print_cr(" [eden_start .. eden_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - eden_start, - eden_end, + p2i(eden_start), + p2i(eden_end), pointer_delta(eden_end, eden_start, sizeof(char))); gclog_or_tty->print_cr(" [from_start .. from_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - from_start, - from_end, + p2i(from_start), + p2i(from_end), pointer_delta(from_end, from_start, sizeof(char))); gclog_or_tty->print_cr(" [ to_start .. to_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - to_start, - to_end, + p2i(to_start), + p2i(to_end), pointer_delta( to_end, to_start, sizeof(char))); } } else { @@ -427,18 +427,18 @@ if (PrintAdaptiveSizePolicy && Verbose) { gclog_or_tty->print_cr(" [eden_start .. eden_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - eden_start, - eden_end, + p2i(eden_start), + p2i(eden_end), pointer_delta(eden_end, eden_start, sizeof(char))); gclog_or_tty->print_cr(" [ to_start .. to_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - to_start, - to_end, + p2i(to_start), + p2i(to_end), pointer_delta( to_end, to_start, sizeof(char))); gclog_or_tty->print_cr(" [from_start .. from_end): " "[" PTR_FORMAT " .. " PTR_FORMAT ") " SIZE_FORMAT, - from_start, - from_end, + p2i(from_start), + p2i(from_end), pointer_delta(from_end, from_start, sizeof(char))); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -478,23 +478,23 @@ gclog_or_tty->print_cr(" " " _covered[%d].start(): " INTPTR_FORMAT " _covered[%d].last(): " INTPTR_FORMAT, - ind, _covered[ind].start(), - ind, _covered[ind].last()); + ind, p2i(_covered[ind].start()), + ind, p2i(_covered[ind].last())); gclog_or_tty->print_cr(" " " _committed[%d].start(): " INTPTR_FORMAT " _committed[%d].last(): " INTPTR_FORMAT, - ind, _committed[ind].start(), - ind, _committed[ind].last()); + ind, p2i(_committed[ind].start()), + ind, p2i(_committed[ind].last())); gclog_or_tty->print_cr(" " " byte_for(start): " INTPTR_FORMAT " byte_for(last): " INTPTR_FORMAT, - byte_for(_covered[ind].start()), - byte_for(_covered[ind].last())); + p2i(byte_for(_covered[ind].start())), + p2i(byte_for(_covered[ind].last()))); gclog_or_tty->print_cr(" " " addr_for(start): " INTPTR_FORMAT " addr_for(last): " INTPTR_FORMAT, - addr_for((jbyte*) _committed[ind].start()), - addr_for((jbyte*) _committed[ind].last())); + p2i(addr_for((jbyte*) _committed[ind].start())), + p2i(addr_for((jbyte*) _committed[ind].last()))); } debug_only(verify_guard();) } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/gcTaskManager.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -31,6 +31,8 @@ #include "runtime/mutex.hpp" #include "runtime/mutexLocker.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // // GCTask // diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/gcTaskThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -34,6 +34,8 @@ #include "runtime/os.hpp" #include "runtime/thread.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + GCTaskThread::GCTaskThread(GCTaskManager* manager, uint which, uint processor_id) : diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/objectStartArray.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -139,11 +139,6 @@ return true; } } - // No object starts in this slice; verify this using - // more traditional methods: Note that no object can - // start before the start_addr. - assert(end_addr == start_addr || - object_start(end_addr - 1) <= start_addr, - "Oops an object does start in this slice?"); + return false; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -38,6 +38,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/parMarkBitMap.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -155,7 +155,7 @@ static inline idx_t bits_required(MemRegion covered_region); void print_on_error(outputStream* st) const { - st->print_cr("Marking Bits: (ParMarkBitMap*) " PTR_FORMAT, this); + st->print_cr("Marking Bits: (ParMarkBitMap*) " PTR_FORMAT, p2i(this)); _beg_bits.print_on_error(st, " Begin Bits: "); _end_bits.print_on_error(st, " End Bits: "); } @@ -390,9 +390,9 @@ inline void ParMarkBitMap::verify_addr(HeapWord* addr) const { // Allow one past the last valid address; useful for loop bounds. assert(addr >= region_start(), - err_msg("addr too small, addr: " PTR_FORMAT " region start: " PTR_FORMAT, addr, region_start())); + err_msg("addr too small, addr: " PTR_FORMAT " region start: " PTR_FORMAT, p2i(addr), p2i(region_start()))); assert(addr <= region_end(), - err_msg("addr too big, addr: " PTR_FORMAT " region end: " PTR_FORMAT, addr, region_end())); + err_msg("addr too big, addr: " PTR_FORMAT " region end: " PTR_FORMAT, p2i(addr), p2i(region_end()))); } #endif // #ifdef ASSERT diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -373,7 +373,7 @@ if ((result == NULL) && (QueuedAllocationWarningCount > 0) && (loop_count % QueuedAllocationWarningCount == 0)) { warning("ParallelScavengeHeap::mem_allocate retries %d times \n\t" - " size=%d", loop_count, size); + " size=" SIZE_FORMAT, loop_count, size); } } @@ -492,6 +492,10 @@ return young_gen()->eden_space()->tlab_capacity(thr); } +size_t ParallelScavengeHeap::tlab_used(Thread* thr) const { + return young_gen()->eden_space()->tlab_used(thr); +} + size_t ParallelScavengeHeap::unsafe_max_tlab_alloc(Thread* thr) const { return young_gen()->eden_space()->unsafe_max_tlab_alloc(thr); } @@ -665,8 +669,10 @@ void ParallelScavengeHeap::trace_heap(GCWhen::Type when, GCTracer* gc_tracer) { const PSHeapSummary& heap_summary = create_ps_heap_summary(); + gc_tracer->report_gc_heap_summary(when, heap_summary); + const MetaspaceSummary& metaspace_summary = create_metaspace_summary(); - gc_tracer->report_gc_heap_summary(when, heap_summary, metaspace_summary); + gc_tracer->report_metaspace_summary(when, metaspace_summary); } ParallelScavengeHeap* ParallelScavengeHeap::heap() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -189,6 +189,7 @@ bool supports_tlab_allocation() const { return true; } size_t tlab_capacity(Thread* thr) const; + size_t tlab_used(Thread* thr) const; size_t unsafe_max_tlab_alloc(Thread* thr) const; // Can a compiler initialize a new object without store barriers? diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -52,7 +52,7 @@ const void* loc = (void*) p; bool result = ((HeapWord*)p) >= young_gen()->reserved().start(); assert(result == young_gen()->is_in_reserved(p), - err_msg("incorrect test - result=%d, p=" PTR_FORMAT, result, (void*)p)); + err_msg("incorrect test - result=%d, p=" PTR_FORMAT, result, p2i((void*)p))); return result; } #endif // SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PARALLELSCAVENGEHEAP_INLINE_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -41,6 +41,8 @@ #include "runtime/vmThread.hpp" #include "services/management.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // // ThreadRootsMarkingTask // diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" #include "gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp" #include "gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.hpp" #include "gc_implementation/parallelScavenge/psScavenge.hpp" @@ -34,6 +35,8 @@ #include +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + PSAdaptiveSizePolicy::PSAdaptiveSizePolicy(size_t init_eden_size, size_t init_promo_size, size_t init_survivor_size, @@ -76,6 +79,38 @@ _old_gen_policy_is_ready = false; } +size_t PSAdaptiveSizePolicy::calculate_free_based_on_live(size_t live, uintx ratio_as_percentage) { + // We want to calculate how much free memory there can be based on the + // amount of live data currently in the old gen. Using the formula: + // ratio * (free + live) = free + // Some equation solving later we get: + // free = (live * ratio) / (1 - ratio) + + const double ratio = ratio_as_percentage / 100.0; + const double ratio_inverse = 1.0 - ratio; + const double tmp = live * ratio; + size_t free = (size_t)(tmp / ratio_inverse); + + return free; +} + +size_t PSAdaptiveSizePolicy::calculated_old_free_size_in_bytes() const { + size_t free_size = (size_t)(_promo_size + avg_promoted()->padded_average()); + size_t live = ParallelScavengeHeap::heap()->old_gen()->used_in_bytes(); + + if (MinHeapFreeRatio != 0) { + size_t min_free = calculate_free_based_on_live(live, MinHeapFreeRatio); + free_size = MAX2(free_size, min_free); + } + + if (MaxHeapFreeRatio != 100) { + size_t max_free = calculate_free_based_on_live(live, MaxHeapFreeRatio); + free_size = MIN2(max_free, free_size); + } + + return free_size; +} + void PSAdaptiveSizePolicy::major_collection_begin() { // Update the interval time _major_timer.stop(); @@ -1000,7 +1035,7 @@ "AdaptiveSizePolicy::adjust_promo_for_footprint " "adjusting tenured gen for footprint. " "starting promo size " SIZE_FORMAT - " reduced promo size " SIZE_FORMAT, + " reduced promo size " SIZE_FORMAT " promo delta " SIZE_FORMAT, desired_promo_size, reduced_size, change ); } @@ -1292,3 +1327,18 @@ st, PSScavenge::tenuring_threshold()); } + +#ifndef PRODUCT + +void TestOldFreeSpaceCalculation_test() { + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 20) == 25, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 50) == 100, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 60) == 150, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(100, 75) == 300, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 20) == 100, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 50) == 400, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 60) == 600, "Calculation of free memory failed"); + assert(PSAdaptiveSizePolicy::calculate_free_based_on_live(400, 75) == 1200, "Calculation of free memory failed"); +} + +#endif /* !PRODUCT */ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psAdaptiveSizePolicy.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -240,7 +240,6 @@ void major_collection_begin(); void major_collection_end(size_t amount_live, GCCause::Cause gc_cause); - // void tenured_allocation(size_t size) { _avg_pretenured->sample(size); } @@ -248,9 +247,9 @@ // Accessors // NEEDS_CLEANUP should use sizes.hpp - size_t calculated_old_free_size_in_bytes() const { - return (size_t)(_promo_size + avg_promoted()->padded_average()); - } + static size_t calculate_free_based_on_live(size_t live, uintx ratio_as_percentage); + + size_t calculated_old_free_size_in_bytes() const; size_t average_old_live_in_bytes() const { return (size_t) avg_old_live()->average(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -54,6 +54,8 @@ #include "utilities/events.hpp" #include "utilities/stack.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + elapsedTimer PSMarkSweep::_accumulated_time; jlong PSMarkSweep::_time_of_last_gc = 0; CollectorCounters* PSMarkSweep::_counters = NULL; @@ -184,7 +186,7 @@ size_t prev_used = heap->used(); // Capture metadata size before collection for sizing. - size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes(); + size_t metadata_prev_used = MetaspaceAux::used_bytes(); // For PrintGCDetails size_t old_gen_prev_used = old_gen->used_in_bytes(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -33,6 +33,8 @@ #include "oops/oop.inline.hpp" #include "runtime/java.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + inline const char* PSOldGen::select_name() { return UseParallelOldGC ? "ParOldGen" : "PSOldGen"; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -61,6 +61,8 @@ #include +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // All sizes are in HeapWords. const size_t ParallelCompactData::Log2RegionSize = 16; // 64K words const size_t ParallelCompactData::RegionSize = (size_t)1 << Log2RegionSize; @@ -851,8 +853,7 @@ true, // mt discovery (int) ParallelGCThreads, // mt discovery degree true, // atomic_discovery - &_is_alive_closure, // non-header is alive closure - false); // write barrier for next field updates + &_is_alive_closure); // non-header is alive closure _counters = new CollectorCounters("PSParallelCompact", 1); // Initialize static fields in ParCompactionManager. @@ -927,7 +928,7 @@ _heap_used = heap->used(); _young_gen_used = heap->young_gen()->used_in_bytes(); _old_gen_used = heap->old_gen()->used_in_bytes(); - _metadata_used = MetaspaceAux::allocated_used_bytes(); + _metadata_used = MetaspaceAux::used_bytes(); }; size_t heap_used() const { return _heap_used; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -35,6 +35,8 @@ #include "oops/oop.inline.hpp" #include "oops/oop.psgc.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + PaddedEnd* PSPromotionManager::_manager_array = NULL; OopStarTaskQueueSet* PSPromotionManager::_stack_array_depth = NULL; PSOldGen* PSPromotionManager::_old_gen = NULL; @@ -136,7 +138,7 @@ } const uint hlines = sizeof(pm_stats_hdr) / sizeof(pm_stats_hdr[0]); - for (uint i = 0; i < hlines; ++i) tty->print_cr(pm_stats_hdr[i]); + for (uint i = 0; i < hlines; ++i) tty->print_cr("%s", pm_stats_hdr[i]); for (uint i = 0; i < ParallelGCThreads + 1; ++i) { manager_array(i)->print_local_stats(i); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -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"); @@ -225,7 +226,7 @@ if (TraceScavenge) { gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", PSScavenge::should_scavenge(&new_obj) ? "copying" : "tenuring", - new_obj->klass()->internal_name(), (void *)o, (void *)new_obj, new_obj->size()); + new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -56,6 +56,7 @@ #include "services/memoryService.hpp" #include "utilities/stack.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC HeapWord* PSScavenge::_to_space_top_before_gc = NULL; int PSScavenge::_consecutive_skipped_scavenges = 0; @@ -466,10 +467,12 @@ } } - GCTraceTime tm("StringTable", false, false, &_gc_timer); - // Unlink any dead interned Strings and process the remaining live ones. - PSScavengeRootsClosure root_closure(promotion_manager); - StringTable::unlink_or_oops_do(&_is_alive_closure, &root_closure); + { + GCTraceTime tm("StringTable", false, false, &_gc_timer); + // Unlink any dead interned Strings and process the remaining live ones. + PSScavengeRootsClosure root_closure(promotion_manager); + StringTable::unlink_or_oops_do(&_is_alive_closure, &root_closure); + } // Finally, flush the promotion_manager's labs, and deallocate its stacks. promotion_failure_occurred = PSPromotionManager::post_scavenge(_gc_tracer); @@ -527,8 +530,19 @@ counters->update_survivor_overflowed(_survivor_overflow); } + size_t max_young_size = young_gen->max_size(); + + // Deciding a free ratio in the young generation is tricky, so if + // MinHeapFreeRatio or MaxHeapFreeRatio are in use (implicating + // that the old generation size may have been limited because of them) we + // should then limit our young generation size using NewRatio to have it + // follow the old generation size. + if (MinHeapFreeRatio != 0 || MaxHeapFreeRatio != 100) { + max_young_size = MIN2(old_gen->capacity_in_bytes() / NewRatio, young_gen->max_size()); + } + size_t survivor_limit = - size_policy->max_survivor_size(young_gen->max_size()); + size_policy->max_survivor_size(max_young_size); _tenuring_threshold = size_policy->compute_survivor_space_size_and_threshold( _survivor_overflow, @@ -551,8 +565,7 @@ // Do call at minor collections? // Don't check if the size_policy is ready at this // level. Let the size_policy check that internally. - if (UseAdaptiveSizePolicy && - UseAdaptiveGenerationSizePolicyAtMinorCollection && + if (UseAdaptiveGenerationSizePolicyAtMinorCollection && ((gc_cause != GCCause::_java_lang_system_gc) || UseAdaptiveSizePolicyWithSystemGC)) { @@ -566,7 +579,7 @@ size_t eden_live = young_gen->eden_space()->used_in_bytes(); size_t cur_eden = young_gen->eden_space()->capacity_in_bytes(); size_t max_old_gen_size = old_gen->max_gen_size(); - size_t max_eden_size = young_gen->max_size() - + size_t max_eden_size = max_young_size - young_gen->from_space()->capacity_in_bytes() - young_gen->to_space()->capacity_in_bytes(); @@ -848,8 +861,7 @@ true, // mt discovery (int) ParallelGCThreads, // mt discovery degree true, // atomic_discovery - NULL, // header provides liveness info - false); // next field updates do not need write barrier + NULL); // header provides liveness info // Cache the cardtable BarrierSet* bs = Universe::heap()->barrier_set(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -81,7 +81,7 @@ if (TraceScavenge && o->is_forwarded()) { gclog_or_tty->print_cr("{%s %s " PTR_FORMAT " -> " PTR_FORMAT " (%d)}", "forwarding", - new_obj->klass()->internal_name(), (void *)o, (void *)new_obj, new_obj->size()); + new_obj->klass()->internal_name(), p2i((void *)o), p2i((void *)new_obj), new_obj->size()); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -35,10 +35,15 @@ #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 +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // PSVirtualSpace PSVirtualSpace::PSVirtualSpace(ReservedSpace rs, size_t alignment) : diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -33,6 +33,8 @@ #include "oops/oop.inline.hpp" #include "runtime/java.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + PSYoungGen::PSYoungGen(size_t initial_size, size_t min_size, size_t max_size) : diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -127,7 +127,7 @@ void adjust_pointers(); void compact(); - // Called during/after gc + // Called during/after GC void swap_spaces(); // Resize generation using suggested free space size and survivor size @@ -146,14 +146,14 @@ size_t free_in_words() const; // The max this generation can grow to - size_t max_size() const { return _reserved.byte_size(); } + size_t max_size() const { return _reserved.byte_size(); } // The max this generation can grow to if the boundary between // the generations are allowed to move. size_t gen_size_limit() const { return _max_gen_size; } bool is_maximal_no_gc() const { - return true; // never expands except at a GC + return true; // Never expands except at a GC } // Allocation diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/adaptiveSizePolicy.cpp --- a/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 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 @@ -171,8 +171,8 @@ "active_workers(): %d new_acitve_workers: %d " "prev_active_workers: %d\n" " active_workers_by_JT: %d active_workers_by_heap_size: %d", - active_workers, new_active_workers, prev_active_workers, - active_workers_by_JT, active_workers_by_heap_size); + (int) active_workers, (int) new_active_workers, (int) prev_active_workers, + (int) active_workers_by_JT, (int) active_workers_by_heap_size); } assert(new_active_workers > 0, "Always need at least 1"); return new_active_workers; @@ -545,13 +545,13 @@ if (UseGCOverheadLimit && PrintGCDetails && Verbose) { if (gc_overhead_limit_exceeded()) { gclog_or_tty->print_cr(" GC is exceeding overhead limit " - "of %d%%", GCTimeLimit); + "of %d%%", (int) GCTimeLimit); reset_gc_overhead_limit_count(); } else if (print_gc_overhead_limit_would_be_exceeded) { assert(gc_overhead_limit_count() > 0, "Should not be printing"); gclog_or_tty->print_cr(" GC would exceed overhead limit " "of %d%% %d consecutive time(s)", - GCTimeLimit, gc_overhead_limit_count()); + (int) GCTimeLimit, gc_overhead_limit_count()); } } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/ageTable.cpp --- a/src/share/vm/gc_implementation/shared/ageTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/ageTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -97,7 +97,7 @@ if (PrintTenuringDistribution) { gclog_or_tty->cr(); gclog_or_tty->print_cr("Desired survivor size " SIZE_FORMAT " bytes, new threshold %u (max %u)", - desired_survivor_size*oopSize, result, MaxTenuringThreshold); + desired_survivor_size*oopSize, result, (int) MaxTenuringThreshold); } total = 0; @@ -106,8 +106,8 @@ total += sizes[age]; if (sizes[age] > 0) { if (PrintTenuringDistribution) { - gclog_or_tty->print_cr("- age %3u: %10ld bytes, %10ld total", - age, sizes[age]*oopSize, total*oopSize); + gclog_or_tty->print_cr("- age %3u: " SIZE_FORMAT_W(10) " bytes, " SIZE_FORMAT_W(10) " total", + age, sizes[age]*oopSize, total*oopSize); } } if (UsePerfData) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/allocationStats.hpp --- a/src/share/vm/gc_implementation/shared/allocationStats.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/allocationStats.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -107,7 +107,7 @@ assert(demand >= 0, err_msg("Demand (" SSIZE_FORMAT ") should be non-negative for " PTR_FORMAT " (size=" SIZE_FORMAT ")", - demand, this, count)); + demand, p2i(this), count)); // Defensive: adjust for imprecision in event counting if (demand < 0) { demand = 0; @@ -120,8 +120,9 @@ float delta_ise = (CMSExtrapolateSweep ? intra_sweep_estimate : 0.0); _desired = (ssize_t)(new_rate * (inter_sweep_estimate + delta_ise)); if (PrintFLSStatistics > 1) { - gclog_or_tty->print_cr("demand: %d, old_rate: %f, current_rate: %f, new_rate: %f, old_desired: %d, new_desired: %d", - demand, old_rate, rate, new_rate, old_desired, _desired); + gclog_or_tty->print_cr( + "demand: " SSIZE_FORMAT ", old_rate: %f, current_rate: %f, new_rate: %f, old_desired: " SSIZE_FORMAT ", new_desired: " SSIZE_FORMAT, + demand, old_rate, rate, new_rate, old_desired, _desired); } } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/gcHeapSummary.hpp --- a/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/gcHeapSummary.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -26,6 +26,7 @@ #define SHARE_VM_GC_IMPLEMENTATION_SHARED_GCHEAPSUMMARY_HPP #include "memory/allocation.hpp" +#include "memory/metaspaceChunkFreeListSummary.hpp" class VirtualSpaceSummary : public StackObj { HeapWord* _start; @@ -61,16 +62,16 @@ }; class MetaspaceSizes : public StackObj { - size_t _capacity; + size_t _committed; size_t _used; size_t _reserved; public: - MetaspaceSizes() : _capacity(0), _used(0), _reserved(0) {} - MetaspaceSizes(size_t capacity, size_t used, size_t reserved) : - _capacity(capacity), _used(used), _reserved(reserved) {} + MetaspaceSizes() : _committed(0), _used(0), _reserved(0) {} + MetaspaceSizes(size_t committed, size_t used, size_t reserved) : + _committed(committed), _used(used), _reserved(reserved) {} - size_t capacity() const { return _capacity; } + size_t committed() const { return _committed; } size_t used() const { return _used; } size_t reserved() const { return _reserved; } }; @@ -125,18 +126,49 @@ }; class MetaspaceSummary : public StackObj { + size_t _capacity_until_GC; MetaspaceSizes _meta_space; MetaspaceSizes _data_space; MetaspaceSizes _class_space; + MetaspaceChunkFreeListSummary _metaspace_chunk_free_list_summary; + MetaspaceChunkFreeListSummary _class_chunk_free_list_summary; public: - MetaspaceSummary() : _meta_space(), _data_space(), _class_space() {} - MetaspaceSummary(const MetaspaceSizes& meta_space, const MetaspaceSizes& data_space, const MetaspaceSizes& class_space) : - _meta_space(meta_space), _data_space(data_space), _class_space(class_space) { } + MetaspaceSummary() : + _capacity_until_GC(0), + _meta_space(), + _data_space(), + _class_space(), + _metaspace_chunk_free_list_summary(), + _class_chunk_free_list_summary() + {} + MetaspaceSummary(size_t capacity_until_GC, + const MetaspaceSizes& meta_space, + const MetaspaceSizes& data_space, + const MetaspaceSizes& class_space, + const MetaspaceChunkFreeListSummary& metaspace_chunk_free_list_summary, + const MetaspaceChunkFreeListSummary& class_chunk_free_list_summary) : + _capacity_until_GC(capacity_until_GC), + _meta_space(meta_space), + _data_space(data_space), + _class_space(class_space), + _metaspace_chunk_free_list_summary(metaspace_chunk_free_list_summary), + _class_chunk_free_list_summary(class_chunk_free_list_summary) + {} + size_t capacity_until_GC() const { return _capacity_until_GC; } const MetaspaceSizes& meta_space() const { return _meta_space; } const MetaspaceSizes& data_space() const { return _data_space; } const MetaspaceSizes& class_space() const { return _class_space; } + + const MetaspaceChunkFreeListSummary& metaspace_chunk_free_list_summary() const { + return _metaspace_chunk_free_list_summary; + } + + const MetaspaceChunkFreeListSummary& class_chunk_free_list_summary() const { + return _class_chunk_free_list_summary; + } + }; #endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_GCHEAPSUMMARY_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/gcTrace.cpp --- a/src/share/vm/gc_implementation/shared/gcTrace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/gcTrace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -139,11 +139,21 @@ } #endif // INCLUDE_SERVICES -void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary, const MetaspaceSummary& meta_space_summary) const { +void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary) const { assert_set_gc_id(); send_gc_heap_summary_event(when, heap_summary); - send_meta_space_summary_event(when, meta_space_summary); +} + +void GCTracer::report_metaspace_summary(GCWhen::Type when, const MetaspaceSummary& summary) const { + assert_set_gc_id(); + + send_meta_space_summary_event(when, summary); + + send_metaspace_chunk_free_list_summary(when, Metaspace::NonClassType, summary.metaspace_chunk_free_list_summary()); + if (UseCompressedClassPointers) { + send_metaspace_chunk_free_list_summary(when, Metaspace::ClassType, summary.class_chunk_free_list_summary()); + } } void YoungGCTracer::report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/gcTrace.hpp --- a/src/share/vm/gc_implementation/shared/gcTrace.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/gcTrace.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -30,6 +30,7 @@ #include "gc_implementation/shared/gcWhen.hpp" #include "gc_implementation/shared/copyFailedInfo.hpp" #include "memory/allocation.hpp" +#include "memory/metaspace.hpp" #include "memory/referenceType.hpp" #if INCLUDE_ALL_GCS #include "gc_implementation/g1/g1YCTypes.hpp" @@ -41,6 +42,7 @@ class EvacuationInfo; class GCHeapSummary; +class MetaspaceChunkFreeListSummary; class MetaspaceSummary; class PSHeapSummary; class ReferenceProcessorStats; @@ -124,7 +126,8 @@ public: void report_gc_start(GCCause::Cause cause, const Ticks& timestamp); void report_gc_end(const Ticks& timestamp, TimePartitions* time_partitions); - void report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary, const MetaspaceSummary& meta_space_summary) const; + void report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary) const; + void report_metaspace_summary(GCWhen::Type when, const MetaspaceSummary& metaspace_summary) const; void report_gc_reference_stats(const ReferenceProcessorStats& rp) const; void report_object_count_after_gc(BoolObjectClosure* object_filter) NOT_SERVICES_RETURN; bool has_reported_gc_start() const; @@ -138,6 +141,7 @@ void send_garbage_collection_event() const; void send_gc_heap_summary_event(GCWhen::Type when, const GCHeapSummary& heap_summary) const; void send_meta_space_summary_event(GCWhen::Type when, const MetaspaceSummary& meta_space_summary) const; + void send_metaspace_chunk_free_list_summary(GCWhen::Type when, Metaspace::MetadataType mdtype, const MetaspaceChunkFreeListSummary& summary) const; void send_reference_stats_event(ReferenceType type, size_t count) const; void send_phase_events(TimePartitions* time_partitions) const; }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/gcTraceSend.cpp --- a/src/share/vm/gc_implementation/shared/gcTraceSend.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/gcTraceSend.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -64,6 +64,30 @@ } } +void GCTracer::send_metaspace_chunk_free_list_summary(GCWhen::Type when, Metaspace::MetadataType mdtype, + const MetaspaceChunkFreeListSummary& summary) const { + EventMetaspaceChunkFreeListSummary e; + if (e.should_commit()) { + e.set_gcId(_shared_gc_info.id()); + e.set_when(when); + e.set_metadataType(mdtype); + + e.set_specializedChunks(summary.num_specialized_chunks()); + e.set_specializedChunksTotalSize(summary.specialized_chunks_size_in_bytes()); + + e.set_smallChunks(summary.num_small_chunks()); + e.set_smallChunksTotalSize(summary.small_chunks_size_in_bytes()); + + e.set_mediumChunks(summary.num_medium_chunks()); + e.set_mediumChunksTotalSize(summary.medium_chunks_size_in_bytes()); + + e.set_humongousChunks(summary.num_humongous_chunks()); + e.set_humongousChunksTotalSize(summary.humongous_chunks_size_in_bytes()); + + e.commit(); + } +} + void ParallelOldTracer::send_parallel_old_event() const { EventGCParallelOld e(UNTIMED); if (e.should_commit()) { @@ -234,7 +258,7 @@ static TraceStructMetaspaceSizes to_trace_struct(const MetaspaceSizes& sizes) { TraceStructMetaspaceSizes meta_sizes; - meta_sizes.set_capacity(sizes.capacity()); + meta_sizes.set_committed(sizes.committed()); meta_sizes.set_used(sizes.used()); meta_sizes.set_reserved(sizes.reserved()); @@ -246,6 +270,7 @@ if (e.should_commit()) { e.set_gcId(_shared_gc_info.id()); e.set_when((u1) when); + e.set_gcThreshold(meta_space_summary.capacity_until_GC()); e.set_metaspace(to_trace_struct(meta_space_summary.meta_space())); e.set_dataSpace(to_trace_struct(meta_space_summary.data_space())); e.set_classSpace(to_trace_struct(meta_space_summary.class_space())); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/immutableSpace.cpp --- a/src/share/vm/gc_implementation/shared/immutableSpace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/immutableSpace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -66,7 +66,7 @@ void ImmutableSpace::print() const { print_short(); - tty->print_cr(" [%#-6lx,%#-6lx)", bottom(), end()); + tty->print_cr(" [" INTPTR_FORMAT_W(#-6) "," INTPTR_FORMAT_W(#-6) ")", p2i(bottom()), p2i(end())); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/markSweep.cpp --- a/src/share/vm/gc_implementation/shared/markSweep.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/markSweep.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -32,6 +32,8 @@ #include "oops/objArrayKlass.inline.hpp" #include "oops/oop.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + uint MarkSweep::_total_invocations = 0; Stack MarkSweep::_marking_stack; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/markSweep.inline.hpp --- a/src/share/vm/gc_implementation/shared/markSweep.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/markSweep.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -30,10 +30,18 @@ #include "utilities/stack.inline.hpp" #include "utilities/macros.hpp" #if INCLUDE_ALL_GCS +#include "gc_implementation/g1/g1StringDedup.hpp" #include "gc_implementation/parallelScavenge/psParallelCompact.hpp" #endif // INCLUDE_ALL_GCS inline void MarkSweep::mark_object(oop obj) { +#if INCLUDE_ALL_GCS + if (G1StringDedup::is_enabled()) { + // We must enqueue the object before it is marked + // as we otherwise can't read the object's age. + G1StringDedup::enqueue_from_mark(obj); + } +#endif // some marks may contain information we need to preserve so we store them away // and overwrite the mark. We'll restore it at the end of markSweep. markOop mark = obj->mark(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp --- a/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 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 @@ -30,6 +30,8 @@ #include "oops/oop.inline.hpp" #include "runtime/thread.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + MutableNUMASpace::MutableNUMASpace(size_t alignment) : MutableSpace(alignment) { _lgrp_spaces = new (ResourceObj::C_HEAP, mtGC) GrowableArray(0, true); _page_size = os::vm_page_size(); @@ -173,6 +175,26 @@ return lgrp_spaces()->at(i)->space()->capacity_in_bytes(); } +size_t MutableNUMASpace::tlab_used(Thread *thr) const { + // Please see the comments for tlab_capacity(). + guarantee(thr != NULL, "No thread"); + int lgrp_id = thr->lgrp_id(); + if (lgrp_id == -1) { + if (lgrp_spaces()->length() > 0) { + return (used_in_bytes()) / lgrp_spaces()->length(); + } else { + assert(false, "There should be at least one locality group"); + return 0; + } + } + int i = lgrp_spaces()->find(&lgrp_id, LGRPSpace::equals); + if (i == -1) { + return 0; + } + return lgrp_spaces()->at(i)->space()->used_in_bytes(); +} + + size_t MutableNUMASpace::unsafe_max_tlab_alloc(Thread *thr) const { // Please see the comments for tlab_capacity(). guarantee(thr != NULL, "No thread"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/mutableNUMASpace.hpp --- a/src/share/vm/gc_implementation/shared/mutableNUMASpace.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/mutableNUMASpace.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -217,6 +217,7 @@ using MutableSpace::capacity_in_words; virtual size_t capacity_in_words(Thread* thr) const; virtual size_t tlab_capacity(Thread* thr) const; + virtual size_t tlab_used(Thread* thr) const; virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; // Allocation (return NULL if full) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/mutableSpace.cpp --- a/src/share/vm/gc_implementation/shared/mutableSpace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/mutableSpace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -32,6 +32,8 @@ #include "runtime/thread.hpp" #endif // INCLUDE_ALL_GCS +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + MutableSpace::MutableSpace(size_t alignment): ImmutableSpace(), _top(NULL), _alignment(alignment) { assert(MutableSpace::alignment() >= 0 && MutableSpace::alignment() % os::vm_page_size() == 0, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/mutableSpace.hpp --- a/src/share/vm/gc_implementation/shared/mutableSpace.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/mutableSpace.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -124,6 +124,7 @@ virtual size_t used_in_words() const { return pointer_delta(top(), bottom()); } virtual size_t free_in_words() const { return pointer_delta(end(), top()); } virtual size_t tlab_capacity(Thread* thr) const { return capacity_in_bytes(); } + virtual size_t tlab_used(Thread* thr) const { return used_in_bytes(); } virtual size_t unsafe_max_tlab_alloc(Thread* thr) const { return free_in_bytes(); } // Allocation (return NULL if full) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp --- a/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/parGCAllocBuffer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -28,6 +28,8 @@ #include "oops/arrayOop.hpp" #include "oops/oop.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ParGCAllocBuffer::ParGCAllocBuffer(size_t desired_plab_sz_) : _word_sz(desired_plab_sz_), _bottom(NULL), _top(NULL), _end(NULL), _hard_end(NULL), @@ -89,6 +91,10 @@ // scavenge; it clears the sensor accumulators. void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) { assert(ResizePLAB, "Not set"); + + assert(is_object_aligned(max_size()) && min_size() <= max_size(), + "PLAB clipping computation may be incorrect"); + if (_allocated == 0) { assert(_unused == 0, err_msg("Inconsistency in PLAB stats: " diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp --- a/src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -158,7 +158,7 @@ // Fills in the unallocated portion of the buffer with a garbage object. // If "end_of_gc" is TRUE, is after the last use in the GC. IF "retain" // is true, attempt to re-use the unused portion in the next GC. - virtual void retire(bool end_of_gc, bool retain); + void retire(bool end_of_gc, bool retain); void print() PRODUCT_RETURN; }; @@ -181,16 +181,7 @@ _used(0), _desired_plab_sz(desired_plab_sz_), _filter(wt) - { - size_t min_sz = min_size(); - size_t max_sz = max_size(); - size_t aligned_min_sz = align_object_size(min_sz); - size_t aligned_max_sz = align_object_size(max_sz); - assert(min_sz <= aligned_min_sz && max_sz >= aligned_max_sz && - min_sz <= max_sz, - "PLAB clipping computation in adjust_desired_plab_sz()" - " may be incorrect"); - } + { } static const size_t min_size() { return ParGCAllocBuffer::min_size(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/spaceDecorator.cpp --- a/src/share/vm/gc_implementation/shared/spaceDecorator.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/spaceDecorator.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -27,6 +27,8 @@ #include "memory/space.inline.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Catch-all file for utility classes #ifndef PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_implementation/shared/vmGCOperations.cpp --- a/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -103,6 +103,15 @@ assert(((_gc_cause != GCCause::_no_gc) && (_gc_cause != GCCause::_no_cause_specified)), "Illegal GCCause"); + // To be able to handle a GC the VM initialization needs to be completed. + if (!is_init_completed()) { + vm_exit_during_initialization( + err_msg("GC triggered before VM initialization completed. Try increasing " + "NewSize, current value " UINTX_FORMAT "%s.", + byte_size_in_proper_unit(NewSize), + proper_unit_for_byte_size(NewSize))); + } + acquire_pending_list_lock(); // If the GC count has changed someone beat us to the collection // Get the Heap_lock after the pending_list_lock. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_interface/collectedHeap.cpp --- a/src/share/vm/gc_interface/collectedHeap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_interface/collectedHeap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -85,19 +85,25 @@ MetaspaceSummary CollectedHeap::create_metaspace_summary() { const MetaspaceSizes meta_space( - MetaspaceAux::allocated_capacity_bytes(), - MetaspaceAux::allocated_used_bytes(), + MetaspaceAux::committed_bytes(), + MetaspaceAux::used_bytes(), MetaspaceAux::reserved_bytes()); const MetaspaceSizes data_space( - MetaspaceAux::allocated_capacity_bytes(Metaspace::NonClassType), - MetaspaceAux::allocated_used_bytes(Metaspace::NonClassType), + MetaspaceAux::committed_bytes(Metaspace::NonClassType), + MetaspaceAux::used_bytes(Metaspace::NonClassType), MetaspaceAux::reserved_bytes(Metaspace::NonClassType)); const MetaspaceSizes class_space( - MetaspaceAux::allocated_capacity_bytes(Metaspace::ClassType), - MetaspaceAux::allocated_used_bytes(Metaspace::ClassType), + MetaspaceAux::committed_bytes(Metaspace::ClassType), + MetaspaceAux::used_bytes(Metaspace::ClassType), MetaspaceAux::reserved_bytes(Metaspace::ClassType)); - return MetaspaceSummary(meta_space, data_space, class_space); + const MetaspaceChunkFreeListSummary& ms_chunk_free_list_summary = + MetaspaceAux::chunk_free_list_summary(Metaspace::NonClassType); + const MetaspaceChunkFreeListSummary& class_chunk_free_list_summary = + MetaspaceAux::chunk_free_list_summary(Metaspace::ClassType); + + return MetaspaceSummary(MetaspaceGC::capacity_until_GC(), meta_space, data_space, class_space, + ms_chunk_free_list_summary, class_chunk_free_list_summary); } void CollectedHeap::print_heap_before_gc() { @@ -128,8 +134,10 @@ void CollectedHeap::trace_heap(GCWhen::Type when, GCTracer* gc_tracer) { const GCHeapSummary& heap_summary = create_heap_summary(); + gc_tracer->report_gc_heap_summary(when, heap_summary); + const MetaspaceSummary& metaspace_summary = create_metaspace_summary(); - gc_tracer->report_gc_heap_summary(when, heap_summary, metaspace_summary); + gc_tracer->report_metaspace_summary(when, metaspace_summary); } void CollectedHeap::trace_heap_before_gc(GCTracer* gc_tracer) { @@ -323,6 +331,21 @@ assert(thread->deferred_card_mark().is_empty(), "invariant"); } +size_t CollectedHeap::max_tlab_size() const { + // TLABs can't be bigger than we can fill with a int[Integer.MAX_VALUE]. + // This restriction could be removed by enabling filling with multiple arrays. + // If we compute that the reasonable way as + // header_size + ((sizeof(jint) * max_jint) / HeapWordSize) + // we'll overflow on the multiply, so we do the divide first. + // We actually lose a little by dividing first, + // but that just makes the TLAB somewhat smaller than the biggest array, + // which is fine, since we'll be able to fill that. + size_t max_int_size = typeArrayOopDesc::header_size(T_INT) + + sizeof(jint) * + ((juint) max_jint / (size_t) HeapWordSize); + return align_size_down(max_int_size, MinObjAlignment); +} + // Helper for ReduceInitialCardMarks. For performance, // compiled code may elide card-marks for initializing stores // to a newly allocated object along the fast-path. We @@ -567,36 +590,6 @@ } } -oop CollectedHeap::Class_obj_allocate(KlassHandle klass, int size, KlassHandle real_klass, TRAPS) { - debug_only(check_for_valid_allocation_state()); - assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); - assert(size >= 0, "int won't convert to size_t"); - HeapWord* obj; - assert(ScavengeRootsInCode > 0, "must be"); - obj = common_mem_allocate_init(real_klass, size, CHECK_NULL); - post_allocation_setup_common(klass, obj); - assert(Universe::is_bootstrapping() || - !((oop)obj)->is_array(), "must not be an array"); - NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); - oop mirror = (oop)obj; - - java_lang_Class::set_oop_size(mirror, size); - - // Setup indirections - if (!real_klass.is_null()) { - java_lang_Class::set_klass(mirror, real_klass()); - real_klass->set_java_mirror(mirror); - } - - InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); - assert(size == mk->instance_size(real_klass), "should have been set"); - - // notify jvmti and dtrace - post_allocation_notify(klass, (oop)obj); - - return mirror; -} - /////////////// Unit tests /////////////// #ifndef PRODUCT @@ -614,12 +607,12 @@ assert(heap_start >= ((uintptr_t)NULL + epsilon), "sanity"); void* before_heap = (void*)(heap_start - epsilon); assert(!heap->is_in(before_heap), - err_msg("before_heap: " PTR_FORMAT " is unexpectedly in the heap", before_heap)); + err_msg("before_heap: " PTR_FORMAT " is unexpectedly in the heap", p2i(before_heap))); // Test that a pointer to after the heap end is reported as outside the heap. assert(heap_end <= ((uintptr_t)-1 - epsilon), "sanity"); void* after_heap = (void*)(heap_end + epsilon); assert(!heap->is_in(after_heap), - err_msg("after_heap: " PTR_FORMAT " is unexpectedly in the heap", after_heap)); + err_msg("after_heap: " PTR_FORMAT " is unexpectedly in the heap", p2i(after_heap))); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_interface/collectedHeap.hpp --- a/src/share/vm/gc_interface/collectedHeap.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_interface/collectedHeap.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -152,7 +152,7 @@ inline static void post_allocation_setup_no_klass_install(KlassHandle klass, HeapWord* objPtr); - inline static void post_allocation_setup_obj(KlassHandle klass, HeapWord* obj); + inline static void post_allocation_setup_obj(KlassHandle klass, HeapWord* obj, int size); inline static void post_allocation_setup_array(KlassHandle klass, HeapWord* obj, int length); @@ -209,6 +209,9 @@ // This is the correct place to place such initialization methods. virtual void post_initialize() = 0; + // Stop any onging concurrent work and prepare for exit. + virtual void stop() {} + MemRegion reserved_region() const { return _reserved; } address base() const { return (address)reserved_region().start(); } @@ -313,9 +316,6 @@ // May be overridden to set additional parallelism. virtual void set_par_threads(uint t) { _n_par_threads = t; }; - // Allocate and initialize instances of Class - static oop Class_obj_allocate(KlassHandle klass, int size, KlassHandle real_klass, TRAPS); - // General obj/array allocation facilities. inline static oop obj_allocate(KlassHandle klass, int size, TRAPS); inline static oop array_allocate(KlassHandle klass, int size, int length, TRAPS); @@ -404,14 +404,16 @@ // the following methods: // Returns "true" iff the heap supports thread-local allocation buffers. // The default is "no". - virtual bool supports_tlab_allocation() const { - return false; - } + virtual bool supports_tlab_allocation() const = 0; + // The amount of space available for thread-local allocation buffers. - virtual size_t tlab_capacity(Thread *thr) const { - guarantee(false, "thread-local allocation buffers not supported"); - return 0; - } + virtual size_t tlab_capacity(Thread *thr) const = 0; + + // The amount of used space for thread-local allocation buffers for the given thread. + virtual size_t tlab_used(Thread *thr) const = 0; + + virtual size_t max_tlab_size() const; + // An estimate of the maximum allocation that could be performed // for thread-local allocation buffers without triggering any // collection or expansion activity. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/gc_interface/collectedHeap.inline.hpp --- a/src/share/vm/gc_interface/collectedHeap.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/gc_interface/collectedHeap.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -70,7 +70,7 @@ } // Support for jvmti and dtrace -inline void post_allocation_notify(KlassHandle klass, oop obj) { +inline void post_allocation_notify(KlassHandle klass, oop obj, int size) { // support low memory notifications (no-op if not enabled) LowMemoryDetector::detect_low_memory_for_collected_pools(); @@ -80,18 +80,19 @@ if (DTraceAllocProbes) { // support for Dtrace object alloc event (no-op most of the time) if (klass() != NULL && klass()->name() != NULL) { - SharedRuntime::dtrace_object_alloc(obj); + SharedRuntime::dtrace_object_alloc(obj, size); } } } void CollectedHeap::post_allocation_setup_obj(KlassHandle klass, - HeapWord* obj) { + HeapWord* obj, + int size) { post_allocation_setup_common(klass, obj); assert(Universe::is_bootstrapping() || !((oop)obj)->is_array(), "must not be an array"); // notify jvmti and dtrace - post_allocation_notify(klass, (oop)obj); + post_allocation_notify(klass, (oop)obj, size); } void CollectedHeap::post_allocation_setup_array(KlassHandle klass, @@ -103,9 +104,10 @@ assert(length >= 0, "length should be non-negative"); ((arrayOop)obj)->set_length(length); post_allocation_setup_common(klass, obj); - assert(((oop)obj)->is_array(), "must be an array"); + oop new_obj = (oop)obj; + assert(new_obj->is_array(), "must be an array"); // notify jvmti and dtrace (must be after length is set for dtrace) - post_allocation_notify(klass, (oop)obj); + post_allocation_notify(klass, new_obj, new_obj->size()); } HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) { @@ -199,7 +201,7 @@ assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL); - post_allocation_setup_obj(klass, obj); + post_allocation_setup_obj(klass, obj, size); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/abstractInterpreter.hpp --- a/src/share/vm/interpreter/abstractInterpreter.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/abstractInterpreter.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -42,8 +42,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 // This file contains the platform-independent parts @@ -178,30 +181,16 @@ // Deoptimization should reexecute this bytecode static bool bytecode_should_reexecute(Bytecodes::Code code); - // share implementation of size_activation and layout_activation: - static int size_activation(Method* method, + // deoptimization support + static int size_activation(int max_stack, int temps, - int popframe_args, + int extra_args, int monitors, - int caller_actual_parameters, int callee_params, int callee_locals, - bool is_top_frame, - bool is_bottom_frame) { - return layout_activation(method, - temps, - popframe_args, - monitors, - caller_actual_parameters, - callee_params, - callee_locals, - (frame*)NULL, - (frame*)NULL, - is_top_frame, - is_bottom_frame); - } + bool is_top_frame); - static int layout_activation(Method* method, + static void layout_activation(Method* method, int temps, int popframe_args, int monitors, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/bytecodeInterpreter.cpp --- a/src/share/vm/interpreter/bytecodeInterpreter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/bytecodeInterpreter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -28,14 +28,16 @@ #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/methodCounters.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" @@ -66,6 +68,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,19 +143,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 @@ -197,6 +203,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; \ } \ } \ @@ -332,12 +342,30 @@ if (UseLoopCounter) { \ bool do_OSR = UseOnStackReplacement; \ mcs->backedge_counter()->increment(); \ - if (do_OSR) do_OSR = mcs->backedge_counter()->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 \ + && mcs->invocation_counter()->reached_InvocationLimit(mcs->backedge_counter()); \ + } \ 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()); \ @@ -345,7 +373,6 @@ } \ } \ } /* UseCompiler ... */ \ - mcs->invocation_counter()->increment(); \ SAFEPOINT; \ } @@ -378,17 +405,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 @@ -410,22 +441,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; \ } /* @@ -502,8 +541,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; @@ -511,12 +548,9 @@ register ConstantPoolCache* 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 @@ -622,16 +656,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; @@ -646,15 +684,12 @@ METHOD->increment_interpreter_invocation_count(THREAD); } mcs->invocation_counter()->increment(); - if (mcs->invocation_counter()->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 (mcs->invocation_counter()->reached_InvocationLimit(mcs->backedge_counter())) { + 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; } @@ -676,117 +711,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()->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()->prototype_header() | (intptr_t) THREAD; - if (Atomic::cmpxchg_ptr((intptr_t)THREAD | (intptr_t) rcvr->klass()->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()->prototype_header() | (intptr_t) THREAD; - if (Atomic::cmpxchg_ptr(rcvr->klass()->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()->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()->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()->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(); @@ -808,9 +825,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; } @@ -836,10 +858,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; } @@ -847,6 +878,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; } @@ -869,6 +905,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: { @@ -881,15 +922,84 @@ 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()->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()->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()->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); @@ -1050,6 +1160,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)); @@ -1093,10 +1208,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); } } @@ -1177,7 +1295,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)), \ @@ -1189,7 +1307,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) */ \ @@ -1402,17 +1520,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; \ @@ -1421,9 +1545,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; \ @@ -1431,9 +1558,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; \ @@ -1441,9 +1571,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; \ @@ -1466,30 +1599,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); @@ -1574,7 +1719,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 */ @@ -1600,8 +1745,11 @@ 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): @@ -1645,21 +1793,24 @@ // arrObj, index are set if (rhsObject != NULL) { /* Check assignability of rhsObject into arrObj */ - Klass* rhsKlassOop = rhsObject->klass(); // EBX (subclass) - Klass* elemKlassOop = ObjArrayKlass::cast(arrObj->klass())->element_klass(); // superklass EAX + Klass* rhsKlass = rhsObject->klass(); // EBX (subclass) + Klass* elemKlass = ObjArrayKlass::cast(arrObj->klass())->element_klass(); // superklass EAX // // Check for compatibilty. This check must not GC!! // Seems way more expensive now that we must dispatch // - if (rhsKlassOop != elemKlassOop && !rhsKlassOop->is_subtype_of(elemKlassOop)) { // ebx->is... - VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), ""); + if (rhsKlass != elemKlass && !rhsKlass->is_subtype_of(elemKlass)) { // ebx->is... + // Decrement counter if subtype check failed. + BI_PROFILE_SUBTYPECHECK_FAILED(rhsKlass); + VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), "", note_arrayCheck_trap); } + // Profile checkcast with null_seen and receiver. + BI_PROFILE_UPDATE_CHECKCAST(/*null_seen=*/false, rhsKlass); + } 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); + ((objArrayOop) arrObj)->obj_at_put(index, rhsObject); UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); } CASE(_bastore): @@ -1700,14 +1851,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()->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()->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()->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); @@ -1729,12 +1953,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); @@ -1807,6 +2034,9 @@ TosState tos_type = cache->flag_state(); int field_offset = cache->f2_as_index(); if (cache->is_volatile()) { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + OrderAccess::fence(); + } if (tos_type == atos) { VERIFY_OOP(obj->obj_field_acquire(field_offset)); SET_STACK_OBJECT(obj->obj_field_acquire(field_offset), -1); @@ -1923,7 +2153,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 == ltos) { @@ -1944,7 +2173,6 @@ } 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); } else if (tos_type == btos) { obj->byte_field_put(field_offset, STACK_INT(-1)); } else if (tos_type == ltos) { @@ -1981,10 +2209,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()) { @@ -1994,6 +2226,7 @@ result = (oop) compare_to; } } +#endif if (result != NULL) { // Initialize object (if nonzero size and need) and then the header if (need_zero ) { @@ -2010,6 +2243,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); } @@ -2018,6 +2254,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); @@ -2027,6 +2266,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); @@ -2041,6 +2283,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)); @@ -2049,61 +2294,63 @@ 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); } Klass* klassOf = (Klass*) METHOD->constants()->slot_at(index).get_klass(); - Klass* objKlassOop = STACK_OBJECT(-1)->klass(); //ebx + Klass* objKlass = 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->is_subtype_of(klassOf)) { + if (objKlass != klassOf && !objKlass->is_subtype_of(klassOf)) { + // Decrement counter at checkcast. + BI_PROFILE_SUBTYPECHECK_FAILED(objKlass); ResourceMark rm(THREAD); - const char* objName = objKlassOop->external_name(); + const char* objName = objKlass->external_name(); const char* klassName = 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, objKlass); } 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); } Klass* klassOf = (Klass*) METHOD->constants()->slot_at(index).get_klass(); - Klass* objKlassOop = STACK_OBJECT(-1)->klass(); + Klass* objKlass = 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->is_subtype_of(klassOf)) { + if ( objKlass == klassOf || objKlass->is_subtype_of(klassOf)) { SET_STACK_INT(1, -1); } else { SET_STACK_INT(0, -1); + // Decrement counter at checkcast. + BI_PROFILE_SUBTYPECHECK_FAILED(objKlass); } + // Profile instanceof with null_seen and receiver. + BI_PROFILE_UPDATE_INSTANCEOF(/*null_seen=*/false, objKlass); } UPDATE_PC_AND_CONTINUE(3); @@ -2246,6 +2493,9 @@ 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... } @@ -2278,6 +2528,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... } @@ -2305,14 +2558,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(); + // Same comments as invokevirtual apply here. + oop rcvr = STACK_OBJECT(-parms); + VERIFY_OOP(rcvr); + InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass(); callee = (Method*) 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()); @@ -2343,15 +2600,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 @@ -2383,8 +2643,11 @@ Method* 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: @@ -2393,8 +2656,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(); + oop rcvr = STACK_OBJECT(-parms); + VERIFY_OOP(rcvr); + InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass(); /* Executing this code in java.lang.String: public String(char value[]) { @@ -2412,12 +2676,17 @@ */ callee = (Method*) 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); @@ -2439,6 +2708,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); @@ -2469,6 +2741,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); @@ -2485,6 +2759,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); @@ -2494,6 +2770,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,23 +2846,26 @@ if (TraceExceptions) { ttyLocker ttyl; ResourceMark rm; - tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), except_oop()); + tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), (void*)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) { ttyLocker ttyl; ResourceMark rm; - tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), except_oop()); + tty->print_cr("Exception <%s> (" INTPTR_FORMAT ")", except_oop->print_value_string(), (void*)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 @@ -2591,32 +2873,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); @@ -2677,15 +3014,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)); + } } } } @@ -2730,27 +3070,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(), "", 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(); + } } } } @@ -2758,6 +3112,8 @@ } } } + // Clear the do_not_unlock flag now. + THREAD->clr_do_not_unlock(); // // Notify jvmti/jvmdi @@ -2802,15 +3158,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); } @@ -2829,13 +3184,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: */ @@ -2883,7 +3237,7 @@ } oop BytecodeInterpreter::stack_object(intptr_t *tos, int offset) { - return (oop)tos [Interpreter::expr_index_at(-offset)]; + return cast_to_oop(tos [Interpreter::expr_index_at(-offset)]); } jdouble BytecodeInterpreter::stack_double(intptr_t *tos, int offset) { @@ -2952,7 +3306,7 @@ return (jfloat)locals[Interpreter::local_index_at(-offset)]; } oop BytecodeInterpreter::locals_object(intptr_t* locals, int offset) { - return (oop)locals[Interpreter::local_index_at(-offset)]; + return cast_to_oop(locals[Interpreter::local_index_at(-offset)]); } jdouble BytecodeInterpreter::locals_double(intptr_t* locals, int offset) { return ((VMJavaVal64*)&locals[Interpreter::local_index_at(-(offset+1))])->d; @@ -3110,9 +3464,8 @@ 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("native_mirror: " INTPTR_FORMAT, (void*) this->_oop_temp); tty->print_cr("stack_base: " INTPTR_FORMAT, (uintptr_t) this->_stack_base); tty->print_cr("stack_limit: " INTPTR_FORMAT, (uintptr_t) this->_stack_limit); tty->print_cr("monitor_base: " INTPTR_FORMAT, (uintptr_t) this->_monitor_base); @@ -3129,9 +3482,9 @@ } extern "C" { - void PI(uintptr_t arg) { - ((BytecodeInterpreter*)arg)->print(); - } + void PI(uintptr_t arg) { + ((BytecodeInterpreter*)arg)->print(); + } } #endif // PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/bytecodeInterpreter.hpp --- a/src/share/vm/interpreter/bytecodeInterpreter.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/bytecodeInterpreter.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -66,27 +66,26 @@ typedef class BytecodeInterpreter* interpreterState; struct call_message { - class Method* _callee; /* method to call during call_method request */ - address _callee_entry_point; /* address to jump to for call_method request */ - int _bcp_advance; /* size of the invoke bytecode operation */ + class Method* _callee; // method to call during call_method request + address _callee_entry_point; // address to jump to for call_method request + int _bcp_advance; // size of the invoke bytecode operation }; struct osr_message { - address _osr_buf; /* the osr buffer */ - address _osr_entry; /* the entry to the osr method */ + address _osr_buf; // the osr buffer + address _osr_entry; // the entry to the osr method }; struct osr_result { - nmethod* nm; /* osr nmethod */ - address return_addr; /* osr blob return address */ + nmethod* nm; // osr nmethod + address return_addr; // osr blob return address }; // Result returned to frame manager union frame_manager_message { - call_message _to_call; /* describes callee */ - Bytecodes::Code _return_kind; /* i_return, a_return, ... */ - osr_message _osr; /* describes the osr */ - osr_result _osr_result; /* result of OSR request */ + call_message _to_call; // describes callee + osr_message _osr; // describes the osr + osr_result _osr_result; // result of OSR request }; class BytecodeInterpreter : StackObj { @@ -115,7 +114,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: @@ -216,8 +216,6 @@ inline int bcp_advance() { return _result._to_call._bcp_advance; } inline void set_bcp_advance(int count) { _result._to_call._bcp_advance = count; } -inline void set_return_kind(Bytecodes::Code kind) { _result._return_kind = kind; } - inline interpreterState prev() { return _prev_link; } inline intptr_t* stack() { return _stack; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/interpreter/bytecodeInterpreterProfiling.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,308 @@ +/* + * 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; \ + MethodData *md = istate->method()->method_data(); \ + tty->cr(); \ + tty->print("method data at mdx " PTR_FORMAT "(0) for", \ + md->data_layout_at(md->bci_to_di(0))); \ + istate->method()->print_short_name(tty); \ + tty->cr(); \ + if (md != NULL) { \ + md->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. */ \ + MethodData *md = istate->method()->method_data(); \ + if (md == NULL) { \ + MethodCounters* mcs; \ + GET_METHOD_COUNTERS(mcs); \ + /* The profiling method data doesn't exist for this method, */ \ + /* create it if the counters have overflowed. */ \ + if (mcs->invocation_counter() \ + ->reached_ProfileLimit(mcs->backedge_counter())) { \ + /* Must use CALL_VM, because an async exception may be pending. */ \ + CALL_VM((InterpreterRuntime::profile_method(THREAD)), \ + exception_handler); \ + md = istate->method()->method_data(); \ + if (md != NULL) { \ + if (TraceProfileInterpreter) { \ + BI_PROFILE_PRINT_METHOD_DATA(); \ + } \ + Method *m = istate->method(); \ + int bci = m->bci_from(pc); \ + jint di = md->bci_to_di(bci); \ + SET_MDX(md->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(md->data_layout_at(md->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() \ + { \ + MethodData *md = istate->method()->method_data(); \ + address mdx = (address) MDX(); \ + address mdx2 = (address) md->data_layout_at(md->bci_to_di(BCI())); \ + guarantee(md != 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) { \ + MethodData *md = istate->method()->method_data(); \ + SET_MDX(md->data_layout_at(md->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(); \ + MethodData *md = 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(md, 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 Klass. +#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 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/bytecodeTracer.cpp --- a/src/share/vm/interpreter/bytecodeTracer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/bytecodeTracer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -215,7 +215,7 @@ st->print_cr(" %s", buf); } } else { - st->print_cr(" " PTR_FORMAT, (void *)value); + st->print_cr(" " PTR_FORMAT, p2i((void *)value)); } } @@ -284,7 +284,7 @@ if (i >= 0 && i < climit) { cp_index = cache->entry_at(i)->constant_pool_index(); } else { - st->print_cr(" not in CP[*]?", i); + st->print_cr("%d not in CP[*]?", i); return false; } return true; @@ -299,7 +299,7 @@ cp_index = constants->object_to_cp_index(i); return true; } else { - st->print_cr(" not in OBJ[*]?", i); + st->print_cr("%d not in OBJ[*]?", i); return false; } } @@ -323,7 +323,7 @@ if (tag.is_int()) { st->print_cr(" " INT32_FORMAT, constants->int_at(i)); } else if (tag.is_long()) { - st->print_cr(" " INT64_FORMAT, constants->long_at(i)); + st->print_cr(" " INT64_FORMAT, (int64_t)(constants->long_at(i))); } else if (tag.is_float()) { st->print_cr(" %f", constants->float_at(i)); } else if (tag.is_double()) { @@ -342,7 +342,7 @@ } else if (tag.is_method_handle()) { int kind = constants->method_handle_ref_kind_at(i); int i2 = constants->method_handle_index_at(i); - st->print(" ", kind, i2); + st->print(" ", kind, i2); print_field_or_method(-i, i2, st); } else { st->print_cr(" bad tag=%d at %d", tag.value(), i); @@ -391,6 +391,7 @@ } +PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL void BytecodePrinter::print_attributes(int bci, outputStream* st) { // Show attributes of pre-rewritten codes Bytecodes::Code code = Bytecodes::java_code(raw_code()); @@ -517,7 +518,10 @@ int idx = ll - lo; const char *format = first ? " %d:" INT32_FORMAT " (delta: %d)" : ", %d:" INT32_FORMAT " (delta: %d)"; +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL st->print(format, ll, dest[idx], dest[idx]-bci); +PRAGMA_DIAG_POP } st->cr(); } @@ -537,7 +541,10 @@ for (int ll = 0; ll < len; ll++, first = false) { const char *format = first ? " " INT32_FORMAT ":" INT32_FORMAT : ", " INT32_FORMAT ":" INT32_FORMAT ; +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL st->print(format, key[ll], dest[ll]); +PRAGMA_DIAG_POP } st->cr(); } @@ -596,7 +603,7 @@ if (data != NULL) { st->print(" %d", mdo->dp_to_di(data->dp())); st->fill_to(6); - data->print_data_on(st); + data->print_data_on(st, mdo); } } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/interpreter.cpp --- a/src/share/vm/interpreter/interpreter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/interpreter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -72,7 +72,7 @@ if (description() != NULL) st->print("%s ", description()); if (bytecode() >= 0 ) st->print("%d %s ", bytecode(), Bytecodes::name(bytecode())); st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", - code_begin(), code_end(), code_size()); + p2i(code_begin()), p2i(code_end()), code_size()); if (PrintInterpreter) { st->cr(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/interpreterRuntime.cpp --- a/src/share/vm/interpreter/interpreterRuntime.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -75,6 +75,8 @@ #include "opto/runtime.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + class UnlockFlagSaver { private: JavaThread* _thread; @@ -241,18 +243,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()) { MethodData* trap_mdo = trap_method->method_data(); if (trap_mdo == NULL) { Method::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 = trap_method->method_data(); @@ -261,12 +260,42 @@ if (trap_mdo != 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, Method *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, Method *method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_null_check, method, trap_bci); } +void InterpreterRuntime::note_div0Check_trap(JavaThread* thread, Method *method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_div0_check, method, trap_bci); } +void InterpreterRuntime::note_rangeCheck_trap(JavaThread* thread, Method *method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_range_check, method, trap_bci); } +void InterpreterRuntime::note_classCheck_trap(JavaThread* thread, Method *method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_class_check, method, trap_bci); } +void InterpreterRuntime::note_arrayCheck_trap(JavaThread* thread, Method *method, int trap_bci) + { if (ProfileTraps) note_trap(thread, Deoptimization::Reason_array_check, method, trap_bci); } +#endif // CC_INTERP + + static Handle get_preinitialized_exception(Klass* k, TRAPS) { // get klass InstanceKlass* klass = InstanceKlass::cast(k); @@ -437,7 +466,7 @@ #ifdef GRAAL if (h_method->method_data() != NULL) { ResourceMark rm(thread); - ProfileData* pdata = h_method->method_data()->allocate_bci_to_data(current_bci); + ProfileData* pdata = h_method->method_data()->allocate_bci_to_data(current_bci, h_method()); if (pdata != NULL && pdata->is_BitData()) { BitData* bit_data = (BitData*) pdata; bit_data->set_exception_seen(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/interpreterRuntime.hpp --- a/src/share/vm/interpreter/interpreterRuntime.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/interpreterRuntime.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -66,9 +66,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, Method *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: @@ -100,6 +106,17 @@ #endif static void throw_pending_exception(JavaThread* thread); +#ifdef CC_INTERP + // Profile traps in C++ interpreter. + static void note_nullCheck_trap (JavaThread* thread, Method *method, int trap_bci); + static void note_div0Check_trap (JavaThread* thread, Method *method, int trap_bci); + static void note_rangeCheck_trap(JavaThread* thread, Method *method, int trap_bci); + static void note_classCheck_trap(JavaThread* thread, Method *method, int trap_bci); + static void note_arrayCheck_trap(JavaThread* thread, Method *method, int trap_bci); + // A dummy for makros that shall not profile traps. + static void note_no_trap(JavaThread* thread, Method *method, int trap_bci) {} +#endif // CC_INTERP + // Statics & fields static void resolve_get_put(JavaThread* thread, Bytecodes::Code bytecode); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/invocationCounter.hpp --- a/src/share/vm/interpreter/invocationCounter.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/invocationCounter.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -99,16 +99,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/linkResolver.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -243,7 +243,8 @@ // Look up method in klasses, including static methods // Then look up local default methods void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, bool checkpolymorphism, bool in_imethod_resolve, TRAPS) { - Method* result_oop = klass->uncached_lookup_method(name, signature); + // Ignore overpasses so statics can be found during resolution + Method* result_oop = klass->uncached_lookup_method(name, signature, Klass::skip_overpass); // JDK 8, JVMS 5.4.3.4: Interface method resolution should // ignore static and non-public methods of java.lang.Object, @@ -256,6 +257,12 @@ result_oop = NULL; } + // Before considering default methods, check for an overpass in the + // current class if a method has not been found. + if (result_oop == NULL) { + result_oop = InstanceKlass::cast(klass())->find_method(name, signature); + } + if (result_oop == NULL) { Array* default_methods = InstanceKlass::cast(klass())->default_methods(); if (default_methods != NULL) { @@ -276,11 +283,11 @@ // returns first instance method // Looks up method in classes, then looks up local default methods void LinkResolver::lookup_instance_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS) { - Method* result_oop = klass->uncached_lookup_method(name, signature); + Method* result_oop = klass->uncached_lookup_method(name, signature, Klass::normal); result = methodHandle(THREAD, result_oop); while (!result.is_null() && result->is_static() && result->method_holder()->super() != NULL) { KlassHandle super_klass = KlassHandle(THREAD, result->method_holder()->super()); - result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature)); + result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature, Klass::normal)); } if (result.is_null()) { @@ -302,7 +309,7 @@ // First check in default method array if (!resolved_method->is_abstract() && (InstanceKlass::cast(klass())->default_methods() != NULL)) { - int index = InstanceKlass::find_method_index(InstanceKlass::cast(klass())->default_methods(), name, signature); + int index = InstanceKlass::find_method_index(InstanceKlass::cast(klass())->default_methods(), name, signature, false); if (index >= 0 ) { vtable_index = InstanceKlass::cast(klass())->default_vtable_indices()->at(index); } @@ -322,7 +329,7 @@ // Specify 'true' in order to skip default methods when searching the // interfaces. Function lookup_method_in_klasses() already looked for // the method in the default methods table. - result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name, signature, true)); + result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name, signature, Klass::skip_defaults)); } void LinkResolver::lookup_polymorphic_method(methodHandle& result, @@ -564,16 +571,7 @@ } } - // 5. check if method is concrete - if (resolved_method->is_abstract() && !resolved_klass->is_abstract()) { - ResourceMark rm(THREAD); - THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), - Method::name_and_sig_as_C_string(resolved_klass(), - method_name, - method_signature)); - } - - // 6. access checks, access checking may be turned off when calling from within the VM. + // 5. access checks, access checking may be turned off when calling from within the VM. if (check_access) { assert(current_klass.not_null() , "current_klass should not be null"); @@ -649,16 +647,6 @@ } } - if (nostatics && resolved_method->is_static()) { - ResourceMark rm(THREAD); - char buf[200]; - jio_snprintf(buf, sizeof(buf), "Expected instance not static method %s", Method::name_and_sig_as_C_string(resolved_klass(), - resolved_method->name(), - resolved_method->signature())); - THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); - } - - if (check_access) { // JDK8 adds non-public interface methods, and accessability check requirement assert(current_klass.not_null() , "current_klass should not be null"); @@ -702,6 +690,15 @@ } } + if (nostatics && resolved_method->is_static()) { + ResourceMark rm(THREAD); + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Expected instance not static method %s", + Method::name_and_sig_as_C_string(resolved_klass(), + resolved_method->name(), resolved_method->signature())); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + if (TraceItables && Verbose) { ResourceMark rm(THREAD); tty->print("invokeinterface resolved method: caller-class:%s, compile-time-class:%s, method:%s, method_holder:%s, access_flags: ", @@ -1637,7 +1634,7 @@ THREAD); if (HAS_PENDING_EXCEPTION) { if (TraceMethodHandles) { - tty->print_cr("invokedynamic throws BSME for "INTPTR_FORMAT, (void *)PENDING_EXCEPTION); + tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); PENDING_EXCEPTION->print(); } if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/oopMapCache.cpp --- a/src/share/vm/interpreter/oopMapCache.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/oopMapCache.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -31,6 +31,8 @@ #include "runtime/handles.inline.hpp" #include "runtime/signature.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + class OopMapCacheEntry: private InterpreterOopMap { friend class InterpreterOopMap; friend class OopMapForCacheEntry; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/rewriter.cpp --- a/src/share/vm/interpreter/rewriter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/rewriter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -250,8 +250,8 @@ // We will reverse the bytecode rewriting _after_ adjusting them. // Adjust the cache index by offset to the invokedynamic entries in the // cpCache plus the delta if the invokedynamic bytecodes were adjusted. - cache_index = cp_cache_delta() + _first_iteration_cp_cache_limit; - int cp_index = invokedynamic_cp_cache_entry_pool_index(cache_index); + int adjustment = cp_cache_delta() + _first_iteration_cp_cache_limit; + int cp_index = invokedynamic_cp_cache_entry_pool_index(cache_index - adjustment); assert(_pool->tag_at(cp_index).is_invoke_dynamic(), "wrong index"); // zero out 4 bytes Bytes::put_Java_u4(p, 0); @@ -453,18 +453,7 @@ return method; } -void Rewriter::rewrite(instanceKlassHandle klass, TRAPS) { - ResourceMark rm(THREAD); - Rewriter rw(klass, klass->constants(), klass->methods(), CHECK); - // (That's all, folks.) -} - - -Rewriter::Rewriter(instanceKlassHandle klass, constantPoolHandle cpool, Array* methods, TRAPS) - : _klass(klass), - _pool(cpool), - _methods(methods) -{ +void Rewriter::rewrite_bytecodes(TRAPS) { assert(_pool->cache() == NULL, "constant pool cache must not be set yet"); // determine index maps for Method* rewriting @@ -508,6 +497,29 @@ // May have to fix invokedynamic bytecodes if invokestatic/InterfaceMethodref // entries had to be added. patch_invokedynamic_bytecodes(); +} + +void Rewriter::rewrite(instanceKlassHandle klass, TRAPS) { + ResourceMark rm(THREAD); + Rewriter rw(klass, klass->constants(), klass->methods(), CHECK); + // (That's all, folks.) +} + + +Rewriter::Rewriter(instanceKlassHandle klass, constantPoolHandle cpool, Array* methods, TRAPS) + : _klass(klass), + _pool(cpool), + _methods(methods) +{ + + // Rewrite bytecodes - exception here exits. + rewrite_bytecodes(CHECK); + + // Stress restoring bytecodes + if (StressRewriter) { + restore_bytecodes(); + rewrite_bytecodes(CHECK); + } // allocate constant pool cache, now that we've seen all the bytecodes make_constant_pool_cache(THREAD); @@ -523,6 +535,7 @@ // so methods with jsrs in custom class lists in aren't attempted to be // rewritten in the RO section of the shared archive. // Relocated bytecodes don't have to be restored, only the cp cache entries + int len = _methods->length(); for (int i = len-1; i >= 0; i--) { methodHandle m(THREAD, _methods->at(i)); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/rewriter.hpp --- a/src/share/vm/interpreter/rewriter.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/rewriter.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -199,6 +199,9 @@ void patch_invokedynamic_bytecodes(); + // Do all the work. + void rewrite_bytecodes(TRAPS); + // Revert bytecodes in case of an exception. void restore_bytecodes(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/templateInterpreter.cpp --- a/src/share/vm/interpreter/templateInterpreter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/templateInterpreter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -104,7 +104,7 @@ tty->print("["); for (int i = 0; i < number_of_states; i++) { if (i > 0) tty->print(", "); - tty->print(INTPTR_FORMAT, _entry[i]); + tty->print(INTPTR_FORMAT, p2i(_entry[i])); } tty->print("]"); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/interpreter/templateTable.hpp --- a/src/share/vm/interpreter/templateTable.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/interpreter/templateTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -370,8 +373,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/libadt/dict.cpp --- a/src/share/vm/libadt/dict.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/libadt/dict.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -38,6 +38,8 @@ #include +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // The iostream is not needed and it gets confused for gcc by the // define of bool. // diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/libadt/port.hpp --- a/src/share/vm/libadt/port.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/libadt/port.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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++ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/libadt/set.cpp --- a/src/share/vm/libadt/set.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/libadt/set.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -116,7 +116,7 @@ void Set::print() const { char *printable_set = setstr(); - tty->print_cr(printable_set); + tty->print_cr("%s", printable_set); FreeHeap(printable_set); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/allocation.cpp --- a/src/share/vm/memory/allocation.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/allocation.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -45,6 +45,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 @@ -71,13 +74,12 @@ return MetaspaceShared::is_in_shared_space(this); } - bool MetaspaceObj::is_metaspace_object() const { return Metaspace::contains((void*)this); } void MetaspaceObj::print_address_on(outputStream* st) const { - st->print(" {"INTPTR_FORMAT"}", this); + st->print(" {" INTPTR_FORMAT "}", p2i(this)); } void* ResourceObj::operator new(size_t size, allocation_type type, MEMFLAGS flags) throw() { @@ -140,7 +142,7 @@ void ResourceObj::set_allocation_type(address res, allocation_type type) { // Set allocation type in the resource object uintptr_t allocation = (uintptr_t)res; - assert((allocation & allocation_mask) == 0, "address should be aligned to 4 bytes at least"); + assert((allocation & allocation_mask) == 0, err_msg("address should be aligned to 4 bytes at least: " INTPTR_FORMAT, p2i(res))); assert(type <= allocation_mask, "incorrect allocation type"); ResourceObj* resobj = (ResourceObj *)res; resobj->_allocation_t[0] = ~(allocation + type); @@ -177,7 +179,7 @@ // Operator new() was called and type was set. assert(!allocated_on_stack(), err_msg("not embedded or stack, this(" PTR_FORMAT ") type %d a[0]=(" PTR_FORMAT ") a[1]=(" PTR_FORMAT ")", - this, get_allocation_type(), _allocation_t[0], _allocation_t[1])); + p2i(this), get_allocation_type(), _allocation_t[0], _allocation_t[1])); } else { // Operator new() was not called. // Assume that it is embedded or stack object. @@ -191,7 +193,7 @@ // Note: garbage may resembles valid value. assert(~(_allocation_t[0] | allocation_mask) != (uintptr_t)this || !is_type_set(), err_msg("embedded or stack only, this(" PTR_FORMAT ") type %d a[0]=(" PTR_FORMAT ") a[1]=(" PTR_FORMAT ")", - this, get_allocation_type(), _allocation_t[0], _allocation_t[1])); + p2i(this), get_allocation_type(), _allocation_t[0], _allocation_t[1])); set_allocation_type((address)this, STACK_OR_EMBEDDED); _allocation_t[1] = 0; // Zap verification value } @@ -200,7 +202,7 @@ // Used in InlineTree::ok_to_inline() for WarmCallInfo. assert(allocated_on_stack(), err_msg("copy only into local, this(" PTR_FORMAT ") type %d a[0]=(" PTR_FORMAT ") a[1]=(" PTR_FORMAT ")", - this, get_allocation_type(), _allocation_t[0], _allocation_t[1])); + p2i(this), get_allocation_type(), _allocation_t[0], _allocation_t[1])); // Keep current _allocation_t value; return *this; } @@ -216,13 +218,13 @@ void trace_heap_malloc(size_t size, const char* name, void* p) { // A lock is not needed here - tty uses a lock internally - tty->print_cr("Heap malloc " INTPTR_FORMAT " " SIZE_FORMAT " %s", p, size, name == NULL ? "" : name); + tty->print_cr("Heap malloc " INTPTR_FORMAT " " SIZE_FORMAT " %s", p2i(p), size, name == NULL ? "" : name); } void trace_heap_free(void* p) { // A lock is not needed here - tty uses a lock internally - tty->print_cr("Heap free " INTPTR_FORMAT, p); + tty->print_cr("Heap free " INTPTR_FORMAT, p2i(p)); } //-------------------------------------------------------------------------------------- @@ -559,6 +561,7 @@ _chunk = new (alloc_failmode, len) Chunk(len); if (_chunk == NULL) { + _chunk = k; // restore the previous value of _chunk return NULL; } if (k) k->set_next(_chunk); // Append new chunk to end of linked list @@ -721,11 +724,11 @@ void AllocatedObj::print_value() const { print_value_on(tty); } void AllocatedObj::print_on(outputStream* st) const { - st->print_cr("AllocatedObj(" INTPTR_FORMAT ")", this); + st->print_cr("AllocatedObj(" INTPTR_FORMAT ")", p2i(this)); } void AllocatedObj::print_value_on(outputStream* st) const { - st->print("AllocatedObj(" INTPTR_FORMAT ")", this); + st->print("AllocatedObj(" INTPTR_FORMAT ")", p2i(this)); } julong Arena::_bytes_allocated = 0; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/allocation.hpp --- a/src/share/vm/memory/allocation.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/allocation.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -220,8 +220,11 @@ class StackObj ALLOCATION_SUPER_CLASS_SPEC { private: void* operator new(size_t size) throw(); + void* operator new [](size_t size) throw(); +#ifdef __IBMCPP__ + public: +#endif void operator delete(void* p); - void* operator new [](size_t size) throw(); void operator delete [](void* p); }; @@ -264,7 +267,7 @@ class MetaspaceObj { public: - bool is_metaspace_object() const; // more specific test but slower + bool is_metaspace_object() const; bool is_shared() const; void print_address_on(outputStream* st) const; // nonvirtual address printing diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/barrierSet.hpp --- a/src/share/vm/memory/barrierSet.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/barrierSet.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -100,9 +100,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/barrierSet.inline.hpp --- a/src/share/vm/memory/barrierSet.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/barrierSet.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/binaryTreeDictionary.cpp --- a/src/share/vm/memory/binaryTreeDictionary.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/binaryTreeDictionary.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -44,16 +44,16 @@ // This is currently used in the Concurrent Mark&Sweep implementation. //////////////////////////////////////////////////////////////////////////////// -template class FreeList_t> +template size_t TreeChunk::_min_tree_chunk_size = sizeof(TreeChunk)/HeapWordSize; -template class FreeList_t> +template TreeChunk* TreeChunk::as_TreeChunk(Chunk_t* fc) { // Do some assertion checking here. return (TreeChunk*) fc; } -template class FreeList_t> +template void TreeChunk::verify_tree_chunk_list() const { TreeChunk* nextTC = (TreeChunk*)next(); if (prev() != NULL) { // interior list node shouldn'r have tree fields @@ -67,11 +67,11 @@ } } -template class FreeList_t> +template TreeList::TreeList() : _parent(NULL), _left(NULL), _right(NULL) {} -template class FreeList_t> +template TreeList* TreeList::as_TreeList(TreeChunk* tc) { // This first free chunk in the list will be the tree list. @@ -88,20 +88,7 @@ return tl; } - -template class FreeList_t> -TreeList* -get_chunk(size_t size, enum FreeBlockDictionary::Dither dither) { - FreeBlockDictionary::verify_par_locked(); - Chunk_t* res = get_chunk_from_tree(size, dither); - assert(res == NULL || res->is_free(), - "Should be returning a free chunk"); - assert(dither != FreeBlockDictionary::exactly || - res->size() == size, "Not correct size"); - return res; -} - -template class FreeList_t> +template TreeList* TreeList::as_TreeList(HeapWord* addr, size_t size) { TreeChunk* tc = (TreeChunk*) addr; @@ -125,17 +112,17 @@ // an over populated size. The general get_better_list() just returns // the current list. template <> -TreeList* -TreeList::get_better_list( - BinaryTreeDictionary* dictionary) { +TreeList >* +TreeList >::get_better_list( + BinaryTreeDictionary >* dictionary) { // A candidate chunk has been found. If it is already under // populated, get a chunk associated with the hint for this // chunk. - TreeList* curTL = this; + TreeList >* curTL = this; if (surplus() <= 0) { /* Use the hint to find a size with a surplus, and reset the hint. */ - TreeList* hintTL = this; + TreeList >* hintTL = this; while (hintTL->hint() != 0) { assert(hintTL->hint() > hintTL->size(), "hint points in the wrong direction"); @@ -163,14 +150,14 @@ } #endif // INCLUDE_ALL_GCS -template class FreeList_t> +template TreeList* TreeList::get_better_list( BinaryTreeDictionary* dictionary) { return this; } -template class FreeList_t> +template TreeList* TreeList::remove_chunk_replace_if_needed(TreeChunk* tc) { TreeList* retTL = this; @@ -286,7 +273,7 @@ return retTL; } -template class FreeList_t> +template void TreeList::return_chunk_at_tail(TreeChunk* chunk) { assert(chunk != NULL, "returning NULL chunk"); assert(chunk->list() == this, "list should be set for chunk"); @@ -301,7 +288,7 @@ this->link_tail(chunk); assert(!tail() || size() == tail()->size(), "Wrong sized chunk in list"); - FreeList_t::increment_count(); + FreeList_t::increment_count(); debug_only(this->increment_returned_bytes_by(chunk->size()*sizeof(HeapWord));) assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); @@ -311,7 +298,7 @@ // is defined to be after the chunk pointer to by head(). This is // because the TreeList is embedded in the first TreeChunk in the // list. See the definition of TreeChunk. -template class FreeList_t> +template void TreeList::return_chunk_at_head(TreeChunk* chunk) { assert(chunk->list() == this, "list should be set for chunk"); assert(head() != NULL, "The tree list is embedded in the first chunk"); @@ -329,13 +316,13 @@ } head()->link_after(chunk); assert(!head() || size() == head()->size(), "Wrong sized chunk in list"); - FreeList_t::increment_count(); + FreeList_t::increment_count(); debug_only(this->increment_returned_bytes_by(chunk->size()*sizeof(HeapWord));) assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); } -template class FreeList_t> +template void TreeChunk::assert_is_mangled() const { assert((ZapUnusedHeapArea && SpaceMangler::is_mangled((HeapWord*) Chunk_t::size_addr()) && @@ -345,14 +332,14 @@ "Space should be clear or mangled"); } -template class FreeList_t> +template TreeChunk* TreeList::head_as_TreeChunk() { assert(head() == NULL || (TreeChunk::as_TreeChunk(head())->list() == this), "Wrong type of chunk?"); return TreeChunk::as_TreeChunk(head()); } -template class FreeList_t> +template TreeChunk* TreeList::first_available() { assert(head() != NULL, "The head of the list cannot be NULL"); Chunk_t* fc = head()->next(); @@ -369,7 +356,7 @@ // Returns the block with the largest heap address amongst // those in the list for this size; potentially slow and expensive, // use with caution! -template class FreeList_t> +template TreeChunk* TreeList::largest_address() { assert(head() != NULL, "The head of the list cannot be NULL"); Chunk_t* fc = head()->next(); @@ -392,7 +379,7 @@ return retTC; } -template class FreeList_t> +template BinaryTreeDictionary::BinaryTreeDictionary(MemRegion mr) { assert((mr.byte_size() > min_size()), "minimum chunk size"); @@ -405,17 +392,17 @@ assert(total_free_blocks() == 1, "reset check failed"); } -template class FreeList_t> +template void BinaryTreeDictionary::inc_total_size(size_t inc) { _total_size = _total_size + inc; } -template class FreeList_t> +template void BinaryTreeDictionary::dec_total_size(size_t dec) { _total_size = _total_size - dec; } -template class FreeList_t> +template void BinaryTreeDictionary::reset(MemRegion mr) { assert((mr.byte_size() > min_size()), "minimum chunk size"); set_root(TreeList::as_TreeList(mr.start(), mr.word_size())); @@ -423,13 +410,13 @@ set_total_free_blocks(1); } -template class FreeList_t> +template void BinaryTreeDictionary::reset(HeapWord* addr, size_t byte_size) { MemRegion mr(addr, heap_word_size(byte_size)); reset(mr); } -template class FreeList_t> +template void BinaryTreeDictionary::reset() { set_root(NULL); set_total_size(0); @@ -437,7 +424,7 @@ } // Get a free block of size at least size from tree, or NULL. -template class FreeList_t> +template TreeChunk* BinaryTreeDictionary::get_chunk_from_tree( size_t size, @@ -496,7 +483,7 @@ return retTC; } -template class FreeList_t> +template TreeList* BinaryTreeDictionary::find_list(size_t size) const { TreeList* curTL; for (curTL = root(); curTL != NULL;) { @@ -515,7 +502,7 @@ } -template class FreeList_t> +template bool BinaryTreeDictionary::verify_chunk_in_free_list(Chunk_t* tc) const { size_t size = tc->size(); TreeList* tl = find_list(size); @@ -526,7 +513,7 @@ } } -template class FreeList_t> +template Chunk_t* BinaryTreeDictionary::find_largest_dict() const { TreeList *curTL = root(); if (curTL != NULL) { @@ -541,7 +528,7 @@ // chunk in a list on a tree node, just unlink it. // If it is the last chunk in the list (the next link is NULL), // remove the node and repair the tree. -template class FreeList_t> +template TreeChunk* BinaryTreeDictionary::remove_chunk_from_tree(TreeChunk* tc) { assert(tc != NULL, "Should not call with a NULL chunk"); @@ -682,7 +669,7 @@ // Remove the leftmost node (lm) in the tree and return it. // If lm has a right child, link it to the left node of // the parent of lm. -template class FreeList_t> +template TreeList* BinaryTreeDictionary::remove_tree_minimum(TreeList* tl) { assert(tl != NULL && tl->parent() != NULL, "really need a proper sub-tree"); // locate the subtree minimum by walking down left branches @@ -717,7 +704,7 @@ return curTL; } -template class FreeList_t> +template void BinaryTreeDictionary::insert_chunk_in_tree(Chunk_t* fc) { TreeList *curTL, *prevTL; size_t size = fc->size(); @@ -783,7 +770,7 @@ } } -template class FreeList_t> +template size_t BinaryTreeDictionary::max_chunk_size() const { FreeBlockDictionary::verify_par_locked(); TreeList* tc = root(); @@ -792,7 +779,7 @@ return tc->size(); } -template class FreeList_t> +template size_t BinaryTreeDictionary::total_list_length(TreeList* tl) const { size_t res; res = tl->count(); @@ -805,7 +792,7 @@ return res; } -template class FreeList_t> +template size_t BinaryTreeDictionary::total_size_in_tree(TreeList* tl) const { if (tl == NULL) return 0; @@ -814,7 +801,7 @@ total_size_in_tree(tl->right()); } -template class FreeList_t> +template double BinaryTreeDictionary::sum_of_squared_block_sizes(TreeList* const tl) const { if (tl == NULL) { return 0.0; @@ -826,7 +813,7 @@ return curr; } -template class FreeList_t> +template size_t BinaryTreeDictionary::total_free_blocks_in_tree(TreeList* tl) const { if (tl == NULL) return 0; @@ -835,14 +822,14 @@ total_free_blocks_in_tree(tl->right()); } -template class FreeList_t> +template size_t BinaryTreeDictionary::num_free_blocks() const { assert(total_free_blocks_in_tree(root()) == total_free_blocks(), "_total_free_blocks inconsistency"); return total_free_blocks(); } -template class FreeList_t> +template size_t BinaryTreeDictionary::tree_height_helper(TreeList* tl) const { if (tl == NULL) return 0; @@ -850,12 +837,12 @@ tree_height_helper(tl->right())); } -template class FreeList_t> +template size_t BinaryTreeDictionary::tree_height() const { return tree_height_helper(root()); } -template class FreeList_t> +template size_t BinaryTreeDictionary::total_nodes_helper(TreeList* tl) const { if (tl == NULL) { return 0; @@ -864,18 +851,18 @@ total_nodes_helper(tl->right()); } -template class FreeList_t> +template size_t BinaryTreeDictionary::total_nodes_in_tree(TreeList* tl) const { return total_nodes_helper(root()); } -template class FreeList_t> +template void BinaryTreeDictionary::dict_census_update(size_t size, bool split, bool birth){} #if INCLUDE_ALL_GCS template <> -void AFLBinaryTreeDictionary::dict_census_update(size_t size, bool split, bool birth){ - TreeList* nd = find_list(size); +void AFLBinaryTreeDictionary::dict_census_update(size_t size, bool split, bool birth) { + TreeList >* nd = find_list(size); if (nd) { if (split) { if (birth) { @@ -903,7 +890,7 @@ } #endif // INCLUDE_ALL_GCS -template class FreeList_t> +template bool BinaryTreeDictionary::coal_dict_over_populated(size_t size) { // For the general type of freelists, encourage coalescing by // returning true. @@ -915,7 +902,7 @@ bool AFLBinaryTreeDictionary::coal_dict_over_populated(size_t size) { if (FLSAlwaysCoalesceLarge) return true; - TreeList* list_of_size = find_list(size); + TreeList >* list_of_size = find_list(size); // None of requested size implies overpopulated. return list_of_size == NULL || list_of_size->coal_desired() <= 0 || list_of_size->count() > list_of_size->coal_desired(); @@ -928,15 +915,15 @@ // do_tree() walks the nodes in the binary tree applying do_list() // to each list at each node. -template class FreeList_t> +template class TreeCensusClosure : public StackObj { protected: - virtual void do_list(FreeList_t* fl) = 0; + virtual void do_list(FreeList_t* fl) = 0; public: virtual void do_tree(TreeList* tl) = 0; }; -template class FreeList_t> +template class AscendTreeCensusClosure : public TreeCensusClosure { public: void do_tree(TreeList* tl) { @@ -948,7 +935,7 @@ } }; -template class FreeList_t> +template class DescendTreeCensusClosure : public TreeCensusClosure { public: void do_tree(TreeList* tl) { @@ -962,7 +949,7 @@ // For each list in the tree, calculate the desired, desired // coalesce, count before sweep, and surplus before sweep. -template class FreeList_t> +template class BeginSweepClosure : public AscendTreeCensusClosure { double _percentage; float _inter_sweep_current; @@ -995,16 +982,16 @@ // Similar to TreeCensusClosure but searches the // tree and returns promptly when found. -template class FreeList_t> +template class TreeSearchClosure : public StackObj { protected: - virtual bool do_list(FreeList_t* fl) = 0; + virtual bool do_list(FreeList_t* fl) = 0; public: virtual bool do_tree(TreeList* tl) = 0; }; #if 0 // Don't need this yet but here for symmetry. -template class FreeList_t> +template class AscendTreeSearchClosure : public TreeSearchClosure { public: bool do_tree(TreeList* tl) { @@ -1018,7 +1005,7 @@ }; #endif -template class FreeList_t> +template class DescendTreeSearchClosure : public TreeSearchClosure { public: bool do_tree(TreeList* tl) { @@ -1033,14 +1020,14 @@ // Searches the tree for a chunk that ends at the // specified address. -template class FreeList_t> +template class EndTreeSearchClosure : public DescendTreeSearchClosure { HeapWord* _target; Chunk_t* _found; public: EndTreeSearchClosure(HeapWord* target) : _target(target), _found(NULL) {} - bool do_list(FreeList_t* fl) { + bool do_list(FreeList_t* fl) { Chunk_t* item = fl->head(); while (item != NULL) { if (item->end() == (uintptr_t*) _target) { @@ -1054,7 +1041,7 @@ Chunk_t* found() { return _found; } }; -template class FreeList_t> +template Chunk_t* BinaryTreeDictionary::find_chunk_ends_at(HeapWord* target) const { EndTreeSearchClosure etsc(target); bool found_target = etsc.do_tree(root()); @@ -1063,7 +1050,7 @@ return etsc.found(); } -template class FreeList_t> +template void BinaryTreeDictionary::begin_sweep_dict_census(double coalSurplusPercent, float inter_sweep_current, float inter_sweep_estimate, float intra_sweep_estimate) { BeginSweepClosure bsc(coalSurplusPercent, inter_sweep_current, @@ -1075,32 +1062,32 @@ // Closures and methods for calculating total bytes returned to the // free lists in the tree. #ifndef PRODUCT -template class FreeList_t> +template class InitializeDictReturnedBytesClosure : public AscendTreeCensusClosure { public: - void do_list(FreeList_t* fl) { + void do_list(FreeList_t* fl) { fl->set_returned_bytes(0); } }; -template class FreeList_t> +template void BinaryTreeDictionary::initialize_dict_returned_bytes() { InitializeDictReturnedBytesClosure idrb; idrb.do_tree(root()); } -template class FreeList_t> +template class ReturnedBytesClosure : public AscendTreeCensusClosure { size_t _dict_returned_bytes; public: ReturnedBytesClosure() { _dict_returned_bytes = 0; } - void do_list(FreeList_t* fl) { + void do_list(FreeList_t* fl) { _dict_returned_bytes += fl->returned_bytes(); } size_t dict_returned_bytes() { return _dict_returned_bytes; } }; -template class FreeList_t> +template size_t BinaryTreeDictionary::sum_dict_returned_bytes() { ReturnedBytesClosure rbc; rbc.do_tree(root()); @@ -1109,17 +1096,17 @@ } // Count the number of entries in the tree. -template class FreeList_t> +template class treeCountClosure : public DescendTreeCensusClosure { public: uint count; treeCountClosure(uint c) { count = c; } - void do_list(FreeList_t* fl) { + void do_list(FreeList_t* fl) { count++; } }; -template class FreeList_t> +template size_t BinaryTreeDictionary::total_count() { treeCountClosure ctc(0); ctc.do_tree(root()); @@ -1128,7 +1115,7 @@ #endif // PRODUCT // Calculate surpluses for the lists in the tree. -template class FreeList_t> +template class setTreeSurplusClosure : public AscendTreeCensusClosure { double percentage; public: @@ -1144,14 +1131,14 @@ #endif // INCLUDE_ALL_GCS }; -template class FreeList_t> +template void BinaryTreeDictionary::set_tree_surplus(double splitSurplusPercent) { setTreeSurplusClosure sts(splitSurplusPercent); sts.do_tree(root()); } // Set hints for the lists in the tree. -template class FreeList_t> +template class setTreeHintsClosure : public DescendTreeCensusClosure { size_t hint; public: @@ -1170,14 +1157,14 @@ #endif // INCLUDE_ALL_GCS }; -template class FreeList_t> +template void BinaryTreeDictionary::set_tree_hints(void) { setTreeHintsClosure sth(0); sth.do_tree(root()); } // Save count before previous sweep and splits and coalesces. -template class FreeList_t> +template class clearTreeCensusClosure : public AscendTreeCensusClosure { void do_list(FreeList* fl) {} @@ -1192,14 +1179,14 @@ #endif // INCLUDE_ALL_GCS }; -template class FreeList_t> +template void BinaryTreeDictionary::clear_tree_census(void) { clearTreeCensusClosure ctc; ctc.do_tree(root()); } // Do reporting and post sweep clean up. -template class FreeList_t> +template void BinaryTreeDictionary::end_sweep_dict_census(double splitSurplusPercent) { // Does walking the tree 3 times hurt? set_tree_surplus(splitSurplusPercent); @@ -1211,41 +1198,41 @@ } // Print summary statistics -template class FreeList_t> +template void BinaryTreeDictionary::report_statistics() const { FreeBlockDictionary::verify_par_locked(); gclog_or_tty->print("Statistics for BinaryTreeDictionary:\n" "------------------------------------\n"); size_t total_size = total_chunk_size(debug_only(NULL)); size_t free_blocks = num_free_blocks(); - gclog_or_tty->print("Total Free Space: %d\n", total_size); - gclog_or_tty->print("Max Chunk Size: %d\n", max_chunk_size()); - gclog_or_tty->print("Number of Blocks: %d\n", free_blocks); + gclog_or_tty->print("Total Free Space: " SIZE_FORMAT "\n", total_size); + gclog_or_tty->print("Max Chunk Size: " SIZE_FORMAT "\n", max_chunk_size()); + gclog_or_tty->print("Number of Blocks: " SIZE_FORMAT "\n", free_blocks); if (free_blocks > 0) { - gclog_or_tty->print("Av. Block Size: %d\n", total_size/free_blocks); + gclog_or_tty->print("Av. Block Size: " SIZE_FORMAT "\n", total_size/free_blocks); } - gclog_or_tty->print("Tree Height: %d\n", tree_height()); + gclog_or_tty->print("Tree Height: " SIZE_FORMAT "\n", tree_height()); } // Print census information - counts, births, deaths, etc. // for each list in the tree. Also print some summary // information. -template class FreeList_t> +template class PrintTreeCensusClosure : public AscendTreeCensusClosure { int _print_line; size_t _total_free; - FreeList_t _total; + FreeList_t _total; public: PrintTreeCensusClosure() { _print_line = 0; _total_free = 0; } - FreeList_t* total() { return &_total; } + FreeList_t* total() { return &_total; } size_t total_free() { return _total_free; } void do_list(FreeList* fl) { if (++_print_line >= 40) { - FreeList_t::print_labels_on(gclog_or_tty, "size"); + FreeList_t::print_labels_on(gclog_or_tty, "size"); _print_line = 0; } fl->print_on(gclog_or_tty); @@ -1256,7 +1243,7 @@ #if INCLUDE_ALL_GCS void do_list(AdaptiveFreeList* fl) { if (++_print_line >= 40) { - FreeList_t::print_labels_on(gclog_or_tty, "size"); + FreeList_t::print_labels_on(gclog_or_tty, "size"); _print_line = 0; } fl->print_on(gclog_or_tty); @@ -1275,16 +1262,16 @@ #endif // INCLUDE_ALL_GCS }; -template class FreeList_t> +template void BinaryTreeDictionary::print_dict_census(void) const { gclog_or_tty->print("\nBinaryTree\n"); - FreeList_t::print_labels_on(gclog_or_tty, "size"); + FreeList_t::print_labels_on(gclog_or_tty, "size"); PrintTreeCensusClosure ptc; ptc.do_tree(root()); - FreeList_t* total = ptc.total(); - FreeList_t::print_labels_on(gclog_or_tty, " "); + FreeList_t* total = ptc.total(); + FreeList_t::print_labels_on(gclog_or_tty, " "); } #if INCLUDE_ALL_GCS @@ -1293,7 +1280,7 @@ gclog_or_tty->print("\nBinaryTree\n"); AdaptiveFreeList::print_labels_on(gclog_or_tty, "size"); - PrintTreeCensusClosure ptc; + PrintTreeCensusClosure > ptc; ptc.do_tree(root()); AdaptiveFreeList* total = ptc.total(); @@ -1311,7 +1298,7 @@ } #endif // INCLUDE_ALL_GCS -template class FreeList_t> +template class PrintFreeListsClosure : public AscendTreeCensusClosure { outputStream* _st; int _print_line; @@ -1321,9 +1308,9 @@ _st = st; _print_line = 0; } - void do_list(FreeList_t* fl) { + void do_list(FreeList_t* fl) { if (++_print_line >= 40) { - FreeList_t::print_labels_on(_st, "size"); + FreeList_t::print_labels_on(_st, "size"); _print_line = 0; } fl->print_on(gclog_or_tty); @@ -1331,16 +1318,16 @@ for (Chunk_t* fc = fl->head(); fc != NULL; fc = fc->next()) { _st->print_cr("\t[" PTR_FORMAT "," PTR_FORMAT ") %s", - fc, (HeapWord*)fc + sz, + p2i(fc), p2i((HeapWord*)fc + sz), fc->cantCoalesce() ? "\t CC" : ""); } } }; -template class FreeList_t> +template void BinaryTreeDictionary::print_free_lists(outputStream* st) const { - FreeList_t::print_labels_on(st, "size"); + FreeList_t::print_labels_on(st, "size"); PrintFreeListsClosure pflc(st); pflc.do_tree(root()); } @@ -1349,7 +1336,7 @@ // . _root has no parent // . parent and child point to each other // . each node's key correctly related to that of its child(ren) -template class FreeList_t> +template void BinaryTreeDictionary::verify_tree() const { guarantee(root() == NULL || total_free_blocks() == 0 || total_size() != 0, "_total_size should't be 0?"); @@ -1357,7 +1344,7 @@ verify_tree_helper(root()); } -template class FreeList_t> +template size_t BinaryTreeDictionary::verify_prev_free_ptrs(TreeList* tl) { size_t ct = 0; for (Chunk_t* curFC = tl->head(); curFC != NULL; curFC = curFC->next()) { @@ -1371,7 +1358,7 @@ // Note: this helper is recursive rather than iterative, so use with // caution on very deep trees; and watch out for stack overflow errors; // In general, to be used only for debugging. -template class FreeList_t> +template void BinaryTreeDictionary::verify_tree_helper(TreeList* tl) const { if (tl == NULL) return; @@ -1400,25 +1387,25 @@ verify_tree_helper(tl->right()); } -template class FreeList_t> +template void BinaryTreeDictionary::verify() const { verify_tree(); guarantee(total_size() == total_size_in_tree(root()), "Total Size inconsistency"); } -template class TreeList; -template class BinaryTreeDictionary; -template class TreeChunk; +template class TreeList >; +template class BinaryTreeDictionary >; +template class TreeChunk >; -template class TreeList; -template class BinaryTreeDictionary; -template class TreeChunk; +template class TreeList >; +template class BinaryTreeDictionary >; +template class TreeChunk >; #if INCLUDE_ALL_GCS // Explicitly instantiate these types for FreeChunk. -template class TreeList; -template class BinaryTreeDictionary; -template class TreeChunk; +template class TreeList >; +template class BinaryTreeDictionary >; +template class TreeChunk >; #endif // INCLUDE_ALL_GCS diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/binaryTreeDictionary.hpp --- a/src/share/vm/memory/binaryTreeDictionary.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/binaryTreeDictionary.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -37,18 +37,18 @@ // A TreeList is a FreeList which can be used to maintain a // binary tree of free lists. -template class FreeList_t> class TreeChunk; -template class FreeList_t> class BinaryTreeDictionary; -template class FreeList_t> class AscendTreeCensusClosure; -template class FreeList_t> class DescendTreeCensusClosure; -template class FreeList_t> class DescendTreeSearchClosure; +template class TreeChunk; +template class BinaryTreeDictionary; +template class AscendTreeCensusClosure; +template class DescendTreeCensusClosure; +template class DescendTreeSearchClosure; class FreeChunk; template class AdaptiveFreeList; -typedef BinaryTreeDictionary AFLBinaryTreeDictionary; +typedef BinaryTreeDictionary > AFLBinaryTreeDictionary; -template class FreeList_t> -class TreeList : public FreeList_t { +template +class TreeList : public FreeList_t { friend class TreeChunk; friend class BinaryTreeDictionary; friend class AscendTreeCensusClosure; @@ -66,12 +66,12 @@ TreeList* right() const { return _right; } // Wrapper on call to base class, to get the template to compile. - Chunk_t* head() const { return FreeList_t::head(); } - Chunk_t* tail() const { return FreeList_t::tail(); } - void set_head(Chunk_t* head) { FreeList_t::set_head(head); } - void set_tail(Chunk_t* tail) { FreeList_t::set_tail(tail); } + Chunk_t* head() const { return FreeList_t::head(); } + Chunk_t* tail() const { return FreeList_t::tail(); } + void set_head(Chunk_t* head) { FreeList_t::set_head(head); } + void set_tail(Chunk_t* tail) { FreeList_t::set_tail(tail); } - size_t size() const { return FreeList_t::size(); } + size_t size() const { return FreeList_t::size(); } // Accessors for links in tree. @@ -90,7 +90,7 @@ void clear_left() { _left = NULL; } void clear_right() { _right = NULL; } void clear_parent() { _parent = NULL; } - void initialize() { clear_left(); clear_right(), clear_parent(); FreeList_t::initialize(); } + void initialize() { clear_left(); clear_right(), clear_parent(); FreeList_t::initialize(); } // For constructing a TreeList from a Tree chunk or // address and size. @@ -139,7 +139,7 @@ // on the free list for a node in the tree and is only removed if // it is the last chunk on the free list. -template class FreeList_t> +template class TreeChunk : public Chunk_t { friend class TreeList; TreeList* _list; @@ -173,7 +173,7 @@ }; -template class FreeList_t> +template class BinaryTreeDictionary: public FreeBlockDictionary { friend class VMStructs; size_t _total_size; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/blockOffsetTable.cpp --- a/src/share/vm/memory/blockOffsetTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/blockOffsetTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -59,12 +59,12 @@ " rs.base(): " INTPTR_FORMAT " rs.size(): " INTPTR_FORMAT " rs end(): " INTPTR_FORMAT, - rs.base(), rs.size(), rs.base() + rs.size()); + p2i(rs.base()), rs.size(), p2i(rs.base() + rs.size())); gclog_or_tty->print_cr(" " " _vs.low_boundary(): " INTPTR_FORMAT " _vs.high_boundary(): " INTPTR_FORMAT, - _vs.low_boundary(), - _vs.high_boundary()); + p2i(_vs.low_boundary()), + p2i(_vs.high_boundary())); } } @@ -537,10 +537,10 @@ q -= (N_words * n_cards_back); assert(q >= _sp->bottom(), err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT, - q, _sp->bottom())); + p2i(q), p2i(_sp->bottom()))); assert(q < _sp->end(), err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT, - q, _sp->end())); + p2i(q), p2i(_sp->end()))); index -= n_cards_back; offset = _array->offset_array(index); } @@ -549,10 +549,10 @@ q -= offset; assert(q >= _sp->bottom(), err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT, - q, _sp->bottom())); + p2i(q), p2i(_sp->bottom()))); assert(q < _sp->end(), err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT, - q, _sp->end())); + p2i(q), p2i(_sp->end()))); HeapWord* n = q; while (n <= addr) { @@ -563,14 +563,14 @@ err_msg("Looping at n = " PTR_FORMAT " with last = " PTR_FORMAT"," " while querying blk_start(" PTR_FORMAT ")" " on _sp = [" PTR_FORMAT "," PTR_FORMAT ")", - n, last, addr, _sp->bottom(), _sp->end())); + p2i(n), p2i(last), p2i(addr), p2i(_sp->bottom()), p2i(_sp->end()))); } assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ")" " <= arg (" INTPTR_FORMAT ")", - q, addr)); + p2i(q), p2i(addr))); assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", - addr, n)); + p2i(addr), p2i(n))); return q; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/cardTableModRefBS.cpp --- a/src/share/vm/memory/cardTableModRefBS.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/cardTableModRefBS.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -138,11 +138,11 @@ gclog_or_tty->print_cr(" " " &_byte_map[0]: " INTPTR_FORMAT " &_byte_map[_last_valid_index]: " INTPTR_FORMAT, - &_byte_map[0], - &_byte_map[_last_valid_index]); + p2i(&_byte_map[0]), + p2i(&_byte_map[_last_valid_index])); gclog_or_tty->print_cr(" " " byte_map_base: " INTPTR_FORMAT, - byte_map_base); + p2i(byte_map_base)); } } @@ -392,23 +392,23 @@ gclog_or_tty->print_cr(" " " _covered[%d].start(): " INTPTR_FORMAT " _covered[%d].last(): " INTPTR_FORMAT, - ind, _covered[ind].start(), - ind, _covered[ind].last()); + ind, p2i(_covered[ind].start()), + ind, p2i(_covered[ind].last())); gclog_or_tty->print_cr(" " " _committed[%d].start(): " INTPTR_FORMAT " _committed[%d].last(): " INTPTR_FORMAT, - ind, _committed[ind].start(), - ind, _committed[ind].last()); + ind, p2i(_committed[ind].start()), + ind, p2i(_committed[ind].last())); gclog_or_tty->print_cr(" " " byte_for(start): " INTPTR_FORMAT " byte_for(last): " INTPTR_FORMAT, - byte_for(_covered[ind].start()), - byte_for(_covered[ind].last())); + p2i(byte_for(_covered[ind].start())), + p2i(byte_for(_covered[ind].last()))); gclog_or_tty->print_cr(" " " addr_for(start): " INTPTR_FORMAT " addr_for(last): " INTPTR_FORMAT, - addr_for((jbyte*) _committed[ind].start()), - addr_for((jbyte*) _committed[ind].last())); + p2i(addr_for((jbyte*) _committed[ind].start())), + p2i(addr_for((jbyte*) _committed[ind].last()))); } // Touch the last card of the covered region to show that it // is committed (or SEGV). @@ -419,8 +419,8 @@ // 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); } @@ -657,14 +657,14 @@ if (failed) { if (!failures) { tty->cr(); - tty->print_cr("== CT verification failed: ["PTR_FORMAT","PTR_FORMAT"]", start, end); + tty->print_cr("== CT verification failed: [" INTPTR_FORMAT "," INTPTR_FORMAT "]", p2i(start), p2i(end)); tty->print_cr("== %sexpecting value: %d", (val_equals) ? "" : "not ", val); failures = true; } tty->print_cr("== card "PTR_FORMAT" ["PTR_FORMAT","PTR_FORMAT"], " - "val: %d", curr, addr_for(curr), - (HeapWord*) (((size_t) addr_for(curr)) + card_size), + "val: %d", p2i(curr), p2i(addr_for(curr)), + p2i((HeapWord*) (((size_t) addr_for(curr)) + card_size)), (int) curr_val); } } @@ -682,7 +682,7 @@ void CardTableModRefBS::print_on(outputStream* st) const { st->print_cr("Card table byte_map: [" INTPTR_FORMAT "," INTPTR_FORMAT "] byte_map_base: " INTPTR_FORMAT, - _byte_map, _byte_map + _byte_map_size, byte_map_base); + p2i(_byte_map), p2i(_byte_map + _byte_map_size), p2i(byte_map_base)); } bool CardTableModRefBSForCTRS::card_will_be_scanned(jbyte cv) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/cardTableModRefBS.hpp --- a/src/share/vm/memory/cardTableModRefBS.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/cardTableModRefBS.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -155,7 +155,7 @@ assert(_whole_heap.contains(p), err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of " " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")", - p, _whole_heap.start(), _whole_heap.end())); + p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()))); jbyte* result = &byte_map_base[uintptr_t(p) >> card_shift]; assert(result >= _byte_map && result < _byte_map + _byte_map_size, "out of bounds accessor for card marking array"); @@ -292,7 +292,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; } @@ -324,9 +324,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 @@ -426,7 +431,7 @@ assert(_whole_heap.contains(result), err_msg("Returning result = "PTR_FORMAT" out of bounds of " " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")", - result, _whole_heap.start(), _whole_heap.end())); + p2i(result), p2i(_whole_heap.start()), p2i(_whole_heap.end()))); return result; } @@ -435,7 +440,7 @@ assert(_whole_heap.contains(p), err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of " " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")", - p, _whole_heap.start(), _whole_heap.end())); + p2i(p), p2i(_whole_heap.start()), p2i(_whole_heap.end()))); return byte_for(p) - _byte_map; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/cardTableRS.cpp --- a/src/share/vm/memory/cardTableRS.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/cardTableRS.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -288,14 +288,14 @@ err_msg("Did you forget to call save_marks()? " "[" PTR_FORMAT ", " PTR_FORMAT ") is not contained in " "[" PTR_FORMAT ", " PTR_FORMAT ")", - urasm.start(), urasm.end(), ur.start(), ur.end())); + p2i(urasm.start()), p2i(urasm.end()), p2i(ur.start()), p2i(ur.end()))); // In the case of CMS+ParNew, issue a warning if (!ur.contains(urasm)) { assert(UseConcMarkSweepGC && UseParNewGC, "Tautology: see assert above"); warning("CMS+ParNew: Did you forget to call save_marks()? " "[" PTR_FORMAT ", " PTR_FORMAT ") is not contained in " "[" PTR_FORMAT ", " PTR_FORMAT ")", - urasm.start(), urasm.end(), ur.start(), ur.end()); + p2i(urasm.start()), p2i(urasm.end()), p2i(ur.start()), p2i(ur.end())); MemRegion ur2 = sp->used_region(); MemRegion urasm2 = sp->used_region_at_save_marks(); if (!ur.equals(ur2)) { @@ -349,12 +349,12 @@ assert(jp >= _begin && jp < _end, err_msg("Error: jp " PTR_FORMAT " should be within " "[_begin, _end) = [" PTR_FORMAT "," PTR_FORMAT ")", - jp, _begin, _end)); + p2i(jp), p2i(_begin), p2i(_end))); oop obj = oopDesc::load_decode_heap_oop(p); guarantee(obj == NULL || (HeapWord*)obj >= _boundary, err_msg("pointer " PTR_FORMAT " at " PTR_FORMAT " on " "clean card crosses boundary" PTR_FORMAT, - (HeapWord*)obj, jp, _boundary)); + p2i((HeapWord*)obj), p2i(jp), p2i(_boundary))); } public: @@ -362,10 +362,10 @@ _boundary(b), _begin(begin), _end(end) { assert(b <= begin, err_msg("Error: boundary " PTR_FORMAT " should be at or below begin " PTR_FORMAT, - b, begin)); + p2i(b), p2i(begin))); assert(begin <= end, err_msg("Error: begin " PTR_FORMAT " should be strictly below end " PTR_FORMAT, - begin, end)); + p2i(begin), p2i(end))); } virtual void do_oop(oop* p) { VerifyCleanCardClosure::do_oop_work(p); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/collectorPolicy.cpp --- a/src/share/vm/memory/collectorPolicy.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/collectorPolicy.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -307,10 +307,13 @@ } // Now take the actual NewSize into account. We will silently increase NewSize - // if the user specified a smaller value. + // if the user specified a smaller or unaligned value. smallest_new_size = MAX2(smallest_new_size, (uintx)align_size_down(NewSize, _gen_alignment)); if (smallest_new_size != NewSize) { - FLAG_SET_ERGO(uintx, NewSize, smallest_new_size); + // Do not use FLAG_SET_ERGO to update NewSize here, since this will override + // if NewSize was set on the command line or not. This information is needed + // later when setting the initial and minimum young generation size. + NewSize = smallest_new_size; } _initial_gen0_size = NewSize; @@ -450,18 +453,20 @@ _max_gen0_size = max_new_size; } else { size_t desired_new_size = 0; - if (!FLAG_IS_DEFAULT(NewSize)) { - // If NewSize is set ergonomically (for example by cms), it - // would make sense to use it. If it is used, also use it - // to set the initial size. Although there is no reason - // the minimum size and the initial size have to be the same, - // the current implementation gets into trouble during the calculation - // of the tenured generation sizes if they are different. - // Note that this makes the initial size and the minimum size - // generally small compared to the NewRatio calculation. + if (FLAG_IS_CMDLINE(NewSize)) { + // If NewSize is set on the command line, we must use it as + // the initial size and it also makes sense to use it as the + // lower limit. _min_gen0_size = NewSize; desired_new_size = NewSize; max_new_size = MAX2(max_new_size, NewSize); + } else if (FLAG_IS_ERGO(NewSize)) { + // If NewSize is set ergonomically, we should use it as a lower + // limit, but use NewRatio to calculate the initial size. + _min_gen0_size = NewSize; + desired_new_size = + MAX2(scale_by_NewRatio_aligned(_initial_heap_byte_size), NewSize); + max_new_size = MAX2(max_new_size, NewSize); } else { // For the case where NewSize is the default, use NewRatio // to size the minimum and initial generation sizes. @@ -764,7 +769,7 @@ if ((QueuedAllocationWarningCount > 0) && (try_count % QueuedAllocationWarningCount == 0)) { warning("TwoGenerationCollectorPolicy::mem_allocate_work retries %d times \n\t" - " size=%d %s", try_count, size, is_tlab ? "(TLAB)" : ""); + " size=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : ""); } } } @@ -933,7 +938,7 @@ if ((QueuedAllocationWarningCount > 0) && (loop_count % QueuedAllocationWarningCount == 0)) { warning("satisfy_failed_metadata_allocation() retries %d times \n\t" - " size=%d", loop_count, word_size); + " size=" SIZE_FORMAT, loop_count, word_size); } } while (true); // Until a GC is done } @@ -989,3 +994,110 @@ _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 3); } } + +/////////////// Unit tests /////////////// + +#ifndef PRODUCT +// Testing that the NewSize flag is handled correct is hard because it +// depends on so many other configurable variables. This test only tries to +// verify that there are some basic rules for NewSize honored by the policies. +class TestGenCollectorPolicy { +public: + static void test() { + size_t flag_value; + + save_flags(); + + // Set some limits that makes the math simple. + FLAG_SET_ERGO(uintx, MaxHeapSize, 180 * M); + FLAG_SET_ERGO(uintx, InitialHeapSize, 120 * M); + Arguments::set_min_heap_size(40 * M); + + // If NewSize is set on the command line, it should be used + // for both min and initial young size if less than min heap. + flag_value = 20 * M; + FLAG_SET_CMDLINE(uintx, NewSize, flag_value); + verify_min(flag_value); + verify_initial(flag_value); + + // If NewSize is set on command line, but is larger than the min + // heap size, it should only be used for initial young size. + flag_value = 80 * M; + FLAG_SET_CMDLINE(uintx, NewSize, flag_value); + verify_initial(flag_value); + + // If NewSize has been ergonomically set, the collector policy + // should use it for min but calculate the initial young size + // using NewRatio. + flag_value = 20 * M; + FLAG_SET_ERGO(uintx, NewSize, flag_value); + verify_min(flag_value); + verify_scaled_initial(InitialHeapSize); + + restore_flags(); + + } + + static void verify_min(size_t expected) { + MarkSweepPolicy msp; + msp.initialize_all(); + + assert(msp.min_gen0_size() <= expected, err_msg("%zu > %zu", msp.min_gen0_size(), expected)); + } + + static void verify_initial(size_t expected) { + MarkSweepPolicy msp; + msp.initialize_all(); + + assert(msp.initial_gen0_size() == expected, err_msg("%zu != %zu", msp.initial_gen0_size(), expected)); + } + + static void verify_scaled_initial(size_t initial_heap_size) { + MarkSweepPolicy msp; + msp.initialize_all(); + + size_t expected = msp.scale_by_NewRatio_aligned(initial_heap_size); + assert(msp.initial_gen0_size() == expected, err_msg("%zu != %zu", msp.initial_gen0_size(), expected)); + assert(FLAG_IS_ERGO(NewSize) && NewSize == expected, + err_msg("NewSize should have been set ergonomically to %zu, but was %zu", expected, NewSize)); + } + +private: + static size_t original_InitialHeapSize; + static size_t original_MaxHeapSize; + static size_t original_MaxNewSize; + static size_t original_MinHeapDeltaBytes; + static size_t original_NewSize; + static size_t original_OldSize; + + static void save_flags() { + original_InitialHeapSize = InitialHeapSize; + original_MaxHeapSize = MaxHeapSize; + original_MaxNewSize = MaxNewSize; + original_MinHeapDeltaBytes = MinHeapDeltaBytes; + original_NewSize = NewSize; + original_OldSize = OldSize; + } + + static void restore_flags() { + InitialHeapSize = original_InitialHeapSize; + MaxHeapSize = original_MaxHeapSize; + MaxNewSize = original_MaxNewSize; + MinHeapDeltaBytes = original_MinHeapDeltaBytes; + NewSize = original_NewSize; + OldSize = original_OldSize; + } +}; + +size_t TestGenCollectorPolicy::original_InitialHeapSize = 0; +size_t TestGenCollectorPolicy::original_MaxHeapSize = 0; +size_t TestGenCollectorPolicy::original_MaxNewSize = 0; +size_t TestGenCollectorPolicy::original_MinHeapDeltaBytes = 0; +size_t TestGenCollectorPolicy::original_NewSize = 0; +size_t TestGenCollectorPolicy::original_OldSize = 0; + +void TestNewSize_test() { + TestGenCollectorPolicy::test(); +} + +#endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/collectorPolicy.hpp --- a/src/share/vm/memory/collectorPolicy.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/collectorPolicy.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -220,6 +220,7 @@ }; class GenCollectorPolicy : public CollectorPolicy { +friend class TestGenCollectorPolicy; protected: size_t _min_gen0_size; size_t _initial_gen0_size; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/defNewGeneration.cpp --- a/src/share/vm/memory/defNewGeneration.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/defNewGeneration.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -46,6 +46,8 @@ #include "utilities/copy.hpp" #include "utilities/stack.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // // DefNewGeneration functions. @@ -1086,6 +1088,10 @@ return eden()->capacity(); } +size_t DefNewGeneration::tlab_used() const { + return eden()->used(); +} + size_t DefNewGeneration::unsafe_max_tlab_alloc() const { return unsafe_max_alloc_nogc(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/defNewGeneration.hpp --- a/src/share/vm/memory/defNewGeneration.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/defNewGeneration.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -239,6 +239,7 @@ // Thread-local allocation buffers bool supports_tlab_allocation() const { return true; } size_t tlab_capacity() const; + size_t tlab_used() const; size_t unsafe_max_tlab_alloc() const; // Grow the generation by the specified number of bytes. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/filemap.cpp --- a/src/share/vm/memory/filemap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/filemap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -40,6 +40,7 @@ #define O_BINARY 0 // otherwise do nothing. #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC extern address JVM_FunctionAtStart(); extern address JVM_FunctionAtEnd(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/freeList.cpp --- a/src/share/vm/memory/freeList.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/freeList.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -34,6 +34,7 @@ #if INCLUDE_ALL_GCS #include "gc_implementation/concurrentMarkSweep/freeChunk.hpp" +#include "gc_implementation/g1/g1CodeCacheRemSet.hpp" #endif // INCLUDE_ALL_GCS // Free list. A FreeList is used to access a linked list of chunks @@ -332,4 +333,5 @@ template class FreeList; #if INCLUDE_ALL_GCS template class FreeList; +template class FreeList; #endif // INCLUDE_ALL_GCS diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/gcLocker.cpp --- a/src/share/vm/memory/gcLocker.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/gcLocker.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -28,7 +28,6 @@ #include "memory/sharedHeap.hpp" volatile jint GC_locker::_jni_lock_count = 0; -volatile jint GC_locker::_lock_count = 0; volatile bool GC_locker::_needs_gc = false; volatile bool GC_locker::_doing_gc = false; @@ -52,7 +51,7 @@ tty->print_cr("critical counts don't match: %d != %d", _jni_lock_count, count); for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) { if (thr->in_critical()) { - tty->print_cr(INTPTR_FORMAT " in_critical %d", thr, thr->in_critical()); + tty->print_cr(INTPTR_FORMAT " in_critical %d", p2i(thr), thr->in_critical()); } } } @@ -102,7 +101,7 @@ // We check that at least one thread is in a critical region before // blocking because blocked threads are woken up by a thread exiting // a JNI critical region. - while ((needs_gc() && is_jni_active()) || _doing_gc) { + while (is_active_and_needs_gc() || _doing_gc) { JNICritical_lock->wait(); } thread->enter_critical(); @@ -116,27 +115,20 @@ _jni_lock_count--; decrement_debug_jni_lock_count(); thread->exit_critical(); - if (needs_gc() && !is_jni_active()) { + if (needs_gc() && !is_active_internal()) { // We're the last thread out. Cause a GC to occur. - // GC will also check is_active, so this check is not - // strictly needed. It's added here to make it clear that - // the GC will NOT be performed if any other caller - // of GC_locker::lock() still needs GC locked. - if (!is_active_internal()) { - _doing_gc = true; - { - // Must give up the lock while at a safepoint - MutexUnlocker munlock(JNICritical_lock); - if (PrintJNIGCStalls && PrintGCDetails) { - ResourceMark rm; // JavaThread::name() allocates to convert to UTF8 - gclog_or_tty->print_cr("%.3f: Thread \"%s\" is performing GC after exiting critical section, %d locked", - gclog_or_tty->time_stamp().seconds(), Thread::current()->name(), _jni_lock_count); - } - Universe::heap()->collect(GCCause::_gc_locker); + _doing_gc = true; + { + // Must give up the lock while at a safepoint + MutexUnlocker munlock(JNICritical_lock); + if (PrintJNIGCStalls && PrintGCDetails) { + ResourceMark rm; // JavaThread::name() allocates to convert to UTF8 + gclog_or_tty->print_cr("%.3f: Thread \"%s\" is performing GC after exiting critical section, %d locked", + gclog_or_tty->time_stamp().seconds(), Thread::current()->name(), _jni_lock_count); } - _doing_gc = false; + Universe::heap()->collect(GCCause::_gc_locker); } - + _doing_gc = false; _needs_gc = false; JNICritical_lock->notify_all(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/gcLocker.hpp --- a/src/share/vm/memory/gcLocker.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/gcLocker.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -54,8 +54,6 @@ // safepointing and decremented during the slow path of GC_locker // unlocking. static volatile jint _jni_lock_count; // number of jni active instances. - - static volatile jint _lock_count; // number of other active instances static volatile bool _needs_gc; // heap is filling, we need a GC // note: bool is typedef'd as jint static volatile bool _doing_gc; // unlock_critical() is doing a GC @@ -66,12 +64,6 @@ static volatile jint _debug_jni_lock_count; #endif - // Accessors - static bool is_jni_active() { - assert(_needs_gc, "only valid when _needs_gc is set"); - return _jni_lock_count > 0; - } - // At a safepoint, visit all threads and count the number of active // critical sections. This is used to ensure that all active // critical sections are exited before a new one is started. @@ -82,7 +74,7 @@ static bool is_active_internal() { verify_critical_count(); - return _lock_count > 0 || _jni_lock_count > 0; + return _jni_lock_count > 0; } public: @@ -132,10 +124,6 @@ // not a stable predicate. static void stall_until_clear(); - // Non-structured GC locking: currently needed for JNI. Use with care! - static void lock(); - static void unlock(); - // The following two methods are used for JNI critical regions. // If we find that we failed to perform a GC because the GC_locker // was active, arrange for one as soon as possible by allowing diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/gcLocker.inline.hpp --- a/src/share/vm/memory/gcLocker.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/gcLocker.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -27,22 +27,6 @@ #include "memory/gcLocker.hpp" -inline void GC_locker::lock() { - // cast away volatile - Atomic::inc(&_lock_count); - CHECK_UNHANDLED_OOPS_ONLY( - if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count++; }) - assert(Universe::heap() == NULL || - !Universe::heap()->is_gc_active(), "locking failed"); -} - -inline void GC_locker::unlock() { - // cast away volatile - Atomic::dec(&_lock_count); - CHECK_UNHANDLED_OOPS_ONLY( - if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count--; }) -} - inline void GC_locker::lock_critical(JavaThread* thread) { if (!thread->in_critical()) { if (needs_gc()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/genCollectedHeap.cpp --- a/src/share/vm/memory/genCollectedHeap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/genCollectedHeap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -374,7 +374,7 @@ ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy()); - const size_t metadata_prev_used = MetaspaceAux::allocated_used_bytes(); + const size_t metadata_prev_used = MetaspaceAux::used_bytes(); print_heap_before_gc(); @@ -809,7 +809,7 @@ bool GenCollectedHeap::is_in_young(oop p) { bool result = ((HeapWord*)p) < _gens[_n_gens - 1]->reserved().start(); assert(result == _gens[0]->is_in_reserved(p), - err_msg("incorrect test - result=%d, p=" PTR_FORMAT, result, (void*)p)); + err_msg("incorrect test - result=%d, p=" PTR_FORMAT, result, p2i((void*)p))); return result; } @@ -937,6 +937,16 @@ return result; } +size_t GenCollectedHeap::tlab_used(Thread* thr) const { + size_t result = 0; + for (int i = 0; i < _n_gens; i += 1) { + if (_gens[i]->supports_tlab_allocation()) { + result += _gens[i]->tlab_used(); + } + } + return result; +} + size_t GenCollectedHeap::unsafe_max_tlab_alloc(Thread* thr) const { size_t result = 0; for (int i = 0; i < _n_gens; i += 1) { @@ -1078,7 +1088,7 @@ for (int i = _n_gens-1; i >= 0; i--) { Generation* g = _gens[i]; if (!silent) { - gclog_or_tty->print(g->name()); + gclog_or_tty->print("%s", g->name()); gclog_or_tty->print(" "); } g->verify(); @@ -1281,7 +1291,7 @@ // back a time later than 'now'. jlong retVal = now - tolgc_cl.time(); if (retVal < 0) { - NOT_PRODUCT(warning("time warp: "INT64_FORMAT, retVal);) + NOT_PRODUCT(warning("time warp: "INT64_FORMAT, (int64_t) retVal);) return 0; } return retVal; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/genCollectedHeap.hpp --- a/src/share/vm/memory/genCollectedHeap.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/genCollectedHeap.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -256,6 +256,7 @@ // Section on TLAB's. virtual bool supports_tlab_allocation() const; virtual size_t tlab_capacity(Thread* thr) const; + virtual size_t tlab_used(Thread* thr) const; virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; virtual HeapWord* allocate_new_tlab(size_t size); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/genOopClosures.hpp --- a/src/share/vm/memory/genOopClosures.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/genOopClosures.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -199,7 +199,7 @@ protected: template inline void do_oop_work(T* p) { oop obj = oopDesc::load_decode_heap_oop(p); - guarantee(obj->is_oop_or_null(), err_msg("invalid oop: " INTPTR_FORMAT, (oopDesc*) obj)); + guarantee(obj->is_oop_or_null(), err_msg("invalid oop: " INTPTR_FORMAT, p2i((oopDesc*) obj))); } public: virtual void do_oop(oop* p); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/generation.cpp --- a/src/share/vm/memory/generation.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/generation.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -43,6 +43,8 @@ #include "utilities/copy.hpp" #include "utilities/events.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + Generation::Generation(ReservedSpace rs, size_t initial_size, int level) : _level(level), _ref_processor(NULL) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/generation.hpp --- a/src/share/vm/memory/generation.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/generation.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -299,6 +299,10 @@ guarantee(false, "Generation doesn't support thread local allocation buffers"); return 0; } + virtual size_t tlab_used() const { + guarantee(false, "Generation doesn't support thread local allocation buffers"); + return 0; + } virtual size_t unsafe_max_tlab_alloc() const { guarantee(false, "Generation doesn't support thread local allocation buffers"); return 0; @@ -418,7 +422,7 @@ // have to guard against non-monotonicity. NOT_PRODUCT( if (now < _time_of_last_gc) { - warning("time warp: "INT64_FORMAT" to "INT64_FORMAT, _time_of_last_gc, now); + warning("time warp: "INT64_FORMAT" to "INT64_FORMAT, (int64_t)_time_of_last_gc, (int64_t)now); } ) return _time_of_last_gc; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/heapInspection.cpp --- a/src/share/vm/memory/heapInspection.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/heapInspection.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -35,6 +35,8 @@ #include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" #endif // INCLUDE_ALL_GCS +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // HeapInspection int KlassInfoEntry::compare(KlassInfoEntry* e1, KlassInfoEntry* e2) { @@ -270,6 +272,7 @@ return true; } +PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL void KlassInfoHisto::print_title(outputStream* st, bool csv_format, bool selected[], int width_table[], const char *name_table[]) { @@ -282,7 +285,10 @@ } else { st->print("Index Super"); for (int c=0; cprint(str_fmt(width_table[c]), name_table[c]);} +PRAGMA_DIAG_POP } st->print(" ClassName"); } @@ -395,12 +401,18 @@ case KlassSizeStats::_index_inst_size: case KlassSizeStats::_index_inst_count: case KlassSizeStats::_index_method_count: +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL st->print(str_fmt(width_table[c]), "-"); +PRAGMA_DIAG_POP break; default: { double perc = (double)(100) * (double)(colsum_table[c]) / (double)sz_sum._total_bytes; +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL st->print(perc_fmt(width_table[c]), perc); +PRAGMA_DIAG_POP } } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/heapInspection.hpp --- a/src/share/vm/memory/heapInspection.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/heapInspection.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -295,6 +295,9 @@ // returns a format string to print a julong with the given width. E.g, // printf(num_fmt(6), julong(10)) would print out the number 10 with 4 // leading spaces. +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED + static void print_julong(outputStream* st, int width, julong n) { int num_spaces = width - julong_width(n); if (num_spaces > 0) { @@ -302,6 +305,7 @@ } st->print(JULONG_FORMAT, n); } +PRAGMA_DIAG_POP static char* perc_fmt(int width) { static char buf[32]; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metachunk.cpp --- a/src/share/vm/memory/metachunk.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/metachunk.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -28,6 +28,8 @@ #include "utilities/copy.hpp" #include "utilities/debug.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + class VirtualSpaceNode; const size_t metadata_chunk_initialize = 0xf7f7f7f7; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metachunk.hpp --- a/src/share/vm/memory/metachunk.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/metachunk.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -143,6 +143,8 @@ void set_is_tagged_free(bool v) { _is_tagged_free = v; } #endif + bool contains(const void* ptr) { return bottom() <= ptr && ptr < _top; } + NOT_PRODUCT(void mangle();) void print_on(outputStream* st) const; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspace.cpp --- a/src/share/vm/memory/metaspace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/metaspace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -32,7 +32,9 @@ #include "memory/gcLocker.hpp" #include "memory/metachunk.hpp" #include "memory/metaspace.hpp" +#include "memory/metaspaceGCThresholdUpdater.hpp" #include "memory/metaspaceShared.hpp" +#include "memory/metaspaceTracer.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "runtime/atomic.inline.hpp" @@ -46,8 +48,10 @@ #include "utilities/copy.hpp" #include "utilities/debug.hpp" -typedef BinaryTreeDictionary BlockTreeDictionary; -typedef BinaryTreeDictionary ChunkTreeDictionary; +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + +typedef BinaryTreeDictionary > BlockTreeDictionary; +typedef BinaryTreeDictionary > ChunkTreeDictionary; // Set this constant to enable slow integrity checking of the free chunk lists const bool metaspace_slow_verify = false; @@ -57,6 +61,7 @@ MetaWord* last_allocated = 0; size_t Metaspace::_compressed_class_space_size; +const MetaspaceTracer* Metaspace::_tracer = NULL; // Used in declarations in SpaceManager and ChunkManager enum ChunkIndex { @@ -182,6 +187,48 @@ // Remove from a list by size. Selects list based on size of chunk. Metachunk* free_chunks_get(size_t chunk_word_size); +#define index_bounds_check(index) \ + assert(index == SpecializedIndex || \ + index == SmallIndex || \ + index == MediumIndex || \ + index == HumongousIndex, err_msg("Bad index: %d", (int) index)) + + size_t num_free_chunks(ChunkIndex index) const { + index_bounds_check(index); + + if (index == HumongousIndex) { + return _humongous_dictionary.total_free_blocks(); + } + + ssize_t count = _free_chunks[index].count(); + return count == -1 ? 0 : (size_t) count; + } + + size_t size_free_chunks_in_bytes(ChunkIndex index) const { + index_bounds_check(index); + + size_t word_size = 0; + if (index == HumongousIndex) { + word_size = _humongous_dictionary.total_size(); + } else { + const size_t size_per_chunk_in_words = _free_chunks[index].size(); + word_size = size_per_chunk_in_words * num_free_chunks(index); + } + + return word_size * BytesPerWord; + } + + MetaspaceChunkFreeListSummary chunk_free_list_summary() const { + return MetaspaceChunkFreeListSummary(num_free_chunks(SpecializedIndex), + num_free_chunks(SmallIndex), + num_free_chunks(MediumIndex), + num_free_chunks(HumongousIndex), + size_free_chunks_in_bytes(SpecializedIndex), + size_free_chunks_in_bytes(SmallIndex), + size_free_chunks_in_bytes(MediumIndex), + size_free_chunks_in_bytes(HumongousIndex)); + } + // Debug support void verify(); void slow_verify() { @@ -269,6 +316,8 @@ MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); } MetaWord* end() const { return (MetaWord*) _virtual_space.high(); } + bool contains(const void* ptr) { return ptr >= low() && ptr < high(); } + size_t reserved_words() const { return _virtual_space.reserved_size() / BytesPerWord; } size_t committed_words() const { return _virtual_space.actual_committed_size() / BytesPerWord; } @@ -510,11 +559,11 @@ void inc_virtual_space_count(); void dec_virtual_space_count(); + bool contains(const void* ptr); + // Unlink empty VirtualSpaceNodes and free it. void purge(ChunkManager* chunk_manager); - bool contains(const void *ptr); - void print_on(outputStream* st) const; class VirtualSpaceListIterator : public StackObj { @@ -558,7 +607,7 @@ private: - // protects allocations and contains. + // protects allocations Mutex* const _lock; // Type of metadata allocated. @@ -595,7 +644,9 @@ private: // Accessors Metachunk* chunks_in_use(ChunkIndex index) const { return _chunks_in_use[index]; } - void set_chunks_in_use(ChunkIndex index, Metachunk* v) { _chunks_in_use[index] = v; } + void set_chunks_in_use(ChunkIndex index, Metachunk* v) { + _chunks_in_use[index] = v; + } BlockFreelist* block_freelists() const { return (BlockFreelist*) &_block_freelists; @@ -786,7 +837,7 @@ return NULL; } - if (word_size < TreeChunk::min_size()) { + if (word_size < TreeChunk >::min_size()) { // Dark matter. Too small for dictionary. return NULL; } @@ -806,7 +857,7 @@ MetaWord* new_block = (MetaWord*)free_block; assert(block_size >= word_size, "Incorrect size of block from freelist"); const size_t unused = block_size - word_size; - if (unused >= TreeChunk::min_size()) { + if (unused >= TreeChunk >::min_size()) { return_block(new_block + word_size, unused); } @@ -1027,6 +1078,7 @@ // nodes with a 0 container_count. Remove Metachunks in // the node from their respective freelists. void VirtualSpaceList::purge(ChunkManager* chunk_manager) { + assert(SafepointSynchronize::is_at_safepoint(), "must be called at safepoint for contains to work"); assert_lock_strong(SpaceManager::expand_lock()); // Don't use a VirtualSpaceListIterator because this // list is being changed and a straightforward use of an iterator is not safe. @@ -1060,8 +1112,8 @@ } #ifdef ASSERT if (purged_vsl != NULL) { - // List should be stable enough to use an iterator here. - VirtualSpaceListIterator iter(virtual_space_list()); + // List should be stable enough to use an iterator here. + VirtualSpaceListIterator iter(virtual_space_list()); while (iter.repeat()) { VirtualSpaceNode* vsl = iter.get_next(); assert(vsl != purged_vsl, "Purge of vsl failed"); @@ -1070,6 +1122,23 @@ #endif } + +// This function looks at the mmap regions in the metaspace without locking. +// The chunks are added with store ordering and not deleted except for at +// unloading time during a safepoint. +bool VirtualSpaceList::contains(const void* ptr) { + // List should be stable enough to use an iterator here because removing virtual + // space nodes is only allowed at a safepoint. + VirtualSpaceListIterator iter(virtual_space_list()); + while (iter.repeat()) { + VirtualSpaceNode* vsn = iter.get_next(); + if (vsn->contains(ptr)) { + return true; + } + } + return false; +} + void VirtualSpaceList::retire_current_virtual_space() { assert_lock_strong(SpaceManager::expand_lock()); @@ -1287,19 +1356,6 @@ } } -bool VirtualSpaceList::contains(const void *ptr) { - VirtualSpaceNode* list = virtual_space_list(); - VirtualSpaceListIterator iter(list); - while (iter.repeat()) { - VirtualSpaceNode* node = iter.get_next(); - if (node->reserved()->contains(ptr)) { - return true; - } - } - return false; -} - - // MetaspaceGC methods // VM_CollectForMetadataAllocation is the vm operation used to GC. @@ -1367,6 +1423,17 @@ return (size_t)Atomic::add_ptr(-(intptr_t)v, &_capacity_until_GC); } +void MetaspaceGC::initialize() { + // Set the high-water mark to MaxMetapaceSize during VM initializaton since + // we can't do a GC during initialization. + _capacity_until_GC = MaxMetaspaceSize; +} + +void MetaspaceGC::post_initialize() { + // Reset the high-water mark once the VM initialization is done. + _capacity_until_GC = MAX2(MetaspaceAux::committed_bytes(), MetaspaceSize); +} + bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { // Check if the compressed class space is full. if (is_class && Metaspace::using_class_space()) { @@ -1387,21 +1454,13 @@ size_t MetaspaceGC::allowed_expansion() { size_t committed_bytes = MetaspaceAux::committed_bytes(); + size_t capacity_until_gc = capacity_until_GC(); + + assert(capacity_until_gc >= committed_bytes, + err_msg("capacity_until_gc: " SIZE_FORMAT " < committed_bytes: " SIZE_FORMAT, + capacity_until_gc, committed_bytes)); size_t left_until_max = MaxMetaspaceSize - committed_bytes; - - // Always grant expansion if we are initiating the JVM, - // or if the GC_locker is preventing GCs. - if (!is_init_completed() || GC_locker::is_active_and_needs_gc()) { - return left_until_max / BytesPerWord; - } - - size_t capacity_until_gc = capacity_until_GC(); - - if (capacity_until_gc <= committed_bytes) { - return 0; - } - size_t left_until_GC = capacity_until_gc - committed_bytes; size_t left_to_commit = MIN2(left_until_GC, left_until_max); @@ -1413,7 +1472,15 @@ uint current_shrink_factor = _shrink_factor; _shrink_factor = 0; - const size_t used_after_gc = MetaspaceAux::allocated_capacity_bytes(); + // Using committed_bytes() for used_after_gc is an overestimation, since the + // chunk free lists are included in committed_bytes() and the memory in an + // un-fragmented chunk free list is available for future allocations. + // However, if the chunk free lists becomes fragmented, then the memory may + // not be available for future allocations and the memory is therefore "in use". + // Including the chunk free lists in the definition of "in use" is therefore + // necessary. Not including the chunk free lists can cause capacity_until_GC to + // shrink below committed_bytes() and this has caused serious bugs in the past. + const size_t used_after_gc = MetaspaceAux::committed_bytes(); const size_t capacity_until_GC = MetaspaceGC::capacity_until_GC(); const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0; @@ -1447,19 +1514,21 @@ expand_bytes = align_size_up(expand_bytes, Metaspace::commit_alignment()); // Don't expand unless it's significant if (expand_bytes >= MinMetaspaceExpansion) { - MetaspaceGC::inc_capacity_until_GC(expand_bytes); - } - if (PrintGCDetails && Verbose) { - size_t new_capacity_until_GC = capacity_until_GC; - gclog_or_tty->print_cr(" expanding:" - " minimum_desired_capacity: %6.1fKB" - " expand_bytes: %6.1fKB" - " MinMetaspaceExpansion: %6.1fKB" - " new metaspace HWM: %6.1fKB", - minimum_desired_capacity / (double) K, - expand_bytes / (double) K, - MinMetaspaceExpansion / (double) K, - new_capacity_until_GC / (double) K); + size_t new_capacity_until_GC = MetaspaceGC::inc_capacity_until_GC(expand_bytes); + Metaspace::tracer()->report_gc_threshold(capacity_until_GC, + new_capacity_until_GC, + MetaspaceGCThresholdUpdater::ComputeNewSize); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr(" expanding:" + " minimum_desired_capacity: %6.1fKB" + " expand_bytes: %6.1fKB" + " MinMetaspaceExpansion: %6.1fKB" + " new metaspace HWM: %6.1fKB", + minimum_desired_capacity / (double) K, + expand_bytes / (double) K, + MinMetaspaceExpansion / (double) K, + new_capacity_until_GC / (double) K); + } } return; } @@ -1538,7 +1607,10 @@ // Don't shrink unless it's significant if (shrink_bytes >= MinMetaspaceExpansion && ((capacity_until_GC - shrink_bytes) >= MetaspaceSize)) { - MetaspaceGC::dec_capacity_until_GC(shrink_bytes); + size_t new_capacity_until_GC = MetaspaceGC::dec_capacity_until_GC(shrink_bytes); + Metaspace::tracer()->report_gc_threshold(capacity_until_GC, + new_capacity_until_GC, + MetaspaceGCThresholdUpdater::ComputeNewSize); } } @@ -1921,7 +1993,7 @@ st->print_cr(" free " SIZE_FORMAT, chunk->free_word_size()); } else { - st->print_cr(""); + st->cr(); } } @@ -2205,7 +2277,7 @@ humongous_chunks = next_humongous_chunks; } if (TraceMetadataChunkAllocation && Verbose) { - gclog_or_tty->print_cr(""); + gclog_or_tty->cr(); gclog_or_tty->print_cr("updated dictionary count %d %s", chunk_manager()->humongous_dictionary()->total_count(), chunk_size_name(HumongousIndex)); @@ -2250,7 +2322,7 @@ void SpaceManager::deallocate(MetaWord* p, size_t word_size) { assert_lock_strong(_lock); size_t raw_word_size = get_raw_word_size(word_size); - size_t min_size = TreeChunk::min_size(); + size_t min_size = TreeChunk >::min_size(); assert(raw_word_size >= min_size, err_msg("Should not deallocate dark matter " SIZE_FORMAT "<" SIZE_FORMAT, word_size, min_size)); block_freelists()->return_block(p, raw_word_size); @@ -2306,7 +2378,7 @@ void SpaceManager::retire_current_chunk() { if (current_chunk() != NULL) { size_t remaining_words = current_chunk()->free_word_size(); - if (remaining_words >= TreeChunk::min_size()) { + if (remaining_words >= TreeChunk >::min_size()) { block_freelists()->return_block(current_chunk()->allocate(remaining_words), remaining_words); inc_used_metrics(remaining_words); } @@ -2483,8 +2555,8 @@ // MetaspaceAux -size_t MetaspaceAux::_allocated_capacity_words[] = {0, 0}; -size_t MetaspaceAux::_allocated_used_words[] = {0, 0}; +size_t MetaspaceAux::_capacity_words[] = {0, 0}; +size_t MetaspaceAux::_used_words[] = {0, 0}; size_t MetaspaceAux::free_bytes(Metaspace::MetadataType mdtype) { VirtualSpaceList* list = Metaspace::get_space_list(mdtype); @@ -2497,38 +2569,38 @@ void MetaspaceAux::dec_capacity(Metaspace::MetadataType mdtype, size_t words) { assert_lock_strong(SpaceManager::expand_lock()); - assert(words <= allocated_capacity_words(mdtype), + assert(words <= capacity_words(mdtype), err_msg("About to decrement below 0: words " SIZE_FORMAT - " is greater than _allocated_capacity_words[%u] " SIZE_FORMAT, - words, mdtype, allocated_capacity_words(mdtype))); - _allocated_capacity_words[mdtype] -= words; + " is greater than _capacity_words[%u] " SIZE_FORMAT, + words, mdtype, capacity_words(mdtype))); + _capacity_words[mdtype] -= words; } void MetaspaceAux::inc_capacity(Metaspace::MetadataType mdtype, size_t words) { assert_lock_strong(SpaceManager::expand_lock()); // Needs to be atomic - _allocated_capacity_words[mdtype] += words; + _capacity_words[mdtype] += words; } void MetaspaceAux::dec_used(Metaspace::MetadataType mdtype, size_t words) { - assert(words <= allocated_used_words(mdtype), + assert(words <= used_words(mdtype), err_msg("About to decrement below 0: words " SIZE_FORMAT - " is greater than _allocated_used_words[%u] " SIZE_FORMAT, - words, mdtype, allocated_used_words(mdtype))); + " is greater than _used_words[%u] " SIZE_FORMAT, + words, mdtype, used_words(mdtype))); // For CMS deallocation of the Metaspaces occurs during the // sweep which is a concurrent phase. Protection by the expand_lock() // is not enough since allocation is on a per Metaspace basis // and protected by the Metaspace lock. jlong minus_words = (jlong) - (jlong) words; - Atomic::add_ptr(minus_words, &_allocated_used_words[mdtype]); + Atomic::add_ptr(minus_words, &_used_words[mdtype]); } void MetaspaceAux::inc_used(Metaspace::MetadataType mdtype, size_t words) { - // _allocated_used_words tracks allocations for + // _used_words tracks allocations for // each piece of metadata. Those allocations are // generally done concurrently by different application // threads so must be done atomically. - Atomic::add_ptr(words, &_allocated_used_words[mdtype]); + Atomic::add_ptr(words, &_used_words[mdtype]); } size_t MetaspaceAux::used_bytes_slow(Metaspace::MetadataType mdtype) { @@ -2575,16 +2647,16 @@ size_t MetaspaceAux::capacity_bytes_slow() { #ifdef PRODUCT - // Use allocated_capacity_bytes() in PRODUCT instead of this function. + // Use capacity_bytes() in PRODUCT instead of this function. guarantee(false, "Should not call capacity_bytes_slow() in the PRODUCT"); #endif size_t class_capacity = capacity_bytes_slow(Metaspace::ClassType); size_t non_class_capacity = capacity_bytes_slow(Metaspace::NonClassType); - assert(allocated_capacity_bytes() == class_capacity + non_class_capacity, - err_msg("bad accounting: allocated_capacity_bytes() " SIZE_FORMAT + assert(capacity_bytes() == class_capacity + non_class_capacity, + err_msg("bad accounting: capacity_bytes() " SIZE_FORMAT " class_capacity + non_class_capacity " SIZE_FORMAT " class_capacity " SIZE_FORMAT " non_class_capacity " SIZE_FORMAT, - allocated_capacity_bytes(), class_capacity + non_class_capacity, + capacity_bytes(), class_capacity + non_class_capacity, class_capacity, non_class_capacity)); return class_capacity + non_class_capacity; @@ -2624,6 +2696,19 @@ return free_chunks_total_words() * BytesPerWord; } +bool MetaspaceAux::has_chunk_free_list(Metaspace::MetadataType mdtype) { + return Metaspace::get_chunk_manager(mdtype) != NULL; +} + +MetaspaceChunkFreeListSummary MetaspaceAux::chunk_free_list_summary(Metaspace::MetadataType mdtype) { + if (!has_chunk_free_list(mdtype)) { + return MetaspaceChunkFreeListSummary(); + } + + const ChunkManager* cm = Metaspace::get_chunk_manager(mdtype); + return cm->chunk_free_list_summary(); +} + void MetaspaceAux::print_metaspace_change(size_t prev_metadata_used) { gclog_or_tty->print(", [Metaspace:"); if (PrintGCDetails && Verbose) { @@ -2631,14 +2716,14 @@ "->" SIZE_FORMAT "(" SIZE_FORMAT ")", prev_metadata_used, - allocated_used_bytes(), + used_bytes(), reserved_bytes()); } else { gclog_or_tty->print(" " SIZE_FORMAT "K" "->" SIZE_FORMAT "K" "(" SIZE_FORMAT "K)", prev_metadata_used/K, - allocated_used_bytes()/K, + used_bytes()/K, reserved_bytes()/K); } @@ -2654,8 +2739,8 @@ "capacity " SIZE_FORMAT "K, " "committed " SIZE_FORMAT "K, " "reserved " SIZE_FORMAT "K", - allocated_used_bytes()/K, - allocated_capacity_bytes()/K, + used_bytes()/K, + capacity_bytes()/K, committed_bytes()/K, reserved_bytes()/K); @@ -2666,8 +2751,8 @@ "capacity " SIZE_FORMAT "K, " "committed " SIZE_FORMAT "K, " "reserved " SIZE_FORMAT "K", - allocated_used_bytes(ct)/K, - allocated_capacity_bytes(ct)/K, + used_bytes(ct)/K, + capacity_bytes(ct)/K, committed_bytes(ct)/K, reserved_bytes(ct)/K); } @@ -2769,42 +2854,42 @@ void MetaspaceAux::verify_capacity() { #ifdef ASSERT - size_t running_sum_capacity_bytes = allocated_capacity_bytes(); + size_t running_sum_capacity_bytes = capacity_bytes(); // For purposes of the running sum of capacity, verify against capacity size_t capacity_in_use_bytes = capacity_bytes_slow(); assert(running_sum_capacity_bytes == capacity_in_use_bytes, - err_msg("allocated_capacity_words() * BytesPerWord " SIZE_FORMAT + err_msg("capacity_words() * BytesPerWord " SIZE_FORMAT " capacity_bytes_slow()" SIZE_FORMAT, running_sum_capacity_bytes, capacity_in_use_bytes)); for (Metaspace::MetadataType i = Metaspace::ClassType; i < Metaspace:: MetadataTypeCount; i = (Metaspace::MetadataType)(i + 1)) { size_t capacity_in_use_bytes = capacity_bytes_slow(i); - assert(allocated_capacity_bytes(i) == capacity_in_use_bytes, - err_msg("allocated_capacity_bytes(%u) " SIZE_FORMAT + assert(capacity_bytes(i) == capacity_in_use_bytes, + err_msg("capacity_bytes(%u) " SIZE_FORMAT " capacity_bytes_slow(%u)" SIZE_FORMAT, - i, allocated_capacity_bytes(i), i, capacity_in_use_bytes)); + i, capacity_bytes(i), i, capacity_in_use_bytes)); } #endif } void MetaspaceAux::verify_used() { #ifdef ASSERT - size_t running_sum_used_bytes = allocated_used_bytes(); + size_t running_sum_used_bytes = used_bytes(); // For purposes of the running sum of used, verify against used size_t used_in_use_bytes = used_bytes_slow(); - assert(allocated_used_bytes() == used_in_use_bytes, - err_msg("allocated_used_bytes() " SIZE_FORMAT + assert(used_bytes() == used_in_use_bytes, + err_msg("used_bytes() " SIZE_FORMAT " used_bytes_slow()" SIZE_FORMAT, - allocated_used_bytes(), used_in_use_bytes)); + used_bytes(), used_in_use_bytes)); for (Metaspace::MetadataType i = Metaspace::ClassType; i < Metaspace:: MetadataTypeCount; i = (Metaspace::MetadataType)(i + 1)) { size_t used_in_use_bytes = used_bytes_slow(i); - assert(allocated_used_bytes(i) == used_in_use_bytes, - err_msg("allocated_used_bytes(%u) " SIZE_FORMAT + assert(used_bytes(i) == used_in_use_bytes, + err_msg("used_bytes(%u) " SIZE_FORMAT " used_bytes_slow(%u)" SIZE_FORMAT, - i, allocated_used_bytes(i), i, used_in_use_bytes)); + i, used_bytes(i), i, used_in_use_bytes)); } #endif } @@ -3019,6 +3104,8 @@ } void Metaspace::global_initialize() { + MetaspaceGC::initialize(); + // Initialize the alignment for shared spaces. int max_alignment = os::vm_page_size(); size_t cds_total = 0; @@ -3126,7 +3213,11 @@ } } - MetaspaceGC::initialize(); + _tracer = new MetaspaceTracer(); +} + +void Metaspace::post_initialize() { + MetaspaceGC::post_initialize(); } Metachunk* Metaspace::get_initialization_chunk(MetadataType mdtype, @@ -3215,8 +3306,12 @@ assert(delta_bytes > 0, "Must be"); size_t after_inc = MetaspaceGC::inc_capacity_until_GC(delta_bytes); + + // capacity_until_GC might be updated concurrently, must calculate previous value. size_t before_inc = after_inc - delta_bytes; + tracer()->report_gc_threshold(before_inc, after_inc, + MetaspaceGCThresholdUpdater::ExpandAndAllocate); if (PrintGCDetails && Verbose) { gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT " to " SIZE_FORMAT, before_inc, after_inc); @@ -3274,7 +3369,7 @@ assert(Thread::current()->is_VM_thread(), "should be the VM thread"); // Don't take Heap_lock MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); - if (word_size < TreeChunk::min_size()) { + if (word_size < TreeChunk >::min_size()) { // Dark matter. Too small for dictionary. #ifdef ASSERT Copy::fill_to_words((HeapWord*)ptr, word_size, 0xf5f5f5f5); @@ -3289,7 +3384,7 @@ } else { MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); - if (word_size < TreeChunk::min_size()) { + if (word_size < TreeChunk >::min_size()) { // Dark matter. Too small for dictionary. #ifdef ASSERT Copy::fill_to_words((HeapWord*)ptr, word_size, 0xf5f5f5f5); @@ -3340,6 +3435,8 @@ MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype); if (result == NULL) { + tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype); + // Allocation failed. if (is_init_completed()) { // Only start a GC if the bootstrapping has completed. @@ -3351,7 +3448,7 @@ } if (result == NULL) { - report_metadata_oome(loader_data, word_size, mdtype, CHECK_NULL); + report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL); } // Zero initialize. @@ -3365,7 +3462,9 @@ return class_vsm()->calc_chunk_size(word_size); } -void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_size, MetadataType mdtype, TRAPS) { +void Metaspace::report_metadata_oome(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type, MetadataType mdtype, TRAPS) { + tracer()->report_metadata_oom(loader_data, word_size, type, mdtype); + // If result is still null, we are out of memory. if (Verbose && TraceMetadataChunkAllocation) { gclog_or_tty->print_cr("Metaspace allocation failed for size " @@ -3408,6 +3507,16 @@ } } +const char* Metaspace::metadata_type_name(Metaspace::MetadataType mdtype) { + switch (mdtype) { + case Metaspace::ClassType: return "Class"; + case Metaspace::NonClassType: return "Metadata"; + default: + assert(false, err_msg("Got bad mdtype: %d", (int) mdtype)); + return NULL; + } +} + void Metaspace::record_allocation(void* ptr, MetaspaceObj::Type type, size_t word_size) { assert(DumpSharedSpaces, "sanity"); @@ -3463,17 +3572,16 @@ } } -bool Metaspace::contains(const void * ptr) { - if (MetaspaceShared::is_in_shared_space(ptr)) { +bool Metaspace::contains(const void* ptr) { + if (UseSharedSpaces && MetaspaceShared::is_in_shared_space(ptr)) { return true; } - // This is checked while unlocked. As long as the virtualspaces are added - // at the end, the pointer will be in one of them. The virtual spaces - // aren't deleted presently. When they are, some sort of locking might - // be needed. Note, locking this can cause inversion problems with the - // caller in MetaspaceObj::is_metadata() function. - return space_list()->contains(ptr) || - (using_class_space() && class_space_list()->contains(ptr)); + + if (using_class_space() && get_space_list(ClassType)->contains(ptr)) { + return true; + } + + return get_space_list(NonClassType)->contains(ptr); } void Metaspace::verify() { @@ -3718,5 +3826,4 @@ TestVirtualSpaceNodeTest::test(); TestVirtualSpaceNodeTest::test_is_available(); } - #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspace.hpp --- a/src/share/vm/memory/metaspace.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/metaspace.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -26,6 +26,7 @@ #include "memory/allocation.hpp" #include "memory/memRegion.hpp" +#include "memory/metaspaceChunkFreeListSummary.hpp" #include "runtime/virtualspace.hpp" #include "utilities/exceptions.hpp" @@ -60,6 +61,7 @@ class ClassLoaderData; class Metablock; class Metachunk; +class MetaspaceTracer; class MetaWord; class Mutex; class outputStream; @@ -120,6 +122,7 @@ static size_t compressed_class_space_size() { return _compressed_class_space_size; } + static void set_compressed_class_space_size(size_t size) { _compressed_class_space_size = size; } @@ -148,6 +151,8 @@ static ChunkManager* _chunk_manager_metadata; static ChunkManager* _chunk_manager_class; + static const MetaspaceTracer* _tracer; + public: static VirtualSpaceList* space_list() { return _space_list; } static VirtualSpaceList* class_space_list() { return _class_space_list; } @@ -163,6 +168,8 @@ return mdtype == ClassType ? chunk_manager_class() : chunk_manager_metadata(); } + static const MetaspaceTracer* tracer() { return _tracer; } + private: // This is used by DumpSharedSpaces only, where only _vsm is used. So we will // maintain a single list for now. @@ -201,6 +208,7 @@ static void ergo_initialize(); static void global_initialize(); + static void post_initialize(); static size_t first_chunk_word_size() { return _first_chunk_word_size; } static size_t first_class_chunk_word_size() { return _first_class_chunk_word_size; } @@ -225,7 +233,8 @@ MetaWord* expand_and_allocate(size_t size, MetadataType mdtype); - static bool contains(const void *ptr); + static bool contains(const void* ptr); + void dump(outputStream* const out) const; // Free empty virtualspaces @@ -233,7 +242,9 @@ static void purge(); static void report_metadata_oome(ClassLoaderData* loader_data, size_t word_size, - MetadataType mdtype, TRAPS); + MetaspaceObj::Type type, MetadataType mdtype, TRAPS); + + static const char* metadata_type_name(Metaspace::MetadataType mdtype); void print_on(outputStream* st) const; // Debugging support @@ -271,11 +282,11 @@ // allocated to a Metaspace. This is used instead of // iterating over all the classloaders. One for each // type of Metadata - static size_t _allocated_capacity_words[Metaspace:: MetadataTypeCount]; - // Running sum of space in all Metachunks that have + static size_t _capacity_words[Metaspace:: MetadataTypeCount]; + // Running sum of space in all Metachunks that // are being used for metadata. One for each // type of Metadata. - static size_t _allocated_used_words[Metaspace:: MetadataTypeCount]; + static size_t _used_words[Metaspace:: MetadataTypeCount]; public: // Decrement and increment _allocated_capacity_words @@ -299,32 +310,32 @@ static size_t free_chunks_total_bytes(); static size_t free_chunks_total_bytes(Metaspace::MetadataType mdtype); - static size_t allocated_capacity_words(Metaspace::MetadataType mdtype) { - return _allocated_capacity_words[mdtype]; + static size_t capacity_words(Metaspace::MetadataType mdtype) { + return _capacity_words[mdtype]; } - static size_t allocated_capacity_words() { - return allocated_capacity_words(Metaspace::NonClassType) + - allocated_capacity_words(Metaspace::ClassType); + static size_t capacity_words() { + return capacity_words(Metaspace::NonClassType) + + capacity_words(Metaspace::ClassType); } - static size_t allocated_capacity_bytes(Metaspace::MetadataType mdtype) { - return allocated_capacity_words(mdtype) * BytesPerWord; + static size_t capacity_bytes(Metaspace::MetadataType mdtype) { + return capacity_words(mdtype) * BytesPerWord; } - static size_t allocated_capacity_bytes() { - return allocated_capacity_words() * BytesPerWord; + static size_t capacity_bytes() { + return capacity_words() * BytesPerWord; } - static size_t allocated_used_words(Metaspace::MetadataType mdtype) { - return _allocated_used_words[mdtype]; + static size_t used_words(Metaspace::MetadataType mdtype) { + return _used_words[mdtype]; } - static size_t allocated_used_words() { - return allocated_used_words(Metaspace::NonClassType) + - allocated_used_words(Metaspace::ClassType); + static size_t used_words() { + return used_words(Metaspace::NonClassType) + + used_words(Metaspace::ClassType); } - static size_t allocated_used_bytes(Metaspace::MetadataType mdtype) { - return allocated_used_words(mdtype) * BytesPerWord; + static size_t used_bytes(Metaspace::MetadataType mdtype) { + return used_words(mdtype) * BytesPerWord; } - static size_t allocated_used_bytes() { - return allocated_used_words() * BytesPerWord; + static size_t used_bytes() { + return used_words() * BytesPerWord; } static size_t free_bytes(); @@ -347,6 +358,9 @@ return min_chunk_size_words() * BytesPerWord; } + static bool has_chunk_free_list(Metaspace::MetadataType mdtype); + static MetaspaceChunkFreeListSummary chunk_free_list_summary(Metaspace::MetadataType mdtype); + // Print change in used metadata. static void print_metaspace_change(size_t prev_metadata_used); static void print_on(outputStream * out); @@ -385,7 +399,8 @@ public: - static void initialize() { _capacity_until_GC = MetaspaceSize; } + static void initialize(); + static void post_initialize(); static size_t capacity_until_GC(); static size_t inc_capacity_until_GC(size_t v); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspaceChunkFreeListSummary.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/memory/metaspaceChunkFreeListSummary.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 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 + * 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_MEMORY_METASPACE_CHUNK_FREE_LIST_SUMMARY_HPP +#define SHARE_VM_MEMORY_METASPACE_CHUNK_FREE_LIST_SUMMARY_HPP + +#include "memory/allocation.hpp" + +class MetaspaceChunkFreeListSummary VALUE_OBJ_CLASS_SPEC { + size_t _num_specialized_chunks; + size_t _num_small_chunks; + size_t _num_medium_chunks; + size_t _num_humongous_chunks; + + size_t _specialized_chunks_size_in_bytes; + size_t _small_chunks_size_in_bytes; + size_t _medium_chunks_size_in_bytes; + size_t _humongous_chunks_size_in_bytes; + + public: + MetaspaceChunkFreeListSummary() : + _num_specialized_chunks(0), + _num_small_chunks(0), + _num_medium_chunks(0), + _num_humongous_chunks(0), + _specialized_chunks_size_in_bytes(0), + _small_chunks_size_in_bytes(0), + _medium_chunks_size_in_bytes(0), + _humongous_chunks_size_in_bytes(0) + {} + + MetaspaceChunkFreeListSummary(size_t num_specialized_chunks, + size_t num_small_chunks, + size_t num_medium_chunks, + size_t num_humongous_chunks, + size_t specialized_chunks_size_in_bytes, + size_t small_chunks_size_in_bytes, + size_t medium_chunks_size_in_bytes, + size_t humongous_chunks_size_in_bytes) : + _num_specialized_chunks(num_specialized_chunks), + _num_small_chunks(num_small_chunks), + _num_medium_chunks(num_medium_chunks), + _num_humongous_chunks(num_humongous_chunks), + _specialized_chunks_size_in_bytes(specialized_chunks_size_in_bytes), + _small_chunks_size_in_bytes(small_chunks_size_in_bytes), + _medium_chunks_size_in_bytes(medium_chunks_size_in_bytes), + _humongous_chunks_size_in_bytes(humongous_chunks_size_in_bytes) + {} + + size_t num_specialized_chunks() const { + return _num_specialized_chunks; + } + + size_t num_small_chunks() const { + return _num_small_chunks; + } + + size_t num_medium_chunks() const { + return _num_medium_chunks; + } + + size_t num_humongous_chunks() const { + return _num_humongous_chunks; + } + + size_t specialized_chunks_size_in_bytes() const { + return _specialized_chunks_size_in_bytes; + } + + size_t small_chunks_size_in_bytes() const { + return _small_chunks_size_in_bytes; + } + + size_t medium_chunks_size_in_bytes() const { + return _medium_chunks_size_in_bytes; + } + + size_t humongous_chunks_size_in_bytes() const { + return _humongous_chunks_size_in_bytes; + } +}; + +#endif // SHARE_VM_MEMORY_METASPACE_CHUNK_FREE_LIST_SUMMARY_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspaceCounters.cpp --- a/src/share/vm/memory/metaspaceCounters.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/metaspaceCounters.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -66,7 +66,7 @@ MetaspacePerfCounters* MetaspaceCounters::_perf_counters = NULL; size_t MetaspaceCounters::used() { - return MetaspaceAux::allocated_used_bytes(); + return MetaspaceAux::used_bytes(); } size_t MetaspaceCounters::capacity() { @@ -98,7 +98,7 @@ MetaspacePerfCounters* CompressedClassSpaceCounters::_perf_counters = NULL; size_t CompressedClassSpaceCounters::used() { - return MetaspaceAux::allocated_used_bytes(Metaspace::ClassType); + return MetaspaceAux::used_bytes(Metaspace::ClassType); } size_t CompressedClassSpaceCounters::capacity() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspaceGCThresholdUpdater.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/memory/metaspaceGCThresholdUpdater.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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 + * 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_MEMORY_METASPACE_GC_THRESHOLD_UPDATER_HPP +#define SHARE_VM_MEMORY_METASPACE_GC_THRESHOLD_UPDATER_HPP + +#include "memory/allocation.hpp" +#include "utilities/debug.hpp" + +class MetaspaceGCThresholdUpdater : public AllStatic { + public: + enum Type { + ComputeNewSize, + ExpandAndAllocate, + Last + }; + + static const char* to_string(MetaspaceGCThresholdUpdater::Type updater) { + switch (updater) { + case ComputeNewSize: + return "compute_new_size"; + case ExpandAndAllocate: + return "expand_and_allocate"; + default: + assert(false, err_msg("Got bad updater: %d", (int) updater)); + return NULL; + }; + } +}; + +#endif // SHARE_VM_MEMORY_METASPACE_GC_THRESHOLD_UPDATER_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspaceShared.cpp --- a/src/share/vm/memory/metaspaceShared.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/metaspaceShared.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -40,6 +40,7 @@ #include "runtime/vmThread.hpp" #include "utilities/hashtable.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC int MetaspaceShared::_max_alignment = 0; @@ -337,13 +338,14 @@ int all_rw_count = 0; int all_rw_bytes = 0; - const char *fmt = "%-20s: %8d %10d %5.1f | %8d %10d %5.1f | %8d %10d %5.1f"; +// To make fmt_stats be a syntactic constant (for format warnings), use #define. +#define fmt_stats "%-20s: %8d %10d %5.1f | %8d %10d %5.1f | %8d %10d %5.1f" const char *sep = "--------------------+---------------------------+---------------------------+--------------------------"; const char *hdr = " ro_cnt ro_bytes % | rw_cnt rw_bytes % | all_cnt all_bytes %"; tty->print_cr("Detailed metadata info (rw includes md and mc):"); - tty->print_cr(hdr); - tty->print_cr(sep); + tty->print_cr("%s", hdr); + tty->print_cr("%s", sep); for (int type = 0; type < int(_number_of_types); type ++) { const char *name = type_name((Type)type); int ro_count = _counts[RO][type]; @@ -357,7 +359,7 @@ double rw_perc = 100.0 * double(rw_bytes) / double(rw_all); double perc = 100.0 * double(bytes) / double(ro_all + rw_all); - tty->print_cr(fmt, name, + tty->print_cr(fmt_stats, name, ro_count, ro_bytes, ro_perc, rw_count, rw_bytes, rw_perc, count, bytes, perc); @@ -375,14 +377,15 @@ double all_rw_perc = 100.0 * double(all_rw_bytes) / double(rw_all); double all_perc = 100.0 * double(all_bytes) / double(ro_all + rw_all); - tty->print_cr(sep); - tty->print_cr(fmt, "Total", + tty->print_cr("%s", sep); + tty->print_cr(fmt_stats, "Total", all_ro_count, all_ro_bytes, all_ro_perc, all_rw_count, all_rw_bytes, all_rw_perc, all_count, all_bytes, all_perc); assert(all_ro_bytes == ro_all, "everything should have been counted"); assert(all_rw_bytes == rw_all, "everything should have been counted"); +#undef fmt_stats } // Populate the shared space. @@ -514,7 +517,8 @@ md_top = wc.get_top(); // Print shared spaces all the time - const char* fmt = "%s space: %9d [ %4.1f%% of total] out of %9d bytes [%4.1f%% used] at " PTR_FORMAT; +// To make fmt_space be a syntactic constant (for format warnings), use #define. +#define fmt_space "%s space: %9d [ %4.1f%% of total] out of %9d bytes [%4.1f%% used] at " PTR_FORMAT Metaspace* ro_space = _loader_data->ro_metaspace(); Metaspace* rw_space = _loader_data->rw_metaspace(); @@ -545,10 +549,10 @@ const double mc_u_perc = mc_bytes / double(mc_alloced) * 100.0; const double total_u_perc = total_bytes / double(total_alloced) * 100.0; - tty->print_cr(fmt, "ro", ro_bytes, ro_t_perc, ro_alloced, ro_u_perc, ro_space->bottom()); - tty->print_cr(fmt, "rw", rw_bytes, rw_t_perc, rw_alloced, rw_u_perc, rw_space->bottom()); - tty->print_cr(fmt, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, md_low); - tty->print_cr(fmt, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, mc_low); + tty->print_cr(fmt_space, "ro", ro_bytes, ro_t_perc, ro_alloced, ro_u_perc, ro_space->bottom()); + tty->print_cr(fmt_space, "rw", rw_bytes, rw_t_perc, rw_alloced, rw_u_perc, rw_space->bottom()); + tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, md_low); + tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, mc_low); tty->print_cr("total : %9d [100.0%% of total] out of %9d bytes [%4.1f%% used]", total_bytes, total_alloced, total_u_perc); @@ -603,6 +607,7 @@ dac.dump_stats(int(ro_bytes), int(rw_bytes), int(md_bytes), int(mc_bytes)); } +#undef fmt_space } static void link_shared_classes(Klass* obj, TRAPS) { @@ -645,9 +650,6 @@ TraceTime timer("Dump Shared Spaces", TraceStartupTime); ResourceMark rm; - // Lock out GC - is it necessary? I don't think we care. - No_GC_Verifier no_gc; - // Preload classes to be shared. // Should use some os:: method rather than fopen() here. aB. // Construct the path to the class list (in jre/lib) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspaceTracer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/memory/metaspaceTracer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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 + * 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/classLoaderData.hpp" +#include "memory/metaspaceTracer.hpp" +#include "oops/oop.inline.hpp" +#include "trace/tracing.hpp" +#include "trace/traceBackend.hpp" + +void MetaspaceTracer::report_gc_threshold(size_t old_val, + size_t new_val, + MetaspaceGCThresholdUpdater::Type updater) const { + EventMetaspaceGCThreshold event; + if (event.should_commit()) { + event.set_oldValue(old_val); + event.set_newValue(new_val); + event.set_updater((u1)updater); + event.commit(); + } +} + +void MetaspaceTracer::report_metaspace_allocation_failure(ClassLoaderData *cld, + size_t word_size, + MetaspaceObj::Type objtype, + Metaspace::MetadataType mdtype) const { + send_allocation_failure_event(cld, word_size, objtype, mdtype); +} + +void MetaspaceTracer::report_metadata_oom(ClassLoaderData *cld, + size_t word_size, + MetaspaceObj::Type objtype, + Metaspace::MetadataType mdtype) const { + send_allocation_failure_event(cld, word_size, objtype, mdtype); +} + +template +void MetaspaceTracer::send_allocation_failure_event(ClassLoaderData *cld, + size_t word_size, + MetaspaceObj::Type objtype, + Metaspace::MetadataType mdtype) const { + E event; + if (event.should_commit()) { + if (cld->is_anonymous()) { + event.set_classLoader(NULL); + event.set_anonymousClassLoader(true); + } else { + if (cld->is_the_null_class_loader_data()) { + event.set_classLoader((Klass*) NULL); + } else { + event.set_classLoader(cld->class_loader()->klass()); + } + event.set_anonymousClassLoader(false); + } + + event.set_size(word_size * BytesPerWord); + event.set_metadataType((u1) mdtype); + event.set_metaspaceObjectType((u1) objtype); + event.commit(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/metaspaceTracer.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/memory/metaspaceTracer.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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 + * 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_MEMORY_METASPACE_TRACER_HPP +#define SHARE_VM_MEMORY_METASPACE_TRACER_HPP + +#include "memory/allocation.hpp" +#include "memory/metaspace.hpp" +#include "memory/metaspaceGCThresholdUpdater.hpp" + +class ClassLoaderData; + +class MetaspaceTracer : public CHeapObj { + template + void send_allocation_failure_event(ClassLoaderData *cld, + size_t word_size, + MetaspaceObj::Type objtype, + Metaspace::MetadataType mdtype) const; + public: + void report_gc_threshold(size_t old_val, + size_t new_val, + MetaspaceGCThresholdUpdater::Type updater) const; + void report_metaspace_allocation_failure(ClassLoaderData *cld, + size_t word_size, + MetaspaceObj::Type objtype, + Metaspace::MetadataType mdtype) const; + void report_metadata_oom(ClassLoaderData *cld, + size_t word_size, + MetaspaceObj::Type objtype, + Metaspace::MetadataType mdtype) const; + +}; + +#endif // SHARE_VM_MEMORY_METASPACE_TRACER_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/modRefBarrierSet.hpp --- a/src/share/vm/memory/modRefBarrierSet.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/modRefBarrierSet.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -60,7 +60,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/padded.hpp --- a/src/share/vm/memory/padded.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/padded.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -90,4 +90,23 @@ static PaddedEnd* create_unfreeable(uint length); }; +// Helper class to create an array of references to arrays of primitive types +// Both the array of references and the data arrays are aligned to the given +// alignment. The allocated memory is zero-filled. +template +class Padded2DArray { + public: + // Creates an aligned padded 2D array. + // The memory cannot be deleted since the raw memory chunk is not returned. + static T** create_unfreeable(uint rows, uint columns, size_t* allocation_size = NULL); +}; + +// Helper class to create an array of T objects. The array as a whole will +// start at a multiple of alignment and its size will be aligned to alignment. +template +class PaddedPrimitiveArray { + public: + static T* create_unfreeable(size_t length); +}; + #endif // SHARE_VM_MEMORY_PADDED_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/padded.inline.hpp --- a/src/share/vm/memory/padded.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/padded.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -47,3 +47,42 @@ return aligned_padded_array; } + +template +T** Padded2DArray::create_unfreeable(uint rows, uint columns, size_t* allocation_size) { + // Calculate and align the size of the first dimension's table. + size_t table_size = align_size_up_(rows * sizeof(T*), alignment); + // The size of the separate rows. + size_t row_size = align_size_up_(columns * sizeof(T), alignment); + // Total size consists of the indirection table plus the rows. + size_t total_size = table_size + rows * row_size + alignment; + + // Allocate a chunk of memory large enough to allow alignment of the chunk. + void* chunk = AllocateHeap(total_size, flags); + // Clear the allocated memory. + memset(chunk, 0, total_size); + // Align the chunk of memory. + T** result = (T**)align_pointer_up(chunk, alignment); + void* data_start = (void*)((uintptr_t)result + table_size); + + // Fill in the row table. + for (size_t i = 0; i < rows; i++) { + result[i] = (T*)((uintptr_t)data_start + i * row_size); + } + + if (allocation_size != NULL) { + *allocation_size = total_size; + } + + return result; +} + +template +T* PaddedPrimitiveArray::create_unfreeable(size_t length) { + // Allocate a chunk of memory large enough to allow for some alignment. + void* chunk = AllocateHeap(length * sizeof(T) + alignment, flags); + + memset(chunk, 0, length * sizeof(T) + alignment); + + return (T*)align_pointer_up(chunk, alignment); +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/referenceProcessor.cpp --- a/src/share/vm/memory/referenceProcessor.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/referenceProcessor.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -35,6 +35,8 @@ #include "runtime/java.hpp" #include "runtime/jniHandles.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ReferencePolicy* ReferenceProcessor::_always_clear_soft_ref_policy = NULL; ReferencePolicy* ReferenceProcessor::_default_soft_ref_policy = NULL; bool ReferenceProcessor::_pending_list_uses_discovered_field = false; @@ -94,13 +96,10 @@ bool mt_discovery, uint mt_discovery_degree, bool atomic_discovery, - BoolObjectClosure* is_alive_non_header, - bool discovered_list_needs_barrier) : + BoolObjectClosure* is_alive_non_header) : _discovering_refs(false), _enqueuing_is_done(false), _is_alive_non_header(is_alive_non_header), - _discovered_list_needs_barrier(discovered_list_needs_barrier), - _bs(NULL), _processing_is_mt(mt_processing), _next_id(0) { @@ -126,10 +125,6 @@ _discovered_refs[i].set_length(0); } - // If we do barriers, cache a copy of the barrier set. - if (discovered_list_needs_barrier) { - _bs = Universe::heap()->barrier_set(); - } setup_policy(false /* default soft ref policy */); } @@ -317,13 +312,9 @@ // Enqueue references that are not made active again, and // clear the decks for the next collection (cycle). ref->enqueue_discovered_reflists((HeapWord*)pending_list_addr, task_executor); - // Do the oop-check on pending_list_addr missed in - // enqueue_discovered_reflist. We should probably - // do a raw oop_check so that future such idempotent - // oop_stores relying on the oop-check side-effect - // may be elided automatically and safely without - // affecting correctness. - oop_store(pending_list_addr, oopDesc::load_decode_heap_oop(pending_list_addr)); + // Do the post-barrier on pending_list_addr missed in + // enqueue_discovered_reflist. + oopDesc::bs()->write_ref_field(pending_list_addr, oopDesc::load_decode_heap_oop(pending_list_addr)); // Stop treating discovered references specially. ref->disable_discovery(); @@ -347,10 +338,18 @@ // (java.lang.ref.Reference.discovered), self-loop their "next" field // thus distinguishing them from active References, then // prepend them to the pending list. + // + // The Java threads will see the Reference objects linked together through + // the discovered field. Instead of trying to do the write barrier updates + // in all places in the reference processor where we manipulate the discovered + // field we make sure to do the barrier here where we anyway iterate through + // all linked Reference objects. Note that it is important to not dirty any + // cards during reference processing since this will cause card table + // verification to fail for G1. + // // BKWRD COMPATIBILITY NOTE: For older JDKs (prior to the fix for 4956777), // the "next" field is used to chain the pending list, not the discovered // field. - if (TraceReferenceGC && PrintGCDetails) { gclog_or_tty->print_cr("ReferenceProcessor::enqueue_discovered_reflist list " INTPTR_FORMAT, (address)refs_list.head()); @@ -358,7 +357,7 @@ oop obj = NULL; oop next_d = refs_list.head(); - if (pending_list_uses_discovered_field()) { // New behaviour + if (pending_list_uses_discovered_field()) { // New behavior // Walk down the list, self-looping the next field // so that the References are not considered active. while (obj != next_d) { @@ -372,15 +371,17 @@ assert(java_lang_ref_Reference::next(obj) == NULL, "Reference not active; should not be discovered"); // Self-loop next, so as to make Ref not active. - java_lang_ref_Reference::set_next(obj, obj); - if (next_d == obj) { // obj is last - // Swap refs_list into pendling_list_addr and + java_lang_ref_Reference::set_next_raw(obj, obj); + if (next_d != obj) { + oopDesc::bs()->write_ref_field(java_lang_ref_Reference::discovered_addr(obj), next_d); + } else { + // This is the last object. + // Swap refs_list into pending_list_addr and // set obj's discovered to what we read from pending_list_addr. oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr); - // Need oop_check on pending_list_addr above; - // see special oop-check code at the end of - // enqueue_discovered_reflists() further below. - java_lang_ref_Reference::set_discovered(obj, old); // old may be NULL + // Need post-barrier on pending_list_addr. See enqueue_discovered_ref_helper() above. + java_lang_ref_Reference::set_discovered_raw(obj, old); // old may be NULL + oopDesc::bs()->write_ref_field(java_lang_ref_Reference::discovered_addr(obj), old); } } } else { // Old behaviour @@ -497,37 +498,28 @@ } else { new_next = _next; } - - if (UseCompressedOops) { - // Remove Reference object from list. - oopDesc::encode_store_heap_oop((narrowOop*)_prev_next, new_next); - } else { - // Remove Reference object from list. - oopDesc::store_heap_oop((oop*)_prev_next, new_next); - } + // Remove Reference object from discovered list. Note that G1 does not need a + // pre-barrier here because we know the Reference has already been found/marked, + // that's how it ended up in the discovered list in the first place. + oop_store_raw(_prev_next, new_next); NOT_PRODUCT(_removed++); _refs_list.dec_length(1); } // Make the Reference object active again. void DiscoveredListIterator::make_active() { - // For G1 we don't want to use set_next - it - // will dirty the card for the next field of - // the reference object and will fail - // CT verification. + // The pre barrier for G1 is probably just needed for the old + // reference processing behavior. Should we guard this with + // ReferenceProcessor::pending_list_uses_discovered_field() ? if (UseG1GC) { - BarrierSet* bs = oopDesc::bs(); HeapWord* next_addr = java_lang_ref_Reference::next_addr(_ref); - if (UseCompressedOops) { - bs->write_ref_field_pre((narrowOop*)next_addr, NULL); + oopDesc::bs()->write_ref_field_pre((narrowOop*)next_addr, NULL); } else { - bs->write_ref_field_pre((oop*)next_addr, NULL); + oopDesc::bs()->write_ref_field_pre((oop*)next_addr, NULL); } - java_lang_ref_Reference::set_next_raw(_ref, NULL); - } else { - java_lang_ref_Reference::set_next(_ref, NULL); } + java_lang_ref_Reference::set_next_raw(_ref, NULL); } void DiscoveredListIterator::clear_referent() { @@ -789,14 +781,6 @@ bool _clear_referent; }; -void ReferenceProcessor::set_discovered(oop ref, oop value) { - if (_discovered_list_needs_barrier) { - java_lang_ref_Reference::set_discovered(ref, value); - } else { - java_lang_ref_Reference::set_discovered_raw(ref, value); - } -} - // Balances reference queues. // Move entries from all queues[0, 1, ..., _max_num_q-1] to // queues[0, 1, ..., _num_q-1] because only the first _num_q @@ -854,9 +838,9 @@ // Add the chain to the to list. if (ref_lists[to_idx].head() == NULL) { // to list is empty. Make a loop at the end. - set_discovered(move_tail, move_tail); + java_lang_ref_Reference::set_discovered_raw(move_tail, move_tail); } else { - set_discovered(move_tail, ref_lists[to_idx].head()); + java_lang_ref_Reference::set_discovered_raw(move_tail, ref_lists[to_idx].head()); } ref_lists[to_idx].set_head(move_head); ref_lists[to_idx].inc_length(refs_to_move); @@ -1079,16 +1063,6 @@ // The last ref must have its discovered field pointing to itself. oop next_discovered = (current_head != NULL) ? current_head : obj; - // Note: In the case of G1, this specific pre-barrier is strictly - // not necessary because the only case we are interested in - // here is when *discovered_addr is NULL (see the CAS further below), - // so this will expand to nothing. As a result, we have manually - // elided this out for G1, but left in the test for some future - // collector that might have need for a pre-barrier here, e.g.:- - // _bs->write_ref_field_pre((oop* or narrowOop*)discovered_addr, next_discovered); - assert(!_discovered_list_needs_barrier || UseG1GC, - "Need to check non-G1 collector: " - "may need a pre-write-barrier for CAS from NULL below"); oop retest = oopDesc::atomic_compare_exchange_oop(next_discovered, discovered_addr, NULL); if (retest == NULL) { @@ -1097,9 +1071,6 @@ // is necessary. refs_list.set_head(obj); refs_list.inc_length(1); - if (_discovered_list_needs_barrier) { - _bs->write_ref_field((void*)discovered_addr, next_discovered); - } if (TraceReferenceGC) { gclog_or_tty->print_cr("Discovered reference (mt) (" INTPTR_FORMAT ": %s)", @@ -1250,24 +1221,14 @@ if (_discovery_is_mt) { add_to_discovered_list_mt(*list, obj, discovered_addr); } else { - // If "_discovered_list_needs_barrier", we do write barriers when - // updating the discovered reference list. Otherwise, we do a raw store - // here: the field will be visited later when processing the discovered - // references. + // We do a raw store here: the field will be visited later when processing + // the discovered references. oop current_head = list->head(); // The last ref must have its discovered field pointing to itself. oop next_discovered = (current_head != NULL) ? current_head : obj; - // As in the case further above, since we are over-writing a NULL - // pre-value, we can safely elide the pre-barrier here for the case of G1. - // e.g.:- _bs->write_ref_field_pre((oop* or narrowOop*)discovered_addr, next_discovered); assert(discovered == NULL, "control point invariant"); - assert(!_discovered_list_needs_barrier || UseG1GC, - "For non-G1 collector, may need a pre-write-barrier for CAS from NULL below"); oop_store_raw(discovered_addr, next_discovered); - if (_discovered_list_needs_barrier) { - _bs->write_ref_field((void*)discovered_addr, next_discovered); - } list->set_head(obj); list->inc_length(1); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/referenceProcessor.hpp --- a/src/share/vm/memory/referenceProcessor.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/referenceProcessor.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -227,15 +227,6 @@ // other collectors in configuration bool _discovery_is_mt; // true if reference discovery is MT. - // If true, setting "next" field of a discovered refs list requires - // write barrier(s). (Must be true if used in a collector in which - // elements of a discovered list may be moved during discovery: for - // example, a collector like Garbage-First that moves objects during a - // long-term concurrent marking phase that does weak reference - // discovery.) - bool _discovered_list_needs_barrier; - - BarrierSet* _bs; // Cached copy of BarrierSet. bool _enqueuing_is_done; // true if all weak references enqueued bool _processing_is_mt; // true during phases when // reference processing is MT. @@ -380,11 +371,6 @@ void enqueue_discovered_reflists(HeapWord* pending_list_addr, AbstractRefProcTaskExecutor* task_executor); protected: - // Set the 'discovered' field of the given reference to - // the given value - emitting barriers depending upon - // the value of _discovered_list_needs_barrier. - void set_discovered(oop ref, oop value); - // "Preclean" the given discovered reference list // by removing references with strongly reachable referents. // Currently used in support of CMS only. @@ -420,32 +406,12 @@ void update_soft_ref_master_clock(); public: - // constructor - ReferenceProcessor(): - _span((HeapWord*)NULL, (HeapWord*)NULL), - _discovered_refs(NULL), - _discoveredSoftRefs(NULL), _discoveredWeakRefs(NULL), - _discoveredFinalRefs(NULL), _discoveredPhantomRefs(NULL), - _discovering_refs(false), - _discovery_is_atomic(true), - _enqueuing_is_done(false), - _discovery_is_mt(false), - _discovered_list_needs_barrier(false), - _bs(NULL), - _is_alive_non_header(NULL), - _num_q(0), - _max_num_q(0), - _processing_is_mt(false), - _next_id(0) - { } - // Default parameters give you a vanilla reference processor. ReferenceProcessor(MemRegion span, bool mt_processing = false, uint mt_processing_degree = 1, bool mt_discovery = false, uint mt_discovery_degree = 1, bool atomic_discovery = true, - BoolObjectClosure* is_alive_non_header = NULL, - bool discovered_list_needs_barrier = false); + BoolObjectClosure* is_alive_non_header = NULL); // RefDiscoveryPolicy values enum DiscoveryPolicy { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/sharedHeap.cpp --- a/src/share/vm/memory/sharedHeap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/sharedHeap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -35,6 +35,8 @@ #include "utilities/copy.hpp" #include "utilities/workgroup.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + SharedHeap* SharedHeap::_sh; // The set of potentially parallel tasks in strong root scanning. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/space.cpp --- a/src/share/vm/memory/space.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/space.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -45,6 +45,8 @@ void SpaceMemRegionOopsIterClosure::do_oop(oop* p) { SpaceMemRegionOopsIterClosure::do_oop_work(p); } void SpaceMemRegionOopsIterClosure::do_oop(narrowOop* p) { SpaceMemRegionOopsIterClosure::do_oop_work(p); } +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + HeapWord* DirtyCardToOopClosure::get_actual_top(HeapWord* top, HeapWord* top_obj) { if (top_obj != NULL) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/space.hpp --- a/src/share/vm/memory/space.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/space.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -45,6 +45,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/threadLocalAllocBuffer.cpp --- a/src/share/vm/memory/threadLocalAllocBuffer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/threadLocalAllocBuffer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -31,9 +31,12 @@ #include "runtime/thread.inline.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Thread-Local Edens support // static member initialization +size_t ThreadLocalAllocBuffer::_max_size = 0; unsigned ThreadLocalAllocBuffer::_target_refills = 0; GlobalTLABStats* ThreadLocalAllocBuffer::_global_stats = NULL; @@ -45,7 +48,7 @@ void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() { global_stats()->initialize(); - for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { thread->tlab().accumulate_statistics(); thread->tlab().initialize_statistics(); #ifdef GRAAL @@ -67,28 +70,32 @@ } void ThreadLocalAllocBuffer::accumulate_statistics() { - size_t capacity = Universe::heap()->tlab_capacity(myThread()) / HeapWordSize; - size_t unused = Universe::heap()->unsafe_max_tlab_alloc(myThread()) / HeapWordSize; - size_t used = capacity - unused; - - // Update allocation history if a reasonable amount of eden was allocated. - bool update_allocation_history = used > 0.5 * capacity; + Thread* thread = myThread(); + size_t capacity = Universe::heap()->tlab_capacity(thread); + size_t used = Universe::heap()->tlab_used(thread); _gc_waste += (unsigned)remaining(); + size_t total_allocated = thread->allocated_bytes(); + size_t allocated_since_last_gc = total_allocated - _allocated_before_last_gc; + _allocated_before_last_gc = total_allocated; if (PrintTLAB && (_number_of_refills > 0 || Verbose)) { print_stats("gc"); } if (_number_of_refills > 0) { + // Update allocation history if a reasonable amount of eden was allocated. + bool update_allocation_history = used > 0.5 * capacity; if (update_allocation_history) { // Average the fraction of eden allocated in a tlab by this // thread for use in the next resize operation. // _gc_waste is not subtracted because it's included in // "used". - size_t allocation = _number_of_refills * desired_size(); - double alloc_frac = allocation / (double) used; + // The result can be larger than 1.0 due to direct to old allocations. + // These allocations should ideally not be counted but since it is not possible + // to filter them out here we just cap the fraction to be at most 1.0. + double alloc_frac = MIN2(1.0, (double) allocated_since_last_gc / used); _allocation_fraction.sample(alloc_frac); } global_stats()->update_allocating_threads(); @@ -134,38 +141,37 @@ } void ThreadLocalAllocBuffer::resize_all_tlabs() { - for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { - thread->tlab().resize(); + if (ResizeTLAB) { + for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { + thread->tlab().resize(); #ifdef GRAAL - for (jint i = 0; i < thread->get_gpu_hsail_tlabs_count(); i++) { - thread->get_gpu_hsail_tlab_at(i)->resize(); + for (jint i = 0; i < thread->get_gpu_hsail_tlabs_count(); i++) { + thread->get_gpu_hsail_tlab_at(i)->resize(); + } +#endif } -#endif } } void ThreadLocalAllocBuffer::resize() { + // Compute the next tlab size using expected allocation amount + assert(ResizeTLAB, "Should not call this otherwise"); + size_t alloc = (size_t)(_allocation_fraction.average() * + (Universe::heap()->tlab_capacity(myThread()) / HeapWordSize)); + size_t new_size = alloc / _target_refills; - if (ResizeTLAB) { - // Compute the next tlab size using expected allocation amount - size_t alloc = (size_t)(_allocation_fraction.average() * - (Universe::heap()->tlab_capacity(myThread()) / HeapWordSize)); - size_t new_size = alloc / _target_refills; - - new_size = MIN2(MAX2(new_size, min_size()), max_size()); - - size_t aligned_new_size = align_object_size(new_size); + new_size = MIN2(MAX2(new_size, min_size()), max_size()); - if (PrintTLAB && Verbose) { - gclog_or_tty->print("TLAB new size: thread: " INTPTR_FORMAT " [id: %2d]" - " refills %d alloc: %8.6f desired_size: " SIZE_FORMAT " -> " SIZE_FORMAT "\n", - myThread(), myThread()->osthread()->thread_id(), - _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); - } - set_desired_size(aligned_new_size); + size_t aligned_new_size = align_object_size(new_size); - set_refill_waste_limit(initial_refill_waste_limit()); + if (PrintTLAB && Verbose) { + gclog_or_tty->print("TLAB new size: thread: " INTPTR_FORMAT " [id: %2d]" + " refills %d alloc: %8.6f desired_size: " SIZE_FORMAT " -> " SIZE_FORMAT "\n", + myThread(), myThread()->osthread()->thread_id(), + _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); } + set_desired_size(aligned_new_size); + set_refill_waste_limit(initial_refill_waste_limit()); } void ThreadLocalAllocBuffer::initialize_statistics() { @@ -262,31 +268,13 @@ return init_sz; } -const size_t ThreadLocalAllocBuffer::max_size() { - - // TLABs can't be bigger than we can fill with a int[Integer.MAX_VALUE]. - // This restriction could be removed by enabling filling with multiple arrays. - // If we compute that the reasonable way as - // header_size + ((sizeof(jint) * max_jint) / HeapWordSize) - // we'll overflow on the multiply, so we do the divide first. - // We actually lose a little by dividing first, - // but that just makes the TLAB somewhat smaller than the biggest array, - // which is fine, since we'll be able to fill that. - - size_t unaligned_max_size = typeArrayOopDesc::header_size(T_INT) + - sizeof(jint) * - ((juint) max_jint / (size_t) HeapWordSize); - return align_size_down(unaligned_max_size, MinObjAlignment); -} - void ThreadLocalAllocBuffer::print_stats(const char* tag) { Thread* thrd = myThread(); size_t waste = _gc_waste + _slow_refill_waste + _fast_refill_waste; size_t alloc = _number_of_refills * _desired_size; double waste_percent = alloc == 0 ? 0.0 : 100.0 * waste / alloc; - size_t tlab_used = Universe::heap()->tlab_capacity(thrd) - - Universe::heap()->unsafe_max_tlab_alloc(thrd); + size_t tlab_used = Universe::heap()->tlab_used(thrd); gclog_or_tty->print("TLAB: %s thread: " INTPTR_FORMAT " [id: %2d]" " desired_size: " SIZE_FORMAT "KB" " slow allocs: %d refill waste: " SIZE_FORMAT "B" diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/threadLocalAllocBuffer.hpp --- a/src/share/vm/memory/threadLocalAllocBuffer.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/threadLocalAllocBuffer.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -48,7 +48,9 @@ HeapWord* _end; // allocation end (excluding alignment_reserve) size_t _desired_size; // desired size (including alignment_reserve) size_t _refill_waste_limit; // hold onto tlab if free() is larger than this + size_t _allocated_before_last_gc; // total bytes allocated up until the last gc + static size_t _max_size; // maximum size of any TLAB static unsigned _target_refills; // expected number of refills between GCs unsigned _number_of_refills; @@ -103,12 +105,13 @@ static GlobalTLABStats* global_stats() { return _global_stats; } public: - ThreadLocalAllocBuffer() : _allocation_fraction(TLABAllocationWeight) { + ThreadLocalAllocBuffer() : _allocation_fraction(TLABAllocationWeight), _allocated_before_last_gc(0) { // do nothing. tlabs must be inited by initialize() calls } static const size_t min_size() { return align_object_size(MinTLABSize / HeapWordSize); } - static const size_t max_size(); + static const size_t max_size() { assert(_max_size != 0, "max_size not set up"); return _max_size; } + static void set_max_size(size_t max_size) { _max_size = max_size; } HeapWord* start() const { return _start; } HeapWord* end() const { return _end; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/threadLocalAllocBuffer.inline.hpp --- a/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -98,7 +98,7 @@ " obj: "SIZE_FORMAT " free: "SIZE_FORMAT " waste: "SIZE_FORMAT"\n", - "slow", thrd, thrd->osthread()->thread_id(), + "slow", p2i(thrd), thrd->osthread()->thread_id(), obj_size, free(), refill_waste_limit()); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/memory/universe.cpp --- a/src/share/vm/memory/universe.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/memory/universe.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -78,6 +78,8 @@ #include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" #endif // INCLUDE_ALL_GCS +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Known objects Klass* Universe::_boolArrayKlassObj = NULL; Klass* Universe::_byteArrayKlassObj = NULL; @@ -632,7 +634,6 @@ guarantee(sizeof(oop) % sizeof(HeapWord) == 0, "oop size is not not a multiple of HeapWord size"); TraceTime timer("Genesis", TraceStartupTime); - GC_locker::lock(); // do not allow gc during bootstrapping JavaClasses::compute_hard_coded_offsets(); jint status = Universe::initialize_heap(); @@ -759,7 +760,7 @@ // the correct no-access prefix. // The final value will be set in initialize_heap() below. Universe::set_narrow_oop_base((address)UnscaledOopHeapMax); -#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. @@ -816,6 +817,8 @@ Universe::_collectedHeap = new GenCollectedHeap(gc_policy); } + ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size()); + jint status = Universe::heap()->initialize(); if (status != JNI_OK) { return status; @@ -839,6 +842,11 @@ // Can't reserve heap below 32Gb. // keep the Universe::narrow_oop_base() set in Universe::reserve_heap() 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 preserved, 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), @@ -1157,8 +1165,6 @@ MemoryService::add_metaspace_memory_pools(); - GC_locker::unlock(); // allow gc after bootstrapping - MemoryService::set_universe_heap(Universe::_collectedHeap); return true; } @@ -1344,7 +1350,7 @@ HandleMark hm; // Handles created during verification can be zapped _verify_count++; - if (!silent) gclog_or_tty->print(prefix); + if (!silent) gclog_or_tty->print("%s", prefix); if (!silent) gclog_or_tty->print("[Verifying "); if (!silent) gclog_or_tty->print("threads "); Threads::verify(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/annotations.cpp --- a/src/share/vm/oops/annotations.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/annotations.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -76,7 +76,7 @@ void Annotations::print_value_on(outputStream* st) const { - st->print("Anotations(" INTPTR_FORMAT ")", this); + st->print("Anotations(" INTPTR_FORMAT ")", p2i(this)); } #if INCLUDE_SERVICES diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/arrayKlass.cpp --- a/src/share/vm/oops/arrayKlass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/arrayKlass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -64,10 +64,10 @@ return NULL; } -Method* ArrayKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const { +Method* ArrayKlass::uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const { // There are no methods in an array klass but the super class (Object) has some assert(super(), "super klass must be present"); - return super()->uncached_lookup_method(name, signature); + return super()->uncached_lookup_method(name, signature, mode); } ArrayKlass::ArrayKlass(Symbol* name) { @@ -214,8 +214,8 @@ // Verification -void ArrayKlass::verify_on(outputStream* st, bool check_dictionary) { - Klass::verify_on(st, check_dictionary); +void ArrayKlass::verify_on(outputStream* st) { + Klass::verify_on(st); if (component_mirror() != NULL) { guarantee(component_mirror()->klass() != NULL, "should have a class"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/arrayKlass.hpp --- a/src/share/vm/oops/arrayKlass.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/arrayKlass.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -86,7 +86,7 @@ objArrayOop allocate_arrayArray(int n, int length, TRAPS); // Lookup operations - Method* uncached_lookup_method(Symbol* name, Symbol* signature) const; + Method* uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const; // Casting from Klass* static ArrayKlass* cast(Klass* k) { @@ -146,7 +146,7 @@ void oop_print_on(oop obj, outputStream* st); // Verification - void verify_on(outputStream* st, bool check_dictionary); + void verify_on(outputStream* st); void oop_verify_on(oop obj, outputStream* st); }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/constMethod.cpp --- a/src/share/vm/oops/constMethod.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/constMethod.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -388,8 +388,8 @@ void ConstMethod::print_on(outputStream* st) const { ResourceMark rm; assert(is_constMethod(), "must be constMethod"); - st->print_cr(internal_name()); - st->print(" - method: " INTPTR_FORMAT " ", (address)method()); + st->print_cr("%s", internal_name()); + st->print(" - method: " INTPTR_FORMAT " ", p2i((address)method())); method()->print_value_on(st); st->cr(); if (has_stackmap_table()) { st->print(" - stackmap data: "); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/constantPool.cpp --- a/src/share/vm/oops/constantPool.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/constantPool.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -42,6 +42,8 @@ #include "runtime/signature.hpp" #include "runtime/vframe.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) { // Tags are RW but comment below applies to tags also. Array* tags = MetadataFactory::new_writeable_array(loader_data, length, 0, CHECK_NULL); @@ -82,6 +84,9 @@ void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) { MetadataFactory::free_metadata(loader_data, cache()); set_cache(NULL); + MetadataFactory::free_array(loader_data, reference_map()); + set_reference_map(NULL); + MetadataFactory::free_array(loader_data, operands()); set_operands(NULL); @@ -141,6 +146,10 @@ // CDS support. Create a new resolved_references array. void ConstantPool::restore_unshareable_info(TRAPS) { + // Only create the new resolved references array and lock if it hasn't been + // attempted before + if (resolved_references() != NULL) return; + // restore the C++ vtable from the shared archive restore_vtable(); @@ -1292,6 +1301,7 @@ } break; case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_UnresolvedClassInError: { // Can be resolved after checking tag, so check the slot first. CPSlot entry = from_cp->slot_at(from_i); @@ -1876,7 +1886,7 @@ void ConstantPool::print_on(outputStream* st) const { EXCEPTION_MARK; assert(is_constantPool(), "must be constantPool"); - st->print_cr(internal_name()); + st->print_cr("%s", internal_name()); if (flags() != 0) { st->print(" - flags: 0x%x", flags()); if (has_preresolution()) st->print(" has_preresolution"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/cpCache.cpp --- a/src/share/vm/oops/cpCache.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/cpCache.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -38,8 +38,9 @@ # include "gc_implementation/parallelScavenge/psPromotionManager.hpp" #endif // INCLUDE_ALL_GCS +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC -// Implememtation of ConstantPoolCacheEntry +// Implementation of ConstantPoolCacheEntry void ConstantPoolCacheEntry::initialize_entry(int index) { assert(0 < index && index < 0x10000, "sanity check"); @@ -363,7 +364,7 @@ // Decode the action of set_method and set_interface_call Bytecodes::Code invoke_code = bytecode_1(); if (invoke_code != (Bytecodes::Code)0) { - Metadata* f1 = (Metadata*)_f1; + Metadata* f1 = f1_ord(); if (f1 != NULL) { switch (invoke_code) { case Bytecodes::_invokeinterface: @@ -405,7 +406,7 @@ oop ConstantPoolCacheEntry::appendix_if_resolved(constantPoolHandle cpool) { - if (is_f1_null() || !has_appendix()) + if (!has_appendix()) return NULL; const int ref_index = f2_as_index() + _indy_resolved_references_appendix_offset; objArrayOop resolved_references = cpool->resolved_references(); @@ -414,7 +415,7 @@ oop ConstantPoolCacheEntry::method_type_if_resolved(constantPoolHandle cpool) { - if (is_f1_null() || !has_method_type()) + if (!has_method_type()) return NULL; const int ref_index = f2_as_index() + _indy_resolved_references_method_type_offset; objArrayOop resolved_references = cpool->resolved_references(); @@ -667,7 +668,7 @@ void ConstantPoolCache::print_on(outputStream* st) const { assert(is_constantPoolCache(), "obj must be constant pool cache"); - st->print_cr(internal_name()); + st->print_cr("%s", internal_name()); // print constant pool cache entries for (int i = 0; i < length(); i++) entry_at(i)->print(st, i); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/cpCache.hpp --- a/src/share/vm/oops/cpCache.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/cpCache.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -138,7 +138,7 @@ void set_bytecode_1(Bytecodes::Code code); void set_bytecode_2(Bytecodes::Code code); - void set_f1(Metadata* f1) { + void set_f1(Metadata* f1) { Metadata* existing_f1 = (Metadata*)_f1; // read once assert(existing_f1 == NULL || existing_f1 == f1, "illegal field change"); _f1 = f1; @@ -325,22 +325,29 @@ // Accessors int indices() const { return _indices; } + int indices_ord() const { return (intx)OrderAccess::load_ptr_acquire(&_indices); } int constant_pool_index() const { return (indices() & cp_index_mask); } - Bytecodes::Code bytecode_1() const { return Bytecodes::cast((indices() >> bytecode_1_shift) & bytecode_1_mask); } - Bytecodes::Code bytecode_2() const { return Bytecodes::cast((indices() >> bytecode_2_shift) & bytecode_2_mask); } - Method* f1_as_method() const { Metadata* f1 = (Metadata*)_f1; assert(f1 == NULL || f1->is_method(), ""); return (Method*)f1; } - Klass* f1_as_klass() const { Metadata* f1 = (Metadata*)_f1; assert(f1 == NULL || f1->is_klass(), ""); return (Klass*)f1; } - bool is_f1_null() const { Metadata* f1 = (Metadata*)_f1; return f1 == NULL; } // classifies a CPC entry as unbound + Bytecodes::Code bytecode_1() const { return Bytecodes::cast((indices_ord() >> bytecode_1_shift) & bytecode_1_mask); } + Bytecodes::Code bytecode_2() const { return Bytecodes::cast((indices_ord() >> bytecode_2_shift) & bytecode_2_mask); } + Metadata* f1_ord() const { return (Metadata *)OrderAccess::load_ptr_acquire(&_f1); } + Method* f1_as_method() const { Metadata* f1 = f1_ord(); assert(f1 == NULL || f1->is_method(), ""); return (Method*)f1; } + Klass* f1_as_klass() const { Metadata* f1 = f1_ord(); assert(f1 == NULL || f1->is_klass(), ""); return (Klass*)f1; } + // 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 { Metadata* f1 = f1_ord(); return f1 == NULL; } // classifies a CPC entry as unbound int f2_as_index() const { assert(!is_vfinal(), ""); return (int) _f2; } - Method* f2_as_vfinal_method() const { assert(is_vfinal(), ""); return (Method*)_f2; } + Method* f2_as_vfinal_method() const { assert(is_vfinal(), ""); return (Method*)_f2; } int field_index() const { assert(is_field_entry(), ""); return (_flags & field_index_mask); } int parameter_size() const { assert(is_method_entry(), ""); return (_flags & parameter_size_mask); } bool is_volatile() const { return (_flags & (1 << is_volatile_shift)) != 0; } bool is_final() const { return (_flags & (1 << is_final_shift)) != 0; } bool is_forced_virtual() const { return (_flags & (1 << is_forced_virtual_shift)) != 0; } bool is_vfinal() const { return (_flags & (1 << is_vfinal_shift)) != 0; } - bool has_appendix() const { return (_flags & (1 << has_appendix_shift)) != 0; } - bool has_method_type() const { return (_flags & (1 << has_method_type_shift)) != 0; } + bool has_appendix() const { return (!is_f1_null()) && (_flags & (1 << has_appendix_shift)) != 0; } + bool has_method_type() const { return (!is_f1_null()) && (_flags & (1 << has_method_type_shift)) != 0; } bool is_method_entry() const { return (_flags & (1 << is_field_entry_shift)) == 0; } bool is_field_entry() const { return (_flags & (1 << is_field_entry_shift)) != 0; } bool is_byte() const { return flag_state() == btos; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/generateOopMap.hpp --- a/src/share/vm/oops/generateOopMap.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/generateOopMap.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -412,9 +412,9 @@ int copy_cts (CellTypeState *dst, CellTypeState *src); // Error handling - void error_work (const char *format, va_list ap); - void report_error (const char *format, ...); - void verify_error (const char *format, ...); + void error_work (const char *format, va_list ap) ATTRIBUTE_PRINTF(2, 0); + void report_error (const char *format, ...) ATTRIBUTE_PRINTF(2, 3); + void verify_error (const char *format, ...) ATTRIBUTE_PRINTF(2, 3); bool got_error() { return _got_error; } // Create result set diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/instanceKlass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -79,6 +79,8 @@ #include "c1/c1_Compiler.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef DTRACE_ENABLED #ifndef USDT2 @@ -1240,7 +1242,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. @@ -1362,17 +1368,18 @@ } -void InstanceKlass::do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS) { +void InstanceKlass::do_local_static_fields(void f(fieldDescriptor*, Handle, TRAPS), Handle mirror, TRAPS) { instanceKlassHandle h_this(THREAD, this); - do_local_static_fields_impl(h_this, f, CHECK); + do_local_static_fields_impl(h_this, f, mirror, CHECK); } -void InstanceKlass::do_local_static_fields_impl(instanceKlassHandle this_oop, void f(fieldDescriptor* fd, TRAPS), TRAPS) { - for (JavaFieldStream fs(this_oop()); !fs.done(); fs.next()) { +void InstanceKlass::do_local_static_fields_impl(instanceKlassHandle this_k, + void f(fieldDescriptor* fd, Handle mirror, TRAPS), Handle mirror, TRAPS) { + for (JavaFieldStream fs(this_k()); !fs.done(); fs.next()) { if (fs.access_flags().is_static()) { fieldDescriptor& fd = fs.field_descriptor(); - f(&fd, CHECK); + f(&fd, mirror, CHECK); } } } @@ -1461,7 +1468,11 @@ // find_method looks up the name/signature in the local methods array Method* InstanceKlass::find_method(Symbol* name, Symbol* signature) const { - return InstanceKlass::find_method(methods(), name, signature); + return find_method_impl(name, signature, false); +} + +Method* InstanceKlass::find_method_impl(Symbol* name, Symbol* signature, bool skipping_overpass) const { + return InstanceKlass::find_method_impl(methods(), name, signature, skipping_overpass); } // find_instance_method looks up the name/signature in the local methods array @@ -1478,40 +1489,49 @@ // find_method looks up the name/signature in the local methods array Method* InstanceKlass::find_method( Array* methods, Symbol* name, Symbol* signature) { - int hit = find_method_index(methods, name, signature); + return InstanceKlass::find_method_impl(methods, name, signature, false); +} + +Method* InstanceKlass::find_method_impl( + Array* methods, Symbol* name, Symbol* signature, bool skipping_overpass) { + int hit = find_method_index(methods, name, signature, skipping_overpass); return hit >= 0 ? methods->at(hit): NULL; } // Used directly for default_methods to find the index into the // default_vtable_indices, and indirectly by find_method // find_method_index looks in the local methods array to return the index -// of the matching name/signature +// of the matching name/signature. If, overpass methods are being ignored, +// the search continues to find a potential non-overpass match. This capability +// is important during method resolution to prefer a static method, for example, +// over an overpass method. int InstanceKlass::find_method_index( - Array* methods, Symbol* name, Symbol* signature) { + Array* methods, Symbol* name, Symbol* signature, bool skipping_overpass) { int hit = binary_search(methods, name); if (hit != -1) { Method* m = methods->at(hit); // Do linear search to find matching signature. First, quick check - // for common case - if (m->signature() == signature) return hit; + // for common case, ignoring overpasses if requested. + if ((m->signature() == signature) && (!skipping_overpass || !m->is_overpass())) return hit; + // search downwards through overloaded methods int i; for (i = hit - 1; i >= 0; --i) { Method* m = methods->at(i); assert(m->is_method(), "must be method"); if (m->name() != name) break; - if (m->signature() == signature) return i; + if ((m->signature() == signature) && (!skipping_overpass || !m->is_overpass())) return i; } // search upwards for (i = hit + 1; i < methods->length(); ++i) { Method* m = methods->at(i); assert(m->is_method(), "must be method"); if (m->name() != name) break; - if (m->signature() == signature) return i; + if ((m->signature() == signature) && (!skipping_overpass || !m->is_overpass())) return i; } // not found #ifdef ASSERT - int index = linear_search(methods, name, signature); + int index = skipping_overpass ? -1 : linear_search(methods, name, signature); assert(index == -1, err_msg("binary search should have found entry %d", index)); #endif } @@ -1537,16 +1557,16 @@ // uncached_lookup_method searches both the local class methods array and all // superclasses methods arrays, skipping any overpass methods in superclasses. -Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const { +Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const { + MethodLookupMode lookup_mode = mode; Klass* klass = const_cast(this); - bool dont_ignore_overpasses = true; // For the class being searched, find its overpasses. while (klass != NULL) { - Method* method = InstanceKlass::cast(klass)->find_method(name, signature); - if ((method != NULL) && (dont_ignore_overpasses || !method->is_overpass())) { + Method* method = InstanceKlass::cast(klass)->find_method_impl(name, signature, (lookup_mode == skip_overpass)); + if (method != NULL) { return method; } klass = InstanceKlass::cast(klass)->super(); - dont_ignore_overpasses = false; // Ignore overpass methods in all superclasses. + lookup_mode = skip_overpass; // Always ignore overpass methods in superclasses } return NULL; } @@ -1561,7 +1581,7 @@ } // Look up interfaces if (m == NULL) { - m = lookup_method_in_all_interfaces(name, signature, false); + m = lookup_method_in_all_interfaces(name, signature, normal); } return m; } @@ -1571,7 +1591,7 @@ // They should only be found in the initial InterfaceMethodRef Method* InstanceKlass::lookup_method_in_all_interfaces(Symbol* name, Symbol* signature, - bool skip_default_methods) const { + MethodLookupMode mode) const { Array* all_ifs = transitive_interfaces(); int num_ifs = all_ifs->length(); InstanceKlass *ik = NULL; @@ -1579,7 +1599,7 @@ ik = InstanceKlass::cast(all_ifs->at(i)); Method* m = ik->lookup_method(name, signature); if (m != NULL && m->is_public() && !m->is_static() && - (!skip_default_methods || !m->is_default_method())) { + ((mode != skip_defaults) || !m->is_default_method())) { return m; } } @@ -2271,15 +2291,7 @@ for (int m = 0; m < methods()->length(); m++) { MethodData* mdo = methods()->at(m)->method_data(); if (mdo != NULL) { - for (ProfileData* data = mdo->first_data(); - mdo->is_valid(data); - data = mdo->next_data(data)) { - data->clean_weak_klass_links(is_alive); - } - ParametersTypeData* parameters = mdo->parameters_type_data(); - if (parameters != NULL) { - parameters->clean_weak_klass_links(is_alive); - } + mdo->clean_method_data(is_alive); } } } @@ -2325,9 +2337,7 @@ int num_methods = methods->length(); for (int index2 = 0; index2 < num_methods; ++index2) { methodHandle m(THREAD, methods->at(index2)); - m()->link_method(m, CHECK); - // restore method's vtable by calling a virtual function - m->restore_vtable(); + m->restore_unshareable_info(CHECK); } if (JvmtiExport::has_redefined_a_class()) { // Reinitialize vtable because RedefineClasses may have changed some @@ -2802,7 +2812,7 @@ Method* m = n->method(); // Search for match while(cur != NULL && cur != n) { - if (TieredCompilation) { + if (TieredCompilation && m == cur->method()) { // Find max level before n max_level = MAX2(max_level, cur->comp_level()); } @@ -2824,7 +2834,9 @@ cur = next; while (cur != NULL) { // Find max level after n - max_level = MAX2(max_level, cur->comp_level()); + if (m == cur->method()) { + max_level = MAX2(max_level, cur->comp_level()); + } cur = cur->osr_link(); } m->set_highest_osr_comp_level(max_level); @@ -2928,7 +2940,7 @@ st->print(BULLET"instance size: %d", size_helper()); st->cr(); st->print(BULLET"klass size: %d", size()); st->cr(); st->print(BULLET"access: "); access_flags().print_on(st); st->cr(); - st->print(BULLET"state: "); st->print_cr(state_names[_init_state]); + st->print(BULLET"state: "); st->print_cr("%s", state_names[_init_state]); st->print(BULLET"name: "); name()->print_value_on(st); st->cr(); st->print(BULLET"super: "); super()->print_value_on_maybe_null(st); st->cr(); st->print(BULLET"sub: "); @@ -3222,7 +3234,7 @@ virtual void do_oop(narrowOop* p) { VerifyFieldClosure::do_oop_work(p); } }; -void InstanceKlass::verify_on(outputStream* st, bool check_dictionary) { +void InstanceKlass::verify_on(outputStream* st) { #ifndef PRODUCT // Avoid redundant verifies, this really should be in product. if (_verify_count == Universe::verify_count()) return; @@ -3230,14 +3242,11 @@ #endif // Verify Klass - Klass::verify_on(st, check_dictionary); - - // Verify that klass is present in SystemDictionary if not already - // verifying the SystemDictionary. - if (is_loaded() && !is_anonymous() && check_dictionary) { - Symbol* h_name = name(); - SystemDictionary::verify_obj_klass_present(h_name, class_loader_data()); - } + Klass::verify_on(st); + + // Verify that klass is present in ClassLoaderData + guarantee(class_loader_data()->contains_klass(this), + "this class isn't found in class loader data"); // Verify vtables if (is_linked()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/instanceKlass.hpp --- a/src/share/vm/oops/instanceKlass.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/instanceKlass.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -310,7 +310,7 @@ // three cases: // NULL: no implementor. // A Klass* that's not itself: one implementor. - // Itsef: more than one implementors. + // Itself: more than one implementors. // embedded host klass follows here // The embedded host klass only exists in an anonymous class for // dynamic language support (JSR 292 enabled). The host class grants @@ -522,14 +522,14 @@ static Method* find_instance_method(Array* methods, Symbol* name, Symbol* signature); // find a local method index in default_methods (returns -1 if not found) - static int find_method_index(Array* methods, Symbol* name, Symbol* signature); + static int find_method_index(Array* methods, Symbol* name, Symbol* signature, bool skipping_overpass); // lookup operation (returns NULL if not found) - Method* uncached_lookup_method(Symbol* name, Symbol* signature) const; + Method* uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const; // lookup a method in all the interfaces that this class implements // (returns NULL if not found) - Method* lookup_method_in_all_interfaces(Symbol* name, Symbol* signature, bool skip_default_methods) const; + Method* lookup_method_in_all_interfaces(Symbol* name, Symbol* signature, MethodLookupMode mode) const; // lookup a method in local defaults then in all interfaces // (returns NULL if not found) @@ -558,6 +558,7 @@ if (hk == NULL) { return NULL; } else { + assert(*hk != NULL, "host klass should always be set if the address is not null"); return *hk; } } @@ -843,7 +844,7 @@ // Iterators void do_local_static_fields(FieldClosure* cl); void do_nonstatic_fields(FieldClosure* cl); // including inherited fields - void do_local_static_fields(void f(fieldDescriptor*, TRAPS), TRAPS); + void do_local_static_fields(void f(fieldDescriptor*, Handle, TRAPS), Handle, TRAPS); void methods_do(void f(Method* method)); void array_klasses_do(void f(Klass* k)); @@ -1051,7 +1052,7 @@ static void set_initialization_state_and_notify_impl (instanceKlassHandle this_oop, ClassState state, TRAPS); static void call_class_initializer_impl (instanceKlassHandle this_oop, TRAPS); static Klass* array_klass_impl (instanceKlassHandle this_oop, bool or_null, int n, TRAPS); - static void do_local_static_fields_impl (instanceKlassHandle this_oop, void f(fieldDescriptor* fd, TRAPS), TRAPS); + static void do_local_static_fields_impl (instanceKlassHandle this_oop, void f(fieldDescriptor* fd, Handle, TRAPS), Handle, TRAPS); /* jni_id_for_impl for jfieldID only */ static JNIid* jni_id_for_impl (instanceKlassHandle this_oop, int offset); @@ -1061,6 +1062,10 @@ // Returns the array class with this class as element type Klass* array_klass_impl(bool or_null, TRAPS); + // find a local method (returns NULL if not found) + Method* find_method_impl(Symbol* name, Symbol* signature, bool skipping_overpass) const; + static Method* find_method_impl(Array* methods, Symbol* name, Symbol* signature, bool skipping_overpass); + // Free CHeap allocated fields. void release_C_heap_structures(); public: @@ -1100,7 +1105,7 @@ const char* internal_name() const; // Verification - void verify_on(outputStream* st, bool check_dictionary); + void verify_on(outputStream* st); void oop_verify_on(oop obj, outputStream* st); }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/instanceMirrorKlass.cpp --- a/src/share/vm/oops/instanceMirrorKlass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/instanceMirrorKlass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -367,7 +367,12 @@ // Query before forming handle. int size = instance_size(k); KlassHandle h_k(THREAD, this); - instanceOop i = (instanceOop) CollectedHeap::Class_obj_allocate(h_k, size, k, CHECK_NULL); + instanceOop i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL); + + // Since mirrors can be variable sized because of the static fields, store + // the size in the mirror itself. + java_lang_Class::set_oop_size(i, size); + return i; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/instanceRefKlass.cpp --- a/src/share/vm/oops/instanceRefKlass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/instanceRefKlass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -45,6 +45,8 @@ #include "oops/oop.pcgc.inline.hpp" #endif // INCLUDE_ALL_GCS +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + template void specialized_oop_follow_contents(InstanceRefKlass* ref, oop obj) { T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/klass.cpp --- a/src/share/vm/oops/klass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/klass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -129,7 +129,7 @@ } -Method* Klass::uncached_lookup_method(Symbol* name, Symbol* signature) const { +Method* Klass::uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const { #ifdef ASSERT tty->print_cr("Error: uncached_lookup_method called on a klass oop." " Likely error: reflection method does not correctly" @@ -376,8 +376,6 @@ } bool Klass::is_loader_alive(BoolObjectClosure* is_alive) { - assert(ClassLoaderDataGraph::contains((address)this), "is in the metaspace"); - #ifdef ASSERT // The class is alive iff the class loader is alive. oop loader = class_loader(); @@ -485,12 +483,8 @@ } void Klass::remove_unshareable_info() { - if (!DumpSharedSpaces) { - // Clean up after OOM during class loading - if (class_loader_data() != NULL) { - class_loader_data()->remove_class(this); - } - } + assert (DumpSharedSpaces, "only called for DumpSharedSpaces"); + set_subklass(NULL); set_next_sibling(NULL); // Clear the java mirror @@ -502,17 +496,27 @@ } void Klass::restore_unshareable_info(TRAPS) { - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - // Restore class_loader_data to the null class loader data - set_class_loader_data(loader_data); + TRACE_INIT_ID(this); + // If an exception happened during CDS restore, some of these fields may already be + // set. We leave the class on the CLD list, even if incomplete so that we don't + // modify the CLD list outside a safepoint. + if (class_loader_data() == NULL) { + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + // Restore class_loader_data to the null class loader data + set_class_loader_data(loader_data); - // Add to null class loader list first before creating the mirror - // (same order as class file parsing) - loader_data->add_class(this); + // Add to null class loader list first before creating the mirror + // (same order as class file parsing) + loader_data->add_class(this); + } // Recreate the class mirror. The protection_domain is always null for // boot loader, for now. - java_lang_Class::create_mirror(this, Handle(NULL), CHECK); + // Only recreate it if not present. A previous attempt to restore may have + // gotten an OOM later but keep the mirror if it was created. + if (java_mirror() == NULL) { + java_lang_Class::create_mirror(this, Handle(NULL), CHECK); + } } Klass* Klass::array_klass_or_null(int rank) { @@ -640,11 +644,11 @@ // Verification -void Klass::verify_on(outputStream* st, bool check_dictionary) { +void Klass::verify_on(outputStream* st) { // This can be expensive, but it is worth checking that this klass is actually // in the CLD graph but not in production. - assert(ClassLoaderDataGraph::contains((address)this), "Should be"); + assert(Metaspace::contains((address)this), "Should be"); guarantee(this->is_klass(),"should be klass"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/klass.hpp --- a/src/share/vm/oops/klass.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/klass.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -182,6 +182,8 @@ void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw(); public: + enum MethodLookupMode { normal, skip_overpass, skip_defaults }; + bool is_klass() const volatile { return true; } // super @@ -421,10 +423,10 @@ virtual void initialize(TRAPS); // lookup operation for MethodLookupCache friend class MethodLookupCache; - virtual Method* uncached_lookup_method(Symbol* name, Symbol* signature) const; + virtual Method* uncached_lookup_method(Symbol* name, Symbol* signature, MethodLookupMode mode) const; public: Method* lookup_method(Symbol* name, Symbol* signature) const { - return uncached_lookup_method(name, signature); + return uncached_lookup_method(name, signature, normal); } // array class with specific rank @@ -695,8 +697,8 @@ virtual const char* internal_name() const = 0; // Verification - virtual void verify_on(outputStream* st, bool check_dictionary); - void verify(bool check_dictionary = true) { verify_on(tty, check_dictionary); } + virtual void verify_on(outputStream* st); + void verify() { verify_on(tty); } #ifndef PRODUCT bool verify_vtable_index(int index); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/klass.inline.hpp --- a/src/share/vm/oops/klass.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/klass.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -63,7 +63,7 @@ assert(!is_null(v), "narrow klass value can never be zero"); int shift = Universe::narrow_klass_shift(); Klass* result = (Klass*)(void*)((uintptr_t)Universe::narrow_klass_base() + ((uintptr_t)v << shift)); - assert(check_klass_alignment(result), err_msg("address not aligned: " PTR_FORMAT, (void*) result)); + assert(check_klass_alignment(result), err_msg("address not aligned: " INTPTR_FORMAT, p2i((void*) result))); return result; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/klassVtable.cpp --- a/src/share/vm/oops/klassVtable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/klassVtable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -39,6 +39,8 @@ #include "runtime/handles.inline.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + inline InstanceKlass* klassVtable::ik() const { Klass* k = _klass(); assert(k->oop_is_instance(), "not an InstanceKlass"); @@ -643,7 +645,7 @@ // this check for all access permissions. InstanceKlass *sk = InstanceKlass::cast(super); if (sk->has_miranda_methods()) { - if (sk->lookup_method_in_all_interfaces(name, signature, false) != NULL) { + if (sk->lookup_method_in_all_interfaces(name, signature, Klass::normal) != NULL) { return false; // found a matching miranda; we do not need a new entry } } @@ -719,7 +721,7 @@ && mo->method_holder() != NULL && mo->method_holder()->super() != NULL) { - mo = mo->method_holder()->super()->uncached_lookup_method(name, signature); + mo = mo->method_holder()->super()->uncached_lookup_method(name, signature, Klass::normal); } if (mo == NULL || mo->access_flags().is_private() ) { // super class hierarchy does not implement it or protection is different @@ -764,7 +766,7 @@ if (is_miranda(im, class_methods, default_methods, super)) { // is it a miranda at all? InstanceKlass *sk = InstanceKlass::cast(super); // check if it is a duplicate of a super's miranda - if (sk->lookup_method_in_all_interfaces(im->name(), im->signature(), false) == NULL) { + if (sk->lookup_method_in_all_interfaces(im->name(), im->signature(), Klass::normal) == NULL) { new_mirandas->append(im); } if (all_mirandas != NULL) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/markOop.cpp --- a/src/share/vm/oops/markOop.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/markOop.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -26,9 +26,11 @@ #include "oops/markOop.hpp" #include "runtime/thread.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void markOopDesc::print_on(outputStream* st) const { if (is_locked()) { - st->print("locked(0x%lx)->", value()); + st->print("locked(" INTPTR_FORMAT ")->", value()); markOop(*(markOop*)value())->print_on(st); } else { assert(is_unlocked() || has_bias_pattern(), "just checking"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/metadata.hpp --- a/src/share/vm/oops/metadata.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/metadata.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -40,8 +40,9 @@ int identity_hash() { return (int)(uintptr_t)this; } // Rehashing support for tables containing pointers to this - unsigned int new_hash(jint seed) { ShouldNotReachHere(); return 0; } + unsigned int new_hash(juint seed) { ShouldNotReachHere(); return 0; } + virtual bool is_metadata() const volatile { return true; } virtual bool is_klass() const volatile { return false; } virtual bool is_method() const volatile { return false; } virtual bool is_methodData() const volatile { return false; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/method.cpp --- a/src/share/vm/oops/method.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/method.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -55,6 +55,7 @@ #include "utilities/quickSort.hpp" #include "utilities/xmlstream.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // Implementation of Method @@ -273,7 +274,7 @@ } address Method::bcp_from(int bci) const { - assert((is_native() && bci == 0) || (!is_native() && 0 <= bci && bci < code_size()), "illegal bci"); + assert((is_native() && bci == 0) || (!is_native() && 0 <= bci && bci < code_size()), err_msg("illegal bci: %d", bci)); address bcp = code_base() + bci; assert(is_native() && bcp == code_base() || contains(bcp), "bcp doesn't belong to this method"); return bcp; @@ -905,6 +906,19 @@ return adapter->get_c2i_entry(); } +void Method::restore_unshareable_info(TRAPS) { + // Since restore_unshareable_info can be called more than once for a method, don't + // redo any work. If this field is restored, there is nothing to do. + if (_from_compiled_entry == NULL) { + // restore method's vtable by calling a virtual function + restore_vtable(); + + methodHandle mh(THREAD, this); + link_method(mh, CHECK); + } +} + + // The verified_code_entry() must be called when a invoke is resolved // on this method. @@ -1413,7 +1427,7 @@ void type_name(const char* name) { if (_use_separator) _st->print(", "); - _st->print(name); + _st->print("%s", name); _use_separator = true; } @@ -1860,6 +1874,14 @@ loader_data->jmethod_ids()->clear_all_methods(); } +bool Method::has_method_vptr(const void* ptr) { + Method m; + // This assumes that the vtbl pointer is the first word of a C++ object. + // This assumption is also in universe.cpp patch_klass_vtble + void* vtbl2 = dereference_vptr((const void*)&m); + void* this_vtbl = dereference_vptr(ptr); + return vtbl2 == this_vtbl; +} // Check that this pointer is valid by checking that the vtbl pointer matches bool Method::is_valid_method() const { @@ -1868,12 +1890,7 @@ } else if (!is_metaspace_object()) { return false; } else { - Method m; - // This assumes that the vtbl pointer is the first word of a C++ object. - // This assumption is also in universe.cpp patch_klass_vtble - void* vtbl2 = dereference_vptr((void*)&m); - void* this_vtbl = dereference_vptr((void*)this); - return vtbl2 == this_vtbl; + return has_method_vptr((const void*)this); } } @@ -1891,7 +1908,7 @@ void Method::print_on(outputStream* st) const { ResourceMark rm; assert(is_method(), "must be method"); - st->print_cr(internal_name()); + st->print_cr("%s", internal_name()); // get the effect of PrintOopAddress, always, for methods: st->print_cr(" - this oop: "INTPTR_FORMAT, (intptr_t)this); st->print (" - method holder: "); method_holder()->print_value_on(st); st->cr(); @@ -1974,7 +1991,7 @@ void Method::print_value_on(outputStream* st) const { assert(is_method(), "must be method"); - st->print(internal_name()); + st->print("%s", internal_name()); print_address_on(st); st->print(" "); name()->print_value_on(st); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/method.hpp --- a/src/share/vm/oops/method.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/method.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -160,6 +160,8 @@ void restore_vtable() { guarantee(is_method(), "vtable restored by this call"); } bool is_method() const volatile { return true; } + void restore_unshareable_info(TRAPS); + // accessors for instance variables ConstMethod* constMethod() const { return _constMethod; } @@ -354,16 +356,21 @@ } void set_method_data(MethodData* data) { - _method_data = 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. + OrderAccess::release_store_ptr((volatile void *)&_method_data, data); } MethodCounters* method_counters() const { return _method_counters; } - void set_method_counters(MethodCounters* counters) { - _method_counters = counters; + // 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. + OrderAccess::release_store_ptr((volatile void *)&_method_counters, counters); } #ifdef TIERED @@ -890,6 +897,7 @@ const char* internal_name() const { return "{method}"; } // Check for valid method pointer + static bool has_method_vptr(const void* ptr); bool is_valid_method() const; // Verify diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/methodData.cpp --- a/src/share/vm/oops/methodData.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/methodData.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/systemDictionary.hpp" +#include "compiler/compilerOracle.hpp" #include "interpreter/bytecode.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/linkResolver.hpp" @@ -34,6 +35,8 @@ #include "runtime/deoptimization.hpp" #include "runtime/handles.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // ================================================================== // DataLayout // @@ -80,8 +83,42 @@ _data = NULL; } +char* ProfileData::print_data_on_helper(const MethodData* md) const { + DataLayout* dp = md->extra_data_base(); + DataLayout* end = md->extra_data_limit(); + stringStream ss; + for (;; dp = MethodData::next_extra(dp)) { + assert(dp < end, "moved past end of extra data"); + switch(dp->tag()) { + case DataLayout::speculative_trap_data_tag: + if (dp->bci() == bci()) { + SpeculativeTrapData* data = new SpeculativeTrapData(dp); + int trap = data->trap_state(); + char buf[100]; + ss.print("trap/"); + data->method()->print_short_name(&ss); + ss.print("(%s) ", Deoptimization::format_trap_state(buf, sizeof(buf), trap)); + } + break; + case DataLayout::bit_data_tag: + break; + case DataLayout::no_tag: + case DataLayout::arg_info_data_tag: + return ss.as_string(); + break; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); + } + } + return NULL; +} + +void ProfileData::print_data_on(outputStream* st, const MethodData* md) const { + print_data_on(st, print_data_on_helper(md)); +} + #ifndef PRODUCT -void ProfileData::print_shared(outputStream* st, const char* name) const { +void ProfileData::print_shared(outputStream* st, const char* name, const char* extra) const { st->print("bci: %d", bci()); st->fill_to(tab_width_one); st->print("%s", name); @@ -91,9 +128,13 @@ char buf[100]; st->print("trap(%s) ", Deoptimization::format_trap_state(buf, sizeof(buf), trap)); } + if (extra != NULL) { + st->print("%s", extra); + } int flags = data()->flags(); - if (flags != 0) + if (flags != 0) { st->print("flags(%d) ", flags); + } } void ProfileData::tab(outputStream* st, bool first) const { @@ -109,8 +150,8 @@ #ifndef PRODUCT -void BitData::print_data_on(outputStream* st) const { - print_shared(st, "BitData"); +void BitData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "BitData", extra); } #endif // !PRODUCT @@ -120,8 +161,8 @@ // A CounterData corresponds to a simple counter. #ifndef PRODUCT -void CounterData::print_data_on(outputStream* st) const { - print_shared(st, "CounterData"); +void CounterData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "CounterData", extra); st->print_cr("count(%u)", count()); } #endif // !PRODUCT @@ -150,8 +191,8 @@ } #ifndef PRODUCT -void JumpData::print_data_on(outputStream* st) const { - print_shared(st, "JumpData"); +void JumpData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "JumpData", extra); st->print_cr("taken(%u) displacement(%d)", taken(), displacement()); } #endif // !PRODUCT @@ -332,8 +373,8 @@ st->cr(); } -void CallTypeData::print_data_on(outputStream* st) const { - CounterData::print_data_on(st); +void CallTypeData::print_data_on(outputStream* st, const char* extra) const { + CounterData::print_data_on(st, extra); if (has_arguments()) { tab(st, true); st->print("argument types"); @@ -346,8 +387,8 @@ } } -void VirtualCallTypeData::print_data_on(outputStream* st) const { - VirtualCallData::print_data_on(st); +void VirtualCallTypeData::print_data_on(outputStream* st, const char* extra) const { + VirtualCallData::print_data_on(st, extra); if (has_arguments()) { tab(st, true); st->print("argument types"); @@ -426,8 +467,8 @@ } } } -void ReceiverTypeData::print_data_on(outputStream* st) const { - print_shared(st, "ReceiverTypeData"); +void ReceiverTypeData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "ReceiverTypeData", extra); print_receiver_data_on(st); } @@ -456,8 +497,8 @@ } #endif -void VirtualCallData::print_data_on(outputStream* st) const { - print_shared(st, "VirtualCallData"); +void VirtualCallData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "VirtualCallData", extra); print_receiver_data_on(st); #ifdef GRAAL print_method_data_on(st); @@ -509,10 +550,15 @@ return mdp; } +#ifdef CC_INTERP +DataLayout* RetData::advance(MethodData *md, int bci) { + return (DataLayout*) md->bci_to_dp(bci); +} +#endif // CC_INTERP #ifndef PRODUCT -void RetData::print_data_on(outputStream* st) const { - print_shared(st, "RetData"); +void RetData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "RetData", extra); uint row; int entries = 0; for (row = 0; row < row_limit(); row++) { @@ -546,8 +592,8 @@ } #ifndef PRODUCT -void BranchData::print_data_on(outputStream* st) const { - print_shared(st, "BranchData"); +void BranchData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "BranchData", extra); st->print_cr("taken(%u) displacement(%d)", taken(), displacement()); tab(st); @@ -620,8 +666,8 @@ } #ifndef PRODUCT -void MultiBranchData::print_data_on(outputStream* st) const { - print_shared(st, "MultiBranchData"); +void MultiBranchData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "MultiBranchData", extra); st->print_cr("default_count(%u) displacement(%d)", default_count(), default_displacement()); int cases = number_of_cases(); @@ -634,8 +680,8 @@ #endif #ifndef PRODUCT -void ArgInfoData::print_data_on(outputStream* st) const { - print_shared(st, "ArgInfoData"); +void ArgInfoData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "ArgInfoData", extra); int nargs = number_of_args(); for (int i = 0; i < nargs; i++) { st->print(" 0x%x", arg_modified(i)); @@ -666,10 +712,17 @@ } #ifndef PRODUCT -void ParametersTypeData::print_data_on(outputStream* st) const { - st->print("parameter types"); +void ParametersTypeData::print_data_on(outputStream* st, const char* extra) const { + st->print("parameter types"); // FIXME extra ignored? _parameters.print_data_on(st); } + +void SpeculativeTrapData::print_data_on(outputStream* st, const char* extra) const { + print_shared(st, "SpeculativeTrapData", extra); + tab(st); + method()->print_short_name(st); + st->cr(); +} #endif // ================================================================== @@ -795,15 +848,47 @@ return DataLayout::compute_size_in_bytes(cell_count); } +bool MethodData::is_speculative_trap_bytecode(Bytecodes::Code code) { + // Bytecodes for which we may use speculation + switch (code) { + case Bytecodes::_checkcast: + case Bytecodes::_instanceof: + case Bytecodes::_aastore: + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_invokestatic: +#ifdef COMPILER2 + return UseTypeSpeculation; +#endif + default: + return false; + } + return false; +} + #ifdef GRAAL -int MethodData::compute_extra_data_count(int data_size, int empty_bc_count) { +int MethodData::compute_extra_data_count(int data_size, int empty_bc_count, bool needs_speculative_traps) { if (!ProfileTraps) return 0; // Assume that up to 30% of the possibly trapping BCIs with no MDP will need to allocate one. - return MIN2(empty_bc_count, MAX2(4, (empty_bc_count * 30) / 100)); + int extra_data_count = MIN2(empty_bc_count, MAX2(4, (empty_bc_count * 30) / 100)); + + // Make sure we have a minimum number of extra data slots to + // allocate SpeculativeTrapData entries. We would want to have one + // entry per compilation that inlines this method and for which + // some type speculation assumption fails. So the room we need for + // the SpeculativeTrapData entries doesn't directly depend on the + // size of the method. Because it's hard to estimate, we reserve + // space for an arbitrary number of entries. + int spec_data_count = (needs_speculative_traps ? SpecTrapLimitExtraEntries : 0) * + (SpeculativeTrapData::static_cell_count() + DataLayout::header_size_in_cells()); + + return MAX2(extra_data_count, spec_data_count); } #else -int MethodData::compute_extra_data_count(int data_size, int empty_bc_count) { +int MethodData::compute_extra_data_count(int data_size, int empty_bc_count, bool needs_speculative_traps) { if (ProfileTraps) { // Assume that up to 3% of BCIs with no MDP will need to allocate one. int extra_data_count = (uint)(empty_bc_count * 3) / 128 + 1; @@ -814,7 +899,18 @@ extra_data_count = one_percent_of_data; if (extra_data_count > empty_bc_count) extra_data_count = empty_bc_count; // no need for more - return extra_data_count; + + // Make sure we have a minimum number of extra data slots to + // allocate SpeculativeTrapData entries. We would want to have one + // entry per compilation that inlines this method and for which + // some type speculation assumption fails. So the room we need for + // the SpeculativeTrapData entries doesn't directly depend on the + // size of the method. Because it's hard to estimate, we reserve + // space for an arbitrary number of entries. + int spec_data_count = (needs_speculative_traps ? SpecTrapLimitExtraEntries : 0) * + (SpeculativeTrapData::static_cell_count() + DataLayout::header_size_in_cells()); + + return MAX2(extra_data_count, spec_data_count); } else { return 0; } @@ -828,15 +924,17 @@ BytecodeStream stream(method); Bytecodes::Code c; int empty_bc_count = 0; // number of bytecodes lacking data + bool needs_speculative_traps = false; while ((c = stream.next()) >= 0) { int size_in_bytes = compute_data_size(&stream); data_size += size_in_bytes; if (size_in_bytes == 0 GRAAL_ONLY(&& Bytecodes::can_trap(c))) empty_bc_count += 1; + needs_speculative_traps = needs_speculative_traps || is_speculative_trap_bytecode(c); } int object_size = in_bytes(data_offset()) + data_size; // Add some extra DataLayout cells (at least one) to track stray traps. - int extra_data_count = compute_extra_data_count(data_size, empty_bc_count); + int extra_data_count = compute_extra_data_count(data_size, empty_bc_count, needs_speculative_traps); object_size += extra_data_count * DataLayout::compute_size_in_bytes(0); // Add a cell to record information about modified arguments. @@ -1052,12 +1150,14 @@ } // Initialize the MethodData* corresponding to a given method. -MethodData::MethodData(methodHandle method, int size, TRAPS) { +MethodData::MethodData(methodHandle method, int size, TRAPS) + : _extra_data_lock(Monitor::leaf, "MDO extra data lock") { // Set the method back-pointer. _method = method(); initialize(); } +//TODO remove useless for_reprofile argument void MethodData::initialize(bool for_reprofile) { No_Safepoint_Verifier no_safepoint; // init function atomic wrt GC ResourceMark rm; @@ -1072,24 +1172,22 @@ _data[0] = 0; // apparently not set below. BytecodeStream stream(method()); Bytecodes::Code c; + bool needs_speculative_traps = false; while ((c = stream.next()) >= 0) { int size_in_bytes = initialize_data(&stream, data_size); data_size += size_in_bytes; if (size_in_bytes == 0 GRAAL_ONLY(&& Bytecodes::can_trap(c))) empty_bc_count += 1; + needs_speculative_traps = needs_speculative_traps || is_speculative_trap_bytecode(c); } _data_size = data_size; int object_size = in_bytes(data_offset()) + data_size; // Add some extra DataLayout cells (at least one) to track stray traps. - int extra_data_count = compute_extra_data_count(data_size, empty_bc_count); + int extra_data_count = compute_extra_data_count(data_size, empty_bc_count, needs_speculative_traps); int extra_size = extra_data_count * DataLayout::compute_size_in_bytes(0); -#ifdef GRAAL - if (for_reprofile) { - // Clear out extra data - Copy::zero_to_bytes((HeapWord*) extra_data_base(), extra_size); - } -#endif + // Let's zero the space for the extra data + Copy::zero_to_bytes(((address)_data) + data_size, extra_size); // Add a cell to record information about modified arguments. // Set up _args_modified array after traps cells so that @@ -1102,17 +1200,17 @@ int arg_data_size = DataLayout::compute_size_in_bytes(arg_size+1); object_size += extra_size + arg_data_size; - int args_cell = ParametersTypeData::compute_cell_count(method()); + int parms_cell = ParametersTypeData::compute_cell_count(method()); // If we are profiling parameters, we reserver an area near the end // of the MDO after the slots for bytecodes (because there's no bci // for method entry so they don't fit with the framework for the // profiling of bytecodes). We store the offset within the MDO of // this area (or -1 if no parameter is profiled) - if (args_cell > 0) { - object_size += DataLayout::compute_size_in_bytes(args_cell); + if (parms_cell > 0) { + object_size += DataLayout::compute_size_in_bytes(parms_cell); _parameters_type_data_di = data_size + extra_size + arg_data_size; DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size); - dp->initialize(DataLayout::parameters_type_data_tag, 0, args_cell); + dp->initialize(DataLayout::parameters_type_data_tag, 0, parms_cell); } else { _parameters_type_data_di = -1; } @@ -1143,6 +1241,21 @@ _graal_node_count = 0; #endif +#if INCLUDE_RTM_OPT + _rtm_state = NoRTM; // No RTM lock eliding by default + if (UseRTMLocking && + !CompilerOracle::has_option_string(_method, "NoRTMLockEliding")) { + if (CompilerOracle::has_option_string(_method, "UseRTMLockEliding") || !UseRTMDeopt) { + // Generate RTM lock eliding code without abort ratio calculation code. + _rtm_state = UseRTM; + } else if (UseRTMDeopt) { + // Generate RTM lock eliding code and include abort ratio calculation + // code if UseRTMDeopt is on. + _rtm_state = ProfileRTM; + } + } +#endif + // Initialize flags and trap history. _nof_decompiles = 0; _nof_overflow_recompiles = 0; @@ -1207,39 +1320,114 @@ break; } } - return bci_to_extra_data(bci, false); + return bci_to_extra_data(bci, NULL, false); } -// Translate a bci to its corresponding extra data, or NULL. -ProfileData* MethodData::bci_to_extra_data(int bci, bool create_if_missing) { - DataLayout* dp = extra_data_base(); - DataLayout* end = extra_data_limit(); - DataLayout* avail = NULL; - for (; dp < end; dp = next_extra(dp)) { +DataLayout* MethodData::next_extra(DataLayout* dp) { + int nb_cells = 0; + switch(dp->tag()) { + case DataLayout::bit_data_tag: + case DataLayout::no_tag: + nb_cells = BitData::static_cell_count(); + break; + case DataLayout::speculative_trap_data_tag: + nb_cells = SpeculativeTrapData::static_cell_count(); + break; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); + } + return (DataLayout*)((address)dp + DataLayout::compute_size_in_bytes(nb_cells)); +} + +ProfileData* MethodData::bci_to_extra_data_helper(int bci, Method* m, DataLayout*& dp, bool concurrent) { + DataLayout* end = extra_data_limit(); + + for (;; dp = next_extra(dp)) { + assert(dp < end, "moved past end of extra data"); // No need for "OrderAccess::load_acquire" ops, // since the data structure is monotonic. - if (dp->tag() == DataLayout::no_tag) break; - if (dp->tag() == DataLayout::arg_info_data_tag) { - dp = end; // ArgInfoData is at the end of extra data section. + switch(dp->tag()) { + case DataLayout::no_tag: + return NULL; + case DataLayout::arg_info_data_tag: + dp = end; + return NULL; // ArgInfoData is at the end of extra data section. + case DataLayout::bit_data_tag: + if (m == NULL && dp->bci() == bci) { + return new BitData(dp); + } break; - } - if (dp->bci() == bci) { - assert(dp->tag() == DataLayout::bit_data_tag, "sane"); - return new BitData(dp); + case DataLayout::speculative_trap_data_tag: + if (m != NULL) { + SpeculativeTrapData* data = new SpeculativeTrapData(dp); + // data->method() may be null in case of a concurrent + // allocation. Maybe it's for the same method. Try to use that + // entry in that case. + if (dp->bci() == bci) { + if (data->method() == NULL) { + assert(concurrent, "impossible because no concurrent allocation"); + return NULL; + } else if (data->method() == m) { + return data; + } + } + } + break; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); } } + return NULL; +} + + +// Translate a bci to its corresponding extra data, or NULL. +ProfileData* MethodData::bci_to_extra_data(int bci, Method* m, bool create_if_missing) { + // This code assumes an entry for a SpeculativeTrapData is 2 cells + assert(2*DataLayout::compute_size_in_bytes(BitData::static_cell_count()) == + DataLayout::compute_size_in_bytes(SpeculativeTrapData::static_cell_count()), + "code needs to be adjusted"); + + DataLayout* dp = extra_data_base(); + DataLayout* end = extra_data_limit(); + + // Allocation in the extra data space has to be atomic because not + // all entries have the same size and non atomic concurrent + // allocation would result in a corrupted extra data space. + ProfileData* result = bci_to_extra_data_helper(bci, m, dp, true); + if (result != NULL) { + return result; + } + if (create_if_missing && dp < end) { - // Allocate this one. There is no mutual exclusion, - // so two threads could allocate different BCIs to the - // same data layout. This means these extra data - // records, like most other MDO contents, must not be - // trusted too much. + MutexLocker ml(&_extra_data_lock); + // Check again now that we have the lock. Another thread may + // have added extra data entries. + ProfileData* result = bci_to_extra_data_helper(bci, m, dp, false); + if (result != NULL || dp >= end) { + return result; + } + + assert(dp->tag() == DataLayout::no_tag || (dp->tag() == DataLayout::speculative_trap_data_tag && m != NULL), "should be free"); + assert(next_extra(dp)->tag() == DataLayout::no_tag || next_extra(dp)->tag() == DataLayout::arg_info_data_tag, "should be free or arg info"); + u1 tag = m == NULL ? DataLayout::bit_data_tag : DataLayout::speculative_trap_data_tag; + // SpeculativeTrapData is 2 slots. Make sure we have room. + if (m != NULL && next_extra(dp)->tag() != DataLayout::no_tag) { + return NULL; + } DataLayout temp; - temp.initialize(DataLayout::bit_data_tag, bci, 0); - dp->release_set_header(temp.header()); - assert(dp->tag() == DataLayout::bit_data_tag, "sane"); - //NO: assert(dp->bci() == bci, "no concurrent allocation"); - return new BitData(dp); + temp.initialize(tag, bci, 0); + + dp->set_header(temp.header()); + assert(dp->tag() == tag, "sane"); + assert(dp->bci() == bci, "no concurrent allocation"); + if (tag == DataLayout::bit_data_tag) { + return new BitData(dp); + } else { + SpeculativeTrapData* data = new SpeculativeTrapData(dp); + data->set_method(m); + return data; + } } return NULL; } @@ -1284,25 +1472,35 @@ for ( ; is_valid(data); data = next_data(data)) { st->print("%d", dp_to_di(data->dp())); st->fill_to(6); - data->print_data_on(st); + data->print_data_on(st, this); } st->print_cr("--- Extra data:"); DataLayout* dp = extra_data_base(); DataLayout* end = extra_data_limit(); - for (; dp < end; dp = next_extra(dp)) { + for (;; dp = next_extra(dp)) { + assert(dp < end, "moved past end of extra data"); // No need for "OrderAccess::load_acquire" ops, // since the data structure is monotonic. - if (dp->tag() == DataLayout::no_tag) continue; - if (dp->tag() == DataLayout::bit_data_tag) { + switch(dp->tag()) { + case DataLayout::no_tag: + continue; + case DataLayout::bit_data_tag: data = new BitData(dp); - } else { - assert(dp->tag() == DataLayout::arg_info_data_tag, "must be BitData or ArgInfo"); + break; + case DataLayout::speculative_trap_data_tag: + data = new SpeculativeTrapData(dp); + break; + case DataLayout::arg_info_data_tag: data = new ArgInfoData(dp); dp = end; // ArgInfoData is at the end of extra data section. + break; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); } st->print("%d", dp_to_di(data->dp())); st->fill_to(6); data->print_data_on(st); + if (dp >= end) return; } } #endif @@ -1426,10 +1624,117 @@ return m->is_compiled_lambda_form(); } +void MethodData::clean_extra_data_helper(DataLayout* dp, int shift, bool reset) { + if (shift == 0) { + return; + } + if (!reset) { + // Move all cells of trap entry at dp left by "shift" cells + intptr_t* start = (intptr_t*)dp; + intptr_t* end = (intptr_t*)next_extra(dp); + for (intptr_t* ptr = start; ptr < end; ptr++) { + *(ptr-shift) = *ptr; + } + } else { + // Reset "shift" cells stopping at dp + intptr_t* start = ((intptr_t*)dp) - shift; + intptr_t* end = (intptr_t*)dp; + for (intptr_t* ptr = start; ptr < end; ptr++) { + *ptr = 0; + } + } +} + +// Remove SpeculativeTrapData entries that reference an unloaded +// method +void MethodData::clean_extra_data(BoolObjectClosure* is_alive) { + DataLayout* dp = extra_data_base(); + DataLayout* end = extra_data_limit(); + + int shift = 0; + for (; dp < end; dp = next_extra(dp)) { + switch(dp->tag()) { + case DataLayout::speculative_trap_data_tag: { + SpeculativeTrapData* data = new SpeculativeTrapData(dp); + Method* m = data->method(); + assert(m != NULL, "should have a method"); + if (!m->method_holder()->is_loader_alive(is_alive)) { + // "shift" accumulates the number of cells for dead + // SpeculativeTrapData entries that have been seen so + // far. Following entries must be shifted left by that many + // cells to remove the dead SpeculativeTrapData entries. + shift += (int)((intptr_t*)next_extra(dp) - (intptr_t*)dp); + } else { + // Shift this entry left if it follows dead + // SpeculativeTrapData entries + clean_extra_data_helper(dp, shift); + } + break; + } + case DataLayout::bit_data_tag: + // Shift this entry left if it follows dead SpeculativeTrapData + // entries + clean_extra_data_helper(dp, shift); + continue; + case DataLayout::no_tag: + case DataLayout::arg_info_data_tag: + // We are at end of the live trap entries. The previous "shift" + // cells contain entries that are either dead or were shifted + // left. They need to be reset to no_tag + clean_extra_data_helper(dp, shift, true); + return; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); + } + } +} + +// Verify there's no unloaded method referenced by a +// SpeculativeTrapData entry +void MethodData::verify_extra_data_clean(BoolObjectClosure* is_alive) { +#ifdef ASSERT + DataLayout* dp = extra_data_base(); + DataLayout* end = extra_data_limit(); + + for (; dp < end; dp = next_extra(dp)) { + switch(dp->tag()) { + case DataLayout::speculative_trap_data_tag: { + SpeculativeTrapData* data = new SpeculativeTrapData(dp); + Method* m = data->method(); + assert(m != NULL && m->method_holder()->is_loader_alive(is_alive), "Method should exist"); + break; + } + case DataLayout::bit_data_tag: + continue; + case DataLayout::no_tag: + case DataLayout::arg_info_data_tag: + return; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); + } + } +#endif +} + +void MethodData::clean_method_data(BoolObjectClosure* is_alive) { + for (ProfileData* data = first_data(); + is_valid(data); + data = next_data(data)) { + data->clean_weak_klass_links(is_alive); + } + ParametersTypeData* parameters = parameters_type_data(); + if (parameters != NULL) { + parameters->clean_weak_klass_links(is_alive); + } + + clean_extra_data(is_alive); + verify_extra_data_clean(is_alive); +} + void MethodData::clean_weak_method_links() { for (ProfileData* data = first_data(); is_valid(data); data = next_data(data)) { data->clean_weak_method_links(); } -} +} \ No newline at end of file diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/methodData.hpp --- a/src/share/vm/oops/methodData.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/methodData.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -120,7 +120,8 @@ arg_info_data_tag, call_type_data_tag, virtual_call_type_data_tag, - parameters_type_data_tag + parameters_type_data_tag, + speculative_trap_data_tag }; enum { @@ -189,9 +190,6 @@ void set_header(intptr_t value) { _header._bits = value; } - void release_set_header(intptr_t value) { - OrderAccess::release_store_ptr(&_header._bits, value); - } intptr_t header() { return _header._bits; } @@ -230,6 +228,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"); @@ -269,6 +272,7 @@ class MultiBranchData; class ArgInfoData; class ParametersTypeData; +class SpeculativeTrapData; // ProfileData // @@ -289,6 +293,8 @@ // This is a pointer to a section of profiling data. DataLayout* _data; + char* print_data_on_helper(const MethodData* md) const; + protected: DataLayout* data() { return _data; } const DataLayout* data() const { return _data; } @@ -370,6 +376,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 cast_to_oop(layout->cell_at(index)); + } + + static void set_intptr_at(DataLayout* layout, int index, intptr_t 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(); @@ -403,6 +444,7 @@ virtual bool is_CallTypeData() const { return false; } virtual bool is_VirtualCallTypeData()const { return false; } virtual bool is_ParametersTypeData() const { return false; } + virtual bool is_SpeculativeTrapData()const { return false; } BitData* as_BitData() const { @@ -457,6 +499,10 @@ assert(is_ParametersTypeData(), "wrong type"); return is_ParametersTypeData() ? (ParametersTypeData*)this : NULL; } + SpeculativeTrapData* as_SpeculativeTrapData() const { + assert(is_SpeculativeTrapData(), "wrong type"); + return is_SpeculativeTrapData() ? (SpeculativeTrapData*)this : NULL; + } // Subclass specific initialization @@ -475,12 +521,14 @@ // translation here, and the required translators are in the ci subclasses. virtual void translate_from(const ProfileData* data) {} - virtual void print_data_on(outputStream* st) const { + virtual void print_data_on(outputStream* st, const char* extra = NULL) const { ShouldNotReachHere(); } + void print_data_on(outputStream* st, const MethodData* md) const; + #ifndef PRODUCT - void print_shared(outputStream* st, const char* name) const; + void print_shared(outputStream* st, const char* name, const char* extra) const; void tab(outputStream* st, bool first = false) const; #endif }; @@ -535,8 +583,22 @@ 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) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -579,8 +641,27 @@ 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) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -649,11 +730,25 @@ 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, MethodData* mdo); #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -941,6 +1036,11 @@ static ByteSize argument_type_offset(int i) { return in_ByteSize(argument_type_local_offset(i) * DataLayout::cell_size); } + + static ByteSize return_only_size() { + return ReturnTypeEntry::size() + in_ByteSize(header_cell_count() * DataLayout::cell_size); + } + }; // CallTypeData @@ -1064,7 +1164,7 @@ } #ifndef PRODUCT - virtual void print_data_on(outputStream* st) const; + virtual void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1203,9 +1303,46 @@ // GC support virtual void clean_weak_klass_links(BoolObjectClosure* is_alive_closure); +#ifdef CC_INTERP + static int receiver_type_data_size_in_bytes() { + return cell_offset_in_bytes(static_cell_count()); + } + + static Klass *receiver_unchecked(DataLayout* layout, uint row) { + Klass* recv = (Klass*)layout->cell_at(receiver_cell_index(row)); + return recv; + } + + static void increment_receiver_count_no_overflow(DataLayout* layout, Klass *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_intptr_at(layout, receiver_cell_index(row), (intptr_t)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) const; - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1237,6 +1374,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 + #ifdef GRAAL static ByteSize method_offset(uint row) { return cell_offset(method_cell_index(row)); @@ -1296,7 +1443,7 @@ #ifdef GRAAL void print_method_data_on(outputStream* st) const; #endif - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1422,7 +1569,7 @@ } #ifndef PRODUCT - virtual void print_data_on(outputStream* st) const; + virtual void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1517,11 +1664,15 @@ return cell_offset(bci_displacement_cell_index(row)); } +#ifdef CC_INTERP + static DataLayout* advance(MethodData *md, int bci); +#endif // CC_INTERP + // Specific initialization. void post_initialize(BytecodeStream* stream, MethodData* mdo); #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1581,11 +1732,25 @@ 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, MethodData* mdo); #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1620,6 +1785,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); @@ -1738,11 +1917,33 @@ 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, MethodData* mdo); #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1769,7 +1970,7 @@ } #ifndef PRODUCT - void print_data_on(outputStream* st) const; + void print_data_on(outputStream* st, const char* extra = NULL) const; #endif }; @@ -1830,7 +2031,7 @@ } #ifndef PRODUCT - virtual void print_data_on(outputStream* st) const; + virtual void print_data_on(outputStream* st, const char* extra = NULL) const; #endif static ByteSize stack_slot_offset(int i) { @@ -1842,6 +2043,54 @@ } }; +// SpeculativeTrapData +// +// A SpeculativeTrapData is used to record traps due to type +// speculation. It records the root of the compilation: that type +// speculation is wrong in the context of one compilation (for +// method1) doesn't mean it's wrong in the context of another one (for +// method2). Type speculation could have more/different data in the +// context of the compilation of method2 and it's worthwhile to try an +// optimization that failed for compilation of method1 in the context +// of compilation of method2. +// Space for SpeculativeTrapData entries is allocated from the extra +// data space in the MDO. If we run out of space, the trap data for +// the ProfileData at that bci is updated. +class SpeculativeTrapData : public ProfileData { +protected: + enum { + method_offset, + speculative_trap_cell_count + }; +public: + SpeculativeTrapData(DataLayout* layout) : ProfileData(layout) { + assert(layout->tag() == DataLayout::speculative_trap_data_tag, "wrong type"); + } + + virtual bool is_SpeculativeTrapData() const { return true; } + + static int static_cell_count() { + return speculative_trap_cell_count; + } + + virtual int cell_count() const { + return static_cell_count(); + } + + // Direct accessor + Method* method() const { + return (Method*)intptr_at(method_offset); + } + + void set_method(Method* m) { + set_intptr_at(method_offset, (intptr_t)m); + } + +#ifndef PRODUCT + virtual void print_data_on(outputStream* st, const char* extra = NULL) const; +#endif +}; + // MethodData* // // A MethodData* holds information which has been collected about @@ -1885,8 +2134,11 @@ // adjusted in the event of a change in control flow. // +CC_INTERP_ONLY(class BytecodeInterpreter;) + class MethodData : public Metadata { friend class VMStructs; + CC_INTERP_ONLY(friend class BytecodeInterpreter;) private: friend class ProfileData; @@ -1899,21 +2151,19 @@ // Cached hint for bci_to_dp and bci_to_data int _hint_di; + Mutex _extra_data_lock; + MethodData(methodHandle method, int size, TRAPS); public: static MethodData* allocate(ClassLoaderData* loader_data, methodHandle method, TRAPS); - MethodData() {}; // For ciMethodData + MethodData() : _extra_data_lock(Monitor::leaf, "MDO extra data lock") {}; // For ciMethodData bool is_methodData() const volatile { return true; } void initialize(bool for_reprofile = false); // Whole-method sticky bits and flags enum { -#ifdef GRAAL - _trap_hist_limit = 19, // decoupled from Deoptimization::Reason_LIMIT -#else - _trap_hist_limit = 17, // decoupled from Deoptimization::Reason_LIMIT -#endif + _trap_hist_limit = 19 GRAAL_ONLY(+2), // decoupled from Deoptimization::Reason_LIMIT _trap_hist_mask = max_jubyte, _extra_data_count = 4 // extra DataLayout headers, for trap history }; // Public flag values @@ -1948,6 +2198,12 @@ // Counter values at the time profiling started. int _invocation_counter_start; int _backedge_counter_start; + +#if INCLUDE_RTM_OPT + // State of RTM code generation during compilation of the method + int _rtm_state; +#endif + // Number of loops and blocks is computed when compiling the first // time with C1. It is used to determine if method is trivial. short _num_loops; @@ -1977,6 +2233,7 @@ // Helper for size computation static int compute_data_size(BytecodeStream* stream); static int bytecode_cell_count(Bytecodes::Code code); + static bool is_speculative_trap_bytecode(Bytecodes::Code code); enum { no_profile_data = -1, variable_cell_count = -2 }; // Helper for initialization @@ -2020,8 +2277,9 @@ // What is the index of the first data entry? int first_di() const { return 0; } + ProfileData* bci_to_extra_data_helper(int bci, Method* m, DataLayout*& dp, bool concurrent); // Find or create an extra ProfileData: - ProfileData* bci_to_extra_data(int bci, bool create_if_missing); + ProfileData* bci_to_extra_data(int bci, Method* m, bool create_if_missing); // return the argument info cell ArgInfoData *arg_info(); @@ -2034,7 +2292,6 @@ static bool profile_jsr292(methodHandle m, int bci); static int profile_arguments_flag(); - static bool profile_arguments_jsr292_only(); static bool profile_all_arguments(); static bool profile_arguments_for_invoke(methodHandle m, int bci); static int profile_return_flag(); @@ -2044,6 +2301,10 @@ static bool profile_parameters_jsr292_only(); static bool profile_all_parameters(); + void clean_extra_data(BoolObjectClosure* is_alive); + void clean_extra_data_helper(DataLayout* dp, int shift, bool reset = false); + void verify_extra_data_clean(BoolObjectClosure* is_alive); + public: static int header_size() { return sizeof(MethodData)/wordSize; @@ -2052,7 +2313,7 @@ // Compute the size of a MethodData* before it is created. static int compute_allocation_size_in_bytes(methodHandle method); static int compute_allocation_size_in_words(methodHandle method); - static int compute_extra_data_count(int data_size, int empty_bc_count); + static int compute_extra_data_count(int data_size, int empty_bc_count, bool needs_speculative_traps); // Determine if a given bytecode can have profile information. static bool bytecode_has_profile(Bytecodes::Code code) { @@ -2110,6 +2371,22 @@ InvocationCounter* invocation_counter() { return &_invocation_counter; } InvocationCounter* backedge_counter() { return &_backedge_counter; } +#if INCLUDE_RTM_OPT + int rtm_state() const { + return _rtm_state; + } + void set_rtm_state(RTMState rstate) { + _rtm_state = (int)rstate; + } + void atomic_set_rtm_state(RTMState rstate) { + Atomic::store((int)rstate, &_rtm_state); + } + + static int rtm_state_offset_in_bytes() { + return offset_of(MethodData, _rtm_state); + } +#endif + void set_would_profile(bool p) { _would_profile = p; } bool would_profile() const { return _would_profile; } @@ -2193,9 +2470,26 @@ ProfileData* bci_to_data(int bci); // Same, but try to create an extra_data record if one is needed: - ProfileData* allocate_bci_to_data(int bci) { - ProfileData* data = bci_to_data(bci); - return (data != NULL) ? data : bci_to_extra_data(bci, true); + ProfileData* allocate_bci_to_data(int bci, Method* m) { + ProfileData* data = NULL; + // If m not NULL, try to allocate a SpeculativeTrapData entry + if (m == NULL) { + data = bci_to_data(bci); + } + if (data != NULL) { + return data; + } + data = bci_to_extra_data(bci, m, true); + if (data != NULL) { + return data; + } + // If SpeculativeTrapData allocation fails try to allocate a + // regular entry + data = bci_to_data(bci); + if (data != NULL) { + return data; + } + return bci_to_extra_data(bci, NULL, true); } // Add a handful of extra data records, for trap tracking. @@ -2203,7 +2497,7 @@ DataLayout* extra_data_limit() const { return (DataLayout*)((address)this + size_in_bytes()); } int extra_data_size() const { return (address)extra_data_limit() - (address)extra_data_base(); } - static DataLayout* next_extra(DataLayout* dp) { return (DataLayout*)((address)dp + in_bytes(DataLayout::cell_offset(0))); } + static DataLayout* next_extra(DataLayout* dp); // Return (uint)-1 for overflow. uint trap_count(int reason) const { @@ -2300,10 +2594,13 @@ static bool profile_parameters_for_method(methodHandle m); static bool profile_arguments(); + static bool profile_arguments_jsr292_only(); static bool profile_return(); static bool profile_parameters(); static bool profile_return_jsr292_only(); + void clean_method_data(BoolObjectClosure* is_alive); + void clean_weak_method_links(); }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/objArrayKlass.cpp --- a/src/share/vm/oops/objArrayKlass.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/objArrayKlass.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -674,8 +674,8 @@ // Verification -void ObjArrayKlass::verify_on(outputStream* st, bool check_dictionary) { - ArrayKlass::verify_on(st, check_dictionary); +void ObjArrayKlass::verify_on(outputStream* st) { + ArrayKlass::verify_on(st); guarantee(element_klass()->is_klass(), "should be klass"); guarantee(bottom_klass()->is_klass(), "should be klass"); Klass* bk = bottom_klass(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/objArrayKlass.hpp --- a/src/share/vm/oops/objArrayKlass.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/objArrayKlass.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -151,7 +151,7 @@ const char* internal_name() const; // Verification - void verify_on(outputStream* st, bool check_dictionary); + void verify_on(outputStream* st); void oop_verify_on(oop obj, outputStream* st); }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/oop.cpp --- a/src/share/vm/oops/oop.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/oop.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -30,6 +30,8 @@ #include "runtime/thread.inline.hpp" #include "utilities/copy.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + bool always_do_update_barrier = false; BarrierSet* oopDesc::_bs = NULL; @@ -102,7 +104,7 @@ } // When String table needs to rehash -unsigned int oopDesc::new_hash(jint seed) { +unsigned int oopDesc::new_hash(juint seed) { EXCEPTION_MARK; ResourceMark rm; int length; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/oop.hpp --- a/src/share/vm/oops/oop.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/oop.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -362,7 +362,7 @@ intptr_t slow_identity_hash(); // Alternate hashing code if string table is rehashed - unsigned int new_hash(jint seed); + unsigned int new_hash(juint seed); // marks are forwarded to stack when object is locked bool has_displaced_mark() const; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/oop.inline.hpp --- a/src/share/vm/oops/oop.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/oop.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -209,7 +209,7 @@ address base = Universe::narrow_oop_base(); int shift = Universe::narrow_oop_shift(); oop result = (oop)(void*)((uintptr_t)base + ((uintptr_t)v << shift)); - assert(check_obj_alignment(result), err_msg("address not aligned: " PTR_FORMAT, (void*) result)); + assert(check_obj_alignment(result), err_msg("address not aligned: " INTPTR_FORMAT, p2i((void*) result))); return result; } @@ -490,9 +490,9 @@ return size_given_klass(klass()); } -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) { @@ -505,7 +505,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 } } @@ -513,7 +516,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 } // Should replace *addr = oop assignments where addr type depends on UseCompressedOops diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/symbol.cpp --- a/src/share/vm/oops/symbol.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/symbol.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -207,7 +207,7 @@ } // Alternate hashing for unbalanced symbol tables. -unsigned int Symbol::new_hash(jint seed) { +unsigned int Symbol::new_hash(juint seed) { ResourceMark rm; // Use alternate hashing algorithm on this symbol. return AltHashing::murmur3_32(seed, (const jbyte*)as_C_string(), utf8_length()); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/symbol.hpp --- a/src/share/vm/oops/symbol.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/symbol.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -154,7 +154,7 @@ int identity_hash() { return _identity_hash; } // For symbol table alternate hashing - unsigned int new_hash(jint seed); + unsigned int new_hash(juint seed); // Reference counting. See comments above this class for when to use. int refcount() const { return _refcount; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/oops/typeArrayOop.hpp --- a/src/share/vm/oops/typeArrayOop.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/oops/typeArrayOop.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/block.cpp --- a/src/share/vm/opto/block.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/block.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -144,6 +144,10 @@ remove_node(find_node(n)); } +bool Block::contains(const Node *n) const { + return _nodes.contains(n); +} + // Return empty status of a block. Empty blocks contain only the head, other // ideal nodes, and an optional trailing goto. int Block::is_Empty() const { @@ -335,7 +339,7 @@ st->print(" FRegPressure: %d",_freg_pressure); st->print(" FHRP Index: %d",_fhrp_index); } - st->print_cr(""); + st->cr(); } void Block::dump() const { @@ -526,18 +530,27 @@ // 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->number_of_nodes() - b->_num_succs-1; - if( branch_idx < 1 ) return false; - Node *bra = b->get_node(branch_idx); - if( bra->is_Catch() ) + if (branch_idx < 1) { + return false; + } + Node *branch = b->get_node(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; } @@ -696,10 +709,66 @@ } // 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->get_node(block->number_of_nodes()-3)->as_MachIf(); + ProjNode *proj0 = block->get_node(block->number_of_nodes()-2)->as_Proj(); + ProjNode *proj1 = block->get_node(block->number_of_nodes()-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, get_block_for_node(proj_never ->raw_out(0))); // The target of the trap. + block->_succs.map(1, get_block_for_node(proj_always->raw_out(0))); // The fall through target. + + if (block->get_node(block->number_of_nodes() - block->_num_succs + 1) != proj_always) { + block->map_node(proj_never, block->number_of_nodes() - block->_num_succs + 0); + block->map_node(proj_always, block->number_of_nodes() - block->_num_succs + 1); + } + + // 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; +} + // 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 < number_of_blocks(); i++) { @@ -719,25 +788,39 @@ // Check for multi-way branches where I cannot negate the test to // exchange the true and false targets. if (no_flip_branch(block)) { - // Find fall through case - if must fall into its target + // Find fall through case - if must fall into its target. + // Get the index of the branch's first successor. int branch_idx = block->number_of_nodes() - block->_num_succs; - for (uint j2 = 0; j2 < block->_num_succs; j2++) { - const ProjNode* p = block->get_node(branch_idx + j2)->as_Proj(); - if (p->_con == 0) { - // successor j2 is fall through case - if (block->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 *branch = block->get_node(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) && + branch->is_Mach() && branch->as_Mach()->is_TrapBasedCheckNode()) { + bnext = fixup_trap_based_check(branch, block, i, bnext); + } else { + // Else, default handling for no-flip branches + for (uint j2 = 0; j2 < block->_num_succs; j2++) { + const ProjNode* p = block->get_node(branch_idx + j2)->as_Proj(); + if (p->_con == 0) { + // successor j2 is fall through case + if (block->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 && block->_num_succs == 2) { + // Flip targets in succs map + Block *tbs0 = block->_succs[0]; + Block *tbs1 = block->_succs[1]; + block->_succs.map(0, tbs1); + block->_succs.map(1, tbs0); + } + break; } - // Put taken branch in slot 0 - if (j2 == 0 && block->_num_succs == 2) { - // Flip targets in succs map - Block *tbs0 = block->_succs[0]; - Block *tbs1 = block->_succs[1]; - block->_succs.map(0, tbs1); - block->_succs.map(1, tbs0); - } - break; } } @@ -844,6 +927,228 @@ } +// postalloc_expand: Expand nodes after register allocation. +// +// postalloc_expand has to be called after register allocation, just +// before output (i.e. scheduling). It only gets called if +// Matcher::require_postalloc_expand is true. +// +// 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 postalloc expand are specified in the ad file by using +// a postalloc_expand statement instead of ins_encode. A postalloc_expand +// contains a single call to an encoding, as does an ins_encode +// statement. Instead of an emit() function a postalloc_expand() function +// is generated that doesn't emit assembler but creates a new +// subgraph. The code below calls this postalloc_expand 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 postalloc_expand 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 postalloc_expand function, +// e.g., holding the operands as specified by the postalloc_expand 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. 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::postalloc_expand(PhaseRegAlloc* _ra) { + GrowableArray new_nodes(32); // Array with new nodes filled by postalloc_expand function of node. + GrowableArray remove(32); + GrowableArray succs(32); + unsigned int max_idx = C->unique(); // Remember to distinguish new from old nodes. + DEBUG_ONLY(bool foundNode = false); + + // for all blocks + for (uint i = 0; i < number_of_blocks(); i++) { + Block *b = _blocks[i]; + // For all instructions in the current block. + for (uint j = 0; j < b->number_of_nodes(); j++) { + Node *n = b->get_node(j); + if (n->is_Mach() && n->as_Mach()->requires_postalloc_expand()) { +#ifdef ASSERT + if (TracePostallocExpand) { + if (!foundNode) { + foundNode = true; + tty->print("POSTALLOC EXPANDING %d %s\n", C->compile_id(), + C->method() ? C->method()->name()->as_utf8() : C->stub_name()); + } + tty->print(" postalloc expanding "); n->dump(); + if (Verbose) { + tty->print(" with ins:\n"); + for (uint k = 0; k < n->len(); ++k) { + if (n->in(k)) { tty->print(" "); n->in(k)->dump(); } + } + } + } +#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 postalloc 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 postalloc expand"); + return; + } + + // Emit (i.e. generate new nodes). + n->as_Mach()->postalloc_expand(&new_nodes, _ra); + + assert(C->unique() < unique_limit, "You allocated too many nodes in your postalloc 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); + } + } + +#ifdef ASSERT + // 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; + os->print("Node %s ", m->Name()); + 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->insert_node(n2, ++index); + map_node_to_block(n2, b); + } + + // Add old node n to remove and remove them all from block. + remove.push(n); + j--; +#ifdef ASSERT + if (TracePostallocExpand && Verbose) { + 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 postalloc 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; + } + } + } + +#ifdef ASSERT + if (foundNode) { + tty->print("FINISHED %d %s\n", C->compile_id(), + C->method() ? C->method()->name()->as_utf8() : C->stub_name()); + tty->flush(); + } +#endif +} + + +//------------------------------dump------------------------------------------- #ifndef PRODUCT void PhaseCFG::_dump_cfg( const Node *end, VectorSet &visited ) const { const Node *x = end->is_block_proj(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/block.hpp --- a/src/share/vm/opto/block.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/block.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -313,10 +313,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 ) { insert_node(n, end_idx()); } - // 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; // Return the empty status of a block enum { not_empty, empty_with_goto, completely_empty }; @@ -588,6 +590,7 @@ // Remove empty basic blocks void remove_empty_blocks(); + Block *fixup_trap_based_check(Node *branch, Block *block, int block_pos, Block *bnext); void fixup_flow(); // Insert a node into a block at index and map the node to the block @@ -596,6 +599,9 @@ map_node_to_block(n, b); } + // Check all nodes and postalloc_expand them if necessary. + void postalloc_expand(PhaseRegAlloc* _ra); + #ifndef PRODUCT bool trace_opto_pipelining() const { return _trace_opto_pipelining; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/bytecodeInfo.cpp --- a/src/share/vm/opto/bytecodeInfo.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/bytecodeInfo.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -50,7 +50,10 @@ _subtrees(c->comp_arena(), 2, 0, NULL), _msg(NULL) { - NOT_PRODUCT(_count_inlines = 0;) +#ifndef PRODUCT + _count_inlines = 0; + _forced_inline = false; +#endif if (_caller_jvms != NULL) { // Keep a private copy of the caller_jvms: _caller_jvms = new (C) JVMState(caller_jvms->method(), caller_tree->caller_jvms()); @@ -60,31 +63,14 @@ assert(_caller_jvms->same_calls_as(caller_jvms), "consistent JVMS"); assert((caller_tree == NULL ? 0 : caller_tree->stack_depth() + 1) == stack_depth(), "correct (redundant) depth parameter"); assert(caller_bci == this->caller_bci(), "correct (redundant) bci parameter"); - if (UseOldInlining) { - // Update hierarchical counts, count_inline_bcs() and count_inlines() - InlineTree *caller = (InlineTree *)caller_tree; - for( ; caller != NULL; caller = ((InlineTree *)(caller->caller_tree())) ) { - caller->_count_inline_bcs += count_inline_bcs(); - NOT_PRODUCT(caller->_count_inlines++;) - } + // Update hierarchical counts, count_inline_bcs() and count_inlines() + InlineTree *caller = (InlineTree *)caller_tree; + for( ; caller != NULL; caller = ((InlineTree *)(caller->caller_tree())) ) { + caller->_count_inline_bcs += count_inline_bcs(); + NOT_PRODUCT(caller->_count_inlines++;) } } -InlineTree::InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, - float site_invoke_ratio, int max_inline_level) : - C(c), - _caller_jvms(caller_jvms), - _caller_tree(NULL), - _method(callee_method), - _site_invoke_ratio(site_invoke_ratio), - _max_inline_level(max_inline_level), - _count_inline_bcs(method()->code_size()), - _msg(NULL) -{ - NOT_PRODUCT(_count_inlines = 0;) - assert(!UseOldInlining, "do not use for old stuff"); -} - /** * Return true when EA is ON and a java constructor is called or * a super constructor is called from an inlined java constructor. @@ -128,9 +114,19 @@ tty->print_cr("Inlined method is hot: "); } set_msg("force inline by CompilerOracle"); + _forced_inline = true; return true; } +#ifndef PRODUCT + int inline_depth = inline_level()+1; + if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) { + set_msg("force inline by ciReplay"); + _forced_inline = true; + return true; + } +#endif + int size = callee_method->code_size_for_inlining(); // Check for too many throws (and not too huge) @@ -145,11 +141,6 @@ return true; } - if (!UseOldInlining) { - set_msg("!UseOldInlining"); - return true; // size and frequency are represented in a new way - } - int default_max_inline_size = C->max_inline_size(); int inline_small_code_size = InlineSmallCode / 4; int max_inline_size = default_max_inline_size; @@ -213,35 +204,6 @@ fail_msg = "don't inline by annotation"; } - if (!UseOldInlining) { - if (fail_msg != NULL) { - *wci_result = *(WarmCallInfo::always_cold()); - set_msg(fail_msg); - return true; - } - - if (callee_method->has_unloaded_classes_in_signature()) { - wci_result->set_profit(wci_result->profit() * 0.1); - } - - // don't inline exception code unless the top method belongs to an - // exception class - if (callee_method->holder()->is_subclass_of(C->env()->Throwable_klass())) { - ciMethod* top_method = jvms->caller() != NULL ? jvms->caller()->of_depth(1)->method() : method(); - if (!top_method->holder()->is_subclass_of(C->env()->Throwable_klass())) { - wci_result->set_profit(wci_result->profit() * 0.1); - } - } - - if (callee_method->has_compiled_code() && - callee_method->instructions_size() > InlineSmallCode) { - wci_result->set_profit(wci_result->profit() * 0.1); - // %%% adjust wci_result->size()? - } - - return false; - } - // one more inlining restriction if (fail_msg == NULL && callee_method->has_unloaded_classes_in_signature()) { fail_msg = "unloaded signature classes"; @@ -264,6 +226,18 @@ } #ifndef PRODUCT + int caller_bci = jvms->bci(); + int inline_depth = inline_level()+1; + if (ciReplay::should_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) { + set_msg("force inline by ciReplay"); + return false; + } + + if (ciReplay::should_not_inline(C->replay_inline_data(), callee_method, caller_bci, inline_depth)) { + set_msg("disallowed by ciReplay"); + return true; + } + if (ciReplay::should_not_inline(callee_method)) { set_msg("disallowed by ciReplay"); return true; @@ -332,9 +306,7 @@ int caller_bci, JVMState* jvms, ciCallProfile& profile, WarmCallInfo* wci_result, bool& should_delay) { - // Old algorithm had funny accumulating BC-size counters - if (UseOldInlining && ClipInlining - && (int)count_inline_bcs() >= DesiredMethodLimit) { + if (ClipInlining && (int)count_inline_bcs() >= DesiredMethodLimit) { if (!callee_method->force_inline() || !IncrementalInline) { set_msg("size > DesiredMethodLimit"); return false; @@ -343,6 +315,7 @@ } } + _forced_inline = false; // Reset if (!should_inline(callee_method, caller_method, caller_bci, profile, wci_result)) { return false; @@ -373,10 +346,10 @@ if ((!UseInterpreter || CompileTheWorld) && is_init_with_ea(callee_method, caller_method, C)) { - // Escape Analysis stress testing when running Xcomp or CTW: // inline constructors even if they are not reached. - + } else if (forced_inline()) { + // Inlining was forced by CompilerOracle or ciReplay } else if (profile.count() == 0) { // don't inline unreached call sites set_msg("call site not reached"); @@ -388,11 +361,14 @@ set_msg("not an accessor"); return false; } + + // Limit inlining depth in case inlining is forced or + // _max_inline_level was increased to compensate for lambda forms. + if (inline_level() > MaxForceInlineLevel) { + set_msg("MaxForceInlineLevel"); + return false; + } if (inline_level() > _max_inline_level) { - if (callee_method->force_inline() && inline_level() > MaxForceInlineLevel) { - set_msg("MaxForceInlineLevel"); - return false; - } if (!callee_method->force_inline() || !IncrementalInline) { set_msg("inlining too deep"); return false; @@ -436,8 +412,7 @@ int size = callee_method->code_size_for_inlining(); - if (UseOldInlining && ClipInlining - && (int)count_inline_bcs() + size >= DesiredMethodLimit) { + if (ClipInlining && (int)count_inline_bcs() + size >= DesiredMethodLimit) { if (!callee_method->force_inline() || !IncrementalInline) { set_msg("size > DesiredMethodLimit"); return false; @@ -555,8 +530,7 @@ jvms, profile, &wci, should_delay); #ifndef PRODUCT - if (UseOldInlining && InlineWarmCalls - && (PrintOpto || C->print_inlining())) { + if (InlineWarmCalls && (PrintOpto || C->print_inlining())) { bool cold = wci.is_cold(); bool hot = !cold && wci.is_hot(); bool old_cold = !success; @@ -570,13 +544,12 @@ } } #endif - if (UseOldInlining) { - if (success) { - wci = *(WarmCallInfo::always_hot()); - } else { - wci = *(WarmCallInfo::always_cold()); - } + if (success) { + wci = *(WarmCallInfo::always_hot()); + } else { + wci = *(WarmCallInfo::always_cold()); } + if (!InlineWarmCalls) { if (!wci.is_cold() && !wci.is_hot()) { // Do not inline the warm calls. @@ -590,8 +563,7 @@ set_msg("inline (hot)"); } print_inlining(callee_method, caller_bci, true /* success */); - if (UseOldInlining) - build_inline_tree_for_callee(callee_method, jvms, caller_bci); + build_inline_tree_for_callee(callee_method, jvms, caller_bci); if (InlineWarmCalls && !wci.is_hot()) return new (C) WarmCallInfo(wci); // copy to heap return WarmCallInfo::always_hot(); @@ -700,12 +672,28 @@ return iltp; } +// Count number of nodes in this subtree +int InlineTree::count() const { + int result = 1; + for (int i = 0 ; i < _subtrees.length(); i++) { + result += _subtrees.at(i)->count(); + } + return result; +} + +void InlineTree::dump_replay_data(outputStream* out) { + out->print(" %d %d ", inline_level(), caller_bci()); + method()->dump_name_as_ascii(out); + for (int i = 0 ; i < _subtrees.length(); i++) { + _subtrees.at(i)->dump_replay_data(out); + } +} #ifndef PRODUCT void InlineTree::print_impl(outputStream* st, int indent) const { for (int i = 0; i < indent; i++) st->print(" "); - st->print(" @ %d ", caller_bci()); + st->print(" @ %d", caller_bci()); method()->print_short_name(st); st->cr(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/c2_globals.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -223,7 +229,8 @@ diagnostic(bool, UnrollLimitCheck, true, \ "Additional overflow checks during loop unroll") \ \ - product(bool, OptimizeFill, true, \ + /* OptimizeFill not yet supported on PowerPC. */ \ + product(bool, OptimizeFill, true PPC64_ONLY(&& false), \ "convert fill/copy loops into intrinsic") \ \ develop(bool, TraceOptimizeFill, false, \ @@ -442,13 +449,16 @@ diagnostic(bool, PrintPreciseBiasedLockingStatistics, false, \ "Print per-lock-site statistics of biased locking in JVM") \ \ + diagnostic(bool, PrintPreciseRTMLockingStatistics, false, \ + "Print per-lock-site statistics of rtm locking in JVM") \ + \ notproduct(bool, PrintEliminateLocks, false, \ "Print out when locks are eliminated") \ \ - product(bool, EliminateAutoBox, false, \ + product(bool, EliminateAutoBox, true, \ "Control optimizations for autobox elimination") \ \ - experimental(bool, UseImplicitStableValues, false, \ + diagnostic(bool, UseImplicitStableValues, true, \ "Mark well-known stable fields as such (e.g. String.value)") \ \ product(intx, AutoBoxCacheMax, 128, \ @@ -457,6 +467,9 @@ experimental(bool, AggressiveUnboxing, false, \ "Control optimizations for aggressive boxing elimination") \ \ + develop(bool, TracePostallocExpand, false, "Trace expanding nodes after" \ + " register allocation.") \ + \ product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ @@ -637,14 +650,22 @@ diagnostic(bool, OptimizeExpensiveOps, true, \ "Find best control for expensive operations") \ \ - experimental(bool, UseMathExactIntrinsics, false, \ + product(bool, UseMathExactIntrinsics, true, \ "Enables intrinsification of various java.lang.Math functions") \ \ experimental(bool, ReplaceInParentMaps, false, \ "Propagate type improvements in callers of inlinee if possible") \ \ - experimental(bool, UseTypeSpeculation, false, \ - "Speculatively propagate types from profiles") + product(bool, UseTypeSpeculation, true, \ + "Speculatively propagate types from profiles") \ + \ + diagnostic(bool, UseInlineDepthForSpeculativeTypes, true, \ + "Carry inline depth of profile point with speculative type " \ + "and give priority to profiling from lower inline depth") \ + \ + product_pd(bool, TrapBasedRangeChecks, \ + "Generate code for range checks that uses a cmp and trap " \ + "instruction raising SIGTRAP. Used on PPC64.") \ C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/c2compiler.cpp --- a/src/share/vm/opto/c2compiler.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/c2compiler.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 // register information defined by ADLC diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/callGenerator.cpp --- a/src/share/vm/opto/callGenerator.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/callGenerator.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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))) { @@ -381,7 +381,7 @@ } // Setup default node notes to be picked up by the inlining - Node_Notes* old_nn = C->default_node_notes(); + Node_Notes* old_nn = C->node_notes_at(call->_idx); if (old_nn != NULL) { Node_Notes* entry_nn = old_nn->clone(C); entry_nn->set_jvms(jvms); @@ -722,7 +722,7 @@ Node* m = kit.map()->in(i); Node* n = slow_map->in(i); if (m != n) { - const Type* t = gvn.type(m)->meet(gvn.type(n)); + const Type* t = gvn.type(m)->meet_speculative(gvn.type(n)); Node* phi = PhiNode::make(region, m, t); phi->set_req(2, n); kit.map()->set_req(i, gvn.transform(phi)); @@ -837,8 +837,11 @@ Node* receiver_node = kit.argument(0); const TypeOopPtr* receiver_type = gvn.type(receiver_node)->isa_oopptr(); // call_does_dispatch and vtable_index are out-parameters. They might be changed. - target = C->optimize_virtual_call(caller, jvms->bci(), klass, target, receiver_type, - is_virtual, + // optimize_virtual_call() takes 2 different holder + // arguments for a corner case that doesn't apply here (see + // Parse::do_call()) + target = C->optimize_virtual_call(caller, jvms->bci(), klass, klass, + target, receiver_type, is_virtual, call_does_dispatch, vtable_index); // out-parameters // We lack profiling at this call but type speculation may // provide us with a type @@ -975,7 +978,7 @@ Node* m = kit.map()->in(i); Node* n = slow_map->in(i); if (m != n) { - const Type* t = gvn.type(m)->meet(gvn.type(n)); + const Type* t = gvn.type(m)->meet_speculative(gvn.type(n)); Node* phi = PhiNode::make(region, m, t); phi->set_req(2, n); kit.map()->set_req(i, gvn.transform(phi)); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/callnode.cpp --- a/src/share/vm/opto/callnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/callnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -111,7 +111,7 @@ #ifndef PRODUCT void ParmNode::dump_spec(outputStream *st) const { if( _con < TypeFunc::Parms ) { - st->print(names[_con]); + st->print("%s", names[_con]); } else { st->print("Parm%d: ",_con-TypeFunc::Parms); // Verbose and WizardMode dump bottom_type for all nodes @@ -342,24 +342,24 @@ 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: case Type::InstPtr: - st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,t->isa_oopptr()->const_oop()); + st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,p2i(t->isa_oopptr()->const_oop())); break; case Type::KlassPtr: - st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,t->make_ptr()->isa_klassptr()->klass()); + st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,p2i(t->make_ptr()->isa_klassptr()->klass())); break; case Type::MetadataPtr: - st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,t->make_ptr()->isa_metadataptr()->metadata()); + st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,p2i(t->make_ptr()->isa_metadataptr()->metadata())); break; case Type::NarrowOop: - st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,t->make_ptr()->isa_oopptr()->const_oop()); + st->print(" %s%d]=#Ptr" INTPTR_FORMAT,msg,i,p2i(t->make_ptr()->isa_oopptr()->const_oop())); break; case Type::RawPtr: - st->print(" %s%d]=#Raw" INTPTR_FORMAT,msg,i,t->is_rawptr()); + st->print(" %s%d]=#Raw" INTPTR_FORMAT,msg,i,p2i(t->is_rawptr())); break; case Type::DoubleCon: st->print(" %s%d]=#%fD",msg,i,t->is_double_constant()->_d); @@ -368,7 +368,7 @@ st->print(" %s%d]=#%fF",msg,i,t->is_float_constant()->_f); break; case Type::Long: - st->print(" %s%d]=#"INT64_FORMAT,msg,i,t->is_long()->get_con()); + st->print(" %s%d]=#"INT64_FORMAT,msg,i,(int64_t)(t->is_long()->get_con())); break; case Type::Half: case Type::Top: @@ -427,7 +427,7 @@ for (i = 0; i < (uint)scobjs.length(); i++) { // Scalar replaced objects. - st->print_cr(""); + st->cr(); st->print(" # ScObj" INT32_FORMAT " ", i); SafePointScalarObjectNode* spobj = scobjs.at(i); ciKlass* cik = spobj->bottom_type()->is_oopptr()->klass(); @@ -484,7 +484,7 @@ st->print(" }"); } } - st->print_cr(""); + st->cr(); if (caller() != NULL) caller()->format(regalloc, n, st); } @@ -595,6 +595,51 @@ } } +// Adapt offsets in in-array after adding or removing an edge. +// Prerequisite is that the JVMState is used by only one node. +void JVMState::adapt_position(int delta) { + for (JVMState* jvms = this; jvms != NULL; jvms = jvms->caller()) { + jvms->set_locoff(jvms->locoff() + delta); + jvms->set_stkoff(jvms->stkoff() + delta); + jvms->set_monoff(jvms->monoff() + delta); + jvms->set_scloff(jvms->scloff() + delta); + jvms->set_endoff(jvms->endoff() + delta); + } +} + +// Mirror the stack size calculation in the deopt code +// How much stack space would we need at this point in the program in +// case of deoptimization? +int JVMState::interpreter_frame_size() const { + const JVMState* jvms = this; + int size = 0; + int callee_parameters = 0; + int callee_locals = 0; + int extra_args = method()->max_stack() - stk_size(); + + while (jvms != NULL) { + int locks = jvms->nof_monitors(); + int temps = jvms->stk_size(); + bool is_top_frame = (jvms == this); + ciMethod* method = jvms->method(); + + int frame_size = BytesPerWord * Interpreter::size_activation(method->max_stack(), + temps + callee_parameters, + extra_args, + locks, + callee_parameters, + callee_locals, + is_top_frame); + size += frame_size; + + callee_parameters = method->size_of_parameters(); + callee_locals = method->max_locals(); + extra_args = 0; + jvms = jvms->caller(); + } + return size + Deoptimization::last_frame_adjust(0, callee_locals) * BytesPerWord; +} + //============================================================================= uint CallNode::cmp( const Node &n ) const { return _tf == ((CallNode&)n)._tf && _jvms == ((CallNode&)n)._jvms; } @@ -887,7 +932,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; } @@ -935,7 +980,7 @@ #ifndef PRODUCT void CallRuntimeNode::dump_spec(outputStream *st) const { st->print("# "); - st->print(_name); + st->print("%s", _name); CallNode::dump_spec(st); } #endif @@ -953,7 +998,7 @@ #ifndef PRODUCT void CallLeafNode::dump_spec(outputStream *st) const { st->print("# "); - st->print(_name); + st->print("%s", _name); CallNode::dump_spec(st); } #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/callnode.hpp --- a/src/share/vm/opto/callnode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/callnode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -299,6 +299,8 @@ JVMState* clone_deep(Compile* C) const; // recursively clones caller chain JVMState* clone_shallow(Compile* C) const; // retains uncloned caller void set_map_deep(SafePointNode *map);// reset map for all callers + void adapt_position(int delta); // Adapt offsets in in-array after adding an edge. + int interpreter_frame_size() const; #ifndef PRODUCT void format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) const; @@ -559,9 +561,15 @@ // Are we guaranteed that this node is a safepoint? Not true for leaf calls and // for some macro nodes whose expansion does not have a safepoint on the fast path. virtual bool guaranteed_safepoint() { return true; } - // For macro nodes, the JVMState gets modified during expansion, so when cloning - // the node the JVMState must be cloned. - virtual void clone_jvms(Compile* C) { } // default is not to clone + // For macro nodes, the JVMState gets modified during expansion. If calls + // use MachConstantBase, it gets modified during matching. So when cloning + // the node the JVMState must be cloned. Default is not to clone. + virtual void clone_jvms(Compile* C) { + if (C->needs_clone_jvms() && jvms() != NULL) { + set_jvms(jvms()->clone_deep(C)); + jvms()->set_map_deep(this); + } + } // Returns true if the call may modify n virtual bool may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/cfgnode.cpp --- a/src/share/vm/opto/cfgnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/cfgnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -951,7 +951,7 @@ if (is_intf != ti_is_intf) { t = _type; break; } } - t = t->meet(ti); + t = t->meet_speculative(ti); } } @@ -968,11 +968,11 @@ // // It is not possible to see Type::BOTTOM values as phi inputs, // because the ciTypeFlow pre-pass produces verifier-quality types. - const Type* ft = t->filter(_type); // Worst case type + const Type* ft = t->filter_speculative(_type); // Worst case type #ifdef ASSERT // The following logic has been moved into TypeOopPtr::filter. - const Type* jt = t->join(_type); + const Type* jt = t->join_speculative(_type); if( jt->empty() ) { // Emptied out??? // Check for evil case of 't' being a class and '_type' expecting an @@ -1018,7 +1018,7 @@ !jtkp->klass_is_exact() && // Keep exact interface klass (6894807) ttkp->is_loaded() && !ttkp->klass()->is_interface() ) { assert(ft == ttkp->cast_to_ptr_type(jtkp->ptr()) || - ft->isa_narrowoop() && ft->make_ptr() == ttkp->cast_to_ptr_type(jtkp->ptr()), ""); + ft->isa_narrowklass() && ft->make_ptr() == ttkp->cast_to_ptr_type(jtkp->ptr()), ""); jt = ft; } } @@ -1757,7 +1757,7 @@ break; } // Accumulate type for resulting Phi - type = type->meet(in(i)->in(AddPNode::Base)->bottom_type()); + type = type->meet_speculative(in(i)->in(AddPNode::Base)->bottom_type()); } Node* base = NULL; if (doit) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/chaitin.cpp --- a/src/share/vm/opto/chaitin.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/chaitin.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -761,7 +761,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 @@ -961,7 +961,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; @@ -2019,25 +2019,25 @@ tty->print_cr("new LRG"); } } - tty->print_cr(""); + tty->cr(); // Dump lo-degree list tty->print("Lo degree: "); for(uint i3 = _lo_degree; i3; i3 = lrgs(i3)._next ) tty->print("L%d ",i3); - tty->print_cr(""); + tty->cr(); // Dump lo-stk-degree list tty->print("Lo stk degree: "); for(uint i4 = _lo_stk_degree; i4; i4 = lrgs(i4)._next ) tty->print("L%d ",i4); - tty->print_cr(""); + tty->cr(); // Dump lo-degree list tty->print("Hi degree: "); for(uint i5 = _hi_degree; i5; i5 = lrgs(i5)._next ) tty->print("L%d ",i5); - tty->print_cr(""); + tty->cr(); } void PhaseChaitin::dump_degree_lists() const { @@ -2045,26 +2045,26 @@ tty->print("Lo degree: "); for( uint i = _lo_degree; i; i = lrgs(i)._next ) tty->print("L%d ",i); - tty->print_cr(""); + tty->cr(); // Dump lo-stk-degree list tty->print("Lo stk degree: "); for(uint i2 = _lo_stk_degree; i2; i2 = lrgs(i2)._next ) tty->print("L%d ",i2); - tty->print_cr(""); + tty->cr(); // Dump lo-degree list tty->print("Hi degree: "); for(uint i3 = _hi_degree; i3; i3 = lrgs(i3)._next ) tty->print("L%d ",i3); - tty->print_cr(""); + tty->cr(); } void PhaseChaitin::dump_simplified() const { tty->print("Simplified: "); for( uint i = _simplified; i; i = lrgs(i)._next ) tty->print("L%d ",i); - tty->print_cr(""); + tty->cr(); } static char *print_reg( OptoReg::Name reg, const PhaseChaitin *pc, char *buf ) { @@ -2143,7 +2143,7 @@ } tty->print(" : parm %d: ", k); domain->field_at(k + TypeFunc::Parms)->dump(); - tty->print_cr(""); + tty->cr(); } } @@ -2165,7 +2165,7 @@ _matcher._parm_regs[j].second() == reg ) { tty->print("parm %d: ",j); domain->field_at(j + TypeFunc::Parms)->dump(); - tty->print_cr(""); + tty->cr(); break; } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/classes.hpp --- a/src/share/vm/opto/classes.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/classes.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -29,8 +29,6 @@ macro(AbsF) macro(AbsI) macro(AddD) -macro(AddExactI) -macro(AddExactL) macro(AddF) macro(AddI) macro(AddL) @@ -135,7 +133,6 @@ macro(ExpD) macro(FastLock) macro(FastUnlock) -macro(FlagsProj) macro(Goto) macro(Halt) macro(If) @@ -170,14 +167,13 @@ macro(LoopLimit) macro(Mach) macro(MachProj) -macro(MathExact) -macro(MathExactI) -macro(MathExactL) macro(MaxI) macro(MemBarAcquire) +macro(LoadFence) macro(MemBarAcquireLock) macro(MemBarCPUOrder) macro(MemBarRelease) +macro(StoreFence) macro(MemBarReleaseLock) macro(MemBarVolatile) macro(MemBarStoreStore) @@ -192,22 +188,25 @@ macro(MoveL2D) macro(MoveD2L) macro(MulD) -macro(MulExactI) -macro(MulExactL) macro(MulF) macro(MulHiL) macro(MulI) macro(MulL) macro(Multi) macro(NegD) -macro(NegExactI) -macro(NegExactL) macro(NegF) macro(NeverBranch) macro(Opaque1) macro(Opaque2) +macro(Opaque3) macro(OrI) macro(OrL) +macro(OverflowAddI) +macro(OverflowSubI) +macro(OverflowMulI) +macro(OverflowAddL) +macro(OverflowSubL) +macro(OverflowMulL) macro(PCTable) macro(Parm) macro(PartialSubtypeCheck) @@ -251,8 +250,6 @@ macro(StrEquals) macro(StrIndexOf) macro(SubD) -macro(SubExactI) -macro(SubExactL) macro(SubF) macro(SubI) macro(SubL) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/compile.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" +#include "ci/ciReplay.hpp" #include "classfile/systemDictionary.hpp" #include "code/exceptionHandlerTable.hpp" #include "code/nmethod.hpp" @@ -81,8 +82,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 @@ -435,6 +439,14 @@ return words; } +// To bang the stack of this compiled method we use the stack size +// that the interpreter would need in case of a deoptimization. This +// removes the need to bang the stack in the deoptimization blob which +// in turn simplifies stack overflow handling. +int Compile::bang_size_in_bytes() const { + return MAX2(_interpreter_frame_size, frame_size_in_bytes()); +} + // ============================================================================ //------------------------------CompileWrapper--------------------------------- class CompileWrapper : public StackObj { @@ -644,9 +656,11 @@ _dead_node_count(0), #ifndef PRODUCT _trace_opto_output(TraceOptoOutput || method()->has_option("TraceOptoOutput")), + _in_dump_cnt(0), _printer(IdealGraphPrinter::printer()), #endif _congraph(NULL), + _replay_inline_data(NULL), _late_inlines(comp_arena(), 2, 0, NULL), _string_late_inlines(comp_arena(), 2, 0, NULL), _boxing_late_inlines(comp_arena(), 2, 0, NULL), @@ -656,7 +670,8 @@ _inlining_incrementally(false), _print_inlining_list(NULL), _print_inlining_idx(0), - _preserve_jvm_state(0) { + _preserve_jvm_state(0), + _interpreter_frame_size(0) { C = this; CompileWrapper cw(this); @@ -680,13 +695,19 @@ } set_print_assembly(print_opto_assembly); set_parsed_irreducible_loop(false); + + if (method()->has_option("ReplayInline")) { + _replay_inline_data = ciReplay::load_inline_data(method(), entry_bci(), ci_env->comp_level()); + } #endif set_print_inlining(PrintInlining || method()->has_option("PrintInlining") NOT_PRODUCT( || PrintOptoInlining)); set_print_intrinsics(PrintIntrinsics || method()->has_option("PrintIntrinsics")); - - if (ProfileTraps) { + set_has_irreducible_loop(true); // conservative until build_loop_tree() reset it + + if (ProfileTraps RTM_OPT_ONLY( || UseRTMLocking )) { // Make sure the method being compiled gets its own MDO, // so we can at least track the decompile_count(). + // Need MDO to record RTM code generation state. method()->ensure_method_data(); } @@ -695,10 +716,7 @@ print_compile_messages(); - if (UseOldInlining || PrintCompilation NOT_PRODUCT( || PrintOpto) ) - _ilt = InlineTree::build_inline_tree_root(); - else - _ilt = NULL; + _ilt = InlineTree::build_inline_tree_root(); // Even if NO memory addresses are used, MergeMem nodes must have at least 1 slice assert(num_alias_types() >= AliasIdxRaw, ""); @@ -849,6 +867,15 @@ #endif NOT_PRODUCT( verify_barriers(); ) + + // Dump compilation data to replay it. + if (method()->has_option("DumpReplay")) { + env()->dump_replay_data(_compile_id); + } + if (method()->has_option("DumpInline") && (ilt() != NULL)) { + env()->dump_inline_data(_compile_id); + } + // Now that we know the size of all the monitors we can add a fixed slot // for the original deopt pc. @@ -856,6 +883,10 @@ int next_slot = _orig_pc_slot + (sizeof(address) / VMRegImpl::stack_slot_size); set_fixed_slots(next_slot); + // Compute when to use implicit null checks. Used by matching trap based + // nodes and NullCheck optimization. + set_allowed_deopt_reasons(); + // Now generate code Code_Gen(); if (failing()) return; @@ -887,7 +918,8 @@ compiler, env()->comp_level(), has_unsafe_access(), - SharedRuntime::is_wide_vector(max_vector_size()) + SharedRuntime::is_wide_vector(max_vector_size()), + rtm_state() ); if (log() != NULL) // Print code cache state into compiler log @@ -933,17 +965,21 @@ _inner_loops(0), #ifndef PRODUCT _trace_opto_output(TraceOptoOutput), + _in_dump_cnt(0), _printer(NULL), #endif _dead_node_list(comp_arena()), _dead_node_count(0), _congraph(NULL), + _replay_inline_data(NULL), _number_of_mh_late_inlines(0), _inlining_progress(false), _inlining_incrementally(false), _print_inlining_list(NULL), _print_inlining_idx(0), - _preserve_jvm_state(0) { + _preserve_jvm_state(0), + _allowed_reasons(0), + _interpreter_frame_size(0) { C = this; #ifndef PRODUCT @@ -952,6 +988,8 @@ set_print_assembly(PrintFrameConverterAssembly); set_parsed_irreducible_loop(false); #endif + set_has_irreducible_loop(false); // no loops + CompileWrapper cw(this); Init(/*AliasLevel=*/ 0); init_tf((*generator)()); @@ -1050,7 +1088,23 @@ set_do_scheduling(OptoScheduling); set_do_count_invocations(false); set_do_method_data_update(false); - + set_rtm_state(NoRTM); // No RTM lock eliding by default +#if INCLUDE_RTM_OPT + if (UseRTMLocking && has_method() && (method()->method_data_or_null() != NULL)) { + int rtm_state = method()->method_data()->rtm_state(); + if (method_has_option("NoRTMLockEliding") || ((rtm_state & NoRTM) != 0)) { + // Don't generate RTM lock eliding code. + set_rtm_state(NoRTM); + } else if (method_has_option("UseRTMLockEliding") || ((rtm_state & UseRTM) != 0) || !UseRTMDeopt) { + // Generate RTM lock eliding code without abort ratio calculation code. + set_rtm_state(UseRTM); + } else if (UseRTMDeopt) { + // Generate RTM lock eliding code and include abort ratio calculation + // code if UseRTMDeopt is on. + set_rtm_state(ProfileRTM); + } + } +#endif if (debug_info()->recording_non_safepoints()) { set_node_note_array(new(comp_arena()) GrowableArray (comp_arena(), 8, 0, NULL)); @@ -1106,7 +1160,7 @@ if( start->is_Start() ) return start->as_Start(); } - ShouldNotReachHere(); + fatal("Did not find Start node!"); return NULL; } @@ -2248,6 +2302,12 @@ peep.do_transform(); } + // Do late expand if CPU requires this. + if (Matcher::require_postalloc_expand) { + NOT_PRODUCT(TracePhase t2c("postalloc_expand", &_t_postalloc_expand, true)); + cfg.postalloc_expand(_regalloc); + } + // Convert Nodes to instruction bits in a buffer { // %%%% workspace merge brought two timers together for one job @@ -2361,7 +2421,7 @@ starts_bundle = ' '; tty->print("\t"); delay->format(_regalloc, tty); - tty->print_cr(""); + tty->cr(); delay = NULL; } @@ -2375,12 +2435,12 @@ if (pcs && n->_idx < pc_limit) tty->print_cr("%3.3x", pcs[n->_idx]); else - tty->print_cr(""); + tty->cr(); assert(cut_short || delay == NULL, "no unconditional delay branch"); } // End of per-block dump - tty->print_cr(""); + tty->cr(); if (cut_short) tty->print_cr("*** disassembly is cut short ***"); } @@ -2552,6 +2612,7 @@ break; case Op_Opaque1: // Remove Opaque Nodes before matching case Op_Opaque2: // Remove Opaque Nodes before matching + case Op_Opaque3: n->subsume_by(n->in(1), this); break; case Op_CallStaticJava: @@ -2999,42 +3060,6 @@ n->set_req(MemBarNode::Precedent, top()); } break; - // Must set a control edge on all nodes that produce a FlagsProj - // so they can't escape the block that consumes the flags. - // Must also set the non throwing branch as the control - // for all nodes that depends on the result. Unless the node - // already have a control that isn't the control of the - // flag producer - case Op_FlagsProj: - { - MathExactNode* math = (MathExactNode*) n->in(0); - Node* ctrl = math->control_node(); - Node* non_throwing = math->non_throwing_branch(); - math->set_req(0, ctrl); - - Node* result = math->result_node(); - if (result != NULL) { - for (DUIterator_Fast jmax, j = result->fast_outs(jmax); j < jmax; j++) { - Node* out = result->fast_out(j); - // Phi nodes shouldn't be moved. They would only match below if they - // had the same control as the MathExactNode. The only time that - // would happen is if the Phi is also an input to the MathExact - // - // Cmp nodes shouldn't have control set at all. - if (out->is_Phi() || - out->is_Cmp()) { - continue; - } - - if (out->in(0) == NULL) { - out->set_req(0, non_throwing); - } else if (out->in(0) == ctrl) { - out->set_req(0, non_throwing); - } - } - } - } - break; default: assert( !n->is_Call(), "" ); assert( !n->is_Mem(), "" ); @@ -3063,8 +3088,12 @@ Node* m = n->in(i); ++i; if (m != NULL && !frc._visited.test_set(m->_idx)) { - if (m->is_SafePoint() && m->as_SafePoint()->jvms() != NULL) + if (m->is_SafePoint() && m->as_SafePoint()->jvms() != NULL) { + // compute worst case interpreter size in case of a deoptimization + update_interpreter_frame_size(m->as_SafePoint()->jvms()->interpreter_frame_size()); + sfpt.push(m); + } cnt = m->req(); nstack.push(n, i); // put on stack parent and next input's index n = m; @@ -3256,7 +3285,8 @@ // because of a transient condition during start-up in the interpreter. return false; } - if (md->has_trap_at(bci, reason) != 0) { + ciMethod* m = Deoptimization::reason_is_speculate(reason) ? this->method() : NULL; + if (md->has_trap_at(bci, m, reason) != 0) { // Assume PerBytecodeTrapLimit==0, for a more conservative heuristic. // Also, if there are multiple reasons, or if there is no per-BCI record, // assume the worst. @@ -3274,7 +3304,7 @@ // Less-accurate variant which does not require a method and bci. bool Compile::too_many_traps(Deoptimization::DeoptReason reason, ciMethodData* logmd) { - if (trap_count(reason) >= (uint)PerMethodTrapLimit) { + if (trap_count(reason) >= Deoptimization::per_method_trap_limit(reason)) { // Too many traps globally. // Note that we use cumulative trap_count, not just md->trap_count. if (log()) { @@ -3309,10 +3339,11 @@ uint m_cutoff = (uint) PerMethodRecompilationCutoff / 2 + 1; // not zero Deoptimization::DeoptReason per_bc_reason = Deoptimization::reason_recorded_per_bytecode_if_any(reason); + ciMethod* m = Deoptimization::reason_is_speculate(reason) ? this->method() : NULL; if ((per_bc_reason == Deoptimization::Reason_none - || md->has_trap_at(bci, reason) != 0) + || md->has_trap_at(bci, m, reason) != 0) // The trap frequency measure we care about is the recompile count: - && md->trap_recompiled_at(bci) + && md->trap_recompiled_at(bci, m) && md->overflow_recompile_count() >= bc_cutoff) { // Do not emit a trap here if it has already caused recompilations. // Also, if there are multiple reasons, or if there is no per-BCI record, @@ -3339,6 +3370,19 @@ } } +// Compute when not to trap. Used by matching trap based nodes and +// NullCheck optimization. +void Compile::set_allowed_deopt_reasons() { + _allowed_reasons = 0; + if (is_method_compilation()) { + for (int rs = (int)Deoptimization::Reason_none+1; rs < Compile::trapHistLength; rs++) { + assert(rs < BitsPerInt, "recode bit map"); + if (!too_many_traps((Deoptimization::DeoptReason) rs)) { + _allowed_reasons |= nth_bit(rs); + } + } + } +} #ifndef PRODUCT //------------------------------verify_graph_edges--------------------------- @@ -3641,7 +3685,8 @@ default: ShouldNotReachHere(); } assert(constant_addr, "consts section too small"); - assert((constant_addr - _masm.code()->consts()->start()) == con.offset(), err_msg_res("must be: %d == %d", constant_addr - _masm.code()->consts()->start(), con.offset())); + assert((constant_addr - _masm.code()->consts()->start()) == con.offset(), + err_msg_res("must be: %d == %d", (int) (constant_addr - _masm.code()->consts()->start()), (int)(con.offset()))); } } @@ -3721,7 +3766,7 @@ for (uint i = 0; i < n->outcnt(); i++) { address* constant_addr = &jump_table_base[i]; - assert(*constant_addr == (((address) n) + i), err_msg_res("all jump-table entries must contain adjusted node pointer: " INTPTR_FORMAT " == " INTPTR_FORMAT, *constant_addr, (((address) n) + i))); + assert(*constant_addr == (((address) n) + i), err_msg_res("all jump-table entries must contain adjusted node pointer: " INTPTR_FORMAT " == " INTPTR_FORMAT, p2i(*constant_addr), p2i(((address) n) + i))); *constant_addr = cb.consts()->target(*labels.at(i), (address) constant_addr); cb.consts()->relocate((address) constant_addr, relocInfo::internal_word_type); } @@ -3752,11 +3797,21 @@ } } for (int i = 0; i < _print_inlining_list->length(); i++) { - tty->print(_print_inlining_list->adr_at(i)->ss()->as_string()); + tty->print("%s", _print_inlining_list->adr_at(i)->ss()->as_string()); } } } +// Dump inlining replay data to the stream. +// Don't change thread state and acquire any locks. +void Compile::dump_inline_data(outputStream* out) { + InlineTree* inl_tree = ilt(); + if (inl_tree != NULL) { + out->print(" inline %d", inl_tree->count()); + inl_tree->dump_replay_data(out); + } +} + int Compile::cmp_expensive_nodes(Node* n1, Node* n2) { if (n1->Opcode() < n2->Opcode()) return -1; else if (n1->Opcode() > n2->Opcode()) return 1; @@ -3893,16 +3948,18 @@ // which may optimize it out. for (uint next = 0; next < worklist.size(); ++next) { Node *n = worklist.at(next); - if (n->is_Type() && n->as_Type()->type()->isa_oopptr() != NULL && - n->as_Type()->type()->is_oopptr()->speculative() != NULL) { + if (n->is_Type()) { TypeNode* tn = n->as_Type(); - const TypeOopPtr* t = tn->type()->is_oopptr(); - bool in_hash = igvn.hash_delete(n); - assert(in_hash, "node should be in igvn hash table"); - tn->set_type(t->remove_speculative()); - igvn.hash_insert(n); - igvn._worklist.push(n); // give it a chance to go away - modified++; + const Type* t = tn->type(); + const Type* t_no_spec = t->remove_speculative(); + if (t_no_spec != t) { + bool in_hash = igvn.hash_delete(n); + assert(in_hash, "node should be in igvn hash table"); + tn->set_type(t_no_spec); + igvn.hash_insert(n); + igvn._worklist.push(n); // give it a chance to go away + modified++; + } } uint max = n->len(); for( uint i = 0; i < max; ++i ) { @@ -3916,6 +3973,27 @@ if (modified > 0) { igvn.optimize(); } +#ifdef ASSERT + // Verify that after the IGVN is over no speculative type has resurfaced + worklist.clear(); + worklist.push(root()); + for (uint next = 0; next < worklist.size(); ++next) { + Node *n = worklist.at(next); + const Type* t = igvn.type_or_null(n); + assert((t == NULL) || (t == t->remove_speculative()), "no more speculative types"); + if (n->is_Type()) { + t = n->as_Type()->type(); + assert(t == t->remove_speculative(), "no more speculative types"); + } + uint max = n->len(); + for( uint i = 0; i < max; ++i ) { + Node *m = n->in(i); + if (not_a_node(m)) continue; + worklist.push(m); + } + } + igvn.check_no_speculative_types(); +#endif } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/compile.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -319,9 +319,10 @@ bool _trace_opto_output; bool _parsed_irreducible_loop; // True if ciTypeFlow detected irreducible loops during parsing #endif - + bool _has_irreducible_loop; // Found irreducible loops // JSR 292 bool _has_method_handle_invokes; // True if this method has MethodHandle invokes. + RTMState _rtm_state; // State of Restricted Transactional Memory usage // Compilation environment. Arena _comp_arena; // Arena with lifetime equivalent to Compile @@ -431,6 +432,8 @@ // Are we within a PreserveJVMState block? int _preserve_jvm_state; + void* _replay_inline_data; // Pointer to data loaded from file + public: outputStream* print_inlining_stream() const { @@ -462,9 +465,14 @@ void print_inlining(ciMethod* method, int inline_level, int bci, const char* msg = NULL) { stringStream ss; CompileTask::print_inlining(&ss, method, inline_level, bci, msg); - print_inlining_stream()->print(ss.as_string()); + print_inlining_stream()->print("%s", ss.as_string()); } + void* replay_inline_data() const { return _replay_inline_data; } + + // Dump inlining replay data to the stream. + void dump_inline_data(outputStream* out); + private: // Matching, CFG layout, allocation, code generation PhaseCFG* _cfg; // Results of CFG finding @@ -479,6 +487,7 @@ RegMask _FIRST_STACK_mask; // All stack slots usable for spills (depends on frame layout) Arena* _indexSet_arena; // control IndexSet allocation within PhaseChaitin void* _indexSet_free_block_list; // free list of IndexSet bit blocks + int _interpreter_frame_size; uint _node_bundling_limit; Bundle* _node_bundling_base; // Information for instruction bundling @@ -584,6 +593,10 @@ void set_print_inlining(bool z) { _print_inlining = z; } bool print_intrinsics() const { return _print_intrinsics; } void set_print_intrinsics(bool z) { _print_intrinsics = z; } + RTMState rtm_state() const { return _rtm_state; } + void set_rtm_state(RTMState s) { _rtm_state = s; } + bool use_rtm() const { return (_rtm_state & NoRTM) == 0; } + bool profile_rtm() const { return _rtm_state == ProfileRTM; } // check the CompilerOracle for special behaviours for this compile bool method_has_option(const char * option) { return method() != NULL && method()->has_option(option); @@ -592,7 +605,10 @@ 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; } // JSR 292 bool has_method_handle_invokes() const { return _has_method_handle_invokes; } @@ -757,6 +773,8 @@ MachConstantBaseNode* mach_constant_base_node(); bool has_mach_constant_base_node() const { return _mach_constant_base_node != NULL; } + // Generated by adlc, true if CallNode requires MachConstantBase. + bool needs_clone_jvms(); // Handy undefined Node Node* top() const { return _top; } @@ -836,8 +854,8 @@ // Helper functions to identify inlining potential at call-site ciMethod* optimize_virtual_call(ciMethod* caller, int bci, ciInstanceKlass* klass, - ciMethod* callee, const TypeOopPtr* receiver_type, - bool is_virtual, + ciKlass* holder, ciMethod* callee, + const TypeOopPtr* receiver_type, bool is_virtual, bool &call_does_dispatch, int &vtable_index); ciMethod* optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* klass, ciMethod* callee, const TypeOopPtr* receiver_type); @@ -853,6 +871,11 @@ 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; } + void set_allowed_deopt_reasons(); // Parsing, optimization PhaseGVN* initial_gvn() { return _initial_gvn; } @@ -924,6 +947,7 @@ PhaseRegAlloc* regalloc() { return _regalloc; } int frame_slots() const { return _frame_slots; } int frame_size_in_words() const; // frame_slots in units of the polymorphic 'words' + int frame_size_in_bytes() const { return _frame_slots << LogBytesPerInt; } RegMask& FIRST_STACK_mask() { return _FIRST_STACK_mask; } Arena* indexSet_arena() { return _indexSet_arena; } void* indexSet_free_block_list() { return _indexSet_free_block_list; } @@ -935,6 +959,13 @@ bool need_stack_bang(int frame_size_in_bytes) const; bool need_register_stack_bang() const; + void update_interpreter_frame_size(int size) { + if (_interpreter_frame_size < size) { + _interpreter_frame_size = size; + } + } + int bang_size_in_bytes() const; + void set_matcher(Matcher* m) { _matcher = m; } //void set_regalloc(PhaseRegAlloc* ra) { _regalloc = ra; } void set_indexSet_arena(Arena* a) { _indexSet_arena = a; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/connode.cpp --- a/src/share/vm/opto/connode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/connode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -188,7 +188,7 @@ const Type *CMoveNode::Value( PhaseTransform *phase ) const { if( phase->type(in(Condition)) == Type::TOP ) return Type::TOP; - return phase->type(in(IfFalse))->meet(phase->type(in(IfTrue))); + return phase->type(in(IfFalse))->meet_speculative(phase->type(in(IfTrue))); } //------------------------------make------------------------------------------- @@ -392,14 +392,14 @@ //============================================================================= // If input is already higher or equal to cast type, then this is an identity. Node *ConstraintCastNode::Identity( PhaseTransform *phase ) { - return phase->type(in(1))->higher_equal(_type) ? in(1) : this; + return phase->type(in(1))->higher_equal_speculative(_type) ? in(1) : this; } //------------------------------Value------------------------------------------ // Take 'join' of input and cast-up type const Type *ConstraintCastNode::Value( PhaseTransform *phase ) const { if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP; - const Type* ft = phase->type(in(1))->filter(_type); +const Type* ft = phase->type(in(1))->filter_speculative(_type); #ifdef ASSERT // Previous versions of this function had some special case logic, @@ -409,7 +409,7 @@ { const Type* t1 = phase->type(in(1)); if( t1 == Type::TOP ) assert(ft == Type::TOP, "special case #1"); - const Type* rt = t1->join(_type); + const Type* rt = t1->join_speculative(_type); if (rt->empty()) assert(ft == Type::TOP, "special case #2"); break; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/connode.hpp --- a/src/share/vm/opto/connode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/connode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -36,7 +36,7 @@ // Simple constants class ConNode : public TypeNode { public: - ConNode( const Type *t ) : TypeNode(t,1) { + ConNode( const Type *t ) : TypeNode(t->remove_speculative(),1) { init_req(0, (Node*)Compile::current()->root()); init_flags(Flag_is_Con); } @@ -642,6 +642,19 @@ virtual const Type *bottom_type() const { return TypeInt::INT; } }; +//------------------------------Opaque3Node------------------------------------ +// A node to prevent unwanted optimizations. Will be optimized only during +// macro nodes expansion. +class Opaque3Node : public Opaque2Node { + int _opt; // what optimization it was used for +public: + enum { RTM_OPT }; + Opaque3Node(Compile* C, Node *n, int opt) : Opaque2Node(C, n), _opt(opt) {} + virtual int Opcode() const; + bool rtm_opt() const { return (_opt == RTM_OPT); } +}; + + //----------------------PartialSubtypeCheckNode-------------------------------- // 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 diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/doCall.cpp --- a/src/share/vm/opto/doCall.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/doCall.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -56,7 +56,7 @@ out->print(" \\-> TypeProfile (%d/%d counts) = ", receiver_count, site_count); stringStream ss; prof_klass->name()->print_symbol_on(&ss); - out->print(ss.as_string()); + out->print("%s", ss.as_string()); out->cr(); } } @@ -161,19 +161,8 @@ // Try inlining a bytecoded method: if (!call_does_dispatch) { - InlineTree* ilt; - if (UseOldInlining) { - ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method()); - } else { - // Make a disembodied, stateless ILT. - // TO DO: When UseOldInlining is removed, copy the ILT code elsewhere. - float site_invoke_ratio = prof_factor; - // Note: ilt is for the root of this parse, not the present call site. - ilt = new InlineTree(this, jvms->method(), jvms->caller(), site_invoke_ratio, MaxInlineLevel); - } + InlineTree* ilt = InlineTree::find_subtree_from_root(this->ilt(), jvms->caller(), jvms->method()); WarmCallInfo scratch_ci; - if (!UseOldInlining) - scratch_ci.init(jvms, callee, profile, prof_factor); bool should_delay = false; WarmCallInfo* ci = ilt->ok_to_inline(callee, jvms, profile, &scratch_ci, should_delay); assert(ci != &scratch_ci, "do not let this pointer escape"); @@ -261,7 +250,7 @@ CallGenerator* miss_cg; Deoptimization::DeoptReason reason = morphism == 2 ? Deoptimization::Reason_bimorphic : - Deoptimization::Reason_class_check; + (speculative_receiver_type == NULL ? Deoptimization::Reason_class_check : Deoptimization::Reason_speculate_class_check); if ((morphism == 1 || (morphism == 2 && next_hit_cg != NULL)) && !too_many_traps(jvms->method(), jvms->bci(), reason) ) { @@ -369,7 +358,7 @@ bool Compile::should_delay_boxing_inlining(ciMethod* call_method, JVMState* jvms) { if (eliminate_boxing() && call_method->is_boxing_method()) { set_has_boxed_value(true); - return true; + return aggressive_unboxing(); } return false; } @@ -471,8 +460,14 @@ Node* receiver_node = stack(sp() - nargs); const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr(); // call_does_dispatch and vtable_index are out-parameters. They might be changed. - callee = C->optimize_virtual_call(method(), bci(), klass, orig_callee, receiver_type, - is_virtual, + // For arrays, klass below is Object. When vtable calls are used, + // resolving the call with Object would allow an illegal call to + // finalize() on an array. We use holder instead: illegal calls to + // finalize() won't be compiled as vtable calls (IC call + // resolution will catch the illegal call) and the few legal calls + // on array types won't be either. + callee = C->optimize_virtual_call(method(), bci(), klass, holder, orig_callee, + receiver_type, is_virtual, call_does_dispatch, vtable_index); // out-parameters speculative_receiver_type = receiver_type != NULL ? receiver_type->speculative_type() : NULL; } @@ -948,8 +943,8 @@ ciMethod* Compile::optimize_virtual_call(ciMethod* caller, int bci, ciInstanceKlass* klass, - ciMethod* callee, const TypeOopPtr* receiver_type, - bool is_virtual, + ciKlass* holder, ciMethod* callee, + const TypeOopPtr* receiver_type, bool is_virtual, bool& call_does_dispatch, int& vtable_index) { // Set default values for out-parameters. call_does_dispatch = true; @@ -964,7 +959,7 @@ call_does_dispatch = false; } else if (!UseInlineCaches && is_virtual && callee->is_loaded()) { // We can make a vtable call at this site - vtable_index = callee->resolve_vtable_index(caller->holder(), klass); + vtable_index = callee->resolve_vtable_index(caller->holder(), holder); } return callee; } @@ -987,8 +982,10 @@ ciInstanceKlass* actual_receiver = klass; if (receiver_type != NULL) { // Array methods are all inherited from Object, and are monomorphic. + // finalize() call on array is not allowed. if (receiver_type->isa_aryptr() && - callee->holder() == env()->Object_klass()) { + callee->holder() == env()->Object_klass() && + callee->name() != ciSymbol::finalize_method_name()) { return callee; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/escape.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -710,7 +710,7 @@ Node *val = n->in(MemNode::ValueIn); PointsToNode* ptn = ptnode_adr(val->_idx); assert(ptn != NULL, "node should be registered"); - ptn->set_escape_state(PointsToNode::GlobalEscape); + set_escape_state(ptn, PointsToNode::GlobalEscape); // Add edge to object for unsafe access with offset. PointsToNode* adr_ptn = ptnode_adr(adr->_idx); assert(adr_ptn != NULL, "node should be registered"); @@ -1579,9 +1579,20 @@ jobj->set_scalar_replaceable(false); return; } + // 2. An object is not scalar replaceable if the field into which it is + // stored has multiple bases one of which is null. + if (field->base_count() > 1) { + for (BaseIterator i(field); i.has_next(); i.next()) { + PointsToNode* base = i.get(); + if (base == null_obj) { + jobj->set_scalar_replaceable(false); + return; + } + } + } } assert(use->is_Field() || use->is_LocalVar(), "sanity"); - // 2. An object is not scalar replaceable if it is merged with other objects. + // 3. An object is not scalar replaceable if it is merged with other objects. for (EdgeIterator j(use); j.has_next(); j.next()) { PointsToNode* ptn = j.get(); if (ptn->is_JavaObject() && ptn != jobj) { @@ -1600,13 +1611,13 @@ FieldNode* field = j.get()->as_Field(); int offset = field->as_Field()->offset(); - // 3. An object is not scalar replaceable if it has a field with unknown + // 4. An object is not scalar replaceable if it has a field with unknown // offset (array's element is accessed in loop). if (offset == Type::OffsetBot) { jobj->set_scalar_replaceable(false); return; } - // 4. Currently an object is not scalar replaceable if a LoadStore node + // 5. Currently an object is not scalar replaceable if a LoadStore node // access its field since the field value is unknown after it. // Node* n = field->ideal_node(); @@ -1617,7 +1628,7 @@ } } - // 5. Or the address may point to more then one object. This may produce + // 6. Or the address may point to more then one object. This may produce // the false positive result (set not scalar replaceable) // since the flow-insensitive escape analysis can't separate // the case when stores overwrite the field's value from the case diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/gcm.cpp --- a/src/share/vm/opto/gcm.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/gcm.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -1326,15 +1330,6 @@ // with suitable memory ops nearby. Use the memory op to do the NULL check. // I can generate a memory op if there is not one nearby. if (C->is_method_compilation()) { - // Don't do it for natives, adapters, or runtime stubs - int allowed_reasons = 0; - // ...and don't do it when there have been too many traps, globally. - for (int reason = (int)Deoptimization::Reason_none+1; - reason < Compile::trapHistLength; reason++) { - assert(reason < BitsPerInt, "recode bit map"); - if (!C->too_many_traps((Deoptimization::DeoptReason) reason)) - allowed_reasons |= nth_bit(reason); - } // By reversing the loop direction we get a very minor gain on mpegaudio. // Feel free to revert to a forward loop for clarity. // for( int i=0; i < (int)matcher._null_check_tests.size(); i+=2 ) { @@ -1342,7 +1337,7 @@ Node* proj = _matcher._null_check_tests[i]; Node* val = _matcher._null_check_tests[i + 1]; Block* block = get_block_for_node(proj); - implicit_null_check(block, proj, val, allowed_reasons); + implicit_null_check(block, proj, val, C->allowed_deopt_reasons()); // The implicit_null_check will only perform the transformation // if the null branch is truly uncommon, *and* it leads to an // uncommon trap. Combined with the too_many_traps guards @@ -2019,7 +2014,7 @@ tty->print("%s: %d trip_count: %6.0f freq: %6.0f\n", _depth == 0 ? "Method" : "Loop", _id, trip_count(), _freq); for (int i = 0; i < _depth; i++) tty->print(" "); - tty->print(" members:", _id); + tty->print(" members:"); int k = 0; for (int i = 0; i < _members.length(); i++) { if (k++ >= 6) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/generateOptoStub.cpp --- a/src/share/vm/opto/generateOptoStub.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/generateOptoStub.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -104,13 +104,12 @@ // Node *adr_sp = basic_plus_adr(top(), thread, in_bytes(JavaThread::last_Java_sp_offset())); Node *last_sp = basic_plus_adr(top(), frameptr(), (intptr_t) STACK_BIAS); - store_to_memory(NULL, adr_sp, last_sp, T_ADDRESS, NoAlias); + store_to_memory(NULL, adr_sp, last_sp, T_ADDRESS, NoAlias, MemNode::unordered); // Set _thread_in_native // 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! @@ -118,8 +117,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 (CCallingConventionRequiresIntsAsLongs && 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 ) @@ -170,12 +177,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 (CCallingConventionRequiresIntsAsLongs && 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,16 +224,15 @@ //----------------------------- // Clear last_Java_sp - store_to_memory(NULL, adr_sp, null(), T_ADDRESS, NoAlias); + store_to_memory(NULL, adr_sp, null(), T_ADDRESS, NoAlias, MemNode::unordered); // Clear last_Java_pc and (optionally)_flags - store_to_memory(NULL, adr_last_Java_pc, null(), T_ADDRESS, NoAlias); + store_to_memory(NULL, adr_last_Java_pc, null(), T_ADDRESS, NoAlias, MemNode::unordered); #if defined(SPARC) - store_to_memory(NULL, adr_flags, intcon(0), T_INT, NoAlias); + store_to_memory(NULL, adr_flags, intcon(0), T_INT, NoAlias, MemNode::unordered); #endif /* defined(SPARC) */ -#ifdef IA64 +#if (defined(IA64) && !defined(AIX)) 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); + store_to_memory(NULL, adr_last_Java_fp, null(), T_ADDRESS, NoAlias, MemNode::unordered); #endif // For is-fancy-jump, the C-return value is also the branch target @@ -226,16 +240,16 @@ // 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); + Node* vm_result = make_load(NULL, adr, TypeOopPtr::BOTTOM, T_OBJECT, NoAlias, MemNode::unordered); map()->set_req(TypeFunc::Parms, vm_result); // vm_result passed as result // clear thread-local-storage(tls) - store_to_memory(NULL, adr, null(), T_ADDRESS, NoAlias); + store_to_memory(NULL, adr, null(), T_ADDRESS, NoAlias, MemNode::unordered); } //----------------------------- // check exception Node* adr = basic_plus_adr(top(), thread, in_bytes(Thread::pending_exception_offset())); - Node* pending = make_load(NULL, adr, TypeOopPtr::BOTTOM, T_OBJECT, NoAlias, false); + Node* pending = make_load(NULL, adr, TypeOopPtr::BOTTOM, T_OBJECT, NoAlias, MemNode::unordered); Node* exit_memory = reset_memory(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/graphKit.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -420,7 +420,7 @@ } const Type* srctype = _gvn.type(src); if (phi->type() != srctype) { - const Type* dsttype = phi->type()->meet(srctype); + const Type* dsttype = phi->type()->meet_speculative(srctype); if (phi->type() != dsttype) { phi->set_type(dsttype); _gvn.set_type(phi, dsttype); @@ -494,7 +494,7 @@ // first must access the should_post_on_exceptions_flag in this thread's JavaThread Node* jthread = _gvn.transform(new (C) ThreadLocalNode()); Node* adr = basic_plus_adr(top(), jthread, in_bytes(JavaThread::should_post_on_exceptions_flag_offset())); - Node* should_post_flag = make_load(control(), adr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, false); + Node* should_post_flag = make_load(control(), adr, TypeInt::INT, T_INT, Compile::AliasIdxRaw, MemNode::unordered); // Test the should_post_on_exceptions_flag vs. 0 Node* chk = _gvn.transform( new (C) CmpINode(should_post_flag, intcon(0)) ); @@ -596,7 +596,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, MemNode::release); add_exception_state(make_exception_state(ex_node)); return; @@ -611,9 +612,10 @@ // Usual case: Bail to interpreter. // Reserve the right to recompile if we haven't seen anything yet. + assert(!Deoptimization::reason_is_speculate(reason), "unsupported"); Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile; if (treat_throw_as_hot - && (method()->method_data()->trap_recompiled_at(bci()) + && (method()->method_data()->trap_recompiled_at(bci(), NULL) || C->too_many_traps(reason))) { // We cannot afford to take more traps here. Suffer in the interpreter. if (C->log() != NULL) @@ -1123,6 +1125,17 @@ } return _gvn.transform( new (C) ConvI2LNode(offset)); } + +Node* GraphKit::ConvI2UL(Node* offset) { + juint offset_con = (juint) find_int_con(offset, Type::OffsetBot); + if (offset_con != (juint) Type::OffsetBot) { + return longcon((julong) offset_con); + } + Node* conv = _gvn.transform( new (C) ConvI2LNode(offset)); + Node* mask = _gvn.transform( ConLNode::make(C, (julong) max_juint) ); + return _gvn.transform( new (C) AndLNode(conv, mask) ); +} + Node* GraphKit::ConvL2I(Node* offset) { // short-circuit a common case jlong offset_con = find_long_con(offset, (jlong)Type::OffsetBot); @@ -1223,7 +1236,7 @@ // See if mixing in the NULL pointer changes type. // If so, then the NULL pointer was not allowed in the original // type. In other words, "value" was not-null. - if (t->meet(TypePtr::NULL_PTR) != t) { + if (t->meet(TypePtr::NULL_PTR) != t->remove_speculative()) { // same as: if (!TypePtr::NULL_PTR->higher_equal(t)) ... explicit_null_checks_elided++; return value; // Elided null check quickly! @@ -1356,7 +1369,7 @@ // Cast obj to not-null on this path Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) { const Type *t = _gvn.type(obj); - const Type *t_not_null = t->join(TypePtr::NOTNULL); + const Type *t_not_null = t->join_speculative(TypePtr::NOTNULL); // Object is already not-null? if( t == t_not_null ) return obj; @@ -1483,16 +1496,16 @@ // 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) { + MemNode::MemOrd mo, bool require_atomic_access) { 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, mo); } else { - ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt); + ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo); } ld = _gvn.transform(ld); if ((bt == T_OBJECT) && C->do_escape_analysis() || C->eliminate_boxing()) { @@ -1504,6 +1517,7 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, int adr_idx, + MemNode::MemOrd mo, bool require_atomic_access) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); const TypePtr* adr_type = NULL; @@ -1511,9 +1525,9 @@ 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, mo); } else { - st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt); + st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo); } st = _gvn.transform(st); set_memory(st, adr_idx); @@ -1613,7 +1627,8 @@ Node* val, const TypeOopPtr* val_type, BasicType bt, - bool use_precise) { + bool use_precise, + MemNode::MemOrd mo) { // 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. @@ -1633,7 +1648,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, mo); post_barrier(control(), store, obj, adr, adr_idx, val, bt, use_precise); return store; } @@ -1644,7 +1659,8 @@ Node* adr, // actual adress to store val at const TypePtr* adr_type, Node* val, - BasicType bt) { + BasicType bt, + MemNode::MemOrd mo) { Compile::AliasType* at = C->alias_type(adr_type); const TypeOopPtr* val_type = NULL; if (adr_type->isa_instptr()) { @@ -1663,7 +1679,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, mo); } @@ -1707,7 +1723,7 @@ const Type* elemtype = arytype->elem(); BasicType elembt = elemtype->array_element_basic_type(); Node* adr = array_element_address(ary, idx, elembt, arytype->size()); - Node* ld = make_load(ctl, adr, elemtype, elembt, arytype); + Node* ld = make_load(ctl, adr, elemtype, elembt, arytype, MemNode::unordered); return ld; } @@ -1942,9 +1958,9 @@ void GraphKit::increment_counter(Node* counter_addr) { int adr_type = Compile::AliasIdxRaw; Node* ctrl = control(); - Node* cnt = make_load(ctrl, counter_addr, TypeInt::INT, T_INT, adr_type); + Node* cnt = make_load(ctrl, counter_addr, TypeInt::INT, T_INT, adr_type, MemNode::unordered); Node* incr = _gvn.transform(new (C) AddINode(cnt, _gvn.intcon(1))); - store_to_memory( ctrl, counter_addr, incr, T_INT, adr_type ); + store_to_memory(ctrl, counter_addr, incr, T_INT, adr_type, MemNode::unordered); } @@ -2108,30 +2124,33 @@ * @return node with improved type */ Node* GraphKit::record_profile_for_speculation(Node* n, ciKlass* exact_kls) { - const TypeOopPtr* current_type = _gvn.type(n)->isa_oopptr(); + const Type* current_type = _gvn.type(n); assert(UseTypeSpeculation, "type speculation must be on"); - if (exact_kls != NULL && - // nothing to improve if type is already exact - (current_type == NULL || - (!current_type->klass_is_exact() && - (current_type->speculative() == NULL || - !current_type->speculative()->klass_is_exact())))) { + + const TypeOopPtr* speculative = current_type->speculative(); + + if (current_type->would_improve_type(exact_kls, jvms()->depth())) { const TypeKlassPtr* tklass = TypeKlassPtr::make(exact_kls); const TypeOopPtr* xtype = tklass->as_instance_type(); assert(xtype->klass_is_exact(), "Should be exact"); - + // record the new speculative type's depth + speculative = xtype->with_inline_depth(jvms()->depth()); + } + + if (speculative != current_type->speculative()) { // Build a type with a speculative type (what we think we know // about the type but will need a guard when we use it) - const TypeOopPtr* spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::OffsetBot, TypeOopPtr::InstanceBot, xtype); - // We're changing the type, we need a new cast node to carry the - // new type. The new type depends on the control: what profiling - // tells us is only valid from here as far as we can tell. - Node* cast = new(C) CastPPNode(n, spec_type); - cast->init_req(0, control()); + const TypeOopPtr* spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::OffsetBot, TypeOopPtr::InstanceBot, speculative); + // We're changing the type, we need a new CheckCast node to carry + // the new type. The new type depends on the control: what + // profiling tells us is only valid from here as far as we can + // tell. + Node* cast = new(C) CheckCastPPNode(control(), n, current_type->remove_speculative()->join_speculative(spec_type)); cast = _gvn.transform(cast); replace_in_map(n, cast); n = cast; } + return n; } @@ -2141,7 +2160,7 @@ * * @param n receiver node * - * @return node with improved type + * @return node with improved type */ Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) { if (!UseTypeSpeculation) { @@ -2439,7 +2458,7 @@ //------------------------------make_slow_call_ex------------------------------ // Make the exception handler hookups for the slow call -void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj) { +void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj, bool deoptimize) { if (stopped()) return; // Make a catch node with just two handlers: fall-through and catch-all @@ -2453,11 +2472,17 @@ set_i_o(i_o); if (excp != top()) { - // Create an exception state also. - // Use an exact type if the caller has specified a specific exception. - const Type* ex_type = TypeOopPtr::make_from_klass_unique(ex_klass)->cast_to_ptr_type(TypePtr::NotNull); - Node* ex_oop = new (C) CreateExNode(ex_type, control(), i_o); - add_exception_state(make_exception_state(_gvn.transform(ex_oop))); + if (deoptimize) { + // Deoptimize if an exception is caught. Don't construct exception state in this case. + uncommon_trap(Deoptimization::Reason_unhandled, + Deoptimization::Action_none); + } else { + // Create an exception state also. + // Use an exact type if the caller has specified a specific exception. + const Type* ex_type = TypeOopPtr::make_from_klass_unique(ex_klass)->cast_to_ptr_type(TypePtr::NotNull); + Node* ex_oop = new (C) CreateExNode(ex_type, control(), i_o); + add_exception_state(make_exception_state(_gvn.transform(ex_oop))); + } } } @@ -2525,7 +2550,8 @@ // First load the super-klass's check-offset Node *p1 = basic_plus_adr( superklass, superklass, in_bytes(Klass::super_check_offset_offset()) ); - Node *chk_off = _gvn.transform( new (C) LoadINode( NULL, memory(p1), p1, _gvn.type(p1)->is_ptr() ) ); + Node *chk_off = _gvn.transform(new (C) LoadINode(NULL, memory(p1), p1, _gvn.type(p1)->is_ptr(), + TypeInt::INT, MemNode::unordered)); int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset()); bool might_be_cache = (find_int_con(chk_off, cacheoff_con) == cacheoff_con); @@ -2734,12 +2760,14 @@ // Subsequent type checks will always fold up. Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj, ciKlass* require_klass, - ciKlass* spec_klass, + ciKlass* spec_klass, bool safe_for_replace) { if (!UseTypeProfile || !TypeProfileCasts) return NULL; + Deoptimization::DeoptReason reason = spec_klass == NULL ? Deoptimization::Reason_class_check : Deoptimization::Reason_speculate_class_check; + // Make sure we haven't already deoptimized from this tactic. - if (too_many_traps(Deoptimization::Reason_class_check)) + if (too_many_traps(reason)) return NULL; // (No, this isn't a call, but it's enough like a virtual call @@ -2761,7 +2789,7 @@ &exact_obj); { PreserveJVMState pjvms(this); set_control(slow_ctl); - uncommon_trap(Deoptimization::Reason_class_check, + uncommon_trap(reason, Deoptimization::Action_maybe_recompile); } if (safe_for_replace) { @@ -2788,8 +2816,10 @@ bool not_null) { // type == NULL if profiling tells us this object is always null if (type != NULL) { - if (!too_many_traps(Deoptimization::Reason_null_check) && - !too_many_traps(Deoptimization::Reason_class_check)) { + Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check; + Deoptimization::DeoptReason null_reason = Deoptimization::Reason_null_check; + if (!too_many_traps(null_reason) && + !too_many_traps(class_reason)) { Node* not_null_obj = NULL; // not_null is true if we know the object is not null and // there's no need for a null check @@ -2808,7 +2838,7 @@ { PreserveJVMState pjvms(this); set_control(slow_ctl); - uncommon_trap(Deoptimization::Reason_class_check, + uncommon_trap(class_reason, Deoptimization::Action_maybe_recompile); } replace_in_map(not_null_obj, exact_obj); @@ -2877,7 +2907,7 @@ } if (known_statically && UseTypeSpeculation) { - // If we know the type check always succeed then we don't use the + // If we know the type check always succeeds then we don't use the // profiling data at this bytecode. Don't lose it, feed it to the // type system as a speculative type. not_null_obj = record_profiled_receiver_for_speculation(not_null_obj); @@ -2994,22 +3024,28 @@ } Node* cast_obj = NULL; - const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr(); - // We may not have profiling here or it may not help us. If we have - // a speculative type use it to perform an exact cast. - ciKlass* spec_obj_type = obj_type->speculative_type(); - if (spec_obj_type != NULL || - (data != NULL && - // Counter has never been decremented (due to cast failure). - // ...This is a reasonable thing to expect. It is true of - // all casts inserted by javac to implement generic types. - data->as_CounterData()->count() >= 0)) { - cast_obj = maybe_cast_profiled_receiver(not_null_obj, tk->klass(), spec_obj_type, safe_for_replace); - if (cast_obj != NULL) { - if (failure_control != NULL) // failure is now impossible - (*failure_control) = top(); - // adjust the type of the phi to the exact klass: - phi->raise_bottom_type(_gvn.type(cast_obj)->meet(TypePtr::NULL_PTR)); + if (tk->klass_is_exact()) { + // The following optimization tries to statically cast the speculative type of the object + // (for example obtained during profiling) to the type of the superklass and then do a + // dynamic check that the type of the object is what we expect. To work correctly + // for checkcast and aastore the type of superklass should be exact. + const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr(); + // We may not have profiling here or it may not help us. If we have + // a speculative type use it to perform an exact cast. + ciKlass* spec_obj_type = obj_type->speculative_type(); + if (spec_obj_type != NULL || + (data != NULL && + // Counter has never been decremented (due to cast failure). + // ...This is a reasonable thing to expect. It is true of + // all casts inserted by javac to implement generic types. + data->as_CounterData()->count() >= 0)) { + cast_obj = maybe_cast_profiled_receiver(not_null_obj, tk->klass(), spec_obj_type, safe_for_replace); + if (cast_obj != NULL) { + if (failure_control != NULL) // failure is now impossible + (*failure_control) = top(); + // adjust the type of the phi to the exact klass: + phi->raise_bottom_type(_gvn.type(cast_obj)->meet_speculative(TypePtr::NULL_PTR)); + } } } @@ -3132,10 +3168,14 @@ Node* mem = reset_memory(); FastLockNode * flock = _gvn.transform(new (C) FastLockNode(0, obj, box) )->as_FastLock(); - if (PrintPreciseBiasedLockingStatistics) { + if (UseBiasedLocking && PrintPreciseBiasedLockingStatistics) { // Create the counters for this fast lock. flock->create_lock_counter(sync_jvms()); // sync_jvms used to get current bci } + + // Create the rtm counters for this fast lock if needed. + flock->create_rtm_lock_counter(sync_jvms()); // sync_jvms used to get current bci + // Add monitor to debug info for the slow path. If we block inside the // slow path and de-opt, we need the monitor hanging around map()->push_monitor( flock ); @@ -3238,7 +3278,7 @@ } constant_value = Klass::_lh_neutral_value; // put in a known value Node* lhp = basic_plus_adr(klass_node, klass_node, in_bytes(Klass::layout_helper_offset())); - return make_load(NULL, lhp, TypeInt::INT, T_INT); + return make_load(NULL, lhp, TypeInt::INT, T_INT, MemNode::unordered); } // We just put in an allocate/initialize with a big raw-memory effect. @@ -3256,7 +3296,8 @@ //---------------------------set_output_for_allocation------------------------- Node* GraphKit::set_output_for_allocation(AllocateNode* alloc, - const TypeOopPtr* oop_type) { + const TypeOopPtr* oop_type, + bool deoptimize_on_exception) { int rawidx = Compile::AliasIdxRaw; alloc->set_req( TypeFunc::FramePtr, frameptr() ); add_safepoint_edges(alloc); @@ -3264,7 +3305,7 @@ set_control( _gvn.transform(new (C) ProjNode(allocx, TypeFunc::Control) ) ); // create memory projection for i_o set_memory ( _gvn.transform( new (C) ProjNode(allocx, TypeFunc::Memory, true) ), rawidx ); - make_slow_call_ex(allocx, env()->Throwable_klass(), true); + make_slow_call_ex(allocx, env()->Throwable_klass(), true, deoptimize_on_exception); // create a memory projection as for the normal control path Node* malloc = _gvn.transform(new (C) ProjNode(allocx, TypeFunc::Memory)); @@ -3342,9 +3383,11 @@ // The optional arguments are for specialized use by intrinsics: // - If 'extra_slow_test' if not null is an extra condition for the slow-path. // - If 'return_size_val', report the the total object size to the caller. +// - deoptimize_on_exception controls how Java exceptions are handled (rethrow vs deoptimize) Node* GraphKit::new_instance(Node* klass_node, Node* extra_slow_test, - Node* *return_size_val) { + Node* *return_size_val, + bool deoptimize_on_exception) { // Compute size in doublewords // The size is always an integral number of doublewords, represented // as a positive bytewise size stored in the klass's layout_helper. @@ -3413,7 +3456,7 @@ size, klass_node, initial_slow_test); - return set_output_for_allocation(alloc, oop_type); + return set_output_for_allocation(alloc, oop_type, deoptimize_on_exception); } //-------------------------------new_array------------------------------------- @@ -3423,7 +3466,8 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) Node* length, // number of array elements int nargs, // number of arguments to push back for uncommon trap - Node* *return_size_val) { + Node* *return_size_val, + bool deoptimize_on_exception) { jint layout_con = Klass::_lh_neutral_value; Node* layout_val = get_layout_helper(klass_node, layout_con); int layout_is_con = (layout_val == NULL); @@ -3566,7 +3610,7 @@ ary_type = ary_type->is_aryptr()->cast_to_size(length_type); } - Node* javaoop = set_output_for_allocation(alloc, ary_type); + Node* javaoop = set_output_for_allocation(alloc, ary_type, deoptimize_on_exception); // Cast length on remaining path to be as narrow as possible if (map()->find_edge(length) >= 0) { @@ -3773,7 +3817,7 @@ // Smash zero into card if( !UseConcMarkSweepGC ) { - __ store(__ ctrl(), card_adr, zero, bt, adr_type); + __ store(__ ctrl(), card_adr, zero, bt, adr_type, MemNode::release); } else { // Specialized path for CM store barrier __ storeCM(__ ctrl(), card_adr, zero, oop_store, adr_idx, bt, adr_type); @@ -3870,9 +3914,9 @@ // 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, MemNode::unordered); // update the index - __ store(__ ctrl(), index_adr, next_index, index_bt, Compile::AliasIdxRaw); + __ store(__ ctrl(), index_adr, next_index, index_bt, Compile::AliasIdxRaw, MemNode::unordered); } __ else_(); { @@ -3912,8 +3956,9 @@ 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); + // Order, see storeCM. + __ store(__ ctrl(), log_addr, card_adr, T_ADDRESS, Compile::AliasIdxRaw, MemNode::unordered); + __ store(__ ctrl(), index_adr, next_index, TypeX_X->basic_type(), Compile::AliasIdxRaw, MemNode::unordered); } __ else_(); { __ make_leaf_call(tf, CAST_FROM_FN_PTR(address, SharedRuntime::g1_wb_post), "g1_wb_post", card_adr, __ thread()); @@ -4043,7 +4088,7 @@ int offset_field_idx = C->get_alias_index(offset_field_type); return make_load(ctrl, basic_plus_adr(str, str, offset_offset), - TypeInt::INT, T_INT, offset_field_idx); + TypeInt::INT, T_INT, offset_field_idx, MemNode::unordered); } else { return intcon(0); } @@ -4058,7 +4103,7 @@ int count_field_idx = C->get_alias_index(count_field_type); return make_load(ctrl, basic_plus_adr(str, str, count_offset), - TypeInt::INT, T_INT, count_field_idx); + TypeInt::INT, T_INT, count_field_idx, MemNode::unordered); } else { return load_array_length(load_String_value(ctrl, str)); } @@ -4074,7 +4119,7 @@ ciTypeArrayKlass::make(T_CHAR), true, 0); int value_field_idx = C->get_alias_index(value_field_type); Node* load = make_load(ctrl, basic_plus_adr(str, str, value_offset), - value_type, T_OBJECT, value_field_idx); + value_type, T_OBJECT, value_field_idx, MemNode::unordered); // String.value field is known to be @Stable. if (UseImplicitStableValues) { load = cast_array_to_stable(load, value_type); @@ -4089,7 +4134,7 @@ const TypePtr* offset_field_type = string_type->add_offset(offset_offset); int offset_field_idx = C->get_alias_index(offset_field_type); store_to_memory(ctrl, basic_plus_adr(str, offset_offset), - value, T_INT, offset_field_idx); + value, T_INT, offset_field_idx, MemNode::unordered); } void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) { @@ -4099,7 +4144,7 @@ const TypePtr* value_field_type = string_type->add_offset(value_offset); store_oop_to_object(ctrl, str, basic_plus_adr(str, value_offset), value_field_type, - value, TypeAryPtr::CHARS, T_OBJECT); + value, TypeAryPtr::CHARS, T_OBJECT, MemNode::unordered); } void GraphKit::store_String_length(Node* ctrl, Node* str, Node* value) { @@ -4109,7 +4154,7 @@ const TypePtr* count_field_type = string_type->add_offset(count_offset); int count_field_idx = C->get_alias_index(count_field_type); store_to_memory(ctrl, basic_plus_adr(str, count_offset), - value, T_INT, count_field_idx); + value, T_INT, count_field_idx, MemNode::unordered); } Node* GraphKit::cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/graphKit.hpp --- a/src/share/vm/opto/graphKit.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/graphKit.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -338,6 +338,7 @@ // Convert between int and long, and size_t. // (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.) Node* ConvI2L(Node* offset); + Node* ConvI2UL(Node* offset); Node* ConvL2I(Node* offset); // Find out the klass of an object. Node* load_object_klass(Node* object); @@ -406,7 +407,7 @@ // Use the type profile to narrow an object type. Node* maybe_cast_profiled_receiver(Node* not_null_obj, ciKlass* require_klass, - ciKlass* spec, + ciKlass* spec, bool safe_for_replace); // Cast obj to type and emit guard unless we had too many traps here already @@ -510,36 +511,50 @@ // 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) { + MemNode::MemOrd mo, bool require_atomic_access = false) { // This version computes alias_index from bottom_type return make_load(ctl, adr, t, bt, adr->bottom_type()->is_ptr(), - require_atomic_access); + mo, require_atomic_access); } - 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, + MemNode::MemOrd mo, bool require_atomic_access = false) { // 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); + mo, require_atomic_access); } // 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, + MemNode::MemOrd mo, bool require_atomic_access = false); // 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, + MemNode::MemOrd mo, bool require_atomic_access = false) { // 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); + mo, require_atomic_access); } // 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, + MemNode::MemOrd, bool require_atomic_access = false); @@ -557,40 +572,44 @@ Node* store_oop(Node* ctl, Node* obj, // containing obj - Node* adr, // actual adress to store val at + Node* adr, // actual adress to store val at const TypePtr* adr_type, Node* val, const TypeOopPtr* val_type, BasicType bt, - bool use_precise); + bool use_precise, + MemNode::MemOrd mo); Node* store_oop_to_object(Node* ctl, Node* obj, // containing obj - Node* adr, // actual adress to store val at + Node* adr, // actual adress to store val at 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, + MemNode::MemOrd mo) { + return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, false, mo); } Node* store_oop_to_array(Node* ctl, Node* obj, // containing obj - Node* adr, // actual adress to store val at + Node* adr, // actual adress to store val at 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, + MemNode::MemOrd mo) { + return store_oop(ctl, obj, adr, adr_type, val, val_type, bt, true, mo); } // Could be an array or object we don't know at compile time (unsafe ref.) Node* store_oop_to_unknown(Node* ctl, Node* obj, // containing obj - Node* adr, // actual adress to store val at + Node* adr, // actual adress to store val at const TypePtr* adr_type, Node* val, - BasicType bt); + BasicType bt, + MemNode::MemOrd mo); // For the few case where the barriers need special help void pre_barrier(bool do_load, Node* ctl, @@ -783,7 +802,7 @@ // merge in all memory slices from new_mem, along the given path void merge_memory(Node* new_mem, Node* region, int new_path); - void make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj); + void make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj, bool deoptimize = false); // Helper functions to build synchronizations int next_monitor(); @@ -825,13 +844,16 @@ // implementation of object creation Node* set_output_for_allocation(AllocateNode* alloc, - const TypeOopPtr* oop_type); + const TypeOopPtr* oop_type, + bool deoptimize_on_exception=false); Node* get_layout_helper(Node* klass_node, jint& constant_value); Node* new_instance(Node* klass_node, Node* slow_test = NULL, - Node* *return_size_val = NULL); + Node* *return_size_val = NULL, + bool deoptimize_on_exception = false); Node* new_array(Node* klass_node, Node* count_val, int nargs, - Node* *return_size_val = NULL); + Node* *return_size_val = NULL, + bool deoptimize_on_exception = false); // java.lang.String helpers Node* load_String_offset(Node* ctrl, Node* str); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/idealGraphPrinter.cpp --- a/src/share/vm/opto/idealGraphPrinter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/idealGraphPrinter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -155,7 +155,7 @@ } else { // It would be nice if we could shut down cleanly but it should // be an error if we can't connect to the visualizer. - fatal(err_msg_res("Couldn't connect to visualizer at %s:%d", + fatal(err_msg_res("Couldn't connect to visualizer at %s:" INTX_FORMAT, PrintIdealGraphAddress, PrintIdealGraphPort)); } } @@ -195,7 +195,7 @@ void IdealGraphPrinter::begin_elem(const char *s) { - _xml->begin_elem(s); + _xml->begin_elem("%s", s); } void IdealGraphPrinter::end_elem() { @@ -203,7 +203,7 @@ } void IdealGraphPrinter::begin_head(const char *s) { - _xml->begin_head(s); + _xml->begin_head("%s", s); } void IdealGraphPrinter::end_head() { @@ -223,7 +223,7 @@ } void IdealGraphPrinter::head(const char *name) { - _xml->head(name); + _xml->head("%s", name); } void IdealGraphPrinter::tail(const char *name) { @@ -231,7 +231,7 @@ } void IdealGraphPrinter::text(const char *s) { - _xml->text(s); + _xml->text("%s", s); } void IdealGraphPrinter::print_prop(const char *name, int val) { @@ -359,7 +359,7 @@ void IdealGraphPrinter::print_indent() { tty->print_cr("printing ident %d", _depth); for (int i = 0; i < _depth; i++) { - _xml->print(INDENT); + _xml->print("%s", INDENT); } } @@ -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", t->msg()); @@ -623,7 +623,7 @@ print_prop("lrg", lrg_id); } - node->_in_dump_cnt--; + Compile::current()->_in_dump_cnt--; #endif tail(PROPERTIES_ELEMENT); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/idealKit.cpp --- a/src/share/vm/opto/idealKit.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/idealKit.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -359,25 +359,25 @@ 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, MemNode::unordered); } else { - ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt); + ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, MemNode::unordered); } return transform(ld); } 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" ); + int adr_idx, + MemNode::MemOrd mo, bool require_atomic_access) { + 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, mo); } else { - st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt); + st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo); } st = transform(st); set_memory(st, adr_idx); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/idealKit.hpp --- a/src/share/vm/opto/idealKit.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/idealKit.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -226,6 +226,7 @@ Node* val, BasicType bt, int adr_idx, + MemNode::MemOrd mo, bool require_atomic_access = false); // Store a card mark ordered after store_oop diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/ifg.cpp --- a/src/share/vm/opto/ifg.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/ifg.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -257,7 +257,7 @@ for( i = 0; i < _maxlrg*2; i++ ) if( h_cnt[i] ) tty->print("%d/%d ",i,h_cnt[i]); - tty->print_cr(""); + tty->cr(); } void PhaseIFG::verify( const PhaseChaitin *pc ) const { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/ifnode.cpp --- a/src/share/vm/opto/ifnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/ifnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -76,7 +76,6 @@ if( !i1->is_Bool() ) return NULL; BoolNode *b = i1->as_Bool(); Node *cmp = b->in(1); - if( cmp->is_FlagsProj() ) return NULL; if( !cmp->is_Cmp() ) return NULL; i1 = cmp->in(1); if( i1 == NULL || !i1->is_Phi() ) return NULL; @@ -674,7 +673,7 @@ // / Region // Node* IfNode::fold_compares(PhaseGVN* phase) { - if (!phase->C->eliminate_boxing() || Opcode() != Op_If) return NULL; + if (Opcode() != Op_If) return NULL; Node* this_cmp = in(1)->in(1); if (this_cmp != NULL && this_cmp->Opcode() == Op_CmpI && diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/lcm.cpp --- a/src/share/vm/opto/lcm.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/lcm.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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. @@ -206,6 +246,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; @@ -472,13 +520,6 @@ break; } - // For nodes that produce a FlagsProj, make the node adjacent to the - // use of the FlagsProj - if (use->is_FlagsProj() && get_block_for_node(use) == block) { - found_machif = true; - break; - } - // More than this instruction pending for successor to be ready, // don't choose this if other opportunities are ready if (ready_cnt.at(use->_idx) > 1) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/library_call.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -203,7 +203,9 @@ bool inline_math_native(vmIntrinsics::ID id); bool inline_trig(vmIntrinsics::ID id); bool inline_math(vmIntrinsics::ID id); - void inline_math_mathExact(Node* math); + template + bool inline_math_overflow(Node* arg1, Node* arg2); + void inline_math_mathExact(Node* math, Node* test); bool inline_math_addExactI(bool is_increment); bool inline_math_addExactL(bool is_increment); bool inline_math_multiplyExactI(); @@ -214,7 +216,7 @@ bool inline_math_subtractExactL(bool is_decrement); bool inline_exp(); bool inline_pow(); - void finish_pow_exp(Node* result, Node* x, Node* y, const TypeFunc* call_type, address funcAddr, const char* funcName); + Node* finish_pow_exp(Node* result, Node* x, Node* y, const TypeFunc* call_type, address funcAddr, const char* funcName); bool inline_min_max(vmIntrinsics::ID id); Node* generate_min_max(vmIntrinsics::ID id, Node* x, Node* y); // This returns Type::AnyPtr, RawPtr, or OopPtr. @@ -304,6 +306,7 @@ bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); + Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); bool inline_encodeISOArray(); bool inline_updateCRC32(); bool inline_updateBytesCRC32(); @@ -516,31 +519,31 @@ case vmIntrinsics::_incrementExactI: case vmIntrinsics::_addExactI: - if (!Matcher::match_rule_supported(Op_AddExactI) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowAddI) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_incrementExactL: case vmIntrinsics::_addExactL: - if (!Matcher::match_rule_supported(Op_AddExactL) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowAddL) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_decrementExactI: case vmIntrinsics::_subtractExactI: - if (!Matcher::match_rule_supported(Op_SubExactI) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowSubI) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_decrementExactL: case vmIntrinsics::_subtractExactL: - if (!Matcher::match_rule_supported(Op_SubExactL) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowSubL) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_negateExactI: - if (!Matcher::match_rule_supported(Op_NegExactI) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowSubI) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_negateExactL: - if (!Matcher::match_rule_supported(Op_NegExactL) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowSubL) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_multiplyExactI: - if (!Matcher::match_rule_supported(Op_MulExactI) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowMulI) || !UseMathExactIntrinsics) return NULL; break; case vmIntrinsics::_multiplyExactL: - if (!Matcher::match_rule_supported(Op_MulExactL) || !UseMathExactIntrinsics) return NULL; + if (!Matcher::match_rule_supported(Op_OverflowMulL) || !UseMathExactIntrinsics) return NULL; break; default: @@ -1057,7 +1060,7 @@ const Type* thread_type = TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull); Node* thread = _gvn.transform(new (C) ThreadLocalNode()); Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(JavaThread::threadObj_offset())); - Node* threadObj = make_load(NULL, p, thread_type, T_OBJECT); + Node* threadObj = make_load(NULL, p, thread_type, T_OBJECT, MemNode::unordered); tls_output = thread; return threadObj; } @@ -1675,7 +1678,7 @@ return true; } -void LibraryCallKit::finish_pow_exp(Node* result, Node* x, Node* y, const TypeFunc* call_type, address funcAddr, const char* funcName) { +Node* LibraryCallKit::finish_pow_exp(Node* result, Node* x, Node* y, const TypeFunc* call_type, address funcAddr, const char* funcName) { //------------------- //result=(result.isNaN())? funcAddr():result; // Check: If isNaN() by checking result!=result? then either trap @@ -1691,7 +1694,7 @@ uncommon_trap(Deoptimization::Reason_intrinsic, Deoptimization::Action_make_not_entrant); } - set_result(result); + return result; } else { // If this inlining ever returned NaN in the past, we compile a call // to the runtime to properly handle corner cases @@ -1721,9 +1724,10 @@ result_region->init_req(2, control()); result_val->init_req(2, value); - set_result(result_region, result_val); + set_control(_gvn.transform(result_region)); + return _gvn.transform(result_val); } else { - set_result(result); + return result; } } } @@ -1735,7 +1739,8 @@ Node* arg = round_double_node(argument(0)); Node* n = _gvn.transform(new (C) ExpDNode(C, control(), arg)); - finish_pow_exp(n, arg, NULL, OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dexp), "EXP"); + n = finish_pow_exp(n, arg, NULL, OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dexp), "EXP"); + set_result(n); C->set_has_split_ifs(true); // Has chance for split-if optimization return true; @@ -1745,27 +1750,48 @@ // Inline power instructions, if possible. bool LibraryCallKit::inline_pow() { // Pseudocode for pow - // if (x <= 0.0) { - // long longy = (long)y; - // if ((double)longy == y) { // if y is long - // if (y + 1 == y) longy = 0; // huge number: even - // result = ((1&longy) == 0)?-DPow(abs(x), y):DPow(abs(x), y); + // if (y == 2) { + // return x * x; + // } else { + // if (x <= 0.0) { + // long longy = (long)y; + // if ((double)longy == y) { // if y is long + // if (y + 1 == y) longy = 0; // huge number: even + // result = ((1&longy) == 0)?-DPow(abs(x), y):DPow(abs(x), y); + // } else { + // result = NaN; + // } // } else { - // result = NaN; + // result = DPow(x,y); // } - // } else { - // result = DPow(x,y); + // if (result != result)? { + // result = uncommon_trap() or runtime_call(); + // } + // return result; // } - // if (result != result)? { - // result = uncommon_trap() or runtime_call(); - // } - // return result; Node* x = round_double_node(argument(0)); Node* y = round_double_node(argument(2)); Node* result = NULL; + Node* const_two_node = makecon(TypeD::make(2.0)); + Node* cmp_node = _gvn.transform(new (C) CmpDNode(y, const_two_node)); + Node* bool_node = _gvn.transform(new (C) BoolNode(cmp_node, BoolTest::eq)); + IfNode* if_node = create_and_xform_if(control(), bool_node, PROB_STATIC_INFREQUENT, COUNT_UNKNOWN); + Node* if_true = _gvn.transform(new (C) IfTrueNode(if_node)); + Node* if_false = _gvn.transform(new (C) IfFalseNode(if_node)); + + RegionNode* region_node = new (C) RegionNode(3); + region_node->init_req(1, if_true); + + Node* phi_node = new (C) PhiNode(region_node, Type::DOUBLE); + // special case for x^y where y == 2, we can convert it to x * x + phi_node->init_req(1, _gvn.transform(new (C) MulDNode(x, x))); + + // set control to if_false since we will now process the false branch + set_control(if_false); + if (!too_many_traps(Deoptimization::Reason_intrinsic)) { // Short form: skip the fancy tests and just check for NaN result. result = _gvn.transform(new (C) PowDNode(C, control(), x, y)); @@ -1889,7 +1915,15 @@ result = _gvn.transform(phi); } - finish_pow_exp(result, x, y, OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "POW"); + result = finish_pow_exp(result, x, y, OptoRuntime::Math_DD_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dpow), "POW"); + + // control from finish_pow_exp is now input to the region node + region_node->set_req(2, control()); + // the result from finish_pow_exp is now input to the phi node + phi_node->init_req(2, result); + set_control(_gvn.transform(region_node)); + record_for_igvn(region_node); + set_result(_gvn.transform(phi_node)); C->set_has_split_ifs(true); // Has chance for split-if optimization return true; @@ -1936,7 +1970,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() : @@ -1969,18 +2003,8 @@ return true; } -void LibraryCallKit::inline_math_mathExact(Node* math) { - // If we didn't get the expected opcode it means we have optimized - // the node to something else and don't need the exception edge. - if (!math->is_MathExact()) { - set_result(math); - return; - } - - Node* result = _gvn.transform( new(C) ProjNode(math, MathExactNode::result_proj_node)); - Node* flags = _gvn.transform( new(C) FlagsProjNode(math, MathExactNode::flags_proj_node)); - - Node* bol = _gvn.transform( new (C) BoolNode(flags, BoolTest::overflow) ); +void LibraryCallKit::inline_math_mathExact(Node* math, Node *test) { + Node* bol = _gvn.transform( new (C) BoolNode(test, BoolTest::overflow) ); IfNode* check = create_and_map_if(control(), bol, PROB_UNLIKELY_MAG(3), COUNT_UNKNOWN); Node* fast_path = _gvn.transform( new (C) IfFalseNode(check)); Node* slow_path = _gvn.transform( new (C) IfTrueNode(check) ); @@ -1998,108 +2022,50 @@ } set_control(fast_path); - set_result(result); + set_result(math); +} + +template +bool LibraryCallKit::inline_math_overflow(Node* arg1, Node* arg2) { + typedef typename OverflowOp::MathOp MathOp; + + MathOp* mathOp = new(C) MathOp(arg1, arg2); + Node* operation = _gvn.transform( mathOp ); + Node* ofcheck = _gvn.transform( new(C) OverflowOp(arg1, arg2) ); + inline_math_mathExact(operation, ofcheck); + return true; } bool LibraryCallKit::inline_math_addExactI(bool is_increment) { - Node* arg1 = argument(0); - Node* arg2 = NULL; - - if (is_increment) { - arg2 = intcon(1); - } else { - arg2 = argument(1); - } - - Node* add = _gvn.transform( new(C) AddExactINode(NULL, arg1, arg2) ); - inline_math_mathExact(add); - return true; + return inline_math_overflow(argument(0), is_increment ? intcon(1) : argument(1)); } bool LibraryCallKit::inline_math_addExactL(bool is_increment) { - Node* arg1 = argument(0); // type long - // argument(1) == TOP - Node* arg2 = NULL; - - if (is_increment) { - arg2 = longcon(1); - } else { - arg2 = argument(2); // type long - // argument(3) == TOP - } - - Node* add = _gvn.transform(new(C) AddExactLNode(NULL, arg1, arg2)); - inline_math_mathExact(add); - return true; + return inline_math_overflow(argument(0), is_increment ? longcon(1) : argument(2)); } bool LibraryCallKit::inline_math_subtractExactI(bool is_decrement) { - Node* arg1 = argument(0); - Node* arg2 = NULL; - - if (is_decrement) { - arg2 = intcon(1); - } else { - arg2 = argument(1); - } - - Node* sub = _gvn.transform(new(C) SubExactINode(NULL, arg1, arg2)); - inline_math_mathExact(sub); - return true; + return inline_math_overflow(argument(0), is_decrement ? intcon(1) : argument(1)); } bool LibraryCallKit::inline_math_subtractExactL(bool is_decrement) { - Node* arg1 = argument(0); // type long - // argument(1) == TOP - Node* arg2 = NULL; - - if (is_decrement) { - arg2 = longcon(1); - } else { - arg2 = argument(2); // type long - // argument(3) == TOP - } - - Node* sub = _gvn.transform(new(C) SubExactLNode(NULL, arg1, arg2)); - inline_math_mathExact(sub); - return true; + return inline_math_overflow(argument(0), is_decrement ? longcon(1) : argument(2)); } bool LibraryCallKit::inline_math_negateExactI() { - Node* arg1 = argument(0); - - Node* neg = _gvn.transform(new(C) NegExactINode(NULL, arg1)); - inline_math_mathExact(neg); - return true; + return inline_math_overflow(intcon(0), argument(0)); } bool LibraryCallKit::inline_math_negateExactL() { - Node* arg1 = argument(0); - // argument(1) == TOP - - Node* neg = _gvn.transform(new(C) NegExactLNode(NULL, arg1)); - inline_math_mathExact(neg); - return true; + return inline_math_overflow(longcon(0), argument(0)); } bool LibraryCallKit::inline_math_multiplyExactI() { - Node* arg1 = argument(0); - Node* arg2 = argument(1); - - Node* mul = _gvn.transform(new(C) MulExactINode(NULL, arg1, arg2)); - inline_math_mathExact(mul); - return true; + return inline_math_overflow(argument(0), argument(1)); } bool LibraryCallKit::inline_math_multiplyExactL() { - Node* arg1 = argument(0); - // argument(1) == TOP - Node* arg2 = argument(2); - // argument(3) == TOP - - Node* mul = _gvn.transform(new(C) MulExactLNode(NULL, arg1, arg2)); - inline_math_mathExact(mul); - return true; + return inline_math_overflow(argument(0), argument(2)); } Node* @@ -2627,8 +2593,13 @@ // rough approximation of type. need_mem_bar = true; // For Stores, place a memory ordering barrier now. - if (is_store) + if (is_store) { insert_mem_bar(Op_MemBarRelease); + } else { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + insert_mem_bar(Op_MemBarVolatile); + } + } } // Memory barrier to prevent normal and 'unsafe' accesses from @@ -2640,7 +2611,7 @@ if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); if (!is_store) { - Node* p = make_load(control(), adr, value_type, type, adr_type, is_volatile); + Node* p = make_load(control(), adr, value_type, type, adr_type, MemNode::unordered, is_volatile); // load value switch (type) { case T_BOOLEAN: @@ -2660,7 +2631,7 @@ case T_ADDRESS: // Cast to an int type. p = _gvn.transform(new (C) CastP2XNode(NULL, p)); - p = ConvX2L(p); + p = ConvX2UL(p); break; default: fatal(err_msg_res("unexpected type %d: %s", type, type2name(type))); @@ -2684,13 +2655,14 @@ break; } + MemNode::MemOrd mo = is_volatile ? MemNode::release : MemNode::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, mo, is_volatile); } 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, mo); } 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 @@ -2702,11 +2674,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, mo); // 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(), mo, is_volatile); } __ end_if(); // Final sync IdealKit and GraphKit. final_sync(ideal); @@ -2716,10 +2688,13 @@ } if (is_volatile) { - if (!is_store) + if (!is_store) { insert_mem_bar(Op_MemBarAcquire); - else - insert_mem_bar(Op_MemBarVolatile); + } else { + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + insert_mem_bar(Op_MemBarVolatile); + } + } } if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); @@ -2979,12 +2954,12 @@ Node *newval_enc = _gvn.transform(new (C) EncodePNode(newval, newval->bottom_type()->make_narrowoop())); if (kind == LS_xchg) { load_store = _gvn.transform(new (C) GetAndSetNNode(control(), mem, adr, - newval_enc, adr_type, value_type->make_narrowoop())); + newval_enc, adr_type, value_type->make_narrowoop())); } else { assert(kind == LS_cmpxchg, "wrong LoadStore operation"); Node *oldval_enc = _gvn.transform(new (C) EncodePNode(oldval, oldval->bottom_type()->make_narrowoop())); load_store = _gvn.transform(new (C) CompareAndSwapNNode(control(), mem, adr, - newval_enc, oldval_enc)); + newval_enc, oldval_enc)); } } else #endif @@ -3090,9 +3065,9 @@ const bool require_atomic_access = true; Node* store; if (type == T_OBJECT) // reference stores need a store barrier. - store = store_oop_to_unknown(control(), base, adr, adr_type, val, type); + store = store_oop_to_unknown(control(), base, adr, adr_type, val, type, MemNode::release); else { - store = store_to_memory(control(), adr, val, type, adr_type, require_atomic_access); + store = store_to_memory(control(), adr, val, type, adr_type, MemNode::release, require_atomic_access); } insert_mem_bar(Op_MemBarCPUOrder); return true; @@ -3104,10 +3079,10 @@ insert_mem_bar(Op_MemBarCPUOrder); switch(id) { case vmIntrinsics::_loadFence: - insert_mem_bar(Op_MemBarAcquire); + insert_mem_bar(Op_LoadFence); return true; case vmIntrinsics::_storeFence: - insert_mem_bar(Op_MemBarRelease); + insert_mem_bar(Op_StoreFence); return true; case vmIntrinsics::_fullFence: insert_mem_bar(Op_MemBarVolatile); @@ -3152,7 +3127,7 @@ Node* insp = basic_plus_adr(kls, in_bytes(InstanceKlass::init_state_offset())); // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler // can generate code to load it as unsigned byte. - Node* inst = make_load(NULL, insp, TypeInt::UBYTE, T_BOOLEAN); + Node* inst = make_load(NULL, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); Node* bits = intcon(InstanceKlass::fully_initialized); test = _gvn.transform(new (C) SubINode(inst, bits)); // The 'test' is non-zero if we need to take a slow path. @@ -3176,14 +3151,14 @@ kls = null_check(kls, T_OBJECT); ByteSize offset = TRACE_ID_OFFSET; Node* insp = basic_plus_adr(kls, in_bytes(offset)); - Node* tvalue = make_load(NULL, insp, TypeLong::LONG, T_LONG); + Node* tvalue = make_load(NULL, insp, TypeLong::LONG, T_LONG, MemNode::unordered); Node* bits = longcon(~0x03l); // ignore bit 0 & 1 Node* andl = _gvn.transform(new (C) AndLNode(tvalue, bits)); Node* clsused = longcon(0x01l); // set the class bit Node* orl = _gvn.transform(new (C) OrLNode(tvalue, clsused)); const TypePtr *adr_type = _gvn.type(insp)->isa_ptr(); - store_to_memory(control(), insp, orl, T_LONG, adr_type); + store_to_memory(control(), insp, orl, T_LONG, adr_type, MemNode::unordered); set_result(andl); return true; } @@ -3192,15 +3167,15 @@ Node* tls_ptr = NULL; Node* cur_thr = generate_current_thread(tls_ptr); Node* p = basic_plus_adr(top()/*!oop*/, tls_ptr, in_bytes(JavaThread::osthread_offset())); - Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS); + Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered); p = basic_plus_adr(top()/*!oop*/, osthread, in_bytes(OSThread::thread_id_offset())); Node* threadid = NULL; size_t thread_id_size = OSThread::thread_id_size(); if (thread_id_size == (size_t) BytesPerLong) { - threadid = ConvL2I(make_load(control(), p, TypeLong::LONG, T_LONG)); + threadid = ConvL2I(make_load(control(), p, TypeLong::LONG, T_LONG, MemNode::unordered)); } else if (thread_id_size == (size_t) BytesPerInt) { - threadid = make_load(control(), p, TypeInt::INT, T_INT); + threadid = make_load(control(), p, TypeInt::INT, T_INT, MemNode::unordered); } else { ShouldNotReachHere(); } @@ -3236,7 +3211,8 @@ // private native boolean java.lang.Thread.isInterrupted(boolean ClearInterrupted); bool LibraryCallKit::inline_native_isInterrupted() { // Add a fast path to t.isInterrupted(clear_int): - // (t == Thread.current() && (!TLS._osthread._interrupted || !clear_int)) + // (t == Thread.current() && + // (!TLS._osthread._interrupted || WINDOWS_ONLY(false) NOT_WINDOWS(!clear_int))) // ? TLS._osthread._interrupted : /*slow path:*/ t.isInterrupted(clear_int) // So, in the common case that the interrupt bit is false, // we avoid making a call into the VM. Even if the interrupt bit @@ -3275,11 +3251,11 @@ // (b) Interrupt bit on TLS must be false. Node* p = basic_plus_adr(top()/*!oop*/, tls_ptr, in_bytes(JavaThread::osthread_offset())); - Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS); + Node* osthread = make_load(NULL, p, TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered); p = basic_plus_adr(top()/*!oop*/, osthread, in_bytes(OSThread::interrupted_offset())); // Set the control input on the field _interrupted read to prevent it floating up. - Node* int_bit = make_load(control(), p, TypeInt::BOOL, T_INT); + Node* int_bit = make_load(control(), p, TypeInt::BOOL, T_INT, MemNode::unordered); Node* cmp_bit = _gvn.transform(new (C) CmpINode(int_bit, intcon(0))); Node* bol_bit = _gvn.transform(new (C) BoolNode(cmp_bit, BoolTest::ne)); @@ -3293,6 +3269,7 @@ // drop through to next case set_control( _gvn.transform(new (C) IfTrueNode(iff_bit))); +#ifndef TARGET_OS_FAMILY_windows // (c) Or, if interrupt bit is set and clear_int is false, use 2nd fast path. Node* clr_arg = argument(1); Node* cmp_arg = _gvn.transform(new (C) CmpINode(clr_arg, intcon(0))); @@ -3306,6 +3283,10 @@ // drop through to next case set_control( _gvn.transform(new (C) IfTrueNode(iff_arg))); +#else + // To return true on Windows you must read the _interrupted field + // and check the the event state i.e. take the slow path. +#endif // TARGET_OS_FAMILY_windows // (d) Otherwise, go to the slow path. slow_region->add_req(control()); @@ -3347,7 +3328,7 @@ // Given a klass oop, load its java mirror (a java.lang.Class oop). Node* LibraryCallKit::load_mirror_from_klass(Node* klass) { Node* p = basic_plus_adr(klass, in_bytes(Klass::java_mirror_offset())); - return make_load(NULL, p, TypeInstPtr::MIRROR, T_OBJECT); + return make_load(NULL, p, TypeInstPtr::MIRROR, T_OBJECT, MemNode::unordered); } //-----------------------load_klass_from_mirror_common------------------------- @@ -3384,7 +3365,7 @@ // Branch around if the given klass has the given modifier bit set. // Like generate_guard, adds a new path onto the region. Node* modp = basic_plus_adr(kls, in_bytes(Klass::access_flags_offset())); - Node* mods = make_load(NULL, modp, TypeInt::INT, T_INT); + Node* mods = make_load(NULL, modp, TypeInt::INT, T_INT, MemNode::unordered); Node* mask = intcon(modifier_mask); Node* bits = intcon(modifier_bits); Node* mbit = _gvn.transform(new (C) AndINode(mods, mask)); @@ -3501,7 +3482,7 @@ case vmIntrinsics::_getModifiers: p = basic_plus_adr(kls, in_bytes(Klass::modifier_flags_offset())); - query_value = make_load(NULL, p, TypeInt::INT, T_INT); + query_value = make_load(NULL, p, TypeInt::INT, T_INT, MemNode::unordered); break; case vmIntrinsics::_isInterface: @@ -3559,7 +3540,7 @@ // Be sure to pin the oop load to the guard edge just created: Node* is_array_ctrl = region->in(region->req()-1); Node* cma = basic_plus_adr(kls, in_bytes(ArrayKlass::component_mirror_offset())); - Node* cmo = make_load(is_array_ctrl, cma, TypeInstPtr::MIRROR, T_OBJECT); + Node* cmo = make_load(is_array_ctrl, cma, TypeInstPtr::MIRROR, T_OBJECT, MemNode::unordered); phi->add_req(cmo); } query_value = null(); // non-array case is null @@ -3567,7 +3548,7 @@ case vmIntrinsics::_getClassAccessFlags: p = basic_plus_adr(kls, in_bytes(Klass::access_flags_offset())); - query_value = make_load(NULL, p, TypeInt::INT, T_INT); + query_value = make_load(NULL, p, TypeInt::INT, T_INT, MemNode::unordered); break; default: @@ -3933,7 +3914,7 @@ vtable_index*vtableEntry::size()) * wordSize + vtableEntry::method_offset_in_bytes(); Node* entry_addr = basic_plus_adr(obj_klass, entry_offset); - Node* target_call = make_load(NULL, entry_addr, TypePtr::NOTNULL, T_ADDRESS); + Node* target_call = make_load(NULL, entry_addr, TypePtr::NOTNULL, T_ADDRESS, MemNode::unordered); // Compare the target method with the expected method (e.g., Object.hashCode). const TypePtr* native_call_addr = TypeMetadataPtr::make(method); @@ -3997,8 +3978,11 @@ } -//------------------------------inline_native_hashcode-------------------- -// Build special case code for calls to hashCode on an object. +/** + * Build special case code for calls to hashCode on an object. This call may + * be virtual (invokevirtual) or bound (invokespecial). For each case we generate + * slightly different code. + */ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) { assert(is_static == callee()->is_static(), "correct intrinsic selection"); assert(!(is_virtual && is_static), "either virtual, special, or static"); @@ -4006,11 +3990,9 @@ enum { _slow_path = 1, _fast_path, _null_path, PATH_LIMIT }; RegionNode* result_reg = new(C) RegionNode(PATH_LIMIT); - PhiNode* result_val = new(C) PhiNode(result_reg, - TypeInt::INT); + PhiNode* result_val = new(C) PhiNode(result_reg, TypeInt::INT); PhiNode* result_io = new(C) PhiNode(result_reg, Type::ABIO); - PhiNode* result_mem = new(C) PhiNode(result_reg, Type::MEMORY, - TypePtr::BOTTOM); + PhiNode* result_mem = new(C) PhiNode(result_reg, Type::MEMORY, TypePtr::BOTTOM); Node* obj = NULL; if (!is_static) { // Check for hashing null object @@ -4036,12 +4018,6 @@ return true; } - // After null check, get the object's klass. - Node* obj_klass = load_object_klass(obj); - - // This call may be virtual (invokevirtual) or bound (invokespecial). - // For each case we generate slightly different code. - // We only go to the fast case code if we pass a number of guards. The // paths which do not pass are accumulated in the slow_region. RegionNode* slow_region = new (C) RegionNode(1); @@ -4054,19 +4030,24 @@ // guard for non-virtual calls -- the caller is known to be the native // Object hashCode(). if (is_virtual) { + // After null check, get the object's klass. + Node* obj_klass = load_object_klass(obj); generate_virtual_guard(obj_klass, slow_region); } // Get the header out of the object, use LoadMarkNode when available Node* header_addr = basic_plus_adr(obj, oopDesc::mark_offset_in_bytes()); - Node* header = make_load(control(), header_addr, TypeX_X, TypeX_X->basic_type()); + // The control of the load must be NULL. Otherwise, the load can move before + // the null check after castPP removal. + Node* no_ctrl = NULL; + Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered); // Test the header to see if it is unlocked. - Node *lock_mask = _gvn.MakeConX(markOopDesc::biased_lock_mask_in_place); - Node *lmasked_header = _gvn.transform(new (C) AndXNode(header, lock_mask)); - Node *unlocked_val = _gvn.MakeConX(markOopDesc::unlocked_value); - Node *chk_unlocked = _gvn.transform(new (C) CmpXNode( lmasked_header, unlocked_val)); - Node *test_unlocked = _gvn.transform(new (C) BoolNode( chk_unlocked, BoolTest::ne)); + Node* lock_mask = _gvn.MakeConX(markOopDesc::biased_lock_mask_in_place); + Node* lmasked_header = _gvn.transform(new (C) AndXNode(header, lock_mask)); + Node* unlocked_val = _gvn.MakeConX(markOopDesc::unlocked_value); + Node* chk_unlocked = _gvn.transform(new (C) CmpXNode( lmasked_header, unlocked_val)); + Node* test_unlocked = _gvn.transform(new (C) BoolNode( chk_unlocked, BoolTest::ne)); generate_slow_guard(test_unlocked, slow_region); @@ -4074,19 +4055,19 @@ // We depend on hash_mask being at most 32 bits and avoid the use of // hash_mask_in_place because it could be larger than 32 bits in a 64-bit // vm: see markOop.hpp. - Node *hash_mask = _gvn.intcon(markOopDesc::hash_mask); - Node *hash_shift = _gvn.intcon(markOopDesc::hash_shift); - Node *hshifted_header= _gvn.transform(new (C) URShiftXNode(header, hash_shift)); + Node* hash_mask = _gvn.intcon(markOopDesc::hash_mask); + Node* hash_shift = _gvn.intcon(markOopDesc::hash_shift); + Node* hshifted_header= _gvn.transform(new (C) URShiftXNode(header, hash_shift)); // This hack lets the hash bits live anywhere in the mark object now, as long // as the shift drops the relevant bits into the low 32 bits. Note that // Java spec says that HashCode is an int so there's no point in capturing // an 'X'-sized hashcode (32 in 32-bit build or 64 in 64-bit build). hshifted_header = ConvX2I(hshifted_header); - Node *hash_val = _gvn.transform(new (C) AndINode(hshifted_header, hash_mask)); - - Node *no_hash_val = _gvn.intcon(markOopDesc::no_hash); - Node *chk_assigned = _gvn.transform(new (C) CmpINode( hash_val, no_hash_val)); - Node *test_assigned = _gvn.transform(new (C) BoolNode( chk_assigned, BoolTest::eq)); + Node* hash_val = _gvn.transform(new (C) AndINode(hshifted_header, hash_mask)); + + Node* no_hash_val = _gvn.intcon(markOopDesc::no_hash); + Node* chk_assigned = _gvn.transform(new (C) CmpINode( hash_val, no_hash_val)); + Node* test_assigned = _gvn.transform(new (C) BoolNode( chk_assigned, BoolTest::eq)); generate_slow_guard(test_assigned, slow_region); @@ -4595,7 +4576,10 @@ // It's an instance, and it passed the slow-path tests. PreserveJVMState pjvms(this); Node* obj_size = NULL; - Node* alloc_obj = new_instance(obj_klass, NULL, &obj_size); + // Need to deoptimize on exception from allocation since Object.clone intrinsic + // is reexecuted if deoptimization occurs and there could be problems when merging + // exception state between multiple Object.clone versions (reexecute=true vs reexecute=false). + Node* alloc_obj = new_instance(obj_klass, NULL, &obj_size, /*deoptimize_on_exception=*/true); copy_to_clone(obj, alloc_obj, obj_size, false, !use_ReduceInitialCardMarks()); @@ -5480,7 +5464,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, MemNode::unordered); mem = _gvn.transform(mem); } } @@ -5530,8 +5514,8 @@ ((src_off ^ dest_off) & (BytesPerLong-1)) == 0) { Node* sptr = basic_plus_adr(src, src_off); Node* dptr = basic_plus_adr(dest, dest_off); - Node* sval = make_load(control(), sptr, TypeInt::INT, T_INT, adr_type); - store_to_memory(control(), dptr, sval, T_INT, adr_type); + Node* sval = make_load(control(), sptr, TypeInt::INT, T_INT, adr_type, MemNode::unordered); + store_to_memory(control(), dptr, sval, T_INT, adr_type, MemNode::unordered); src_off += BytesPerInt; dest_off += BytesPerInt; } else { @@ -5596,7 +5580,7 @@ // super_check_offset, for the desired klass. int sco_offset = in_bytes(Klass::super_check_offset_offset()); Node* p3 = basic_plus_adr(dest_elem_klass, sco_offset); - Node* n3 = new(C) LoadINode(NULL, memory(p3), p3, _gvn.type(p3)->is_ptr()); + Node* n3 = new(C) LoadINode(NULL, memory(p3), p3, _gvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered); Node* check_offset = ConvI2X(_gvn.transform(n3)); Node* check_value = dest_elem_klass; @@ -5737,7 +5721,7 @@ Node* base = makecon(TypeRawPtr::make(StubRoutines::crc_table_addr())); Node* offset = _gvn.transform(new (C) LShiftINode(result, intcon(0x2))); Node* adr = basic_plus_adr(top(), base, ConvI2X(offset)); - result = make_load(control(), adr, TypeInt::INT, T_INT); + result = make_load(control(), adr, TypeInt::INT, T_INT, MemNode::unordered); crc = _gvn.transform(new (C) URShiftINode(crc, intcon(8))); result = _gvn.transform(new (C) XorINode(crc, result)); @@ -5838,7 +5822,7 @@ const TypeOopPtr* object_type = TypeOopPtr::make_from_klass(klass); Node* no_ctrl = NULL; - Node* result = make_load(no_ctrl, adr, object_type, T_OBJECT); + Node* result = make_load(no_ctrl, adr, object_type, T_OBJECT, MemNode::unordered); // Use the pre-barrier to record the value in the referent field pre_barrier(false /* do_load */, @@ -5885,7 +5869,7 @@ const Type *type = TypeOopPtr::make_from_klass(field_klass->as_klass()); // Build the load. - Node* loadedField = make_load(NULL, adr, type, bt, adr_type, is_vol); + Node* loadedField = make_load(NULL, adr, type, bt, adr_type, MemNode::unordered, is_vol); return loadedField; } @@ -5936,10 +5920,22 @@ Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); if (k_start == NULL) return false; - // Call the stub. - make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start); + if (Matcher::pass_original_key_for_aes()) { + // on SPARC we need to pass the original key since key expansion needs to happen in intrinsics due to + // compatibility issues between Java key expansion and SPARC crypto instructions + Node* original_k_start = get_original_key_start_from_aescrypt_object(aescrypt_object); + if (original_k_start == NULL) return false; + + // Call the stub. + make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, original_k_start); + } else { + // Call the stub. + make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start); + } return true; } @@ -6017,14 +6013,29 @@ if (objRvec == NULL) return false; Node* r_start = array_element_address(objRvec, intcon(0), T_BYTE); - // Call the stub, passing src_start, dest_start, k_start, r_start and src_len - make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::cipherBlockChaining_aescrypt_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start, r_start, len); - - // return is void so no result needs to be pushed - + Node* cbcCrypt; + if (Matcher::pass_original_key_for_aes()) { + // on SPARC we need to pass the original key since key expansion needs to happen in intrinsics due to + // compatibility issues between Java key expansion and SPARC crypto instructions + Node* original_k_start = get_original_key_start_from_aescrypt_object(aescrypt_object); + if (original_k_start == NULL) return false; + + // Call the stub, passing src_start, dest_start, k_start, r_start, src_len and original_k_start + cbcCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::cipherBlockChaining_aescrypt_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, r_start, len, original_k_start); + } else { + // Call the stub, passing src_start, dest_start, k_start, r_start and src_len + cbcCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::cipherBlockChaining_aescrypt_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, r_start, len); + } + + // return cipher length (int) + Node* retvalue = _gvn.transform(new (C) ProjNode(cbcCrypt, TypeFunc::Parms)); + set_result(retvalue); return true; } @@ -6039,6 +6050,17 @@ return k_start; } +//------------------------------get_original_key_start_from_aescrypt_object----------------------- +Node * LibraryCallKit::get_original_key_start_from_aescrypt_object(Node *aescrypt_object) { + Node* objAESCryptKey = load_field_from_object(aescrypt_object, "lastKey", "[B", /*is_exact*/ false); + assert (objAESCryptKey != NULL, "wrong version of com.sun.crypto.provider.AESCrypt"); + if (objAESCryptKey == NULL) return (Node *) NULL; + + // now have the array, need to get the start address of the lastKey array + Node* original_k_start = array_element_address(objAESCryptKey, intcon(0), T_BYTE); + return original_k_start; +} + //----------------------------inline_cipherBlockChaining_AESCrypt_predicate---------------------------- // Return node representing slow path of predicate check. // the pseudo code we want to emulate with this predicate is: diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/locknode.cpp --- a/src/share/vm/opto/locknode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/locknode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -136,6 +136,8 @@ //-----------------------------hash-------------------------------------------- uint FastLockNode::hash() const { return NO_HASH; } +uint FastLockNode::size_of() const { return sizeof(*this); } + //------------------------------cmp-------------------------------------------- uint FastLockNode::cmp( const Node &n ) const { return (&n == this); // Always fail except on self @@ -159,6 +161,22 @@ _counters = blnc->counters(); } +void FastLockNode::create_rtm_lock_counter(JVMState* state) { +#if INCLUDE_RTM_OPT + Compile* C = Compile::current(); + if (C->profile_rtm() || (PrintPreciseRTMLockingStatistics && C->use_rtm())) { + RTMLockingNamedCounter* rlnc = (RTMLockingNamedCounter*) + OptoRuntime::new_named_counter(state, NamedCounter::RTMLockingCounter); + _rtm_counters = rlnc->counters(); + if (UseRTMForStackLocks) { + rlnc = (RTMLockingNamedCounter*) + OptoRuntime::new_named_counter(state, NamedCounter::RTMLockingCounter); + _stack_rtm_counters = rlnc->counters(); + } + } +#endif +} + //============================================================================= //------------------------------do_monitor_enter------------------------------- void Parse::do_monitor_enter() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/locknode.hpp --- a/src/share/vm/opto/locknode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/locknode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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------------------------------------ @@ -89,13 +92,17 @@ //------------------------------FastLockNode----------------------------------- class FastLockNode: public CmpNode { private: - BiasedLockingCounters* _counters; + BiasedLockingCounters* _counters; + RTMLockingCounters* _rtm_counters; // RTM lock counters for inflated locks + RTMLockingCounters* _stack_rtm_counters; // RTM lock counters for stack locks public: FastLockNode(Node *ctrl, Node *oop, Node *box) : CmpNode(oop,box) { init_req(0,ctrl); init_class_id(Class_FastLock); _counters = NULL; + _rtm_counters = NULL; + _stack_rtm_counters = NULL; } Node* obj_node() const { return in(1); } Node* box_node() const { return in(2); } @@ -104,13 +111,17 @@ // FastLock and FastUnlockNode do not hash, we need one for each correspoding // LockNode/UnLockNode to avoid creating Phi's. virtual uint hash() const ; // { return NO_HASH; } + virtual uint size_of() const; virtual uint cmp( const Node &n ) const ; // Always fail, except on self virtual int Opcode() const; virtual const Type *Value( PhaseTransform *phase ) const { return TypeInt::CC; } const Type *sub(const Type *t1, const Type *t2) const { return TypeInt::CC;} void create_lock_counter(JVMState* s); - BiasedLockingCounters* counters() const { return _counters; } + void create_rtm_lock_counter(JVMState* state); + BiasedLockingCounters* counters() const { return _counters; } + RTMLockingCounters* rtm_counters() const { return _rtm_counters; } + RTMLockingCounters* stack_rtm_counters() const { return _stack_rtm_counters; } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/loopPredicate.cpp --- a/src/share/vm/opto/loopPredicate.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/loopPredicate.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -637,7 +637,7 @@ if (TraceLoopPredicate) { predString->print_cr("print(predString->as_string()); + tty->print("%s", predString->as_string()); } return bol; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/loopTransform.cpp --- a/src/share/vm/opto/loopTransform.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/loopTransform.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -617,6 +617,15 @@ case Op_AryEq: { return false; } +#if INCLUDE_RTM_OPT + case Op_FastLock: + case Op_FastUnlock: { + // Don't unroll RTM locking code because it is large. + if (UseRTMLocking) { + return false; + } + } +#endif } // switch } @@ -713,10 +722,6 @@ case Op_ModL: body_size += 30; break; case Op_DivL: body_size += 30; break; case Op_MulL: body_size += 10; break; - case Op_FlagsProj: - // Can't handle unrolling of loops containing - // nodes that generate a FlagsProj at the moment - return false; case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: @@ -726,6 +731,15 @@ // String intrinsics are large and have loops. return false; } +#if INCLUDE_RTM_OPT + case Op_FastLock: + case Op_FastUnlock: { + // Don't unroll RTM locking code because it is large. + if (UseRTMLocking) { + return false; + } + } +#endif } // switch } @@ -780,10 +794,6 @@ continue; // not RC Node *cmp = bol->in(1); - if (cmp->is_FlagsProj()) { - continue; - } - Node *rc_exp = cmp->in(1); Node *limit = cmp->in(2); @@ -1137,6 +1147,7 @@ // Now force out all loop-invariant dominating tests. The optimizer // finds some, but we _know_ they are all useless. peeled_dom_test_elim(loop,old_new); + loop->record_for_igvn(); } //------------------------------is_invariant----------------------------- @@ -2699,27 +2710,38 @@ _igvn.register_new_node_with_optimizer(store_value); } + if (CCallingConventionRequiresIntsAsLongs && + // See StubRoutines::select_fill_function for types. FLOAT has been converted to INT. + (t == T_FLOAT || t == T_INT || is_subword_type(t))) { + 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 (CCallingConventionRequiresIntsAsLongs) { + 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) ); - call->init_req( TypeFunc::ReturnAdr, C->start()->proj_out(TypeFunc::ReturnAdr) ); - call->init_req( TypeFunc::FramePtr, C->start()->proj_out(TypeFunc::FramePtr) ); + 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)); + call->init_req(TypeFunc::ReturnAdr, C->start()->proj_out(TypeFunc::ReturnAdr)); + 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); _igvn.register_new_node_with_optimizer(result_ctrl); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/loopnode.cpp --- a/src/share/vm/opto/loopnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/loopnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -266,9 +266,9 @@ // Counted loop head must be a good RegionNode with only 3 not NULL // control input edges: Self, Entry, LoopBack. - if (x->in(LoopNode::Self) == NULL || x->req() != 3) + if (x->in(LoopNode::Self) == NULL || x->req() != 3 || loop->_irreducible) { return false; - + } Node *init_control = x->in(LoopNode::EntryControl); Node *back_control = x->in(LoopNode::LoopBackControl); if (init_control == NULL || back_control == NULL) // Partially dead @@ -1522,11 +1522,11 @@ // If I have one hot backedge, peel off myself loop. // I better be the outermost loop. - if( _head->req() > 3 ) { + if (_head->req() > 3 && !_irreducible) { split_outer_loop( phase ); result = true; - } else if( !_head->is_Loop() && !_irreducible ) { + } else if (!_head->is_Loop() && !_irreducible) { // Make a new LoopNode to replace the old loop head Node *l = new (phase->C) LoopNode( _head->in(1), _head->in(2) ); l = igvn.register_new_node_with_optimizer(l, _head); @@ -2938,6 +2938,7 @@ return pre_order; } } + C->set_has_irreducible_loop(_has_irreducible_loops); } // This Node might be a decision point for loops. It is only if @@ -3171,17 +3172,16 @@ bool had_error = false; #ifdef ASSERT if (early != C->root()) { - // Make sure that there's a dominance path from use to LCA - Node* d = use; - while (d != LCA) { - d = idom(d); + // Make sure that there's a dominance path from LCA to early + Node* d = LCA; + while (d != early) { if (d == C->root()) { - tty->print_cr("*** Use %d isn't dominated by def %s", use->_idx, n->_idx); - n->dump(); - use->dump(); + dump_bad_graph("Bad graph detected in compute_lca_of_uses", n, early, LCA); + tty->print_cr("*** Use %d isn't dominated by def %d ***", use->_idx, n->_idx); had_error = true; break; } + d = idom(d); } } #endif @@ -3434,6 +3434,13 @@ _igvn._worklist.push(n); // Maybe we'll normalize it, if no more loops. } +#ifdef ASSERT + if (_verify_only && !n->is_CFG()) { + // Check def-use domination. + compute_lca_of_uses(n, get_ctrl(n), true /* verify */); + } +#endif + // CFG and pinned nodes already handled if( n->in(0) ) { if( n->in(0)->is_top() ) return; // Dead? @@ -3561,7 +3568,7 @@ #ifdef ASSERT void PhaseIdealLoop::dump_bad_graph(const char* msg, Node* n, Node* early, Node* LCA) { - tty->print_cr(msg); + tty->print_cr("%s", msg); tty->print("n: "); n->dump(); tty->print("early(n): "); early->dump(); if (n->in(0) != NULL && !n->in(0)->is_top() && diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/loopopts.cpp --- a/src/share/vm/opto/loopopts.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/loopopts.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -43,12 +43,6 @@ return NULL; } - if (n->is_MathExact()) { - // MathExact has projections that are not correctly handled in the code - // below. - return NULL; - } - int wins = 0; assert(!n->is_CFG(), ""); assert(region->is_Region(), ""); @@ -1115,8 +1109,8 @@ Node *n2 = phi->in(i)->in(1)->in(2); phi1->set_req( i, n1 ); phi2->set_req( i, n2 ); - phi1->set_type( phi1->type()->meet(n1->bottom_type()) ); - phi2->set_type( phi2->type()->meet(n2->bottom_type()) ); + phi1->set_type( phi1->type()->meet_speculative(n1->bottom_type())); + phi2->set_type( phi2->type()->meet_speculative(n2->bottom_type())); } // See if these Phis have been made before. // Register with optimizer @@ -1189,8 +1183,8 @@ } phi1->set_req( j, n1 ); phi2->set_req( j, n2 ); - phi1->set_type( phi1->type()->meet(n1->bottom_type()) ); - phi2->set_type( phi2->type()->meet(n2->bottom_type()) ); + phi1->set_type(phi1->type()->meet_speculative(n1->bottom_type())); + phi2->set_type(phi2->type()->meet_speculative(n2->bottom_type())); } // See if these Phis have been made before. @@ -1407,7 +1401,8 @@ // loop. Happens if people set a loop-exit flag; then test the flag // in the loop to break the loop, then test is again outside of the // loop to determine which way the loop exited. - if( use->is_If() || use->is_CMove() ) { + // Loop predicate If node connects to Bool node through Opaque1 node. + if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use)) { // Since this code is highly unlikely, we lazily build the worklist // of such Nodes to go split. if( !split_if_set ) @@ -2362,8 +2357,7 @@ opc == Op_Catch || opc == Op_CatchProj || opc == Op_Jump || - opc == Op_JumpProj || - opc == Op_FlagsProj) { + opc == Op_JumpProj) { #if !defined(PRODUCT) if (TracePartialPeeling) { tty->print_cr("\nExit control too complex: lp: %d", head->_idx); @@ -2705,6 +2699,7 @@ // Inhibit more partial peeling on this loop new_head_clone->set_partial_peel_loop(); C->set_major_progress(); + loop->record_for_igvn(); #if !defined(PRODUCT) if (TracePartialPeeling) { @@ -2774,11 +2769,11 @@ // Hit! Refactor use to use the post-incremented tripcounter. // Compute a post-increment tripcounter. Node *opaq = new (C) Opaque2Node( C, cle->incr() ); - register_new_node( opaq, u_ctrl ); + register_new_node(opaq, exit); Node *neg_stride = _igvn.intcon(-cle->stride_con()); set_ctrl(neg_stride, C->root()); Node *post = new (C) AddINode( opaq, neg_stride); - register_new_node( post, u_ctrl ); + register_new_node(post, exit); _igvn.rehash_node_delayed(use); for (uint j = 1; j < use->req(); j++) { if (use->in(j) == phi) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/machnode.cpp --- a/src/share/vm/opto/machnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/machnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -134,6 +134,10 @@ ShouldNotCallThis(); } +//---------------------------postalloc_expand---------------------------------- +// Expand node after register allocation. +void MachNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) {} + //------------------------------size------------------------------------------- // Size of instruction in bytes uint MachNode::size(PhaseRegAlloc *ra_) const { @@ -393,6 +397,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 @@ -501,6 +516,9 @@ return _constant.offset(); } +int MachConstantNode::constant_offset_unchecked() const { + return _constant.offset(); +} //============================================================================= #ifndef PRODUCT @@ -641,10 +659,15 @@ //------------------------------Registers-------------------------------------- -const RegMask &MachCallNode::in_RegMask( uint idx ) const { +const RegMask &MachCallNode::in_RegMask(uint idx) const { // Values in the domain use the users calling convention, embodied in the // _in_rms array of RegMasks. - if (idx < tf()->domain()->cnt()) return _in_rms[idx]; + if (idx < tf()->domain()->cnt()) { + return _in_rms[idx]; + } + if (idx == mach_constant_base_node_input()) { + return MachConstantBaseNode::static_out_RegMask(); + } // Values outside the domain represent debug info return *Compile::current()->matcher()->idealreg2debugmask[in(idx)->ideal_reg()]; } @@ -671,7 +694,12 @@ const RegMask &MachCallJavaNode::in_RegMask(uint idx) const { // Values in the domain use the users calling convention, embodied in the // _in_rms array of RegMasks. - if (idx < tf()->domain()->cnt()) return _in_rms[idx]; + if (idx < tf()->domain()->cnt()) { + return _in_rms[idx]; + } + if (idx == mach_constant_base_node_input()) { + return MachConstantBaseNode::static_out_RegMask(); + } // Values outside the domain represent debug info Matcher* m = Compile::current()->matcher(); // If this call is a MethodHandle invoke we have to use a different diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/machnode.hpp --- a/src/share/vm/opto/machnode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/machnode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -31,6 +31,7 @@ #include "opto/node.hpp" #include "opto/regmask.hpp" +class BiasedLockingCounters; class BufferBlob; class CodeBuffer; class JVMState; @@ -52,6 +53,7 @@ class Matcher; class PhaseRegAlloc; class RegMask; +class RTMLockingCounters; class State; //---------------------------MachOper------------------------------------------ @@ -102,6 +104,15 @@ return ::as_XMMRegister(reg(ra_, node, idx)); } #endif + // CondRegister reg converter +#if defined(PPC64) + ConditionRegister as_ConditionRegister(PhaseRegAlloc *ra_, const Node *node) const { + return ::as_ConditionRegister(reg(ra_, node)); + } + ConditionRegister as_ConditionRegister(PhaseRegAlloc *ra_, const Node *node, int idx) const { + return ::as_ConditionRegister(reg(ra_, node, idx)); + } +#endif virtual intptr_t constant() const; virtual relocInfo::relocType constant_reloc() const; @@ -155,7 +166,15 @@ 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 -#endif + + // 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 // !PRODUCT }; //------------------------------MachNode--------------------------------------- @@ -173,6 +192,9 @@ // Number of inputs which come before the first operand. // Generally at least 1, to skip the Control input virtual uint oper_input_base() const { return 1; } + // Position of constant base node in node's inputs. -1 if + // no constant base node input. + virtual uint mach_constant_base_node_input() const { return (uint)-1; } // Copy inputs and operands to new node of instruction. // Called from cisc_version() and short_branch_version(). @@ -188,13 +210,21 @@ bool may_be_short_branch() const { return (flags() & Flag_may_be_short_branch) != 0; } // Avoid back to back some instructions on some CPUs. - bool avoid_back_to_back() const { return (flags() & Flag_avoid_back_to_back) != 0; } + enum AvoidBackToBackFlag { AVOID_NONE = 0, + AVOID_BEFORE = Flag_avoid_back_to_back_before, + AVOID_AFTER = Flag_avoid_back_to_back_after, + AVOID_BEFORE_AND_AFTER = AVOID_BEFORE | AVOID_AFTER }; + + bool avoid_back_to_back(AvoidBackToBackFlag flag_value) const { + return (flags() & flag_value) == flag_value; + } // instruction implemented with a call bool has_call() const { return (flags() & Flag_has_call) != 0; } // 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; @@ -220,6 +250,12 @@ // Emit bytes into cbuf virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + // Expand node after register allocation. + // Node is replaced by several nodes in the postalloc expand phase. + // Corresponding methods are generated for nodes if they specify + // postalloc_expand. See block.cpp for more documentation. + virtual bool requires_postalloc_expand() const { return false; } + virtual void postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_); // Size of instruction in bytes virtual uint size(PhaseRegAlloc *ra_) const; // Helper function that computes size by emitting code @@ -236,6 +272,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 +332,9 @@ static const Pipeline *pipeline_class(); virtual const Pipeline *pipeline() const; + // Returns true if this node is a check that can be implemented with a trap. + virtual bool is_TrapBasedCheckNode() const { return false; } + #ifndef PRODUCT virtual const char *Name() const = 0; // Machine-specific name virtual void dump_spec(outputStream *st) const; // Print per-node info @@ -356,6 +398,9 @@ virtual uint ideal_reg() const { return Op_RegP; } virtual uint oper_input_base() const { return 1; } + virtual bool requires_postalloc_expand() const; + virtual void postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_); + virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const; virtual uint size(PhaseRegAlloc* ra_) const; virtual bool pinned() const { return UseRDPCForConstantTableBase; } @@ -395,10 +440,12 @@ } // Input edge of MachConstantBaseNode. - uint mach_constant_base_node_input() const { return req() - 1; } + virtual uint mach_constant_base_node_input() const { return req() - 1; } int constant_offset(); int constant_offset() const { return ((MachConstantNode*) this)->constant_offset(); } + // Unchecked version to avoid assertions in debug output. + int constant_offset_unchecked() const; }; //------------------------------MachUEPNode----------------------------------- @@ -620,8 +667,9 @@ class MachFastLockNode : public MachNode { virtual uint size_of() const { return sizeof(*this); } // Size is bigger public: - BiasedLockingCounters* _counters; - + BiasedLockingCounters* _counters; + RTMLockingCounters* _rtm_counters; // RTM lock counters for inflated locks + RTMLockingCounters* _stack_rtm_counters; // RTM lock counters for stack locks MachFastLockNode() : MachNode() {} }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/macro.cpp --- a/src/share/vm/opto/macro.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/macro.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1084,7 +1084,7 @@ Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { Node* adr = basic_plus_adr(base, offset); const TypePtr* adr_type = adr->bottom_type()->is_ptr(); - Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt); + Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt, MemNode::unordered); transform_later(value); return value; } @@ -1092,7 +1092,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, MemNode::unordered); transform_later(mem); return mem; } @@ -1272,8 +1272,8 @@ // Load(-locked) the heap top. // See note above concerning the control input when using a TLAB Node *old_eden_top = UseTLAB - ? new (C) LoadPNode (ctrl, contended_phi_rawmem, eden_top_adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM) - : new (C) LoadPLockedNode(contended_region, contended_phi_rawmem, eden_top_adr); + ? new (C) LoadPNode (ctrl, contended_phi_rawmem, eden_top_adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, MemNode::unordered) + : new (C) LoadPLockedNode(contended_region, contended_phi_rawmem, eden_top_adr, MemNode::acquire); transform_later(old_eden_top); // Add to heap top to get a new heap top @@ -1320,7 +1320,7 @@ if (UseTLAB) { Node* store_eden_top = new (C) StorePNode(needgc_false, contended_phi_rawmem, eden_top_adr, - TypeRawPtr::BOTTOM, new_eden_top); + TypeRawPtr::BOTTOM, new_eden_top, MemNode::unordered); transform_later(store_eden_top); fast_oop_ctrl = needgc_false; // No contention, so this is the fast path fast_oop_rawmem = store_eden_top; @@ -1700,9 +1700,10 @@ _igvn.MakeConX(in_bytes(JavaThread::tlab_pf_top_offset())) ); transform_later(eden_pf_adr); - Node *old_pf_wm = new (C) LoadPNode( needgc_false, + Node *old_pf_wm = new (C) LoadPNode(needgc_false, contended_phi_rawmem, eden_pf_adr, - TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM ); + TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, + MemNode::unordered); transform_later(old_pf_wm); // check against new_eden_top @@ -1726,9 +1727,10 @@ transform_later(new_pf_wmt ); new_pf_wmt->set_req(0, need_pf_true); - Node *store_new_wmt = new (C) StorePNode( need_pf_true, + Node *store_new_wmt = new (C) StorePNode(need_pf_true, contended_phi_rawmem, eden_pf_adr, - TypeRawPtr::BOTTOM, new_pf_wmt ); + TypeRawPtr::BOTTOM, new_pf_wmt, + MemNode::unordered); transform_later(store_new_wmt); // adding prefetches @@ -2437,6 +2439,7 @@ } } // Next, attempt to eliminate allocations + _has_locks = false; progress = true; while (progress) { progress = false; @@ -2455,11 +2458,13 @@ case Node::Class_Lock: case Node::Class_Unlock: assert(!n->as_AbstractLock()->is_eliminated(), "sanity"); + _has_locks = true; break; default: assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque1 || - n->Opcode() == Op_Opaque2, "unknown node type in macro list"); + n->Opcode() == Op_Opaque2 || + n->Opcode() == Op_Opaque3, "unknown node type in macro list"); } assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); progress = progress || success; @@ -2500,6 +2505,30 @@ } else if (n->Opcode() == Op_Opaque1 || n->Opcode() == Op_Opaque2) { _igvn.replace_node(n, n->in(1)); success = true; +#if INCLUDE_RTM_OPT + } else if ((n->Opcode() == Op_Opaque3) && ((Opaque3Node*)n)->rtm_opt()) { + assert(C->profile_rtm(), "should be used only in rtm deoptimization code"); + assert((n->outcnt() == 1) && n->unique_out()->is_Cmp(), ""); + Node* cmp = n->unique_out(); +#ifdef ASSERT + // Validate graph. + assert((cmp->outcnt() == 1) && cmp->unique_out()->is_Bool(), ""); + BoolNode* bol = cmp->unique_out()->as_Bool(); + assert((bol->outcnt() == 1) && bol->unique_out()->is_If() && + (bol->_test._test == BoolTest::ne), ""); + IfNode* ifn = bol->unique_out()->as_If(); + assert((ifn->outcnt() == 2) && + ifn->proj_out(1)->is_uncommon_trap_proj(Deoptimization::Reason_rtm_state_change), ""); +#endif + Node* repl = n->in(1); + if (!_has_locks) { + // Remove RTM state check if there are no locks in the code. + // Replace input to compare the same value. + repl = (cmp->in(1) == n) ? cmp->in(2) : cmp->in(1); + } + _igvn.replace_node(n, repl); + success = true; +#endif } assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); progress = progress || success; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/macro.hpp --- a/src/share/vm/opto/macro.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/macro.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -76,6 +76,8 @@ ProjNode *_memproj_catchall; ProjNode *_resproj; + // Additional data collected during macro expansion + bool _has_locks; void expand_allocate(AllocateNode *alloc); void expand_allocate_array(AllocateArrayNode *alloc); @@ -118,7 +120,7 @@ Node* length); public: - PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn) { + PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn), _has_locks(false) { _igvn.set_delay_transform(true); } void eliminate_macro_nodes(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/matcher.cpp --- a/src/share/vm/opto/matcher.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/matcher.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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; @@ -842,16 +845,15 @@ // Compute generic short-offset Loads #ifdef _LP64 - MachNode *spillCP = match_tree(new (C) LoadNNode(NULL,mem,fp,atp,TypeInstPtr::BOTTOM)); + MachNode *spillCP = match_tree(new (C) LoadNNode(NULL,mem,fp,atp,TypeInstPtr::BOTTOM,MemNode::unordered)); #endif - MachNode *spillI = match_tree(new (C) LoadINode(NULL,mem,fp,atp)); - MachNode *spillL = match_tree(new (C) LoadLNode(NULL,mem,fp,atp)); - MachNode *spillF = match_tree(new (C) LoadFNode(NULL,mem,fp,atp)); - MachNode *spillD = match_tree(new (C) LoadDNode(NULL,mem,fp,atp)); - MachNode *spillP = match_tree(new (C) LoadPNode(NULL,mem,fp,atp,TypeInstPtr::BOTTOM)); + MachNode *spillI = match_tree(new (C) LoadINode(NULL,mem,fp,atp,TypeInt::INT,MemNode::unordered)); + MachNode *spillL = match_tree(new (C) LoadLNode(NULL,mem,fp,atp,TypeLong::LONG,MemNode::unordered,false)); + MachNode *spillF = match_tree(new (C) LoadFNode(NULL,mem,fp,atp,Type::FLOAT,MemNode::unordered)); + MachNode *spillD = match_tree(new (C) LoadDNode(NULL,mem,fp,atp,Type::DOUBLE,MemNode::unordered)); + MachNode *spillP = match_tree(new (C) LoadPNode(NULL,mem,fp,atp,TypeInstPtr::BOTTOM,MemNode::unordered)); assert(spillI != NULL && spillL != NULL && spillF != NULL && spillD != NULL && spillP != NULL, ""); - // Get the ADLC notion of the right regmask, for each basic type. #ifdef _LP64 idealreg2regmask[Op_RegN] = &spillCP->out_RegMask(); @@ -1336,12 +1338,24 @@ } // Debug inputs begin just after the last incoming parameter - assert( (mcall == NULL) || (mcall->jvms() == NULL) || - (mcall->jvms()->debug_start() + mcall->_jvmadj == mcall->tf()->domain()->cnt()), "" ); + assert((mcall == NULL) || (mcall->jvms() == NULL) || + (mcall->jvms()->debug_start() + mcall->_jvmadj == mcall->tf()->domain()->cnt()), ""); // Move the OopMap msfpt->_oop_map = sfpt->_oop_map; + // Add additional edges. + if (msfpt->mach_constant_base_node_input() != (uint)-1 && !msfpt->is_MachCallLeaf()) { + // For these calls we can not add MachConstantBase in expand(), as the + // ins are not complete then. + msfpt->ins_req(msfpt->mach_constant_base_node_input(), C->mach_constant_base_node()); + if (msfpt->jvms() && + msfpt->mach_constant_base_node_input() <= msfpt->jvms()->debug_start() + msfpt->_jvmadj) { + // We added an edge before jvms, so we must adapt the position of the ins. + msfpt->jvms()->adapt_position(+1); + } + } + // Registers killed by the call are set in the local scheduling pass // of Global Code Motion. return msfpt; @@ -1908,6 +1922,105 @@ return OptoReg::as_OptoReg(regs.first()); } +// This function identifies sub-graphs in which a 'load' node is +// input to two different nodes, and such that it can be matched +// with BMI instructions like blsi, blsr, etc. +// Example : for b = -a[i] & a[i] can be matched to blsi r32, m32. +// The graph is (AndL (SubL Con0 LoadL*) LoadL*), where LoadL* +// refers to the same node. +#ifdef X86 +// Match the generic fused operations pattern (op1 (op2 Con{ConType} mop) mop) +// This is a temporary solution until we make DAGs expressible in ADL. +template +class FusedPatternMatcher { + Node* _op1_node; + Node* _mop_node; + int _con_op; + + static int match_next(Node* n, int next_op, int next_op_idx) { + if (n->in(1) == NULL || n->in(2) == NULL) { + return -1; + } + + if (next_op_idx == -1) { // n is commutative, try rotations + if (n->in(1)->Opcode() == next_op) { + return 1; + } else if (n->in(2)->Opcode() == next_op) { + return 2; + } + } else { + assert(next_op_idx > 0 && next_op_idx <= 2, "Bad argument index"); + if (n->in(next_op_idx)->Opcode() == next_op) { + return next_op_idx; + } + } + return -1; + } +public: + FusedPatternMatcher(Node* op1_node, Node *mop_node, int con_op) : + _op1_node(op1_node), _mop_node(mop_node), _con_op(con_op) { } + + bool match(int op1, int op1_op2_idx, // op1 and the index of the op1->op2 edge, -1 if op1 is commutative + int op2, int op2_con_idx, // op2 and the index of the op2->con edge, -1 if op2 is commutative + typename ConType::NativeType con_value) { + if (_op1_node->Opcode() != op1) { + return false; + } + if (_mop_node->outcnt() > 2) { + return false; + } + op1_op2_idx = match_next(_op1_node, op2, op1_op2_idx); + if (op1_op2_idx == -1) { + return false; + } + // Memory operation must be the other edge + int op1_mop_idx = (op1_op2_idx & 1) + 1; + + // Check that the mop node is really what we want + if (_op1_node->in(op1_mop_idx) == _mop_node) { + Node *op2_node = _op1_node->in(op1_op2_idx); + if (op2_node->outcnt() > 1) { + return false; + } + assert(op2_node->Opcode() == op2, "Should be"); + op2_con_idx = match_next(op2_node, _con_op, op2_con_idx); + if (op2_con_idx == -1) { + return false; + } + // Memory operation must be the other edge + int op2_mop_idx = (op2_con_idx & 1) + 1; + // Check that the memory operation is the same node + if (op2_node->in(op2_mop_idx) == _mop_node) { + // Now check the constant + const Type* con_type = op2_node->in(op2_con_idx)->bottom_type(); + if (con_type != Type::TOP && ConType::as_self(con_type)->get_con() == con_value) { + return true; + } + } + } + return false; + } +}; + + +bool Matcher::is_bmi_pattern(Node *n, Node *m) { + if (n != NULL && m != NULL) { + if (m->Opcode() == Op_LoadI) { + FusedPatternMatcher bmii(n, m, Op_ConI); + return bmii.match(Op_AndI, -1, Op_SubI, 1, 0) || + bmii.match(Op_AndI, -1, Op_AddI, -1, -1) || + bmii.match(Op_XorI, -1, Op_AddI, -1, -1); + } else if (m->Opcode() == Op_LoadL) { + FusedPatternMatcher bmil(n, m, Op_ConL); + return bmil.match(Op_AndL, -1, Op_SubL, 1, 0) || + bmil.match(Op_AndL, -1, Op_AddL, -1, -1) || + bmil.match(Op_XorL, -1, Op_AddL, -1, -1); + } + } + return false; +} +#endif // X86 + // A method-klass-holder may be passed in the inline_cache_reg // and then expanded into the inline_cache_reg and a method_oop register // defined in ad_.cpp @@ -1984,7 +2097,6 @@ case Op_Catch: case Op_CatchProj: case Op_CProj: - case Op_FlagsProj: case Op_JumpProj: case Op_JProj: case Op_NeverBranch: @@ -2064,6 +2176,14 @@ set_shared(m->in(AddPNode::Base)->in(1)); } + // if 'n' and 'm' are part of a graph for BMI instruction, clone this node. +#ifdef X86 + if (UseBMI1Instructions && is_bmi_pattern(n, m)) { + mstack.push(m, Visit); + continue; + } +#endif + // Clone addressing expressions as they are "free" in memory access instructions if( mem_op && i == MemNode::Address && mop == Op_AddP ) { // Some inputs for address expression are not put on stack @@ -2331,7 +2451,7 @@ bool Matcher::post_store_load_barrier(const Node* vmb) { Compile* C = Compile::current(); assert(vmb->is_MemBar(), ""); - assert(vmb->Opcode() != Op_MemBarAcquire, ""); + assert(vmb->Opcode() != Op_MemBarAcquire && vmb->Opcode() != Op_LoadFence, ""); const MemBarNode* membar = vmb->as_MemBar(); // Get the Ideal Proj node, ctrl, that can be used to iterate forward @@ -2376,7 +2496,7 @@ if (x->is_MemBar()) { // We must retain this membar if there is an upcoming volatile // load, which will be followed by acquire membar. - if (xop == Op_MemBarAcquire) { + if (xop == Op_MemBarAcquire || xop == Op_LoadFence) { return false; } else { // For other kinds of barriers, check by pretending we @@ -2393,6 +2513,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 *C = Compile::current(); + if (!C->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(C->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) { @@ -2439,7 +2622,7 @@ tty->print_cr("%s %d %s", ruleName[i], _cost[i], ruleName[_rule[i]] ); } - tty->print_cr(""); + tty->cr(); for( i=0; i<2; i++ ) if( _kids[i] ) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/matcher.hpp --- a/src/share/vm/opto/matcher.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/matcher.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -79,6 +79,9 @@ // Find shared Nodes, or Nodes that otherwise are Matcher roots void find_shared( Node *n ); +#ifdef X86 + bool is_bmi_pattern(Node *n, Node *m); +#endif // Debug and profile information for nodes in old space: GrowableArray* _old_node_note_array; @@ -286,6 +289,9 @@ // CPU supports misaligned vectors store/load. static const bool misaligned_vectors_ok(); + // Should original key array reference be passed to AES stubs + static const bool pass_original_key_for_aes(); + // Used to determine a "low complexity" 64-bit constant. (Zero is simple.) // The standard of comparison is one (StoreL ConL) vs. two (StoreI ConI). // Depends on the details of 64-bit constant generation on the CPU. @@ -337,10 +343,6 @@ // Register for MODL projection of divmodL static RegMask modL_proj_mask(); - static const RegMask mathExactI_result_proj_mask(); - static const RegMask mathExactL_result_proj_mask(); - static const RegMask mathExactI_flags_proj_mask(); - // Use hardware DIV instruction when it is faster than // a code which use multiply for division by constant. static bool use_asm_for_ldiv_by_con( jlong divisor ); @@ -449,6 +451,10 @@ // aligned. static const bool misaligned_doubles_ok; + // Does the CPU require postalloc expand (see block.cpp for description of + // postalloc expand)? + static const bool require_postalloc_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); @@ -481,6 +487,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/mathexactnode.cpp --- a/src/share/vm/opto/mathexactnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/mathexactnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -31,358 +31,93 @@ #include "opto/mathexactnode.hpp" #include "opto/subnode.hpp" -MathExactNode::MathExactNode(Node* ctrl, Node* in1) : MultiNode(2) { - init_class_id(Class_MathExact); - init_req(0, ctrl); - init_req(1, in1); -} - -MathExactNode::MathExactNode(Node* ctrl, Node* in1, Node* in2) : MultiNode(3) { - init_class_id(Class_MathExact); - init_req(0, ctrl); - init_req(1, in1); - init_req(2, in2); -} - -BoolNode* MathExactNode::bool_node() const { - Node* flags = flags_node(); - BoolNode* boolnode = flags->unique_out()->as_Bool(); - assert(boolnode != NULL, "must have BoolNode"); - return boolnode; -} - -IfNode* MathExactNode::if_node() const { - BoolNode* boolnode = bool_node(); - IfNode* ifnode = boolnode->unique_out()->as_If(); - assert(ifnode != NULL, "must have IfNode"); - return ifnode; -} - -Node* MathExactNode::control_node() const { - IfNode* ifnode = if_node(); - return ifnode->in(0); -} - -Node* MathExactNode::non_throwing_branch() const { - IfNode* ifnode = if_node(); - if (bool_node()->_test._test == BoolTest::overflow) { - return ifnode->proj_out(0); - } - return ifnode->proj_out(1); -} - -// If the MathExactNode won't overflow we have to replace the -// FlagsProjNode and ProjNode that is generated by the MathExactNode -Node* MathExactNode::no_overflow(PhaseGVN* phase, Node* new_result) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - if (igvn) { - ProjNode* result = result_node(); - ProjNode* flags = flags_node(); - - if (result != NULL) { - igvn->replace_node(result, new_result); - } +template +class AddHelper { +public: + typedef typename OverflowOp::TypeClass TypeClass; + typedef typename TypeClass::NativeType NativeType; - if (flags != NULL) { - BoolNode* boolnode = bool_node(); - switch (boolnode->_test._test) { - case BoolTest::overflow: - // if the check is for overflow - never taken - igvn->replace_node(boolnode, phase->intcon(0)); - break; - case BoolTest::no_overflow: - // if the check is for no overflow - always taken - igvn->replace_node(boolnode, phase->intcon(1)); - break; - default: - fatal("Unexpected value of BoolTest"); - break; - } - flags->del_req(0); + static bool will_overflow(NativeType value1, NativeType value2) { + NativeType result = value1 + value2; + // Hacker's Delight 2-12 Overflow if both arguments have the opposite sign of the result + if (((value1 ^ result) & (value2 ^ result)) >= 0) { + return false; } - } - return new_result; -} - -Node* MathExactINode::match(const ProjNode* proj, const Matcher* m) { - uint ideal_reg = proj->ideal_reg(); - RegMask rm; - if (proj->_con == result_proj_node) { - rm = m->mathExactI_result_proj_mask(); - } else { - assert(proj->_con == flags_proj_node, "must be result or flags"); - assert(ideal_reg == Op_RegFlags, "sanity"); - rm = m->mathExactI_flags_proj_mask(); - } - return new (m->C) MachProjNode(this, proj->_con, rm, ideal_reg); -} - -Node* MathExactLNode::match(const ProjNode* proj, const Matcher* m) { - uint ideal_reg = proj->ideal_reg(); - RegMask rm; - if (proj->_con == result_proj_node) { - rm = m->mathExactL_result_proj_mask(); - } else { - assert(proj->_con == flags_proj_node, "must be result or flags"); - assert(ideal_reg == Op_RegFlags, "sanity"); - rm = m->mathExactI_flags_proj_mask(); - } - return new (m->C) MachProjNode(this, proj->_con, rm, ideal_reg); -} - -Node* AddExactINode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* arg1 = in(1); - Node* arg2 = in(2); - - const Type* type1 = phase->type(arg1); - const Type* type2 = phase->type(arg2); - - if (type1 != Type::TOP && type1->singleton() && - type2 != Type::TOP && type2->singleton()) { - jint val1 = arg1->get_int(); - jint val2 = arg2->get_int(); - jint result = val1 + val2; - // Hacker's Delight 2-12 Overflow if both arguments have the opposite sign of the result - if ( (((val1 ^ result) & (val2 ^ result)) >= 0)) { - Node* con_result = ConINode::make(phase->C, result); - return no_overflow(phase, con_result); - } - return NULL; + return true; } - if (type1 == TypeInt::ZERO || type2 == TypeInt::ZERO) { // (Add 0 x) == x - Node* add_result = new (phase->C) AddINode(arg1, arg2); - return no_overflow(phase, add_result); - } - - if (type2->singleton()) { - return NULL; // no change - keep constant on the right + static bool can_overflow(const Type* type1, const Type* type2) { + if (type1 == TypeClass::ZERO || type2 == TypeClass::ZERO) { + return false; + } + return true; } - - if (type1->singleton()) { - // Make it x + Constant - move constant to the right - swap_edges(1, 2); - return this; - } - - if (arg2->is_Load()) { - return NULL; // no change - keep load on the right - } - - if (arg1->is_Load()) { - // Make it x + Load - move load to the right - swap_edges(1, 2); - return this; - } +}; - if (arg1->_idx > arg2->_idx) { - // Sort the edges - swap_edges(1, 2); - return this; - } - - return NULL; -} - -Node* AddExactLNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* arg1 = in(1); - Node* arg2 = in(2); +template +class SubHelper { +public: + typedef typename OverflowOp::TypeClass TypeClass; + typedef typename TypeClass::NativeType NativeType; - const Type* type1 = phase->type(arg1); - const Type* type2 = phase->type(arg2); - - if (type1 != Type::TOP && type1->singleton() && - type2 != Type::TOP && type2->singleton()) { - jlong val1 = arg1->get_long(); - jlong val2 = arg2->get_long(); - jlong result = val1 + val2; - // Hacker's Delight 2-12 Overflow if both arguments have the opposite sign of the result - if ( (((val1 ^ result) & (val2 ^ result)) >= 0)) { - Node* con_result = ConLNode::make(phase->C, result); - return no_overflow(phase, con_result); + static bool will_overflow(NativeType value1, NativeType value2) { + NativeType result = value1 - value2; + // hacker's delight 2-12 overflow iff the arguments have different signs and + // the sign of the result is different than the sign of arg1 + if (((value1 ^ value2) & (value1 ^ result)) >= 0) { + return false; } - return NULL; + return true; } - if (type1 == TypeLong::ZERO || type2 == TypeLong::ZERO) { // (Add 0 x) == x - Node* add_result = new (phase->C) AddLNode(arg1, arg2); - return no_overflow(phase, add_result); + static bool can_overflow(const Type* type1, const Type* type2) { + if (type2 == TypeClass::ZERO) { + return false; + } + return true; } - - if (type2->singleton()) { - return NULL; // no change - keep constant on the right - } - - if (type1->singleton()) { - // Make it x + Constant - move constant to the right - swap_edges(1, 2); - return this; - } +}; - if (arg2->is_Load()) { - return NULL; // no change - keep load on the right - } - - if (arg1->is_Load()) { - // Make it x + Load - move load to the right - swap_edges(1, 2); - return this; - } - - if (arg1->_idx > arg2->_idx) { - // Sort the edges - swap_edges(1, 2); - return this; - } +template +class MulHelper { +public: + typedef typename OverflowOp::TypeClass TypeClass; - return NULL; -} - -Node* SubExactINode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* arg1 = in(1); - Node* arg2 = in(2); - - const Type* type1 = phase->type(arg1); - const Type* type2 = phase->type(arg2); - - if (type1 != Type::TOP && type1->singleton() && - type2 != Type::TOP && type2->singleton()) { - jint val1 = arg1->get_int(); - jint val2 = arg2->get_int(); - jint result = val1 - val2; + static bool can_overflow(const Type* type1, const Type* type2) { + if (type1 == TypeClass::ZERO || type2 == TypeClass::ZERO) { + return false; + } else if (type1 == TypeClass::ONE || type2 == TypeClass::ONE) { + return false; + } + return true; + } +}; - // Hacker's Delight 2-12 Overflow iff the arguments have different signs and - // the sign of the result is different than the sign of arg1 - if (((val1 ^ val2) & (val1 ^ result)) >= 0) { - Node* con_result = ConINode::make(phase->C, result); - return no_overflow(phase, con_result); - } - return NULL; - } - - if (type1 == TypeInt::ZERO || type2 == TypeInt::ZERO) { - // Sub with zero is the same as add with zero - Node* add_result = new (phase->C) AddINode(arg1, arg2); - return no_overflow(phase, add_result); - } - - return NULL; +bool OverflowAddINode::will_overflow(jint v1, jint v2) const { + return AddHelper::will_overflow(v1, v2); } -Node* SubExactLNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* arg1 = in(1); - Node* arg2 = in(2); - - const Type* type1 = phase->type(arg1); - const Type* type2 = phase->type(arg2); - - if (type1 != Type::TOP && type1->singleton() && - type2 != Type::TOP && type2->singleton()) { - jlong val1 = arg1->get_long(); - jlong val2 = arg2->get_long(); - jlong result = val1 - val2; - - // Hacker's Delight 2-12 Overflow iff the arguments have different signs and - // the sign of the result is different than the sign of arg1 - if (((val1 ^ val2) & (val1 ^ result)) >= 0) { - Node* con_result = ConLNode::make(phase->C, result); - return no_overflow(phase, con_result); - } - return NULL; - } - - if (type1 == TypeLong::ZERO || type2 == TypeLong::ZERO) { - // Sub with zero is the same as add with zero - Node* add_result = new (phase->C) AddLNode(arg1, arg2); - return no_overflow(phase, add_result); - } - - return NULL; -} - -Node* NegExactINode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node *arg = in(1); - - const Type* type = phase->type(arg); - if (type != Type::TOP && type->singleton()) { - jint value = arg->get_int(); - if (value != min_jint) { - Node* neg_result = ConINode::make(phase->C, -value); - return no_overflow(phase, neg_result); - } - } - return NULL; +bool OverflowSubINode::will_overflow(jint v1, jint v2) const { + return SubHelper::will_overflow(v1, v2); } -Node* NegExactLNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node *arg = in(1); - - const Type* type = phase->type(arg); - if (type != Type::TOP && type->singleton()) { - jlong value = arg->get_long(); - if (value != min_jlong) { - Node* neg_result = ConLNode::make(phase->C, -value); - return no_overflow(phase, neg_result); +bool OverflowMulINode::will_overflow(jint v1, jint v2) const { + jlong result = (jlong) v1 * (jlong) v2; + if ((jint) result == result) { + return false; } - } - return NULL; + return true; } -Node* MulExactINode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* arg1 = in(1); - Node* arg2 = in(2); - - const Type* type1 = phase->type(arg1); - const Type* type2 = phase->type(arg2); - - if (type1 != Type::TOP && type1->singleton() && - type2 != Type::TOP && type2->singleton()) { - jint val1 = arg1->get_int(); - jint val2 = arg2->get_int(); - jlong result = (jlong) val1 * (jlong) val2; - if ((jint) result == result) { - // no overflow - Node* mul_result = ConINode::make(phase->C, result); - return no_overflow(phase, mul_result); - } - } - - if (type1 == TypeInt::ZERO || type2 == TypeInt::ZERO) { - return no_overflow(phase, ConINode::make(phase->C, 0)); - } - - if (type1 == TypeInt::ONE) { - Node* mul_result = new (phase->C) AddINode(arg2, phase->intcon(0)); - return no_overflow(phase, mul_result); - } - if (type2 == TypeInt::ONE) { - Node* mul_result = new (phase->C) AddINode(arg1, phase->intcon(0)); - return no_overflow(phase, mul_result); - } - - if (type1 == TypeInt::MINUS_1) { - return new (phase->C) NegExactINode(NULL, arg2); - } - - if (type2 == TypeInt::MINUS_1) { - return new (phase->C) NegExactINode(NULL, arg1); - } - - return NULL; +bool OverflowAddLNode::will_overflow(jlong v1, jlong v2) const { + return AddHelper::will_overflow(v1, v2); } -Node* MulExactLNode::Ideal(PhaseGVN* phase, bool can_reshape) { - Node* arg1 = in(1); - Node* arg2 = in(2); +bool OverflowSubLNode::will_overflow(jlong v1, jlong v2) const { + return SubHelper::will_overflow(v1, v2); +} - const Type* type1 = phase->type(arg1); - const Type* type2 = phase->type(arg2); - - if (type1 != Type::TOP && type1->singleton() && - type2 != Type::TOP && type2->singleton()) { - jlong val1 = arg1->get_long(); - jlong val2 = arg2->get_long(); - +bool OverflowMulLNode::will_overflow(jlong val1, jlong val2) const { jlong result = val1 * val2; jlong ax = (val1 < 0 ? -val1 : val1); jlong ay = (val2 < 0 ? -val2 : val2); @@ -398,33 +133,125 @@ } } - if (!overflow) { - Node* mul_result = ConLNode::make(phase->C, result); - return no_overflow(phase, mul_result); + return overflow; +} + +bool OverflowAddINode::can_overflow(const Type* t1, const Type* t2) const { + return AddHelper::can_overflow(t1, t2); +} + +bool OverflowSubINode::can_overflow(const Type* t1, const Type* t2) const { + if (in(1) == in(2)) { + return false; + } + return SubHelper::can_overflow(t1, t2); +} + +bool OverflowMulINode::can_overflow(const Type* t1, const Type* t2) const { + return MulHelper::can_overflow(t1, t2); +} + +bool OverflowAddLNode::can_overflow(const Type* t1, const Type* t2) const { + return AddHelper::can_overflow(t1, t2); +} + +bool OverflowSubLNode::can_overflow(const Type* t1, const Type* t2) const { + if (in(1) == in(2)) { + return false; + } + return SubHelper::can_overflow(t1, t2); +} + +bool OverflowMulLNode::can_overflow(const Type* t1, const Type* t2) const { + return MulHelper::can_overflow(t1, t2); +} + +const Type* OverflowNode::sub(const Type* t1, const Type* t2) const { + fatal(err_msg_res("sub() should not be called for '%s'", NodeClassNames[this->Opcode()])); + return TypeInt::CC; +} + +template +struct IdealHelper { + typedef typename OverflowOp::TypeClass TypeClass; // TypeInt, TypeLong + typedef typename TypeClass::NativeType NativeType; + + static Node* Ideal(const OverflowOp* node, PhaseGVN* phase, bool can_reshape) { + Node* arg1 = node->in(1); + Node* arg2 = node->in(2); + const Type* type1 = phase->type(arg1); + const Type* type2 = phase->type(arg2); + + if (type1 == NULL || type2 == NULL) { + return NULL; } - } - if (type1 == TypeLong::ZERO || type2 == TypeLong::ZERO) { - return no_overflow(phase, ConLNode::make(phase->C, 0)); + if (type1 != Type::TOP && type1->singleton() && + type2 != Type::TOP && type2->singleton()) { + NativeType val1 = TypeClass::as_self(type1)->get_con(); + NativeType val2 = TypeClass::as_self(type2)->get_con(); + if (node->will_overflow(val1, val2) == false) { + Node* con_result = ConINode::make(phase->C, 0); + return con_result; + } + return NULL; + } + return NULL; } - if (type1 == TypeLong::ONE) { - Node* mul_result = new (phase->C) AddLNode(arg2, phase->longcon(0)); - return no_overflow(phase, mul_result); - } - if (type2 == TypeLong::ONE) { - Node* mul_result = new (phase->C) AddLNode(arg1, phase->longcon(0)); - return no_overflow(phase, mul_result); - } + static const Type* Value(const OverflowOp* node, PhaseTransform* phase) { + const Type *t1 = phase->type( node->in(1) ); + const Type *t2 = phase->type( node->in(2) ); + if( t1 == Type::TOP ) return Type::TOP; + if( t2 == Type::TOP ) return Type::TOP; + + const TypeClass* i1 = TypeClass::as_self(t1); + const TypeClass* i2 = TypeClass::as_self(t2); + + if (i1 == NULL || i2 == NULL) { + return TypeInt::CC; + } - if (type1 == TypeLong::MINUS_1) { - return new (phase->C) NegExactLNode(NULL, arg2); - } + if (t1->singleton() && t2->singleton()) { + NativeType val1 = i1->get_con(); + NativeType val2 = i2->get_con(); + if (node->will_overflow(val1, val2)) { + return TypeInt::CC; + } + return TypeInt::ZERO; + } else if (i1 != TypeClass::TYPE_DOMAIN && i2 != TypeClass::TYPE_DOMAIN) { + if (node->will_overflow(i1->_lo, i2->_lo)) { + return TypeInt::CC; + } else if (node->will_overflow(i1->_lo, i2->_hi)) { + return TypeInt::CC; + } else if (node->will_overflow(i1->_hi, i2->_lo)) { + return TypeInt::CC; + } else if (node->will_overflow(i1->_hi, i2->_hi)) { + return TypeInt::CC; + } + return TypeInt::ZERO; + } - if (type2 == TypeLong::MINUS_1) { - return new (phase->C) NegExactLNode(NULL, arg1); + if (!node->can_overflow(t1, t2)) { + return TypeInt::ZERO; + } + return TypeInt::CC; } +}; - return NULL; +Node* OverflowINode::Ideal(PhaseGVN* phase, bool can_reshape) { + return IdealHelper::Ideal(this, phase, can_reshape); } +Node* OverflowLNode::Ideal(PhaseGVN* phase, bool can_reshape) { + return IdealHelper::Ideal(this, phase, can_reshape); +} + +const Type* OverflowINode::Value(PhaseTransform* phase) const { + return IdealHelper::Value(this, phase); +} + +const Type* OverflowLNode::Value(PhaseTransform* phase) const { + return IdealHelper::Value(this, phase); +} + diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/mathexactnode.hpp --- a/src/share/vm/opto/mathexactnode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/mathexactnode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -27,128 +27,111 @@ #include "opto/multnode.hpp" #include "opto/node.hpp" +#include "opto/addnode.hpp" #include "opto/subnode.hpp" #include "opto/type.hpp" -class BoolNode; -class IfNode; -class Node; - class PhaseGVN; class PhaseTransform; -class MathExactNode : public MultiNode { +class OverflowNode : public CmpNode { public: - MathExactNode(Node* ctrl, Node* in1); - MathExactNode(Node* ctrl, Node* in1, Node* in2); - enum { - result_proj_node = 0, - flags_proj_node = 1 - }; - virtual int Opcode() const; - virtual Node* Identity(PhaseTransform* phase) { return this; } - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) { return NULL; } - virtual const Type* Value(PhaseTransform* phase) const { return bottom_type(); } - virtual uint hash() const { return NO_HASH; } - virtual bool is_CFG() const { return false; } - virtual uint ideal_reg() const { return NotAMachineReg; } + OverflowNode(Node* in1, Node* in2) : CmpNode(in1, in2) {} - ProjNode* result_node() const { return proj_out(result_proj_node); } - ProjNode* flags_node() const { return proj_out(flags_proj_node); } - Node* control_node() const; - Node* non_throwing_branch() const; -protected: - IfNode* if_node() const; - BoolNode* bool_node() const; - Node* no_overflow(PhaseGVN *phase, Node* new_result); -}; - -class MathExactINode : public MathExactNode { - public: - MathExactINode(Node* ctrl, Node* in1) : MathExactNode(ctrl, in1) {} - MathExactINode(Node* ctrl, Node* in1, Node* in2) : MathExactNode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node* match(const ProjNode* proj, const Matcher* m); - virtual const Type* bottom_type() const { return TypeTuple::INT_CC_PAIR; } -}; - -class MathExactLNode : public MathExactNode { -public: - MathExactLNode(Node* ctrl, Node* in1) : MathExactNode(ctrl, in1) {} - MathExactLNode(Node* ctrl, Node* in1, Node* in2) : MathExactNode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node* match(const ProjNode* proj, const Matcher* m); - virtual const Type* bottom_type() const { return TypeTuple::LONG_CC_PAIR; } -}; - -class AddExactINode : public MathExactINode { -public: - AddExactINode(Node* ctrl, Node* in1, Node* in2) : MathExactINode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual uint ideal_reg() const { return Op_RegFlags; } + virtual const Type* sub(const Type* t1, const Type* t2) const; }; -class AddExactLNode : public MathExactLNode { -public: - AddExactLNode(Node* ctrl, Node* in1, Node* in2) : MathExactLNode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; - -class SubExactINode : public MathExactINode { -public: - SubExactINode(Node* ctrl, Node* in1, Node* in2) : MathExactINode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; - -class SubExactLNode : public MathExactLNode { -public: - SubExactLNode(Node* ctrl, Node* in1, Node* in2) : MathExactLNode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; - -class NegExactINode : public MathExactINode { -public: - NegExactINode(Node* ctrl, Node* in1) : MathExactINode(ctrl, in1) {} - virtual int Opcode() const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; - -class NegExactLNode : public MathExactLNode { +class OverflowINode : public OverflowNode { public: - NegExactLNode(Node* ctrl, Node* in1) : MathExactLNode(ctrl, in1) {} - virtual int Opcode() const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; - -class MulExactINode : public MathExactINode { -public: - MulExactINode(Node* ctrl, Node* in1, Node* in2) : MathExactINode(ctrl, in1, in2) {} - virtual int Opcode() const; - virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; + typedef TypeInt TypeClass; -class MulExactLNode : public MathExactLNode { -public: - MulExactLNode(Node* ctrl, Node* in1, Node* in2) : MathExactLNode(ctrl, in1, in2) {} - virtual int Opcode() const; + OverflowINode(Node* in1, Node* in2) : OverflowNode(in1, in2) {} virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); -}; + virtual const Type* Value(PhaseTransform* phase) const; -class FlagsProjNode : public ProjNode { -public: - FlagsProjNode(Node* src, uint con) : ProjNode(src, con) { - init_class_id(Class_FlagsProj); - } - - virtual int Opcode() const; - virtual bool is_CFG() const { return false; } - virtual const Type* bottom_type() const { return TypeInt::CC; } - virtual uint ideal_reg() const { return Op_RegFlags; } + virtual bool will_overflow(jint v1, jint v2) const = 0; + virtual bool can_overflow(const Type* t1, const Type* t2) const = 0; }; +class OverflowLNode : public OverflowNode { +public: + typedef TypeLong TypeClass; + + OverflowLNode(Node* in1, Node* in2) : OverflowNode(in1, in2) {} + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape); + virtual const Type* Value(PhaseTransform* phase) const; + + virtual bool will_overflow(jlong v1, jlong v2) const = 0; + virtual bool can_overflow(const Type* t1, const Type* t2) const = 0; +}; + +class OverflowAddINode : public OverflowINode { +public: + typedef AddINode MathOp; + + OverflowAddINode(Node* in1, Node* in2) : OverflowINode(in1, in2) {} + virtual int Opcode() const; + + virtual bool will_overflow(jint v1, jint v2) const; + virtual bool can_overflow(const Type* t1, const Type* t2) const; +}; + +class OverflowSubINode : public OverflowINode { +public: + typedef SubINode MathOp; + + OverflowSubINode(Node* in1, Node* in2) : OverflowINode(in1, in2) {} + virtual int Opcode() const; + + virtual bool will_overflow(jint v1, jint v2) const; + virtual bool can_overflow(const Type* t1, const Type* t2) const; +}; + +class OverflowMulINode : public OverflowINode { +public: + typedef MulINode MathOp; + + OverflowMulINode(Node* in1, Node* in2) : OverflowINode(in1, in2) {} + virtual int Opcode() const; + + virtual bool will_overflow(jint v1, jint v2) const; + virtual bool can_overflow(const Type* t1, const Type* t2) const; +}; + +class OverflowAddLNode : public OverflowLNode { +public: + typedef AddLNode MathOp; + + OverflowAddLNode(Node* in1, Node* in2) : OverflowLNode(in1, in2) {} + virtual int Opcode() const; + + virtual bool will_overflow(jlong v1, jlong v2) const; + virtual bool can_overflow(const Type* t1, const Type* t2) const; +}; + +class OverflowSubLNode : public OverflowLNode { +public: + typedef SubLNode MathOp; + + OverflowSubLNode(Node* in1, Node* in2) : OverflowLNode(in1, in2) {} + virtual int Opcode() const; + + virtual bool will_overflow(jlong v1, jlong v2) const; + virtual bool can_overflow(const Type* t1, const Type* t2) const; +}; + +class OverflowMulLNode : public OverflowLNode { +public: + typedef MulLNode MathOp; + + OverflowMulLNode(Node* in1, Node* in2) : OverflowLNode(in1, in2) {} + virtual int Opcode() const; + + virtual bool will_overflow(jlong v1, jlong v2) const; + virtual bool can_overflow(const Type* t1, const Type* t2) const; +}; + #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/memnode.cpp --- a/src/share/vm/opto/memnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/memnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -306,33 +306,16 @@ int alias_idx = phase->C->get_alias_index(t_adr->is_ptr()); } -#ifdef ASSERT Node* base = NULL; - if (address->is_AddP()) + if (address->is_AddP()) { base = address->in(AddPNode::Base); + } if (base != NULL && phase->type(base)->higher_equal(TypePtr::NULL_PTR) && !t_adr->isa_rawptr()) { // Note: raw address has TOP base and top->higher_equal(TypePtr::NULL_PTR) is true. - Compile* C = phase->C; - tty->cr(); - tty->print_cr("===== NULL+offs not RAW address ====="); - if (C->is_dead_node(this->_idx)) tty->print_cr("'this' is dead"); - if ((ctl != NULL) && C->is_dead_node(ctl->_idx)) tty->print_cr("'ctl' is dead"); - if (C->is_dead_node(mem->_idx)) tty->print_cr("'mem' is dead"); - if (C->is_dead_node(address->_idx)) tty->print_cr("'address' is dead"); - if (C->is_dead_node(base->_idx)) tty->print_cr("'base' is dead"); - tty->cr(); - base->dump(1); - tty->cr(); - this->dump(2); - tty->print("this->adr_type(): "); adr_type()->dump(); tty->cr(); - tty->print("phase->type(address): "); t_adr->dump(); tty->cr(); - tty->print("phase->type(base): "); phase->type(address)->dump(); tty->cr(); - tty->cr(); + // Skip this node optimization if its address has TOP base. + return NodeSentinel; // caller will return NULL } - assert(base == NULL || t_adr->isa_rawptr() || - !phase->type(base)->higher_equal(TypePtr::NULL_PTR), "NULL+offs not RAW address?"); -#endif // Avoid independent memory operations Node* old_mem = mem; @@ -657,7 +640,7 @@ // disregarding "null"-ness. // (We make an exception for TypeRawPtr::BOTTOM, which is a bit bucket.) const TypePtr* tp_notnull = tp->join(TypePtr::NOTNULL)->is_ptr(); - assert(cross_check->meet(tp_notnull) == cross_check, + assert(cross_check->meet(tp_notnull) == cross_check->remove_speculative(), "real address must not escape from expected memory type"); } #endif @@ -907,7 +890,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, MemOrd mo) { Compile* C = gvn.C; // sanity check the alias category against the created node type @@ -923,34 +906,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(), mo); + case T_BYTE: return new (C) LoadBNode (ctl, mem, adr, adr_type, rt->is_int(), mo); + case T_INT: return new (C) LoadINode (ctl, mem, adr, adr_type, rt->is_int(), mo); + case T_CHAR: return new (C) LoadUSNode(ctl, mem, adr, adr_type, rt->is_int(), mo); + case T_SHORT: return new (C) LoadSNode (ctl, mem, adr, adr_type, rt->is_int(), mo); + case T_LONG: return new (C) LoadLNode (ctl, mem, adr, adr_type, rt->is_long(), mo); + case T_FLOAT: return new (C) LoadFNode (ctl, mem, adr, adr_type, rt, mo); + case T_DOUBLE: return new (C) LoadDNode (ctl, mem, adr, adr_type, rt, mo); + case T_ADDRESS: return new (C) LoadPNode (ctl, mem, adr, adr_type, rt->is_ptr(), mo); 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(), mo)); return new (C) DecodeNNode(load, load->bottom_type()->make_ptr()); } else #endif { assert(!adr->bottom_type()->is_ptr_to_narrowoop() && !adr->bottom_type()->is_ptr_to_narrowklass(), "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(), mo); } } 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, MemOrd mo) { 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(), mo, require_atomic); } @@ -1002,9 +985,13 @@ // a synchronized region. while (current->is_Proj()) { int opc = current->in(0)->Opcode(); - if ((final && (opc == Op_MemBarAcquire || opc == Op_MemBarAcquireLock)) || - opc == Op_MemBarRelease || opc == Op_MemBarCPUOrder || - opc == Op_MemBarReleaseLock) { + if ((final && (opc == Op_MemBarAcquire || + opc == Op_MemBarAcquireLock || + opc == Op_LoadFence)) || + opc == Op_MemBarRelease || + opc == Op_StoreFence || + opc == Op_MemBarReleaseLock || + opc == Op_MemBarCPUOrder) { Node* mem = current->in(0)->in(TypeFunc::Memory); if (mem->is_MergeMem()) { MergeMemNode* merge = mem->as_MergeMem(); @@ -1589,35 +1576,33 @@ // Try to constant-fold a stable array element. static const Type* fold_stable_ary_elem(const TypeAryPtr* ary, int off, BasicType loadbt) { + assert(ary->const_oop(), "array should be constant"); assert(ary->is_stable(), "array should be stable"); - if (ary->const_oop() != NULL) { - // Decode the results of GraphKit::array_element_address. - ciArray* aobj = ary->const_oop()->as_array(); - ciConstant con = aobj->element_value_by_offset(off); - - if (con.basic_type() != T_ILLEGAL && !con.is_null_or_zero()) { - const Type* con_type = Type::make_from_constant(con); - if (con_type != NULL) { - if (con_type->isa_aryptr()) { - // Join with the array element type, in case it is also stable. - int dim = ary->stable_dimension(); - con_type = con_type->is_aryptr()->cast_to_stable(true, dim-1); - } - if (loadbt == T_NARROWOOP && con_type->isa_oopptr()) { - con_type = con_type->make_narrowoop(); - } + // Decode the results of GraphKit::array_element_address. + ciArray* aobj = ary->const_oop()->as_array(); + ciConstant con = aobj->element_value_by_offset(off); + + if (con.basic_type() != T_ILLEGAL && !con.is_null_or_zero()) { + const Type* con_type = Type::make_from_constant(con); + if (con_type != NULL) { + if (con_type->isa_aryptr()) { + // Join with the array element type, in case it is also stable. + int dim = ary->stable_dimension(); + con_type = con_type->is_aryptr()->cast_to_stable(true, dim-1); + } + if (loadbt == T_NARROWOOP && con_type->isa_oopptr()) { + con_type = con_type->make_narrowoop(); + } #ifndef PRODUCT - if (TraceIterativeGVN) { - tty->print("FoldStableValues: array element [off=%d]: con_type=", off); - con_type->dump(); tty->cr(); - } + if (TraceIterativeGVN) { + tty->print("FoldStableValues: array element [off=%d]: con_type=", off); + con_type->dump(); tty->cr(); + } #endif //PRODUCT - return con_type; - } + return con_type; } } - return NULL; } @@ -1637,7 +1622,7 @@ // Try to guess loaded type from pointer type if (tp->isa_aryptr()) { const TypeAryPtr* ary = tp->is_aryptr(); - const Type *t = ary->elem(); + const Type* t = ary->elem(); // Determine whether the reference is beyond the header or not, by comparing // the offset against the offset of the start of the array's data. @@ -1649,10 +1634,9 @@ const bool off_beyond_header = ((uint)off >= (uint)min_base_off); // Try to constant-fold a stable array element. - if (FoldStableValues && ary->is_stable()) { - // Make sure the reference is not into the header - if (off_beyond_header && off != Type::OffsetBot) { - assert(adr->is_AddP() && adr->in(AddPNode::Offset)->is_Con(), "offset is a constant"); + if (FoldStableValues && ary->is_stable() && ary->const_oop() != NULL) { + // Make sure the reference is not into the header and the offset is constant + if (off_beyond_header && adr->is_AddP() && off != Type::OffsetBot) { const Type* con_type = fold_stable_ary_elem(ary, off, memory_type()); if (con_type != NULL) { return con_type; @@ -1681,7 +1665,7 @@ // t might actually be lower than _type, if _type is a unique // concrete subclass of abstract class t. if (off_beyond_header) { // is the offset beyond the header? - const Type* jt = t->join(_type); + const Type* jt = t->join_speculative(_type); // In any case, do not allow the join, per se, to empty out the type. if (jt->empty() && !t->empty()) { // This can happen if a interface-typed array narrows to a class type. @@ -2032,12 +2016,12 @@ #ifdef _LP64 if (adr_type->is_ptr_to_narrowklass()) { assert(UseCompressedClassPointers, "no compressed klasses"); - Node* load_klass = gvn.transform(new (C) LoadNKlassNode(ctl, mem, adr, at, tk->make_narrowklass())); + Node* load_klass = gvn.transform(new (C) LoadNKlassNode(ctl, mem, adr, at, tk->make_narrowklass(), MemNode::unordered)); return new (C) DecodeNKlassNode(load_klass, load_klass->bottom_type()->make_ptr()); } #endif assert(!adr_type->is_ptr_to_narrowklass() && !adr_type->is_ptr_to_narrowoop(), "should have got back a narrow oop"); - return new (C) LoadKlassNode(ctl, mem, adr, at, tk); + return new (C) LoadKlassNode(ctl, mem, adr, at, tk, MemNode::unordered); } //------------------------------Value------------------------------------------ @@ -2352,45 +2336,46 @@ //============================================================================= //---------------------------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, MemOrd mo) { + assert((mo == unordered || mo == release), "unexpected"); Compile* C = gvn.C; - assert( C->get_alias_index(adr_type) != Compile::AliasIdxRaw || - ctl != NULL, "raw memory operations should have control edge"); + assert(C->get_alias_index(adr_type) != Compile::AliasIdxRaw || + ctl != NULL, "raw memory operations should have control edge"); switch (bt) { case T_BOOLEAN: - 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, mo); + case T_INT: return new (C) StoreINode(ctl, mem, adr, adr_type, val, mo); 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, mo); + case T_LONG: return new (C) StoreLNode(ctl, mem, adr, adr_type, val, mo); + case T_FLOAT: return new (C) StoreFNode(ctl, mem, adr, adr_type, val, mo); + case T_DOUBLE: return new (C) StoreDNode(ctl, mem, adr, adr_type, val, mo); case T_METADATA: case T_ADDRESS: case T_OBJECT: #ifdef _LP64 if (adr->bottom_type()->is_ptr_to_narrowoop()) { 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, mo); } else if (adr->bottom_type()->is_ptr_to_narrowklass() || (UseCompressedClassPointers && val->bottom_type()->isa_klassptr() && adr->bottom_type()->isa_rawptr())) { val = gvn.transform(new (C) EncodePKlassNode(val, val->bottom_type()->make_narrowklass())); - return new (C) StoreNKlassNode(ctl, mem, adr, adr_type, val); + return new (C) StoreNKlassNode(ctl, mem, adr, adr_type, val, mo); } #endif { - return new (C) StorePNode(ctl, mem, adr, adr_type, val); + return new (C) StorePNode(ctl, mem, adr, adr_type, val, mo); } } 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, MemOrd mo) { 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, mo, require_atomic); } @@ -2783,12 +2768,12 @@ Node *zero = phase->makecon(TypeLong::ZERO); Node *off = phase->MakeConX(BytesPerLong); - mem = new (phase->C) StoreLNode(in(0),mem,adr,atp,zero); + mem = new (phase->C) StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false); count--; while( count-- ) { mem = phase->transform(mem); adr = phase->transform(new (phase->C) AddPNode(base,adr,off)); - mem = new (phase->C) StoreLNode(in(0),mem,adr,atp,zero); + mem = new (phase->C) StoreLNode(in(0),mem,adr,atp,zero,MemNode::unordered,false); } return mem; } @@ -2832,7 +2817,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, MemNode::unordered); mem = phase->transform(mem); offset += BytesPerInt; } @@ -2893,7 +2878,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, MemNode::unordered); mem = phase->transform(mem); done_offset += BytesPerInt; } @@ -2977,15 +2962,17 @@ //------------------------------make------------------------------------------- MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { switch (opcode) { - case Op_MemBarAcquire: return new(C) MemBarAcquireNode(C, atp, pn); - case Op_MemBarRelease: return new(C) MemBarReleaseNode(C, atp, pn); - case Op_MemBarAcquireLock: return new(C) MemBarAcquireLockNode(C, atp, pn); - case Op_MemBarReleaseLock: return new(C) MemBarReleaseLockNode(C, atp, pn); - case Op_MemBarVolatile: return new(C) MemBarVolatileNode(C, atp, pn); - case Op_MemBarCPUOrder: return new(C) MemBarCPUOrderNode(C, atp, pn); - case Op_Initialize: return new(C) InitializeNode(C, atp, pn); - case Op_MemBarStoreStore: return new(C) MemBarStoreStoreNode(C, atp, pn); - default: ShouldNotReachHere(); return NULL; + case Op_MemBarAcquire: return new(C) MemBarAcquireNode(C, atp, pn); + case Op_LoadFence: return new(C) LoadFenceNode(C, atp, pn); + case Op_MemBarRelease: return new(C) MemBarReleaseNode(C, atp, pn); + case Op_StoreFence: return new(C) StoreFenceNode(C, atp, pn); + case Op_MemBarAcquireLock: return new(C) MemBarAcquireLockNode(C, atp, pn); + case Op_MemBarReleaseLock: return new(C) MemBarReleaseLockNode(C, atp, pn); + case Op_MemBarVolatile: return new(C) MemBarVolatileNode(C, atp, pn); + case Op_MemBarCPUOrder: return new(C) MemBarCPUOrderNode(C, atp, pn); + case Op_Initialize: return new(C) InitializeNode(C, atp, pn); + case Op_MemBarStoreStore: return new(C) MemBarStoreStoreNode(C, atp, pn); + default: ShouldNotReachHere(); return NULL; } } @@ -3767,14 +3754,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, MemNode::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, MemNode::unordered); } if (con1 != 0) { ++new_int; @@ -3782,7 +3769,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, MemNode::unordered); } } @@ -4036,7 +4023,7 @@ intptr_t st_off = get_store_offset(st, phase); if (st_off < 0) continue; // ignore dead garbage if (last_off > st_off) { - tty->print_cr("*** bad store offset at %d: %d > %d", i, last_off, st_off); + tty->print_cr("*** bad store offset at %d: " INTX_FORMAT " > " INTX_FORMAT, i, last_off, st_off); this->dump(2); assert(false, "ascending store offsets"); return false; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/memnode.hpp --- a/src/share/vm/opto/memnode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/memnode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -51,6 +51,10 @@ ValueIn, // Value to store OopStore // Preceeding oop store, only in StoreCM }; + typedef enum { unordered = 0, + acquire, // Load has to acquire or be succeeded by MemBarAcquire. + release // Store has to release or be preceded by MemBarRelease. + } MemOrd; protected: MemNode( Node *c0, Node *c1, Node *c2, const TypePtr* at ) : Node(c0,c1,c2 ) { @@ -134,20 +138,32 @@ //------------------------------LoadNode--------------------------------------- // Load value; requires Memory and Address class LoadNode : public MemNode { +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 MemOrd _mo; + 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, MemOrd mo) + : MemNode(c,mem,adr,at), _type(rt), _mo(mo) { init_class_id(Class_Load); } + inline bool is_unordered() const { return !is_acquire(); } + inline bool is_acquire() const { + assert(_mo == unordered || _mo == acquire, "unexpected"); + return _mo == 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, MemOrd mo); virtual uint hash() const; // Check the type @@ -221,8 +237,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, MemOrd mo) + : LoadNode(c, mem, adr, at, ti, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -235,8 +251,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, MemOrd mo) + : LoadNode(c, mem, adr, at, ti, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); @@ -249,8 +265,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, MemOrd mo) + : LoadNode(c, mem, adr, at, ti, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -263,8 +279,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, MemOrd mo) + : LoadNode(c, mem, adr, at, ti, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -277,8 +293,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, MemOrd mo) + : LoadNode(c, mem, adr, at, ti, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual int store_Opcode() const { return Op_StoreI; } @@ -289,8 +305,8 @@ // Load an array length from the array class LoadRangeNode : public LoadINode { public: - LoadRangeNode( Node *c, Node *mem, Node *adr, const TypeInt *ti = TypeInt::POS ) - : LoadINode(c,mem,adr,TypeAryPtr::RANGE,ti) {} + LoadRangeNode(Node *c, Node *mem, Node *adr, const TypeInt *ti = TypeInt::POS) + : LoadINode(c, mem, adr, TypeAryPtr::RANGE, ti, MemNode::unordered) {} virtual int Opcode() const; virtual const Type *Value( PhaseTransform *phase ) const; virtual Node *Identity( PhaseTransform *phase ); @@ -309,18 +325,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, + MemOrd mo, bool require_atomic_access = false) + : LoadNode(c, mem, adr, at, tl, mo), _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, MemOrd mo); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { LoadNode::dump_spec(st); @@ -333,8 +347,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, MemOrd mo) + : LoadLNode(c, mem, adr, at, TypeLong::LONG, mo) {} virtual int Opcode() const; }; @@ -342,8 +356,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, MemOrd mo) + : LoadNode(c, mem, adr, at, t, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegF; } virtual int store_Opcode() const { return Op_StoreF; } @@ -354,8 +368,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, MemOrd mo) + : LoadNode(c, mem, adr, at, t, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegD; } virtual int store_Opcode() const { return Op_StoreD; } @@ -366,8 +380,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, MemOrd mo) + : LoadDNode(c, mem, adr, at, Type::DOUBLE, mo) {} virtual int Opcode() const; }; @@ -375,8 +389,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, MemOrd mo) + : LoadNode(c, mem, adr, at, t, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } virtual int store_Opcode() const { return Op_StoreP; } @@ -388,8 +402,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, MemOrd mo) + : LoadNode(c, mem, adr, at, t, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegN; } virtual int store_Opcode() const { return Op_StoreN; } @@ -400,8 +414,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, MemOrd mo) + : LoadPNode(c, mem, adr, at, tk, mo) {} virtual int Opcode() const; virtual const Type *Value( PhaseTransform *phase ) const; virtual Node *Identity( PhaseTransform *phase ); @@ -416,8 +430,8 @@ // Load a narrow Klass from an object. class LoadNKlassNode : public LoadNNode { public: - LoadNKlassNode( Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeNarrowKlass *tk ) - : LoadNNode(c,mem,adr,at,tk) {} + LoadNKlassNode(Node *c, Node *mem, Node *adr, const TypePtr *at, const TypeNarrowKlass *tk, MemOrd mo) + : LoadNNode(c, mem, adr, at, tk, mo) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegN; } virtual int store_Opcode() const { return Op_StoreNKlass; } @@ -432,6 +446,14 @@ //------------------------------StoreNode-------------------------------------- // Store value; requires Store, Address and Value class StoreNode : public MemNode { +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 MemOrd _mo; + // 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 +462,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, MemOrd mo) + : MemNode(c, mem, adr, at, val), _mo(mo) { 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, MemOrd mo) + : MemNode(c, mem, adr, at, val, oop_store), _mo(mo) { 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((_mo == unordered || _mo == release), "unexpected"); + return _mo == release; + } + + // Conservatively release stores of object references in order to + // ensure visibility of object initialization. + static inline MemOrd release_if_reference(const BasicType t) { + const MemOrd mo = (t == T_ARRAY || + t == T_ADDRESS || // Might be the address of an object reference (`boxing'). + t == T_OBJECT) ? release : unordered; + return mo; + } + + // 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, MemOrd mo); virtual uint hash() const; // Check the type @@ -482,7 +530,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual BasicType memory_type() const { return T_BYTE; } @@ -492,7 +541,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual BasicType memory_type() const { return T_CHAR; } @@ -502,7 +552,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_INT; } }; @@ -519,15 +570,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, MemOrd mo, bool require_atomic_access = false) + : StoreNode(c, mem, adr, at, val, mo), _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, MemOrd mo); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { StoreNode::dump_spec(st); @@ -540,7 +588,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_FLOAT; } }; @@ -549,7 +598,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_DOUBLE; } }; @@ -558,7 +608,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_ADDRESS; } }; @@ -567,7 +618,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, MemOrd mo) + : StoreNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_NARROWOOP; } }; @@ -576,7 +628,8 @@ // Store narrow klass to memory class StoreNKlassNode : public StoreNNode { public: - StoreNKlassNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val ) : StoreNNode(c,mem,adr,at,val) {} + StoreNKlassNode(Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, MemOrd mo) + : StoreNNode(c, mem, adr, at, val, mo) {} virtual int Opcode() const; virtual BasicType memory_type() const { return T_NARROWKLASS; } }; @@ -597,7 +650,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, MemNode::release), _oop_alias_idx(oop_alias_idx) { assert(_oop_alias_idx >= Compile::AliasIdxRaw || _oop_alias_idx == Compile::AliasIdxBot && Compile::current()->AliasLevel() == 0, @@ -617,8 +670,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, MemOrd mo) + : LoadPNode(c, mem, adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, mo) {} virtual int Opcode() const; virtual int store_Opcode() const { return Op_StorePConditional; } virtual bool depends_only_on_test() const { return true; } @@ -941,6 +994,17 @@ virtual int Opcode() const; }; +// "Acquire" - no following ref can move before (but earlier refs can +// follow, like an early Load stalled in cache). Requires multi-cpu +// visibility. Inserted independ of any load, as required +// for intrinsic sun.misc.Unsafe.loadFence(). +class LoadFenceNode: public MemBarNode { +public: + LoadFenceNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // "Release" - no earlier ref can move after (but later refs can move // up, like a speculative pipelined cache-hitting Load). Requires // multi-cpu visibility. Inserted before a volatile store. @@ -951,6 +1015,17 @@ virtual int Opcode() const; }; +// "Release" - no earlier ref can move after (but later refs can move +// up, like a speculative pipelined cache-hitting Load). Requires +// multi-cpu visibility. Inserted independent of any store, as required +// for intrinsic sun.misc.Unsafe.storeFence(). +class StoreFenceNode: public MemBarNode { +public: + StoreFenceNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // "Acquire" - no following ref can move before (but earlier refs can // follow, like an early Load stalled in cache). Requires multi-cpu // visibility. Inserted after a FastLock. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/mulnode.cpp --- a/src/share/vm/opto/mulnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/mulnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -485,7 +485,8 @@ Node *ldus = new (phase->C) LoadUSNode(load->in(MemNode::Control), load->in(MemNode::Memory), load->in(MemNode::Address), - load->adr_type()); + load->adr_type(), + TypeInt::CHAR, MemNode::unordered); ldus = phase->transform(ldus); return new (phase->C) AndINode(ldus, phase->intcon(mask & 0xFFFF)); } @@ -496,7 +497,8 @@ Node* ldub = new (phase->C) LoadUBNode(load->in(MemNode::Control), load->in(MemNode::Memory), load->in(MemNode::Address), - load->adr_type()); + load->adr_type(), + TypeInt::UBYTE, MemNode::unordered); ldub = phase->transform(ldub); return new (phase->C) AndINode(ldub, phase->intcon(mask)); } @@ -931,9 +933,10 @@ ld->outcnt() == 1 && ld->unique_out() == shl) // Replace zero-extension-load with sign-extension-load return new (phase->C) LoadSNode( ld->in(MemNode::Control), - ld->in(MemNode::Memory), - ld->in(MemNode::Address), - ld->adr_type()); + ld->in(MemNode::Memory), + ld->in(MemNode::Address), + ld->adr_type(), TypeInt::SHORT, + MemNode::unordered); } // Check for "(byte[i] <<24)>>24" which simply sign-extends diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/multnode.cpp --- a/src/share/vm/opto/multnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/multnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -54,11 +54,6 @@ assert(Opcode() != Op_If || proj->Opcode() == (which_proj?Op_IfTrue:Op_IfFalse), "bad if #2"); return proj; } - } else if (p->is_FlagsProj()) { - FlagsProjNode *proj = p->as_FlagsProj(); - if (proj->_con == which_proj) { - return proj; - } } else { assert(p == this && this->is_Start(), "else must be proj"); continue; @@ -94,7 +89,7 @@ if ((_con == TypeFunc::Parms) && n->is_CallStaticJava() && n->as_CallStaticJava()->is_boxing_method()) { // The result of autoboxing is always non-null on normal path. - t = t->join(TypePtr::NOTNULL); + t = t->join_speculative(TypePtr::NOTNULL); } return t; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/node.cpp --- a/src/share/vm/opto/node.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/node.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -27,6 +27,7 @@ #include "memory/allocation.inline.hpp" #include "opto/cfgnode.hpp" #include "opto/connode.hpp" +#include "opto/loopnode.hpp" #include "opto/machnode.hpp" #include "opto/matcher.hpp" #include "opto/node.hpp" @@ -1003,13 +1004,13 @@ if (is_Type()) { TypeNode *n = this->as_Type(); if (VerifyAliases) { - assert(new_type->higher_equal(n->type()), "new type must refine old type"); + assert(new_type->higher_equal_speculative(n->type()), "new type must refine old type"); } n->set_type(new_type); } else if (is_Load()) { LoadNode *n = this->as_Load(); if (VerifyAliases) { - assert(new_type->higher_equal(n->type()), "new type must refine old type"); + assert(new_type->higher_equal_speculative(n->type()), "new type must refine old type"); } n->set_type(new_type); } @@ -1263,6 +1264,7 @@ Node *top = igvn->C->top(); nstack.push(dead); + bool has_irreducible_loop = igvn->C->has_irreducible_loop(); while (nstack.size() > 0) { dead = nstack.pop(); @@ -1277,13 +1279,31 @@ assert (!use->is_Con(), "Control for Con node should be Root node."); use->set_req(0, top); // Cut dead edge to prevent processing nstack.push(use); // the dead node again. + } else if (!has_irreducible_loop && // Backedge could be alive in irreducible loop + use->is_Loop() && !use->is_Root() && // Don't kill Root (RootNode extends LoopNode) + use->in(LoopNode::EntryControl) == dead) { // Dead loop if its entry is dead + use->set_req(LoopNode::EntryControl, top); // Cut dead edge to prevent processing + use->set_req(0, top); // Cut self edge + nstack.push(use); } else { // Else found a not-dead user + // Dead if all inputs are top or null + bool dead_use = !use->is_Root(); // Keep empty graph alive for (uint j = 1; j < use->req(); j++) { - if (use->in(j) == dead) { // Turn all dead inputs into TOP + Node* in = use->in(j); + if (in == dead) { // Turn all dead inputs into TOP use->set_req(j, top); + } else if (in != NULL && !in->is_top()) { + dead_use = false; } } - igvn->_worklist.push(use); + if (dead_use) { + if (use->is_Region()) { + use->set_req(0, top); // Cut self edge + } + nstack.push(use); + } else { + igvn->_worklist.push(use); + } } // Refresh the iterator, since any number of kills might have happened. k = dead->last_outs(kmin); @@ -1531,7 +1551,6 @@ #ifndef PRODUCT -int Node::_in_dump_cnt = 0; // -----------------------------Name------------------------------------------- extern const char *NodeClassNames[]; @@ -1603,7 +1622,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 @@ -1618,7 +1637,7 @@ dump_orig(debug_orig(), st); #endif st->cr(); - _in_dump_cnt--; + C->_in_dump_cnt--; return; // don't process dead nodes } @@ -1669,8 +1688,8 @@ } } } - if (suffix) st->print(suffix); - _in_dump_cnt--; + if (suffix) st->print("%s", suffix); + C->_in_dump_cnt--; } //------------------------------dump_req-------------------------------------- diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/node.hpp --- a/src/share/vm/opto/node.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/node.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -69,7 +69,6 @@ class EncodePKlassNode; class FastLockNode; class FastUnlockNode; -class FlagsProjNode; class IfNode; class IfFalseNode; class IfTrueNode; @@ -100,7 +99,6 @@ class MachSpillCopyNode; class MachTempNode; class Matcher; -class MathExactNode; class MemBarNode; class MemBarStoreStoreNode; class MemNode; @@ -357,6 +355,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]; } @@ -384,6 +384,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 del_req_ordered( uint idx ); // Delete required edge & compact with preserved order @@ -569,7 +573,6 @@ DEFINE_CLASS_ID(MemBar, Multi, 3) DEFINE_CLASS_ID(Initialize, MemBar, 0) DEFINE_CLASS_ID(MemBarStoreStore, MemBar, 1) - DEFINE_CLASS_ID(MathExact, Multi, 4) DEFINE_CLASS_ID(Mach, Node, 1) DEFINE_CLASS_ID(MachReturn, Mach, 0) @@ -626,7 +629,6 @@ DEFINE_CLASS_ID(Cmp, Sub, 0) DEFINE_CLASS_ID(FastLock, Cmp, 0) DEFINE_CLASS_ID(FastUnlock, Cmp, 1) - DEFINE_CLASS_ID(FlagsProj, Cmp, 2) DEFINE_CLASS_ID(MergeMem, Node, 7) DEFINE_CLASS_ID(Bool, Node, 8) @@ -643,17 +645,18 @@ // Flags are sorted by usage frequency. enum NodeFlags { - Flag_is_Copy = 0x01, // should be first bit to avoid shift - Flag_rematerialize = Flag_is_Copy << 1, + Flag_is_Copy = 0x01, // should be first bit to avoid shift + Flag_rematerialize = Flag_is_Copy << 1, Flag_needs_anti_dependence_check = Flag_rematerialize << 1, - Flag_is_macro = Flag_needs_anti_dependence_check << 1, - Flag_is_Con = Flag_is_macro << 1, - Flag_is_cisc_alternate = Flag_is_Con << 1, - Flag_is_dead_loop_safe = Flag_is_cisc_alternate << 1, - Flag_may_be_short_branch = Flag_is_dead_loop_safe << 1, - Flag_avoid_back_to_back = Flag_may_be_short_branch << 1, - Flag_has_call = Flag_avoid_back_to_back << 1, - Flag_is_expensive = Flag_has_call << 1, + Flag_is_macro = Flag_needs_anti_dependence_check << 1, + Flag_is_Con = Flag_is_macro << 1, + Flag_is_cisc_alternate = Flag_is_Con << 1, + Flag_is_dead_loop_safe = Flag_is_cisc_alternate << 1, + Flag_may_be_short_branch = Flag_is_dead_loop_safe << 1, + Flag_avoid_back_to_back_before = Flag_may_be_short_branch << 1, + Flag_avoid_back_to_back_after = Flag_avoid_back_to_back_before << 1, + Flag_has_call = Flag_avoid_back_to_back_after << 1, + Flag_is_expensive = Flag_has_call << 1, _max_flags = (Flag_is_expensive << 1) - 1 // allow flags combination }; @@ -730,7 +733,6 @@ DEFINE_CLASS_QUERY(EncodePKlass) DEFINE_CLASS_QUERY(FastLock) DEFINE_CLASS_QUERY(FastUnlock) - DEFINE_CLASS_QUERY(FlagsProj) DEFINE_CLASS_QUERY(If) DEFINE_CLASS_QUERY(IfFalse) DEFINE_CLASS_QUERY(IfTrue) @@ -759,7 +761,6 @@ DEFINE_CLASS_QUERY(MachSafePoint) DEFINE_CLASS_QUERY(MachSpillCopy) DEFINE_CLASS_QUERY(MachTemp) - DEFINE_CLASS_QUERY(MathExact) DEFINE_CLASS_QUERY(Mem) DEFINE_CLASS_QUERY(MemBar) DEFINE_CLASS_QUERY(MemBarStoreStore) @@ -1027,8 +1028,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++) @@ -1350,7 +1350,7 @@ public: Node_List() : Node_Array(Thread::current()->resource_area()), _cnt(0) {} Node_List(Arena *a) : Node_Array(a), _cnt(0) {} - bool contains(Node* n) { + bool contains(const Node* n) const { for (uint e = 0; e < size(); e++) { if (at(e) == n) return true; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/output.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -42,18 +42,12 @@ #include "runtime/handles.inline.hpp" #include "utilities/xmlstream.hpp" -extern uint size_exception_handler(); -extern uint size_deopt_handler(); - #ifndef PRODUCT #define DEBUG_ARG(x) , x #else #define DEBUG_ARG(x) #endif -extern int emit_exception_handler(CodeBuffer &cbuf); -extern int emit_deopt_handler(CodeBuffer &cbuf); - // Convert Nodes to instruction bits and pass off to the VM void Compile::Output() { // RootNode goes @@ -171,8 +165,13 @@ // Determine if we need to generate a stack overflow check. // Do it if the method is not a stub function and // has java calls or has frame size > vm_page_size/8. + // The debug VM checks that deoptimization doesn't trigger an + // unexpected stack overflow (compiled method stack banging should + // guarantee it doesn't happen) so we always need the stack bang in + // a debug VM. return (UseStackBanging && stub_function() == NULL && - (has_java_calls() || frame_size_in_bytes > os::vm_page_size()>>3)); + (has_java_calls() || frame_size_in_bytes > os::vm_page_size()>>3 + DEBUG_ONLY(|| true))); } bool Compile::need_register_stack_bang() const { @@ -394,6 +393,11 @@ blk_size += (mach->alignment_required() - 1) * relocInfo::addr_unit(); // assume worst case padding reloc_size += mach->reloc(); if (mach->is_MachCall()) { + // add size information for trampoline stub + // class CallStubImpl is platform-specific and defined in the *.ad files. + stub_size += CallStubImpl::size_call_trampoline(); + reloc_size += CallStubImpl::reloc_call_trampoline(); + MachCallNode *mcall = mach->as_MachCall(); // This destination address is NOT PC-relative @@ -412,7 +416,7 @@ blk_size += nop_size; } } - if (mach->avoid_back_to_back()) { + if (mach->avoid_back_to_back(MachNode::AVOID_BEFORE)) { // Nop is inserted between "avoid back to back" instructions. // ScheduleAndBundle() can rearrange nodes in a block, // check for all offsets inside this block. @@ -440,7 +444,7 @@ last_call_adr = blk_starts[i]+blk_size; } // Remember end of avoid_back_to_back offset - if (nj->is_Mach() && nj->as_Mach()->avoid_back_to_back()) { + if (nj->is_Mach() && nj->as_Mach()->avoid_back_to_back(MachNode::AVOID_AFTER)) { last_avoid_back_to_back_adr = blk_starts[i]+blk_size; } } @@ -526,11 +530,11 @@ int new_size = replacement->size(_regalloc); int diff = br_size - new_size; assert(diff >= (int)nop_size, "short_branch size should be smaller"); - // Conservatively take into accound padding between + // Conservatively take into account padding between // avoid_back_to_back branches. Previous branch could be // converted into avoid_back_to_back branch during next // rounds. - if (needs_padding && replacement->avoid_back_to_back()) { + if (needs_padding && replacement->avoid_back_to_back(MachNode::AVOID_BEFORE)) { jmp_offset[i] += nop_size; diff -= nop_size; } @@ -549,7 +553,7 @@ } } // (mach->may_be_short_branch()) if (mach != NULL && (mach->may_be_short_branch() || - mach->avoid_back_to_back())) { + mach->avoid_back_to_back(MachNode::AVOID_AFTER))) { last_may_be_short_branch_adr = blk_starts[i] + jmp_offset[i] + jmp_size[i]; } blk_starts[i+1] -= adjust_block_start; @@ -1081,7 +1085,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 @@ -1099,6 +1103,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->number_of_blocks(); i++) { @@ -1112,6 +1117,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); } } } @@ -1119,7 +1127,7 @@ // 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; } // Initialize the space for the BufferBlob used to find and verify @@ -1132,10 +1140,9 @@ shorten_branches(blk_starts, code_req, locs_req, stub_req); // nmethod and CodeBuffer count stubs & constants as part of method's code. - int exception_handler_req = size_exception_handler(); - int deopt_handler_req = size_deopt_handler(); - exception_handler_req += MAX_stubs_size; // add marginal slop for handler - deopt_handler_req += MAX_stubs_size; // add marginal slop for handler + // class HandlerImpl is platform-specific and defined in the *.ad files. + int exception_handler_req = HandlerImpl::size_exception_handler() + MAX_stubs_size; // add marginal slop for handler + int deopt_handler_req = HandlerImpl::size_deopt_handler() + MAX_stubs_size; // add marginal slop for handler stub_req += MAX_stubs_size; // ensure per-stub margin code_req += MAX_inst_size; // ensure per-instruction margin @@ -1314,7 +1321,7 @@ if (is_sfn && !is_mcall && padding == 0 && current_offset == last_call_offset) { padding = nop_size; } - if (padding == 0 && mach->avoid_back_to_back() && + if (padding == 0 && mach->avoid_back_to_back(MachNode::AVOID_BEFORE) && current_offset == last_avoid_back_to_back_offset) { // Avoid back to back some instructions. padding = nop_size; @@ -1390,7 +1397,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); } @@ -1408,7 +1415,7 @@ int new_size = replacement->size(_regalloc); assert((br_size - new_size) >= (int)nop_size, "short_branch size should be smaller"); // Insert padding between avoid_back_to_back branches. - if (needs_padding && replacement->avoid_back_to_back()) { + if (needs_padding && replacement->avoid_back_to_back(MachNode::AVOID_BEFORE)) { MachNode *nop = new (this) MachNopNode(); block->insert_node(nop, j++); _cfg->map_node_to_block(nop, block); @@ -1471,6 +1478,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 @@ -1510,7 +1523,7 @@ last_call_offset = current_offset; } - if (n->is_Mach() && n->as_Mach()->avoid_back_to_back()) { + if (n->is_Mach() && n->as_Mach()->avoid_back_to_back(MachNode::AVOID_AFTER)) { // Avoid back to back some instructions. last_avoid_back_to_back_offset = current_offset; } @@ -1615,17 +1628,18 @@ FillExceptionTables(inct_cnt, call_returns, inct_starts, blk_labels); // Only java methods have exception handlers and deopt handlers + // class HandlerImpl is platform-specific and defined in the *.ad files. if (_method) { // Emit the exception handler code. - _code_offsets.set_value(CodeOffsets::Exceptions, emit_exception_handler(*cb)); + _code_offsets.set_value(CodeOffsets::Exceptions, HandlerImpl::emit_exception_handler(*cb)); // Emit the deopt handler code. - _code_offsets.set_value(CodeOffsets::Deopt, emit_deopt_handler(*cb)); + _code_offsets.set_value(CodeOffsets::Deopt, HandlerImpl::emit_deopt_handler(*cb)); // Emit the MethodHandle deopt handler code (if required). if (has_method_handle_invokes()) { // We can use the same code as for the normal deopt handler, we // just need a different entry point address. - _code_offsets.set_value(CodeOffsets::DeoptMH, emit_deopt_handler(*cb)); + _code_offsets.set_value(CodeOffsets::DeoptMH, HandlerImpl::emit_deopt_handler(*cb)); } } @@ -1737,6 +1751,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_Mach() && n->as_Mach()->is_TrapBasedCheckNode()) { + uint block_num = block->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 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/output.hpp --- a/src/share/vm/opto/output.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/output.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/parse.hpp --- a/src/share/vm/opto/parse.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/parse.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -141,6 +141,13 @@ GrowableArray subtrees() { return _subtrees; } void print_value_on(outputStream* st) const PRODUCT_RETURN; + + bool _forced_inline; // Inlining was forced by CompilerOracle or ciReplay + bool forced_inline() const { return _forced_inline; } + // Count number of nodes in this subtree + int count() const; + // Dump inlining replay data to the stream. + void dump_replay_data(outputStream* out); }; @@ -330,7 +337,8 @@ GraphKit _exits; // Record all normal returns and throws here. bool _wrote_final; // Did we write a final field? - bool _count_invocations; // update and test invocation counter + bool _wrote_volatile; // Did we write a volatile field? + bool _count_invocations; // update and test invocation counter bool _method_data_update; // update method data oop Node* _alloc_with_final; // An allocation node with final field @@ -373,6 +381,8 @@ GraphKit& exits() { return _exits; } bool wrote_final() const { return _wrote_final; } void set_wrote_final(bool z) { _wrote_final = z; } + bool wrote_volatile() const { return _wrote_volatile; } + void set_wrote_volatile(bool z) { _wrote_volatile = z; } bool count_invocations() const { return _count_invocations; } bool method_data_update() const { return _method_data_update; } Node* alloc_with_final() const { return _alloc_with_final; } @@ -470,6 +480,8 @@ // Helper function to compute array addressing Node* array_addressing(BasicType type, int vals, const Type* *result2=NULL); + void rtm_deopt(); + // Pass current map to exits void return_current(Node* value); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/parse1.cpp --- a/src/share/vm/opto/parse1.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/parse1.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -106,24 +106,24 @@ // Very similar to LoadNode::make, except we handle un-aligned longs and // doubles on Sparc. Intel can handle them just fine directly. Node *l; - switch( bt ) { // Signature is flattened - case T_INT: l = new (C) LoadINode( ctl, mem, adr, TypeRawPtr::BOTTOM ); break; - case T_FLOAT: l = new (C) LoadFNode( ctl, mem, adr, TypeRawPtr::BOTTOM ); break; - case T_ADDRESS: l = new (C) LoadPNode( ctl, mem, adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM ); break; - case T_OBJECT: l = new (C) LoadPNode( ctl, mem, adr, TypeRawPtr::BOTTOM, TypeInstPtr::BOTTOM ); break; + switch (bt) { // Signature is flattened + case T_INT: l = new (C) LoadINode(ctl, mem, adr, TypeRawPtr::BOTTOM, TypeInt::INT, MemNode::unordered); break; + case T_FLOAT: l = new (C) LoadFNode(ctl, mem, adr, TypeRawPtr::BOTTOM, Type::FLOAT, MemNode::unordered); break; + case T_ADDRESS: l = new (C) LoadPNode(ctl, mem, adr, TypeRawPtr::BOTTOM, TypeRawPtr::BOTTOM, MemNode::unordered); break; + case T_OBJECT: l = new (C) LoadPNode(ctl, mem, adr, TypeRawPtr::BOTTOM, TypeInstPtr::BOTTOM, MemNode::unordered); break; case T_LONG: case T_DOUBLE: { // Since arguments are in reverse order, the argument address 'adr' // refers to the back half of the long/double. Recompute adr. - adr = basic_plus_adr( local_addrs_base, local_addrs, -(index+1)*wordSize ); - if( Matcher::misaligned_doubles_ok ) { + adr = basic_plus_adr(local_addrs_base, local_addrs, -(index+1)*wordSize); + if (Matcher::misaligned_doubles_ok) { l = (bt == T_DOUBLE) - ? (Node*)new (C) LoadDNode( ctl, mem, adr, TypeRawPtr::BOTTOM ) - : (Node*)new (C) LoadLNode( ctl, mem, adr, TypeRawPtr::BOTTOM ); + ? (Node*)new (C) LoadDNode(ctl, mem, adr, TypeRawPtr::BOTTOM, Type::DOUBLE, MemNode::unordered) + : (Node*)new (C) LoadLNode(ctl, mem, adr, TypeRawPtr::BOTTOM, TypeLong::LONG, MemNode::unordered); } else { l = (bt == T_DOUBLE) - ? (Node*)new (C) LoadD_unalignedNode( ctl, mem, adr, TypeRawPtr::BOTTOM ) - : (Node*)new (C) LoadL_unalignedNode( ctl, mem, adr, TypeRawPtr::BOTTOM ); + ? (Node*)new (C) LoadD_unalignedNode(ctl, mem, adr, TypeRawPtr::BOTTOM, MemNode::unordered) + : (Node*)new (C) LoadL_unalignedNode(ctl, mem, adr, TypeRawPtr::BOTTOM, MemNode::unordered); } break; } @@ -229,7 +229,7 @@ Node *displaced_hdr = fetch_interpreter_state((index*2) + 1, T_ADDRESS, monitors_addr, osr_buf); - store_to_memory(control(), box, displaced_hdr, T_ADDRESS, Compile::AliasIdxRaw); + store_to_memory(control(), box, displaced_hdr, T_ADDRESS, Compile::AliasIdxRaw, MemNode::unordered); // Build a bogus FastLockNode (no code will be generated) and push the // monitor into our debug info. @@ -390,6 +390,7 @@ _expected_uses = expected_uses; _depth = 1 + (caller->has_method() ? caller->depth() : 0); _wrote_final = false; + _wrote_volatile = false; _alloc_with_final = NULL; _entry_bci = InvocationEntryBci; _tf = NULL; @@ -564,6 +565,10 @@ set_map(entry_map); do_method_entry(); } + if (depth() == 1) { + // Add check to deoptimize the nmethod if RTM state was changed + rtm_deopt(); + } // Check for bailouts during method entry. if (failing()) { @@ -602,7 +607,7 @@ set_map(entry_map); do_exits(); - if (log) log->done("parse nodes='%d' live='%d' memory='%d'", + if (log) log->done("parse nodes='%d' live='%d' memory='" SIZE_FORMAT "'", C->unique(), C->live_nodes(), C->node_arena()->used()); } @@ -907,7 +912,13 @@ Node* iophi = _exits.i_o(); _exits.set_i_o(gvn().transform(iophi)); - if (wrote_final()) { + // On PPC64, also add MemBarRelease for constructors which write + // volatile fields. As support_IRIW_for_not_multiple_copy_atomic_cpu + // is set on PPC64, no sync instruction is issued after volatile + // stores. We want to quarantee the same behaviour as on platforms + // with total store order, although this is not required by the Java + // memory model. So as with finals, we add a barrier here. + 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 @@ -1358,7 +1369,7 @@ tty->print((( i < ns) ? " %d" : " %d(e)"), b->successor_at(i)->rpo()); } if (b->is_loop_head()) tty->print(" lphd"); - tty->print_cr(""); + tty->cr(); } assert(block()->is_merged(), "must be merged before being parsed"); @@ -1649,7 +1660,7 @@ assert(bt1 != Type::BOTTOM, "should not be building conflict phis"); map()->set_req(j, _gvn.transform_no_reclaim(phi)); debug_only(const Type* bt2 = phi->bottom_type()); - assert(bt2->higher_equal(bt1), "must be consistent with type-flow"); + assert(bt2->higher_equal_speculative(bt1), "must be consistent with type-flow"); record_for_igvn(phi); } } @@ -1931,7 +1942,7 @@ Node* klass = _gvn.transform( LoadKlassNode::make(_gvn, immutable_memory(), klass_addr, TypeInstPtr::KLASS) ); Node* access_flags_addr = basic_plus_adr(klass, klass, in_bytes(Klass::access_flags_offset())); - Node* access_flags = make_load(NULL, access_flags_addr, TypeInt::INT, T_INT); + Node* access_flags = make_load(NULL, access_flags_addr, TypeInt::INT, T_INT, MemNode::unordered); Node* mask = _gvn.transform(new (C) AndINode(access_flags, intcon(JVM_ACC_HAS_FINALIZER))); Node* check = _gvn.transform(new (C) CmpINode(mask, intcon(0))); @@ -1975,6 +1986,42 @@ set_control( _gvn.transform(result_rgn) ); } +// Add check to deoptimize if RTM state is not ProfileRTM +void Parse::rtm_deopt() { +#if INCLUDE_RTM_OPT + if (C->profile_rtm()) { + assert(C->method() != NULL, "only for normal compilations"); + assert(!C->method()->method_data()->is_empty(), "MDO is needed to record RTM state"); + assert(depth() == 1, "generate check only for main compiled method"); + + // Set starting bci for uncommon trap. + set_parse_bci(is_osr_parse() ? osr_bci() : 0); + + // Load the rtm_state from the MethodData. + const TypePtr* adr_type = TypeMetadataPtr::make(C->method()->method_data()); + Node* mdo = makecon(adr_type); + int offset = MethodData::rtm_state_offset_in_bytes(); + Node* adr_node = basic_plus_adr(mdo, mdo, offset); + Node* rtm_state = make_load(control(), adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); + + // Separate Load from Cmp by Opaque. + // In expand_macro_nodes() it will be replaced either + // with this load when there are locks in the code + // or with ProfileRTM (cmp->in(2)) otherwise so that + // the check will fold. + Node* profile_state = makecon(TypeInt::make(ProfileRTM)); + Node* opq = _gvn.transform( new (C) Opaque3Node(C, rtm_state, Opaque3Node::RTM_OPT) ); + Node* chk = _gvn.transform( new (C) CmpINode(opq, profile_state) ); + Node* tst = _gvn.transform( new (C) BoolNode(chk, BoolTest::eq) ); + // Branch to failure if state was changed + { BuildCutout unless(this, tst, PROB_ALWAYS); + uncommon_trap(Deoptimization::Reason_rtm_state_change, + Deoptimization::Action_make_not_entrant); + } + } +#endif +} + //------------------------------return_current--------------------------------- // Append current _map to _exit_return void Parse::return_current(Node* value) { @@ -2022,7 +2069,7 @@ !tp->klass()->is_interface()) { // sharpen the type eagerly; this eases certain assert checking if (tp->higher_equal(TypeInstPtr::NOTNULL)) - tr = tr->join(TypeInstPtr::NOTNULL)->is_instptr(); + tr = tr->join_speculative(TypeInstPtr::NOTNULL)->is_instptr(); value = _gvn.transform(new (C) CheckCastPPNode(0,value,tr)); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/parse2.cpp --- a/src/share/vm/opto/parse2.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/parse2.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -50,7 +50,7 @@ if (stopped()) return; // guaranteed null or range check dec_sp(2); // Pop array and index const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(elem_type); - Node* ld = make_load(control(), adr, elem, elem_type, adr_type); + Node* ld = make_load(control(), adr, elem, elem_type, adr_type, MemNode::unordered); push(ld); } @@ -62,7 +62,7 @@ Node* val = pop(); dec_sp(2); // Pop array and index const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(elem_type); - store_to_memory(control(), adr, val, elem_type, adr_type); + store_to_memory(control(), adr, val, elem_type, adr_type, StoreNode::release_if_reference(elem_type)); } @@ -88,7 +88,7 @@ if (toop->klass()->as_instance_klass()->unique_concrete_subklass()) { // If we load from "AbstractClass[]" we must see "ConcreteSubClass". const Type* subklass = Type::get_const_type(toop->klass()); - elemtype = subklass->join(el); + elemtype = subklass->join_speculative(el); } } } @@ -626,7 +626,7 @@ _method->print_short_name(); tty->print_cr(" switch decision tree"); tty->print_cr(" %d ranges (%d singletons), max_depth=%d, est_depth=%d", - hi-lo+1, nsing, _max_switch_depth, _est_switch_depth); + (int) (hi-lo+1), nsing, _max_switch_depth, _est_switch_depth); if (_max_switch_depth > _est_switch_depth) { tty->print_cr("******** BAD SWITCH DEPTH ********"); } @@ -634,7 +634,7 @@ for( r = lo; r <= hi; r++ ) { r->print(); } - tty->print_cr(""); + tty->cr(); } #endif } @@ -1278,7 +1278,7 @@ // Bool(CmpP(LoadKlass(obj._klass), ConP(Foo.klass)), [eq]) // or the narrowOop equivalent. const Type* obj_type = _gvn.type(obj); - const TypeOopPtr* tboth = obj_type->join(con_type)->isa_oopptr(); + const TypeOopPtr* tboth = obj_type->join_speculative(con_type)->isa_oopptr(); if (tboth != NULL && tboth->klass_is_exact() && tboth != obj_type && tboth->higher_equal(obj_type)) { // obj has to be of the exact type Foo if the CmpP succeeds. @@ -1288,7 +1288,7 @@ (jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) { TypeNode* ccast = new (C) CheckCastPPNode(control(), obj, tboth); const Type* tcc = ccast->as_Type()->type(); - assert(tcc != obj_type && tcc->higher_equal(obj_type), "must improve"); + assert(tcc != obj_type && tcc->higher_equal_speculative(obj_type), "must improve"); // Delay transform() call to allow recovery of pre-cast value // at the control merge. _gvn.set_type_bottom(ccast); @@ -1318,7 +1318,7 @@ switch (btest) { case BoolTest::eq: // Constant test? { - const Type* tboth = tcon->join(tval); + const Type* tboth = tcon->join_speculative(tval); if (tboth == tval) break; // Nothing to gain. if (tcon->isa_int()) { ccast = new (C) CastIINode(val, tboth); @@ -1352,7 +1352,7 @@ if (ccast != NULL) { const Type* tcc = ccast->as_Type()->type(); - assert(tcc != tval && tcc->higher_equal(tval), "must improve"); + assert(tcc != tval && tcc->higher_equal_speculative(tval), "must improve"); // Delay transform() call to allow recovery of pre-cast value // at the control merge. ccast->set_req(0, control()); @@ -1720,14 +1720,14 @@ a = array_addressing(T_LONG, 0); if (stopped()) return; // guaranteed null or range check dec_sp(2); // Pop array and index - push_pair(make_load(control(), a, TypeLong::LONG, T_LONG, TypeAryPtr::LONGS)); + push_pair(make_load(control(), a, TypeLong::LONG, T_LONG, TypeAryPtr::LONGS, MemNode::unordered)); break; } case Bytecodes::_daload: { a = array_addressing(T_DOUBLE, 0); if (stopped()) return; // guaranteed null or range check dec_sp(2); // Pop array and index - push_pair(make_load(control(), a, Type::DOUBLE, T_DOUBLE, TypeAryPtr::DOUBLES)); + push_pair(make_load(control(), a, Type::DOUBLE, T_DOUBLE, TypeAryPtr::DOUBLES, MemNode::unordered)); break; } case Bytecodes::_bastore: array_store(T_BYTE); break; @@ -1744,7 +1744,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, MemNode::release); break; } case Bytecodes::_lastore: { @@ -1752,7 +1752,7 @@ if (stopped()) return; // guaranteed null or range check c = pop_pair(); dec_sp(2); // Pop array and index - store_to_memory(control(), a, c, T_LONG, TypeAryPtr::LONGS); + store_to_memory(control(), a, c, T_LONG, TypeAryPtr::LONGS, MemNode::unordered); break; } case Bytecodes::_dastore: { @@ -1761,7 +1761,7 @@ c = pop_pair(); dec_sp(2); // Pop array and index c = dstore_rounding(c); - store_to_memory(control(), a, c, T_DOUBLE, TypeAryPtr::DOUBLES); + store_to_memory(control(), a, c, T_DOUBLE, TypeAryPtr::DOUBLES, MemNode::unordered); break; } case Bytecodes::_getfield: diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/parse3.cpp --- a/src/share/vm/opto/parse3.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/parse3.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -227,8 +227,13 @@ } else { type = Type::get_const_basic_type(bt); } + if (support_IRIW_for_not_multiple_copy_atomic_cpu && field->is_volatile()) { + insert_mem_bar(Op_MemBarVolatile); // StoreLoad barrier + } // Build the load. - Node* ld = make_load(NULL, adr, type, bt, adr_type, is_vol); + // + MemNode::MemOrd mo = is_vol ? MemNode::acquire : MemNode::unordered; + Node* ld = make_load(NULL, adr, type, bt, adr_type, mo, is_vol); // Adjust Java stack if (type2size[bt] == 1) @@ -288,6 +293,16 @@ // Round doubles before storing if (bt == T_DOUBLE) val = dstore_rounding(val); + // Conservatively release stores of object references. + const MemNode::MemOrd mo = + is_vol ? + // Volatile fields need releasing stores. + MemNode::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) { @@ -297,15 +312,24 @@ } 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, mo); } else { - store = store_to_memory( control(), adr, val, bt, adr_type, is_vol ); + store = store_to_memory(control(), adr, val, bt, adr_type, mo, is_vol); } // If reference is volatile, prevent following volatiles ops from // floating up before the volatile write. if (is_vol) { - insert_mem_bar(Op_MemBarVolatile); // Use fat membar + // If not multiple copy atomic, we do the MemBarVolatile before the load. + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + insert_mem_bar(Op_MemBarVolatile); // Use fat membar + } + // Remember we wrote a volatile field. + // For not multiple copy atomic cpu (ppc64) a barrier should be issued + // in constructors which have such stores. See do_exits() in parse1.cpp. + if (is_field) { + set_wrote_volatile(true); + } } // If the field is final, the rules of Java say we are in or . @@ -337,7 +361,7 @@ // should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2) // An oop is not scavengable if it is in the perm gen. if (stable_type != NULL && con_type != NULL && con_type->isa_oopptr()) - con_type = con_type->join(stable_type); + con_type = con_type->join_speculative(stable_type); break; case T_ILLEGAL: @@ -414,7 +438,7 @@ Node* elem = expand_multianewarray(array_klass_1, &lengths[1], ndimensions-1, nargs); intptr_t offset = header + ((intptr_t)i << LogBytesPerHeapOop); Node* eaddr = basic_plus_adr(array, offset); - store_oop_to_array(control(), array, eaddr, adr_type, elem, elemtype, T_OBJECT); + store_oop_to_array(control(), array, eaddr, adr_type, elem, elemtype, T_OBJECT, MemNode::unordered); } } return array; @@ -503,7 +527,7 @@ // Fill-in it with values for (j = 0; j < ndimensions; j++) { Node *dims_elem = array_element_address(dims, intcon(j), T_INT); - store_to_memory(control(), dims_elem, length[j], T_INT, TypeAryPtr::INTS); + store_to_memory(control(), dims_elem, length[j], T_INT, TypeAryPtr::INTS, MemNode::unordered); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/parseHelper.cpp --- a/src/share/vm/opto/parseHelper.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/parseHelper.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -222,7 +222,7 @@ Node* init_thread_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_thread_offset())); Node* adr_node = basic_plus_adr(kls, kls, init_thread_offset); - Node* init_thread = make_load(NULL, adr_node, TypeRawPtr::BOTTOM, T_ADDRESS); + Node* init_thread = make_load(NULL, adr_node, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); Node *tst = Bool( CmpP( init_thread, cur_thread), BoolTest::eq); IfNode* iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN); set_control(IfTrue(iff)); @@ -232,7 +232,7 @@ adr_node = basic_plus_adr(kls, kls, init_state_offset); // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler // can generate code to load it as unsigned byte. - Node* init_state = make_load(NULL, adr_node, TypeInt::UBYTE, T_BOOLEAN); + Node* init_state = make_load(NULL, adr_node, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); Node* being_init = _gvn.intcon(InstanceKlass::being_initialized); tst = Bool( CmpI( init_state, being_init), BoolTest::eq); iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN); @@ -354,13 +354,13 @@ Node *counters_node = makecon(adr_type); Node* adr_iic_node = basic_plus_adr(counters_node, counters_node, MethodCounters::interpreter_invocation_counter_offset_in_bytes()); - Node* cnt = make_load(ctrl, adr_iic_node, TypeInt::INT, T_INT, adr_type); + Node* cnt = make_load(ctrl, adr_iic_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); test_counter_against_threshold(cnt, limit); // Add one to the counter and store Node* incr = _gvn.transform(new (C) AddINode(cnt, _gvn.intcon(1))); - store_to_memory( ctrl, adr_iic_node, incr, T_INT, adr_type ); + store_to_memory(ctrl, adr_iic_node, incr, T_INT, adr_type, MemNode::unordered); } //----------------------------method_data_addressing--------------------------- @@ -392,9 +392,9 @@ Node* adr_node = method_data_addressing(md, data, counter_offset, idx, stride); const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); - Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type); + Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); Node* incr = _gvn.transform(new (C) AddINode(cnt, _gvn.intcon(DataLayout::counter_increment))); - store_to_memory(NULL, adr_node, incr, T_INT, adr_type ); + store_to_memory(NULL, adr_node, incr, T_INT, adr_type, MemNode::unordered); } //--------------------------test_for_osr_md_counter_at------------------------- @@ -402,7 +402,7 @@ Node* adr_node = method_data_addressing(md, data, counter_offset); const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); - Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type); + Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered); test_counter_against_threshold(cnt, limit); } @@ -412,9 +412,9 @@ Node* adr_node = method_data_addressing(md, data, DataLayout::flags_offset()); const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr(); - Node* flags = make_load(NULL, adr_node, TypeInt::BYTE, T_BYTE, adr_type); + Node* flags = make_load(NULL, adr_node, TypeInt::BYTE, T_BYTE, adr_type, MemNode::unordered); Node* incr = _gvn.transform(new (C) OrINode(flags, _gvn.intcon(flag_constant))); - store_to_memory(NULL, adr_node, incr, T_BYTE, adr_type); + store_to_memory(NULL, adr_node, incr, T_BYTE, adr_type, MemNode::unordered); } //----------------------------profile_taken_branch----------------------------- diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/phase.cpp --- a/src/share/vm/opto/phase.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/phase.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -26,6 +26,7 @@ #include "code/nmethod.hpp" #include "compiler/compileBroker.hpp" #include "opto/compile.hpp" +#include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/phase.hpp" @@ -55,6 +56,7 @@ elapsedTimer Phase::_t_macroEliminate; elapsedTimer Phase::_t_macroExpand; elapsedTimer Phase::_t_peephole; +elapsedTimer Phase::_t_postalloc_expand; elapsedTimer Phase::_t_codeGeneration; elapsedTimer Phase::_t_registerMethod; elapsedTimer Phase::_t_temporaryTimer1; @@ -144,6 +146,9 @@ } tty->print_cr (" blockOrdering : %3.3f sec", Phase::_t_blockOrdering.seconds()); tty->print_cr (" peephole : %3.3f sec", Phase::_t_peephole.seconds()); + if (Matcher::require_postalloc_expand) { + tty->print_cr (" postalloc_expand: %3.3f sec", Phase::_t_postalloc_expand.seconds()); + } tty->print_cr (" codeGen : %3.3f sec", Phase::_t_codeGeneration.seconds()); tty->print_cr (" install_code : %3.3f sec", Phase::_t_registerMethod.seconds()); tty->print_cr (" -------------- : ----------"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/phase.hpp --- a/src/share/vm/opto/phase.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/phase.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -91,6 +91,7 @@ static elapsedTimer _t_macroEliminate; static elapsedTimer _t_macroExpand; static elapsedTimer _t_peephole; + static elapsedTimer _t_postalloc_expand; static elapsedTimer _t_codeGeneration; static elapsedTimer _t_registerMethod; static elapsedTimer _t_temporaryTimer1; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/phaseX.cpp --- a/src/share/vm/opto/phaseX.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/phaseX.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -323,6 +323,23 @@ } } + +void NodeHash::check_no_speculative_types() { +#ifdef ASSERT + uint max = size(); + Node *sentinel_node = sentinel(); + for (uint i = 0; i < max; ++i) { + Node *n = at(i); + if(n != NULL && n != sentinel_node && n->is_Type()) { + TypeNode* tn = n->as_Type(); + const Type* t = tn->type(); + const Type* t_no_spec = t->remove_speculative(); + assert(t == t_no_spec, "dead node in hash table or missed node during speculative cleanup"); + } + } +#endif +} + #ifndef PRODUCT //------------------------------dump------------------------------------------- // Dump statistics for the hash table @@ -985,10 +1002,10 @@ if ( VerifyIterativeGVN && PrintOpto ) { if ( _verify_counter == _verify_full_passes ) tty->print_cr("VerifyIterativeGVN: %d transforms and verify passes", - _verify_full_passes); + (int) _verify_full_passes); else tty->print_cr("VerifyIterativeGVN: %d transforms, %d full verify passes", - _verify_counter, _verify_full_passes); + (int) _verify_counter, (int) _verify_full_passes); } #endif } @@ -1362,6 +1379,15 @@ _worklist.push(u); } } + // If changed AddI/SubI inputs, check CmpU for range check optimization. + if (use_op == Op_AddI || use_op == Op_SubI) { + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->is_Cmp() && (u->Opcode() == Op_CmpU)) { + _worklist.push(u); + } + } + } // If changed AddP inputs, check Stores for loop invariant if( use_op == Op_AddP ) { for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { @@ -1392,11 +1418,11 @@ assert(UseTypeSpeculation, "speculation is off"); for (uint i = 0; i < _types.Size(); i++) { const Type* t = _types.fast_lookup(i); - if (t != NULL && t->isa_oopptr()) { - const TypeOopPtr* to = t->is_oopptr(); - _types.map(i, to->remove_speculative()); + if (t != NULL) { + _types.map(i, t->remove_speculative()); } } + _table.check_no_speculative_types(); } //============================================================================= diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/phaseX.hpp --- a/src/share/vm/opto/phaseX.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/phaseX.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -92,7 +92,8 @@ } void remove_useless_nodes(VectorSet &useful); // replace with sentinel - void replace_with(NodeHash* nh); + void replace_with(NodeHash* nh); + void check_no_speculative_types(); // Check no speculative part for type nodes in table Node *sentinel() { return _sentinel; } @@ -501,6 +502,9 @@ Deoptimization::DeoptReason reason); void remove_speculative_types(); + void check_no_speculative_types() { + _table.check_no_speculative_types(); + } #ifndef PRODUCT protected: diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/regalloc.cpp --- a/src/share/vm/opto/regalloc.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/regalloc.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/regmask.cpp --- a/src/share/vm/opto/regmask.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/regmask.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 */ @@ -113,7 +116,7 @@ case Special: st->print("r---"); break; case Bad: st->print("rBAD"); break; default: - if (r < _last_Mach_Reg) st->print(Matcher::regName[r]); + if (r < _last_Mach_Reg) st->print("%s", Matcher::regName[r]); else st->print("rS%d",r); break; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/regmask.hpp --- a/src/share/vm/opto/regmask.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/regmask.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/runtime.cpp --- a/src/share/vm/opto/runtime.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/runtime.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -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 @@ -793,11 +796,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 (CCallingConventionRequiresIntsAsLongs) { + // 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); @@ -814,12 +826,18 @@ const TypeFunc* OptoRuntime::aescrypt_block_Type() { // create input type (domain) int num_args = 3; + if (Matcher::pass_original_key_for_aes()) { + num_args = 4; + } int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; fields[argp++] = TypePtr::NOTNULL; // src fields[argp++] = TypePtr::NOTNULL; // dest fields[argp++] = TypePtr::NOTNULL; // k array + if (Matcher::pass_original_key_for_aes()) { + fields[argp++] = TypePtr::NOTNULL; // original k array + } assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); @@ -852,10 +870,13 @@ return TypeFunc::make(domain, range); } -// for cipherBlockChaining calls of aescrypt encrypt/decrypt, four pointers and a length, returning void +// for cipherBlockChaining calls of aescrypt encrypt/decrypt, four pointers and a length, returning int const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { // create input type (domain) int num_args = 5; + if (Matcher::pass_original_key_for_aes()) { + num_args = 6; + } int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; @@ -864,13 +885,16 @@ fields[argp++] = TypePtr::NOTNULL; // k array fields[argp++] = TypePtr::NOTNULL; // r array fields[argp++] = TypeInt::INT; // src len + if (Matcher::pass_original_key_for_aes()) { + fields[argp++] = TypePtr::NOTNULL; // original k array + } assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); - // no result type needed + // returning cipher len (int) fields = TypeTuple::fields(1); - fields[TypeFunc::Parms+0] = NULL; // void - const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); + fields[TypeFunc::Parms+0] = TypeInt::INT; + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms+1, fields); return TypeFunc::make(domain, range); } @@ -1035,7 +1059,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(); @@ -1078,7 +1102,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 @@ -1287,6 +1311,14 @@ tty->print_cr("%s", c->name()); blc->print_on(tty); } +#if INCLUDE_RTM_OPT + } else if (c->tag() == NamedCounter::RTMLockingCounter) { + RTMLockingCounters* rlc = ((RTMLockingNamedCounter*)c)->counters(); + if (rlc->nonzero()) { + tty->print_cr("%s", c->name()); + rlc->print_on(tty); + } +#endif } c = c->next(); } @@ -1326,6 +1358,8 @@ NamedCounter* c; if (tag == NamedCounter::BiasedLockingCounter) { c = new BiasedLockingNamedCounter(strdup(st.as_string())); + } else if (tag == NamedCounter::RTMLockingCounter) { + c = new RTMLockingNamedCounter(strdup(st.as_string())); } else { c = new NamedCounter(strdup(st.as_string()), tag); } @@ -1334,6 +1368,7 @@ // add counters so this is safe. NamedCounter* head; do { + c->set_next(NULL); head = _named_counters; c->set_next(head); } while (Atomic::cmpxchg_ptr(c, &_named_counters, head) != head); @@ -1360,7 +1395,7 @@ } else { tty->print(""); } - tty->print(" at " INTPTR_FORMAT, exception_pc); + tty->print(" at " INTPTR_FORMAT, p2i(exception_pc)); tty->print_cr("]"); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/runtime.hpp --- a/src/share/vm/opto/runtime.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/runtime.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -29,6 +29,7 @@ #include "opto/machnode.hpp" #include "opto/type.hpp" #include "runtime/biasedLocking.hpp" +#include "runtime/rtmLocking.hpp" #include "runtime/deoptimization.hpp" #include "runtime/vframe.hpp" @@ -61,7 +62,8 @@ NoTag, LockCounter, EliminatedLockCounter, - BiasedLockingCounter + BiasedLockingCounter, + RTMLockingCounter }; private: @@ -85,7 +87,7 @@ NamedCounter* next() const { return _next; } void set_next(NamedCounter* next) { - assert(_next == NULL, "already set"); + assert(_next == NULL || next == NULL, "already set"); _next = next; } @@ -102,6 +104,18 @@ BiasedLockingCounters* counters() { return &_counters; } }; + +class RTMLockingNamedCounter : public NamedCounter { + private: + RTMLockingCounters _counters; + + public: + RTMLockingNamedCounter(const char *n) : + NamedCounter(n, RTMLockingCounter), _counters() {} + + RTMLockingCounters* counters() { return &_counters; } +}; + typedef const TypeFunc*(*TypeFunc_generator)(); class OptoRuntime : public AllStatic { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/stringopts.cpp --- a/src/share/vm/opto/stringopts.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/stringopts.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1122,7 +1122,8 @@ return kit.make_load(NULL, kit.basic_plus_adr(klass_node, field->offset_in_bytes()), type, T_OBJECT, - C->get_alias_index(mirror_type->add_offset(field->offset_in_bytes()))); + C->get_alias_index(mirror_type->add_offset(field->offset_in_bytes())), + MemNode::unordered); } Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { @@ -1314,7 +1315,7 @@ Node* ch = __ AddI(r, __ intcon('0')); Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), - ch, T_CHAR, char_adr_idx); + ch, T_CHAR, char_adr_idx, MemNode::unordered); IfNode* iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), @@ -1356,7 +1357,7 @@ } else { Node* m1 = __ SubI(charPos, __ intcon(1)); Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), - sign, T_CHAR, char_adr_idx); + sign, T_CHAR, char_adr_idx, MemNode::unordered); final_merge->init_req(1, kit.control()); final_mem->init_req(1, st); @@ -1387,7 +1388,8 @@ ciTypeArray* value_array = t->const_oop()->as_type_array(); for (int e = 0; e < c; e++) { __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), - __ intcon(value_array->char_at(o + e)), T_CHAR, char_adr_idx); + __ intcon(value_array->char_at(o + e)), T_CHAR, char_adr_idx, + MemNode::unordered); start = __ AddI(start, __ intcon(1)); } } else { @@ -1607,7 +1609,7 @@ } case StringConcat::CharMode: { __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), - arg, T_CHAR, char_adr_idx); + arg, T_CHAR, char_adr_idx, MemNode::unordered); start = __ AddI(start, __ intcon(1)); break; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/subnode.cpp --- a/src/share/vm/opto/subnode.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/subnode.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -80,7 +80,7 @@ //------------------------------Value------------------------------------------ // A subtract node differences it's two inputs. -const Type *SubNode::Value( PhaseTransform *phase ) const { +const Type* SubNode::Value_common(PhaseTransform *phase) const { const Node* in1 = in(1); const Node* in2 = in(2); // Either input is TOP ==> the result is TOP @@ -97,6 +97,16 @@ if( t1 == Type::BOTTOM || t2 == Type::BOTTOM ) return bottom_type(); + return NULL; +} + +const Type* SubNode::Value(PhaseTransform *phase) const { + const Type* t = Value_common(phase); + if (t != NULL) { + return t; + } + const Type* t1 = phase->type(in(1)); + const Type* t2 = phase->type(in(2)); return sub(t1,t2); // Local flavor of type subtraction } @@ -570,6 +580,81 @@ return TypeInt::CC; // else use worst case results } +const Type* CmpUNode::Value(PhaseTransform *phase) const { + const Type* t = SubNode::Value_common(phase); + if (t != NULL) { + return t; + } + const Node* in1 = in(1); + const Node* in2 = in(2); + const Type* t1 = phase->type(in1); + const Type* t2 = phase->type(in2); + assert(t1->isa_int(), "CmpU has only Int type inputs"); + if (t2 == TypeInt::INT) { // Compare to bottom? + return bottom_type(); + } + uint in1_op = in1->Opcode(); + if (in1_op == Op_AddI || in1_op == Op_SubI) { + // The problem rise when result of AddI(SubI) may overflow + // signed integer value. Let say the input type is + // [256, maxint] then +128 will create 2 ranges due to + // overflow: [minint, minint+127] and [384, maxint]. + // But C2 type system keep only 1 type range and as result + // it use general [minint, maxint] for this case which we + // can't optimize. + // + // Make 2 separate type ranges based on types of AddI(SubI) inputs + // and compare results of their compare. If results are the same + // CmpU node can be optimized. + const Node* in11 = in1->in(1); + const Node* in12 = in1->in(2); + const Type* t11 = (in11 == in1) ? Type::TOP : phase->type(in11); + const Type* t12 = (in12 == in1) ? Type::TOP : phase->type(in12); + // Skip cases when input types are top or bottom. + if ((t11 != Type::TOP) && (t11 != TypeInt::INT) && + (t12 != Type::TOP) && (t12 != TypeInt::INT)) { + const TypeInt *r0 = t11->is_int(); + const TypeInt *r1 = t12->is_int(); + jlong lo_r0 = r0->_lo; + jlong hi_r0 = r0->_hi; + jlong lo_r1 = r1->_lo; + jlong hi_r1 = r1->_hi; + if (in1_op == Op_SubI) { + jlong tmp = hi_r1; + hi_r1 = -lo_r1; + lo_r1 = -tmp; + // Note, for substructing [minint,x] type range + // long arithmetic provides correct overflow answer. + // The confusion come from the fact that in 32-bit + // -minint == minint but in 64-bit -minint == maxint+1. + } + jlong lo_long = lo_r0 + lo_r1; + jlong hi_long = hi_r0 + hi_r1; + int lo_tr1 = min_jint; + int hi_tr1 = (int)hi_long; + int lo_tr2 = (int)lo_long; + int hi_tr2 = max_jint; + bool underflow = lo_long != (jlong)lo_tr2; + bool overflow = hi_long != (jlong)hi_tr1; + // Use sub(t1, t2) when there is no overflow (one type range) + // or when both overflow and underflow (too complex). + if ((underflow != overflow) && (hi_tr1 < lo_tr2)) { + // Overflow only on one boundary, compare 2 separate type ranges. + int w = MAX2(r0->_widen, r1->_widen); // _widen does not matter here + const TypeInt* tr1 = TypeInt::make(lo_tr1, hi_tr1, w); + const TypeInt* tr2 = TypeInt::make(lo_tr2, hi_tr2, w); + const Type* cmp1 = sub(tr1, t2); + const Type* cmp2 = sub(tr2, t2); + if (cmp1 == cmp2) { + return cmp1; // Hit! + } + } + } + } + + return sub(t1, t2); // Local flavor of type subtraction +} + bool CmpUNode::is_index_range_check() const { // Check for the "(X ModI Y) CmpU Y" shape return (in(1)->Opcode() == Op_ModI && @@ -1065,7 +1150,7 @@ #ifndef PRODUCT void BoolTest::dump_on(outputStream *st) const { const char *msg[] = {"eq","gt","of","lt","ne","le","nof","ge"}; - st->print(msg[_test]); + st->print("%s", msg[_test]); } #endif @@ -1126,11 +1211,15 @@ Node *cmp = in(1); if( !cmp->is_Sub() ) return NULL; int cop = cmp->Opcode(); - if( cop == Op_FastLock || cop == Op_FastUnlock || cop == Op_FlagsProj) return NULL; + if( cop == Op_FastLock || cop == Op_FastUnlock) return NULL; Node *cmp1 = cmp->in(1); Node *cmp2 = cmp->in(2); if( !cmp1 ) return NULL; + if (_test._test == BoolTest::overflow || _test._test == BoolTest::no_overflow) { + return NULL; + } + // Constant on left? Node *con = cmp1; uint op2 = cmp2->Opcode(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/subnode.hpp --- a/src/share/vm/opto/subnode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/subnode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -50,6 +50,7 @@ // Compute a new Type for this node. Basically we just do the pre-check, // then call the virtual add() to set the type. virtual const Type *Value( PhaseTransform *phase ) const; + const Type* Value_common( PhaseTransform *phase ) const; // Supplied function returns the subtractend of the inputs. // This also type-checks the inputs for sanity. Guaranteed never to @@ -158,6 +159,7 @@ CmpUNode( Node *in1, Node *in2 ) : CmpNode(in1,in2) {} virtual int Opcode() const; virtual const Type *sub( const Type *, const Type * ) const; + const Type *Value( PhaseTransform *phase ) const; bool is_index_range_check() const; }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/superword.cpp --- a/src/share/vm/opto/superword.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/superword.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1262,8 +1262,9 @@ memops.clear(); for (DUIterator i = upper_insert_pt->outs(); upper_insert_pt->has_out(i); i++) { Node* use = upper_insert_pt->out(i); - if (!use->is_Store()) + if (use->is_Mem() && !use->is_Store()) { memops.push(use); + } } MemNode* lower_insert_pt = last; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/type.cpp --- a/src/share/vm/opto/type.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/type.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -41,6 +41,8 @@ #include "opto/opcodes.hpp" #include "opto/type.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Portions of code courtesy of Clifford Click // Optimization - Graph Style @@ -61,17 +63,22 @@ { Bad, T_ILLEGAL, "tuple:", false, Node::NotAMachineReg, relocInfo::none }, // Tuple { Bad, T_ARRAY, "array:", false, Node::NotAMachineReg, relocInfo::none }, // Array -#ifndef SPARC +#ifdef SPARC + { Bad, T_ILLEGAL, "vectors:", false, 0, relocInfo::none }, // VectorS + { Bad, T_ILLEGAL, "vectord:", false, Op_RegD, relocInfo::none }, // VectorD + { Bad, T_ILLEGAL, "vectorx:", false, 0, relocInfo::none }, // VectorX + { Bad, T_ILLEGAL, "vectory:", false, 0, relocInfo::none }, // VectorY +#elif defined(PPC64) + { Bad, T_ILLEGAL, "vectors:", false, 0, relocInfo::none }, // VectorS + { Bad, T_ILLEGAL, "vectord:", false, Op_RegL, relocInfo::none }, // VectorD + { Bad, T_ILLEGAL, "vectorx:", false, 0, relocInfo::none }, // VectorX + { Bad, T_ILLEGAL, "vectory:", false, 0, relocInfo::none }, // VectorY +#else // all other { Bad, T_ILLEGAL, "vectors:", false, Op_VecS, relocInfo::none }, // VectorS { Bad, T_ILLEGAL, "vectord:", false, Op_VecD, relocInfo::none }, // VectorD { Bad, T_ILLEGAL, "vectorx:", false, Op_VecX, relocInfo::none }, // VectorX { Bad, T_ILLEGAL, "vectory:", false, Op_VecY, relocInfo::none }, // VectorY -#else - { Bad, T_ILLEGAL, "vectors:", false, 0, relocInfo::none }, // VectorS - { Bad, T_ILLEGAL, "vectord:", false, Op_RegD, relocInfo::none }, // VectorD - { Bad, T_ILLEGAL, "vectorx:", false, 0, relocInfo::none }, // VectorX - { Bad, T_ILLEGAL, "vectory:", false, 0, relocInfo::none }, // VectorY -#endif // IA32 || AMD64 +#endif { Bad, T_ADDRESS, "anyptr:", false, Op_RegP, relocInfo::none }, // AnyPtr { Bad, T_ADDRESS, "rawptr:", false, Op_RegP, relocInfo::none }, // RawPtr { Bad, T_OBJECT, "oop:", true, Op_RegP, relocInfo::oop_type }, // OopPtr @@ -236,6 +243,13 @@ return !t1->eq(t2); // Return ZERO if equal } +const Type* Type::maybe_remove_speculative(bool include_speculative) const { + if (!include_speculative) { + return remove_speculative(); + } + return this; +} + //------------------------------hash------------------------------------------- int Type::uhash( const Type *const t ) { return t->hash(); @@ -294,6 +308,7 @@ TypeInt::POS1 = TypeInt::make(1,max_jint, WidenMin); // Positive values TypeInt::INT = TypeInt::make(min_jint,max_jint, WidenMax); // 32-bit integers TypeInt::SYMINT = TypeInt::make(-max_jint,max_jint,WidenMin); // symmetric range + TypeInt::TYPE_DOMAIN = TypeInt::INT; // CmpL is overloaded both as the bytecode computation returning // a trinary (-1,0,+1) integer result AND as an efficient long // compare returning optimizer ideal-type flags. @@ -310,6 +325,7 @@ TypeLong::LONG = TypeLong::make(min_jlong,max_jlong,WidenMax); // 64-bit integers TypeLong::INT = TypeLong::make((jlong)min_jint,(jlong)max_jint,WidenMin); TypeLong::UINT = TypeLong::make(0,(jlong)max_juint,WidenMin); + TypeLong::TYPE_DOMAIN = TypeLong::LONG; const Type **fboth =(const Type**)shared_type_arena->Amalloc_4(2*sizeof(Type*)); fboth[0] = Type::CONTROL; @@ -628,41 +644,44 @@ //------------------------------meet------------------------------------------- // Compute the MEET of two types. NOT virtual. It enforces that meet is // commutative and the lattice is symmetric. -const Type *Type::meet( const Type *t ) const { +const Type *Type::meet_helper(const Type *t, bool include_speculative) const { if (isa_narrowoop() && t->isa_narrowoop()) { - const Type* result = make_ptr()->meet(t->make_ptr()); + const Type* result = make_ptr()->meet_helper(t->make_ptr(), include_speculative); return result->make_narrowoop(); } if (isa_narrowklass() && t->isa_narrowklass()) { - const Type* result = make_ptr()->meet(t->make_ptr()); + const Type* result = make_ptr()->meet_helper(t->make_ptr(), include_speculative); return result->make_narrowklass(); } - const Type *mt = xmeet(t); + const Type *this_t = maybe_remove_speculative(include_speculative); + t = t->maybe_remove_speculative(include_speculative); + + const Type *mt = this_t->xmeet(t); if (isa_narrowoop() || t->isa_narrowoop()) return mt; if (isa_narrowklass() || t->isa_narrowklass()) return mt; #ifdef ASSERT - assert( mt == t->xmeet(this), "meet not commutative" ); + assert(mt == t->xmeet(this_t), "meet not commutative"); const Type* dual_join = mt->_dual; const Type *t2t = dual_join->xmeet(t->_dual); - const Type *t2this = dual_join->xmeet( _dual); + const Type *t2this = dual_join->xmeet(this_t->_dual); // Interface meet Oop is Not Symmetric: // Interface:AnyNull meet Oop:AnyNull == Interface:AnyNull // Interface:NotNull meet Oop:NotNull == java/lang/Object:NotNull - if( !interface_vs_oop(t) && (t2t != t->_dual || t2this != _dual) ) { + if( !interface_vs_oop(t) && (t2t != t->_dual || t2this != this_t->_dual) ) { tty->print_cr("=== Meet Not Symmetric ==="); - tty->print("t = "); t->dump(); tty->cr(); - tty->print("this= "); dump(); tty->cr(); - tty->print("mt=(t meet this)= "); mt->dump(); tty->cr(); - - tty->print("t_dual= "); t->_dual->dump(); tty->cr(); - tty->print("this_dual= "); _dual->dump(); tty->cr(); - tty->print("mt_dual= "); mt->_dual->dump(); tty->cr(); - - tty->print("mt_dual meet t_dual= "); t2t ->dump(); tty->cr(); - tty->print("mt_dual meet this_dual= "); t2this ->dump(); tty->cr(); + tty->print("t = "); t->dump(); tty->cr(); + tty->print("this= "); this_t->dump(); tty->cr(); + tty->print("mt=(t meet this)= "); mt->dump(); tty->cr(); + + tty->print("t_dual= "); t->_dual->dump(); tty->cr(); + tty->print("this_dual= "); this_t->_dual->dump(); tty->cr(); + tty->print("mt_dual= "); mt->_dual->dump(); tty->cr(); + + tty->print("mt_dual meet t_dual= "); t2t ->dump(); tty->cr(); + tty->print("mt_dual meet this_dual= "); t2this ->dump(); tty->cr(); fatal("meet not symmetric" ); } @@ -754,8 +773,8 @@ } //-----------------------------filter------------------------------------------ -const Type *Type::filter( const Type *kills ) const { - const Type* ft = join(kills); +const Type *Type::filter_helper(const Type *kills, bool include_speculative) const { + const Type* ft = join_helper(kills, include_speculative); if (ft->empty()) return Type::TOP; // Canonical empty value return ft; @@ -825,7 +844,7 @@ #ifndef PRODUCT //------------------------------dump2------------------------------------------ void Type::dump2( Dict &d, uint depth, outputStream *st ) const { - st->print(_type_info[_base].msg); + st->print("%s", _type_info[_base].msg); } //------------------------------dump------------------------------------------- @@ -1146,6 +1165,7 @@ const TypeInt *TypeInt::POS1; // Positive 32-bit integers const TypeInt *TypeInt::INT; // 32-bit integers const TypeInt *TypeInt::SYMINT; // symmetric range [-max_jint..max_jint] +const TypeInt *TypeInt::TYPE_DOMAIN; // alias for TypeInt::INT //------------------------------TypeInt---------------------------------------- TypeInt::TypeInt( jint lo, jint hi, int w ) : Type(Int), _lo(lo), _hi(hi), _widen(w) { @@ -1309,8 +1329,8 @@ } //-----------------------------filter------------------------------------------ -const Type *TypeInt::filter( const Type *kills ) const { - const TypeInt* ft = join(kills)->isa_int(); +const Type *TypeInt::filter_helper(const Type *kills, bool include_speculative) const { + const TypeInt* ft = join_helper(kills, include_speculative)->isa_int(); if (ft == NULL || ft->empty()) return Type::TOP; // Canonical empty value if (ft->_widen < this->_widen) { @@ -1403,6 +1423,7 @@ const TypeLong *TypeLong::LONG; // 64-bit integers const TypeLong *TypeLong::INT; // 32-bit subrange const TypeLong *TypeLong::UINT; // 32-bit unsigned subrange +const TypeLong *TypeLong::TYPE_DOMAIN; // alias for TypeLong::LONG //------------------------------TypeLong--------------------------------------- TypeLong::TypeLong( jlong lo, jlong hi, int w ) : Type(Long), _lo(lo), _hi(hi), _widen(w) { @@ -1570,8 +1591,8 @@ } //-----------------------------filter------------------------------------------ -const Type *TypeLong::filter( const Type *kills ) const { - const TypeLong* ft = join(kills)->isa_long(); +const Type *TypeLong::filter_helper(const Type *kills, bool include_speculative) const { + const TypeLong* ft = join_helper(kills, include_speculative)->isa_long(); if (ft == NULL || ft->empty()) return Type::TOP; // Canonical empty value if (ft->_widen < this->_widen) { @@ -1726,7 +1747,7 @@ total_fields++; field_array = fields(total_fields); // Use get_const_type here because it respects UseUniqueSubclasses: - field_array[pos++] = get_const_type(recv)->join(TypePtr::NOTNULL); + field_array[pos++] = get_const_type(recv)->join_speculative(TypePtr::NOTNULL); } else { field_array = fields(total_fields); } @@ -1916,7 +1937,7 @@ case Array: { // Meeting 2 arrays? const TypeAry *a = t->is_ary(); - return TypeAry::make(_elem->meet(a->_elem), + return TypeAry::make(_elem->meet_speculative(a->_elem), _size->xmeet(a->_size)->is_int(), _stable & a->_stable); } @@ -1949,6 +1970,13 @@ return (intptr_t)_elem + (intptr_t)_size + (_stable ? 43 : 0); } +/** + * Return same type without a speculative part in the element + */ +const Type* TypeAry::remove_speculative() const { + return make(_elem->remove_speculative(), _size, _stable); +} + //----------------------interface_vs_oop--------------------------------------- #ifdef ASSERT bool TypeAry::interface_vs_oop(const Type *t) const { @@ -2035,6 +2063,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(); @@ -2436,7 +2465,7 @@ const TypeOopPtr *TypeOopPtr::BOTTOM; //------------------------------TypeOopPtr------------------------------------- -TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative) +TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative, int inline_depth) : TypePtr(t, ptr, offset), _const_oop(o), _klass(k), _klass_is_exact(xk), @@ -2444,7 +2473,8 @@ _is_ptr_to_narrowklass(false), _is_ptr_to_boxed_value(false), _instance_id(instance_id), - _speculative(speculative) { + _speculative(speculative), + _inline_depth(inline_depth){ if (Compile::current()->eliminate_boxing() && (t == InstPtr) && (offset > 0) && xk && (k != 0) && k->is_instance_klass()) { _is_ptr_to_boxed_value = k->as_instance_klass()->is_boxed_value_offset(offset); @@ -2511,12 +2541,12 @@ //------------------------------make------------------------------------------- const TypeOopPtr *TypeOopPtr::make(PTR ptr, - int offset, int instance_id, const TypeOopPtr* speculative) { + int offset, int instance_id, const TypeOopPtr* speculative, int inline_depth) { assert(ptr != Constant, "no constant generic pointers"); ciKlass* k = Compile::current()->env()->Object_klass(); bool xk = false; ciObject* o = NULL; - return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, instance_id, speculative))->hashcons(); + return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, instance_id, speculative, inline_depth))->hashcons(); } @@ -2524,7 +2554,7 @@ const Type *TypeOopPtr::cast_to_ptr_type(PTR ptr) const { assert(_base == OopPtr, "subclass must override cast_to_ptr_type"); if( ptr == _ptr ) return this; - return make(ptr, _offset, _instance_id, _speculative); + return make(ptr, _offset, _instance_id, _speculative, _inline_depth); } //-----------------------------cast_to_instance_id---------------------------- @@ -2560,14 +2590,14 @@ return res; } - if (res->isa_oopptr() != NULL) { + const TypeOopPtr* res_oopptr = res->is_oopptr(); + if (res_oopptr->speculative() != NULL) { // type->speculative() == NULL means that speculation is no better // than type, i.e. type->speculative() == type. So there are 2 // ways to represent the fact that we have no useful speculative // data and we should use a single one to be able to test for // equality between types. Check whether type->speculative() == // type and set speculative to NULL if it is the case. - const TypeOopPtr* res_oopptr = res->is_oopptr(); if (res_oopptr->remove_speculative() == res_oopptr->speculative()) { return res_oopptr->remove_speculative(); } @@ -2621,7 +2651,7 @@ case AnyNull: { int instance_id = meet_instance_id(InstanceTop); const TypeOopPtr* speculative = _speculative; - return make(ptr, offset, instance_id, speculative); + return make(ptr, offset, instance_id, speculative, _inline_depth); } case BotPTR: case NotNull: @@ -2633,8 +2663,9 @@ case OopPtr: { // Meeting to other OopPtrs const TypeOopPtr *tp = t->is_oopptr(); int instance_id = meet_instance_id(tp->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tp); - return make(meet_ptr(tp->ptr()), meet_offset(tp->offset()), instance_id, speculative); + const TypeOopPtr* speculative = xmeet_speculative(tp); + int depth = meet_inline_depth(tp->inline_depth()); + return make(meet_ptr(tp->ptr()), meet_offset(tp->offset()), instance_id, speculative, depth); } case InstPtr: // For these, flip the call around to cut down @@ -2651,7 +2682,7 @@ const Type *TypeOopPtr::xdual() const { assert(klass() == Compile::current()->env()->Object_klass(), "no klasses here"); assert(const_oop() == NULL, "no constants here"); - return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative()); + return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative(), dual_inline_depth()); } //--------------------------make_from_klass_common----------------------------- @@ -2742,7 +2773,7 @@ } else if (!o->should_be_constant()) { return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0); } - const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0, InstanceBot, NULL, is_autobox_cache); + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0, InstanceBot, NULL, InlineDepthBottom, is_autobox_cache); return arr; } else if (klass->is_type_array_klass()) { // Element is an typeArray @@ -2787,9 +2818,9 @@ //-----------------------------filter------------------------------------------ // Do not allow interface-vs.-noninterface joins to collapse to top. -const Type *TypeOopPtr::filter(const Type *kills) const { - - const Type* ft = join(kills); +const Type *TypeOopPtr::filter_helper(const Type *kills, bool include_speculative) const { + + const Type* ft = join_helper(kills, include_speculative); const TypeInstPtr* ftip = ft->isa_instptr(); const TypeInstPtr* ktip = kills->isa_instptr(); @@ -2831,7 +2862,8 @@ const TypeOopPtr *a = (const TypeOopPtr*)t; if (_klass_is_exact != a->_klass_is_exact || _instance_id != a->_instance_id || - !eq_speculative(a)) return false; + !eq_speculative(a) || + _inline_depth != a->_inline_depth) return false; ciObject* one = const_oop(); ciObject* two = a->const_oop(); if (one == NULL || two == NULL) { @@ -2849,6 +2881,7 @@ _klass_is_exact + _instance_id + hash_speculative() + + _inline_depth + TypePtr::hash(); } @@ -2869,6 +2902,7 @@ else if (_instance_id != InstanceBot) st->print(",iid=%d",_instance_id); + dump_inline_depth(st); dump_speculative(st); } @@ -2882,6 +2916,16 @@ st->print(")"); } } + +void TypeOopPtr::dump_inline_depth(outputStream *st) const { + if (_inline_depth != InlineDepthBottom) { + if (_inline_depth == InlineDepthTop) { + st->print(" (inline_depth=InlineDepthTop)"); + } else { + st->print(" (inline_depth=%d)", _inline_depth); + } + } +} #endif //------------------------------singleton-------------------------------------- @@ -2895,14 +2939,62 @@ //------------------------------add_offset------------------------------------- const TypePtr *TypeOopPtr::add_offset(intptr_t offset) const { - return make(_ptr, xadd_offset(offset), _instance_id, add_offset_speculative(offset)); + return make(_ptr, xadd_offset(offset), _instance_id, add_offset_speculative(offset), _inline_depth); } /** * Return same type without a speculative part */ -const TypeOopPtr* TypeOopPtr::remove_speculative() const { - return make(_ptr, _offset, _instance_id, NULL); +const Type* TypeOopPtr::remove_speculative() const { + if (_speculative == NULL) { + return this; + } + assert(_inline_depth == InlineDepthTop || _inline_depth == InlineDepthBottom, "non speculative type shouldn't have inline depth"); + return make(_ptr, _offset, _instance_id, NULL, _inline_depth); +} + +/** + * Return same type but with a different inline depth (used for speculation) + * + * @param depth depth to meet with + */ +const TypeOopPtr* TypeOopPtr::with_inline_depth(int depth) const { + if (!UseInlineDepthForSpeculativeTypes) { + return this; + } + return make(_ptr, _offset, _instance_id, _speculative, depth); +} + +/** + * Check whether new profiling would improve speculative type + * + * @param exact_kls class from profiling + * @param inline_depth inlining depth of profile point + * + * @return true if type profile is valuable + */ +bool TypeOopPtr::would_improve_type(ciKlass* exact_kls, int inline_depth) const { + // no way to improve an already exact type + if (klass_is_exact()) { + return false; + } + // no profiling? + if (exact_kls == NULL) { + return false; + } + // no speculative type or non exact speculative type? + if (speculative_type() == NULL) { + return true; + } + // If the node already has an exact speculative type keep it, + // unless it was provided by profiling that is at a deeper + // inlining level. Profiling at a higher inlining depth is + // expected to be less accurate. + if (_speculative->inline_depth() == InlineDepthBottom) { + return false; + } + assert(_speculative->inline_depth() != InlineDepthTop, "can't do the comparison"); + return inline_depth < _speculative->inline_depth(); } //------------------------------meet_instance_id-------------------------------- @@ -2927,7 +3019,7 @@ * * @param other type to meet with */ -const TypeOopPtr* TypeOopPtr::meet_speculative(const TypeOopPtr* other) const { +const TypeOopPtr* TypeOopPtr::xmeet_speculative(const TypeOopPtr* other) const { bool this_has_spec = (_speculative != NULL); bool other_has_spec = (other->speculative() != NULL); @@ -2952,7 +3044,7 @@ other_spec = other; } - return this_spec->meet(other_spec)->is_oopptr(); + return this_spec->meet_speculative(other_spec)->is_oopptr(); } /** @@ -3005,6 +3097,21 @@ return _speculative->hash(); } +/** + * dual of the inline depth for this type (used for speculation) + */ +int TypeOopPtr::dual_inline_depth() const { + return -inline_depth(); +} + +/** + * meet of 2 inline depth (used for speculation) + * + * @param depth depth to meet with + */ +int TypeOopPtr::meet_inline_depth(int depth) const { + return MAX2(inline_depth(), depth); +} //============================================================================= // Convenience common pre-built types. @@ -3015,8 +3122,8 @@ const TypeInstPtr *TypeInstPtr::KLASS; //------------------------------TypeInstPtr------------------------------------- -TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int off, int instance_id, const TypeOopPtr* speculative) - : TypeOopPtr(InstPtr, ptr, k, xk, o, off, instance_id, speculative), _name(k->name()) { +TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int off, int instance_id, const TypeOopPtr* speculative, int inline_depth) + : TypeOopPtr(InstPtr, ptr, k, xk, o, off, instance_id, speculative, inline_depth), _name(k->name()) { assert(k != NULL && (k->is_loaded() || o == NULL), "cannot have constants with non-loaded klass"); @@ -3029,7 +3136,8 @@ ciObject* o, int offset, int instance_id, - const TypeOopPtr* speculative) { + const TypeOopPtr* speculative, + int inline_depth) { assert( !k->is_loaded() || k->is_instance_klass(), "Must be for instance"); // Either const_oop() is NULL or else ptr is Constant assert( (!o && ptr != Constant) || (o && ptr == Constant), @@ -3050,7 +3158,7 @@ // Now hash this baby TypeInstPtr *result = - (TypeInstPtr*)(new TypeInstPtr(ptr, k, xk, o ,offset, instance_id, speculative))->hashcons(); + (TypeInstPtr*)(new TypeInstPtr(ptr, k, xk, o ,offset, instance_id, speculative, inline_depth))->hashcons(); return result; } @@ -3083,7 +3191,7 @@ if( ptr == _ptr ) return this; // Reconstruct _sig info here since not a problem with later lazy // construction, _sig will show up on demand. - return make(ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, _speculative); + return make(ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, _speculative, _inline_depth); } @@ -3095,13 +3203,13 @@ ciInstanceKlass* ik = _klass->as_instance_klass(); if( (ik->is_final() || _const_oop) ) return this; // cannot clear xk if( ik->is_interface() ) return this; // cannot set xk - return make(ptr(), klass(), klass_is_exact, const_oop(), _offset, _instance_id, _speculative); + return make(ptr(), klass(), klass_is_exact, const_oop(), _offset, _instance_id, _speculative, _inline_depth); } //-----------------------------cast_to_instance_id---------------------------- const TypeOopPtr *TypeInstPtr::cast_to_instance_id(int instance_id) const { if( instance_id == _instance_id ) return this; - return make(_ptr, klass(), _klass_is_exact, const_oop(), _offset, instance_id, _speculative); + return make(_ptr, klass(), _klass_is_exact, const_oop(), _offset, instance_id, _speculative, _inline_depth); } //------------------------------xmeet_unloaded--------------------------------- @@ -3111,7 +3219,8 @@ int off = meet_offset(tinst->offset()); PTR ptr = meet_ptr(tinst->ptr()); int instance_id = meet_instance_id(tinst->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tinst); + const TypeOopPtr* speculative = xmeet_speculative(tinst); + int depth = meet_inline_depth(tinst->inline_depth()); const TypeInstPtr *loaded = is_loaded() ? this : tinst; const TypeInstPtr *unloaded = is_loaded() ? tinst : this; @@ -3132,7 +3241,7 @@ assert(loaded->ptr() != TypePtr::Null, "insanity check"); // if( loaded->ptr() == TypePtr::TopPTR ) { return unloaded; } - else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make(ptr, unloaded->klass(), false, NULL, off, instance_id, speculative); } + else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make(ptr, unloaded->klass(), false, NULL, off, instance_id, speculative, depth); } else if (loaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } else if (loaded->ptr() == TypePtr::Constant || loaded->ptr() == TypePtr::NotNull) { if (unloaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } @@ -3188,7 +3297,8 @@ int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); int instance_id = meet_instance_id(tp->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tp); + const TypeOopPtr* speculative = xmeet_speculative(tp); + int depth = meet_inline_depth(tp->inline_depth()); switch (ptr) { case TopPTR: case AnyNull: // Fall 'down' to dual of object klass @@ -3196,12 +3306,12 @@ // below the centerline when the superclass is exact. We need to // do the same here. if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) { - return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id, speculative); + return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id, speculative, depth); } else { // cannot subclass, so the meet has to fall badly below the centerline ptr = NotNull; instance_id = InstanceBot; - return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative); + return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative, depth); } case Constant: case NotNull: @@ -3216,7 +3326,7 @@ if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) { // that is, tp's array type is a subtype of my klass return TypeAryPtr::make(ptr, (ptr == Constant ? tp->const_oop() : NULL), - tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id, speculative); + tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id, speculative, depth); } } // The other case cannot happen, since I cannot be a subtype of an array. @@ -3224,7 +3334,7 @@ if( ptr == Constant ) ptr = NotNull; instance_id = InstanceBot; - return make(ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative); + return make(ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative, depth); default: typerr(t); } } @@ -3238,15 +3348,17 @@ case TopPTR: case AnyNull: { int instance_id = meet_instance_id(InstanceTop); - const TypeOopPtr* speculative = meet_speculative(tp); + const TypeOopPtr* speculative = xmeet_speculative(tp); + int depth = meet_inline_depth(tp->inline_depth()); return make(ptr, klass(), klass_is_exact(), - (ptr == Constant ? const_oop() : NULL), offset, instance_id, speculative); + (ptr == Constant ? const_oop() : NULL), offset, instance_id, speculative, depth); } case NotNull: case BotPTR: { int instance_id = meet_instance_id(tp->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tp); - return TypeOopPtr::make(ptr, offset, instance_id, speculative); + const TypeOopPtr* speculative = xmeet_speculative(tp); + int depth = meet_inline_depth(tp->inline_depth()); + return TypeOopPtr::make(ptr, offset, instance_id, speculative, depth); } default: typerr(t); } @@ -3266,7 +3378,7 @@ int instance_id = meet_instance_id(InstanceTop); const TypeOopPtr* speculative = _speculative; return make(ptr, klass(), klass_is_exact(), - (ptr == Constant ? const_oop() : NULL), offset, instance_id, speculative); + (ptr == Constant ? const_oop() : NULL), offset, instance_id, speculative, _inline_depth); } case NotNull: case BotPTR: @@ -3297,14 +3409,15 @@ int off = meet_offset( tinst->offset() ); PTR ptr = meet_ptr( tinst->ptr() ); int instance_id = meet_instance_id(tinst->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tinst); + const TypeOopPtr* speculative = xmeet_speculative(tinst); + int depth = meet_inline_depth(tinst->inline_depth()); // Check for easy case; klasses are equal (and perhaps not loaded!) // If we have constants, then we created oops so classes are loaded // and we can handle the constants further down. This case handles // both-not-loaded or both-loaded classes if (ptr != Constant && klass()->equals(tinst->klass()) && klass_is_exact() == tinst->klass_is_exact()) { - return make(ptr, klass(), klass_is_exact(), NULL, off, instance_id, speculative); + return make(ptr, klass(), klass_is_exact(), NULL, off, instance_id, speculative, depth); } // Classes require inspection in the Java klass hierarchy. Must be loaded. @@ -3368,7 +3481,7 @@ // Find out which constant. o = (this_klass == klass()) ? const_oop() : tinst->const_oop(); } - return make(ptr, k, xk, o, off, instance_id, speculative); + return make(ptr, k, xk, o, off, instance_id, speculative, depth); } // Either oop vs oop or interface vs interface or interface vs Object @@ -3445,7 +3558,7 @@ else ptr = NotNull; } - return make(ptr, this_klass, this_xk, o, off, instance_id, speculative); + return make(ptr, this_klass, this_xk, o, off, instance_id, speculative, depth); } // Else classes are not equal // Since klasses are different, we require a LCA in the Java @@ -3456,7 +3569,7 @@ // Now we find the LCA of Java classes ciKlass* k = this_klass->least_common_ancestor(tinst_klass); - return make(ptr, k, false, NULL, off, instance_id, speculative); + return make(ptr, k, false, NULL, off, instance_id, speculative, depth); } // End of case InstPtr } // End of switch @@ -3480,7 +3593,7 @@ // Dual: do NOT dual on klasses. This means I do NOT understand the Java // inheritance mechanism. const Type *TypeInstPtr::xdual() const { - return new TypeInstPtr(dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative()); + return new TypeInstPtr(dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative(), dual_inline_depth()); } //------------------------------eq--------------------------------------------- @@ -3537,6 +3650,7 @@ else if (_instance_id != InstanceBot) st->print(",iid=%d",_instance_id); + dump_inline_depth(st); dump_speculative(st); } #endif @@ -3546,8 +3660,19 @@ return make(_ptr, klass(), klass_is_exact(), const_oop(), xadd_offset(offset), _instance_id, add_offset_speculative(offset)); } -const TypeOopPtr *TypeInstPtr::remove_speculative() const { - return make(_ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, NULL); +const Type *TypeInstPtr::remove_speculative() const { + if (_speculative == NULL) { + return this; + } + assert(_inline_depth == InlineDepthTop || _inline_depth == InlineDepthBottom, "non speculative type shouldn't have inline depth"); + return make(_ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, NULL, _inline_depth); +} + +const TypeOopPtr *TypeInstPtr::with_inline_depth(int depth) const { + if (!UseInlineDepthForSpeculativeTypes) { + return this; + } + return make(_ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, _speculative, depth); } //============================================================================= @@ -3564,30 +3689,30 @@ const TypeAryPtr *TypeAryPtr::DOUBLES; //------------------------------make------------------------------------------- -const TypeAryPtr *TypeAryPtr::make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, const TypeOopPtr* speculative) { +const TypeAryPtr *TypeAryPtr::make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, const TypeOopPtr* speculative, int inline_depth) { assert(!(k == NULL && ary->_elem->isa_int()), "integral arrays must be pre-equipped with a class"); if (!xk) xk = ary->ary_must_be_exact(); assert(instance_id <= 0 || xk || !UseExactTypes, "instances are always exactly typed"); if (!UseExactTypes) xk = (ptr == Constant); - return (TypeAryPtr*)(new TypeAryPtr(ptr, NULL, ary, k, xk, offset, instance_id, false, speculative))->hashcons(); + return (TypeAryPtr*)(new TypeAryPtr(ptr, NULL, ary, k, xk, offset, instance_id, false, speculative, inline_depth))->hashcons(); } //------------------------------make------------------------------------------- -const TypeAryPtr *TypeAryPtr::make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, const TypeOopPtr* speculative, bool is_autobox_cache) { +const TypeAryPtr *TypeAryPtr::make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, const TypeOopPtr* speculative, int inline_depth, bool is_autobox_cache) { assert(!(k == NULL && ary->_elem->isa_int()), "integral arrays must be pre-equipped with a class"); assert( (ptr==Constant && o) || (ptr!=Constant && !o), "" ); if (!xk) xk = (o != NULL) || ary->ary_must_be_exact(); assert(instance_id <= 0 || xk || !UseExactTypes, "instances are always exactly typed"); if (!UseExactTypes) xk = (ptr == Constant); - return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, instance_id, is_autobox_cache, speculative))->hashcons(); + return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, instance_id, is_autobox_cache, speculative, inline_depth))->hashcons(); } //------------------------------cast_to_ptr_type------------------------------- const Type *TypeAryPtr::cast_to_ptr_type(PTR ptr) const { if( ptr == _ptr ) return this; - return make(ptr, const_oop(), _ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative); + return make(ptr, const_oop(), _ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth); } @@ -3596,13 +3721,13 @@ if( klass_is_exact == _klass_is_exact ) return this; if (!UseExactTypes) return this; if (_ary->ary_must_be_exact()) return this; // cannot clear xk - return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _instance_id, _speculative); + return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _instance_id, _speculative, _inline_depth); } //-----------------------------cast_to_instance_id---------------------------- const TypeOopPtr *TypeAryPtr::cast_to_instance_id(int instance_id) const { if( instance_id == _instance_id ) return this; - return make(_ptr, const_oop(), _ary, klass(), _klass_is_exact, _offset, instance_id, _speculative); + return make(_ptr, const_oop(), _ary, klass(), _klass_is_exact, _offset, instance_id, _speculative, _inline_depth); } //-----------------------------narrow_size_type------------------------------- @@ -3665,7 +3790,7 @@ new_size = narrow_size_type(new_size); if (new_size == size()) return this; const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable()); - return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative); + return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth); } @@ -3744,19 +3869,20 @@ const TypeOopPtr *tp = t->is_oopptr(); int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); + int depth = meet_inline_depth(tp->inline_depth()); switch (tp->ptr()) { case TopPTR: case AnyNull: { int instance_id = meet_instance_id(InstanceTop); - const TypeOopPtr* speculative = meet_speculative(tp); + const TypeOopPtr* speculative = xmeet_speculative(tp); return make(ptr, (ptr == Constant ? const_oop() : NULL), - _ary, _klass, _klass_is_exact, offset, instance_id, speculative); + _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth); } case BotPTR: case NotNull: { int instance_id = meet_instance_id(tp->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tp); - return TypeOopPtr::make(ptr, offset, instance_id, speculative); + const TypeOopPtr* speculative = xmeet_speculative(tp); + return TypeOopPtr::make(ptr, offset, instance_id, speculative, depth); } default: ShouldNotReachHere(); } @@ -3780,7 +3906,7 @@ int instance_id = meet_instance_id(InstanceTop); const TypeOopPtr* speculative = _speculative; return make(ptr, (ptr == Constant ? const_oop() : NULL), - _ary, _klass, _klass_is_exact, offset, instance_id, speculative); + _ary, _klass, _klass_is_exact, offset, instance_id, speculative, _inline_depth); } default: ShouldNotReachHere(); } @@ -3793,10 +3919,11 @@ case AryPtr: { // Meeting 2 references? const TypeAryPtr *tap = t->is_aryptr(); int off = meet_offset(tap->offset()); - const TypeAry *tary = _ary->meet(tap->_ary)->is_ary(); + const TypeAry *tary = _ary->meet_speculative(tap->_ary)->is_ary(); PTR ptr = meet_ptr(tap->ptr()); int instance_id = meet_instance_id(tap->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tap); + const TypeOopPtr* speculative = xmeet_speculative(tap); + int depth = meet_inline_depth(tap->inline_depth()); ciKlass* lazy_klass = NULL; if (tary->_elem->isa_int()) { // Integral array element types have irrelevant lattice relations. @@ -3812,17 +3939,17 @@ tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable); } } else // Non integral arrays. - // Must fall to bottom if exact klasses in upper lattice - // are not equal or super klass is exact. - if ( above_centerline(ptr) && klass() != tap->klass() && - // meet with top[] and bottom[] are processed further down: - tap ->_klass != NULL && this->_klass != NULL && - // both are exact and not equal: - ((tap ->_klass_is_exact && this->_klass_is_exact) || - // 'tap' is exact and super or unrelated: - (tap ->_klass_is_exact && !tap->klass()->is_subtype_of(klass())) || - // 'this' is exact and super or unrelated: - (this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) { + // Must fall to bottom if exact klasses in upper lattice + // are not equal or super klass is exact. + if ((above_centerline(ptr) || ptr == Constant) && klass() != tap->klass() && + // meet with top[] and bottom[] are processed further down: + tap->_klass != NULL && this->_klass != NULL && + // both are exact and not equal: + ((tap->_klass_is_exact && this->_klass_is_exact) || + // 'tap' is exact and super or unrelated: + (tap->_klass_is_exact && !tap->klass()->is_subtype_of(klass())) || + // 'this' is exact and super or unrelated: + (this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) { tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable); return make(NotNull, NULL, tary, lazy_klass, false, off, InstanceBot); } @@ -3837,7 +3964,7 @@ } else { xk = (tap->_klass_is_exact | this->_klass_is_exact); } - return make(ptr, const_oop(), tary, lazy_klass, xk, off, instance_id, speculative); + return make(ptr, const_oop(), tary, lazy_klass, xk, off, instance_id, speculative, depth); case Constant: { ciObject* o = const_oop(); if( _ptr == Constant ) { @@ -3856,7 +3983,7 @@ // Only precise for identical arrays xk = this->_klass_is_exact && (klass() == tap->klass()); } - return TypeAryPtr::make(ptr, o, tary, lazy_klass, xk, off, instance_id, speculative); + return TypeAryPtr::make(ptr, o, tary, lazy_klass, xk, off, instance_id, speculative, depth); } case NotNull: case BotPTR: @@ -3865,7 +3992,7 @@ xk = tap->_klass_is_exact; else xk = (tap->_klass_is_exact & this->_klass_is_exact) && (klass() == tap->klass()); // Only precise for identical arrays - return TypeAryPtr::make(ptr, NULL, tary, lazy_klass, xk, off, instance_id, speculative); + return TypeAryPtr::make(ptr, NULL, tary, lazy_klass, xk, off, instance_id, speculative, depth); default: ShouldNotReachHere(); } } @@ -3876,7 +4003,8 @@ int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); int instance_id = meet_instance_id(tp->instance_id()); - const TypeOopPtr* speculative = meet_speculative(tp); + const TypeOopPtr* speculative = xmeet_speculative(tp); + int depth = meet_inline_depth(tp->inline_depth()); switch (ptr) { case TopPTR: case AnyNull: // Fall 'down' to dual of object klass @@ -3884,12 +4012,12 @@ // below the centerline when the superclass is exact. We need to // do the same here. if (tp->klass()->equals(ciEnv::current()->Object_klass()) && !tp->klass_is_exact()) { - return TypeAryPtr::make(ptr, _ary, _klass, _klass_is_exact, offset, instance_id, speculative); + return TypeAryPtr::make(ptr, _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth); } else { // cannot subclass, so the meet has to fall badly below the centerline ptr = NotNull; instance_id = InstanceBot; - return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative); + return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative, depth); } case Constant: case NotNull: @@ -3904,7 +4032,7 @@ if (tp->klass()->equals(ciEnv::current()->Object_klass()) && !tp->klass_is_exact()) { // that is, my array type is a subtype of 'tp' klass return make(ptr, (ptr == Constant ? const_oop() : NULL), - _ary, _klass, _klass_is_exact, offset, instance_id, speculative); + _ary, _klass, _klass_is_exact, offset, instance_id, speculative, depth); } } // The other case cannot happen, since t cannot be a subtype of an array. @@ -3912,7 +4040,7 @@ if( ptr == Constant ) ptr = NotNull; instance_id = InstanceBot; - return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative); + return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative, depth); default: typerr(t); } } @@ -3923,7 +4051,7 @@ //------------------------------xdual------------------------------------------ // Dual: compute field-by-field dual const Type *TypeAryPtr::xdual() const { - return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative()); + return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative(), dual_inline_depth()); } //----------------------interface_vs_oop--------------------------------------- @@ -3976,6 +4104,7 @@ else if (_instance_id != InstanceBot) st->print(",iid=%d",_instance_id); + dump_inline_depth(st); dump_speculative(st); } #endif @@ -3987,11 +4116,22 @@ //------------------------------add_offset------------------------------------- const TypePtr *TypeAryPtr::add_offset(intptr_t offset) const { - return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _instance_id, add_offset_speculative(offset)); -} - -const TypeOopPtr *TypeAryPtr::remove_speculative() const { - return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, _offset, _instance_id, NULL); + return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _instance_id, add_offset_speculative(offset), _inline_depth); +} + +const Type *TypeAryPtr::remove_speculative() const { + if (_speculative == NULL) { + return this; + } + assert(_inline_depth == InlineDepthTop || _inline_depth == InlineDepthBottom, "non speculative type shouldn't have inline depth"); + return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _instance_id, NULL, _inline_depth); +} + +const TypeOopPtr *TypeAryPtr::with_inline_depth(int depth) const { + if (!UseInlineDepthForSpeculativeTypes) { + return this; + } + return make(_ptr, _const_oop, _ary->remove_speculative()->is_ary(), _klass, _klass_is_exact, _offset, _instance_id, _speculative, depth); } //============================================================================= @@ -4031,9 +4171,9 @@ } -const Type *TypeNarrowPtr::filter( const Type *kills ) const { +const Type *TypeNarrowPtr::filter_helper(const Type *kills, bool include_speculative) const { if (isa_same_narrowptr(kills)) { - const Type* ft =_ptrtype->filter(is_same_narrowptr(kills)->_ptrtype); + const Type* ft =_ptrtype->filter_helper(is_same_narrowptr(kills)->_ptrtype, include_speculative); if (ft->empty()) return Type::TOP; // Canonical empty value if (ft->isa_ptr()) { @@ -4041,7 +4181,7 @@ } return ft; } else if (kills->isa_ptr()) { - const Type* ft = _ptrtype->join(kills); + const Type* ft = _ptrtype->join_helper(kills, include_speculative); if (ft->empty()) return Type::TOP; // Canonical empty value return ft; @@ -4171,8 +4311,8 @@ //-----------------------------filter------------------------------------------ // Do not allow interface-vs.-noninterface joins to collapse to top. -const Type *TypeMetadataPtr::filter( const Type *kills ) const { - const TypeMetadataPtr* ft = join(kills)->isa_metadataptr(); +const Type *TypeMetadataPtr::filter_helper(const Type *kills, bool include_speculative) const { + const TypeMetadataPtr* ft = join_helper(kills, include_speculative)->isa_metadataptr(); if (ft == NULL || ft->empty()) return Type::TOP; // Canonical empty value return ft; @@ -4242,7 +4382,7 @@ // else fall through: case TopPTR: case AnyNull: { - return make(ptr, NULL, offset); + return make(ptr, _metadata, offset); } case BotPTR: case NotNull: @@ -4374,10 +4514,10 @@ } // Do not allow interface-vs.-noninterface joins to collapse to top. -const Type *TypeKlassPtr::filter(const Type *kills) const { +const Type *TypeKlassPtr::filter_helper(const Type *kills, bool include_speculative) const { // logic here mirrors the one from TypeOopPtr::filter. See comments // there. - const Type* ft = join(kills); + const Type* ft = join_helper(kills, include_speculative); const TypeKlassPtr* ftkp = ft->isa_klassptr(); const TypeKlassPtr* ktkp = kills->isa_klassptr(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/type.hpp --- a/src/share/vm/opto/type.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/type.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -164,6 +164,8 @@ virtual bool interface_vs_oop_helper(const Type *t) const; #endif + const Type *meet_helper(const Type *t, bool include_speculative) const; + protected: // Each class of type is also identified by its base. const TYPES _base; // Enum of Types type @@ -171,6 +173,10 @@ Type( TYPES t ) : _dual(NULL), _base(t) {} // Simple types // ~Type(); // Use fast deallocation const Type *hashcons(); // Hash-cons the type + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; + const Type *join_helper(const Type *t, bool include_speculative) const { + return dual()->meet_helper(t->dual(), include_speculative)->dual(); + } public: @@ -202,10 +208,24 @@ // Test for equivalence of types static int cmp( const Type *const t1, const Type *const t2 ); // Test for higher or equal in lattice - int higher_equal( const Type *t ) const { return !cmp(meet(t),t); } + // Variant that drops the speculative part of the types + int higher_equal(const Type *t) const { + return !cmp(meet(t),t->remove_speculative()); + } + // Variant that keeps the speculative part of the types + int higher_equal_speculative(const Type *t) const { + return !cmp(meet_speculative(t),t); + } // MEET operation; lower in lattice. - const Type *meet( const Type *t ) const; + // Variant that drops the speculative part of the types + const Type *meet(const Type *t) const { + return meet_helper(t, false); + } + // Variant that keeps the speculative part of the types + const Type *meet_speculative(const Type *t) const { + return meet_helper(t, true); + } // WIDEN: 'widens' for Ints and other range types virtual const Type *widen( const Type *old, const Type* limit ) const { return this; } // NARROW: complement for widen, used by pessimistic phases @@ -221,13 +241,26 @@ // JOIN operation; higher in lattice. Done by finding the dual of the // meet of the dual of the 2 inputs. - const Type *join( const Type *t ) const { - return dual()->meet(t->dual())->dual(); } + // Variant that drops the speculative part of the types + const Type *join(const Type *t) const { + return join_helper(t, false); + } + // Variant that keeps the speculative part of the types + const Type *join_speculative(const Type *t) const { + return join_helper(t, true); + } // Modified version of JOIN adapted to the needs Node::Value. // Normalizes all empty values to TOP. Does not kill _widen bits. // Currently, it also works around limitations involving interface types. - virtual const Type *filter( const Type *kills ) const; + // Variant that drops the speculative part of the types + const Type *filter(const Type *kills) const { + return filter_helper(kills, false); + } + // Variant that keeps the speculative part of the types + const Type *filter_speculative(const Type *kills) const { + return filter_helper(kills, true); + } #ifdef ASSERT // One type is interface, the other is oop @@ -382,7 +415,14 @@ bool is_autobox_cache = false); // Speculative type. See TypeInstPtr + virtual const TypeOopPtr* speculative() const { return NULL; } virtual ciKlass* speculative_type() const { return NULL; } + const Type* maybe_remove_speculative(bool include_speculative) const; + virtual const Type* remove_speculative() const { return this; } + + virtual bool would_improve_type(ciKlass* exact_kls, int inline_depth) const { + return exact_kls != NULL; + } private: // support arrays @@ -450,12 +490,15 @@ // upper bound, inclusive. class TypeInt : public Type { TypeInt( jint lo, jint hi, int w ); +protected: + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; + public: + typedef jint NativeType; virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous -public: const jint _lo, _hi; // Lower bound, upper bound const short _widen; // Limit on times we widen this sucker @@ -475,7 +518,6 @@ virtual const Type *widen( const Type *t, const Type* limit_type ) const; virtual const Type *narrow( const Type *t ) const; // Do not kill _widen bits. - virtual const Type *filter( const Type *kills ) const; // Convenience common pre-built types. static const TypeInt *MINUS_1; static const TypeInt *ZERO; @@ -495,6 +537,9 @@ static const TypeInt *POS1; static const TypeInt *INT; static const TypeInt *SYMINT; // symmetric range [-max_jint..max_jint] + static const TypeInt *TYPE_DOMAIN; // alias for TypeInt::INT + + static const TypeInt *as_self(const Type *t) { return t->is_int(); } #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif @@ -506,7 +551,11 @@ // an upper bound, inclusive. class TypeLong : public Type { TypeLong( jlong lo, jlong hi, int w ); +protected: + // Do not kill _widen bits. + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; public: + typedef jlong NativeType; virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton @@ -524,14 +573,16 @@ 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; virtual const Type *xdual() const; // Compute dual right now. virtual const Type *widen( const Type *t, const Type* limit_type ) const; virtual const Type *narrow( const Type *t ) const; - // Do not kill _widen bits. - virtual const Type *filter( const Type *kills ) const; // Convenience common pre-built types. static const TypeLong *MINUS_1; static const TypeLong *ZERO; @@ -540,6 +591,11 @@ static const TypeLong *LONG; static const TypeLong *INT; // 32-bit subrange [min_jint..max_jint] static const TypeLong *UINT; // 32-bit unsigned [0..max_juint] + static const TypeLong *TYPE_DOMAIN; // alias for TypeLong::LONG + + // static convenience methods. + static const TypeLong *as_self(const Type *t) { return t->is_long(); } + #ifndef PRODUCT virtual void dump2( Dict &d, uint, outputStream *st ) const;// Specialized per-Type dumping #endif @@ -622,6 +678,7 @@ virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. bool ary_must_be_exact() const; // true if arrays of such are never generic + virtual const Type* remove_speculative() const; #ifdef ASSERT // One type is interface, the other is oop virtual bool interface_vs_oop(const Type *t) const; @@ -793,7 +850,7 @@ // Some kind of oop (Java pointer), either klass or instance or array. class TypeOopPtr : public TypePtr { protected: - TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative); + TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative, int inline_depth); public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -804,6 +861,10 @@ }; protected: + enum { + InlineDepthBottom = INT_MAX, + InlineDepthTop = -InlineDepthBottom + }; // Oop is NULL, unless this is a constant oop. ciObject* _const_oop; // Constant oop // If _klass is NULL, then so is _sig. This is an unloaded klass. @@ -824,6 +885,11 @@ // use it, then we have to emit a guard: this part of the type is // not something we know but something we speculate about the type. const TypeOopPtr* _speculative; + // For speculative types, we record at what inlining depth the + // profiling point that provided the data is. We want to favor + // profile data coming from outer scopes which are likely better for + // the current compilation. + int _inline_depth; static const TypeOopPtr* make_from_klass_common(ciKlass* klass, bool klass_change, bool try_for_exact); @@ -832,13 +898,22 @@ // utility methods to work on the speculative part of the type const TypeOopPtr* dual_speculative() const; - const TypeOopPtr* meet_speculative(const TypeOopPtr* other) const; + const TypeOopPtr* xmeet_speculative(const TypeOopPtr* other) const; bool eq_speculative(const TypeOopPtr* other) const; int hash_speculative() const; const TypeOopPtr* add_offset_speculative(intptr_t offset) const; #ifndef PRODUCT void dump_speculative(outputStream *st) const; #endif + // utility methods to work on the inline depth of the type + int dual_inline_depth() const; + int meet_inline_depth(int depth) const; +#ifndef PRODUCT + void dump_inline_depth(outputStream *st) const; +#endif + + // Do not allow interface-vs.-noninterface joins to collapse to top. + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; public: // Creates a type given a klass. Correctly handles multi-dimensional arrays @@ -866,7 +941,7 @@ bool not_null_elements = false); // Make a generic (unclassed) pointer to an oop. - static const TypeOopPtr* make(PTR ptr, int offset, int instance_id, const TypeOopPtr* speculative); + static const TypeOopPtr* make(PTR ptr, int offset, int instance_id, const TypeOopPtr* speculative = NULL, int inline_depth = InlineDepthBottom); ciObject* const_oop() const { return _const_oop; } virtual ciKlass* klass() const { return _klass; } @@ -880,7 +955,7 @@ bool is_known_instance() const { return _instance_id > 0; } int instance_id() const { return _instance_id; } bool is_known_instance_field() const { return is_known_instance() && _offset >= 0; } - const TypeOopPtr* speculative() const { return _speculative; } + virtual const TypeOopPtr* speculative() const { return _speculative; } virtual intptr_t get_con() const; @@ -895,16 +970,13 @@ virtual const TypePtr *add_offset( intptr_t offset ) const; // Return same type without a speculative part - virtual const TypeOopPtr* remove_speculative() const; + virtual const Type* remove_speculative() const; virtual const Type *xmeet(const Type *t) const; virtual const Type *xdual() const; // Compute dual right now. // the core of the computation of the meet for TypeOopPtr and for its subclasses virtual const Type *xmeet_helper(const Type *t) const; - // Do not allow interface-vs.-noninterface joins to collapse to top. - virtual const Type *filter( const Type *kills ) const; - // Convenience common pre-built type. static const TypeOopPtr *BOTTOM; #ifndef PRODUCT @@ -916,18 +988,23 @@ if (_speculative != NULL) { const TypeOopPtr* speculative = _speculative->join(this)->is_oopptr(); if (speculative->klass_is_exact()) { - return speculative->klass(); + return speculative->klass(); } } return NULL; } + int inline_depth() const { + return _inline_depth; + } + virtual const TypeOopPtr* with_inline_depth(int depth) const; + virtual bool would_improve_type(ciKlass* exact_kls, int inline_depth) const; }; //------------------------------TypeInstPtr------------------------------------ // Class of Java object pointers, pointing either to non-array Java instances // or to a Klass* (including array klasses). class TypeInstPtr : public TypeOopPtr { - TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative); + TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative, int inline_depth); virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -963,7 +1040,7 @@ } // Make a pointer to an oop. - static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL); + static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL, int inline_depth = InlineDepthBottom); /** Create constant type for a constant boxed value */ const Type* get_const_boxed_value() const; @@ -981,7 +1058,8 @@ virtual const TypePtr *add_offset( intptr_t offset ) const; // Return same type without a speculative part - virtual const TypeOopPtr* remove_speculative() const; + virtual const Type* remove_speculative() const; + virtual const TypeOopPtr* with_inline_depth(int depth) const; // the core of the computation of the meet of 2 types virtual const Type *xmeet_helper(const Type *t) const; @@ -1003,8 +1081,8 @@ // Class of Java array pointers class TypeAryPtr : public TypeOopPtr { TypeAryPtr( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, - int offset, int instance_id, bool is_autobox_cache, const TypeOopPtr* speculative) - : TypeOopPtr(AryPtr,ptr,k,xk,o,offset, instance_id, speculative), + int offset, int instance_id, bool is_autobox_cache, const TypeOopPtr* speculative, int inline_depth) + : TypeOopPtr(AryPtr,ptr,k,xk,o,offset, instance_id, speculative, inline_depth), _ary(ary), _is_autobox_cache(is_autobox_cache) { @@ -1042,9 +1120,9 @@ bool is_autobox_cache() const { return _is_autobox_cache; } - static const TypeAryPtr *make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL); + static const TypeAryPtr *make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL, int inline_depth = InlineDepthBottom); // Constant pointer to array - static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL, bool is_autobox_cache = false); + static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL, int inline_depth = InlineDepthBottom, bool is_autobox_cache= false); // Return a 'ptr' version of this type virtual const Type *cast_to_ptr_type(PTR ptr) const; @@ -1059,7 +1137,8 @@ virtual bool empty(void) const; // TRUE if type is vacuous virtual const TypePtr *add_offset( intptr_t offset ) const; // Return same type without a speculative part - virtual const TypeOopPtr* remove_speculative() const; + virtual const Type* remove_speculative() const; + virtual const TypeOopPtr* with_inline_depth(int depth) const; // the core of the computation of the meet of 2 types virtual const Type *xmeet_helper(const Type *t) const; @@ -1100,6 +1179,8 @@ class TypeMetadataPtr : public TypePtr { protected: TypeMetadataPtr(PTR ptr, ciMetadata* metadata, int offset); + // Do not allow interface-vs.-noninterface joins to collapse to top. + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -1125,9 +1206,6 @@ virtual intptr_t get_con() const; - // Do not allow interface-vs.-noninterface joins to collapse to top. - virtual const Type *filter( const Type *kills ) const; - // Convenience common pre-built types. static const TypeMetadataPtr *BOTTOM; @@ -1141,6 +1219,8 @@ class TypeKlassPtr : public TypePtr { TypeKlassPtr( PTR ptr, ciKlass* klass, int offset ); +protected: + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -1202,9 +1282,6 @@ virtual intptr_t get_con() const; - // Do not allow interface-vs.-noninterface joins to collapse to top. - virtual const Type *filter( const Type *kills ) const; - // Convenience common pre-built types. static const TypeKlassPtr* OBJECT; // Not-null object klass or below static const TypeKlassPtr* OBJECT_OR_NULL; // Maybe-null version of same @@ -1228,6 +1305,8 @@ virtual const TypeNarrowPtr *is_same_narrowptr(const Type *t) const = 0; virtual const TypeNarrowPtr *make_same_narrowptr(const TypePtr *t) const = 0; virtual const TypeNarrowPtr *make_hash_same_narrowptr(const TypePtr *t) const = 0; + // Do not allow interface-vs.-noninterface joins to collapse to top. + virtual const Type *filter_helper(const Type *kills, bool include_speculative) const; public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -1238,9 +1317,6 @@ virtual intptr_t get_con() const; - // Do not allow interface-vs.-noninterface joins to collapse to top. - virtual const Type *filter( const Type *kills ) const; - virtual bool empty(void) const; // TRUE if type is vacuous // returns the equivalent ptr type for this compressed pointer @@ -1291,6 +1367,10 @@ static const TypeNarrowOop *BOTTOM; static const TypeNarrowOop *NULL_PTR; + virtual const Type* remove_speculative() const { + return make(_ptrtype->remove_speculative()->is_ptr()); + } + #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif @@ -1636,6 +1716,7 @@ #define ConvL2X(x) (x) #define ConvX2I(x) ConvL2I(x) #define ConvX2L(x) (x) +#define ConvX2UL(x) (x) #else @@ -1680,6 +1761,7 @@ #define ConvL2X(x) ConvL2I(x) #define ConvX2I(x) (x) #define ConvX2L(x) ConvI2L(x) +#define ConvX2UL(x) ConvI2UL(x) #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/opto/vectornode.hpp --- a/src/share/vm/opto/vectornode.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/opto/vectornode.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -356,7 +356,7 @@ class LoadVectorNode : public LoadNode { public: LoadVectorNode(Node* c, Node* mem, Node* adr, const TypePtr* at, const TypeVect* vt) - : LoadNode(c, mem, adr, at, vt) { + : LoadNode(c, mem, adr, at, vt, MemNode::unordered) { init_class_id(Class_LoadVector); } @@ -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, MemNode::unordered) { assert(val->is_Vector() || val->is_LoadVector(), "sanity"); init_class_id(Class_StoreVector); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/forte.cpp --- a/src/share/vm/prims/forte.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/forte.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -71,7 +71,7 @@ // Native interfaces for use by Forte tools. -#ifndef IA64 +#if !defined(IA64) && !defined(PPC64) class vframeStreamForte : public vframeStreamCommon { public: @@ -629,16 +629,16 @@ #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 } #else // INCLUDE_JVMTI diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/jni.cpp --- a/src/share/vm/prims/jni.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/jni.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -312,7 +312,7 @@ class JNITraceWrapper : public StackObj { public: - JNITraceWrapper(const char* format, ...) { + JNITraceWrapper(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { if (TraceJNICalls) { va_list ap; va_start(ap, format); @@ -1360,9 +1360,13 @@ // interface call KlassHandle h_holder(THREAD, holder); - int itbl_index = m->itable_index(); - Klass* k = h_recv->klass(); - selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK); + if (call_type == JNI_VIRTUAL) { + int itbl_index = m->itable_index(); + Klass* k = h_recv->klass(); + selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK); + } else { + selected_method = m; + } } } @@ -4450,8 +4454,23 @@ // Get needed field and method IDs directByteBufferConstructor = env->GetMethodID(directByteBufferClass, "", "(JI)V"); + if (env->ExceptionCheck()) { + env->ExceptionClear(); + directBufferSupportInitializeFailed = 1; + return false; + } directBufferAddressField = env->GetFieldID(bufferClass, "address", "J"); + if (env->ExceptionCheck()) { + env->ExceptionClear(); + directBufferSupportInitializeFailed = 1; + return false; + } bufferCapacityField = env->GetFieldID(bufferClass, "capacity", "I"); + if (env->ExceptionCheck()) { + env->ExceptionClear(); + directBufferSupportInitializeFailed = 1; + return false; + } if ((directByteBufferConstructor == NULL) || (directBufferAddressField == NULL) || @@ -5065,8 +5084,11 @@ void TestMetaspaceAux_test(); void TestMetachunk_test(); void TestVirtualSpaceNode_test(); +void TestNewSize_test(); #if INCLUDE_ALL_GCS +void TestOldFreeSpaceCalculation_test(); void TestG1BiasedArray_test(); +void TestCodeCacheRemSet_test(); #endif void execute_internal_vm_tests() { @@ -5085,12 +5107,15 @@ run_unit_test(QuickSort::test_quick_sort()); run_unit_test(AltHashing::test_alt_hash()); run_unit_test(test_loggc_filename()); + run_unit_test(TestNewSize_test()); #if INCLUDE_VM_STRUCTS run_unit_test(VMStructs::test()); #endif #if INCLUDE_ALL_GCS + run_unit_test(TestOldFreeSpaceCalculation_test()); run_unit_test(TestG1BiasedArray_test()); run_unit_test(HeapRegionRemSet::test_prt()); + run_unit_test(TestCodeCacheRemSet_test()); #endif tty->print_cr("All internal VM tests passed"); } @@ -5196,7 +5221,7 @@ } #ifndef PRODUCT - #ifndef TARGET_OS_FAMILY_windows + #ifndef CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) f() #endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/jniCheck.cpp --- a/src/share/vm/prims/jniCheck.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/jniCheck.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -100,7 +100,7 @@ result_type JNICALL header { \ JavaThread* thr = (JavaThread*)ThreadLocalStorage::get_thread_slow();\ if (thr == NULL || !thr->is_Java_thread()) { \ - tty->print_cr(fatal_using_jnienv_in_nonjava); \ + tty->print_cr("%s", fatal_using_jnienv_in_nonjava); \ os::abort(true); \ } \ JNIEnv* xenv = thr->jni_environment(); \ @@ -184,7 +184,7 @@ functionEnter(JavaThread* thr) { if (thr->in_critical()) { - tty->print_cr(warn_other_function_in_critical); + tty->print_cr("%s", warn_other_function_in_critical); } if (thr->has_pending_exception()) { NativeReportJNIWarning(thr, "JNI call made with exception pending"); @@ -195,7 +195,7 @@ functionEnterExceptionAllowed(JavaThread* thr) { if (thr->in_critical()) { - tty->print_cr(warn_other_function_in_critical); + tty->print_cr("%s", warn_other_function_in_critical); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/jvm.cpp --- a/src/share/vm/prims/jvm.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/jvm.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -76,6 +76,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 @@ -217,7 +220,7 @@ #ifdef ASSERT class JVMTraceWrapper : public StackObj { public: - JVMTraceWrapper(const char* format, ...) { + JVMTraceWrapper(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { if (TraceJVMCalls) { va_list ap; va_start(ap, format); @@ -537,6 +540,12 @@ JavaThreadInObjectWaitState jtiows(thread, ms != 0); if (JvmtiExport::should_post_monitor_wait()) { JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); + + // The current thread already owns the monitor and it has not yet + // been added to the wait queue so the current thread cannot be + // made the successor. This means that the JVMTI_EVENT_MONITOR_WAIT + // event handler cannot accidentally consume an unpark() meant for + // the ParkEvent associated with this ObjectMonitor. } ObjectSynchronizer::wait(obj, ms, CHECK); JVM_END @@ -1226,7 +1235,8 @@ // get run() method Method* m_oop = object->klass()->uncached_lookup_method( vmSymbols::run_method_name(), - vmSymbols::void_object_signature()); + vmSymbols::void_object_signature(), + Klass::normal); methodHandle m (THREAD, m_oop); if (m.is_null() || !m->is_method() || !m()->is_public() || m()->is_static()) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "No run method"); @@ -2724,14 +2734,14 @@ JVM_LEAF(jlong, JVM_Lseek(jint fd, jlong offset, jint whence)) - JVMWrapper4("JVM_Lseek (0x%x, %Ld, %d)", fd, offset, whence); + JVMWrapper4("JVM_Lseek (0x%x, " INT64_FORMAT ", %d)", fd, (int64_t) offset, whence); //%note jvm_r6 return os::lseek(fd, offset, whence); JVM_END JVM_LEAF(jint, JVM_SetLength(jint fd, jlong length)) - JVMWrapper3("JVM_SetLength (0x%x, %Ld)", fd, length); + JVMWrapper3("JVM_SetLength (0x%x, " INT64_FORMAT ")", fd, (int64_t) length); return os::ftruncate(fd, length); JVM_END @@ -2746,13 +2756,14 @@ // Printing support ////////////////////////////////////////////////// extern "C" { +ATTRIBUTE_PRINTF(3, 0) 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); } - +ATTRIBUTE_PRINTF(3, 0) int jio_snprintf(char *str, size_t count, const char *fmt, ...) { va_list args; int len; @@ -2762,7 +2773,7 @@ return len; } - +ATTRIBUTE_PRINTF(2,3) int jio_fprintf(FILE* f, const char *fmt, ...) { int len; va_list args; @@ -2772,7 +2783,7 @@ return len; } - +ATTRIBUTE_PRINTF(2, 0) int jio_vfprintf(FILE* f, const char *fmt, va_list args) { if (Arguments::vfprintf_hook() != NULL) { return Arguments::vfprintf_hook()(f, fmt, args); @@ -2781,7 +2792,7 @@ } } - +ATTRIBUTE_PRINTF(1, 2) JNIEXPORT int jio_printf(const char *fmt, ...) { int len; va_list args; @@ -2918,7 +2929,7 @@ JavaThread* receiver = java_lang_Thread::thread(java_thread); Events::log_exception(JavaThread::current(), "JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]", - receiver, (address)java_thread, throwable); + p2i(receiver), p2i((address)java_thread), p2i(throwable)); // First check if thread is alive if (receiver != NULL) { // Check if exception is getting thrown at self (use oop equality, since the diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/jvm.h --- a/src/share/vm/prims/jvm.h Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/jvm.h Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/jvmtiCodeBlobEvents.cpp --- a/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -26,6 +26,7 @@ #include "code/codeBlob.hpp" #include "code/codeCache.hpp" #include "code/scopeDesc.hpp" +#include "code/vtableStubs.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiCodeBlobEvents.hpp" @@ -63,6 +64,7 @@ // used during a collection static GrowableArray* _global_code_blobs; static void do_blob(CodeBlob* cb); + static void do_vtable_stub(VtableStub* vs); public: CodeBlobCollector() { _code_blobs = NULL; @@ -119,6 +121,10 @@ if (cb->is_nmethod()) { return; } + // exclude VtableStubs, which are processed separately + if (cb->is_buffer_blob() && strcmp(cb->name(), "vtable chunks") == 0) { + return; + } // check if this starting address has been seen already - the // assumption is that stubs are inserted into the list before the @@ -136,6 +142,13 @@ _global_code_blobs->append(scb); } +// called for each VtableStub in VtableStubs + +void CodeBlobCollector::do_vtable_stub(VtableStub* vs) { + JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(vs->is_vtable_stub() ? "vtable stub" : "itable stub", + vs->code_begin(), vs->code_end()); + _global_code_blobs->append(scb); +} // collects a list of CodeBlobs in the CodeCache. // @@ -166,6 +179,10 @@ _global_code_blobs->append(new JvmtiCodeBlobDesc(desc->name(), desc->begin(), desc->end())); } + // Vtable stubs are not described with StubCodeDesc, + // process them separately + VtableStubs::vtable_stub_do(do_vtable_stub); + // next iterate over all the non-nmethod code blobs and add them to // the list - as noted above this will filter out duplicates and // enclosing blobs. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/prims/jvmtiEnter.xsl --- a/src/share/vm/prims/jvmtiEnter.xsl Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/prims/jvmtiEnter.xsl Wed Oct 15 16:02:50 2014 +0200 @@ -1,6 +1,6 @@ " INTPTR_FORMAT, first->top_method()->code()); + if (TraceCompilationPolicy) tty->print_cr(" --> " INTPTR_FORMAT, p2i(first->top_method()->code())); } else { if (TimeCompilationPolicy) accumulated_time()->start(); GrowableArray* stack = new GrowableArray(50); @@ -643,7 +648,7 @@ if (TraceCompilationPolicy && Verbose) { tty->print("\n\t check caller: "); next_m->print_short_name(tty); - tty->print(" ( interpreted " INTPTR_FORMAT ", size=%d ) ", (address)next_m(), next_m->code_size()); + tty->print(" ( interpreted " INTPTR_FORMAT ", size=%d ) ", p2i((address)next_m()), next_m->code_size()); } current = next; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/deoptimization.cpp --- a/src/share/vm/runtime/deoptimization.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/deoptimization.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -82,10 +82,15 @@ #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 + +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC #ifdef GRAAL #include "graal/graalRuntime.hpp" @@ -439,15 +444,9 @@ // frame[number_of_frames - 1 ] = on_stack_size(youngest) // frame[number_of_frames - 2 ] = on_stack_size(sender(youngest)) // frame[number_of_frames - 3 ] = on_stack_size(sender(sender(youngest))) - int caller_parms = callee_parameters; - if ((index == array->frames() - 1) && caller_was_method_handle) { - caller_parms = 0; - } - frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(caller_parms, - callee_parameters, + frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(callee_parameters, callee_locals, index == 0, - index == array->frames() - 1, popframe_extra_args); // This pc doesn't have to be perfect just good enough to identify the frame // as interpreted so the skeleton frame will be walkable @@ -1295,9 +1294,19 @@ load_class_by_index(constant_pool, index, THREAD); if (HAS_PENDING_EXCEPTION) { // Exception happened during classloading. We ignore the exception here, since it - // is going to be rethrown since the current activation is going to be deoptimzied and + // is going to be rethrown since the current activation is going to be deoptimized and // the interpreter will re-execute the bytecode. CLEAR_PENDING_EXCEPTION; + // Class loading called java code which may have caused a stack + // overflow. If the exception was thrown right before the return + // to the runtime the stack is no longer guarded. Reguard the + // stack otherwise if we return to the uncommon trap blob and the + // stack bang causes a stack overflow we crash. + assert(THREAD->is_Java_thread(), "only a java thread can be here"); + JavaThread* thread = (JavaThread*)THREAD; + bool guard_pages_enabled = thread->stack_yellow_zone_enabled(); + if (!guard_pages_enabled) guard_pages_enabled = thread->reguard_stack(); + assert(guard_pages_enabled, "stack banging in uncommon trap blob may cause crash"); } } @@ -1416,7 +1425,8 @@ gather_statistics(reason, action, trap_bc); // Ensure that we can record deopt. history: - bool create_if_missing = ProfileTraps; + // Need MDO to record RTM code generation state. + bool create_if_missing = ProfileTraps RTM_OPT_ONLY( || UseRTMLocking ); methodHandle profiled_method; #ifdef GRAAL @@ -1651,6 +1661,7 @@ #ifdef GRAAL nm->is_compiled_by_graal() && nm->is_osr_method(), #endif + nm->method(), //outputs: this_trap_count, maybe_prior_trap, @@ -1696,7 +1707,7 @@ } // Go back to the compiler if there are too many traps in this method. - if (this_trap_count >= (uint)PerMethodTrapLimit) { + if (this_trap_count >= per_method_trap_limit(reason)) { // If there are too many traps in this method, force a recompile. // This will allow the compiler to see the limit overflow, and // take corrective action, if possible. @@ -1730,6 +1741,17 @@ if (tstate1 != tstate0) pdata->set_trap_state(tstate1); } + +#if INCLUDE_RTM_OPT + // Restart collecting RTM locking abort statistic if the method + // is recompiled for a reason other than RTM state change. + // Assume that in new recompiled code the statistic could be different, + // for example, due to different inlining. + if ((reason != Reason_rtm_state_change) && (trap_mdo != NULL) && + UseRTMDeopt && (nm->rtm_state() != ProfileRTM)) { + trap_mdo->atomic_set_rtm_state(ProfileRTM); + } +#endif } if (inc_recompile_count) { @@ -1788,6 +1810,7 @@ #ifdef GRAAL bool is_osr, #endif + Method* compiled_method, //outputs: uint& ret_this_trap_count, bool& ret_maybe_prior_trap, @@ -1823,9 +1846,16 @@ // Find the profile data for this BCI. If there isn't one, // try to allocate one from the MDO's set of spares. // This will let us detect a repeated trap at this point. - pdata = trap_mdo->allocate_bci_to_data(trap_bci); + pdata = trap_mdo->allocate_bci_to_data(trap_bci, reason_is_speculate(reason) ? compiled_method : NULL); if (pdata != NULL) { + if (reason_is_speculate(reason) && !pdata->is_SpeculativeTrapData()) { + if (LogCompilation && xtty != NULL) { + ttyLocker ttyl; + // no more room for speculative traps in this MDO + xtty->elem("speculative_traps_oom"); + } + } // Query the trap state of this profile datum. int tstate0 = pdata->trap_state(); if (!trap_state_has_reason(tstate0, per_bc_reason)) @@ -1863,6 +1893,7 @@ uint ignore_this_trap_count; bool ignore_maybe_prior_trap; bool ignore_maybe_prior_recompile; + assert(!reason_is_speculate(reason), "reason speculate only used by compiler"); // Graal uses the total counts to determine if deoptimizations are happening too frequently -> do not adjust total counts bool update_total_counts = GRAAL_ONLY(false) NOT_GRAAL(true); query_update_method_data(trap_mdo, trap_bci, @@ -1871,6 +1902,7 @@ #ifdef GRAAL false, #endif + NULL, ignore_this_trap_count, ignore_maybe_prior_trap, ignore_maybe_prior_recompile); @@ -2001,6 +2033,8 @@ "age" GRAAL_ONLY("_or_jsr_mismatch"), "predicate", "loop_limit_check", + "speculate_class_check", + "rtm_state_change" #ifdef GRAAL "aliasing", "transfer_to_interpreter", diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/deoptimization.hpp --- a/src/share/vm/runtime/deoptimization.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/deoptimization.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -72,6 +72,8 @@ Reason_age, // nmethod too old; tier threshold reached Reason_predicate, // compiler generated predicate failed Reason_loop_limit_check, // compiler generated loop limits check failed + Reason_speculate_class_check, // saw unexpected object class from type speculation + Reason_rtm_state_change, // rtm state change detected #ifdef GRAAL Reason_aliasing, // optimistic assumption about aliasing failed Reason_transfer_to_interpreter, // explicit transferToInterpreter() @@ -353,10 +355,23 @@ return reason; else if (reason == Reason_div0_check) // null check due to divide-by-zero? return Reason_null_check; // recorded per BCI as a null check + else if (reason == Reason_speculate_class_check) + return Reason_class_check; else return Reason_none; } + static bool reason_is_speculate(int reason) { + if (reason == Reason_speculate_class_check) { + return true; + } + return false; + } + + static uint per_method_trap_limit(int reason) { + return reason_is_speculate(reason) ? (uint)PerMethodSpecTrapLimit : (uint)PerMethodTrapLimit; + } + static const char* trap_reason_name(int reason); static const char* trap_action_name(int action); // Format like reason='foo' action='bar' index='123'. @@ -383,6 +398,7 @@ #ifdef GRAAL bool is_osr, #endif + Method* compiled_method, //outputs: uint& ret_this_trap_count, bool& ret_maybe_prior_trap, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/fprofiler.cpp --- a/src/share/vm/runtime/fprofiler.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/fprofiler.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -41,6 +41,8 @@ #include "runtime/vframe.hpp" #include "utilities/macros.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Static fields of FlatProfiler int FlatProfiler::received_gc_ticks = 0; int FlatProfiler::vm_operation_ticks = 0; @@ -308,7 +310,7 @@ st->fill_to(col2); t->print_native(st); st->fill_to(col3); - st->print(msg); + st->print("%s", msg); st->cr(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/frame.cpp --- a/src/share/vm/runtime/frame.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/frame.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -62,6 +62,8 @@ # include "nativeInst_ppc.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + RegisterMap::RegisterMap(JavaThread *thread, bool update_map) { _thread = thread; _update_map = update_map; @@ -531,13 +533,16 @@ // Number of elements on the interpreter expression stack // Callers should span by stackElementWords int element_size = Interpreter::stackElementWords; + size_t stack_size = 0; if (frame::interpreter_frame_expression_stack_direction() < 0) { - return (interpreter_frame_expression_stack() - - interpreter_frame_tos_address() + 1)/element_size; + stack_size = (interpreter_frame_expression_stack() - + interpreter_frame_tos_address() + 1)/element_size; } else { - return (interpreter_frame_tos_address() - - interpreter_frame_expression_stack() + 1)/element_size; + stack_size = (interpreter_frame_tos_address() - + interpreter_frame_expression_stack() + 1)/element_size; } + assert( stack_size <= (size_t)max_jint, "stack size too big"); + return ((jint)stack_size); } @@ -933,20 +938,9 @@ cld_f->do_cld(m->method_holder()->class_loader_data()); } -#if !defined(PPC) || defined(ZERO) - if (m->is_native()) { -#ifdef CC_INTERP - interpreterState istate = get_interpreterState(); - 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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/frame.hpp --- a/src/share/vm/runtime/frame.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/frame.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -46,10 +46,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" @@ -311,6 +314,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 @@ -347,9 +353,6 @@ void interpreter_frame_set_method(Method* method); Method** interpreter_frame_method_addr() const; ConstantPoolCache** interpreter_frame_cache_addr() const; -#ifdef PPC - oop* interpreter_frame_mirror_addr() const; -#endif public: // Entry frames diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/frame.inline.hpp --- a/src/share/vm/runtime/frame.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/frame.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/globals.cpp --- a/src/share/vm/runtime/globals.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/globals.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -31,6 +31,7 @@ #include "utilities/ostream.hpp" #include "utilities/macros.hpp" #include "utilities/top.hpp" +#include "trace/tracing.hpp" #if INCLUDE_ALL_GCS #include "gc_implementation/g1/g1_globals.hpp" #endif // INCLUDE_ALL_GCS @@ -47,6 +48,8 @@ #include "shark/shark_globals.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + RUNTIME_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, \ MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, \ MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_EXPERIMENTAL_FLAG, \ @@ -65,6 +68,14 @@ MATERIALIZE_FLAGS_EXT +static bool is_product_build() { +#ifdef PRODUCT + return true; +#else + return false; +#endif +} + void Flag::check_writable() { if (is_constant_in_binary()) { fatal(err_msg("flag is constant: %s", _name)); @@ -238,6 +249,27 @@ // Get custom message for this locked flag, or return NULL if // none is available. void Flag::get_locked_message(char* buf, int buflen) const { + buf[0] = '\0'; + if (is_diagnostic() && !is_unlocked()) { + jio_snprintf(buf, buflen, "Error: VM option '%s' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions.\n", + _name); + return; + } + if (is_experimental() && !is_unlocked()) { + jio_snprintf(buf, buflen, "Error: VM option '%s' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.\n", + _name); + return; + } + if (is_develop() && is_product_build()) { + jio_snprintf(buf, buflen, "Error: VM option '%s' is develop and is available only in debug version of VM.\n", + _name); + return; + } + if (is_notproduct() && is_product_build()) { + jio_snprintf(buf, buflen, "Error: VM option '%s' is notproduct and is available only in debug version of VM.\n", + _name); + return; + } get_locked_message_ext(buf, buflen); } @@ -256,6 +288,7 @@ // Length of format string (e.g. "%.1234s") for printing ccstr below #define FORMAT_BUFFER_LEN 16 +PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL void Flag::print_on(outputStream* st, bool withComments) { // Don't print notproduct and develop flags in a product build. if (is_constant_in_binary()) { @@ -288,7 +321,10 @@ size_t llen = pointer_delta(eol, cp, sizeof(char)); jio_snprintf(format_buffer, FORMAT_BUFFER_LEN, "%%." SIZE_FORMAT "s", llen); +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL st->print(format_buffer, cp); +PRAGMA_DIAG_POP st->cr(); cp = eol+1; st->print("%5s %-35s += ", "", _name); @@ -298,7 +334,7 @@ else st->print("%-16s", ""); } - st->print("%-20"); + st->print("%-20s", " "); print_kind(st); if (withComments) { @@ -346,7 +382,7 @@ } else { st->print(" "); } - st->print(d.name); + st->print_raw(d.name); } } @@ -477,13 +513,13 @@ } // Search the flag table for a named flag -Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked, bool allow_constant) { +Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked, bool return_flag) { for (Flag* current = &flagTable[0]; current->_name != NULL; current++) { if (str_equal(current->_name, name, length)) { // Found a matching entry. // Don't report notproduct and develop flags in product builds. - if (current->is_constant_in_binary() && !allow_constant) { - return NULL; + if (current->is_constant_in_binary()) { + return (return_flag == true ? current : NULL); } // Report locked flags only if allowed. if (!(current->is_unlocked() || current->is_unlocker())) { @@ -577,6 +613,17 @@ return true; } +template +static void trace_flag_changed(const char* name, const T old_value, const T new_value, const Flag::Flags origin) +{ + E e; + e.set_name(name); + e.set_old_value(old_value); + e.set_new_value(new_value); + e.set_origin(origin); + e.commit(); +} + bool CommandLineFlags::boolAt(char* name, size_t len, bool* value) { Flag* result = Flag::find_flag(name, len); if (result == NULL) return false; @@ -590,6 +637,7 @@ if (result == NULL) return false; if (!result->is_bool()) return false; bool old_value = result->get_bool(); + trace_flag_changed(name, old_value, *value, origin); result->set_bool(*value); *value = old_value; result->set_origin(origin); @@ -599,6 +647,7 @@ void CommandLineFlagsEx::boolAtPut(CommandLineFlagWithType flag, bool value, Flag::Flags origin) { Flag* faddr = address_of_flag(flag); guarantee(faddr != NULL && faddr->is_bool(), "wrong flag type"); + trace_flag_changed(faddr->_name, faddr->get_bool(), value, origin); faddr->set_bool(value); faddr->set_origin(origin); } @@ -616,6 +665,7 @@ if (result == NULL) return false; if (!result->is_intx()) return false; intx old_value = result->get_intx(); + trace_flag_changed(name, old_value, *value, origin); result->set_intx(*value); *value = old_value; result->set_origin(origin); @@ -625,6 +675,7 @@ void CommandLineFlagsEx::intxAtPut(CommandLineFlagWithType flag, intx value, Flag::Flags origin) { Flag* faddr = address_of_flag(flag); guarantee(faddr != NULL && faddr->is_intx(), "wrong flag type"); + trace_flag_changed(faddr->_name, faddr->get_intx(), value, origin); faddr->set_intx(value); faddr->set_origin(origin); } @@ -642,6 +693,7 @@ if (result == NULL) return false; if (!result->is_uintx()) return false; uintx old_value = result->get_uintx(); + trace_flag_changed(name, old_value, *value, origin); result->set_uintx(*value); *value = old_value; result->set_origin(origin); @@ -651,6 +703,7 @@ void CommandLineFlagsEx::uintxAtPut(CommandLineFlagWithType flag, uintx value, Flag::Flags origin) { Flag* faddr = address_of_flag(flag); guarantee(faddr != NULL && faddr->is_uintx(), "wrong flag type"); + trace_flag_changed(faddr->_name, faddr->get_uintx(), value, origin); faddr->set_uintx(value); faddr->set_origin(origin); } @@ -668,6 +721,7 @@ if (result == NULL) return false; if (!result->is_uint64_t()) return false; uint64_t old_value = result->get_uint64_t(); + trace_flag_changed(name, old_value, *value, origin); result->set_uint64_t(*value); *value = old_value; result->set_origin(origin); @@ -677,6 +731,7 @@ void CommandLineFlagsEx::uint64_tAtPut(CommandLineFlagWithType flag, uint64_t value, Flag::Flags origin) { Flag* faddr = address_of_flag(flag); guarantee(faddr != NULL && faddr->is_uint64_t(), "wrong flag type"); + trace_flag_changed(faddr->_name, faddr->get_uint64_t(), value, origin); faddr->set_uint64_t(value); faddr->set_origin(origin); } @@ -694,6 +749,7 @@ if (result == NULL) return false; if (!result->is_double()) return false; double old_value = result->get_double(); + trace_flag_changed(name, old_value, *value, origin); result->set_double(*value); *value = old_value; result->set_origin(origin); @@ -703,6 +759,7 @@ void CommandLineFlagsEx::doubleAtPut(CommandLineFlagWithType flag, double value, Flag::Flags origin) { Flag* faddr = address_of_flag(flag); guarantee(faddr != NULL && faddr->is_double(), "wrong flag type"); + trace_flag_changed(faddr->_name, faddr->get_double(), value, origin); faddr->set_double(value); faddr->set_origin(origin); } @@ -722,6 +779,7 @@ if (result == NULL) return false; if (!result->is_ccstr()) return false; ccstr old_value = result->get_ccstr(); + trace_flag_changed(name, old_value, *value, origin); char* new_value = NULL; if (*value != NULL) { new_value = NEW_C_HEAP_ARRAY(char, strlen(*value)+1, mtInternal); @@ -744,6 +802,7 @@ Flag* faddr = address_of_flag(flag); guarantee(faddr != NULL && faddr->is_ccstr(), "wrong flag type"); ccstr old_value = faddr->get_ccstr(); + trace_flag_changed(faddr->_name, old_value, value, origin); char* new_value = NEW_C_HEAP_ARRAY(char, strlen(value)+1, mtInternal); strcpy(new_value, value); faddr->set_ccstr(new_value); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/globals.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -144,6 +153,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 @@ -153,6 +165,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 @@ -181,7 +196,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); @@ -256,7 +270,7 @@ // number of flags static size_t numFlags; - static Flag* find_flag(const char* name, size_t length, bool allow_locked = false, bool allow_constant = false); + static Flag* find_flag(const char* name, size_t length, bool allow_locked = false, bool return_flag = false); static Flag* fuzzy_match(const char* name, size_t length, bool allow_locked = false); void check_writable(); @@ -524,13 +538,6 @@ product_pd(bool, UseMembar, \ "(Unstable) Issues membars on thread state transitions") \ \ - /* Temp PPC Flag to allow disabling the use of lwsync on ppc platforms \ - * that don't support it. This will be replaced by processor detection \ - * logic. \ - */ \ - product(bool, UsePPCLWSYNC, true, \ - "Use lwsync instruction if true, else use slower sync") \ - \ develop(bool, CleanChunkPoolAsync, falseInEmbedded, \ "Clean the chunk pool asynchronously") \ \ @@ -1279,6 +1286,9 @@ develop(bool, TraceJNICalls, false, \ "Trace JNI calls") \ \ + develop(bool, StressRewriter, false, \ + "Stress linktime bytecode rewriting") \ + \ notproduct(bool, TraceJVMCalls, false, \ "Trace JVM calls") \ \ @@ -2426,9 +2436,9 @@ "Number of gclog files in rotation " \ "(default: 0, no rotation)") \ \ - product(uintx, GCLogFileSize, 0, \ - "GC log file size (default: 0 bytes, no rotation). " \ - "It requires UseGCLogFileRotation") \ + product(uintx, GCLogFileSize, 8*K, \ + "GC log file size, requires UseGCLogFileRotation. " \ + "Set to 0 to only trigger rotation via jcmd") \ \ /* JVMTI heap profiling */ \ \ @@ -2507,6 +2517,12 @@ develop_pd(bool, ImplicitNullChecks, \ "Generate code for implicit null checks") \ \ + product_pd(bool, TrapBasedNullChecks, \ + "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, i.e.," \ + "ImplicitNullChecks don't work (PPC64).") \ + \ product(bool, PrintSafepointStatistics, false, \ "Print statistics about safepoint synchronization") \ \ @@ -2552,6 +2568,9 @@ develop(bool, PrintMethodFlushing, false, \ "Print the nmethods being flushed") \ \ + diagnostic(bool, PrintMethodFlushingStatistics, false, \ + "print statistics about method flushing") \ + \ develop(bool, UseRelocIndex, false, \ "Use an index to speed random access to relocations") \ \ @@ -2814,6 +2833,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") \ \ @@ -2979,7 +3003,8 @@ "maximum number of nested recursive calls that are inlined") \ \ develop(intx, MaxForceInlineLevel, 100, \ - "maximum number of nested @ForceInline calls that are inlined") \ + "maximum number of nested calls that are forced for inlining " \ + "(using CompilerOracle or marked w/ @ForceInline)") \ \ product_pd(intx, InlineSmallCode, \ "Only inline already compiled methods if their code size is " \ @@ -3076,9 +3101,15 @@ product(intx, PerMethodTrapLimit, 100, \ "Limit on traps (of one kind) in a method (includes inlines)") \ \ + experimental(intx, PerMethodSpecTrapLimit, 5000, \ + "Limit on speculative traps (of one kind) in a method (includes inlines)") \ + \ product(intx, PerBytecodeTrapLimit, 4, \ "Limit on traps (of one kind) at a particular BCI") \ \ + experimental(intx, SpecTrapLimitExtraEntries, 3, \ + "Extra method data trap entries for speculation") \ + \ develop(intx, InlineFrequencyRatio, 20, \ "Ratio of call site execution to caller method invocation") \ \ @@ -3157,15 +3188,15 @@ "Maximum size of class area in Metaspace when compressed " \ "class pointers are used") \ \ - product(uintx, MinHeapFreeRatio, 40, \ + manageable(uintx, MinHeapFreeRatio, 40, \ "The minimum percentage of heap free after GC to avoid expansion."\ - " For most GCs this applies to the old generation. In G1 it" \ - " applies to the whole heap. Not supported by ParallelGC.") \ - \ - product(uintx, MaxHeapFreeRatio, 70, \ + " For most GCs this applies to the old generation. In G1 and" \ + " ParallelGC it applies to the whole heap.") \ + \ + manageable(uintx, MaxHeapFreeRatio, 70, \ "The maximum percentage of heap free after GC to avoid shrinking."\ - " For most GCs this applies to the old generation. In G1 it" \ - " applies to the whole heap. Not supported by ParallelGC.") \ + " For most GCs this applies to the old generation. In G1 and" \ + " ParallelGC it applies to the whole heap.") \ \ product(intx, SoftRefLRUPolicyMSPerMB, 1000, \ "Number of milliseconds per MB of free space in the heap") \ @@ -3284,7 +3315,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") \ \ @@ -3333,21 +3365,21 @@ develop(intx, CIStart, 0, \ "The id of the first compilation to permit") \ \ - develop(intx, CIStop, -1, \ + develop(intx, CIStop, max_jint, \ "The id of the last compilation to permit") \ \ - develop(intx, CIStartOSR, 0, \ + develop(intx, CIStartOSR, 0, \ "The id of the first osr compilation to permit " \ "(CICountOSR must be on)") \ \ - develop(intx, CIStopOSR, -1, \ + develop(intx, CIStopOSR, max_jint, \ "The id of the last osr compilation to permit " \ "(CICountOSR must be on)") \ \ - develop(intx, CIBreakAtOSR, -1, \ + develop(intx, CIBreakAtOSR, -1, \ "The id of osr compilation to break at") \ \ - develop(intx, CIBreakAt, -1, \ + develop(intx, CIBreakAt, -1, \ "The id of compilation to break at") \ \ product(ccstrlist, CompileOnly, "", \ @@ -3366,6 +3398,10 @@ "File containing compilation replay information" \ "[default: ./replay_pid%p.log] (%p replaced with pid)") \ \ + product(ccstr, InlineDataFile, NULL, \ + "File containing inlining replay information" \ + "[default: ./inline_pid%p.log] (%p replaced with pid)") \ + \ develop(intx, ReplaySuppressInitializers, 2, \ "Control handling of class initialization during replay: " \ "0 - don't do anything special; " \ @@ -3782,8 +3818,8 @@ experimental(bool, TrustFinalNonStaticFields, false, \ "trust final non-static declarations for constant folding") \ \ - experimental(bool, FoldStableValues, false, \ - "Private flag to control optimizations for stable variables") \ + diagnostic(bool, FoldStableValues, true, \ + "Optimize loads from stable fields (marked w/ @Stable)") \ \ develop(bool, TraceInvokeDynamic, false, \ "trace internal invoke dynamic operations") \ @@ -3823,6 +3859,22 @@ experimental(uintx, SymbolTableSize, defaultSymbolTableSize, \ "Number of buckets in the JVM internal Symbol table") \ \ + product(bool, UseStringDeduplication, false, \ + "Use string deduplication") \ + \ + product(bool, PrintStringDeduplicationStatistics, false, \ + "Print string deduplication statistics") \ + \ + product(uintx, StringDeduplicationAgeThreshold, 3, \ + "A string must reach this age (or be promoted to an old region) " \ + "to be considered for deduplication") \ + \ + diagnostic(bool, StringDeduplicationResizeALot, false, \ + "Force table resize every time the table is scanned") \ + \ + diagnostic(bool, StringDeduplicationRehashALot, false, \ + "Force table rehash every time the table is scanned") \ + \ develop(bool, TraceDefaultMethods, false, \ "Trace the default method processing steps") \ \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/handles.cpp --- a/src/share/vm/runtime/handles.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/handles.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -41,6 +41,8 @@ # include "os_bsd.inline.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef ASSERT oop* HandleArea::allocate_handle(oop obj) { assert(_handle_mark_nesting > 1, "memory leak: allocating handle outside HandleMark"); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/interfaceSupport.cpp --- a/src/share/vm/runtime/interfaceSupport.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/interfaceSupport.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, 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 @@ -34,6 +34,7 @@ #include "runtime/vframe.hpp" #include "utilities/preserveException.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // Implementation of InterfaceSupport diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/interfaceSupport.hpp --- a/src/share/vm/runtime/interfaceSupport.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/interfaceSupport.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -107,6 +107,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/java.cpp --- a/src/share/vm/runtime/java.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/java.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -56,6 +56,7 @@ #include "runtime/memprofiler.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/statSampler.hpp" +#include "runtime/sweeper.hpp" #include "runtime/task.hpp" #include "runtime/thread.inline.hpp" #include "runtime/timer.hpp" @@ -116,6 +117,7 @@ } } +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC GrowableArray* collected_profiled_methods; @@ -221,9 +223,7 @@ // General statistics printing (profiling ...) - void print_statistics() { - #ifdef ASSERT if (CountRuntimeCalls) { @@ -271,7 +271,7 @@ os::print_statistics(); } - if (PrintLockStatistics || PrintPreciseBiasedLockingStatistics) { + if (PrintLockStatistics || PrintPreciseBiasedLockingStatistics || PrintPreciseRTMLockingStatistics) { OptoRuntime::print_named_counters(); } @@ -321,6 +321,10 @@ CodeCache::print(); } + if (PrintMethodFlushingStatistics) { + NMethodSweeper::print(); + } + if (PrintCodeCache2) { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); CodeCache::print_internals(); @@ -370,7 +374,7 @@ BaselineTTYOutputer outputer(tty); MemTracker::print_memory_usage(outputer, K, false); } else { - tty->print_cr(MemTracker::reason()); + tty->print_cr("%s", MemTracker::reason()); } } } @@ -388,8 +392,12 @@ CodeCache::print(); } + if (PrintMethodFlushingStatistics) { + NMethodSweeper::print(); + } + #ifdef COMPILER2 - if (PrintPreciseBiasedLockingStatistics) { + if (PrintPreciseBiasedLockingStatistics || PrintPreciseRTMLockingStatistics) { OptoRuntime::print_named_counters(); } #endif @@ -406,7 +414,7 @@ BaselineTTYOutputer outputer(tty); MemTracker::print_memory_usage(outputer, K, false); } else { - tty->print_cr(MemTracker::reason()); + tty->print_cr("%s", MemTracker::reason()); } } } @@ -516,10 +524,8 @@ StatSampler::disengage(); StatSampler::destroy(); - // We do not need to explicitly stop concurrent GC threads because the - // JVM will be taken down at a safepoint when such threads are inactive -- - // except for some concurrent G1 threads, see (comment in) - // Threads::destroy_vm(). + // Stop concurrent GC threads + Universe::heap()->stop(); // Print GC/heap related information. if (PrintGCDetails) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/javaFrameAnchor.hpp --- a/src/share/vm/runtime/javaFrameAnchor.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/javaFrameAnchor.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/jniHandles.cpp --- a/src/share/vm/runtime/jniHandles.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/jniHandles.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -30,6 +30,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/thread.inline.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC JNIHandleBlock* JNIHandles::_global_handles = NULL; JNIHandleBlock* JNIHandles::_weak_global_handles = NULL; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/mutex.cpp --- a/src/share/vm/runtime/mutex.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/mutex.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,6 +1,6 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -41,6 +41,8 @@ # include "mutex_bsd.inline.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o-o // // Native Monitor-Mutex locking - theory of operations diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/mutexLocker.cpp --- a/src/share/vm/runtime/mutexLocker.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/mutexLocker.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -58,6 +58,8 @@ Mutex* VtableStubs_lock = NULL; Mutex* SymbolTable_lock = NULL; Mutex* StringTable_lock = NULL; +Monitor* StringDedupQueue_lock = NULL; +Mutex* StringDedupTable_lock = NULL; Mutex* CodeCache_lock = NULL; Mutex* MethodData_lock = NULL; Mutex* RetData_lock = NULL; @@ -196,6 +198,9 @@ def(MMUTracker_lock , Mutex , leaf , true ); def(HotCardCache_lock , Mutex , special , true ); def(EvacFailureStack_lock , Mutex , nonleaf , true ); + + def(StringDedupQueue_lock , Monitor, leaf, true ); + def(StringDedupTable_lock , Mutex , leaf, true ); } def(ParGCRareEvent_lock , Mutex , leaf , true ); def(DerivedPointerTableGC_lock , Mutex, leaf, true ); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/mutexLocker.hpp --- a/src/share/vm/runtime/mutexLocker.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/mutexLocker.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -63,6 +66,8 @@ extern Mutex* VtableStubs_lock; // a lock on the VtableStubs extern Mutex* SymbolTable_lock; // a lock on the symbol table extern Mutex* StringTable_lock; // a lock on the interned string table +extern Monitor* StringDedupQueue_lock; // a lock on the string deduplication queue +extern Mutex* StringDedupTable_lock; // a lock on the string deduplication table extern Mutex* CodeCache_lock; // a lock on the CodeCache, rank is special, use MutexLockerEx extern Mutex* MethodData_lock; // a lock on installation of method data extern Mutex* RetData_lock; // a lock on installation of RetData inside method data diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/objectMonitor.cpp --- a/src/share/vm/runtime/objectMonitor.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/objectMonitor.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -54,7 +54,7 @@ # include "os_bsd.inline.hpp" #endif -#if defined(__GNUC__) && !defined(IA64) +#if defined(__GNUC__) && !defined(IA64) && !defined(PPC64) // Need to inhibit inlining for older versions of GCC to avoid build-time failures #define ATTR __attribute__((noinline)) #else @@ -382,6 +382,12 @@ DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt); if (JvmtiExport::should_post_monitor_contended_enter()) { JvmtiExport::post_monitor_contended_enter(jt, this); + + // The current thread does not yet own the monitor and does not + // yet appear on any queues that would get it made the successor. + // This means that the JVMTI_EVENT_MONITOR_CONTENDED_ENTER event + // handler cannot accidentally consume an unpark() meant for the + // ParkEvent associated with this ObjectMonitor. } OSThreadContendState osts(Self->osthread()); @@ -412,6 +418,15 @@ jt->java_suspend_self(); } Self->set_current_pending_monitor(NULL); + + // We cleared the pending monitor info since we've just gotten past + // the enter-check-for-suspend dance and we now own the monitor free + // and clear, i.e., it is no longer pending. The ThreadBlockInVM + // destructor can go to a safepoint at the end of this block. If we + // do a thread dump during that safepoint, then this thread will show + // as having "-locked" the monitor, but the OS and java.lang.Thread + // states will still report that the thread is blocked trying to + // acquire it. } Atomic::dec_ptr(&_count); @@ -439,6 +454,12 @@ DTRACE_MONITOR_PROBE(contended__entered, this, object(), jt); if (JvmtiExport::should_post_monitor_contended_entered()) { JvmtiExport::post_monitor_contended_entered(jt, this); + + // The current thread already owns the monitor and is not going to + // call park() for the remainder of the monitor enter protocol. So + // it doesn't matter if the JVMTI_EVENT_MONITOR_CONTENDED_ENTERED + // event handler consumed an unpark() issued by the thread that + // just exited the monitor. } if (event.should_commit()) { @@ -1456,6 +1477,14 @@ // Note: 'false' parameter is passed here because the // wait was not timed out due to thread interrupt. JvmtiExport::post_monitor_waited(jt, this, false); + + // In this short circuit of the monitor wait protocol, the + // current thread never drops ownership of the monitor and + // never gets added to the wait queue so the current thread + // cannot be made the successor. This means that the + // JVMTI_EVENT_MONITOR_WAITED event handler cannot accidentally + // consume an unpark() meant for the ParkEvent associated with + // this ObjectMonitor. } if (event.should_commit()) { post_monitor_wait_event(&event, 0, millis, false); @@ -1499,21 +1528,6 @@ exit (true, Self) ; // exit the monitor guarantee (_owner != Self, "invariant") ; - // As soon as the ObjectMonitor's ownership is dropped in the exit() - // call above, another thread can enter() the ObjectMonitor, do the - // notify(), and exit() the ObjectMonitor. If the other thread's - // exit() call chooses this thread as the successor and the unpark() - // call happens to occur while this thread is posting a - // MONITOR_CONTENDED_EXIT event, then we run the risk of the event - // handler using RawMonitors and consuming the unpark(). - // - // To avoid the problem, we re-post the event. This does no harm - // even if the original unpark() was not consumed because we are the - // chosen successor for this monitor. - if (node._notified != 0 && _succ == Self) { - node._event->unpark(); - } - // The thread is on the WaitSet list - now park() it. // On MP systems it's conceivable that a brief spin before we park // could be profitable. @@ -1595,6 +1609,25 @@ // post monitor waited event. Note that this is past-tense, we are done waiting. if (JvmtiExport::should_post_monitor_waited()) { JvmtiExport::post_monitor_waited(jt, this, ret == OS_TIMEOUT); + + if (node._notified != 0 && _succ == Self) { + // In this part of the monitor wait-notify-reenter protocol it + // is possible (and normal) for another thread to do a fastpath + // monitor enter-exit while this thread is still trying to get + // to the reenter portion of the protocol. + // + // The ObjectMonitor was notified and the current thread is + // the successor which also means that an unpark() has already + // been done. The JVMTI_EVENT_MONITOR_WAITED event handler can + // consume the unpark() that was done when the successor was + // set because the same ParkEvent is shared between Java + // monitors and JVM/TI RawMonitors (for now). + // + // We redo the unpark() to ensure forward progress, i.e., we + // don't want all pending threads hanging (parked) with none + // entering the unlocked monitor. + node._event->unpark(); + } } if (event.should_commit()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/os.cpp --- a/src/share/vm/runtime/os.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/os.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -65,6 +65,8 @@ # include +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + OSThread* os::_starting_thread = NULL; address os::_polling_page = NULL; volatile int32_t* os::_mem_serialize_page = NULL; @@ -909,9 +911,9 @@ for (int i = 0; env_list[i] != NULL; i++) { if (getenv(env_list[i], buffer, len)) { - st->print(env_list[i]); + st->print("%s", env_list[i]); st->print("="); - st->print_cr(buffer); + st->print_cr("%s", buffer); } } } @@ -929,6 +931,10 @@ } void os::print_date_and_time(outputStream *st) { + const int secs_per_day = 86400; + const int secs_per_hour = 3600; + const int secs_per_min = 60; + time_t tloc; (void)time(&tloc); st->print("time: %s", ctime(&tloc)); // ctime adds newline. @@ -937,7 +943,17 @@ // NOTE: It tends to crash after a SEGV if we want to printf("%f",...) in // Linux. Must be a bug in glibc ? Workaround is to round "t" to int // before printf. We lost some precision, but who cares? - st->print_cr("elapsed time: %d seconds", (int)t); + int eltime = (int)t; // elapsed time in seconds + + // print elapsed time in a human-readable format: + int eldays = eltime / secs_per_day; + int day_secs = eldays * secs_per_day; + int elhours = (eltime - day_secs) / secs_per_hour; + int hour_secs = elhours * secs_per_hour; + int elmins = (eltime - day_secs - hour_secs) / secs_per_min; + int minute_secs = elmins * secs_per_min; + int elsecs = (eltime - day_secs - hour_secs - minute_secs); + st->print_cr("elapsed time: %d seconds (%dd %dh %dm %ds)", eltime, eldays, elhours, elmins, elsecs); } // moved from debug.cpp (used to be find()) but still called from there @@ -1081,15 +1097,17 @@ } -#ifndef PRODUCT - // Check if in metaspace. - if (ClassLoaderDataGraph::contains((address)addr)) { - // Use addr->print() from the debugger instead (not here) - st->print_cr(INTPTR_FORMAT - " is pointing into metadata", addr); + // Check if in metaspace and print types that have vptrs (only method now) + if (Metaspace::contains(addr)) { + if (Method::has_method_vptr((const void*)addr)) { + ((Method*)addr)->print_value_on(st); + st->cr(); + } else { + // Use addr->print() from the debugger instead (not here) + st->print_cr(INTPTR_FORMAT " is pointing into metadata", addr); + } return; } -#endif // Try an OS specific find if (os::find(addr, st)) { @@ -1103,7 +1121,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) { -#if defined(IA64) && !defined(_WIN32) +#if (defined(IA64) && !defined(AIX)) && !defined(_WIN32) // On IA64 we have to check if the callers bsp is still valid // (i.e. within the register stack bounds). // Notice: this only works for threads created by the VM and only if diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/os.hpp --- a/src/share/vm/runtime/os.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/os.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -41,9 +41,16 @@ #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 +# ifdef __APPLE__ +# include +# endif #endif class AgentLibrary; @@ -433,7 +440,10 @@ static intx current_thread_id(); static int current_process_id(); static int sleep(Thread* thread, jlong ms, bool interruptable); - static int naked_sleep(); + // Short standalone OS sleep suitable for slow path spin loop. + // Ignores Thread.interrupt() (so keep it short). + // ms = 0, will sleep for the least amount of time allowed by the OS. + static void naked_short_sleep(jlong ms); static void infinite_sleep(); // never returns, use with CAUTION static void yield(); // Yields to all threads with same priority enum YieldResult { @@ -772,6 +782,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_posix.hpp" # include "os_bsd.hpp" @@ -800,6 +814,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 diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/osThread.cpp --- a/src/share/vm/runtime/osThread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/osThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -26,6 +26,7 @@ #include "oops/oop.inline.hpp" #include "runtime/osThread.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC OSThread::OSThread(OSThreadStartFunc start_proc, void* start_parm) { pd_initialize(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/osThread.hpp --- a/src/share/vm/runtime/osThread.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/osThread.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/park.cpp --- a/src/share/vm/runtime/park.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/park.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -59,58 +59,22 @@ // Start by trying to recycle an existing but unassociated // ParkEvent from the global free list. - for (;;) { - ev = FreeList ; - if (ev == NULL) break ; - // 1: Detach - sequester or privatize the list - // Tantamount to ev = Swap (&FreeList, NULL) - if (Atomic::cmpxchg_ptr (NULL, &FreeList, ev) != ev) { - continue ; + // Using a spin lock since we are part of the mutex impl. + // 8028280: using concurrent free list without memory management can leak + // pretty badly it turns out. + Thread::SpinAcquire(&ListLock, "ParkEventFreeListAllocate"); + { + ev = FreeList; + if (ev != NULL) { + FreeList = ev->FreeNext; } - - // We've detached the list. The list in-hand is now - // local to this thread. This thread can operate on the - // list without risk of interference from other threads. - // 2: Extract -- pop the 1st element from the list. - ParkEvent * List = ev->FreeNext ; - if (List == NULL) break ; - for (;;) { - // 3: Try to reattach the residual list - guarantee (List != NULL, "invariant") ; - ParkEvent * Arv = (ParkEvent *) Atomic::cmpxchg_ptr (List, &FreeList, NULL) ; - if (Arv == NULL) break ; - - // New nodes arrived. Try to detach the recent arrivals. - if (Atomic::cmpxchg_ptr (NULL, &FreeList, Arv) != Arv) { - continue ; - } - guarantee (Arv != NULL, "invariant") ; - // 4: Merge Arv into List - ParkEvent * Tail = List ; - while (Tail->FreeNext != NULL) Tail = Tail->FreeNext ; - Tail->FreeNext = Arv ; - } - break ; } + Thread::SpinRelease(&ListLock); if (ev != NULL) { guarantee (ev->AssociatedWith == NULL, "invariant") ; } else { // Do this the hard way -- materialize a new ParkEvent. - // In rare cases an allocating thread might detach a long list -- - // installing null into FreeList -- and then stall or be obstructed. - // A 2nd thread calling Allocate() would see FreeList == null. - // The list held privately by the 1st thread is unavailable to the 2nd thread. - // In that case the 2nd thread would have to materialize a new ParkEvent, - // even though free ParkEvents existed in the system. In this case we end up - // with more ParkEvents in circulation than we need, but the race is - // rare and the outcome is benign. Ideally, the # of extant ParkEvents - // is equal to the maximum # of threads that existed at any one time. - // Because of the race mentioned above, segments of the freelist - // can be transiently inaccessible. At worst we may end up with the - // # of ParkEvents in circulation slightly above the ideal. - // Note that if we didn't have the TSM/immortal constraint, then - // when reattaching, above, we could trim the list. ev = new ParkEvent () ; guarantee ((intptr_t(ev) & 0xFF) == 0, "invariant") ; } @@ -124,13 +88,14 @@ if (ev == NULL) return ; guarantee (ev->FreeNext == NULL , "invariant") ; ev->AssociatedWith = NULL ; - for (;;) { - // Push ev onto FreeList - // The mechanism is "half" lock-free. - ParkEvent * List = FreeList ; - ev->FreeNext = List ; - if (Atomic::cmpxchg_ptr (ev, &FreeList, List) == List) break ; + // Note that if we didn't have the TSM/immortal constraint, then + // when reattaching we could trim the list. + Thread::SpinAcquire(&ListLock, "ParkEventFreeListRelease"); + { + ev->FreeNext = FreeList; + FreeList = ev; } + Thread::SpinRelease(&ListLock); } // Override operator new and delete so we can ensure that the @@ -164,56 +129,21 @@ // Start by trying to recycle an existing but unassociated // Parker from the global free list. - for (;;) { - p = FreeList ; - if (p == NULL) break ; - // 1: Detach - // Tantamount to p = Swap (&FreeList, NULL) - if (Atomic::cmpxchg_ptr (NULL, &FreeList, p) != p) { - continue ; + // 8028280: using concurrent free list without memory management can leak + // pretty badly it turns out. + Thread::SpinAcquire(&ListLock, "ParkerFreeListAllocate"); + { + p = FreeList; + if (p != NULL) { + FreeList = p->FreeNext; } - - // We've detached the list. The list in-hand is now - // local to this thread. This thread can operate on the - // list without risk of interference from other threads. - // 2: Extract -- pop the 1st element from the list. - Parker * List = p->FreeNext ; - if (List == NULL) break ; - for (;;) { - // 3: Try to reattach the residual list - guarantee (List != NULL, "invariant") ; - Parker * Arv = (Parker *) Atomic::cmpxchg_ptr (List, &FreeList, NULL) ; - if (Arv == NULL) break ; - - // New nodes arrived. Try to detach the recent arrivals. - if (Atomic::cmpxchg_ptr (NULL, &FreeList, Arv) != Arv) { - continue ; - } - guarantee (Arv != NULL, "invariant") ; - // 4: Merge Arv into List - Parker * Tail = List ; - while (Tail->FreeNext != NULL) Tail = Tail->FreeNext ; - Tail->FreeNext = Arv ; - } - break ; } + Thread::SpinRelease(&ListLock); if (p != NULL) { guarantee (p->AssociatedWith == NULL, "invariant") ; } else { // Do this the hard way -- materialize a new Parker.. - // In rare cases an allocating thread might detach - // a long list -- installing null into FreeList --and - // then stall. Another thread calling Allocate() would see - // FreeList == null and then invoke the ctor. In this case we - // end up with more Parkers in circulation than we need, but - // the race is rare and the outcome is benign. - // Ideally, the # of extant Parkers is equal to the - // maximum # of threads that existed at any one time. - // Because of the race mentioned above, segments of the - // freelist can be transiently inaccessible. At worst - // we may end up with the # of Parkers in circulation - // slightly above the ideal. p = new Parker() ; } p->AssociatedWith = t ; // Associate p with t @@ -227,11 +157,12 @@ guarantee (p->AssociatedWith != NULL, "invariant") ; guarantee (p->FreeNext == NULL , "invariant") ; p->AssociatedWith = NULL ; - for (;;) { - // Push p onto FreeList - Parker * List = FreeList ; - p->FreeNext = List ; - if (Atomic::cmpxchg_ptr (p, &FreeList, List) == List) break ; + + Thread::SpinAcquire(&ListLock, "ParkerFreeListRelease"); + { + p->FreeNext = FreeList; + FreeList = p; } + Thread::SpinRelease(&ListLock); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/perfData.cpp --- a/src/share/vm/runtime/perfData.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/perfData.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -34,6 +34,8 @@ #include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + PerfDataList* PerfDataManager::_all = NULL; PerfDataList* PerfDataManager::_sampled = NULL; PerfDataList* PerfDataManager::_constants = NULL; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/perfMemory.cpp --- a/src/share/vm/runtime/perfMemory.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/perfMemory.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -34,6 +34,8 @@ #include "runtime/statSampler.hpp" #include "utilities/globalDefinitions.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Prefix of performance data file. const char PERFDATA_NAME[] = "hsperfdata"; @@ -155,7 +157,7 @@ void PerfMemory::destroy() { - assert(_prologue != NULL, "prologue pointer must be initialized"); + if (_prologue == NULL) return; if (_start != NULL && _prologue->overflow != 0) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/rtmLocking.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/runtime/rtmLocking.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 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 + * 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_RUNTIME_RTMLOCKING_HPP +#define SHARE_VM_RUNTIME_RTMLOCKING_HPP + +// Generate RTM (Restricted Transactional Memory) locking code for all inflated +// locks when "UseRTMLocking" option is on with normal locking mechanism as fall back +// handler. +// +// On abort/lock busy the lock will be retried a fixed number of times under RTM +// as specified by "RTMRetryCount" option. The locks which abort too often +// can be auto tuned or manually tuned. +// +// Auto-tuning can be done on an option like UseRTMDeopt and it will need abort +// ratio calculation for each lock. The abort ratio will be calculated after +// "RTMAbortThreshold" number of aborts is reached. The formulas are: +// +// Aborted transactions = abort_count * 100 +// All transactions = total_count * RTMTotalCountIncrRate +// +// Aborted transactions >= All transactions * RTMAbortRatio +// +// If "UseRTMDeopt" is on and the aborts ratio reaches "RTMAbortRatio" +// the method containing the lock will be deoptimized and recompiled with +// all locks as normal locks. If the abort ratio continues to remain low after +// "RTMLockingThreshold" locks are attempted, then the method will be deoptimized +// and recompiled with all locks as RTM locks without abort ratio calculation code. +// The abort ratio calculation can be delayed by specifying flag +// -XX:RTMLockingCalculationDelay in millisecond. +// +// For manual tuning the abort statistics for each lock needs to be provided +// to the user on some JVM option like "PrintPreciseRTMLockingStatistics". +// Based on the abort statistics users can create a .hotspot_compiler file +// or use -XX:CompileCommand=option,class::method,NoRTMLockEliding +// to specify for which methods to disable RTM locking. +// +// When UseRTMForStackLocks option is enabled along with UseRTMLocking option, +// the RTM locking code is generated for stack locks too. +// The retries, auto-tuning support and rtm locking statistics are all +// supported for stack locks just like inflated locks. + +// RTM locking counters +class RTMLockingCounters VALUE_OBJ_CLASS_SPEC { + private: + uintx _total_count; // Total RTM locks count + uintx _abort_count; // Total aborts count + + public: + enum { ABORT_STATUS_LIMIT = 6 }; + // Counters per RTM Abort Status. Incremented with +PrintPreciseRTMLockingStatistics + // RTM uses the EAX register to communicate abort status to software. + // Following an RTM abort the EAX register has the following definition. + // + // EAX register bit position Meaning + // 0 Set if abort caused by XABORT instruction. + // 1 If set, the transaction may succeed on a retry. This bit is always clear if bit 0 is set. + // 2 Set if another logical processor conflicted with a memory address that was part of the transaction that aborted. + // 3 Set if an internal buffer overflowed. + // 4 Set if a debug breakpoint was hit. + // 5 Set if an abort occurred during execution of a nested transaction. + private: + uintx _abortX_count[ABORT_STATUS_LIMIT]; + + public: + static uintx _calculation_flag; + static uintx* rtm_calculation_flag_addr() { return &_calculation_flag; } + + static void init(); + + RTMLockingCounters() : _total_count(0), _abort_count(0) { + for (int i = 0; i < ABORT_STATUS_LIMIT; i++) { + _abortX_count[i] = 0; + } + } + + uintx* total_count_addr() { return &_total_count; } + uintx* abort_count_addr() { return &_abort_count; } + uintx* abortX_count_addr() { return &_abortX_count[0]; } + + static int total_count_offset() { return (int)offset_of(RTMLockingCounters, _total_count); } + static int abort_count_offset() { return (int)offset_of(RTMLockingCounters, _abort_count); } + static int abortX_count_offset() { return (int)offset_of(RTMLockingCounters, _abortX_count[0]); } + + + bool nonzero() { return (_abort_count + _total_count) > 0; } + + void print_on(outputStream* st); + void print() { print_on(tty); } +}; + +#endif // SHARE_VM_RUNTIME_RTMLOCKING_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/safepoint.cpp --- a/src/share/vm/runtime/safepoint.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/safepoint.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -82,6 +82,8 @@ #include "c1/c1_globals.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // -------------------------------------------------------------------------------------------------- // Implementation of Safepoint begin/end @@ -548,7 +550,14 @@ // rotate log files? if (UseGCLogFileRotation) { - gclog_or_tty->rotate_log(); + gclog_or_tty->rotate_log(false); + } + + { + // CMS delays purging the CLDG until the beginning of the next safepoint and to + // make sure concurrent sweep is done + TraceTime t7("purging class loader data graph", TraceSafepointCleanupTime); + ClassLoaderDataGraph::purge_if_needed(); } if (MemTracker::is_on()) { @@ -799,7 +808,7 @@ old_sp += incr*32; new_sp += incr*32; was_oops += incr*32; for( int i2=0; i2<16; i2++ ) { tty->print("call %c%d |"PTR_FORMAT" ","LI"[i2>>3],i2&7,new_sp); print_ptrs(*old_sp++,*new_sp++,*was_oops++); } - tty->print_cr(""); + tty->cr(); } #endif // SPARC #endif // PRODUCT @@ -841,7 +850,7 @@ timeout_error_printed = true; // Print out the thread infor which didn't reach the safepoint for debugging // purposes (useful when there are lots of threads in the debugger). - tty->print_cr(""); + tty->cr(); tty->print_cr("# SafepointSynchronize::begin: Timeout detected:"); if (reason == _spinning_timeout) { tty->print_cr("# SafepointSynchronize::begin: Timed out while spinning to reach a safepoint."); @@ -861,7 +870,7 @@ (reason == _blocking_timeout && !cur_state->has_called_back()))) { tty->print("# "); cur_thread->print(); - tty->print_cr(""); + tty->cr(); } } tty->print_cr("# SafepointSynchronize::begin: (End of list)"); @@ -1334,7 +1343,7 @@ spstat->_time_to_sync > PrintSafepointStatisticsTimeout * MICROUNITS) { print_statistics(); } - tty->print_cr(""); + tty->cr(); // Print out polling page sampling status. if (!need_to_track_page_armed_status) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/safepoint.hpp --- a/src/share/vm/runtime/safepoint.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/safepoint.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -174,7 +174,7 @@ // debugging static void print_state() PRODUCT_RETURN; - static void safepoint_msg(const char* format, ...) PRODUCT_RETURN; + static void safepoint_msg(const char* format, ...) ATTRIBUTE_PRINTF(1, 2) PRODUCT_RETURN; static void deferred_initialize_stat(); static void print_stat_on_exit(); @@ -240,7 +240,7 @@ static void create(JavaThread *thread); static void destroy(JavaThread *thread); - void safepoint_msg(const char* format, ...) { + void safepoint_msg(const char* format, ...) ATTRIBUTE_PRINTF(2, 3) { if (ShowSafepointMsgs) { va_list ap; va_start(ap, format); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/sharedRuntime.cpp --- a/src/share/vm/runtime/sharedRuntime.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/sharedRuntime.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -82,6 +82,8 @@ #include "c1/c1_Runtime1.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Shared stub locations RuntimeStub* SharedRuntime::_wrong_method_blob; RuntimeStub* SharedRuntime::_wrong_method_abstract_blob; @@ -408,7 +410,7 @@ #endif -#if defined(__SOFTFP__) || defined(PPC) +#if defined(__SOFTFP__) || defined(PPC32) double SharedRuntime::dsqrt(double f) { return sqrt(f); } @@ -500,6 +502,13 @@ assert(!nm->is_native_method(), "no exception handler"); assert(nm->header_begin() != nm->exception_begin(), "no exception handler"); if (nm->is_deopt_pc(return_address)) { + // If we come here because of a stack overflow, the stack may be + // unguarded. Reguard the stack otherwise if we return to the + // deopt blob and the stack bang causes a stack overflow we + // crash. + bool guard_pages_enabled = thread->stack_yellow_zone_enabled(); + if (!guard_pages_enabled) guard_pages_enabled = thread->reguard_stack(); + assert(guard_pages_enabled, "stack banging in deopt blob may cause crash"); return SharedRuntime::deopt_blob()->unpack_with_exception(); } else { return nm->exception_begin(); @@ -812,10 +821,13 @@ // going to be unwound. Dispatch to a shared runtime stub // which will cause the StackOverflowError to be fabricated // and processed. - // For stack overflow in deoptimization blob, cleanup thread. - if (thread->deopt_mark() != NULL) { - Deoptimization::cleanup_deopt_info(thread, NULL); - } + // Stack overflow should never occur during deoptimization: + // the compiled method bangs the stack by as much as the + // interpreter would need in case of a deoptimization. The + // deoptimization blob and uncommon trap blob bang the stack + // in a debug VM to verify the correctness of the compiled + // method stack banging. + assert(thread->deopt_mark() == NULL, "no stack overflow from deopt blob/uncommon trap"); Events::log_exception(thread, "StackOverflowError at " INTPTR_FORMAT, pc); return StubRoutines::throw_StackOverflowError_entry(); } @@ -1019,14 +1031,13 @@ * it gets turned into a tail-call on sparc, which runs into dtrace bug * 6254741. Once that is fixed we can remove the dummy return value. */ -int SharedRuntime::dtrace_object_alloc(oopDesc* o) { - return dtrace_object_alloc_base(Thread::current(), o); +int SharedRuntime::dtrace_object_alloc(oopDesc* o, int size) { + return dtrace_object_alloc_base(Thread::current(), o, size); } -int SharedRuntime::dtrace_object_alloc_base(Thread* thread, oopDesc* o) { +int SharedRuntime::dtrace_object_alloc_base(Thread* thread, oopDesc* o, int size) { assert(DTraceAllocProbes, "wrong call"); Klass* klass = o->klass(); - int size = o->size(); Symbol* name = klass->name(); #ifndef USDT2 HS_DTRACE_PROBE4(hotspot, object__alloc, get_java_tid(thread), @@ -2465,7 +2476,7 @@ ResourceMark rm; NOT_PRODUCT(int insts_size); - AdapterBlob* B = NULL; + AdapterBlob* new_adapter = NULL; AdapterHandlerEntry* entry = NULL; AdapterFingerPrint* fingerprint = NULL; { @@ -2497,7 +2508,8 @@ #ifdef ASSERT AdapterHandlerEntry* shared_entry = NULL; - if (VerifyAdapterSharing && entry != NULL) { + // Start adapter sharing verification only after the VM is booted. + if (VerifyAdapterSharing && (entry != NULL)) { shared_entry = entry; entry = NULL; } @@ -2513,41 +2525,44 @@ // Make a C heap allocated version of the fingerprint to store in the adapter fingerprint = new AdapterFingerPrint(total_args_passed, sig_bt); + // StubRoutines::code2() is initialized after this function can be called. As a result, + // VerifyAdapterCalls and VerifyAdapterSharing can fail if we re-use code that generated + // prior to StubRoutines::code2() being set. Checks refer to checks generated in an I2C + // stub that ensure that an I2C stub is called from an interpreter frame. + bool contains_all_checks = StubRoutines::code2() != NULL; + // Create I2C & C2I handlers - BufferBlob* buf = buffer_blob(); // the temporary code buffer in CodeCache if (buf != NULL) { CodeBuffer buffer(buf); short buffer_locs[20]; buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs, sizeof(buffer_locs)/sizeof(relocInfo)); + MacroAssembler _masm(&buffer); - entry = SharedRuntime::generate_i2c2i_adapters(&_masm, total_args_passed, comp_args_on_stack, sig_bt, regs, fingerprint); - #ifdef ASSERT if (VerifyAdapterSharing) { if (shared_entry != NULL) { - assert(shared_entry->compare_code(buf->code_begin(), buffer.insts_size(), total_args_passed, sig_bt), - "code must match"); + assert(shared_entry->compare_code(buf->code_begin(), buffer.insts_size()), "code must match"); // Release the one just created and return the original _adapters->free_entry(entry); return shared_entry; } else { - entry->save_code(buf->code_begin(), buffer.insts_size(), total_args_passed, sig_bt); + entry->save_code(buf->code_begin(), buffer.insts_size()); } } #endif - B = AdapterBlob::create(&buffer); + new_adapter = AdapterBlob::create(&buffer); NOT_PRODUCT(insts_size = buffer.insts_size()); } - if (B == NULL) { + if (new_adapter == NULL) { // CodeCache is full, disable compilation // Ought to log this but compile log is only per compile thread // and we're some non descript Java thread. @@ -2555,7 +2570,7 @@ CompileBroker::handle_full_code_cache(); return NULL; // Out of CodeCache space } - entry->relocate(B->content_begin()); + entry->relocate(new_adapter->content_begin()); #ifndef PRODUCT // debugging suppport if (PrintAdapterHandlers || PrintStubCode) { @@ -2574,22 +2589,25 @@ } } #endif - - _adapters->add(entry); + // Add the entry only if the entry contains all required checks (see sharedRuntime_xxx.cpp) + // The checks are inserted only if -XX:+VerifyAdapterCalls is specified. + if (contains_all_checks || !VerifyAdapterCalls) { + _adapters->add(entry); + } } // Outside of the lock - if (B != NULL) { + if (new_adapter != NULL) { char blob_id[256]; jio_snprintf(blob_id, sizeof(blob_id), "%s(%s)@" PTR_FORMAT, - B->name(), + new_adapter->name(), fingerprint->as_string(), - B->content_begin()); - Forte::register_stub(blob_id, B->content_begin(), B->content_end()); + new_adapter->content_begin()); + Forte::register_stub(blob_id, new_adapter->content_begin(),new_adapter->content_end()); if (JvmtiExport::should_post_dynamic_code_generated()) { - JvmtiExport::post_dynamic_code_generated(blob_id, B->content_begin(), B->content_end()); + JvmtiExport::post_dynamic_code_generated(blob_id, new_adapter->content_begin(), new_adapter->content_end()); } } return entry; @@ -2621,7 +2639,6 @@ delete _fingerprint; #ifdef ASSERT if (_saved_code) FREE_C_HEAP_ARRAY(unsigned char, _saved_code, mtCode); - if (_saved_sig) FREE_C_HEAP_ARRAY(Basictype, _saved_sig, mtCode); #endif } @@ -2630,35 +2647,30 @@ // Capture the code before relocation so that it can be compared // against other versions. If the code is captured after relocation // then relative instructions won't be equivalent. -void AdapterHandlerEntry::save_code(unsigned char* buffer, int length, int total_args_passed, BasicType* sig_bt) { +void AdapterHandlerEntry::save_code(unsigned char* buffer, int length) { _saved_code = NEW_C_HEAP_ARRAY(unsigned char, length, mtCode); - _code_length = length; + _saved_code_length = length; memcpy(_saved_code, buffer, length); - _total_args_passed = total_args_passed; - _saved_sig = NEW_C_HEAP_ARRAY(BasicType, _total_args_passed, mtCode); - memcpy(_saved_sig, sig_bt, _total_args_passed * sizeof(BasicType)); } -bool AdapterHandlerEntry::compare_code(unsigned char* buffer, int length, int total_args_passed, BasicType* sig_bt) { - if (length != _code_length) { +bool AdapterHandlerEntry::compare_code(unsigned char* buffer, int length) { + if (length != _saved_code_length) { return false; } - for (int i = 0; i < length; i++) { - if (buffer[i] != _saved_code[i]) { - return false; - } - } - return true; + + return (memcmp(buffer, _saved_code, length) == 0) ? true : false; } #endif -// Create a native wrapper for this native method. The wrapper converts the -// java compiled calling convention to the native convention, handlizes -// arguments, and transitions to native. On return from the native we transition -// back to java blocking if a safepoint is in progress. -nmethod *AdapterHandlerLibrary::create_native_wrapper(methodHandle method, int compile_id) { +/** + * Create a native wrapper for this native method. The wrapper converts the + * Java-compiled calling convention to the native convention, handles + * arguments, and transitions to native. On return from the native we transition + * back to java blocking if a safepoint is in progress. + */ +void AdapterHandlerLibrary::create_native_wrapper(methodHandle method) { ResourceMark rm; nmethod* nm = NULL; @@ -2667,16 +2679,19 @@ method->has_native_function(), "must have something valid to call!"); { - // perform the work while holding the lock, but perform any printing outside the lock + // Perform the work while holding the lock, but perform any printing outside the lock MutexLocker mu(AdapterHandlerLibrary_lock); // See if somebody beat us to it nm = method->code(); - if (nm) { - return nm; + if (nm != NULL) { + return; } + const int compile_id = CompileBroker::assign_compile_id(method, CompileBroker::standard_entry_bci); + assert(compile_id > 0, "Must generate native wrapper"); + + ResourceMark rm; - BufferBlob* buf = buffer_blob(); // the temporary code buffer in CodeCache if (buf != NULL) { CodeBuffer buffer(buf); @@ -2708,16 +2723,14 @@ int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, is_outgoing); // Generate the compiled-to-native wrapper code - nm = SharedRuntime::generate_native_wrapper(&_masm, - method, - compile_id, - sig_bt, - regs, - ret_type); + nm = SharedRuntime::generate_native_wrapper(&_masm, method, compile_id, sig_bt, regs, ret_type); + + if (nm != NULL) { + method->set_code(method, nm); + } } - } - - // Must unlock before calling set_code + } // Unlock AdapterHandlerLibrary_lock + // Install the generated code. if (nm != NULL) { @@ -2725,13 +2738,11 @@ ttyLocker ttyl; CompileTask::print_compilation(tty, nm, method->is_static() ? "(static)" : ""); } - method->set_code(method, nm); nm->post_compiled_method_load_event(); } else { // CodeCache is full, disable compilation CompileBroker::handle_full_code_cache(); } - return nm; } JRT_ENTRY_NO_ASYNC(void, SharedRuntime::block_for_jni_critical(JavaThread* thread)) @@ -2749,19 +2760,20 @@ JRT_END #ifdef HAVE_DTRACE_H -// Create a dtrace nmethod for this method. The wrapper converts the -// java compiled calling convention to the native convention, makes a dummy call -// (actually nops for the size of the call instruction, which become a trap if -// probe is enabled). The returns to the caller. Since this all looks like a -// leaf no thread transition is needed. - +/** + * Create a dtrace nmethod for this method. The wrapper converts the + * Java-compiled calling convention to the native convention, makes a dummy call + * (actually nops for the size of the call instruction, which become a trap if + * probe is enabled), and finally returns to the caller. Since this all looks like a + * leaf, no thread transition is needed. + */ nmethod *AdapterHandlerLibrary::create_dtrace_nmethod(methodHandle method) { ResourceMark rm; nmethod* nm = NULL; if (PrintCompilation) { ttyLocker ttyl; - tty->print("--- n%s "); + tty->print("--- n "); method->print_short_name(tty); if (method->is_static()) { tty->print(" (static)"); @@ -2809,6 +2821,71 @@ } #endif // ndef HAVE_DTRACE_H +int SharedRuntime::convert_ints_to_longints_argcnt(int in_args_count, BasicType* in_sig_bt) { + int argcnt = in_args_count; + if (CCallingConventionRequiresIntsAsLongs) { + 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; + } + } + } else { + assert(0, "This should not be needed on this platform"); + } + + return argcnt; +} + +void SharedRuntime::convert_ints_to_longints(int i2l_argcnt, int& in_args_count, + BasicType*& in_sig_bt, VMRegPair*& in_regs) { + if (CCallingConventionRequiresIntsAsLongs) { + 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; + } else { + assert(0, "This should not be needed on this platform"); + } +} + // ------------------------------------------------------------------------- // Java-Java calling convention // (what you use when Java calls Java) diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/sharedRuntime.hpp --- a/src/share/vm/runtime/sharedRuntime.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/sharedRuntime.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -141,7 +141,7 @@ static double dabs(double f); #endif -#if defined(__SOFTFP__) || defined(PPC) +#if defined(__SOFTFP__) || defined(PPC32) static double dsqrt(double f); #endif @@ -264,8 +264,8 @@ static void register_finalizer(JavaThread* thread, oopDesc* obj); // dtrace notifications - static int dtrace_object_alloc(oopDesc* o); - static int dtrace_object_alloc_base(Thread* thread, oopDesc* o); + static int dtrace_object_alloc(oopDesc* o, int size); + static int dtrace_object_alloc_base(Thread* thread, oopDesc* o, int size); static int dtrace_method_entry(JavaThread* thread, Method* m); static int dtrace_method_exit(JavaThread* thread, Method* m); @@ -365,7 +365,25 @@ 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); + + // Compute the new number of arguments in the signature if 32 bit ints + // must be converted to longs. Needed if CCallingConventionRequiresIntsAsLongs + // is true. + 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. Needed if CCallingConventionRequiresIntsAsLongs + // is true. + 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 @@ -379,7 +397,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 @@ -622,9 +640,7 @@ // Captures code and signature used to generate this adapter when // verifing adapter equivalence. unsigned char* _saved_code; - int _code_length; - BasicType* _saved_sig; - int _total_args_passed; + int _saved_code_length; #endif void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) { @@ -634,9 +650,7 @@ _c2i_unverified_entry = c2i_unverified_entry; #ifdef ASSERT _saved_code = NULL; - _code_length = 0; - _saved_sig = NULL; - _total_args_passed = 0; + _saved_code_length = 0; #endif } @@ -649,7 +663,6 @@ address get_i2c_entry() const { return _i2c_entry; } address get_c2i_entry() const { return _c2i_entry; } address get_c2i_unverified_entry() const { return _c2i_unverified_entry; } - address base_address(); void relocate(address new_base); @@ -661,8 +674,8 @@ #ifdef ASSERT // Used to verify that code generated for shared adapters is equivalent - void save_code(unsigned char* code, int length, int total_args_passed, BasicType* sig_bt); - bool compare_code(unsigned char* code, int length, int total_args_passed, BasicType* sig_bt); + void save_code (unsigned char* code, int length); + bool compare_code(unsigned char* code, int length); #endif //virtual void print_on(outputStream* st) const; DO NOT USE @@ -681,7 +694,7 @@ static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry); - static nmethod* create_native_wrapper(methodHandle method, int compile_id); + static void create_native_wrapper(methodHandle method); static AdapterHandlerEntry* get_adapter(methodHandle method); #ifdef HAVE_DTRACE_H diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/sharedRuntimeTrans.cpp --- a/src/share/vm/runtime/sharedRuntimeTrans.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/sharedRuntimeTrans.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/sharedRuntimeTrig.cpp --- a/src/share/vm/runtime/sharedRuntimeTrig.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/sharedRuntimeTrig.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -658,7 +658,7 @@ static double __kernel_cos(double x, double y) { - double a,hz,z,r,qx; + double a,h,z,r,qx; int ix; ix = __HI(x)&0x7fffffff; /* ix = |x|'s high word*/ if(ix<0x3e400000) { /* if x < 2**27 */ @@ -675,9 +675,9 @@ __HI(qx) = ix-0x00200000; /* x/4 */ __LO(qx) = 0; } - hz = 0.5*z-qx; - a = one-qx; - return a - (hz - (z*r-x*y)); + h = 0.5*z-qx; + a = one-qx; + return a - (h - (z*r-x*y)); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/signature.cpp --- a/src/share/vm/runtime/signature.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/signature.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -32,6 +32,7 @@ #include "oops/typeArrayKlass.hpp" #include "runtime/signature.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // Implementation of SignatureIterator diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/simpleThresholdPolicy.cpp --- a/src/share/vm/runtime/simpleThresholdPolicy.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/simpleThresholdPolicy.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -142,7 +142,8 @@ count = MAX2(log2_intptr(os::active_processor_count()), 1) * 3 / 2; } set_c1_count(MAX2(count / 3, 1)); - set_c2_count(MAX2(count - count / 3, 1)); + set_c2_count(MAX2(count - c1_count(), 1)); + FLAG_SET_ERGO(intx, CICompilerCount, c1_count() + c2_count()); } void SimpleThresholdPolicy::set_carry_if_necessary(InvocationCounter *counter) { @@ -191,6 +192,10 @@ thread->is_interp_only_mode()) { return NULL; } + if (CompileTheWorld || ReplayCompiles) { + // Don't trigger other compiles in testing mode + return NULL; + } nmethod *osr_nm = NULL; handle_counter_overflow(method()); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/stackValue.cpp --- a/src/share/vm/runtime/stackValue.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/stackValue.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, 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 @@ -196,7 +196,7 @@ case T_OBJECT: _o()->print_value_on(st); - st->print(" <" INTPTR_FORMAT ">", (address)_o()); + st->print(" <" INTPTR_FORMAT ">", p2i((address)_o())); break; case T_CONFLICT: diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/stackValueCollection.cpp --- a/src/share/vm/runtime/stackValueCollection.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/stackValueCollection.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -40,6 +40,8 @@ # include "jniTypes_ppc.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + jint StackValueCollection::int_at(int slot) const { intptr_t val = at(slot)->get_int(); jint ival = *((jint*) (&val)); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/stubCodeGenerator.cpp --- a/src/share/vm/runtime/stubCodeGenerator.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/stubCodeGenerator.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -60,10 +60,10 @@ void StubCodeDesc::print_on(outputStream* st) const { - st->print(group()); + st->print("%s", group()); st->print("::"); - st->print(name()); - st->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT "[ (%d bytes)", begin(), end(), size_in_bytes()); + st->print("%s", name()); + st->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT "[ (%d bytes)", p2i(begin()), p2i(end()), size_in_bytes()); } // Implementation of StubCodeGenerator diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/stubRoutines.hpp --- a/src/share/vm/runtime/stubRoutines.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/stubRoutines.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/sweeper.cpp --- a/src/share/vm/runtime/sweeper.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/sweeper.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -41,6 +41,8 @@ #include "utilities/ticks.inline.hpp" #include "utilities/xmlstream.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef ASSERT #define SWEEP(nm) record_sweep(nm, __LINE__) @@ -129,6 +131,7 @@ nmethod* NMethodSweeper::_current = NULL; // Current nmethod long NMethodSweeper::_traversals = 0; // Stack scan count, also sweep ID. +long NMethodSweeper::_total_nof_code_cache_sweeps = 0; // Total number of full sweeps of the code cache long NMethodSweeper::_time_counter = 0; // Virtual time used to periodically invoke sweeper long NMethodSweeper::_last_sweep = 0; // Value of _time_counter when the last sweep happened int NMethodSweeper::_seen = 0; // Nof. nmethod we have currently processed in current pass of CodeCache @@ -143,13 +146,16 @@ // 1) alive -> not_entrant // 2) not_entrant -> zombie // 3) zombie -> marked_for_reclamation +int NMethodSweeper::_hotness_counter_reset_val = 0; -int NMethodSweeper::_total_nof_methods_reclaimed = 0; // Accumulated nof methods flushed -Tickspan NMethodSweeper::_total_time_sweeping; // Accumulated time sweeping -Tickspan NMethodSweeper::_total_time_this_sweep; // Total time this sweep -Tickspan NMethodSweeper::_peak_sweep_time; // Peak time for a full sweep -Tickspan NMethodSweeper::_peak_sweep_fraction_time; // Peak time sweeping one fraction -int NMethodSweeper::_hotness_counter_reset_val = 0; +long NMethodSweeper::_total_nof_methods_reclaimed = 0; // Accumulated nof methods flushed +long NMethodSweeper::_total_nof_c2_methods_reclaimed = 0; // Accumulated nof methods flushed +size_t NMethodSweeper::_total_flushed_size = 0; // Total number of bytes flushed from the code cache +Tickspan NMethodSweeper::_total_time_sweeping; // Accumulated time sweeping +Tickspan NMethodSweeper::_total_time_this_sweep; // Total time this sweep +Tickspan NMethodSweeper::_peak_sweep_time; // Peak time for a full sweep +Tickspan NMethodSweeper::_peak_sweep_fraction_time; // Peak time sweeping one fraction + class MarkActivationClosure: public CodeBlobClosure { @@ -257,9 +263,14 @@ // Large ReservedCodeCacheSize: (e.g., 256M + code Cache is 90% full). The formula // computes: (256 / 16) - 10 = 6. if (!_should_sweep) { - int time_since_last_sweep = _time_counter - _last_sweep; - double wait_until_next_sweep = (ReservedCodeCacheSize / (16 * M)) - time_since_last_sweep - - CodeCache::reverse_free_ratio(); + const int time_since_last_sweep = _time_counter - _last_sweep; + // ReservedCodeCacheSize has an 'unsigned' type. We need a 'signed' type for max_wait_time, + // since 'time_since_last_sweep' can be larger than 'max_wait_time'. If that happens using + // an unsigned type would cause an underflow (wait_until_next_sweep becomes a large positive + // value) that disables the intended periodic sweeps. + const int max_wait_time = ReservedCodeCacheSize / (16 * M); + double wait_until_next_sweep = max_wait_time - time_since_last_sweep - CodeCache::reverse_free_ratio(); + assert(wait_until_next_sweep <= (double)max_wait_time, "Calculation of code cache sweeper interval is incorrect"); if ((wait_until_next_sweep <= 0.0) || !CompileBroker::should_compile_new_jobs()) { _should_sweep = true; @@ -287,6 +298,7 @@ // We are done with sweeping the code cache once. if (_sweep_fractions_left == 0) { + _total_nof_code_cache_sweeps++; _last_sweep = _time_counter; // Reset flag; temporarily disables sweeper _should_sweep = false; @@ -299,7 +311,8 @@ _bytes_changed = 0; } } - _sweep_started = 0; + // Release work, because another compiler thread could continue. + OrderAccess::release_store((int*)&_sweep_started, 0); } } @@ -373,6 +386,7 @@ _total_time_sweeping += sweep_time; _total_time_this_sweep += sweep_time; _peak_sweep_fraction_time = MAX2(sweep_time, _peak_sweep_fraction_time); + _total_flushed_size += freed_memory; _total_nof_methods_reclaimed += _flushed_count; EventSweepCodeCache event(UNTIMED); @@ -512,6 +526,9 @@ tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (marked for reclamation) being flushed", nm->compile_id(), nm); } freed_memory = nm->total_size(); + if (nm->is_compiled_by_c2()) { + _total_nof_c2_methods_reclaimed++; + } release_nmethod(nm); _flushed_count++; } else { @@ -550,6 +567,9 @@ SWEEP(nm); // No inline caches will ever point to osr methods, so we can just remove it freed_memory = nm->total_size(); + if (nm->is_compiled_by_c2()) { + _total_nof_c2_methods_reclaimed++; + } release_nmethod(nm); _flushed_count++; } else { @@ -615,7 +635,7 @@ tty->vprint(format, ap); va_end(ap); } - tty->print_cr(s.as_string()); + tty->print_cr("%s", s.as_string()); } if (LogCompilation && (xtty != NULL)) { @@ -632,8 +652,18 @@ xtty->vprint(format, ap); va_end(ap); } - xtty->print(s.as_string()); + xtty->print("%s", s.as_string()); xtty->stamp(); xtty->end_elem(); } } + +void NMethodSweeper::print() { + ttyLocker ttyl; + tty->print_cr("Code cache sweeper statistics:"); + tty->print_cr(" Total sweep time: %1.0lfms", (double)_total_time_sweeping.value()/1000000); + tty->print_cr(" Total number of full sweeps: %ld", _total_nof_code_cache_sweeps); + tty->print_cr(" Total number of flushed methods: %ld(%ld C2 methods)", _total_nof_methods_reclaimed, + _total_nof_c2_methods_reclaimed); + tty->print_cr(" Total size of flushed methods: " SIZE_FORMAT "kB", _total_flushed_size/K); +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/sweeper.hpp --- a/src/share/vm/runtime/sweeper.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/sweeper.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -54,28 +54,33 @@ // is full. class NMethodSweeper : public AllStatic { - static long _traversals; // Stack scan count, also sweep ID. - static long _time_counter; // Virtual time used to periodically invoke sweeper - static long _last_sweep; // Value of _time_counter when the last sweep happened - static nmethod* _current; // Current nmethod - static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache - static int _flushed_count; // Nof. nmethods flushed in current sweep - static int _zombified_count; // Nof. nmethods made zombie in current sweep - static int _marked_for_reclamation_count; // Nof. nmethods marked for reclaim in current sweep + static long _traversals; // Stack scan count, also sweep ID. + static long _total_nof_code_cache_sweeps; // Total number of full sweeps of the code cache + static long _time_counter; // Virtual time used to periodically invoke sweeper + static long _last_sweep; // Value of _time_counter when the last sweep happened + static nmethod* _current; // Current nmethod + static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache + static int _flushed_count; // Nof. nmethods flushed in current sweep + static int _zombified_count; // Nof. nmethods made zombie in current sweep + static int _marked_for_reclamation_count; // Nof. nmethods marked for reclaim in current sweep - static volatile int _sweep_fractions_left; // Nof. invocations left until we are completed with this pass - static volatile int _sweep_started; // Flag to control conc sweeper - static volatile bool _should_sweep; // Indicates if we should invoke the sweeper - static volatile int _bytes_changed; // Counts the total nmethod size if the nmethod changed from: - // 1) alive -> not_entrant - // 2) not_entrant -> zombie - // 3) zombie -> marked_for_reclamation + static volatile int _sweep_fractions_left; // Nof. invocations left until we are completed with this pass + static volatile int _sweep_started; // Flag to control conc sweeper + static volatile bool _should_sweep; // Indicates if we should invoke the sweeper + static volatile int _bytes_changed; // Counts the total nmethod size if the nmethod changed from: + // 1) alive -> not_entrant + // 2) not_entrant -> zombie + // 3) zombie -> marked_for_reclamation // Stat counters - static int _total_nof_methods_reclaimed; // Accumulated nof methods flushed - static Tickspan _total_time_sweeping; // Accumulated time sweeping - static Tickspan _total_time_this_sweep; // Total time this sweep - static Tickspan _peak_sweep_time; // Peak time for a full sweep - static Tickspan _peak_sweep_fraction_time; // Peak time sweeping one fraction + static long _total_nof_methods_reclaimed; // Accumulated nof methods flushed + static long _total_nof_c2_methods_reclaimed; // Accumulated nof C2-compiled methods flushed + static size_t _total_flushed_size; // Total size of flushed methods + static int _hotness_counter_reset_val; + + static Tickspan _total_time_sweeping; // Accumulated time sweeping + static Tickspan _total_time_this_sweep; // Total time this sweep + static Tickspan _peak_sweep_time; // Peak time for a full sweep + static Tickspan _peak_sweep_fraction_time; // Peak time sweeping one fraction static int process_nmethod(nmethod *nm); static void release_nmethod(nmethod* nm); @@ -83,15 +88,13 @@ static bool sweep_in_progress(); static void sweep_code_cache(); - static int _hotness_counter_reset_val; - public: static long traversal_count() { return _traversals; } static int total_nof_methods_reclaimed() { return _total_nof_methods_reclaimed; } static const Tickspan total_time_sweeping() { return _total_time_sweeping; } static const Tickspan peak_sweep_time() { return _peak_sweep_time; } static const Tickspan peak_sweep_fraction_time() { return _peak_sweep_fraction_time; } - static void log_sweep(const char* msg, const char* format = NULL, ...); + static void log_sweep(const char* msg, const char* format = NULL, ...) ATTRIBUTE_PRINTF(2, 3); #ifdef ASSERT @@ -105,10 +108,10 @@ static void mark_active_nmethods(); // Invoked at the end of each safepoint static void possibly_sweep(); // Compiler threads call this to sweep - static int sort_nmethods_by_hotness(nmethod** nm1, nmethod** nm2); static int hotness_counter_reset_val(); static void report_state_change(nmethod* nm); static void possibly_enable_sweeper(); + static void print(); // Printing/debugging }; #endif // SHARE_VM_RUNTIME_SWEEPER_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/synchronizer.cpp --- a/src/share/vm/runtime/synchronizer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/synchronizer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -53,13 +53,15 @@ # include "os_bsd.inline.hpp" #endif -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(PPC64) // Need to inhibit inlining for older versions of GCC to avoid build-time failures #define ATTR __attribute__((noinline)) #else #define ATTR #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // 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(), diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/task.cpp --- a/src/share/vm/runtime/task.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/task.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -105,7 +105,6 @@ _counter(0), _interval((int) interval_time) { // Sanity check the interval time assert(_interval >= PeriodicTask::min_interval && - _interval <= PeriodicTask::max_interval && _interval % PeriodicTask::interval_gran == 0, "improper PeriodicTask interval time"); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/thread.cpp --- a/src/share/vm/runtime/thread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/thread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -115,6 +115,11 @@ #include "opto/c2compiler.hpp" #include "opto/idealGraphPrinter.hpp" #endif +#if INCLUDE_RTM_OPT +#include "runtime/rtmLocking.hpp" +#endif + +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC #ifdef DTRACE_ENABLED @@ -244,7 +249,6 @@ debug_only(_allow_allocation_count = 0;) NOT_PRODUCT(_allow_safepoint_count = 0;) NOT_PRODUCT(_skip_gcalot = false;) - CHECK_UNHANDLED_OOPS_ONLY(_gc_locked_out_count = 0;) _jvmti_env_iteration_count = 0; set_allocated_bytes(0); _vm_operation_started_count = 0; @@ -320,6 +324,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). @@ -1451,9 +1458,9 @@ void JavaThread::initialize() { // Initialize fields - // Set the claimed par_id to -1 (ie not claiming any par_ids) - set_claimed_par_id(-1); - + // Set the claimed par_id to UINT_MAX (ie not claiming any par_ids) + set_claimed_par_id(UINT_MAX); + _buffer_blob = NULL; set_saved_exception_pc(NULL); set_threadObj(NULL); @@ -3665,6 +3672,8 @@ // debug stuff, that does not work until all basic classes have been initialized. set_init_completed(); + Metaspace::post_initialize(); + #ifndef USDT2 HS_DTRACE_PROBE(hotspot, vm__init__end); #else /* USDT2 */ @@ -3781,6 +3790,10 @@ BiasedLocking::init(); +#if INCLUDE_RTM_OPT + RTMLockingCounters::init(); +#endif + if (JDK_Version::current().post_vm_init_hook_enabled()) { call_postVMInitHook(THREAD); // The Java side of PostVMInitHook.run must deal with all @@ -4111,14 +4124,8 @@ // GC vm_operations can get caught at the safepoint, and the // heap is unparseable if they are caught. Grab the Heap_lock // to prevent this. The GC vm_operations will not be able to - // queue until after the vm thread is dead. - // After this point, we'll never emerge out of the safepoint before - // the VM exits, so concurrent GC threads do not need to be explicitly - // stopped; they remain inactive until the process exits. - // Note: some concurrent G1 threads may be running during a safepoint, - // but these will not be accessing the heap, just some G1-specific side - // data structures that are not accessed by any other threads but them - // after this point in a terminal safepoint. + // queue until after the vm thread is dead. After this point, + // we'll never emerge out of the safepoint before the VM exits. MutexLocker ml(Heap_lock); @@ -4426,7 +4433,7 @@ // Threads::print_on() is called at safepoint by VM_PrintThreads operation. void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks) { char buf[32]; - st->print_cr(os::local_time_string(buf, sizeof(buf))); + st->print_raw_cr(os::local_time_string(buf, sizeof(buf))); st->print_cr("Full thread dump %s (%s %s):", Abstract_VM_Version::vm_name(), @@ -4563,9 +4570,7 @@ ++ctr ; if ((ctr & 0xFFF) == 0 || !os::is_MP()) { if (Yields > 5) { - // Consider using a simple NakedSleep() instead. - // Then SpinAcquire could be called by non-JVM threads - Thread::current()->_ParkEvent->park(1) ; + os::naked_short_sleep(1); } else { os::NakedYield() ; ++Yields ; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/thread.hpp --- a/src/share/vm/runtime/thread.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/thread.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -249,9 +249,6 @@ // Used by SkipGCALot class. NOT_PRODUCT(bool _skip_gcalot;) // Should we elide gc-a-lot? - // Record when GC is locked out via the GC_locker mechanism - CHECK_UNHANDLED_OOPS_ONLY(int _gc_locked_out_count;) - friend class No_Alloc_Verifier; friend class No_Safepoint_Verifier; friend class Pause_No_Safepoint_Verifier; @@ -400,7 +397,6 @@ void clear_unhandled_oops() { if (CheckUnhandledOops) unhandled_oops()->clear_unhandled_oops(); } - bool is_gc_locked_out() { return _gc_locked_out_count > 0; } #endif // CHECK_UNHANDLED_OOPS #ifndef PRODUCT @@ -702,7 +698,7 @@ NamedThread(); ~NamedThread(); // May only be called once per thread. - void set_name(const char* format, ...); + void set_name(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); virtual bool is_Named_thread() const { return true; } virtual char* name() const { return _name == NULL ? (char*)"Unknown Thread" : _name; } JavaThread *processed_thread() { return _processed_thread; } @@ -931,10 +927,15 @@ static void collect_counters(typeArrayOop array); private: #endif // GRAAL - StackGuardState _stack_guard_state; nmethod* _scanned_nmethod; // nmethod being scanned by the sweeper + 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 // code) @@ -1089,20 +1090,31 @@ // Last frame anchor routines - JavaFrameAnchor* frame_anchor(void) { return &_anchor; } + JavaFrameAnchor* frame_anchor(void) { return &_anchor; } // last_Java_sp - bool has_last_Java_frame() const { return _anchor.has_last_Java_frame(); } - intptr_t* last_Java_sp() const { return _anchor.last_Java_sp(); } + bool has_last_Java_frame() const { return _anchor.has_last_Java_frame(); } + intptr_t* last_Java_sp() const { return _anchor.last_Java_sp(); } // last_Java_pc - address last_Java_pc(void) { return _anchor.last_Java_pc(); } + address last_Java_pc(void) { return _anchor.last_Java_pc(); } // Safepoint support +#ifndef PPC64 JavaThreadState thread_state() const { return _thread_state; } - void set_thread_state(JavaThreadState s) { _thread_state=s; } - ThreadSafepointState *safepoint_state() const { return _safepoint_state; } + void set_thread_state(JavaThreadState s) { _thread_state = s; } +#else + // 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); + } +#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(); } @@ -1384,6 +1396,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; } @@ -1423,6 +1443,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 ); } @@ -1786,6 +1807,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 @@ -1825,12 +1849,12 @@ void set_done_attaching_via_jni() { _jni_attach_state = _attached_via_jni; OrderAccess::fence(); } private: // This field is used to determine if a thread has claimed - // a par_id: it is -1 if the thread has not claimed a par_id; + // a par_id: it is UINT_MAX if the thread has not claimed a par_id; // otherwise its value is the par_id that has been claimed. - int _claimed_par_id; + uint _claimed_par_id; public: - int get_claimed_par_id() { return _claimed_par_id; } - void set_claimed_par_id(int id) { _claimed_par_id = id;} + uint get_claimed_par_id() { return _claimed_par_id; } + void set_claimed_par_id(uint id) { _claimed_par_id = id;} }; // Inline implementation of JavaThread::current diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/thread.inline.hpp --- a/src/share/vm/runtime/thread.inline.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/thread.inline.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/threadLocalStorage.hpp --- a/src/share/vm/runtime/threadLocalStorage.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/threadLocalStorage.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/timer.cpp --- a/src/share/vm/runtime/timer.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/timer.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -217,7 +220,7 @@ _logfile->print("[Error in TraceCPUTime]"); } if (_print_cr) { - _logfile->print_cr(""); + _logfile->cr(); } _logfile->flush(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/unhandledOops.cpp --- a/src/share/vm/runtime/unhandledOops.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/unhandledOops.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -31,6 +31,8 @@ #include "runtime/unhandledOops.hpp" #include "utilities/globalDefinitions.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef CHECK_UNHANDLED_OOPS const int free_list_size = 256; @@ -113,9 +115,7 @@ void UnhandledOops::clear_unhandled_oops() { assert (CheckUnhandledOops, "should only be called with checking option"); - if (_thread->is_gc_locked_out()) { - return; - } + for (int k = 0; k < _oop_list->length(); k++) { UnhandledOopEntry entry = _oop_list->at(k); // If an entry is on the unhandled oop list but isn't on the stack diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vframe.cpp --- a/src/share/vm/runtime/vframe.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vframe.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -46,6 +46,8 @@ #include "runtime/vframeArray.hpp" #include "runtime/vframe_hp.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + vframe::vframe(const frame* fr, const RegisterMap* reg_map, JavaThread* thread) : _reg_map(reg_map), _thread(thread) { assert(fr != NULL, "must have frame"); @@ -197,6 +199,7 @@ continue; } if (monitor->owner() != NULL) { + // the monitor is associated with an object, i.e., it is locked // First, assume we have the monitor locked. If we haven't found an // owned monitor before and this is the first frame, then we need to @@ -207,7 +210,11 @@ if (!found_first_monitor && frame_count == 0) { markOop mark = monitor->owner()->mark(); if (mark->has_monitor() && - mark->monitor() == thread()->current_pending_monitor()) { + ( // we have marked ourself as pending on this monitor + mark->monitor() == thread()->current_pending_monitor() || + // we are not the owner of this monitor + !mark->monitor()->is_entered(thread()) + )) { lock_state = "waiting to lock"; } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vframe.hpp --- a/src/share/vm/runtime/vframe.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vframe.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -399,7 +399,7 @@ if (WizardMode) { tty->print_cr("Error in fill_from_frame: pc_desc for " INTPTR_FORMAT " not found or invalid at %d", - _frame.pc(), decode_offset); + p2i(_frame.pc()), decode_offset); nm()->print(); nm()->method()->print_codes(); nm()->print_code(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vframeArray.cpp --- a/src/share/vm/runtime/vframeArray.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vframeArray.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -43,6 +43,7 @@ #include "opto/runtime.hpp" #endif +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC int vframeArrayElement:: bci(void) const { return (_bci == SynchronizationEntryBCI ? 0 : _bci); } @@ -454,24 +455,20 @@ } -int vframeArrayElement::on_stack_size(int caller_actual_parameters, - int callee_parameters, +int vframeArrayElement::on_stack_size(int callee_parameters, int callee_locals, bool is_top_frame, - bool is_bottom_frame, int popframe_extra_stack_expression_els) const { assert(method()->max_locals() == locals()->size(), "just checking"); int locks = monitors() == NULL ? 0 : monitors()->number_of_monitors(); int temps = expressions()->size(); - return Interpreter::size_activation(method(), + return Interpreter::size_activation(method()->max_stack(), temps + callee_parameters, popframe_extra_stack_expression_els, locks, - caller_actual_parameters, callee_parameters, callee_locals, - is_top_frame, - is_bottom_frame); + is_top_frame); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vframeArray.hpp --- a/src/share/vm/runtime/vframeArray.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vframeArray.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -85,10 +85,8 @@ // Returns the on stack word size for this frame // callee_parameters is the number of callee locals residing inside this frame - int on_stack_size(int caller_actual_parameters, - int callee_parameters, + int on_stack_size(int callee_parameters, int callee_locals, - bool is_bottom_frame, bool is_top_frame, int popframe_extra_stack_expression_els) const; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/virtualspace.cpp --- a/src/share/vm/runtime/virtualspace.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/virtualspace.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -36,10 +36,14 @@ #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 +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // ReservedSpace diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vmStructs.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -151,6 +151,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 @@ -208,10 +211,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. @@ -252,7 +258,7 @@ typedef Hashtable KlassHashtable; typedef HashtableEntry KlassHashtableEntry; typedef TwoOopHashtable SymbolTwoOopHashtable; -typedef BinaryTreeDictionary MetablockTreeDictionary; +typedef BinaryTreeDictionary > MetablockTreeDictionary; //-------------------------------------------------------------------------------- // VM_STRUCTS @@ -1867,6 +1873,8 @@ declare_c2_type(MemBarNode, MultiNode) \ declare_c2_type(MemBarAcquireNode, MemBarNode) \ declare_c2_type(MemBarReleaseNode, MemBarNode) \ + declare_c2_type(LoadFenceNode, MemBarNode) \ + declare_c2_type(StoreFenceNode, MemBarNode) \ declare_c2_type(MemBarVolatileNode, MemBarNode) \ declare_c2_type(MemBarCPUOrderNode, MemBarNode) \ declare_c2_type(InitializeNode, MemBarNode) \ @@ -1991,15 +1999,6 @@ declare_c2_type(CmpF3Node, CmpFNode) \ declare_c2_type(CmpDNode, CmpNode) \ declare_c2_type(CmpD3Node, CmpDNode) \ - declare_c2_type(MathExactNode, MultiNode) \ - declare_c2_type(MathExactINode, MathExactNode) \ - declare_c2_type(AddExactINode, MathExactINode) \ - declare_c2_type(AddExactLNode, MathExactLNode) \ - declare_c2_type(SubExactINode, MathExactINode) \ - declare_c2_type(SubExactLNode, MathExactLNode) \ - declare_c2_type(NegExactINode, MathExactINode) \ - declare_c2_type(MulExactINode, MathExactINode) \ - declare_c2_type(FlagsProjNode, ProjNode) \ declare_c2_type(BoolNode, Node) \ declare_c2_type(AbsNode, Node) \ declare_c2_type(AbsINode, AbsNode) \ @@ -2080,6 +2079,15 @@ declare_c2_type(ExtractLNode, ExtractNode) \ declare_c2_type(ExtractFNode, ExtractNode) \ declare_c2_type(ExtractDNode, ExtractNode) \ + declare_c2_type(OverflowNode, CmpNode) \ + declare_c2_type(OverflowINode, OverflowNode) \ + declare_c2_type(OverflowAddINode, OverflowINode) \ + declare_c2_type(OverflowSubINode, OverflowINode) \ + declare_c2_type(OverflowMulINode, OverflowINode) \ + declare_c2_type(OverflowLNode, OverflowNode) \ + declare_c2_type(OverflowAddLNode, OverflowLNode) \ + declare_c2_type(OverflowSubLNode, OverflowLNode) \ + declare_c2_type(OverflowMulLNode, OverflowLNode) \ \ /*********************/ \ /* Adapter Blob Entries */ \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vmThread.cpp --- a/src/share/vm/runtime/vmThread.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vmThread.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -46,6 +46,8 @@ HS_DTRACE_PROBE_DECL3(hotspot, vmops__end, char *, uintptr_t, int); #endif /* !USDT2 */ +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Dummy VM operation to act as first element in our circular double-linked list class VM_Dummy: public VM_Operation { VMOp_Type type() const { return VMOp_Dummy; } @@ -316,6 +318,9 @@ _terminate_lock->notify(); } + // Thread destructor usually does this. + ThreadLocalStorage::set_thread(NULL); + // Deletion must be done synchronously by the JNI DestroyJavaVM thread // so that the VMThread deletion completes before the main thread frees // up the CodeHeap. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vm_operations.cpp --- a/src/share/vm/runtime/vm_operations.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vm_operations.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -39,6 +39,8 @@ #include "services/threadService.hpp" #include "trace/tracing.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #define VM_OP_NAME_INITIALIZE(name) #name, const char* VM_Operation::_names[VM_Operation::VMOp_Terminating] = \ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vm_operations.hpp --- a/src/share/vm/runtime/vm_operations.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vm_operations.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -94,6 +94,8 @@ template(JFRCheckpoint) \ template(Exit) \ template(LinuxDllLoad) \ + template(RotateGCLog) \ + template(WhiteBoxOperation) \ class VM_Operation: public CHeapObj { public: @@ -398,4 +400,15 @@ void doit(); }; + +class VM_RotateGCLog: public VM_Operation { + private: + outputStream* _out; + + public: + VM_RotateGCLog(outputStream* st) : _out(st) {} + VMOp_Type type() const { return VMOp_RotateGCLog; } + void doit() { gclog_or_tty->rotate_log(true, _out); } +}; + #endif // SHARE_VM_RUNTIME_VM_OPERATIONS_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/runtime/vm_version.cpp --- a/src/share/vm/runtime/vm_version.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/runtime/vm_version.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -182,6 +182,7 @@ #define OS LINUX_ONLY("linux") \ WINDOWS_ONLY("windows") \ SOLARIS_ONLY("solaris") \ + AIX_ONLY("aix") \ BSD_ONLY("bsd") #ifdef ZERO @@ -191,7 +192,8 @@ IA64_ONLY("ia64") \ AMD64_ONLY("amd64") \ ARM_ONLY("arm") \ - PPC_ONLY("ppc") \ + PPC32_ONLY("ppc") \ + PPC64_ONLY("ppc64") \ SPARC_ONLY("sparc") #endif // ZERO @@ -243,6 +245,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 @@ -255,7 +260,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/attachListener.cpp --- a/src/share/vm/services/attachListener.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/attachListener.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -282,6 +282,20 @@ return JNI_ERR; } } + + if (strncmp(name, "MaxHeapFreeRatio", 17) == 0) { + FormatBuffer<80> err_msg("%s", ""); + if (!Arguments::verify_MaxHeapFreeRatio(err_msg, value)) { + out->print_cr("%s", err_msg.buffer()); + return JNI_ERR; + } + } else if (strncmp(name, "MinHeapFreeRatio", 17) == 0) { + FormatBuffer<80> err_msg("%s", ""); + if (!Arguments::verify_MinHeapFreeRatio(err_msg, value)) { + out->print_cr("%s", err_msg.buffer()); + return JNI_ERR; + } + } bool res = CommandLineFlags::uintxAtPut((char*)name, &value, Flag::ATTACH_ON_DEMAND); if (! res) { out->print_cr("setting flag %s failed", name); @@ -367,7 +381,7 @@ Flag* f = Flag::find_flag((char*)name, strlen(name)); if (f) { f->print_as_flag(out); - out->print_cr(""); + out->cr(); } else { out->print_cr("no such flag '%s'", name); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/classLoadingService.cpp --- a/src/share/vm/services/classLoadingService.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/classLoadingService.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -157,7 +157,7 @@ if (TraceClassUnloading) { ResourceMark rm; - tty->print_cr("[Unloading class %s " INTPTR_FORMAT "]", k->external_name(), k); + tty->print_cr("[Unloading class %s " INTPTR_FORMAT "]", k->external_name(), p2i(k)); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/diagnosticCommand.cpp --- a/src/share/vm/services/diagnosticCommand.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/diagnosticCommand.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -32,6 +32,8 @@ #include "services/management.hpp" #include "utilities/macros.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + void DCmdRegistrant::register_dcmds(){ // Registration of the diagnostic commands // First argument specifies which interfaces will export the command @@ -53,6 +55,7 @@ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); #endif // INCLUDE_SERVICES DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); // Enhanced JMX Agent Support // These commands won't be exported via the DiagnosticCommandMBean until an @@ -98,7 +101,7 @@ if (factory != NULL) { output()->print_cr("%s%s", factory->name(), factory->is_enabled() ? "" : " [disabled]"); - output()->print_cr(factory->description()); + output()->print_cr("%s", factory->description()); output()->print_cr("\nImpact: %s", factory->impact()); JavaPermission p = factory->permission(); if(p._class != NULL) { @@ -650,3 +653,11 @@ JavaCalls::call_static(&result, ik, vmSymbols::stopRemoteAgent_name(), vmSymbols::void_method_signature(), CHECK); } +void RotateGCLogDCmd::execute(DCmdSource source, TRAPS) { + if (UseGCLogFileRotation) { + VM_RotateGCLog rotateop(output()); + VMThread::execute(&rotateop); + } else { + output()->print_cr("Target VM does not support GC log file rotation."); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/diagnosticCommand.hpp --- a/src/share/vm/services/diagnosticCommand.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/diagnosticCommand.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -360,4 +360,21 @@ virtual void execute(DCmdSource source, TRAPS); }; +class RotateGCLogDCmd : public DCmd { +public: + RotateGCLogDCmd(outputStream* output, bool heap) : DCmd(output, heap) {} + static const char* name() { return "GC.rotate_log"; } + static const char* description() { + return "Force the GC log file to be rotated."; + } + static const char* impact() { return "Low"; } + virtual void execute(DCmdSource source, TRAPS); + static int num_arguments() { return 0; } + static const JavaPermission permission() { + JavaPermission p = {"java.lang.management.ManagementPermission", + "control", NULL}; + return p; + } +}; + #endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/diagnosticFramework.cpp --- a/src/share/vm/services/diagnosticFramework.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/diagnosticFramework.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -259,7 +259,7 @@ } arg = arg->next(); } - out->print_cr(""); + out->cr(); if (_arguments_list != NULL) { out->print_cr("\nArguments:"); arg = _arguments_list; @@ -268,7 +268,7 @@ arg->is_mandatory() ? "" : "[optional]", arg->description(), arg->type()); if (arg->has_default()) { - out->print(arg->default_string()); + out->print("%s", arg->default_string()); } else { out->print("no default value"); } @@ -284,7 +284,7 @@ arg->is_mandatory() ? "" : "[optional]", arg->description(), arg->type()); if (arg->has_default()) { - out->print(arg->default_string()); + out->print("%s", arg->default_string()); } else { out->print("no default value"); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/heapDumper.cpp --- a/src/share/vm/services/heapDumper.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/heapDumper.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -1830,6 +1830,7 @@ } // dump the heap to given path. +PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL int HeapDumper::dump(const char* path) { assert(path != NULL && strlen(path) > 0, "path missing"); @@ -1870,7 +1871,10 @@ char msg[256]; sprintf(msg, "Heap dump file created [%s bytes in %3.3f secs]", JLONG_FORMAT, timer()->seconds()); +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL tty->print_cr(msg, writer.bytes_written()); +PRAGMA_DIAG_POP } else { tty->print_cr("Dump file is incomplete: %s", writer.error()); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/lowMemoryDetector.cpp --- a/src/share/vm/services/lowMemoryDetector.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/lowMemoryDetector.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -353,7 +353,7 @@ #ifndef PRODUCT void SensorInfo::print() { - tty->print_cr("%s count = " SIZE_FORMAT " pending_triggers = %ld pending_clears = %ld", + tty->print_cr("%s count = " SIZE_FORMAT " pending_triggers = %d pending_clears = %d", (_sensor_on ? "on" : "off"), _sensor_count, _pending_trigger_count, _pending_clear_count); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/management.cpp --- a/src/share/vm/services/management.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/management.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -55,6 +55,8 @@ #include "services/threadService.hpp" #include "utilities/macros.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + PerfVariable* Management::_begin_vm_creation_time = NULL; PerfVariable* Management::_end_vm_creation_time = NULL; PerfVariable* Management::_vm_init_done_time = NULL; @@ -1830,6 +1832,18 @@ succeed = CommandLineFlags::intxAtPut(name, &ivalue, Flag::MANAGEMENT); } else if (flag->is_uintx()) { uintx uvalue = (uintx)new_value.j; + + if (strncmp(name, "MaxHeapFreeRatio", 17) == 0) { + FormatBuffer<80> err_msg("%s", ""); + if (!Arguments::verify_MaxHeapFreeRatio(err_msg, uvalue)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), err_msg.buffer()); + } + } else if (strncmp(name, "MinHeapFreeRatio", 17) == 0) { + FormatBuffer<80> err_msg("%s", ""); + if (!Arguments::verify_MinHeapFreeRatio(err_msg, uvalue)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), err_msg.buffer()); + } + } succeed = CommandLineFlags::uintxAtPut(name, &uvalue, Flag::MANAGEMENT); } else if (flag->is_uint64_t()) { uint64_t uvalue = (uint64_t)new_value.j; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/memReporter.cpp --- a/src/share/vm/services/memReporter.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/memReporter.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -28,6 +28,8 @@ #include "services/memPtrArray.hpp" #include "services/memTracker.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + const char* BaselineOutputer::memory_unit(size_t scale) { switch(scale) { case K: return "KB"; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/memSnapshot.cpp --- a/src/share/vm/services/memSnapshot.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/memSnapshot.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -31,6 +31,8 @@ #include "services/memSnapshot.hpp" #include "services/memTracker.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef ASSERT void decode_pointer_record(MemPointerRecord* rec) { @@ -733,7 +735,7 @@ if (os::dll_address_to_function_name(ex->pc(), buf, sizeof(buf), NULL)) { tty->print_cr("\t%s", buf); } else { - tty->print_cr(""); + tty->cr(); } } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/memTrackWorker.cpp --- a/src/share/vm/services/memTrackWorker.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/memTrackWorker.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -43,7 +43,7 @@ // create thread uses cgc thread type for now. We should revisit // the option, or create new thread type. _has_error = !os::create_thread(this, os::cgc_thread); - set_name("MemTrackWorker", 0); + set_name("MemTrackWorker"); // initial generation circuit buffer if (!has_error()) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/memoryPool.cpp --- a/src/share/vm/services/memoryPool.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/memoryPool.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -268,7 +268,7 @@ } size_t MetaspacePool::used_in_bytes() { - return MetaspaceAux::allocated_used_bytes(); + return MetaspaceAux::used_bytes(); } size_t MetaspacePool::calculate_max_size() const { @@ -280,7 +280,7 @@ MemoryPool("Compressed Class Space", NonHeap, 0, CompressedClassSpaceSize, true, false) { } size_t CompressedKlassSpacePool::used_in_bytes() { - return MetaspaceAux::allocated_used_bytes(Metaspace::ClassType); + return MetaspaceAux::used_bytes(Metaspace::ClassType); } MemoryUsage CompressedKlassSpacePool::get_memory_usage() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/nmtDCmd.cpp --- a/src/share/vm/services/nmtDCmd.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/nmtDCmd.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -128,7 +128,7 @@ // native memory tracking has to be on if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) { // if it is not on, what's the reason? - output()->print_cr(MemTracker::reason()); + output()->print_cr("%s", MemTracker::reason()); return; } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/services/threadService.cpp --- a/src/share/vm/services/threadService.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/services/threadService.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -37,6 +37,8 @@ #include "runtime/vm_operations.hpp" #include "services/threadService.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // TODO: we need to define a naming convention for perf counters // to distinguish counters for: // - standard JSR174 use diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/shark/llvmHeaders.hpp --- a/src/share/vm/shark/llvmHeaders.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/shark/llvmHeaders.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -36,21 +36,43 @@ #endif #include +#include + +// includes specific to each version +#if SHARK_LLVM_VERSION <= 31 +#include +#include #include #include #include -#include #include #include #include -#if SHARK_LLVM_VERSION <= 31 -#include -#else +#elif SHARK_LLVM_VERSION <= 32 #include +#include +#include +#include +#include +#include +#include +#include +#else // SHARK_LLVM_VERSION <= 34 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif + +// common includes #include #include -#include #include #include #include diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/shark/sharkMemoryManager.cpp --- a/src/share/vm/shark/sharkMemoryManager.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/shark/sharkMemoryManager.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -59,18 +59,6 @@ entry->set_code_limit(FunctionEnd); } -unsigned char* SharkMemoryManager::startExceptionTable(const Function* F, - uintptr_t& ActualSize) { - return mm()->startExceptionTable(F, ActualSize); -} - -void SharkMemoryManager::endExceptionTable(const Function* F, - unsigned char* TableStart, - unsigned char* TableEnd, - unsigned char* FrameRegister) { - mm()->endExceptionTable(F, TableStart, TableEnd, FrameRegister); -} - void SharkMemoryManager::setMemoryWritable() { mm()->setMemoryWritable(); } @@ -79,10 +67,6 @@ mm()->setMemoryExecutable(); } -void SharkMemoryManager::deallocateExceptionTable(void *ptr) { - mm()->deallocateExceptionTable(ptr); -} - void SharkMemoryManager::deallocateFunctionBody(void *ptr) { mm()->deallocateFunctionBody(ptr); } @@ -96,6 +80,17 @@ return mm()->getPointerToNamedFunction(Name, AbortOnFailure); } +void SharkMemoryManager::setPoisonMemory(bool poison) { + mm()->setPoisonMemory(poison); +} + +unsigned char *SharkMemoryManager::allocateSpace(intptr_t Size, + unsigned int Alignment) { + return mm()->allocateSpace(Size, Alignment); +} + +#if SHARK_LLVM_VERSION <= 32 + uint8_t* SharkMemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID) { return mm()->allocateCodeSection(Size, Alignment, SectionID); } @@ -104,11 +99,34 @@ return mm()->allocateDataSection(Size, Alignment, SectionID); } -void SharkMemoryManager::setPoisonMemory(bool poison) { - mm()->setPoisonMemory(poison); +void SharkMemoryManager::deallocateExceptionTable(void *ptr) { + mm()->deallocateExceptionTable(ptr); +} + +unsigned char* SharkMemoryManager::startExceptionTable(const Function* F, + uintptr_t& ActualSize) { + return mm()->startExceptionTable(F, ActualSize); +} + +void SharkMemoryManager::endExceptionTable(const Function* F, + unsigned char* TableStart, + unsigned char* TableEnd, + unsigned char* FrameRegister) { + mm()->endExceptionTable(F, TableStart, TableEnd, FrameRegister); } -unsigned char *SharkMemoryManager::allocateSpace(intptr_t Size, - unsigned int Alignment) { - return mm()->allocateSpace(Size, Alignment); +#else + +uint8_t *SharkMemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) { + return mm()->allocateCodeSection(Size, Alignment, SectionID, SectionName); } + +uint8_t* SharkMemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, bool IsReadOnly) { + return mm()->allocateDataSection(Size, Alignment, SectionID, SectionName, IsReadOnly); +} + +bool SharkMemoryManager::finalizeMemory(std::string *ErrMsg) { + return mm()->finalizeMemory(ErrMsg); +} + +#endif diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/shark/sharkMemoryManager.hpp --- a/src/share/vm/shark/sharkMemoryManager.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/shark/sharkMemoryManager.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -69,23 +69,32 @@ void endFunctionBody(const llvm::Function* F, unsigned char* FunctionStart, unsigned char* FunctionEnd); - unsigned char* startExceptionTable(const llvm::Function* F, - uintptr_t& ActualSize); - void endExceptionTable(const llvm::Function* F, - unsigned char* TableStart, - unsigned char* TableEnd, - unsigned char* FrameRegister); + void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true); - uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID); - uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID); void setPoisonMemory(bool); uint8_t* allocateGlobal(uintptr_t, unsigned int); void setMemoryWritable(); void setMemoryExecutable(); - void deallocateExceptionTable(void *ptr); void deallocateFunctionBody(void *ptr); unsigned char *allocateSpace(intptr_t Size, unsigned int Alignment); + +#if SHARK_LLVM_VERSION <= 32 +uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID); +uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID); +unsigned char* startExceptionTable(const llvm::Function* F, + uintptr_t& ActualSize); +void deallocateExceptionTable(void *ptr); +void endExceptionTable(const llvm::Function* F, + unsigned char* TableStart, + unsigned char* TableEnd, + unsigned char* FrameRegister); +#else +uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, llvm::StringRef SectionName); +uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, llvm::StringRef SectionName, bool IsReadOnly); +bool finalizeMemory(std::string *ErrMsg = 0); +#endif + }; #endif // SHARE_VM_SHARK_SHARKMEMORYMANAGER_HPP diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/trace/trace.xml --- a/src/share/vm/trace/trace.xml Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/trace/trace.xml Wed Oct 15 16:02:50 2014 +0200 @@ -122,6 +122,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -145,7 +185,7 @@ - + @@ -153,11 +193,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/trace/traceStream.hpp --- a/src/share/vm/trace/traceStream.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/trace/traceStream.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -66,7 +66,7 @@ } void print_val(const char* label, s8 val) { - _st.print("%s = "INT64_FORMAT, label, val); + _st.print("%s = "INT64_FORMAT, label, (int64_t) val); } void print_val(const char* label, bool val) { @@ -113,7 +113,7 @@ } void print(const char* val) { - _st.print(val); + _st.print("%s", val); } }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/trace/tracetypes.xml --- a/src/share/vm/trace/tracetypes.xml Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/trace/tracetypes.xml Wed Oct 15 16:02:50 2014 +0200 @@ -85,12 +85,6 @@ - - - - - @@ -116,17 +110,6 @@ - - - - - - - - - - @@ -147,11 +130,26 @@ + + + + + + + + + + + + @@ -167,6 +165,11 @@ + + + + @@ -336,10 +339,22 @@ + + + + + + + + + @@ -351,6 +366,10 @@ + + + diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/accessFlags.cpp --- a/src/share/vm/utilities/accessFlags.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/accessFlags.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/array.hpp --- a/src/share/vm/utilities/array.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/array.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -58,7 +58,7 @@ void initialize(size_t esize, int length) { assert(length >= 0, "illegal length"); - assert(_data == NULL, "must be new object"); + assert(StressRewriter || _data == NULL, "must be new object"); _length = length; _data = resource_allocate_bytes(esize * length); DEBUG_ONLY(init_nesting();) @@ -375,7 +375,7 @@ // FIXME: How to handle this? void print_value_on(outputStream* st) const { - st->print("Array(" INTPTR_FORMAT ")", this); + st->print("Array(" INTPTR_FORMAT ")", p2i(this)); } #ifndef PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/bitMap.cpp --- a/src/share/vm/utilities/bitMap.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/bitMap.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -107,7 +110,7 @@ while (true) { intptr_t res = Atomic::cmpxchg_ptr(nw, pw, w); if (res == w) break; - w = *pw; + w = res; nw = value ? (w | ~mr) : (w & mr); } } @@ -520,13 +523,13 @@ void BitMap::print_on_error(outputStream* st, const char* prefix) const { st->print_cr("%s[" PTR_FORMAT ", " PTR_FORMAT ")", - prefix, map(), (char*)map() + (size() >> LogBitsPerByte)); + prefix, p2i(map()), p2i((char*)map() + (size() >> LogBitsPerByte))); } #ifndef PRODUCT void BitMap::print_on(outputStream* st) const { - tty->print("Bitmap(%d):", size()); + tty->print("Bitmap(" SIZE_FORMAT "):", size()); for (idx_t index = 0; index < size(); index++) { tty->print("%c", at(index) ? '1' : '0'); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/constantTag.cpp --- a/src/share/vm/utilities/constantTag.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/constantTag.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -28,7 +28,7 @@ #ifndef PRODUCT void constantTag::print_on(outputStream* st) const { - st->print(internal_name()); + st->print("%s", internal_name()); } #endif // PRODUCT diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/debug.cpp --- a/src/share/vm/utilities/debug.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/debug.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -88,6 +88,8 @@ # endif #endif // PRODUCT +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + FormatBufferResource::FormatBufferResource(const char * format, ...) : FormatBufferBase((char*)resource_allocate_bytes(RES_BUFSZ)) { va_list argp; @@ -96,6 +98,7 @@ va_end(argp); } +ATTRIBUTE_PRINTF(1, 2) void warning(const char* format, ...) { if (PrintWarnings) { FILE* const err = defaultStream::error_stream(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/debug.hpp --- a/src/share/vm/utilities/debug.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/debug.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -43,17 +43,17 @@ #define RES_BUFSZ 256 class FormatBufferResource : public FormatBufferBase { public: - FormatBufferResource(const char * format, ...); + FormatBufferResource(const char * format, ...) ATTRIBUTE_PRINTF(2, 3); }; // Use stack for buffer template class FormatBuffer : public FormatBufferBase { public: - inline FormatBuffer(const char * format, ...); - inline void append(const char* format, ...); - inline void print(const char* format, ...); - inline void printv(const char* format, va_list ap); + inline FormatBuffer(const char * format, ...) ATTRIBUTE_PRINTF(2, 3); + inline void append(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + inline void print(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + inline void printv(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0); char* buffer() { return _buf; } int size() { return bufsz; } @@ -223,7 +223,7 @@ void report_unimplemented(const char* file, int line); void report_untested(const char* file, int line, const char* message); -void warning(const char* format, ...); +void warning(const char* format, ...) ATTRIBUTE_PRINTF(1, 2); #ifdef ASSERT // Compile-time asserts. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/decoder.cpp --- a/src/share/vm/utilities/decoder.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/decoder.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -32,6 +32,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 @@ -66,6 +68,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/decoder_elf.cpp --- a/src/share/vm/utilities/decoder_elf.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/decoder_elf.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -73,4 +73,4 @@ return file; } -#endif +#endif // !_WINDOWS && !__APPLE__ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/decoder_elf.hpp --- a/src/share/vm/utilities/decoder_elf.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/decoder_elf.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfFile.cpp --- a/src/share/vm/utilities/elfFile.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/elfFile.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfFile.hpp --- a/src/share/vm/utilities/elfFile.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/elfFile.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfFuncDescTable.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/utilities/elfFuncDescTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfFuncDescTable.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/utilities/elfFuncDescTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfStringTable.cpp --- a/src/share/vm/utilities/elfStringTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/elfStringTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -87,4 +87,4 @@ } } -#endif // _WINDOWS +#endif // !_WINDOWS && !__APPLE__ diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfStringTable.hpp --- a/src/share/vm/utilities/elfStringTable.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/elfStringTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfSymbolTable.cpp --- a/src/share/vm/utilities/elfSymbolTable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/elfSymbolTable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/elfSymbolTable.hpp --- a/src/share/vm/utilities/elfSymbolTable.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/elfSymbolTable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/events.cpp --- a/src/share/vm/utilities/events.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/events.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -82,7 +82,7 @@ va_start(ap, format); // Save a copy of begin message and log it. _buffer.printv(format, ap); - Events::log(NULL, "%s", (const char*)_buffer); + Events::log(NULL, "%s", _buffer.buffer()); va_end(ap); } } @@ -91,6 +91,6 @@ if (LogEvents) { // Append " done" to the begin message and log it _buffer.append(" done"); - Events::log(NULL, "%s", (const char*)_buffer); + Events::log(NULL, "%s", _buffer.buffer()); } } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/events.hpp --- a/src/share/vm/utilities/events.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/events.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -128,7 +128,7 @@ void print(outputStream* out, EventRecord& e) { out->print("Event: %.3f ", e.timestamp); if (e.thread != NULL) { - out->print("Thread " INTPTR_FORMAT " ", e.thread); + out->print("Thread " INTPTR_FORMAT " ", p2i(e.thread)); } print(out, e.data); } @@ -148,7 +148,7 @@ public: StringEventLog(const char* name, int count = LogEventsBufferEntries) : EventLogBase(name, count) {} - void logv(Thread* thread, const char* format, va_list ap) { + void logv(Thread* thread, const char* format, va_list ap) ATTRIBUTE_PRINTF(3, 0) { if (!should_log()) return; double timestamp = fetch_timestamp(); @@ -159,7 +159,7 @@ _records[index].data.printv(format, ap); } - void log(Thread* thread, const char* format, ...) { + void log(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(3, 4) { va_list ap; va_start(ap, format); logv(thread, format, ap); @@ -193,18 +193,17 @@ static void print(); // Logs a generic message with timestamp and format as printf. - static void log(Thread* thread, const char* format, ...); + static void log(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); // Log exception related message - static void log_exception(Thread* thread, const char* format, ...); + static void log_exception(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); - static void log_deopt_message(Thread* thread, const char* format, ...); + static void log_deopt_message(Thread* thread, const char* format, ...) ATTRIBUTE_PRINTF(2, 3); // Register default loggers static void init(); }; - inline void Events::log(Thread* thread, const char* format, ...) { if (LogEvents) { va_list ap; @@ -283,7 +282,7 @@ public: // log a begin event, format as printf - EventMark(const char* format, ...); + EventMark(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); // log an end event ~EventMark(); }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/exceptions.cpp --- a/src/share/vm/utilities/exceptions.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/exceptions.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -35,6 +35,7 @@ #include "utilities/events.hpp" #include "utilities/exceptions.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC // Implementation of ThreadShadow void check_ThreadShadow() { @@ -237,6 +238,7 @@ _throw_msg(thread, file, line, h_name, msg); } + // Creates an exception oop, calls the method with the given signature. // and returns a Handle Handle Exceptions::new_exception(Thread *thread, Symbol* name, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/exceptions.hpp --- a/src/share/vm/utilities/exceptions.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/exceptions.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -157,7 +157,7 @@ // There is no THROW... macro for this method. Caller should remember // to do a return after calling it. static void fthrow(Thread* thread, const char* file, int line, Symbol* name, - const char* format, ...); + const char* format, ...) ATTRIBUTE_PRINTF(5, 6); // Create and initialize a new exception static Handle new_exception(Thread* thread, Symbol* name, diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/globalDefinitions.hpp --- a/src/share/vm/utilities/globalDefinitions.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/globalDefinitions.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -38,6 +38,35 @@ #ifdef TARGET_COMPILER_sparcWorks # include "utilities/globalDefinitions_sparcWorks.hpp" #endif +#ifdef TARGET_COMPILER_xlc +# include "utilities/globalDefinitions_xlc.hpp" +#endif + +#ifndef PRAGMA_DIAG_PUSH +#define PRAGMA_DIAG_PUSH +#endif +#ifndef PRAGMA_DIAG_POP +#define PRAGMA_DIAG_POP +#endif +#ifndef PRAGMA_FORMAT_NONLITERAL_IGNORED +#define PRAGMA_FORMAT_NONLITERAL_IGNORED +#endif +#ifndef PRAGMA_FORMAT_IGNORED +#define PRAGMA_FORMAT_IGNORED +#endif +#ifndef PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL +#define PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL +#endif +#ifndef PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL +#define PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL +#endif +#ifndef PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC +#define PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC +#endif +#ifndef ATTRIBUTE_PRINTF +#define ATTRIBUTE_PRINTF(fmt, vargs) +#endif + #include "utilities/macros.hpp" @@ -370,6 +399,21 @@ // Machine dependent stuff +#if defined(X86) && defined(COMPILER2) && !defined(JAVASE_EMBEDDED) +// Include Restricted Transactional Memory lock eliding optimization +#define INCLUDE_RTM_OPT 1 +#define RTM_OPT_ONLY(code) code +#else +#define INCLUDE_RTM_OPT 0 +#define RTM_OPT_ONLY(code) +#endif +// States of Restricted Transactional Memory usage. +enum RTMState { + NoRTM = 0x2, // Don't use RTM + UseRTM = 0x1, // Use RTM + ProfileRTM = 0x0 // Use RTM with abort ratio calculation +}; + #ifdef TARGET_ARCH_x86 # include "globalDefinitions_x86.hpp" #endif @@ -395,6 +439,17 @@ #define PLATFORM_NATIVE_STACK_WALKING_SUPPORTED 1 #endif +// To assure the IRIW property on processors that are not multiple copy +// atomic, sync instructions must be issued between volatile reads to +// assure their ordering, instead of after volatile stores. +// (See "A Tutorial Introduction to the ARM and POWER Relaxed Memory Models" +// by Luc Maranget, Susmit Sarkar and Peter Sewell, INRIA/Cambridge) +#ifdef CPU_NOT_MULTIPLE_COPY_ATOMIC +const bool support_IRIW_for_not_multiple_copy_atomic_cpu = true; +#else +const bool support_IRIW_for_not_multiple_copy_atomic_cpu = false; +#endif + // The byte alignment to be used by Arena::Amalloc. See bugid 4169348. // Note: this value must be a power of 2 @@ -1255,6 +1310,11 @@ return ((int)((unsigned int)high << 16) | (unsigned int)low); } +// Convert pointer to intptr_t, for use in printing pointers. +inline intptr_t p2i(const void * p) { + return (intptr_t) p; +} + // Printf-style formatters for fixed- and variable-width types as pointers and // integers. These are derived from the definitions in inttypes.h. If the platform // doesn't provide appropriate definitions, they should be provided in @@ -1273,6 +1333,7 @@ // Format 64-bit quantities. #define INT64_FORMAT "%" PRId64 #define UINT64_FORMAT "%" PRIu64 +#define UINT64_FORMAT_X "%" PRIx64 #define INT64_FORMAT_W(width) "%" #width PRId64 #define UINT64_FORMAT_W(width) "%" #width PRIu64 @@ -1295,10 +1356,14 @@ #define PTR_FORMAT "0x%08" PRIxPTR #endif // _LP64 -#define SSIZE_FORMAT "%" PRIdPTR -#define SIZE_FORMAT "%" PRIuPTR -#define SSIZE_FORMAT_W(width) "%" #width PRIdPTR -#define SIZE_FORMAT_W(width) "%" #width PRIuPTR +#define INTPTR_FORMAT_W(width) "%" #width PRIxPTR + +#define SSIZE_FORMAT "%" PRIdPTR +#define SIZE_FORMAT "%" PRIuPTR +#define SIZE_FORMAT_HEX "0x%" PRIxPTR +#define SSIZE_FORMAT_W(width) "%" #width PRIdPTR +#define SIZE_FORMAT_W(width) "%" #width PRIuPTR +#define SIZE_FORMAT_HEX_W(width) "0x%" #width PRIxPTR #define INTX_FORMAT "%" PRIdPTR #define UINTX_FORMAT "%" PRIuPTR @@ -1320,11 +1385,10 @@ // All C++ compilers that we know of have the vtbl pointer in the first // word. If there are exceptions, this function needs to be made compiler // specific. -static inline void* dereference_vptr(void* addr) { +static inline void* dereference_vptr(const void* addr) { return *(void**)addr; } - #ifndef PRODUCT // For unit testing only diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/globalDefinitions_gcc.hpp --- a/src/share/vm/utilities/globalDefinitions_gcc.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/globalDefinitions_gcc.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -282,6 +282,47 @@ #define PRAGMA_IMPLEMENTATION #pragma implementation #define VALUE_OBJ_CLASS_SPEC +#ifndef ATTRIBUTE_PRINTF +// Diagnostic pragmas like the ones defined below in PRAGMA_FORMAT_NONLITERAL_IGNORED +// were only introduced in GCC 4.2. Because we have no other possibility to ignore +// these warnings for older versions of GCC, we simply don't decorate our printf-style +// functions with __attribute__(format) in that case. +#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ > 4) +#define ATTRIBUTE_PRINTF(fmt,vargs) __attribute__((format(printf, fmt, vargs))) +#else +#define ATTRIBUTE_PRINTF(fmt,vargs) +#endif +#endif + +#define PRAGMA_FORMAT_NONLITERAL_IGNORED _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") \ + _Pragma("GCC diagnostic ignored \"-Wformat-security\"") +#define PRAGMA_FORMAT_IGNORED _Pragma("GCC diagnostic ignored \"-Wformat\"") + +#if defined(__clang_major__) && \ + (__clang_major__ >= 4 || \ + (__clang_major__ >= 3 && __clang_minor__ >= 1)) || \ + ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) || (__GNUC__ > 4) +// Tested to work with clang version 3.1 and better. +#define PRAGMA_DIAG_PUSH _Pragma("GCC diagnostic push") +#define PRAGMA_DIAG_POP _Pragma("GCC diagnostic pop") +#define PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL +#define PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL PRAGMA_FORMAT_NONLITERAL_IGNORED + +// Hack to deal with gcc yammering about non-security format stuff +#else +// Old versions of gcc don't do push/pop, also do not cope with this pragma within a function +// One method does so much varied printing that it is decorated with both internal and external +// versions of the macro-pragma to obtain better checking with newer compilers. +#define PRAGMA_DIAG_PUSH +#define PRAGMA_DIAG_POP +#define PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL PRAGMA_FORMAT_NONLITERAL_IGNORED +#define PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNAL +#endif + +#ifndef __clang_major__ +#define PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC _Pragma("GCC diagnostic ignored \"-Wformat\"") _Pragma("GCC diagnostic error \"-Wformat-nonliteral\"") _Pragma("GCC diagnostic error \"-Wformat-security\"") +#endif + #if (__GNUC__ == 2) && (__GNUC_MINOR__ < 95) #define TEMPLATE_TABLE_BUG #endif diff -r 45d7b2c7029d -r 52b4284cb496 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 Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,202 @@ +/* + * 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. + * + */ + +#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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/hashtable.cpp --- a/src/share/vm/utilities/hashtable.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/hashtable.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -93,7 +93,7 @@ return false; } -template jint Hashtable::_seed = 0; +template juint Hashtable::_seed = 0; // Create a new table and using alternate hash code, populate the new table // with the existing elements. This can be used to change the hash code diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/hashtable.hpp --- a/src/share/vm/utilities/hashtable.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/hashtable.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -280,7 +280,7 @@ // Function to move these elements into the new table. void move_to(Hashtable* new_table); static bool use_alternate_hashcode() { return _seed != 0; } - static jint seed() { return _seed; } + static juint seed() { return _seed; } static int literal_size(Symbol *symbol); static int literal_size(oop oop); @@ -296,7 +296,7 @@ void dump_table(outputStream* st, const char *table_name); private: - static jint _seed; + static juint _seed; }; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/histogram.hpp --- a/src/share/vm/utilities/histogram.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/histogram.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/macros.hpp --- a/src/share/vm/utilities/macros.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/macros.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -276,6 +276,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) @@ -342,7 +350,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 @@ -366,14 +378,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 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/numberSeq.cpp --- a/src/share/vm/utilities/numberSeq.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/numberSeq.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -258,5 +258,5 @@ } s->print("\t[%d]=%7.3f", i, _sequence[i]); } - s->print_cr(""); + s->cr(); } diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/ostream.cpp --- a/src/share/vm/utilities/ostream.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/ostream.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -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 @@ -265,7 +268,7 @@ size_t limit = (len + 16) / 16 * 16; for (size_t i = 0; i < limit; ++i) { if (i % 16 == 0) { - indent().print("%07x:", i); + indent().print(SIZE_FORMAT_HEX_W(07)":", i); } if (i % 2 == 0) { print(" "); @@ -286,7 +289,7 @@ } } } - print_cr(""); + cr(); } } } @@ -603,7 +606,7 @@ // memory usage and command line flags into header void gcLogFileStream::dump_loggc_header() { if (is_open()) { - print_cr(Abstract_VM_Version::internal_vm_info_string()); + print_cr("%s", Abstract_VM_Version::internal_vm_info_string()); os::print_memory_info(this); print("CommandLine flags: "); CommandLineFlags::printSetFlags(this); @@ -659,13 +662,13 @@ // write to gc log file at safepoint. If in future, changes made for mutator threads or // concurrent GC threads to run parallel with VMThread at safepoint, write and rotate_log // must be synchronized. -void gcLogFileStream::rotate_log() { +void gcLogFileStream::rotate_log(bool force, outputStream* out) { char time_msg[FILENAMEBUFLEN]; char time_str[EXTRACHARLEN]; char current_file_name[FILENAMEBUFLEN]; char renamed_file_name[FILENAMEBUFLEN]; - if (_bytes_written < (jlong)GCLogFileSize) { + if (!should_rotate(force)) { return; } @@ -682,6 +685,11 @@ jio_snprintf(time_msg, sizeof(time_msg), "File %s rotated at %s\n", _file_name, os::local_time_string((char *)time_str, sizeof(time_str))); write(time_msg, strlen(time_msg)); + + if (out != NULL) { + out->print("%s", time_msg); + } + dump_loggc_header(); return; } @@ -703,12 +711,18 @@ _file_name, _cur_file_num); jio_snprintf(current_file_name, filename_len + EXTRACHARLEN, "%s.%d" CURRENTAPPX, _file_name, _cur_file_num); - jio_snprintf(time_msg, sizeof(time_msg), "%s GC log file has reached the" - " maximum size. Saved as %s\n", - os::local_time_string((char *)time_str, sizeof(time_str)), - renamed_file_name); + + const char* msg = force ? "GC log rotation request has been received." + : "GC log file has reached the maximum size."; + jio_snprintf(time_msg, sizeof(time_msg), "%s %s Saved as %s\n", + os::local_time_string((char *)time_str, sizeof(time_str)), + msg, renamed_file_name); write(time_msg, strlen(time_msg)); + if (out != NULL) { + out->print("%s", time_msg); + } + fclose(_file); _file = NULL; @@ -749,6 +763,11 @@ os::local_time_string((char *)time_str, sizeof(time_str)), current_file_name); write(time_msg, strlen(time_msg)); + + if (out != NULL) { + out->print("%s", time_msg); + } + dump_loggc_header(); // remove the existing file if (access(current_file_name, F_OK) == 0) { @@ -826,7 +845,7 @@ xs->head("hotspot_log version='%d %d'" " process='%d' time_ms='"INT64_FORMAT"'", LOG_MAJOR_VERSION, LOG_MINOR_VERSION, - os::current_process_id(), time_ms); + os::current_process_id(), (int64_t)time_ms); // Write VM version header immediately. xs->head("vm_version"); xs->head("name"); xs->text("%s", VM_Version::vm_name()); xs->cr(); @@ -1238,7 +1257,7 @@ #ifndef PRODUCT -#if defined(SOLARIS) || defined(LINUX) || defined(_ALLBSD_SOURCE) +#if defined(SOLARIS) || defined(LINUX) || defined(AIX) || defined(_ALLBSD_SOURCE) #include #include #include diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/ostream.hpp --- a/src/share/vm/utilities/ostream.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/ostream.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, 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 @@ -53,7 +53,7 @@ static const char* do_vsnprintf(char* buffer, size_t buflen, const char* format, va_list ap, bool add_cr, - size_t& result_len); + size_t& result_len) ATTRIBUTE_PRINTF(3, 0); public: // creation @@ -80,10 +80,10 @@ void set_position(int pos) { _position = pos; } // printing - void print(const char* format, ...); - void print_cr(const char* format, ...); - void vprint(const char *format, va_list argptr); - void vprint_cr(const char* format, va_list argptr); + void print(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void vprint(const char *format, va_list argptr) ATTRIBUTE_PRINTF(2, 0); + void vprint_cr(const char* format, va_list argptr) ATTRIBUTE_PRINTF(2, 0); void print_raw(const char* str) { write(str, strlen(str)); } void print_raw(const char* str, int len) { write(str, len); } void print_raw_cr(const char* str) { write(str, strlen(str)); cr(); } @@ -115,7 +115,7 @@ // flushing virtual void flush() {} virtual void write(const char* str, size_t len) = 0; - virtual void rotate_log() {} // GC log rotation + virtual void rotate_log(bool force, outputStream* out = NULL) {} // GC log rotation virtual ~outputStream() {} // close properly on deletion void dec_cr() { dec(); cr(); } @@ -240,8 +240,15 @@ gcLogFileStream(const char* file_name); ~gcLogFileStream(); virtual void write(const char* c, size_t len); - virtual void rotate_log(); + virtual void rotate_log(bool force, outputStream* out = NULL); void dump_loggc_header(); + + /* If "force" sets true, force log file rotation from outside JVM */ + bool should_rotate(bool force) { + return force || + ((GCLogFileSize != 0) && ((uintx)_bytes_written >= GCLogFileSize)); + } + }; #ifndef PRODUCT @@ -268,10 +275,10 @@ ~staticBufferStream() {}; virtual void write(const char* c, size_t len); void flush(); - void print(const char* format, ...); - void print_cr(const char* format, ...); - void vprint(const char *format, va_list argptr); - void vprint_cr(const char* format, va_list argptr); + void print(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void vprint(const char *format, va_list argptr) ATTRIBUTE_PRINTF(2, 0); + void vprint_cr(const char* format, va_list argptr) ATTRIBUTE_PRINTF(2, 0); }; // In the non-fixed buffer case an underlying buffer will be created and diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/quickSort.cpp --- a/src/share/vm/utilities/quickSort.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/quickSort.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -77,7 +77,7 @@ for (int i = 0; i < length; i++) { tty->print(" %d", array[i]); } - tty->print_cr(""); + tty->cr(); } bool QuickSort::compare_arrays(int* actual, int* expected, int length) { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/resourceHash.hpp --- a/src/share/vm/utilities/resourceHash.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/resourceHash.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -44,8 +44,12 @@ template< typename K, typename V, - typename ResourceHashtableFns::hash_fn HASH = primitive_hash, - typename ResourceHashtableFns::equals_fn EQUALS = primitive_equals, + // xlC does not compile this: + // http://stackoverflow.com/questions/8532961/template-argument-of-type-that-is-defined-by-inner-typedef-from-other-template-c + //typename ResourceHashtableFns::hash_fn HASH = primitive_hash, + //typename ResourceHashtableFns::equals_fn EQUALS = primitive_equals, + unsigned (*HASH) (K const&) = primitive_hash, + bool (*EQUALS)(K const&, K const&) = primitive_equals, unsigned SIZE = 256 > class ResourceHashtable : public ResourceObj { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/taskqueue.cpp --- a/src/share/vm/utilities/taskqueue.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/taskqueue.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -30,6 +30,8 @@ #include "utilities/stack.inline.hpp" #include "utilities/taskqueue.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + #ifdef TRACESPINNING uint ParallelTaskTerminator::_total_yields = 0; uint ParallelTaskTerminator::_total_spins = 0; diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/taskqueue.hpp --- a/src/share/vm/utilities/taskqueue.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/taskqueue.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -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 diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/vmError.cpp --- a/src/share/vm/utilities/vmError.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/vmError.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -43,6 +43,8 @@ #include "utilities/top.hpp" #include "utilities/vmError.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // List of environment variables that should be reported in error log file. const char *env_list[] = { // All platforms @@ -359,17 +361,17 @@ st->print((_id == (int)OOM_MALLOC_ERROR) ? "(malloc) failed to allocate " : "(mmap) failed to map "); jio_snprintf(buf, sizeof(buf), SIZE_FORMAT, _size); - st->print(buf); + st->print("%s", buf); st->print(" bytes"); if (_message != NULL) { st->print(" for "); - st->print(_message); + st->print("%s", _message); } st->cr(); } else { if (_message != NULL) st->print("# "); - st->print_cr(_message); + st->print_cr("%s", _message); } // In error file give some solutions if (_verbose) { @@ -486,7 +488,7 @@ } else { st->print("Failed to write core dump. %s", coredump_message); } - st->print_cr(""); + st->cr(); st->print_cr("#"); STEP(65, "(printing bug submit message)") @@ -619,13 +621,24 @@ st->cr(); // Compiled code may use EBP register on x86 so it looks like // non-walkable C frame. Use frame.sender() for java frames. - if (_thread && _thread->is_Java_thread() && fr.is_java_frame()) { - RegisterMap map((JavaThread*)_thread, false); // No update - fr = fr.sender(&map); - continue; + if (_thread && _thread->is_Java_thread()) { + // Catch very first native frame by using stack address. + // For JavaThread stack_base and stack_size should be set. + if (!_thread->on_local_stack((address)(fr.sender_sp() + 1))) { + break; + } + if (fr.is_java_frame()) { + RegisterMap map((JavaThread*)_thread, false); // No update + fr = fr.sender(&map); + } else { + fr = os::get_sender_for_C_frame(&fr); + } + } else { + // is_first_C_frame() does only simple checks for frame pointer, + // it will pass if java compiled code has a pointer in EBP. + if (os::is_first_C_frame(&fr)) break; + fr = os::get_sender_for_C_frame(&fr); } - if (os::is_first_C_frame(&fr)) break; - fr = os::get_sender_for_C_frame(&fr); } if (count > StackPrintLimit) { @@ -1066,7 +1079,7 @@ OnError = NULL; } - static bool skip_replay = false; + static bool skip_replay = ReplayCompiles; // Do not overwrite file during replay if (DumpReplayDataOnError && _thread && _thread->is_Compiler_thread() && !skip_replay) { skip_replay = true; ciEnv* env = ciEnv::current(); diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/workgroup.cpp --- a/src/share/vm/utilities/workgroup.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/workgroup.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -28,6 +28,8 @@ #include "runtime/os.hpp" #include "utilities/workgroup.hpp" +PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + // Definitions of WorkGang methods. AbstractWorkGang::AbstractWorkGang(const char* name, @@ -376,21 +378,22 @@ WorkGangBarrierSync::WorkGangBarrierSync() : _monitor(Mutex::safepoint, "work gang barrier sync", true), - _n_workers(0), _n_completed(0), _should_reset(false) { + _n_workers(0), _n_completed(0), _should_reset(false), _aborted(false) { } WorkGangBarrierSync::WorkGangBarrierSync(uint n_workers, const char* name) : _monitor(Mutex::safepoint, name, true), - _n_workers(n_workers), _n_completed(0), _should_reset(false) { + _n_workers(n_workers), _n_completed(0), _should_reset(false), _aborted(false) { } void WorkGangBarrierSync::set_n_workers(uint n_workers) { - _n_workers = n_workers; - _n_completed = 0; + _n_workers = n_workers; + _n_completed = 0; _should_reset = false; + _aborted = false; } -void WorkGangBarrierSync::enter() { +bool WorkGangBarrierSync::enter() { MutexLockerEx x(monitor(), Mutex::_no_safepoint_check_flag); if (should_reset()) { // The should_reset() was set and we are the first worker to enter @@ -413,10 +416,17 @@ set_should_reset(true); monitor()->notify_all(); } else { - while (n_completed() != n_workers()) { + while (n_completed() != n_workers() && !aborted()) { monitor()->wait(/* no_safepoint_check */ true); } } + return !aborted(); +} + +void WorkGangBarrierSync::abort() { + MutexLockerEx x(monitor(), Mutex::_no_safepoint_check_flag); + set_aborted(); + monitor()->notify_all(); } // SubTasksDone functions. diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/workgroup.hpp --- a/src/share/vm/utilities/workgroup.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/workgroup.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -359,18 +359,20 @@ class WorkGangBarrierSync : public StackObj { protected: Monitor _monitor; - uint _n_workers; - uint _n_completed; + uint _n_workers; + uint _n_completed; bool _should_reset; + bool _aborted; Monitor* monitor() { return &_monitor; } uint n_workers() { return _n_workers; } uint n_completed() { return _n_completed; } bool should_reset() { return _should_reset; } + bool aborted() { return _aborted; } void zero_completed() { _n_completed = 0; } void inc_completed() { _n_completed++; } - + void set_aborted() { _aborted = true; } void set_should_reset(bool v) { _should_reset = v; } public: @@ -383,8 +385,14 @@ // Enter the barrier. A worker that enters the barrier will // not be allowed to leave until all other threads have - // also entered the barrier. - void enter(); + // also entered the barrier or the barrier is aborted. + // Returns false if the barrier was aborted. + bool enter(); + + // Aborts the barrier and wakes up any threads waiting for + // the barrier to complete. The barrier will remain in the + // aborted state until the next call to set_n_workers(). + void abort(); }; // A class to manage claiming of subtasks within a group of tasks. The diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/xmlstream.cpp --- a/src/share/vm/utilities/xmlstream.cpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/xmlstream.cpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -336,6 +336,8 @@ print_raw_cr(">"); } +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED // ------------------------------------------------------------------ void xmlStream::va_done(const char* format, va_list ap) { char buffer[200]; @@ -354,6 +356,7 @@ buffer[kind_len] = 0; tail(buffer); } +PRAGMA_DIAG_POP // Output a timestamp attribute. void xmlStream::stamp() { diff -r 45d7b2c7029d -r 52b4284cb496 src/share/vm/utilities/xmlstream.hpp --- a/src/share/vm/utilities/xmlstream.hpp Thu Oct 16 10:21:29 2014 +0200 +++ b/src/share/vm/utilities/xmlstream.hpp Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -80,7 +80,7 @@ outputStream* out() { return _out; } // helpers for writing XML elements - void va_tag(bool push, const char* format, va_list ap); + void va_tag(bool push, const char* format, va_list ap) ATTRIBUTE_PRINTF(3, 0); virtual void see_tag(const char* tag, bool push) NOT_DEBUG({}); virtual void pop_tag(const char* tag) NOT_DEBUG({}); @@ -109,29 +109,29 @@ int unflushed_count() { return (int)(out()->count() - _last_flush); } // writing complete XML elements - void elem(const char* format, ...); - void begin_elem(const char* format, ...); - void end_elem(const char* format, ...); + void elem(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void begin_elem(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void end_elem(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); void end_elem(); - void head(const char* format, ...); - void begin_head(const char* format, ...); - void end_head(const char* format, ...); + void head(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void begin_head(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void end_head(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); void end_head(); - void done(const char* format, ...); // xxx_done event, plus tail + void done(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); // xxx_done event, plus tail void done_raw(const char * kind); void tail(const char* kind); // va_list versions - void va_elem(const char* format, va_list ap); - void va_begin_elem(const char* format, va_list ap); - void va_head(const char* format, va_list ap); - void va_begin_head(const char* format, va_list ap); - void va_done(const char* format, va_list ap); + void va_elem(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0); + void va_begin_elem(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0); + void va_head(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0); + void va_begin_head(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0); + void va_done(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0); // write text (with quoting of special XML characters <>&'" etc.) outputStream* text() { return _text; } - void text(const char* format, ...); - void va_text(const char* format, va_list ap) { + void text(const char* format, ...) ATTRIBUTE_PRINTF(2, 3); + void va_text(const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0) { text()->vprint(format, ap); } diff -r 45d7b2c7029d -r 52b4284cb496 test/TEST.groups --- a/test/TEST.groups Thu Oct 16 10:21:29 2014 +0200 +++ b/test/TEST.groups Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 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 @@ -84,6 +84,7 @@ runtime/NMT/ThreadedVirtualAllocTestType.java \ runtime/NMT/VirtualAllocTestType.java \ runtime/RedefineObject/TestRedefineObject.java \ + runtime/Thread/TestThreadDumpMonitorContention.java \ runtime/XCheckJniJsig/XCheckJSig.java \ serviceability/attach/AttachWithStalePidFile.java \ serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java @@ -130,8 +131,15 @@ gc/g1/TestHumongousAllocInitialMark.java \ gc/arguments/TestG1HeapRegionSize.java \ gc/metaspace/TestMetaspaceMemoryPool.java \ + gc/arguments/TestDynMinHeapFreeRatio.java \ + gc/arguments/TestDynMaxHeapFreeRatio.java \ runtime/InternalApi/ThreadCpuTimesDeadlock.java \ - serviceability/threads/TestFalseDeadLock.java + serviceability/threads/TestFalseDeadLock.java \ + serviceability/jvmti/GetObjectSizeOverflow.java \ + serviceability/jvmti/TestRedefineWithUnresolvedClass.java \ + compiler/tiered/NonTieredLevelsTest.java \ + compiler/tiered/TieredLevelsTest.java \ + compiler/intrinsics/bmi/verifycode # Compact 2 adds full VM tests compact2 = \ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/6826736/Test.java --- a/test/compiler/6826736/Test.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/6826736/Test.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,7 +27,7 @@ * @bug 6826736 * @summary CMS: core dump with -XX:+UseCompressedOops * - * @run main/othervm/timeout=600 -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:+ScavengeALot -XX:+UseCompressedOops -XX:HeapBaseMinAddress=32g -XX:CompileThreshold=100 -XX:CompileOnly=Test.test -XX:-BlockLayoutRotateLoops -XX:LoopUnrollLimit=0 Test + * @run main/othervm/timeout=600 -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:+ScavengeALot -XX:+UseCompressedOops -XX:HeapBaseMinAddress=32g -XX:CompileThreshold=100 -XX:CompileOnly=Test.test -XX:-BlockLayoutRotateLoops -XX:LoopUnrollLimit=0 -Xmx256m -XX:ParallelGCThreads=4 Test */ public class Test { diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/7184394/TestAESBase.java --- a/test/compiler/7184394/TestAESBase.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/7184394/TestAESBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -40,9 +40,20 @@ int msgSize = Integer.getInteger("msgSize", 646); boolean checkOutput = Boolean.getBoolean("checkOutput"); boolean noReinit = Boolean.getBoolean("noReinit"); + boolean testingMisalignment; + private static final int ALIGN = 8; + int encInputOffset = Integer.getInteger("encInputOffset", 0) % ALIGN; + int encOutputOffset = Integer.getInteger("encOutputOffset", 0) % ALIGN; + int decOutputOffset = Integer.getInteger("decOutputOffset", 0) % ALIGN; + int lastChunkSize = Integer.getInteger("lastChunkSize", 32); int keySize = Integer.getInteger("keySize", 128); + int inputLength; + int encodeLength; + int decodeLength; + int decodeMsgSize; String algorithm = System.getProperty("algorithm", "AES"); String mode = System.getProperty("mode", "CBC"); + String paddingStr = System.getProperty("paddingStr", "PKCS5Padding"); byte[] input; byte[] encode; byte[] expectedEncode; @@ -51,7 +62,6 @@ Random random = new Random(0); Cipher cipher; Cipher dCipher; - String paddingStr = "PKCS5Padding"; AlgorithmParameters algParams; SecretKey key; @@ -67,7 +77,10 @@ public void prepare() { try { - System.out.println("\nalgorithm=" + algorithm + ", mode=" + mode + ", msgSize=" + msgSize + ", keySize=" + keySize + ", noReinit=" + noReinit + ", checkOutput=" + checkOutput); + System.out.println("\nalgorithm=" + algorithm + ", mode=" + mode + ", paddingStr=" + paddingStr + ", msgSize=" + msgSize + ", keySize=" + keySize + ", noReinit=" + noReinit + ", checkOutput=" + checkOutput + ", encInputOffset=" + encInputOffset + ", encOutputOffset=" + encOutputOffset + ", decOutputOffset=" + decOutputOffset + ", lastChunkSize=" +lastChunkSize ); + + if (encInputOffset % ALIGN != 0 || encOutputOffset % ALIGN != 0 || decOutputOffset % ALIGN !=0 ) + testingMisalignment = true; int keyLenBytes = (keySize == 0 ? 16 : keySize/8); byte keyBytes[] = new byte[keyLenBytes]; @@ -81,10 +94,6 @@ System.out.println("Algorithm: " + key.getAlgorithm() + "(" + key.getEncoded().length * 8 + "bit)"); } - input = new byte[msgSize]; - for (int i=0; i 0 ? Integer.valueOf(args[0]) : 1000000); + int warmupIters = (args.length > 1 ? Integer.valueOf(args[1]) : 20000); System.out.println(iters + " iterations"); TestAESEncode etest = new TestAESEncode(); etest.prepare(); + // warm-up + System.out.println("Starting encryption warm-up"); + for (int i=0; i markerList = new ArrayList<>(); + + // Suppress compilation of this method, it must be processed + // by the bytecode escape analyzer. + + // Make a new marker and put it on the List + Marker getMarker() { + // result escapes through markerList + final Marker result = new Marker(); + markerList.add(result); + return result; + } + + void visit(int depth) { + // Make a new marker + getMarker(); + + // Call visitAndPop every once in a while + // Cap the depth of our recursive visits + if (depth % 10 == 2) { + visitAndPop(depth + 1); + } else if (depth < 15) { + visit(depth + 1); + } + } + + void visitAndPop(int depth) { + // Random dummy allocation to force EscapeAnalysis to process this method + dummy = new TestAllocatedEscapesPtrComparison(); + + // Make a new marker + Marker marker = getMarker(); + + visit(depth + 1); + + // Walk and pop the marker list up to the current marker + boolean found = false; + for (int i = markerList.size() - 1; i >= 0; i--) { + Marker removed = markerList.remove(i); + + // In the failure, EA mistakenly converts this comparison to false + if (removed == marker) { + found = true; + break; + } + } + + if (!found) { + throw new RuntimeException("test fails"); + } + } + + + public static void main(String args[]) { + TestAllocatedEscapesPtrComparison tc = new TestAllocatedEscapesPtrComparison(); + + // Warmup and run enough times + for (int i = 0; i < 20000; i++) { + tc.visit(0); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/EscapeAnalysis/TestUnsafePutAddressNullObjMustNotEscape.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/EscapeAnalysis/TestUnsafePutAddressNullObjMustNotEscape.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright 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. + */ + +/** + * @test + * @bug 8038048 + * @summary assert(null_obj->escape_state() == PointsToNode::NoEscape,etc) + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+DoEscapeAnalysis -XX:-TieredCompilation -Xbatch TestUnsafePutAddressNullObjMustNotEscape + * @author Richard Reingruber richard DOT reingruber AT sap DOT com + */ + +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +public class TestUnsafePutAddressNullObjMustNotEscape { + + public static Unsafe usafe; + public static long mem; + public static long checksum; + + public static void main(String[] args) throws Exception { + System.out.println("EXECUTING test."); + + { + System.out.println("Acquiring sun.misc.Unsafe.theUnsafe using reflection."); + getUnsafe(); + System.out.println("Allocating raw memory."); + mem = (usafe.allocateMemory(1024) + 8L) & ~7L; + System.out.println("Triggering JIT compilation of the test method"); + triggerJitCompilationOfTestMethod(); + } + + System.out.println("SUCCESSFULLY passed test."); + } + + public static void triggerJitCompilationOfTestMethod() { + long sum = 0; + for (int ii = 50000; ii >= 0; ii--) { + sum = testMethod(); + } + checksum = sum; + } + + public static class IDGen { + private static long id; + public long nextId() { + return id++; + } + } + + public static long testMethod() { + // dummy alloc to trigger escape analysis + IDGen gen = new IDGen(); + // StoreP of null_obj to raw mem triggers assertion in escape analysis + usafe.putAddress(mem, 0L); + return gen.nextId(); + } + + private static void getUnsafe() throws Exception { + Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + usafe = (sun.misc.Unsafe) field.get(null); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/BMICommandLineOptionTestBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/BMICommandLineOptionTestBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import com.oracle.java.testlibrary.cli.*; + +/** + * Base class for all X86 bit manipulation related command line options. + */ +public abstract class BMICommandLineOptionTestBase + extends CPUSpecificCommandLineOptionTest { + + public static final String LZCNT_WARNING = + "lzcnt instruction is not available on this CPU"; + public static final String TZCNT_WARNING = + "tzcnt instruction is not available on this CPU"; + public static final String BMI1_WARNING = + "BMI1 instructions are not available on this CPU"; + + protected final String optionName; + protected final String warningMessage; + protected final String errorMessage; + + /** + * Construct new test on {@code optionName} option. + * + * @param optionName Name of the option to be tested + * without -XX:[+-] prefix. + * @param warningMessage Message that can occur in VM output + * if CPU on test box does not support + * features required by the option. + * @param supportedCPUFeatures CPU features requires by the option, + * that should be supported on test box. + * @param unsupportedCPUFeatures CPU features requires by the option, + * that should not be supported on test box. + */ + public BMICommandLineOptionTestBase(String optionName, + String warningMessage, + String supportedCPUFeatures[], + String unsupportedCPUFeatures[]) { + super(".*", supportedCPUFeatures, unsupportedCPUFeatures); + this.optionName = optionName; + this.warningMessage = warningMessage; + this.errorMessage = CommandLineOptionTest. + UNRECOGNIZED_OPTION_ERROR_FORMAT.format(optionName); + } + +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/BMISupportedCPUTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/BMISupportedCPUTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; + +/** + * Test on bit manipulation related command line options, + * that should be executed on CPU that supports all required + * features. + */ +public class BMISupportedCPUTest extends BMICommandLineOptionTestBase { + + /** + * Construct new test on {@code optionName} option. + * + * @param optionName Name of the option to be tested + * without -XX:[+-] prefix. + * @param warningMessage Message that can occur in VM output + * if CPU on test box does not support + * features required by the option. + * @param cpuFeatures CPU features requires by the option. + */ + public BMISupportedCPUTest(String optionName, + String warningMessage, + String... cpuFeatures) { + super(optionName, warningMessage, cpuFeatures, null); + } + + @Override + public void runTestCases() throws Throwable { + // verify that VM will succesfully start up whithout warnings + CommandLineOptionTest. + verifyJVMStartup("-XX:+" + optionName, + null, new String[] { warningMessage }, + ExitCode.OK); + + // verify that VM will succesfully start up whithout warnings + CommandLineOptionTest. + verifyJVMStartup("-XX:-" + optionName, + null, new String[] { warningMessage }, + ExitCode.OK); + + // verify that on appropriate CPU option in on by default + CommandLineOptionTest.verifyOptionValue(optionName, "true"); + + // verify that option could be explicitly turned off + CommandLineOptionTest.verifyOptionValue(optionName, "false", + "-XX:-" + optionName); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/BMIUnsupportedCPUTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/BMIUnsupportedCPUTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; + +/** + * Test on bit manipulation related command line options, + * that should be executed on CPU that does not support + * required features. + */ +public class BMIUnsupportedCPUTest extends BMICommandLineOptionTestBase { + + /** + * Construct new test on {@code optionName} option. + * + * @param optionName Name of the option to be tested + * without -XX:[+-] prefix. + * @param warningMessage Message that can occur in VM output + * if CPU on test box does not support + * features required by the option. + * @param cpuFeatures CPU features requires by the option. + */ + public BMIUnsupportedCPUTest(String optionName, + String warningMessage, + String... cpuFeatures) { + super(optionName, warningMessage, null, cpuFeatures); + } + + @Override + public void runTestCases() throws Throwable { + if (Platform.isX86() || Platform.isX64()) { + unsupportedX86CPUTestCases(); + } else { + unsupportedNonX86CPUTestCases(); + } + } + + /** + * Run test cases common for all bit manipulation related VM options + * targeted to X86 CPU that does not support required features. + * + * @throws Throwable if test failed. + */ + public void unsupportedX86CPUTestCases() throws Throwable { + + // verify that VM will succesfully start up, but output will + // contain a warning + CommandLineOptionTest. + verifyJVMStartup("-XX:+" + optionName, + new String[] { warningMessage }, + new String[] { errorMessage }, + ExitCode.OK); + + // verify that VM will succesfully startup without any warnings + CommandLineOptionTest. + verifyJVMStartup("-XX:-" + optionName, + null, + new String[] { warningMessage, errorMessage }, + ExitCode.OK); + + // verify that on unsupported CPUs option is off by default + CommandLineOptionTest.verifyOptionValue(optionName, "false"); + + // verify that on unsupported CPUs option will be off even if + // it was explicitly turned on by uset + CommandLineOptionTest.verifyOptionValue(optionName, "false", + "-XX:+" + optionName); + + } + + /** + * Run test cases common for all bit manipulation related VM options + * targeted to non-X86 CPU that does not support required features. + * + * @throws Throwable if test failed. + */ + public void unsupportedNonX86CPUTestCases() throws Throwable { + + // verify that VM known nothing about tested option + CommandLineOptionTest. + verifyJVMStartup("-XX:+" + optionName, + new String[] { errorMessage }, + null, + ExitCode.FAIL); + + CommandLineOptionTest. + verifyJVMStartup("-XX:-" + optionName, + new String[] { errorMessage }, + null, + ExitCode.FAIL); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/TestUseBMI1InstructionsOnSupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/TestUseBMI1InstructionsOnSupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify processing of UseBMI1Instructions option on CPU with + * BMI1 feature support. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseBMI1InstructionsOnSupportedCPU + * BMISupportedCPUTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseBMI1InstructionsOnSupportedCPU + */ + +import sun.hotspot.cpuinfo.CPUInfo; +import com.oracle.java.testlibrary.*; + +public class TestUseBMI1InstructionsOnSupportedCPU + extends BMISupportedCPUTest { + + public TestUseBMI1InstructionsOnSupportedCPU() { + super("UseBMI1Instructions", BMI1_WARNING, "bmi1"); + } + + public static void main(String args[]) throws Throwable { + new TestUseBMI1InstructionsOnSupportedCPU().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/TestUseBMI1InstructionsOnUnsupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/TestUseBMI1InstructionsOnUnsupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify processing of UseBMI1Instructions option on CPU without + * BMI1 feature support. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseBMI1InstructionsOnUnsupportedCPU + * BMIUnsupportedCPUTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseBMI1InstructionsOnUnsupportedCPU + */ + +import sun.hotspot.cpuinfo.CPUInfo; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; + +public class TestUseBMI1InstructionsOnUnsupportedCPU + extends BMIUnsupportedCPUTest { + + public TestUseBMI1InstructionsOnUnsupportedCPU() { + super("UseBMI1Instructions", BMI1_WARNING, "bmi1"); + } + + public static void main(String args[]) throws Throwable { + new TestUseBMI1InstructionsOnUnsupportedCPU().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/TestUseCountLeadingZerosInstructionOnSupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnSupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify processing of UseCountLeadingZerosInstruction option + * on CPU with LZCNT support. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseCountLeadingZerosInstructionOnSupportedCPU + * BMISupportedCPUTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestUseCountLeadingZerosInstructionOnSupportedCPU + */ + +import sun.hotspot.cpuinfo.CPUInfo; +import com.oracle.java.testlibrary.*; + +public class TestUseCountLeadingZerosInstructionOnSupportedCPU + extends BMISupportedCPUTest { + + public TestUseCountLeadingZerosInstructionOnSupportedCPU() { + super("UseCountLeadingZerosInstruction", LZCNT_WARNING, "lzcnt"); + } + + public static void main(String args[]) throws Throwable { + new TestUseCountLeadingZerosInstructionOnSupportedCPU().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/TestUseCountLeadingZerosInstructionOnUnsupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnUnsupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify processing of UseCountLeadingZerosInstruction option + * on CPU without LZCNT support. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseCountLeadingZerosInstructionOnUnsupportedCPU + * BMIUnsupportedCPUTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestUseCountLeadingZerosInstructionOnUnsupportedCPU + */ + +import sun.hotspot.cpuinfo.CPUInfo; +import com.oracle.java.testlibrary.*; + +public class TestUseCountLeadingZerosInstructionOnUnsupportedCPU + extends BMIUnsupportedCPUTest { + + public TestUseCountLeadingZerosInstructionOnUnsupportedCPU() { + super("UseCountLeadingZerosInstruction", LZCNT_WARNING, "lzcnt"); + } + + public static void main(String args[]) throws Throwable { + new TestUseCountLeadingZerosInstructionOnUnsupportedCPU().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/TestUseCountTrailingZerosInstructionOnSupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnSupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify processing of UseCountTrailingZerosInstruction option + * on CPU with TZCNT (BMI1 feature) support. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseCountTrailingZerosInstructionOnSupportedCPU + * BMISupportedCPUTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestUseCountTrailingZerosInstructionOnSupportedCPU + */ + +import sun.hotspot.cpuinfo.CPUInfo; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; + +public class TestUseCountTrailingZerosInstructionOnSupportedCPU + extends BMISupportedCPUTest { + + public TestUseCountTrailingZerosInstructionOnSupportedCPU() { + super("UseCountTrailingZerosInstruction", TZCNT_WARNING, "bmi1"); + } + + @Override + public void runTestCases() throws Throwable { + + super.runTestCases(); + + // verify that option will be disabled if all BMI1 instuctions + // are explicitly disabled + CommandLineOptionTest. + verifyOptionValue("UseCountTrailingZerosInstruction", "false", + "-XX:-UseBMI1Instructions"); + + // verify that option could be turned on even if other BMI1 + // instructions were turned off + CommandLineOptionTest. + verifyOptionValue("UseCountTrailingZerosInstruction", "true", + "-XX:-UseBMI1Instructions", + "-XX:+UseCountTrailingZerosInstruction"); + } + + public static void main(String args[]) throws Throwable { + new TestUseCountTrailingZerosInstructionOnSupportedCPU().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/arguments/TestUseCountTrailingZerosInstructionOnUnsupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnUnsupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify processing of UseCountTrailingZerosInstruction option + * on CPU without TZCNT instuction (BMI1 feature) support. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseCountTrailingZerosInstructionOnUnsupportedCPU + * BMIUnsupportedCPUTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestUseCountTrailingZerosInstructionOnUnsupportedCPU + */ + +import sun.hotspot.cpuinfo.CPUInfo; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; + +public class TestUseCountTrailingZerosInstructionOnUnsupportedCPU + extends BMIUnsupportedCPUTest { + + public TestUseCountTrailingZerosInstructionOnUnsupportedCPU() { + super("UseCountTrailingZerosInstruction", TZCNT_WARNING, "bmi1"); + } + + @Override + public void unsupportedX86CPUTestCases() throws Throwable { + + super.unsupportedX86CPUTestCases(); + + // verify that option will not be turned on during + // UseBMI1Instuctions processing + CommandLineOptionTest. + verifyOptionValue("UseCountTrailingZerosInstruction", "false", + "-XX:+UseBMI1Instructions"); + + CommandLineOptionTest. + verifyOptionValue("UseCountTrailingZerosInstruction", "false", + "-XX:+UseCountTrailingZerosInstruction", + "-XX:+UseBMI1Instructions"); + } + + public static void main(String args[]) throws Throwable { + new TestUseCountTrailingZerosInstructionOnUnsupportedCPU().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/ciReplay/TestVM.sh --- a/test/compiler/ciReplay/TestVM.sh Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/ciReplay/TestVM.sh Wed Oct 15 16:02:50 2014 +0200 @@ -78,8 +78,8 @@ positive_test `expr $stop_level + 50` "TIERED LEVEL $stop_level :: REPLAY" \ "-XX:TieredStopAtLevel=$stop_level" stop_level=`expr $stop_level + 1` + cleanup done - cleanup fi echo TEST PASSED diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/ciReplay/common.sh --- a/test/compiler/ciReplay/common.sh Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/ciReplay/common.sh Wed Oct 15 16:02:50 2014 +0200 @@ -99,14 +99,13 @@ # $2 - non-tiered comp_level nontiered_tests() { level=`grep "^compile " $replay_data | awk '{print $6}'` - # is level available in non-tiere + # is level available in non-tiered if [ "$level" -eq $2 ] then positive_test $1 "NON-TIERED :: AVAILABLE COMP_LEVEL" \ -XX:-TieredCompilation else negative_test `expr $1 + 1` "NON-TIERED :: UNAVAILABLE COMP_LEVEL" \ - negative_test `expr $1 + 1` "NON-TIERED :: UNAVAILABLE COMP_LEVEL" \ -XX:-TieredCompilation fi } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/codegen/BMI1.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/codegen/BMI1.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,301 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Support BMI1 instructions on x86/x64 + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileCommand=compileonly,BMITests.* BMI1 + * + */ + +class MemI { + public int x; + public MemI(int x) { this.x = x; } +} + +class MemL { + public long x; + public MemL(long x) { this.x = x; } +} + +class BMITests { + static int andnl(int src1, int src2) { + return ~src1 & src2; + } + static long andnq(long src1, long src2) { + return ~src1 & src2; + } + static int andnl(int src1, MemI src2) { + return ~src1 & src2.x; + } + static long andnq(long src1, MemL src2) { + return ~src1 & src2.x; + } + static int blsil(int src1) { + return src1 & -src1; + } + static long blsiq(long src1) { + return src1 & -src1; + } + static int blsil(MemI src1) { + return src1.x & -src1.x; + } + static long blsiq(MemL src1) { + return src1.x & -src1.x; + } + static int blsmskl(int src1) { + return (src1 - 1) ^ src1; + } + static long blsmskq(long src1) { + return (src1 - 1) ^ src1; + } + static int blsmskl(MemI src1) { + return (src1.x - 1) ^ src1.x; + } + static long blsmskq(MemL src1) { + return (src1.x - 1) ^ src1.x; + } + static int blsrl(int src1) { + return (src1 - 1) & src1; + } + static long blsrq(long src1) { + return (src1 - 1) & src1; + } + static int blsrl(MemI src1) { + return (src1.x - 1) & src1.x; + } + static long blsrq(MemL src1) { + return (src1.x - 1) & src1.x; + } + static int lzcntl(int src1) { + return Integer.numberOfLeadingZeros(src1); + } + static int lzcntq(long src1) { + return Long.numberOfLeadingZeros(src1); + } + static int tzcntl(int src1) { + return Integer.numberOfTrailingZeros(src1); + } + static int tzcntq(long src1) { + return Long.numberOfTrailingZeros(src1); + } +} + +public class BMI1 { + private final static int ITERATIONS = 1000000; + + public static void main(String[] args) { + int ix = 0x01234567; + int iy = 0x89abcdef; + MemI imy = new MemI(iy); + long lx = 0x0123456701234567L; + long ly = 0x89abcdef89abcdefL; + MemL lmy = new MemL(ly); + + { // match(Set dst (AndI (XorI src1 minus_1) src2)) + int z = BMITests.andnl(ix, iy); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.andnl(ix, iy); + if (ii != z) { + throw new Error("andnl with register failed"); + } + } + } + { // match(Set dst (AndL (XorL src1 minus_1) src2)) + long z = BMITests.andnq(lx, ly); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.andnq(lx, ly); + if (ll != z) { + throw new Error("andnq with register failed"); + } + } + } + { // match(Set dst (AndI (XorI src1 minus_1) (LoadI src2))) + int z = BMITests.andnl(ix, imy); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.andnl(ix, imy); + if (ii != z) { + throw new Error("andnl with memory failed"); + } + } + } + { // match(Set dst (AndL (XorL src1 minus_1) (LoadL src2))) + long z = BMITests.andnq(lx, lmy); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.andnq(lx, lmy); + if (ll != z) { + throw new Error("andnq with memory failed"); + } + } + } + { // match(Set dst (AndI (SubI imm_zero src) src)) + int z = BMITests.blsil(ix); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.blsil(ix); + if (ii != z) { + throw new Error("blsil with register failed"); + } + } + } + { // match(Set dst (AndL (SubL imm_zero src) src)) + long z = BMITests.blsiq(lx); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.blsiq(lx); + if (ll != z) { + throw new Error("blsiq with register failed"); + } + } + } + { // match(Set dst (AndI (SubI imm_zero (LoadI src) ) (LoadI src) )) + int z = BMITests.blsil(imy); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.blsil(imy); + if (ii != z) { + throw new Error("blsil with memory failed"); + } + } + } + { // match(Set dst (AndL (SubL imm_zero (LoadL src) ) (LoadL src) )) + long z = BMITests.blsiq(lmy); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.blsiq(lmy); + if (ll != z) { + throw new Error("blsiq with memory failed"); + } + } + } + + { // match(Set dst (XorI (AddI src minus_1) src)) + int z = BMITests.blsmskl(ix); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.blsmskl(ix); + if (ii != z) { + throw new Error("blsmskl with register failed"); + } + } + } + { // match(Set dst (XorL (AddL src minus_1) src)) + long z = BMITests.blsmskq(lx); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.blsmskq(lx); + if (ll != z) { + throw new Error("blsmskq with register failed"); + } + } + } + { // match(Set dst (XorI (AddI (LoadI src) minus_1) (LoadI src) ) ) + int z = BMITests.blsmskl(imy); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.blsmskl(imy); + if (ii != z) { + throw new Error("blsmskl with memory failed"); + } + } + } + { // match(Set dst (XorL (AddL (LoadL src) minus_1) (LoadL src) ) ) + long z = BMITests.blsmskq(lmy); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.blsmskq(lmy); + if (ll != z) { + throw new Error("blsmskq with memory failed"); + } + } + } + + { // match(Set dst (AndI (AddI src minus_1) src) ) + int z = BMITests.blsrl(ix); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.blsrl(ix); + if (ii != z) { + throw new Error("blsrl with register failed"); + } + } + } + { // match(Set dst (AndL (AddL src minus_1) src) ) + long z = BMITests.blsrq(lx); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.blsrq(lx); + if (ll != z) { + throw new Error("blsrq with register failed"); + } + } + } + { // match(Set dst (AndI (AddI (LoadI src) minus_1) (LoadI src) ) ) + int z = BMITests.blsrl(imy); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.blsrl(imy); + if (ii != z) { + throw new Error("blsrl with memory failed"); + } + } + } + { // match(Set dst (AndL (AddL (LoadL src) minus_1) (LoadL src)) ) + long z = BMITests.blsrq(lmy); + for (int i = 0; i < ITERATIONS; i++) { + long ll = BMITests.blsrq(lmy); + if (ll != z) { + throw new Error("blsrq with memory failed"); + } + } + } + + { + int z = BMITests.lzcntl(ix); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.lzcntl(ix); + if (ii != z) { + throw new Error("lzcntl failed"); + } + } + } + { + int z = BMITests.lzcntq(lx); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.lzcntq(lx); + if (ii != z) { + throw new Error("lzcntq failed"); + } + } + } + + { + int z = BMITests.tzcntl(ix); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.tzcntl(ix); + if (ii != z) { + throw new Error("tzcntl failed"); + } + } + } + { + int z = BMITests.tzcntq(lx); + for (int i = 0; i < ITERATIONS; i++) { + int ii = BMITests.tzcntq(lx); + if (ii != z) { + throw new Error("tzcntq failed"); + } + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/codegen/C1NullCheckOfNullStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/codegen/C1NullCheckOfNullStore.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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 + * 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 8039043 + * @summary Null check is placed in a wrong place when storing a null to an object field on x64 with compressed oops off + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:CompileCommand=compileonly,C1NullCheckOfNullStore::test -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:-UseCompressedOops C1NullCheckOfNullStore + * + */ + +public class C1NullCheckOfNullStore { + private static class Foo { + Object bar; + } + static private void test(Foo x) { + x.bar = null; + } + static public void main(String args[]) { + Foo x = new Foo(); + for (int i = 0; i < 10000; i++) { + test(x); + } + boolean gotNPE = false; + try { + for (int i = 0; i < 10000; i++) { + test(null); + } + } + catch(NullPointerException e) { + gotNPE = true; + } + if (!gotNPE) { + throw new Error("Expecting a NullPointerException"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/debug/VerifyAdapterSharing.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/debug/VerifyAdapterSharing.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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 + * 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 8030783 + * @summary Regression test for 8026478 + * @library /testlibrary + * + */ +import com.oracle.java.testlibrary.*; + +public class VerifyAdapterSharing { + public static void main(String[] args) throws Exception { + ProcessBuilder pb; + OutputAnalyzer out; + + pb = ProcessTools.createJavaProcessBuilder("-Xcomp", "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+VerifyAdapterSharing", "-version"); + out = new OutputAnalyzer(pb.start()); + out.shouldHaveExitValue(0); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/BMITestRunner.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/BMITestRunner.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,442 @@ +/* + * Copyright (c) 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 + * 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. + * + */ + +import java.util.*; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.charset.StandardCharsets; + +import com.oracle.java.testlibrary.*; + +/** + * Test runner that invokes all methods implemented by particular Expr + * with random arguments in two different JVM processes and compares output. + * JVMs being started in different modes - one in int and other in comp + * with C2 and disabled tiered compilation. + */ +public class BMITestRunner { + + enum VMMode { + COMP, INT; + }; + + public static int DEFAULT_ITERATIONS_COUNT = 4000; + + /** + * Execute all methods implemented by expr in int and comp modes + * and compare output. + * Test pass only of output obtained with different VM modes is equal. + * To control behaviour of test following options could be passed: + *
    + *
  • -iterations=<N> each operation implemented by + * expr will be executed N times. Default value + * is 4000.
  • + *
  • -seed=<SEED> arguments for expr's methods + * obtained via RNG initiated with seed SEED. By default + * some random seed will be used.
  • + *
+ * + * @param expr operation that should be tested + * @param testOpts options to control test behaviour + * @param additionalVMOpts additional options for VM + * + * @throws Throwable if test failed. + */ + public static void runTests(Class expr, + String testOpts[], + String... additionalVMOpts) + throws Throwable { + + int seed = new Random().nextInt(); + int iterations = DEFAULT_ITERATIONS_COUNT; + + for (String testOption : testOpts) { + if (testOption.startsWith("-iterations=")) { + iterations = Integer.valueOf(testOption. + replace("-iterations=", "")); + } else if (testOption.startsWith("-seed=")) { + seed = Integer.valueOf(testOption.replace("-seed=", "")); + } + } + + System.out.println("Running test with seed: " + seed); + + OutputAnalyzer intOutput = runTest(expr, VMMode.INT, + additionalVMOpts, + seed, iterations); + OutputAnalyzer compOutput = runTest(expr, VMMode.COMP, + additionalVMOpts, + seed, iterations); + + dumpOutput(intOutput, "int"); + dumpOutput(compOutput, "comp"); + + Asserts.assertStringsEqual(intOutput.getStdout(), + compOutput.getStdout(), + "Results obtained in -Xint and " + + "-Xcomp should be the same."); + } + + /** + * Execute tests on methods implemented by expr in new VM + * started in testVMMode mode. + * + * @param expr operation that should be tested + * @param testVMMode VM mode for test + * @param additionalVMOpts additional options for VM + * @param seed for RNG used it tests + * @param iterations that will be used to invoke expr's methods. + * + * @return OutputAnalyzer for executed test. + * @throws Throwable when something goes wrong. + */ + public static OutputAnalyzer runTest(Class expr, + VMMode testVMMode, + String additionalVMOpts[], + int seed, int iterations) + throws Throwable { + + List vmOpts = new LinkedList(); + + Collections.addAll(vmOpts, additionalVMOpts); + + //setup mode-specific options + switch (testVMMode) { + case INT: + Collections.addAll(vmOpts, new String[] { "-Xint" }); + break; + case COMP: + Collections.addAll(vmOpts, new String[] { + "-Xcomp", + "-XX:-TieredCompilation", + String.format("-XX:CompileCommand=compileonly,%s::*", + expr.getName()) + }); + break; + } + + Collections.addAll(vmOpts, new String[] { + "-XX:+DisplayVMOutputToStderr", + Executor.class.getName(), + expr.getName(), + new Integer(seed).toString(), + new Integer(iterations).toString() + }); + + OutputAnalyzer outputAnalyzer = ProcessTools. + executeTestJvm(vmOpts.toArray(new String[vmOpts.size()])); + + outputAnalyzer.shouldHaveExitValue(0); + + return outputAnalyzer; + } + + /** + * Dump stdout and stderr of test process to prefix.test.out + * and prefix.test.err respectively. + * + * @param outputAnalyzer OutputAnalyzer whom output should be dumped + * @param prefix Prefix that will be used in file names. + * @throws IOException if unable to dump output to file. + */ + protected static void dumpOutput(OutputAnalyzer outputAnalyzer, + String prefix) + throws IOException { + Files.write(Paths.get(prefix + ".test.out"), + outputAnalyzer.getStdout().getBytes()); + + Files.write(Paths.get(prefix + ".test.err"), + outputAnalyzer.getStderr().getBytes()); + } + + + /** + * Executor that invoke all methods implemented by particular + * Expr instance. + */ + public static class Executor { + + /** + * Usage: BMITestRunner$Executor + */ + public static void main(String args[]) throws Exception { + @SuppressWarnings("unchecked") + Class exprClass = + (Class)Class.forName(args[0]); + Expr expr = exprClass.getConstructor().newInstance(); + Random rng = new Random(Integer.valueOf(args[1])); + int iterations = Integer.valueOf(args[2]); + runTests(expr, iterations, rng); + } + + + public static int[] getIntBitShifts() { + //SIZE+1 shift is for zero. + int data[] = new int[Integer.SIZE+1]; + for (int s = 0; s < data.length; s++) { + data[s] = 1< 0X%x", + value, expr.intExpr(value)); + } + + for (int i = 0; i < iterations; i++) { + int value = rng.nextInt(); + log("UnaryIntReg(0X%x) -> 0X%x", + value, expr.intExpr(value)); + } + } + + public static void runUnaryIntMemTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isUnaryArgumentSupported() + && expr.isIntExprSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int value : getIntBitShifts()) { + log("UnaryIntMem(0X%x) -> 0X%x", + value, expr.intExpr(new Expr.MemI(value))); + } + + for (int i = 0; i < iterations; i++) { + int value = rng.nextInt(); + log("UnaryIntMem(0X%x) -> 0X%x", + value, expr.intExpr(new Expr.MemI(value))); + } + } + + public static void runUnaryLongRegTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isUnaryArgumentSupported() + && expr.isLongExprSupported())) { + return; + } + + for (long value : getLongBitShifts()) { + log("UnaryLongReg(0X%x) -> 0X%x", + value, expr.longExpr(value)); + } + + for (int i = 0; i < iterations; i++) { + long value = rng.nextLong(); + log("UnaryLongReg(0X%x) -> 0X%x", + value, expr.longExpr(value)); + } + } + + public static void runUnaryLongMemTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isUnaryArgumentSupported() + && expr.isLongExprSupported() + && expr.isMemExprSupported())) { + return; + } + + for (long value : getLongBitShifts()) { + log("UnaryLongMem(0X%x) -> 0X%x", + value, expr.longExpr(new Expr.MemL(value))); + } + + for (int i = 0; i < iterations; i++) { + long value = rng.nextLong(); + log("UnaryLongMem(0X%x) -> 0X%x", + value, expr.longExpr(new Expr.MemL(value))); + } + } + + public static void runBinaryRegRegIntTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isIntExprSupported() + && expr.isBinaryArgumentSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + int aValue = rng.nextInt(); + int bValue = rng.nextInt(); + log("BinaryIntRegReg(0X%x, 0X%x) -> 0X%x", + aValue, bValue, expr.intExpr(aValue, bValue)); + } + } + + public static void runBinaryRegMemIntTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isIntExprSupported() + && expr.isBinaryArgumentSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + int aValue = rng.nextInt(); + int bValue = rng.nextInt(); + log("BinaryIntRegMem(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.intExpr(aValue, new Expr.MemI(bValue))); + } + } + + public static void runBinaryMemRegIntTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isIntExprSupported() + && expr.isBinaryArgumentSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + int aValue = rng.nextInt(); + int bValue = rng.nextInt(); + log("BinaryIntMemReg(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.intExpr(new Expr.MemI(aValue), bValue)); + } + } + + public static void runBinaryMemMemIntTest(Expr expr, int iterations, + Random rng) { + if (!(expr.isIntExprSupported() + && expr.isBinaryArgumentSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + int aValue = rng.nextInt(); + int bValue = rng.nextInt(); + log("BinaryIntMemMem(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.intExpr(new Expr.MemI(aValue), + new Expr.MemI(bValue))); + } + } + + public static void runBinaryRegRegLongTest(Expr expr, + int iterations, + Random rng) { + if (!(expr.isLongExprSupported() + && expr.isBinaryArgumentSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + long aValue = rng.nextLong(); + long bValue = rng.nextLong(); + log("BinaryLongRegReg(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.longExpr(aValue, bValue)); + } + } + + public static void runBinaryRegMemLongTest(Expr expr, + int iterations, + Random rng) { + if (!(expr.isLongExprSupported() + && expr.isBinaryArgumentSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + long aValue = rng.nextLong(); + long bValue = rng.nextLong(); + log("BinaryLongRegMem(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.longExpr(aValue, new Expr.MemL(bValue))); + } + } + + public static void runBinaryMemRegLongTest(Expr expr, + int iterations, + Random rng) { + if (!(expr.isLongExprSupported() + && expr.isBinaryArgumentSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + long aValue = rng.nextLong(); + long bValue = rng.nextLong(); + log("BinaryLongMemReg(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.longExpr(new Expr.MemL(aValue), bValue)); + } + } + + public static void runBinaryMemMemLongTest(Expr expr, + int iterations, + Random rng) { + if (!(expr.isLongExprSupported() + && expr.isBinaryArgumentSupported() + && expr.isMemExprSupported())) { + return; + } + + for (int i = 0; i < iterations; i++) { + long aValue = rng.nextLong(); + long bValue = rng.nextLong(); + log("BinaryLongMemMem(0X%x, 0X%x) -> 0X%x", aValue, bValue, + expr.longExpr(new Expr.MemL(aValue), + new Expr.MemL(bValue))); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/Expr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/Expr.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 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 + * 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. + * + */ + +/** + * Expression that should be replaced by particular instrinsic + * or intruction during compilation. + */ + +public abstract class Expr { + + public static class MemI { + public MemI(int i) { + this.value = i; + } + + public int value; + } + + public static class MemL { + public MemL(long l) { + this.value = l; + } + + public long value; + } + + public boolean isUnaryArgumentSupported() { + return false; + } + + public boolean isIntExprSupported() { + return false; + } + + public boolean isBinaryArgumentSupported() { + return false; + } + + public boolean isLongExprSupported() { + return false; + } + + public boolean isMemExprSupported() { + return false; + } + + public int intExpr(int reg) { + throw new UnsupportedOperationException(); + } + + public int intExpr(MemI mem) { + throw new UnsupportedOperationException(); + } + + public int intExpr(int a, int b) { + throw new UnsupportedOperationException(); + } + + public int intExpr(int a, MemI b) { + throw new UnsupportedOperationException(); + } + + public int intExpr(MemI a, int b) { + throw new UnsupportedOperationException(); + } + + public int intExpr(MemI a, MemI b) { + throw new UnsupportedOperationException(); + } + + public long longExpr(long reg) { + throw new UnsupportedOperationException(); + } + + public long longExpr(MemL mem) { + throw new UnsupportedOperationException(); + } + + public long longExpr(long a, long b) { + throw new UnsupportedOperationException(); + } + + public long longExpr(long a, MemL b) { + throw new UnsupportedOperationException(); + } + + public long longExpr(MemL a, long b) { + throw new UnsupportedOperationException(); + } + + public long longExpr(MemL a, MemL b) { + throw new UnsupportedOperationException(); + } + + public static class BMIExpr extends Expr { + + public boolean isMemExprSupported() { + return true; + } + } + + public static class BMIBinaryExpr extends BMIExpr { + + public boolean isBinaryArgumentSupported() { + return true; + } + + } + + public static class BMIUnaryExpr extends BMIExpr { + public boolean isUnaryArgumentSupported() { + return true; + } + } + + public static class BMIBinaryIntExpr extends BMIBinaryExpr { + public boolean isIntExprSupported() { + return true; + } + } + + public static class BMIBinaryLongExpr extends BMIBinaryExpr { + public boolean isLongExprSupported() { + return true; + } + } + + public static class BMIUnaryIntExpr extends BMIUnaryExpr { + public boolean isIntExprSupported() { + return true; + } + } + + public static class BMIUnaryLongExpr extends BMIUnaryExpr { + public boolean isLongExprSupported() { + return true; + } + } + + public static class BitCountingExpr extends Expr { + public boolean isUnaryArgumentSupported() { + return true; + } + } + + public static class BitCountingIntExpr extends BitCountingExpr { + public boolean isIntExprSupported() { + return true; + } + } + + public static class BitCountingLongExpr extends BitCountingExpr { + public boolean isLongExprSupported() { + return true; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestAndnI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestAndnI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of ANDN instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestAndnI BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestAndnI + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestAndnI { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. "+ + "Test skipped."); + return; + } + + BMITestRunner.runTests(AndnIExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(AndnICommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class AndnIExpr extends Expr.BMIBinaryIntExpr { + + public int intExpr(int src1, int src2) { + return ~src1 & src2; + } + + public int intExpr(int src1, Expr.MemI src2) { + return ~src1 & src2.value; + } + + public int intExpr(Expr.MemI src1, int src2) { + return ~src1.value & src2; + } + + public int intExpr(Expr.MemI src1, Expr.MemI src2) { + return ~src1.value & src2.value; + } + } + + public static class AndnICommutativeExpr extends Expr.BMIBinaryIntExpr { + + public int intExpr(int src1, int src2) { + return src1 & ~src2; + } + + public int intExpr(int src1, Expr.MemI src2) { + return src1 & ~src2.value; + } + + public int intExpr(Expr.MemI src1, int src2) { + return src1.value & ~src2; + } + + public int intExpr(Expr.MemI src1, Expr.MemI src2) { + return src1.value & ~src2.value; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestAndnL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestAndnL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of ANDN instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestAndnL BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestAndnL + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestAndnL { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(AndnLExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(AndnLCommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class AndnLExpr extends Expr.BMIBinaryLongExpr { + + public long longExpr(long src1, long src2) { + return ~src1 & src2; + } + + public long longExpr(long src1, Expr.MemL src2) { + return ~src1 & src2.value; + } + + public long longExpr(Expr.MemL src1, long src2) { + return ~src1.value & src2; + } + + public long longExpr(Expr.MemL src1, Expr.MemL src2) { + return ~src1.value & src2.value; + } + + + } + + public static class AndnLCommutativeExpr extends Expr.BMIBinaryLongExpr { + + public long longExpr(long src1, long src2) { + return src1 & ~src2; + } + + public long longExpr(long src1, Expr.MemL src2) { + return src1 & ~src2.value; + } + + public long longExpr(Expr.MemL src1, long src2) { + return src1.value & ~src2; + } + + public long longExpr(Expr.MemL src1, Expr.MemL src2) { + return src1.value & ~src2.value; + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestBlsiI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestBlsiI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of BLSI instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestBlsiI BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestBlsiI + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestBlsiI { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(BlsiIExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(BlsiICommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class BlsiIExpr extends Expr.BMIUnaryIntExpr { + + public int intExpr(int src) { + return -src & src; + } + + public int intExpr(Expr.MemI src) { + return -src.value & src.value; + } + + } + + public static class BlsiICommutativeExpr extends Expr.BMIUnaryIntExpr { + + public int intExpr(int src) { + return src & -src; + } + + public int intExpr(Expr.MemI src) { + return src.value & -src.value; + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestBlsiL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestBlsiL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of BLSI instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestBlsiL BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestBlsiL + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestBlsiL { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(BlsiLExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(BlsiLCommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class BlsiLExpr extends Expr.BMIUnaryLongExpr { + + public long longExpr(long src) { + return -src & src; + } + + public long longExpr(Expr.MemL src) { + return -src.value & src.value; + } + + } + + public static class BlsiLCommutativeExpr extends Expr.BMIUnaryLongExpr { + + public long longExpr(long src) { + return src & -src; + } + + public long longExpr(Expr.MemL src) { + return src.value & -src.value; + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestBlsmskI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestBlsmskI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of BLSMSK instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestBlsmskI BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestBlsmskI + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestBlsmskI { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(BlsmskIExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(BlsmskICommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class BlsmskIExpr extends Expr.BMIUnaryIntExpr { + + public int intExpr(int src) { + return (src - 1) ^ src; + } + + public int intExpr(Expr.MemI src) { + return (src.value - 1) ^ src.value; + } + + } + + public static class BlsmskICommutativeExpr extends Expr.BMIUnaryIntExpr { + + public int intExpr(int src) { + return src ^ (src - 1); + } + + public int intExpr(Expr.MemI src) { + return src.value ^ (src.value - 1); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestBlsmskL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestBlsmskL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of BLSMSK instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestBlsmskL BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestBlsmskL + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestBlsmskL { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(BlsmskLExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(BlsmskLCommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class BlsmskLExpr + extends Expr.BMIUnaryLongExpr { + + public long longExpr(long src) { + return (src - 1) ^ src; + } + + public long longExpr(Expr.MemL src) { + return (src.value - 1) ^ src.value; + } + + } + + public static class BlsmskLCommutativeExpr + extends Expr.BMIUnaryLongExpr { + + public long longExpr(long src) { + return src ^ (src - 1); + } + + public long longExpr(Expr.MemL src) { + return src.value ^ (src.value - 1); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestBlsrI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestBlsrI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of BLSR instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestBlsrI BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestBlsrI + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestBlsrI { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(BlsrIExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(BlsrICommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class BlsrIExpr extends Expr.BMIUnaryIntExpr { + + public int intExpr(int src) { + return (src - 1) & src; + } + + public int intExpr(Expr.MemI src) { + return (src.value - 1) & src.value; + } + + } + + public static class BlsrICommutativeExpr extends Expr.BMIUnaryIntExpr { + + public int intExpr(int src) { + return src & (src - 1); + } + + public int intExpr(Expr.MemI src) { + return src.value & (src.value - 1); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestBlsrL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestBlsrL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of BLSR instruction + * @library /testlibrary /testlibrary/whitebox + * @build TestBlsrL BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestBlsrL + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestBlsrL { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(BlsrLExpr.class, args, + "-XX:+UseBMI1Instructions"); + BMITestRunner.runTests(BlsrLCommutativeExpr.class, args, + "-XX:+UseBMI1Instructions"); + } + + public static class BlsrLExpr extends Expr.BMIUnaryLongExpr { + + public long longExpr(long src) { + return (src - 1) & src; + } + + public long longExpr(Expr.MemL src) { + return (src.value - 1) & src.value; + } + + } + + public static class BlsrLCommutativeExpr extends Expr.BMIUnaryLongExpr { + + public long longExpr(long src) { + return src & (src - 1); + } + + public long longExpr(Expr.MemL src) { + return src.value & (src.value - 1); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestLzcntI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestLzcntI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of intrinsic + * @library /testlibrary /testlibrary/whitebox + * @build TestLzcntI BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestLzcntI + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestLzcntI { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("lzcnt")) { + System.out.println("CPU does not support lzcnt feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(LzcntIExpr.class, args, + "-XX:+UseCountLeadingZerosInstruction"); + } + + public static class LzcntIExpr extends Expr.BitCountingIntExpr { + + public int intExpr(int src) { + return Integer.numberOfLeadingZeros(src); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestLzcntL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestLzcntL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of intrinsic + * @library /testlibrary /testlibrary/whitebox + * @build TestLzcntL BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestLzcntL + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestLzcntL { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("lzcnt")) { + System.out.println("CPU does not support lzcnt feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(LzcntLExpr.class, args, + "-XX:+UseCountLeadingZerosInstruction"); + } + + public static class LzcntLExpr extends Expr.BitCountingLongExpr { + + public long longExpr(long src) { + return Long.numberOfLeadingZeros(src); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestTzcntI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestTzcntI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of intrinsic + * @library /testlibrary /testlibrary/whitebox + * @build TestTzcntI BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestTzcntI + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestTzcntI { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(TzcntIExpr.class, args, + "-XX:+UseCountTrailingZerosInstruction"); + } + + public static class TzcntIExpr extends Expr.BitCountingIntExpr { + + public int intExpr(int src) { + return Integer.numberOfTrailingZeros(src); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/TestTzcntL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/TestTzcntL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @summary Verify that results of computations are the same w/ + * and w/o usage of intrinsic + * @library /testlibrary /testlibrary/whitebox + * @build TestTzcntL BMITestRunner Expr + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestTzcntL + */ + +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestTzcntL { + + public static void main(String args[]) throws Throwable { + if (!CPUInfo.hasFeature("bmi1")) { + System.out.println("CPU does not support bmi1 feature. " + + "Test skipped."); + return; + } + + BMITestRunner.runTests(TzcntLExpr.class, args, + "-XX:+UseCountTrailingZerosInstruction"); + } + + public static class TzcntLExpr extends Expr.BitCountingLongExpr { + + public long longExpr(long src) { + return Long.numberOfTrailingZeros(src); + } + + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/AddnTestI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build AddnTestI + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AddnTestI + */ + +import java.lang.reflect.Method; + +public class AddnTestI extends BmiIntrinsicBase.BmiTestCase { + + protected AddnTestI(Method method) { + super(method); + // from intel manual VEX.NDS.LZ.0F38.W0 F2 /r, example c4e260f2c2 + instrMask = new byte[]{ + (byte) 0xFF, + (byte) 0x1F, + (byte) 0x00, + (byte) 0xFF}; + instrPattern = new byte[]{ + (byte) 0xC4, // prefix for 3-byte VEX instruction + (byte) 0x02, // 00010 implied 0F 38 leading opcode bytes + (byte) 0x00, + (byte) 0xF2}; + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(AddnTestI::new, TestAndnI.AndnIExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(AddnTestI::new, TestAndnI.AndnICommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/AddnTestL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build AddnTestL + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AddnTestL + */ + +import java.lang.reflect.Method; + +public class AddnTestL extends AddnTestI { + + protected AddnTestL(Method method) { + super(method); + isLongOperation = true; + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(AddnTestL::new, TestAndnL.AndnLExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(AddnTestL::new, TestAndnL.AndnLCommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build BlsiTestI + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsiTestI + */ + +import java.lang.reflect.Method; + +public class BlsiTestI extends BmiIntrinsicBase.BmiTestCase { + + protected BlsiTestI(Method method) { + super(method); + //from intel manual VEX.NDD.LZ.0F38.W0 F3 /3 + instrMask = new byte[]{ + (byte) 0xFF, + (byte) 0x1F, + (byte) 0x00, + (byte) 0xFF, + (byte) 0b0011_1000}; + instrPattern = new byte[]{ + (byte) 0xC4, // prefix for 3-byte VEX instruction + (byte) 0x02, // 00010 implied 0F 38 leading opcode bytes + (byte) 0x00, + (byte) 0xF3, + (byte) 0b0001_1000}; // bits 543 == 011 (3) + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(BlsiTestI::new, TestBlsiI.BlsiIExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(BlsiTestI::new, TestBlsiI.BlsiICommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build BlsiTestL + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsiTestL + */ + +import java.lang.reflect.Method; + +public class BlsiTestL extends BlsiTestI { + + protected BlsiTestL(Method method) { + super(method); + isLongOperation = true; + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(BlsiTestL::new, TestBlsiL.BlsiLExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(BlsiTestL::new, TestBlsiL.BlsiLCommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build BlsmskTestI + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsmskTestI + */ + +import java.lang.reflect.Method; + +public class BlsmskTestI extends BmiIntrinsicBase.BmiTestCase { + + protected BlsmskTestI(Method method) { + super(method); + //from intel manual VEX.NDD.LZ.0F38.W0 F3 /2 + instrMask = new byte[]{ + (byte) 0xFF, + (byte) 0x1F, + (byte) 0x00, + (byte) 0xFF, + (byte) 0b0011_1000}; + instrPattern = new byte[]{ + (byte) 0xC4, // prefix for 3-byte VEX instruction + (byte) 0x02, // 00010 implied 0F 38 leading opcode bytes + (byte) 0x00, + (byte) 0xF3, + (byte) 0b0001_0000}; // bits 543 == 011 (3) + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(BlsmskTestI::new, TestBlsmskI.BlsmskIExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(BlsmskTestI::new, TestBlsmskI.BlsmskICommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build BlsmskTestL + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsmskTestL + */ + +import java.lang.reflect.Method; + +public class BlsmskTestL extends BlsmskTestI { + + protected BlsmskTestL(Method method) { + super(method); + isLongOperation = true; + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(BlsmskTestL::new, TestBlsmskL.BlsmskLExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(BlsmskTestL::new, TestBlsmskL.BlsmskLCommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build BlsrTestI + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsrTestI + */ + +import java.lang.reflect.Method; + +public class BlsrTestI extends BmiIntrinsicBase.BmiTestCase { + + protected BlsrTestI(Method method) { + super(method); + //from intel manual VEX.NDD.LZ.0F38.W0 F3 /1 + instrMask = new byte[]{ + (byte) 0xFF, + (byte) 0x1F, + (byte) 0x00, + (byte) 0xFF, + (byte) 0b0011_1000}; + instrPattern = new byte[]{ + (byte) 0xC4, // prefix for 3-byte VEX instruction + (byte) 0x02, // 00010 implied 0F 38 leading opcode bytes + (byte) 0x00, + (byte) 0xF3, + (byte) 0b0000_1000}; // bits 543 == 011 (3) + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(BlsrTestI::new, TestBlsrI.BlsrIExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(BlsrTestI::new, TestBlsrI.BlsrICommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build BlsrTestL + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsrTestL + */ + +import java.lang.reflect.Method; + +public class BlsrTestL extends BlsrTestI { + + protected BlsrTestL(Method method) { + super(method); + isLongOperation = true; + } + + public static void main(String[] args) throws Exception { + BmiIntrinsicBase.verifyTestCase(BlsrTestL::new, TestBlsrL.BlsrLExpr.class.getDeclaredMethods()); + BmiIntrinsicBase.verifyTestCase(BlsrTestL::new, TestBlsrL.BlsrLCommutativeExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/BmiIntrinsicBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,186 @@ +/* + * Copyright (c) 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 + * 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. + * + */ + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.Utils; +import sun.hotspot.code.NMethod; +import sun.hotspot.cpuinfo.CPUInfo; + +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import java.util.function.Function; + +public class BmiIntrinsicBase extends CompilerWhiteBoxTest { + + protected BmiIntrinsicBase(BmiTestCase testCase) { + super(testCase); + } + + public static void verifyTestCase(Function constructor, Method... methods) throws Exception { + for (Method method : methods) { + new BmiIntrinsicBase(constructor.apply(method)).test(); + } + } + + @Override + protected void test() throws Exception { + BmiTestCase bmiTestCase = (BmiTestCase) testCase; + + if (!(Platform.isX86() || Platform.isX64())) { + System.out.println("Unsupported platform, test SKIPPED"); + return; + } + + if (!Platform.isServer()) { + System.out.println("Not server VM, test SKIPPED"); + return; + } + + if (!CPUInfo.hasFeature(bmiTestCase.getCpuFlag())) { + System.out.println("Unsupported hardware, no required CPU flag " + bmiTestCase.getCpuFlag() + " , test SKIPPED"); + return; + } + + if (!Boolean.valueOf(getVMOption(bmiTestCase.getVMFlag()))) { + System.out.println("VM flag " + bmiTestCase.getVMFlag() + " disabled, test SKIPPED"); + return; + } + + System.out.println(testCase.name()); + + switch (MODE) { + case "compiled mode": + case "mixed mode": + if (TIERED_COMPILATION && TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_MAX) { + System.out.println("TieredStopAtLevel value (" + TIERED_STOP_AT_LEVEL + ") is too low, test SKIPPED"); + return; + } + deoptimize(); + compileAtLevelAndCheck(CompilerWhiteBoxTest.COMP_LEVEL_MAX); + break; + case "interpreted mode": // test is not applicable in this mode; + System.err.println("Warning: This test is not applicable in mode: " + MODE); + break; + default: + throw new AssertionError("Test bug, unknown VM mode: " + MODE); + } + } + + protected void compileAtLevelAndCheck(int level) { + WHITE_BOX.enqueueMethodForCompilation(method, level); + waitBackgroundCompilation(); + checkCompilation(method, level); + checkEmittedCode(method); + } + + protected void checkCompilation(Executable executable, int level) { + if (!WHITE_BOX.isMethodCompiled(executable)) { + throw new AssertionError("Test bug, expected compilation (level): " + level + ", but not compiled" + WHITE_BOX.isMethodCompilable(executable, level)); + } + final int compilationLevel = WHITE_BOX.getMethodCompilationLevel(executable); + if (compilationLevel != level) { + throw new AssertionError("Test bug, expected compilation (level): " + level + ", but level: " + compilationLevel); + } + } + + protected void checkEmittedCode(Executable executable) { + final byte[] nativeCode = NMethod.get(executable, false).insts; + if (!((BmiTestCase) testCase).verifyPositive(nativeCode)) { + throw new AssertionError(testCase.name() + "CPU instructions expected not found: " + Utils.toHexString(nativeCode)); + } else { + System.out.println("CPU instructions found, PASSED"); + } + } + + abstract static class BmiTestCase implements CompilerWhiteBoxTest.TestCase { + private final Method method; + protected byte[] instrMask; + protected byte[] instrPattern; + protected boolean isLongOperation; + + public BmiTestCase(Method method) { + this.method = method; + } + + @Override + public String name() { + return method.toGenericString(); + } + + @Override + public Executable getExecutable() { + return method; + } + + @Override + public Callable getCallable() { + return null; + } + + @Override + public boolean isOsr() { + return false; + } + + protected int countCpuInstructions(byte[] nativeCode) { + int count = 0; + int patternSize = Math.min(instrMask.length, instrPattern.length); + boolean found; + Asserts.assertGreaterThan(patternSize, 0); + for (int i = 0, n = nativeCode.length - patternSize; i < n; i++) { + found = true; + for (int j = 0; j < patternSize; j++) { + if ((nativeCode[i + j] & instrMask[j]) != instrPattern[j]) { + found = false; + break; + } + } + if (found) { + ++count; + i += patternSize - 1; + } + } + return count; + } + + public boolean verifyPositive(byte[] nativeCode) { + final int cnt = countCpuInstructions(nativeCode); + if (Platform.isX86()) { + return cnt >= (isLongOperation ? 2 : 1); + } else { + return Platform.isX64() && cnt >= 1; + } + } + + protected String getCpuFlag() { + return "bmi1"; + } + + protected String getVMFlag() { + return "UseBMI1Instructions"; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build LZcntTestI + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountLeadingZerosInstruction LZcntTestI + */ + +import java.lang.reflect.Method; + +public class LZcntTestI extends BmiIntrinsicBase.BmiTestCase { + + protected LZcntTestI(Method method) { + super(method); + instrMask = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + instrPattern = new byte[]{(byte) 0xF3, (byte) 0x0F, (byte) 0xBD}; + } + + public static void main(String[] args) throws Exception { + // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods + System.out.println("class java.lang.Integer should be loaded. Proof: " + Integer.class); + BmiIntrinsicBase.verifyTestCase(LZcntTestI::new, TestLzcntI.LzcntIExpr.class.getDeclaredMethods()); + } + + @Override + protected String getVMFlag() { + return "UseCountLeadingZerosInstruction"; + } + + @Override + protected String getCpuFlag() { + return "lzcnt"; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build LZcntTestL + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountLeadingZerosInstruction LZcntTestL + */ + +import com.oracle.java.testlibrary.Platform; + +import java.lang.reflect.Method; + +public class LZcntTestL extends LZcntTestI { + + protected LZcntTestL(Method method) { + super(method); + isLongOperation = true; + if (Platform.isX64()) { + instrMask = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF}; + instrPattern = new byte[]{(byte) 0xF3, (byte) 0x00, (byte) 0x0F, (byte) 0xBD}; + } + } + + public static void main(String[] args) throws Exception { + // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods + System.out.println("classes java.lang.Long should be loaded. Proof: " + Long.class); + BmiIntrinsicBase.verifyTestCase(LZcntTestL::new, TestLzcntL.LzcntLExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build TZcntTestI + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountTrailingZerosInstruction TZcntTestI + */ + +import java.lang.reflect.Method; + +public class TZcntTestI extends BmiIntrinsicBase.BmiTestCase { + + protected TZcntTestI(Method method) { + super(method); + instrMask = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + instrPattern = new byte[]{(byte) 0xF3, (byte) 0x0F, (byte) 0xBC}; + } + + public static void main(String[] args) throws Exception { + // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods + System.out.println("class java.lang.Integer should be loaded. Proof: " + Integer.class); + BmiIntrinsicBase.verifyTestCase(TZcntTestI::new, TestTzcntI.TzcntIExpr.class.getDeclaredMethods()); + } + + @Override + protected String getVMFlag() { + return "UseCountTrailingZerosInstruction"; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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 + * 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 8031321 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. + * @build TZcntTestL + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountTrailingZerosInstruction TZcntTestL + */ + +import com.oracle.java.testlibrary.Platform; + +import java.lang.reflect.Method; + +public class TZcntTestL extends TZcntTestI { + + protected TZcntTestL(Method method) { + super(method); + isLongOperation = true; + if (Platform.isX64()) { + instrMask = new byte[]{(byte) 0xFF, (byte) 0x00, (byte) 0xFF, (byte) 0xFF}; + instrPattern = new byte[]{(byte) 0xF3, (byte) 0x00, (byte) 0x0F, (byte) 0xBC}; + } + isLongOperation = true; + } + + public static void main(String[] args) throws Exception { + // j.l.Integer and Long should be loaded to allow a compilation of the methods that use their methods + System.out.println("classes java.lang.Long should be loaded. Proof: " + Long.class); + BmiIntrinsicBase.verifyTestCase(TZcntTestL::new, TestTzcntL.TzcntLExpr.class.getDeclaredMethods()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/clone/TestObjectClone.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/clone/TestObjectClone.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 + * 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 8033626 + * @summary assert(ex_map->jvms()->same_calls_as(_exceptions->jvms())) failed: all collected exceptions must come from the same place + * @library /testlibrary + * @run main/othervm -XX:-TieredCompilation -Xbatch -XX:CompileOnly=TestObjectClone::f TestObjectClone + */ +import com.oracle.java.testlibrary.Asserts; + +public class TestObjectClone implements Cloneable { + static class A extends TestObjectClone {} + static class B extends TestObjectClone { + public B clone() { + return (B)TestObjectClone.b; + } + } + static class C extends TestObjectClone { + public C clone() { + return (C)TestObjectClone.c; + } + } + static class D extends TestObjectClone { + public D clone() { + return (D)TestObjectClone.d; + } + } + static TestObjectClone a = new A(), b = new B(), c = new C(), d = new D(); + + public static Object f(TestObjectClone o) throws CloneNotSupportedException { + // Polymorphic call site: >90% Object::clone / <10% other methods + return o.clone(); + } + + public static void main(String[] args) throws Exception { + TestObjectClone[] params1 = {a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, + b, c, d}; + + for (int i = 0; i < 15000; i++) { + f(params1[i % params1.length]); + } + + Asserts.assertTrue(f(a) != a); + Asserts.assertTrue(f(b) == b); + Asserts.assertTrue(f(c) == c); + Asserts.assertTrue(f(d) == d); + + try { + f(null); + throw new AssertionError(""); + } catch (NullPointerException e) { /* expected */ } + + System.out.println("TEST PASSED"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/hashcode/TestHashCode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/hashcode/TestHashCode.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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 + * 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 8011646 + * @summary SEGV in compiled code with loop predication + * @run main/othervm -XX:-TieredCompilation -XX:CompileOnly=TestHashCode.m1,Object.hashCode TestHashCode + * + */ + +public class TestHashCode { + static class A { + int i; + } + + static class B extends A { + } + + static boolean crash = false; + + static A m2() { + if (crash) { + return null; + } + return new A(); + } + + static int m1(A aa) { + int res = 0; + for (int i = 0; i < 10; i++) { + A a = m2(); + int j = a.i; + if (aa instanceof B) { + } + res += a.hashCode(); + } + return res; + } + + public static void main(String[] args) { + A a = new A(); + for (int i = 0; i < 20000; i++) { + m1(a); + } + crash = true; + try { + m1(a); + } catch (NullPointerException e) { + System.out.println("Test passed"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactICondTest.java --- a/test/compiler/intrinsics/mathexact/AddExactICondTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactICondTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8024924 * @summary Test non constant addExact * @compile AddExactICondTest.java - * @run main AddExactICondTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactICondTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactIConstantTest.java --- a/test/compiler/intrinsics/mathexact/AddExactIConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactIConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8024924 * @summary Test constant addExact * @compile AddExactIConstantTest.java Verify.java - * @run main AddExactIConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactIConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactILoadTest.java --- a/test/compiler/intrinsics/mathexact/AddExactILoadTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactILoadTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8024924 * @summary Test non constant addExact * @compile AddExactILoadTest.java Verify.java - * @run main AddExactILoadTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactILoadTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java --- a/test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8024924 * @summary Test non constant addExact * @compile AddExactILoopDependentTest.java Verify.java - * @run main AddExactILoopDependentTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactILoopDependentTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java --- a/test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8024924 * @summary Test non constant addExact * @compile AddExactINonConstantTest.java Verify.java - * @run main AddExactINonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactINonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java --- a/test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8025657 * @summary Test repeating addExact * @compile AddExactIRepeatTest.java Verify.java - * @run main AddExactIRepeatTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactIRepeatTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactLConstantTest.java --- a/test/compiler/intrinsics/mathexact/AddExactLConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactLConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant addExact * @compile AddExactLConstantTest.java Verify.java - * @run main AddExactLConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactLConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java --- a/test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant addExact * @compile AddExactLNonConstantTest.java Verify.java - * @run main AddExactLNonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main AddExactLNonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/CompareTest.java --- a/test/compiler/intrinsics/mathexact/CompareTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/CompareTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026722 * @summary Verify that the compare after addExact is a signed compare * @compile CompareTest.java - * @run main CompareTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main CompareTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/DecExactITest.java --- a/test/compiler/intrinsics/mathexact/DecExactITest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/DecExactITest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test decrementExact * @compile DecExactITest.java Verify.java - * @run main DecExactITest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main DecExactITest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/DecExactLTest.java --- a/test/compiler/intrinsics/mathexact/DecExactLTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/DecExactLTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test decrementExact * @compile DecExactLTest.java Verify.java - * @run main DecExactLTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main DecExactLTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/GVNTest.java --- a/test/compiler/intrinsics/mathexact/GVNTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/GVNTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8028207 * @summary Verify that GVN doesn't mess up the two addExacts * @compile GVNTest.java - * @run main GVNTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main GVNTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/IncExactITest.java --- a/test/compiler/intrinsics/mathexact/IncExactITest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/IncExactITest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test incrementExact * @compile IncExactITest.java Verify.java - * @run main IncExactITest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main IncExactITest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/IncExactLTest.java --- a/test/compiler/intrinsics/mathexact/IncExactLTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/IncExactLTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test incrementExact * @compile IncExactLTest.java Verify.java - * @run main IncExactLTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main IncExactLTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactICondTest.java --- a/test/compiler/intrinsics/mathexact/MulExactICondTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactICondTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test multiplyExact as condition * @compile MulExactICondTest.java - * @run main MulExactICondTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactICondTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactIConstantTest.java --- a/test/compiler/intrinsics/mathexact/MulExactIConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactIConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant multiplyExact * @compile MulExactIConstantTest.java Verify.java - * @run main MulExactIConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactIConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactILoadTest.java --- a/test/compiler/intrinsics/mathexact/MulExactILoadTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactILoadTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test multiplyExact * @compile MulExactILoadTest.java Verify.java - * @run main MulExactILoadTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactILoadTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java --- a/test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test loop dependent multiplyExact * @compile MulExactILoopDependentTest.java Verify.java - * @run main MulExactILoopDependentTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactILoopDependentTest * */ public class MulExactILoopDependentTest { diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java --- a/test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant multiplyExact * @compile MulExactINonConstantTest.java Verify.java - * @run main MulExactINonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactINonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java --- a/test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test repeating multiplyExact * @compile MulExactIRepeatTest.java Verify.java - * @run main MulExactIRepeatTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactIRepeatTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactLConstantTest.java --- a/test/compiler/intrinsics/mathexact/MulExactLConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactLConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant mulExact * @compile MulExactLConstantTest.java Verify.java - * @run main MulExactLConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactLConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java --- a/test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant mulExact * @compile MulExactLNonConstantTest.java Verify.java - * @run main MulExactLNonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main MulExactLNonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NegExactIConstantTest.java --- a/test/compiler/intrinsics/mathexact/NegExactIConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NegExactIConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant negExact * @compile NegExactIConstantTest.java Verify.java - * @run main NegExactIConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NegExactIConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NegExactILoadTest.java --- a/test/compiler/intrinsics/mathexact/NegExactILoadTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NegExactILoadTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,14 +26,14 @@ * @bug 8026844 * @summary Test negExact * @compile NegExactILoadTest.java Verify.java - * @run main NegExactILoadTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NegExactILoadTest * */ public class NegExactILoadTest { public static void main(String[] args) { - Verify.LoadTest.init(); - Verify.LoadTest.verify(new Verify.UnaryToBinary(new Verify.NegExactI())); + Verify.LoadTest.init(); + Verify.LoadTest.verify(new Verify.UnaryToBinary(new Verify.NegExactI())); } } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java --- a/test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test negExact loop dependent * @compile NegExactILoopDependentTest.java Verify.java - * @run main NegExactILoopDependentTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NegExactILoopDependentTest * */ public class NegExactILoopDependentTest { diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java --- a/test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant negExact * @compile NegExactINonConstantTest.java Verify.java - * @run main NegExactINonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NegExactINonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NegExactLConstantTest.java --- a/test/compiler/intrinsics/mathexact/NegExactLConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NegExactLConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant negExact * @compile NegExactLConstantTest.java Verify.java - * @run main NegExactLConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NegExactLConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java --- a/test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant negExact * @compile NegExactLNonConstantTest.java Verify.java - * @run main NegExactLNonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NegExactLNonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/NestedMathExactTest.java --- a/test/compiler/intrinsics/mathexact/NestedMathExactTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/NestedMathExactTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8027444 * @summary Test nested loops * @compile NestedMathExactTest.java - * @run main NestedMathExactTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main NestedMathExactTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SplitThruPhiTest.java --- a/test/compiler/intrinsics/mathexact/SplitThruPhiTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SplitThruPhiTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8028198 * @summary Verify that split through phi does the right thing * @compile SplitThruPhiTest.java - * @run main SplitThruPhiTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SplitThruPhiTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactICondTest.java --- a/test/compiler/intrinsics/mathexact/SubExactICondTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactICondTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test subtractExact as condition * @compile SubExactICondTest.java Verify.java - * @run main SubExactICondTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactICondTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactIConstantTest.java --- a/test/compiler/intrinsics/mathexact/SubExactIConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactIConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test constant subtractExact * @compile SubExactIConstantTest.java Verify.java - * @run main SubExactIConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactIConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactILoadTest.java --- a/test/compiler/intrinsics/mathexact/SubExactILoadTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactILoadTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant subtractExact * @compile SubExactILoadTest.java Verify.java - * @run main SubExactILoadTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactILoadTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java --- a/test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant subtractExact * @compile SubExactILoopDependentTest.java Verify.java - * @run main SubExactILoopDependentTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactILoopDependentTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java --- a/test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test non constant subtractExact * @compile SubExactINonConstantTest.java Verify.java - * @run main SubExactINonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactINonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java --- a/test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -26,7 +26,7 @@ * @bug 8026844 * @summary Test repeating subtractExact * @compile SubExactIRepeatTest.java Verify.java - * @run main SubExactIRepeatTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactIRepeatTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactLConstantTest.java --- a/test/compiler/intrinsics/mathexact/SubExactLConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactLConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,7 +27,7 @@ * @bug 8027353 * @summary Test constant subtractExact * @compile SubExactLConstantTest.java Verify.java - * @run main SubExactLConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactLConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java --- a/test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,7 +27,7 @@ * @bug 8027353 * @summary Test non constant subtractExact * @compile SubExactLNonConstantTest.java Verify.java - * @run main SubExactLNonConstantTest -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseMathExactIntrinsics + * @run main SubExactLNonConstantTest * */ diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/Verify.java --- a/test/compiler/intrinsics/mathexact/Verify.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/intrinsics/mathexact/Verify.java Wed Oct 15 16:02:50 2014 +0200 @@ -160,6 +160,7 @@ public static class NonConstantTest { public static java.util.Random rnd = new java.util.Random(); + public static int[] values = new int[] { Integer.MAX_VALUE, Integer.MIN_VALUE }; public static void verify(BinaryMethod method) { for (int i = 0; i < 50000; ++i) { @@ -169,6 +170,10 @@ Verify.verifyBinary(rnd1 + 1, rnd2, method); Verify.verifyBinary(rnd1 - 1, rnd2, method); Verify.verifyBinary(rnd1, rnd2 - 1, method); + Verify.verifyBinary(0, values[0], method); + Verify.verifyBinary(values[0], 0, method); + Verify.verifyBinary(0, values[1], method); + Verify.verifyBinary(values[1], 0, method); } } } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build AddExactIntTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics AddExactIntTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics AddExactIntTest + * @run main Verifier hs_neg.log hs.log + */ + +public class AddExactIntTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.IntTest(MathIntrinsic.IntIntrinsic.Add).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build AddExactLongTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics AddExactLongTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics AddExactLongTest + * @run main Verifier hs_neg.log hs.log + */ + +public class AddExactLongTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.LongTest(MathIntrinsic.LongIntrinsic.Add).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build DecrementExactIntTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics DecrementExactIntTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics DecrementExactIntTest + * @run main Verifier hs_neg.log hs.log + */ + +public class DecrementExactIntTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.IntTest(MathIntrinsic.IntIntrinsic.Decrement).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build DecrementExactLongTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics DecrementExactLongTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics DecrementExactLongTest + * @run main Verifier hs_neg.log hs.log + */ + +public class DecrementExactLongTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.LongTest(MathIntrinsic.LongIntrinsic.Decrement).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build IncrementExactIntTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics IncrementExactIntTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics IncrementExactIntTest + * @run main Verifier hs_neg.log hs.log + */ + +public class IncrementExactIntTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.IntTest(MathIntrinsic.IntIntrinsic.Increment).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build IncrementExactLongTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics IncrementExactLongTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics IncrementExactLongTest + * @run main Verifier hs_neg.log hs.log + */ + +public class IncrementExactLongTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.LongTest(MathIntrinsic.LongIntrinsic.Increment).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import com.oracle.java.testlibrary.Platform; + +import java.io.FileOutputStream; +import java.lang.reflect.Executable; +import java.util.Properties; + +public abstract class IntrinsicBase extends CompilerWhiteBoxTest { + protected String javaVmName; + protected String useMathExactIntrinsics; + + protected IntrinsicBase(TestCase testCase) { + super(testCase); + javaVmName = System.getProperty("java.vm.name"); + useMathExactIntrinsics = getVMOption("UseMathExactIntrinsics"); + } + + @Override + protected void test() throws Exception { + //java.lang.Math should be loaded to allow a compilation of the methods that use Math's method + System.out.println("class java.lang.Math should be loaded. Proof: " + Math.class); + printEnvironmentInfo(); + + int expectedIntrinsicCount = 0; + + switch (MODE) { + case "compiled mode": + case "mixed mode": + if (isServerVM()) { + if (TIERED_COMPILATION) { + int max_level = TIERED_STOP_AT_LEVEL; + expectedIntrinsicCount = (max_level == COMP_LEVEL_MAX) ? 1 : 0; + for (int i = CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE; i <= max_level; ++i) { + deoptimize(); + compileAtLevel(i); + } + } else { + expectedIntrinsicCount = 1; + deoptimize(); + compileAtLevel(CompilerWhiteBoxTest.COMP_LEVEL_MAX); + } + } else { + deoptimize(); + compileAtLevel(CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE); + } + + if (!isIntrinsicSupported()) { + expectedIntrinsicCount = 0; + } + break; + case "interpreted mode": //test is not applicable in this mode; + System.err.println("Warning: This test is not applicable in mode: " + MODE); + break; + default: + throw new RuntimeException("Test bug, unknown VM mode: " + MODE); + } + + System.out.println("Expected intrinsic count is " + expectedIntrinsicCount + " name " + getIntrinsicId()); + + final FileOutputStream out = new FileOutputStream(getVMOption("LogFile") + ".verify.properties"); + Properties expectedProps = new Properties(); + expectedProps.setProperty("intrinsic.name", getIntrinsicId()); + expectedProps.setProperty("intrinsic.expectedCount", String.valueOf(expectedIntrinsicCount)); + expectedProps.store(out, null); + + out.close(); + } + + protected void printEnvironmentInfo() { + System.out.println("java.vm.name=" + javaVmName); + System.out.println("os.arch=" + Platform.getOsArch()); + System.out.println("java.vm.info=" + MODE); + System.out.println("useMathExactIntrinsics=" + useMathExactIntrinsics); + } + + protected void compileAtLevel(int level) { + WHITE_BOX.enqueueMethodForCompilation(method, level); + waitBackgroundCompilation(); + checkCompilation(method, level); + } + + protected void checkCompilation(Executable executable, int level) { + if (!WHITE_BOX.isMethodCompiled(executable)) { + throw new RuntimeException("Test bug, expected compilation (level): " + level + ", but not compiled"); + } + final int compilationLevel = WHITE_BOX.getMethodCompilationLevel(executable); + if (compilationLevel != level) { + if (!(TIERED_COMPILATION && level == COMP_LEVEL_FULL_PROFILE && compilationLevel == COMP_LEVEL_LIMITED_PROFILE)) { //possible case + throw new RuntimeException("Test bug, expected compilation (level): " + level + ", but level: " + compilationLevel); + } + } + } + + protected abstract boolean isIntrinsicSupported(); + + protected abstract String getIntrinsicId(); + + protected boolean isServerVM() { + return javaVmName.toLowerCase().contains("server"); + } + + static class IntTest extends IntrinsicBase { + protected IntTest(MathIntrinsic.IntIntrinsic testCase) { + super(testCase); + } + + @Override + protected boolean isIntrinsicSupported() { + return isServerVM() && Boolean.valueOf(useMathExactIntrinsics) && (Platform.isX86() || Platform.isX64()); + } + + @Override + protected String getIntrinsicId() { + return "_" + testCase.name().toLowerCase() + "ExactI"; + } + } + + static class LongTest extends IntrinsicBase { + protected LongTest(MathIntrinsic.LongIntrinsic testCase) { + super(testCase); + } + + @Override + protected boolean isIntrinsicSupported() { + return isServerVM() && Boolean.valueOf(useMathExactIntrinsics) && Platform.isX64(); + } + + @Override + protected String getIntrinsicId() { + return "_" + testCase.name().toLowerCase() + "ExactL"; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/MathIntrinsic.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/MathIntrinsic.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.lang.reflect.Executable; +import java.util.concurrent.Callable; + +public class MathIntrinsic { + + enum IntIntrinsic implements CompilerWhiteBoxTest.TestCase { + Add { + @Override + Object execMathMethod() { + return intR = Math.addExact(int1, int2); + } + }, + Subtract { + @Override + Object execMathMethod() { + return intR = Math.subtractExact(int1, int2); + } + }, + Multiply { + @Override + Object execMathMethod() { + return intR = Math.multiplyExact(int1, int2); + } + }, + Increment { + @Override + Object execMathMethod() { + return intR = Math.incrementExact(int1); + } + }, + Decrement { + @Override + Object execMathMethod() { + return intR = Math.decrementExact(int1); + } + }, + Negate { + @Override + Object execMathMethod() { + return intR = Math.negateExact(int1); + } + }; + protected int int1; + protected int int2; + protected int intR; + + abstract Object execMathMethod(); + + @Override + public Executable getExecutable() { + try { + return getClass().getDeclaredMethod("execMathMethod"); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Test bug, no such method: " + e); + } + } + + @Override + public Callable getCallable() { + return null; + } + + @Override + public boolean isOsr() { + return false; + } + + } + + enum LongIntrinsic implements CompilerWhiteBoxTest.TestCase { + Add { + @Override + Object execMathMethod() { + return longR = Math.addExact(long1, long2); + } + }, + Subtract { + @Override + Object execMathMethod() { + return longR = Math.subtractExact(long1, long2); + } + }, + Multiply { + @Override + Object execMathMethod() { + return longR = Math.multiplyExact(long1, long2); + } + }, + Increment { + @Override + Object execMathMethod() { + return longR = Math.incrementExact(long1); + } + }, + Decrement { + @Override + Object execMathMethod() { + return longR = Math.decrementExact(long1); + } + }, + Negate { + @Override + Object execMathMethod() { + return longR = Math.negateExact(long1); + } + }; + protected long long1; + protected long long2; + protected long longR; + + abstract Object execMathMethod(); + + @Override + public Executable getExecutable() { + try { + return getClass().getDeclaredMethod("execMathMethod"); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Test bug, no such method: " + e); + } + } + + @Override + public Callable getCallable() { + return null; + } + + @Override + public boolean isOsr() { + return false; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build MultiplyExactIntTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics MultiplyExactIntTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics MultiplyExactIntTest + * @run main Verifier hs_neg.log hs.log + */ + +public class MultiplyExactIntTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.IntTest(MathIntrinsic.IntIntrinsic.Multiply).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build MultiplyExactLongTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics MultiplyExactLongTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics MultiplyExactLongTest + * @run main Verifier hs_neg.log hs.log + */ + +public class MultiplyExactLongTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.LongTest(MathIntrinsic.LongIntrinsic.Multiply).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build NegateExactIntTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics NegateExactIntTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics NegateExactIntTest + * @run main Verifier hs_neg.log hs.log + */ + +public class NegateExactIntTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.IntTest(MathIntrinsic.IntIntrinsic.Negate).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build NegateExactLongTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics NegateExactLongTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics NegateExactLongTest + * @run main Verifier hs_neg.log hs.log + */ + +public class NegateExactLongTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.LongTest(MathIntrinsic.LongIntrinsic.Negate).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build SubtractExactIntTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics SubtractExactIntTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics SubtractExactIntTest + * @run main Verifier hs_neg.log hs.log + + */ + +public class SubtractExactIntTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.IntTest(MathIntrinsic.IntIntrinsic.Subtract).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build SubtractExactLongTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs_neg.log -XX:-UseMathExactIntrinsics SubtractExactLongTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation + * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod + * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics SubtractExactLongTest + * @run main Verifier hs_neg.log hs.log + */ + +public class SubtractExactLongTest { + + public static void main(String[] args) throws Exception { + new IntrinsicBase.LongTest(MathIntrinsic.LongIntrinsic.Subtract).test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/intrinsics/mathexact/sanity/Verifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/intrinsics/mathexact/sanity/Verifier.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.Properties; + +public class Verifier { + + public static void main(String[] args) throws Exception { + if (args.length == 0) + throw new RuntimeException("Test bug, nothing to verify"); + for (String hsLogFile : args) { + verify(hsLogFile); + } + } + + private static void verify(String hsLogFile) throws Exception { + System.out.println("Verifying " + hsLogFile); + + final Properties expectedProperties = new Properties(); + final FileReader reader = new FileReader(hsLogFile + ".verify.properties"); + expectedProperties.load(reader); + reader.close(); + + int fullMatchCnt = 0; + int suspectCnt = 0; + final String intrinsicId = expectedProperties.getProperty("intrinsic.name"); + final String prefix = ", Double> cache = new HashMap, Double>(); + public static double computeSum(int x, int y) { + List key = Arrays.asList(new Integer[] {x, y}); + + if (!cache.containsKey(key)) { + + // explicitly creating/updating a double[] array, instead of using the LogSumArray wrapper object, will prevent the error + LogSumArray toReturn = new LogSumArray(x); + + // changing loop indices will prevent the error + // in particular, for(z=0; z options = new LinkedList<>(); + Collections.addAll(options, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + CommandLineOptionTest.prepareBooleanFlag("UseRTMLocking", + useRTMLocking)); + + options.add(prepareOptionValue(value)); + + CommandLineOptionTest.verifySameJVMStartup( + (isWarningExpected ? warnings : null), + (isWarningExpected ? null : warnings), + ExitCode.OK, options.toArray(new String[options.size()])); + } + + private void verifyOptionValues(String value, boolean useRTMLocking, + String expectedValue) throws Throwable { + List options = new LinkedList<>(); + Collections.addAll(options, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + CommandLineOptionTest.prepareBooleanFlag("UseRTMLocking", + useRTMLocking)); + + options.add(prepareOptionValue(value)); + + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + expectedValue, options.toArray(new String[options.size()])); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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 + * 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. + * + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; + +import java.util.function.BooleanSupplier; + +public abstract class TestPrintPreciseRTMLockingStatisticsBase + extends RTMGenericCommandLineOptionTest { + protected static final String DEFAULT_VALUE = "false"; + + protected TestPrintPreciseRTMLockingStatisticsBase( + BooleanSupplier predicate) { + super(predicate, "PrintPreciseRTMLockingStatistics", true, false, + TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE); + } + + @Override + protected void runNonX86TestCases() throws Throwable { + verifyJVMStartup(); + verifyOptionValues(); + } + + @Override + protected void verifyJVMStartup() throws Throwable { + if (Platform.isServer()) { + if (!Platform.isDebugBuild()) { + String errorMessage = CommandLineOptionTest. + getDiagnosticOptionErrorMessage(optionName); + // verify that option is actually diagnostic + CommandLineOptionTest.verifySameJVMStartup( + new String[] { errorMessage }, null, ExitCode.FAIL, + prepareOptionValue("true")); + + CommandLineOptionTest.verifySameJVMStartup(null, + new String[] { errorMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + prepareOptionValue("true")); + } else { + CommandLineOptionTest.verifySameJVMStartup( + null, null, ExitCode.OK, prepareOptionValue("true")); + } + } else { + String errorMessage = CommandLineOptionTest. + getUnrecognizedOptionErrorMessage(optionName); + + CommandLineOptionTest.verifySameJVMStartup( + new String[]{errorMessage}, null, ExitCode.FAIL, + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + prepareOptionValue("true")); + } + } + + @Override + protected void verifyOptionValues() throws Throwable { + if (Platform.isServer()) { + // Verify default value + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify PrintPreciseRTMLockingStatistics on CPUs with + * rtm support and on VM with rtm locking support, + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig + */ + +import com.oracle.java.testlibrary.cli.*; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig + extends TestPrintPreciseRTMLockingStatisticsBase { + private TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + protected void verifyOptionValues() throws Throwable { + super.verifyOptionValues(); + // verify default value + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking", prepareOptionValue("true")); + + // verify that option could be turned on + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, "true", + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", prepareOptionValue("true")); + } + + public static void main(String args[]) throws Throwable { + new TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig() + .test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify PrintPreciseRTMLockingStatistics on CPUs without + * rtm support and/or unsupported VM. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig + */ + +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig + extends TestPrintPreciseRTMLockingStatisticsBase { + private TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig() { + super(new NotPredicate(new AndPredicate(new SupportedCPU(), + new SupportedVM()))); + } + + public static void main(String args[]) throws Throwable { + new TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig() + .test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMAbortRatioOptionOnSupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnSupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify RTMAbortRatio option processing on CPU with rtm + * support and on VM with rtm locking support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMAbortRatioOptionOnSupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMAbortRatioOptionOnSupportedConfig + */ + +public class TestRTMAbortRatioOptionOnSupportedConfig + extends RTMLockingAwareTest { + private static final String DEFAULT_VALUE = "50"; + + private TestRTMAbortRatioOptionOnSupportedConfig() { + super("RTMAbortRatio", false, true, + TestRTMAbortRatioOptionOnSupportedConfig.DEFAULT_VALUE, + /* correct values */ + new String[] { "0", "20", "100" }, + /* incorrect values */ + new String[] { "-1", "101" }, + RTMGenericCommandLineOptionTest.RTM_ABORT_RATIO_WARNING); + } + + public static void main(String args[]) throws Throwable { + new TestRTMAbortRatioOptionOnSupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMAbortRatioOptionOnUnsupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnUnsupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify RTMAbortRatio option processing on CPU without rtm + * support or on VM that does not support rtm locking. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMAbortRatioOptionOnUnsupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMAbortRatioOptionOnUnsupportedConfig + */ + +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestRTMAbortRatioOptionOnUnsupportedConfig + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "50"; + + private TestRTMAbortRatioOptionOnUnsupportedConfig() { + super(new NotPredicate(new AndPredicate(new SupportedVM(), + new SupportedCPU())), + "RTMAbortRatio", false, true, + TestRTMAbortRatioOptionOnUnsupportedConfig.DEFAULT_VALUE, + "0", "10", "100", "200"); + } + + public static void main(String args[]) throws Throwable { + new TestRTMAbortRatioOptionOnUnsupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMAbortThresholdOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMAbortThresholdOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of RTMAbortThreshold option. + * @library /testlibrary + * @build TestRTMAbortThresholdOption + * @run main/othervm TestRTMAbortThresholdOption + */ + +public class TestRTMAbortThresholdOption + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "1000"; + + private TestRTMAbortThresholdOption() { + super(Boolean.TRUE::booleanValue, "RTMAbortThreshold", false, true, + TestRTMAbortThresholdOption.DEFAULT_VALUE, + "0", "42", "100", "10000"); + } + + public static void main(String args[]) throws Throwable { + new TestRTMAbortThresholdOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMLockingCalculationDelayOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMLockingCalculationDelayOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of RTMLockingCalculationDelay option. + * @library /testlibrary + * @build TestRTMLockingCalculationDelayOption + * @run main/othervm TestRTMLockingCalculationDelayOption + */ + +public class TestRTMLockingCalculationDelayOption + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "0"; + + private TestRTMLockingCalculationDelayOption() { + super(Boolean.TRUE::booleanValue, "RTMLockingCalculationDelay", false, + true, TestRTMLockingCalculationDelayOption.DEFAULT_VALUE); + } + + public static void main(String agrs[]) throws Throwable { + new TestRTMLockingCalculationDelayOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMLockingThresholdOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMLockingThresholdOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of RTMLockingThreshold option. + * @library /testlibrary + * @build TestRTMLockingThresholdOption + * @run main/othervm TestRTMLockingThresholdOption + */ + +public class TestRTMLockingThresholdOption + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "10000"; + + private TestRTMLockingThresholdOption() { + super(Boolean.TRUE::booleanValue, "RTMLockingThreshold", false, true, + TestRTMLockingThresholdOption.DEFAULT_VALUE); + } + + public static void main(String args[]) throws Throwable { + new TestRTMLockingThresholdOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMRetryCountOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMRetryCountOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of RTMRetryCount option. + * @library /testlibrary + * @build TestRTMRetryCountOption + * @run main/othervm TestRTMRetryCountOption + */ + +public class TestRTMRetryCountOption extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "5"; + + private TestRTMRetryCountOption() { + super(Boolean.TRUE::booleanValue, "RTMRetryCount", false, true, + TestRTMRetryCountOption.DEFAULT_VALUE, + "0", "10", "100", "1000"); + } + + public static void main(String args[]) throws Throwable { + new TestRTMRetryCountOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMSpinLoopCountOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMSpinLoopCountOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of RTMSpinLoopCount option. + * @library /testlibrary + * @build TestRTMSpinLoopCountOption + * @run main/othervm TestRTMSpinLoopCountOption + */ + +public class TestRTMSpinLoopCountOption + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "100"; + + private TestRTMSpinLoopCountOption() { + super(Boolean.TRUE::booleanValue, "RTMSpinLoopCount", false, true, + TestRTMSpinLoopCountOption.DEFAULT_VALUE, + "0", "10", "42", "1000"); + } + + public static void main(String args[]) throws Throwable { + new TestRTMSpinLoopCountOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnSupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnSupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify RTMTotalCountIncrRate option processing on CPU with + * rtm support and on VM with rtm locking support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMTotalCountIncrRateOptionOnSupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestRTMTotalCountIncrRateOptionOnSupportedConfig + */ + +public class TestRTMTotalCountIncrRateOptionOnSupportedConfig + extends RTMLockingAwareTest { + private static final String DEFAULT_VALUE = "64"; + + private TestRTMTotalCountIncrRateOptionOnSupportedConfig() { + super("RTMTotalCountIncrRate", false, true, + TestRTMTotalCountIncrRateOptionOnSupportedConfig.DEFAULT_VALUE, + /* correct values */ + new String[] { "1", "2", "128", "1024" }, + /* incorrect values */ + new String[] { "-1", "0", "3", "42" }, + RTMGenericCommandLineOptionTest.RTM_COUNT_INCR_WARNING); + } + + public static void main(String args[]) throws Throwable { + new TestRTMTotalCountIncrRateOptionOnSupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnUnsupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnUnsupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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 + * 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. + * + */ + +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * @test + * @bug 8031320 + * @summary Verify RTMTotalCountIncrRate option processing on CPU without + * rtm support and/or on VM without rtm locking support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMTotalCountIncrRateOptionOnUnsupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestRTMTotalCountIncrRateOptionOnUnsupportedConfig + */ + +public class TestRTMTotalCountIncrRateOptionOnUnsupportedConfig + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "64"; + + private TestRTMTotalCountIncrRateOptionOnUnsupportedConfig() { + super(new NotPredicate(new AndPredicate(new SupportedCPU(), + new SupportedVM())), + "RTMTotalCountIncrRate", false, true, + TestRTMTotalCountIncrRateOptionOnUnsupportedConfig + .DEFAULT_VALUE, + "-1", "0", "42", "128"); + } + + public static void main(String args[]) throws Throwable { + new TestRTMTotalCountIncrRateOptionOnUnsupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMDeopt option processing on CPUs with rtm support + * when rtm locking is supported by VM. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMDeoptOptionOnSupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMDeoptOptionOnSupportedConfig + */ + +import com.oracle.java.testlibrary.ExitCode; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMDeoptOptionOnSupportedConfig + extends CommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMDeoptOptionOnSupportedConfig() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + public void runTestCases() throws Throwable { + String experimentalOptionError + = CommandLineOptionTest.getExperimentalOptionErrorMessage( + "UseRTMDeopt"); + // verify that option is experimental + CommandLineOptionTest.verifySameJVMStartup( + new String[] { experimentalOptionError }, null, ExitCode.FAIL, + "-XX:+UseRTMDeopt"); + // verify that option could be turned on + CommandLineOptionTest.verifySameJVMStartup(null, null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMDeopt"); + // verify that option could be turned off + CommandLineOptionTest.verifySameJVMStartup(null, null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMDeopt"); + // verify default value + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", + TestUseRTMDeoptOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + // verify default value + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", + TestUseRTMDeoptOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + // verify that option is off when UseRTMLocking is off + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", "false", + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking", "-XX:+UseRTMDeopt"); + // verify that option could be turned on + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", "true", + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseRTMDeopt"); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMDeoptOptionOnSupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMDeoptOptionOnUnsupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnUnsupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMDeopt option processing on CPUs without rtm support + * or on VMs without rtm locking support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMDeoptOptionOnUnsupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMDeoptOptionOnUnsupportedConfig + */ + +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; + +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMDeoptOptionOnUnsupportedConfig + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMDeoptOptionOnUnsupportedConfig() { + super(new NotPredicate(new AndPredicate(new SupportedCPU(), + new SupportedVM())), + "UseRTMDeopt", true, true, + TestUseRTMDeoptOptionOnUnsupportedConfig.DEFAULT_VALUE, "true"); + } + + @Override + protected void runX86SupportedVMTestCases() throws Throwable { + super.verifyJVMStartup(); + // verify default value + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + defaultValue, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + // verify that until RTMLocking is not used, value + // will be set to default false. + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + defaultValue, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMDeopt"); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMDeoptOptionOnUnsupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnSupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnSupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMForStackLocks option processing on CPU with + * rtm support when VM supports rtm locking. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMForStackLocksOptionOnSupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestUseRTMForStackLocksOptionOnSupportedConfig + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMForStackLocksOptionOnSupportedConfig + extends CommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMForStackLocksOptionOnSupportedConfig() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + public void runTestCases() throws Throwable { + String errorMessage + = CommandLineOptionTest.getExperimentalOptionErrorMessage( + "UseRTMForStackLocks"); + String warningMessage + = RTMGenericCommandLineOptionTest.RTM_FOR_STACK_LOCKS_WARNING; + + CommandLineOptionTest.verifySameJVMStartup( + new String[] { errorMessage }, null, ExitCode.FAIL, + "-XX:+UseRTMForStackLocks"); + // verify that we get a warning when trying to use rtm for stack + // lock, but not using rtm locking. + CommandLineOptionTest.verifySameJVMStartup( + new String[] { warningMessage }, null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMForStackLocks", + "-XX:-UseRTMLocking"); + // verify that we don't get a warning when no using rtm for stack + // lock and not using rtm locking. + CommandLineOptionTest.verifySameJVMStartup(null, + new String[] { warningMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMForStackLocks", + "-XX:-UseRTMLocking"); + // verify that we don't get a warning when using rtm for stack + // lock and using rtm locking. + CommandLineOptionTest.verifySameJVMStartup(null, + new String[] { warningMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMForStackLocks", + "-XX:+UseRTMLocking"); + // verify that default value if false + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMForStackLocks", + TestUseRTMForStackLocksOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + // verify that default value is false even with +UseRTMLocking + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMForStackLocks", + TestUseRTMForStackLocksOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + // verify that we can turn the option on + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMForStackLocks", + "true", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseRTMForStackLocks"); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMForStackLocksOptionOnSupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnUnsupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnUnsupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMForStackLocks option processing on CPUs without + * rtm support and/or on VMs without rtm locking support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMForStackLocksOptionOnUnsupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * TestUseRTMForStackLocksOptionOnUnsupportedConfig + */ + +import com.oracle.java.testlibrary.ExitCode; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMForStackLocksOptionOnUnsupportedConfig + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMForStackLocksOptionOnUnsupportedConfig() { + super(new NotPredicate(new AndPredicate(new SupportedCPU(), + new SupportedVM())), + "UseRTMForStackLocks", true, true, + TestUseRTMForStackLocksOptionOnUnsupportedConfig.DEFAULT_VALUE, + "true"); + } + + @Override + protected void runX86SupportedVMTestCases() throws Throwable { + // verify that option is experimental + CommandLineOptionTest.verifySameJVMStartup( + new String[]{ experimentalOptionError }, + null, ExitCode.FAIL, prepareOptionValue("true")); + + CommandLineOptionTest.verifySameJVMStartup( + new String[]{ experimentalOptionError }, + null, ExitCode.FAIL, prepareOptionValue("false")); + + // verify that if we turn it on, then VM output will contain + // warning saying that this option could be turned on only + // when we use rtm locking + CommandLineOptionTest.verifySameJVMStartup( + new String[]{ + RTMGenericCommandLineOptionTest.RTM_FOR_STACK_LOCKS_WARNING + }, + null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + prepareOptionValue("true") + ); + // verify that options is turned off by default + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + TestUseRTMForStackLocksOptionOnUnsupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + // verify that it could not be turned on without rtm locking + CommandLineOptionTest.verifyOptionValueForSameVM(optionName, + TestUseRTMForStackLocksOptionOnUnsupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + prepareOptionValue("true")); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMForStackLocksOptionOnUnsupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMLocking option processing on CPU with rtm support and + * on VM with rtm-locking support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMLockingOptionOnSupportedConfig + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMLockingOptionOnSupportedConfig + */ + +import com.oracle.java.testlibrary.ExitCode; +import com.oracle.java.testlibrary.cli.*; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMLockingOptionOnSupportedConfig + extends CommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMLockingOptionOnSupportedConfig() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + public void runTestCases() throws Throwable { + String unrecongnizedOption + = CommandLineOptionTest.getUnrecognizedOptionErrorMessage( + "UseRTMLocking"); + String experimentalOptionError + = CommandLineOptionTest.getExperimentalOptionErrorMessage( + "UseRTMLocking"); + // verify that options is experimental + CommandLineOptionTest.verifySameJVMStartup( + new String[] { experimentalOptionError }, null, ExitCode.FAIL, + "-XX:+UseRTMLocking"); + // verify that there are no warning or error in VM output + CommandLineOptionTest.verifySameJVMStartup(null, + new String[]{ + RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR, + unrecongnizedOption + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + + CommandLineOptionTest.verifySameJVMStartup(null, + new String[]{ + RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR, + unrecongnizedOption + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking"); + // verify that UseRTMLocking is of by default + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", + TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + // verify that we can change UseRTMLocking value + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", + TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking"); + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", + "true", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMLockingOptionOnSupportedConfig().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMLocking option processing on CPU without + * rtm support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMLockingOptionOnUnsupportedCPU + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMLockingOptionOnUnsupportedCPU + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMLockingOptionOnUnsupportedCPU + extends CommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMLockingOptionOnUnsupportedCPU() { + super(new AndPredicate(new NotPredicate(new SupportedCPU()), + new SupportedVM())); + } + + @Override + public void runTestCases() throws Throwable { + String unrecongnizedOption + = CommandLineOptionTest.getUnrecognizedOptionErrorMessage( + "UseRTMLocking"); + String errorMessage = RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR; + + if (Platform.isX86() || Platform.isX64()) { + // verify that we get an error when use +UseRTMLocking + // on unsupported CPU + CommandLineOptionTest.verifySameJVMStartup( + new String[] { errorMessage }, + new String[] { unrecongnizedOption }, + ExitCode.FAIL, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + // verify that we can pass -UseRTMLocking without + // getting any error messages + CommandLineOptionTest.verifySameJVMStartup( + null, + new String[]{ + errorMessage, + unrecongnizedOption + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking"); + + // verify that UseRTMLocking is false by default + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", + TestUseRTMLockingOptionOnUnsupportedCPU.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + } else { + // verify that on non-x86 CPUs RTMLocking could not be used + CommandLineOptionTest.verifySameJVMStartup( + new String[] { unrecongnizedOption }, + null, ExitCode.FAIL, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + + CommandLineOptionTest.verifySameJVMStartup( + new String[] { unrecongnizedOption }, + null, ExitCode.FAIL, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking"); + } + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMLockingOptionOnUnsupportedCPU().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedVM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedVM.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify UseRTMLocking option processing on CPU with rtm support + * in case when VM should not support this option. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMLockingOptionOnUnsupportedVM + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMLockingOptionOnUnsupportedVM + */ + +import com.oracle.java.testlibrary.ExitCode; +import com.oracle.java.testlibrary.cli.*; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import com.oracle.java.testlibrary.cli.predicate.NotPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMLockingOptionOnUnsupportedVM + extends CommandLineOptionTest { + private static final String DEFAULT_VALUE = "false"; + + private TestUseRTMLockingOptionOnUnsupportedVM() { + super(new AndPredicate(new SupportedCPU(), + new NotPredicate(new SupportedVM()))); + } + @Override + public void runTestCases() throws Throwable { + String errorMessage + = RTMGenericCommandLineOptionTest.RTM_UNSUPPORTED_VM_ERROR; + String experimentalOptionError + = CommandLineOptionTest.getExperimentalOptionErrorMessage( + "UseRTMLocking"); + // verify that options is experimental + CommandLineOptionTest.verifySameJVMStartup( + new String[] { experimentalOptionError }, null, ExitCode.FAIL, + "-XX:+UseRTMLocking"); + // verify that we can't use +UseRTMLocking + CommandLineOptionTest.verifySameJVMStartup( + new String[] { errorMessage }, null, ExitCode.FAIL, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + // verify that we can turn it off + CommandLineOptionTest.verifySameJVMStartup(null, + new String[] { errorMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking"); + // verify that it is off by default + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", + TestUseRTMLockingOptionOnUnsupportedVM.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMLockingOptionOnUnsupportedVM().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of UseRTMLocking and UseBiasedLocking + * options combination on CPU and VM with rtm support. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMLockingOptionWithBiasedLocking + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMLockingOptionWithBiasedLocking + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.*; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +public class TestUseRTMLockingOptionWithBiasedLocking + extends CommandLineOptionTest { + private TestUseRTMLockingOptionWithBiasedLocking() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + public void runTestCases() throws Throwable { + String warningMessage + = RTMGenericCommandLineOptionTest.RTM_BIASED_LOCKING_WARNING; + // verify that we will not get a warning + CommandLineOptionTest.verifySameJVMStartup(null, + new String[] { warningMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:-UseBiasedLocking"); + // verify that we will get a warning + CommandLineOptionTest.verifySameJVMStartup( + new String[] { warningMessage }, null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); + // verify that UseBiasedLocking is false when we use rtm locking + CommandLineOptionTest.verifyOptionValueForSameVM("UseBiasedLocking", + "false", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + // verify that we can't turn on biased locking when + // using rtm locking + CommandLineOptionTest.verifyOptionValueForSameVM("UseBiasedLocking", + "false", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMLockingOptionWithBiasedLocking().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/cli/TestUseRTMXendForLockBusyOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/cli/TestUseRTMXendForLockBusyOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify processing of UseRTMXendForLockBusy option. + * @library /testlibrary + * @build TestUseRTMXendForLockBusyOption + * @run main/othervm TestUseRTMXendForLockBusyOption + */ + +public class TestUseRTMXendForLockBusyOption + extends RTMGenericCommandLineOptionTest { + private static final String DEFAULT_VALUE = "true"; + + public TestUseRTMXendForLockBusyOption() { + super(Boolean.TRUE::booleanValue, "UseRTMXendForLockBusy", true, true, + TestUseRTMXendForLockBusyOption.DEFAULT_VALUE, "true"); + } + + public static void main(String agrs[]) throws Throwable { + new TestUseRTMXendForLockBusyOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMAbortRatio.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMAbortRatio.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMAbortRatio affects amount of aborts before + * deoptimization. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMAbortRatio + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMAbortRatio + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; +import sun.misc.Unsafe; + +/** + * Test verifies that method will be deoptimized on high abort ratio + * as soon as abort ratio reaches RTMAbortRatio's value. + */ +public class TestRTMAbortRatio extends CommandLineOptionTest { + private TestRTMAbortRatio() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyAbortRatio(0, false); + verifyAbortRatio(10, false); + verifyAbortRatio(50, false); + verifyAbortRatio(100, false); + + verifyAbortRatio(0, true); + verifyAbortRatio(10, true); + verifyAbortRatio(50, true); + verifyAbortRatio(100, true); + } + + private void verifyAbortRatio(int abortRatio, boolean useStackLock) + throws Throwable { + CompilableTest test = new Test(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + test, + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + "-XX:+UseRTMDeopt", + "-XX:RTMTotalCountIncrRate=1", + "-XX:RTMAbortThreshold=0", + CommandLineOptionTest.prepareNumericFlag("RTMLockingThreshold", + 10 * Test.TOTAL_ITERATIONS), + CommandLineOptionTest.prepareNumericFlag("RTMAbortRatio", + abortRatio), + "-XX:+PrintPreciseRTMLockingStatistics", + test.getClass().getName(), + Boolean.toString(!useStackLock)); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, "VM output should contain " + + "exactly one RTM locking statistics entry."); + + RTMLockingStatistics lock = statistics.get(0); + int actualRatio; + + if (lock.getTotalAborts() == 1L) { + actualRatio = 0; + } else { + actualRatio = (int) (lock.getTotalLocks() + / (lock.getTotalAborts() - 1L)); + } + + Asserts.assertLTE(actualRatio, abortRatio, String.format( + "Actual abort ratio (%d) should lower or equal to " + + "specified (%d).", actualRatio, abortRatio)); + } + + /** + * Force abort after {@code Test.WARMUP_ITERATIONS} is done. + */ + public static class Test implements CompilableTest { + private static final int TOTAL_ITERATIONS = 10000; + private static final int WARMUP_ITERATIONS = 1000; + private static final Unsafe UNSAFE = Utils.getUnsafe(); + private final Object monitor = new Object(); + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::lock"; + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { + getMethodWithLockName(), + Unsafe.class.getName() + "::addressSize" + }; + } + + public void lock(boolean abort) { + synchronized(monitor) { + if (abort) { + Test.UNSAFE.addressSize(); + } + } + } + + /** + * Usage: + * Test <inflate monitor> + */ + public static void main(String args[]) throws Throwable { + Asserts.assertGTE(args.length, 1, "One argument required."); + Test t = new Test(); + if (Boolean.valueOf(args[0])) { + AbortProvoker.inflateMonitor(t.monitor); + } + for (int i = 0; i < Test.TOTAL_ITERATIONS; i++) { + t.lock(i >= Test.WARMUP_ITERATIONS); + } + } + } + + public static void main(String args[]) throws Throwable { + new TestRTMAbortRatio().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMAbortThreshold.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMAbortThreshold.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMAbortThreshold option affects + * amount of aborts after which abort ratio is calculated. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMAbortThreshold + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMAbortThreshold + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that on RTMAbortThreshold option actually affects how soon + * method will be deoptimized on high abort ratio. + */ +public class TestRTMAbortThreshold extends CommandLineOptionTest { + private TestRTMAbortThreshold() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyAbortThreshold(false, 1); + verifyAbortThreshold(false, 10); + verifyAbortThreshold(false, 1000); + + verifyAbortThreshold(true, 1); + verifyAbortThreshold(true, 10); + verifyAbortThreshold(true, 1000); + } + + private void verifyAbortThreshold(boolean useStackLock, + long abortThreshold) throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + provoker, + "-XX:+UseRTMDeopt", + "-XX:RTMAbortRatio=0", + CommandLineOptionTest.prepareNumericFlag("RTMAbortThreshold", + abortThreshold), + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + AbortProvoker.class.getName(), + AbortType.XABORT.toString(), + Boolean.toString(!useStackLock)); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, "VM output should contain " + + "exactly one RTM locking statistics entry for method " + + provoker.getMethodWithLockName()); + + Asserts.assertEQ(statistics.get(0).getTotalLocks(), abortThreshold, + String.format("Expected that method with rtm lock elision was" + + " deoptimized after %d lock attempts", + abortThreshold)); + } + + public static void main(String args[]) throws Throwable { + new TestRTMAbortThreshold().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that if we use RTMDeopt, then deoptimization + * caused by reason other then rtm_state_change will reset + * method's RTM state. And if we don't use RTMDeopt, then + * RTM state remain the same after such deoptimization. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMAfterNonRTMDeopt + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMAfterNonRTMDeopt + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; +import sun.misc.Unsafe; + +/** + * To verify that with +UseRTMDeopt method's RTM state will be + * changed to ProfileRTM on deoptimization unrelated to + * rtm_state_change following sequence of events is used: + *
+ *
+ *     rtm state ^
+ *               |
+ *       UseRTM  |      ******|     ******
+ *               |            |
+ *   ProfileRTM  |******|     |*****|
+ *               |      |     |     |
+ *              0-------|-----|-----|---------------------> time
+ *                      |     |     \ force abort
+ *                      |     |
+ *                      |     \ force deoptimization
+ *                      |
+ *                      \ force xabort
+ * 
+ * When xabort is forced by native method call method should + * change it's state to UseRTM, because we use RTMAbortRatio=100 + * and low RTMLockingThreshold, so at this point actual abort + * ratio will be below 100% and there should be enough lock + * attempts to recompile method without RTM profiling. + */ +public class TestRTMAfterNonRTMDeopt extends CommandLineOptionTest { + private static final int ABORT_THRESHOLD = 1000; + private static final String RANGE_CHECK = "range_check"; + + private TestRTMAfterNonRTMDeopt() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyRTMAfterDeopt(false, false); + verifyRTMAfterDeopt(true, false); + + verifyRTMAfterDeopt(false, true); + verifyRTMAfterDeopt(true, true); + } + + private void verifyRTMAfterDeopt(boolean useStackLock, + boolean useRTMDeopt) throws Throwable { + CompilableTest test = new Test(); + String logFile = String.format("rtm_%s_stack_lock_%s_deopt.xml", + (useStackLock ? "use" : "no"), (useRTMDeopt ? "use" : "no")); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFile, + test, + "-XX:CompileThreshold=1", + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + CommandLineOptionTest.prepareBooleanFlag("UseRTMDeopt", + useRTMDeopt), + "-XX:RTMAbortRatio=100", + CommandLineOptionTest.prepareNumericFlag("RTMAbortThreshold", + TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD), + CommandLineOptionTest.prepareNumericFlag("RTMLockingThreshold", + TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD / 2L), + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + Test.class.getName(), + Boolean.toString(!useStackLock) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int traps = RTMTestBase.firedRTMStateChangeTraps(logFile); + + if (useRTMDeopt) { + Asserts.assertEQ(traps, 2, "Two uncommon traps with " + + "reason rtm_state_change should be fired."); + } else { + Asserts.assertEQ(traps, 0, "No uncommon traps with " + + "reason rtm_state_change should be fired."); + } + + int rangeCheckTraps = RTMTestBase.firedUncommonTraps(logFile, + TestRTMAfterNonRTMDeopt.RANGE_CHECK); + + Asserts.assertEQ(rangeCheckTraps, 1, + "One range_check uncommon trap should be fired."); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + int expectedStatEntries = (useRTMDeopt ? 4 : 2); + + Asserts.assertEQ(statistics.size(), expectedStatEntries, + String.format("VM output should contain %d RTM locking " + + "statistics entries.", expectedStatEntries)); + } + + public static class Test implements CompilableTest { + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + private static final int ITERATIONS = 10000; + private static final int RANGE_CHECK_AT = ITERATIONS / 2; + private static final Unsafe UNSAFE = Utils.getUnsafe(); + private final Object monitor = new Object(); + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::forceAbort"; + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { + getMethodWithLockName(), + sun.misc.Unsafe.class.getName() + "::forceAbort" + }; + } + + public void forceAbort(int a[], boolean abort) { + try { + synchronized(monitor) { + a[0]++; + if (abort) { + Test.field = Test.UNSAFE.addressSize(); + } + } + } catch (Throwable t) { + // suppress any throwables + } + } + + /** + * Usage: + * Test <inflate monitor> + */ + public static void main(String args[]) throws Throwable { + Test t = new Test(); + + if (Boolean.valueOf(args[0])) { + AbortProvoker.inflateMonitor(t.monitor); + } + + int tmp[] = new int[1]; + + for (int i = 0; i < Test.ITERATIONS; i++ ) { + if (i == Test.RANGE_CHECK_AT) { + t.forceAbort(new int[0], false); + } else { + boolean isThreshold + = (i == TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD); + boolean isThresholdPlusRange + = (i == TestRTMAfterNonRTMDeopt.ABORT_THRESHOLD + + Test.RANGE_CHECK_AT); + t.forceAbort(tmp, isThreshold || isThresholdPlusRange); + } + } + } + } + + public static void main(String args[]) throws Throwable { + new TestRTMAfterNonRTMDeopt().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that on high abort ratio method will be recompiled + * without rtm locking. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMDeoptOnHighAbortRatio + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMDeoptOnHighAbortRatio + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that on high abort ratio method wil be deoptimized with + * rtm_state_change reason and after that RTM-based lock elision will not + * be used for that method. + * This test make asserts on total locks count done by compiled method, + * so in order to avoid issue with retriable locks -XX:RTMRetryCount=0 is used. + * For more details on that issue see {@link TestUseRTMAfterLockInflation}. + */ +public class TestRTMDeoptOnHighAbortRatio extends CommandLineOptionTest { + private static final long ABORT_THRESHOLD + = AbortProvoker.DEFAULT_ITERATIONS / 2L; + + private TestRTMDeoptOnHighAbortRatio() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyDeopt(false); + verifyDeopt(true); + } + + private void verifyDeopt(boolean useStackLock) throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + String logFileName = String.format("rtm_deopt_%s_stack_lock.xml", + (useStackLock ? "use" : "no")); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFileName, + provoker, + "-XX:+UseRTMDeopt", + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + "-XX:RTMRetryCount=0", + CommandLineOptionTest.prepareNumericFlag("RTMAbortThreshold", + TestRTMDeoptOnHighAbortRatio.ABORT_THRESHOLD), + "-XX:RTMAbortRatio=100", + "-XX:CompileThreshold=1", + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + AbortProvoker.class.getName(), + AbortType.XABORT.toString(), + Boolean.toString(!useStackLock) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int firedTraps = RTMTestBase.firedRTMStateChangeTraps(logFileName); + + Asserts.assertEQ(firedTraps, 1, "Expected to get only one " + + "deoptimization due to rtm state change"); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, "VM output should contain " + + "exactly one RTM locking statistics entry for method " + + provoker.getMethodWithLockName()); + + Asserts.assertEQ(statistics.get(0).getTotalLocks(), + TestRTMDeoptOnHighAbortRatio.ABORT_THRESHOLD, + "After AbortThreshold was reached, method should be" + + " recompiled without rtm lock eliding."); + } + + public static void main(String args[]) throws Throwable { + new TestRTMDeoptOnHighAbortRatio().test(); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that on low abort ratio method will be recompiled. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMDeoptOnLowAbortRatio + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMDeoptOnLowAbortRatio + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; +import sun.misc.Unsafe; + +/** + * Test verifies that low abort ratio method will be deoptimized with + * rtm_state_change reason and will continue to use RTM-based lock + * elision after that. + * This test make asserts on total locks count done by compiled method, + * so in order to avoid issue with retriable locks -XX:RTMRetryCount=0 is used. + * For more details on that issue see {@link TestUseRTMAfterLockInflation}. + */ +public class TestRTMDeoptOnLowAbortRatio extends CommandLineOptionTest { + private static final long LOCKING_THRESHOLD = 100L; + + private TestRTMDeoptOnLowAbortRatio() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyRTMDeopt(false); + verifyRTMDeopt(true); + } + + private void verifyRTMDeopt(boolean useStackLock) throws Throwable { + CompilableTest test = new Test(); + String logFileName = String.format("rtm_deopt_%s_stack_lock.xml", + useStackLock ? "use" : "no"); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFileName, + test, + "-XX:+UseRTMDeopt", + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + CommandLineOptionTest.prepareNumericFlag("RTMLockingThreshold", + TestRTMDeoptOnLowAbortRatio.LOCKING_THRESHOLD), + "-XX:RTMAbortThreshold=1", + "-XX:RTMAbortRatio=100", + "-XX:CompileThreshold=1", + "-XX:RTMRetryCount=0", + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + Test.class.getName(), + Boolean.toString(!useStackLock) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int firedTraps = RTMTestBase.firedRTMStateChangeTraps(logFileName); + + Asserts.assertEQ(firedTraps, 1, + "Expected to get only one deoptimization due to rtm" + + " state change"); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 2, + "VM output should contain two RTM locking " + + "statistics entries for method " + + test.getMethodWithLockName()); + + RTMLockingStatistics statisticsBeforeDeopt = null; + + for (RTMLockingStatistics s : statistics) { + if (s.getTotalLocks() + == TestRTMDeoptOnLowAbortRatio.LOCKING_THRESHOLD + 1L) { + Asserts.assertNull(statisticsBeforeDeopt, + "Only one abort was expected during test run"); + statisticsBeforeDeopt = s; + } + } + + Asserts.assertNotNull(statisticsBeforeDeopt, + "After LockThreshold was reached, method should be recompiled " + + "with rtm lock eliding."); + } + + public static class Test implements CompilableTest { + private static final Unsafe UNSAFE = Utils.getUnsafe(); + private final Object monitor = new Object(); + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::forceAbort"; + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { + getMethodWithLockName(), + sun.misc.Unsafe.class.getName() + "::addressSize" + }; + } + + public void forceAbort(boolean abort) { + synchronized(monitor) { + if (abort) { + Test.UNSAFE.addressSize(); + } + } + } + + /** + * Usage: + * Test <inflate monitor> + */ + public static void main(String args[]) throws Throwable { + Asserts.assertGTE(args.length, 1, "One argument required."); + Test t = new Test(); + + if (Boolean.valueOf(args[0])) { + AbortProvoker.inflateMonitor(t.monitor); + } + for (int i = 0; i < AbortProvoker.DEFAULT_ITERATIONS; i++) { + t.forceAbort( + i == TestRTMDeoptOnLowAbortRatio.LOCKING_THRESHOLD); + } + } + } + + public static void main(String args[]) throws Throwable { + new TestRTMDeoptOnLowAbortRatio().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMLockingCalculationDelay.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMLockingCalculationDelay.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMLockingCalculationDelay affect when + * abort ratio calculation is started. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMLockingCalculationDelay + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMLockingCalculationDelay + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that abort ratio calculation could be delayed using + * RTMLockingCalculationDelay option. + */ +public class TestRTMLockingCalculationDelay extends CommandLineOptionTest { + private static final boolean INFLATE_MONITOR = true; + + private TestRTMLockingCalculationDelay() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + // verify that calculation will be started immediately + verifyLockingCalculationDelay(0, 0, true); + + // verify that calculation will not be started during + // first 10 minutes, while test will be started immediately + verifyLockingCalculationDelay(600000, 0, false); + + // verify that calculation will be started after a second + verifyLockingCalculationDelay(1000, 1000, true); + } + + private void verifyLockingCalculationDelay(long delay, long testDelay, + boolean deoptExpected) throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + String logFileName = String.format("rtm_delay_%d_%d.xml", delay, + testDelay); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFileName, + provoker, + "-XX:+UseRTMDeopt", + CommandLineOptionTest.prepareNumericFlag( + "RTMLockingCalculationDelay", delay), + "-XX:RTMAbortRatio=0", + "-XX:RTMAbortThreshold=0", + AbortProvoker.class.getName(), + AbortType.XABORT.toString(), + Boolean.toString( + TestRTMLockingCalculationDelay.INFLATE_MONITOR), + Long.toString(AbortProvoker.DEFAULT_ITERATIONS), + Long.toString(testDelay) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int deopts = RTMTestBase.firedRTMStateChangeTraps(logFileName); + + if (deoptExpected) { + Asserts.assertGT(deopts, 0, "At least one deoptimization due to " + + "rtm_state_chage is expected"); + } else { + Asserts.assertEQ(deopts, 0, "No deoptimizations due to " + + "rtm_state_chage are expected"); + } + } + + public static void main(String args[]) throws Throwable { + new TestRTMLockingCalculationDelay().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMLockingThreshold.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMLockingThreshold.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,179 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMLockingThreshold affects rtm state transition + * ProfileRTM => UseRTM. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMLockingThreshold + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMLockingThreshold + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; +import sun.misc.Unsafe; + +/** + * Test verifies that RTMLockingThreshold option actually affects how soon + * method will be deoptimized on low abort ratio. + */ +public class TestRTMLockingThreshold extends CommandLineOptionTest { + private TestRTMLockingThreshold() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + /** + * We use non-zero abort threshold to avoid abort related to + * interrupts, VMM calls, etc. during first lock attempt. + * + */ + private static final int ABORT_THRESHOLD = 10; + + @Override + protected void runTestCases() throws Throwable { + verifyLockingThreshold(0, false); + verifyLockingThreshold(100, false); + verifyLockingThreshold(1000, false); + + verifyLockingThreshold(0, true); + verifyLockingThreshold(100, true); + verifyLockingThreshold(1000, true); + } + + private void verifyLockingThreshold(int lockingThreshold, + boolean useStackLock) throws Throwable { + CompilableTest test = new Test(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + test, + "-XX:CompileThreshold=1", + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + "-XX:+UseRTMDeopt", + "-XX:RTMTotalCountIncrRate=1", + "-XX:RTMRetryCount=0", + CommandLineOptionTest.prepareNumericFlag("RTMAbortThreshold", + TestRTMLockingThreshold.ABORT_THRESHOLD), + CommandLineOptionTest.prepareNumericFlag("RTMLockingThreshold", + lockingThreshold), + "-XX:RTMAbortRatio=100", + "-XX:+PrintPreciseRTMLockingStatistics", + Test.class.getName(), + Boolean.toString(!useStackLock), + Integer.toString(lockingThreshold) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 2, "VM output should contain two " + + "RTM locking statistics entries."); + + /** + * We force abort on each odd iteration, so if RTMLockingThreshold==0, + * then we have to make 1 call without abort to avoid rtm state + * transition to NoRTM (otherwise actual abort ratio will be 100%), + * and after that make 1 call with abort to force deoptimization. + * This leads us to two locks for threshold 0. + * For other threshold values we have to make RTMLockingThreshold + 1 + * locks if locking threshold is even, or + 0 if odd. + */ + long expectedValue = lockingThreshold + + (lockingThreshold == 0L ? 2L : lockingThreshold % 2L); + + RTMLockingStatistics statBeforeDeopt = null; + for (RTMLockingStatistics s : statistics) { + if (s.getTotalLocks() == expectedValue) { + Asserts.assertNull(statBeforeDeopt, + "Only one statistics entry should contain aborts"); + statBeforeDeopt = s; + } + } + + Asserts.assertNotNull(statBeforeDeopt, "There should be exactly one " + + "statistics entry corresponding to ProfileRTM state."); + } + + public static class Test implements CompilableTest { + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + private static final int TOTAL_ITERATIONS = 10000; + private static final Unsafe UNSAFE = Utils.getUnsafe(); + private final Object monitor = new Object(); + + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::lock"; + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { + getMethodWithLockName(), + sun.misc.Unsafe.class.getName() + "::addressSize" + }; + } + + public void lock(boolean abort) { + synchronized(monitor) { + if (abort) { + Test.field += Test.UNSAFE.addressSize(); + } + } + } + + /** + * Usage: + * Test <inflate monitor> + */ + public static void main(String args[]) throws Throwable { + Asserts.assertGTE(args.length, 1, "One argument required."); + Test t = new Test(); + + if (Boolean.valueOf(args[0])) { + AbortProvoker.inflateMonitor(t.monitor); + } + for (int i = 0; i < Test.TOTAL_ITERATIONS; i++) { + t.lock(i % 2 == 1); + } + } + } + + public static void main(String args[]) throws Throwable { + new TestRTMLockingThreshold().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMRetryCount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMRetryCount.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMRetryCount affects actual amount of retries. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMRetryCount + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMRetryCount + */ + +import java.util.List; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that RTMRetryCount option actually affects amount of + * retries on lock busy. + */ +public class TestRTMRetryCount extends CommandLineOptionTest { + /** + * Time in ms, during which busy lock will be locked. + */ + private static final int LOCKING_TIME = 5000; + private static final boolean INFLATE_MONITOR = true; + + private TestRTMRetryCount() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyRTMRetryCount(0); + verifyRTMRetryCount(1); + verifyRTMRetryCount(5); + verifyRTMRetryCount(10); + } + + private void verifyRTMRetryCount(int retryCount) throws Throwable { + CompilableTest busyLock = new BusyLock(); + long expectedAborts = retryCount + 1L; + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + busyLock, + "-XX:-UseRTMXendForLockBusy", + "-XX:RTMTotalCountIncrRate=1", + CommandLineOptionTest.prepareNumericFlag("RTMRetryCount", + retryCount), + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + BusyLock.class.getName(), + Boolean.toString(TestRTMRetryCount.INFLATE_MONITOR), + Integer.toString(TestRTMRetryCount.LOCKING_TIME) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + busyLock.getMethodWithLockName(), outputAnalyzer.getStdout()); + + Asserts.assertEQ(statistics.size(), 1, "VM output should contain " + + "exactly one rtm locking statistics entry for method " + + busyLock.getMethodWithLockName()); + + Asserts.assertEQ(statistics.get(0).getTotalAborts(), expectedAborts, + String.format("It is expected to get %d aborts", + expectedAborts)); + } + + public static void main(String args[]) throws Throwable { + new TestRTMRetryCount().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMSpinLoopCount.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMSpinLoopCount.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMSpinLoopCount affects time spent + * between locking attempts. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMSpinLoopCount + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMSpinLoopCount + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that RTMSpinLoopCount increase time spent between retries + * by comparing amount of retries done with different RTMSpinLoopCount's values. + */ +public class TestRTMSpinLoopCount extends CommandLineOptionTest { + private static final int LOCKING_TIME = 1000; + private static final int RTM_RETRY_COUNT = 1000; + private static final boolean INFLATE_MONITOR = true; + private static final long MAX_ABORTS = RTM_RETRY_COUNT + 1L; + private static final int[] SPIN_LOOP_COUNTS + = new int[] { 0, 100, 1_000, 1_000_000, 10_000_000 }; + + private TestRTMSpinLoopCount() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + protected void runTestCases() throws Throwable { + long[] aborts = new long[TestRTMSpinLoopCount.SPIN_LOOP_COUNTS.length]; + for (int i = 0; i < TestRTMSpinLoopCount.SPIN_LOOP_COUNTS.length; i++) { + aborts[i] = getAbortsCountOnLockBusy( + TestRTMSpinLoopCount.SPIN_LOOP_COUNTS[i]); + } + + for (int i = 1; i < aborts.length; i++) { + Asserts.assertLTE(aborts[i], aborts[i - 1], "Increased spin loop " + + "count should not increase retries count."); + } + } + + private long getAbortsCountOnLockBusy(int spinLoopCount) throws Throwable { + CompilableTest test = new BusyLock(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + test, + CommandLineOptionTest.prepareNumericFlag("RTMRetryCount", + TestRTMSpinLoopCount.RTM_RETRY_COUNT), + CommandLineOptionTest.prepareNumericFlag("RTMSpinLoopCount", + spinLoopCount), + "-XX:-UseRTMXendForLockBusy", + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + BusyLock.class.getName(), + Boolean.toString(TestRTMSpinLoopCount.INFLATE_MONITOR), + Integer.toString(TestRTMSpinLoopCount.LOCKING_TIME) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, + "VM output should contain exactly one entry for method " + + test.getMethodWithLockName()); + + RTMLockingStatistics lock = statistics.get(0); + + Asserts.assertLTE(lock.getTotalAborts(), + TestRTMSpinLoopCount.MAX_ABORTS, String.format("Total aborts " + + "count (%d) should be less or equal to %d", + lock.getTotalAborts(), + TestRTMSpinLoopCount.MAX_ABORTS)); + + return lock.getTotalAborts(); + } + + public static void main(String args[]) throws Throwable { + new TestRTMSpinLoopCount().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestRTMTotalCountIncrRate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestRTMTotalCountIncrRate.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that RTMTotalCountIncrRate option affects + * RTM locking statistics. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestRTMTotalCountIncrRate + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestRTMTotalCountIncrRate + */ + +import java.util.List; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that with RTMTotalCountIncrRate=1 RTM locking statistics + * contains precise information abort attempted locks and that with other values + * statistics contains information abort non-zero locking attempts. + * Since assert done for RTMTotalCountIncrRate=1 is pretty strict, test uses + * -XX:RTMRetryCount=0 to avoid issue with retriable aborts. For more details on + * that issue see {@link TestUseRTMAfterLockInflation}. + */ +public class TestRTMTotalCountIncrRate extends CommandLineOptionTest { + private TestRTMTotalCountIncrRate() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyLocksCount(1, false); + verifyLocksCount(64, false); + verifyLocksCount(128, false); + verifyLocksCount(1, true); + verifyLocksCount(64, true); + verifyLocksCount(128, true); + } + + private void verifyLocksCount(int incrRate, boolean useStackLock) + throws Throwable{ + CompilableTest test = new Test(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + test, + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + CommandLineOptionTest.prepareNumericFlag( + "RTMTotalCountIncrRate", incrRate), + "-XX:RTMRetryCount=0", + "-XX:+PrintPreciseRTMLockingStatistics", + Test.class.getName(), + Boolean.toString(!useStackLock) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, "VM output should contain " + + "exactly one RTM locking statistics entry for method " + + test.getMethodWithLockName()); + + RTMLockingStatistics lock = statistics.get(0); + if (incrRate == 1) { + Asserts.assertEQ(lock.getTotalLocks(), Test.TOTAL_ITERATIONS, + "Total locks should be exactly the same as amount of " + + "iterations."); + } else { + Asserts.assertGT(lock.getTotalLocks(), 0L, "RTM statistics " + + "should contain information for at least on lock."); + } + } + + public static class Test implements CompilableTest { + private static final long TOTAL_ITERATIONS = 10000L; + private final Object monitor = new Object(); + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::lock"; + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { + getMethodWithLockName() + }; + } + + public void lock() { + synchronized(monitor) { + Test.field++; + } + } + + /** + * Usage: + * Test <inflate monitor> + */ + public static void main(String args[]) throws Throwable { + Asserts.assertGTE(args.length, 1, "One argument required."); + Test test = new Test(); + + if (Boolean.valueOf(args[0])) { + AbortProvoker.inflateMonitor(test.monitor); + } + for (long i = 0L; i < Test.TOTAL_ITERATIONS; i++) { + test.lock(); + } + } + } + + public static void main(String args[]) throws Throwable { + new TestRTMTotalCountIncrRate().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestUseRTMAfterLockInflation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestUseRTMAfterLockInflation.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that rtm locking is used for stack locks before + * inflation and after it used for inflated locks. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMAfterLockInflation + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMAfterLockInflation + */ + +import java.util.List; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that RTM is used after lock inflation by executing compiled + * method with RTM-based lock elision using stack lock first, then that lock + * is inflated and the same compiled method invoked again. + * + * Compiled method invoked {@code AbortProvoker.DEFAULT_ITERATIONS} times before + * lock inflation and the same amount of times after inflation. + * As a result total locks count should be equal to + * {@code 2*AbortProvoker.DEFAULT_ITERATIONS}. + * It is a pretty strict assertion which could fail if some retriable abort + * happened: it could be {@code AbortType.RETRIABLE} or + * {@code AbortType.MEM_CONFLICT}, but unfortunately abort can has both these + * reasons simultaneously. In order to avoid false negative failures related + * to incorrect aborts counting, -XX:RTMRetryCount=0 is used. + */ +public class TestUseRTMAfterLockInflation extends CommandLineOptionTest { + private static final long EXPECTED_LOCKS + = 2L * AbortProvoker.DEFAULT_ITERATIONS; + + private TestUseRTMAfterLockInflation() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + protected void runTestCases() throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + long totalLocksCount = 0; + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + provoker, + "-XX:+UseRTMForStackLocks", + "-XX:RTMTotalCountIncrRate=1", + "-XX:RTMRetryCount=0", + "-XX:+PrintPreciseRTMLockingStatistics", + Test.class.getName(), + AbortType.XABORT.toString()); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 2, + "VM output should contain two rtm locking statistics entries " + + "for method " + provoker.getMethodWithLockName()); + + for (RTMLockingStatistics s : statistics) { + totalLocksCount += s.getTotalLocks(); + } + + Asserts.assertEQ(totalLocksCount, + TestUseRTMAfterLockInflation.EXPECTED_LOCKS, + "Total lock count should be greater or equal to " + + TestUseRTMAfterLockInflation.EXPECTED_LOCKS); + } + + public static class Test { + + /** + * Usage: + * Test <provoker type> + */ + public static void main(String args[]) throws Throwable { + Asserts.assertGT(args.length, 0, + "AbortType name is expected as first argument."); + + AbortProvoker provoker + = AbortType.lookup(Integer.valueOf(args[0])).provoker(); + for (int i = 0; i < AbortProvoker.DEFAULT_ITERATIONS; i++) { + provoker.forceAbort(); + } + provoker.inflateMonitor(); + for (int i = 0; i < AbortProvoker.DEFAULT_ITERATIONS; i++) { + provoker.forceAbort(); + } + } + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMAfterLockInflation().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestUseRTMDeopt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestUseRTMDeopt.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that UseRTMDeopt affects uncommon trap installation in + * copmpiled methods with synchronized block. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMDeopt + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMDeopt + */ + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that usage of UseRTMDeopt option affects uncommon traps usage + * for methods that use locking. + */ +public class TestUseRTMDeopt extends CommandLineOptionTest { + private TestUseRTMDeopt() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + protected void runTestCases() throws Throwable { + verifyUseRTMDeopt(false); + verifyUseRTMDeopt(true); + } + + private void verifyUseRTMDeopt(boolean useRTMDeopt) throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + String logFileName = String.format("rtm_%s_deopt.xml", + useRTMDeopt ? "use" : "no"); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFileName, + provoker, + CommandLineOptionTest.prepareBooleanFlag("UseRTMDeopt", + useRTMDeopt), + AbortProvoker.class.getName(), + AbortType.XABORT.toString() + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int expectedUncommonTraps = useRTMDeopt ? 1 : 0; + int installedUncommonTraps + = RTMTestBase.installedRTMStateChangeTraps(logFileName); + + Asserts.assertEQ(expectedUncommonTraps, installedUncommonTraps, + String.format("Expected to find %d uncommon traps " + + "installed with reason rtm_state_change.", + expectedUncommonTraps)); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMDeopt().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestUseRTMForInflatedLocks.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestUseRTMForInflatedLocks.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that rtm locking is used for inflated locks. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMForInflatedLocks + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMForInflatedLocks + */ + +import java.util.List; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that RTM-based lock elision could be used for inflated locks + * by calling compiled method that use RTM-based lock elision and using + * manually inflated lock. + * Compiled method invoked {@code AbortProvoker.DEFAULT_ITERATIONS} times, + * so total locks count should be the same. + * This test could also be affected by retriable aborts, so -XX:RTMRetryCount=0 + * is used. For more information abort that issue see + * {@link TestUseRTMAfterLockInflation}. + */ +public class TestUseRTMForInflatedLocks extends CommandLineOptionTest { + private TestUseRTMForInflatedLocks() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + RTMLockingStatistics lock; + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + provoker, + "-XX:-UseRTMForStackLocks", + "-XX:RTMTotalCountIncrRate=1", + "-XX:RTMRetryCount=0", + "-XX:+PrintPreciseRTMLockingStatistics", + AbortProvoker.class.getName(), + AbortType.XABORT.toString()); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, + "VM output should contain exactly one rtm locking statistics " + + "entry for method " + provoker.getMethodWithLockName()); + + lock = statistics.get(0); + Asserts.assertEQ(lock.getTotalLocks(), AbortProvoker.DEFAULT_ITERATIONS, + "Total lock count should be greater or equal to " + + AbortProvoker.DEFAULT_ITERATIONS); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMForInflatedLocks().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestUseRTMForStackLocks.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestUseRTMForStackLocks.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that rtm locking is used for stack locks. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMForStackLocks + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMForStackLocks + */ + +import java.util.List; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that RTM-based lock elision could be used for stack locks + * by calling compiled method that use RTM-based lock elision and using + * stack lock. + * Compiled method invoked {@code AbortProvoker.DEFAULT_ITERATIONS} times, + * so total locks count should be the same. + * This test could also be affected by retriable aborts, so -XX:RTMRetryCount=0 + * is used. For more information abort that issue see + * {@link TestUseRTMAfterLockInflation}. + */ +public class TestUseRTMForStackLocks extends CommandLineOptionTest { + private static final boolean INFLATE_MONITOR = false; + + private TestUseRTMForStackLocks() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + protected void runTestCases() throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + RTMLockingStatistics lock; + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + provoker, + "-XX:+UseRTMForStackLocks", + "-XX:RTMTotalCountIncrRate=1", + "-XX:RTMRetryCount=0", + "-XX:+PrintPreciseRTMLockingStatistics", + AbortProvoker.class.getName(), + AbortType.XABORT.toString(), + Boolean.toString(TestUseRTMForStackLocks.INFLATE_MONITOR)); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, + "VM output should contain exactly one rtm locking statistics " + + "entry for method " + provoker.getMethodWithLockName()); + + lock = statistics.get(0); + Asserts.assertEQ(lock.getTotalLocks(), AbortProvoker.DEFAULT_ITERATIONS, + "Total locks count should be greater or equal to " + + AbortProvoker.DEFAULT_ITERATIONS); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMForStackLocks().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/locking/TestUseRTMXendForLockBusy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/locking/TestUseRTMXendForLockBusy.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that UseRTMXendForLockBusy option affects + * method behaviour if lock is busy. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMXendForLockBusy + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMXendForLockBusy + */ + +import java.util.List; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that with +UseRTMXendForLockBusy there will be no aborts + * forced by the test. + */ +public class TestUseRTMXendForLockBusy extends CommandLineOptionTest { + private final static int LOCKING_TIME = 5000; + + private TestUseRTMXendForLockBusy() { + super(new AndPredicate(new SupportedVM(), new SupportedCPU())); + } + + @Override + protected void runTestCases() throws Throwable { + // inflated lock, xabort on lock busy + verifyXendForLockBusy(true, false); + // inflated lock, xend on lock busy + verifyXendForLockBusy(true, true); + // stack lock, xabort on lock busy + verifyXendForLockBusy(false, false); + // stack lock, xend on lock busy + verifyXendForLockBusy(false, true); + } + + private void verifyXendForLockBusy(boolean inflateMonitor, + boolean useXend) throws Throwable { + CompilableTest test = new BusyLock(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + test, + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + inflateMonitor), + CommandLineOptionTest.prepareBooleanFlag( + "UseRTMXendForLockBusy", + useXend), + "-XX:RTMRetryCount=0", + "-XX:RTMTotalCountIncrRate=1", + "-XX:+PrintPreciseRTMLockingStatistics", + BusyLock.class.getName(), + Boolean.toString(inflateMonitor), + Integer.toString(TestUseRTMXendForLockBusy.LOCKING_TIME) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + test.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, "VM output should contain " + + "exactly one rtm locking statistics entry for method " + + test.getMethodWithLockName()); + + long aborts = statistics.get(0).getAborts(AbortType.XABORT); + + if (useXend) { + Asserts.assertEQ(aborts, 0L, + "Expected to get no aborts on busy lock"); + } else { + Asserts.assertGT(aborts, 0L, + "Expected to get at least one abort on busy lock"); + } + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMXendForLockBusy().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/method_options/TestNoRTMLockElidingOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/method_options/TestNoRTMLockElidingOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that NoRTMLockEliding option could be applied to + * specified method and that such method will not use rtm. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestNoRTMLockElidingOption + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestNoRTMLockElidingOption + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that method tagged with option NoRTMLockElidingOption + * will not use RTM-based lock elision. + * Test invokes compiled method and checks that no deoptimization with + * rtm_state_change reason had happened and that that VM output + * does not contain RTM locking statistics for compiled method. + */ +public class TestNoRTMLockElidingOption extends CommandLineOptionTest { + private TestNoRTMLockElidingOption() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + public void runTestCases() throws Throwable { + verifyOption(false); + verifyOption(true); + } + + public void verifyOption(boolean useStackLock) throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + String logFileName = String.format("rtm_deopt_%s_stack_lock.xml", + (useStackLock ? "use" : "no")); + String methodOption = String.format("-XX:CompileCommand=option," + + "%s,NoRTMLockEliding", provoker.getMethodWithLockName()); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFileName, + provoker, + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + methodOption, + "-XX:RTMTotalCountIncrRate=1", + "-XX:+UseRTMDeopt", + "-XX:+PrintPreciseRTMLockingStatistics", + AbortProvoker.class.getName(), + AbortType.XABORT.toString(), + Boolean.toString(!useStackLock) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int firedTraps = RTMTestBase.firedRTMStateChangeTraps(logFileName); + + Asserts.assertEQ(firedTraps, 0, + "No deoptimizations with rtm_state_change reason are expected"); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 0, + "VM output should not contain RTM locking statistics entries " + + "for method " + provoker.getMethodWithLockName()); + } + + public static void main(String args[]) throws Throwable { + new TestNoRTMLockElidingOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/method_options/TestUseRTMLockElidingOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/method_options/TestUseRTMLockElidingOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that UseRTMLockEliding option could be applied to + * specified method and that such method will not be deoptimized + * on high abort ratio. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestUseRTMLockElidingOption + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestUseRTMLockElidingOption + */ + +import java.util.List; +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that method tagged with option UseRTMLockElidingOption + * will use RTM-based lock elision, but will be never deoptimized with + * rtm_state_change reason. + * Test invokes compiled method and checks that no deoptimization with + * rtm_state_change reason had happened and that that VM output + * contains RTM locking statistics for compiled method and that total locks + * count equals to method's invocations. + * Since last assert is pretty strict, test uses -XX:RTMRetryCount=0 in order + * to avoid issue with retriable aborts described in + * {@link TestUseRTMAfterLockInflation}. + */ +public class TestUseRTMLockElidingOption extends CommandLineOptionTest { + private TestUseRTMLockElidingOption() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + public void runTestCases() throws Throwable { + verifyOption(false); + verifyOption(true); + } + + public void verifyOption(boolean useStackLock) throws Throwable { + AbortProvoker provoker = AbortType.XABORT.provoker(); + String logFileName = String.format("rtm_deopt_%s_stack_lock.xml", + (useStackLock ? "use" : "no")); + String methodOption = String.format("-XX:CompileCommand=option," + + "%s,UseRTMLockEliding", provoker.getMethodWithLockName()); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + logFileName, + provoker, + CommandLineOptionTest.prepareBooleanFlag("UseRTMForStackLocks", + useStackLock), + methodOption, + "-XX:RTMTotalCountIncrRate=1", + "-XX:RTMRetryCount=0", + "-XX:+UseRTMDeopt", + "-XX:+PrintPreciseRTMLockingStatistics", + provoker.getClass().getName(), + AbortType.XABORT.toString(), + Boolean.toString(!useStackLock) + ); + + outputAnalyzer.shouldHaveExitValue(0); + + int firedTraps = RTMTestBase.firedRTMStateChangeTraps(logFileName); + + Asserts.assertEQ(firedTraps, 0, + "Method deoptimization with rtm_state_change is unexpected"); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(), outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 1, + "VM output should contain exactly one RTM locking " + + "statistics entry for method " + + provoker.getMethodWithLockName()); + + RTMLockingStatistics lock = statistics.get(0); + + Asserts.assertEQ(lock.getTotalLocks(), AbortProvoker.DEFAULT_ITERATIONS, + "Expected to get total locks count equal to total amount of " + + "lock attempts."); + } + + public static void main(String args[]) throws Throwable { + new TestUseRTMLockElidingOption().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 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 + * 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 8031320 + * @summary Verify that rtm locking statistics contain proper information + * on overall aborts and locks count and count of aborts of + * different types. Test also verify that VM output does not + * contain rtm locking statistics when it should not. + * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary + * @build TestPrintPreciseRTMLockingStatistics + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI TestPrintPreciseRTMLockingStatistics + */ + +import java.util.*; + +import com.oracle.java.testlibrary.*; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; +import com.oracle.java.testlibrary.cli.predicate.AndPredicate; +import rtm.*; +import rtm.predicate.SupportedCPU; +import rtm.predicate.SupportedVM; + +/** + * Test verifies that VM output does not contain RTM locking statistics when it + * should not (when PrintPreciseRTMLockingStatistics is off) and that with + * -XX:+PrintPreciseRTMLockingStatistics locking statistics contains sane + * total locks and aborts count as well as for specific abort types. + */ +public class TestPrintPreciseRTMLockingStatistics + extends CommandLineOptionTest { + private TestPrintPreciseRTMLockingStatistics() { + super(new AndPredicate(new SupportedCPU(), new SupportedVM())); + } + + @Override + public void runTestCases() throws Throwable { + verifyNoStatistics(); + verifyStatistics(); + } + + // verify that VM output does not contain + // rtm locking statistics + private void verifyNoStatistics() throws Throwable { + verifyNoStatistics(AbortType.XABORT); + + verifyNoStatistics(AbortType.XABORT, + "-XX:-PrintPreciseRTMLockingStatistics"); + + verifyNoStatistics(AbortType.XABORT, "-XX:-UseRTMLocking", + "-XX:+PrintPreciseRTMLockingStatistics"); + } + + // verify that rtm locking statistics contain information + // about each type of aborts + private void verifyStatistics() throws Throwable { + verifyAbortsCount(AbortType.XABORT); + verifyAbortsCount(AbortType.MEM_CONFLICT); + verifyAbortsCount(AbortType.BUF_OVERFLOW); + verifyAbortsCount(AbortType.NESTED_ABORT); + } + + private void verifyNoStatistics(AbortType abortProvokerType, + String... vmOpts) throws Throwable { + AbortProvoker provoker = abortProvokerType.provoker(); + List finalVMOpts = new LinkedList<>(); + Collections.addAll(finalVMOpts, vmOpts); + Collections.addAll(finalVMOpts, AbortProvoker.class.getName(), + abortProvokerType.toString()); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest(provoker, + finalVMOpts.toArray(new String[finalVMOpts.size()])); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + outputAnalyzer.getOutput()); + + Asserts.assertEQ(statistics.size(), 0, "VM output should not contain " + + "any RTM locking statistics"); + } + + private void verifyAbortsCount(AbortType abortType) throws Throwable { + AbortProvoker provoker = abortType.provoker(); + + OutputAnalyzer outputAnalyzer = RTMTestBase.executeRTMTest( + provoker, + "-XX:+PrintPreciseRTMLockingStatistics", + AbortProvoker.class.getName(), + abortType.toString()); + + outputAnalyzer.shouldHaveExitValue(0); + + List statistics = RTMLockingStatistics.fromString( + provoker.getMethodWithLockName(),outputAnalyzer.getOutput()); + + Asserts.assertGT(statistics.size(), 0, "VM output should contain one " + + "rtm locking statistics entry for method " + + provoker.getMethodWithLockName()); + + RTMLockingStatistics lock = statistics.get(0); + + Asserts.assertGT(lock.getTotalLocks(), 0L, "RTM locking statistics " + + "should contain non zero total locks count"); + + Asserts.assertGT(lock.getTotalAborts(), 0L, + "RTM locking statistics should contain non zero total aborts " + + "count"); + + Asserts.assertGT(lock.getAborts(abortType), 0L, String.format( + "RTM locking statistics should contain non zero aborts count " + + "for abort reason %s", abortType)); + } + + public static void main(String args[]) throws Throwable { + new TestPrintPreciseRTMLockingStatistics().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/StableConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/StableConfiguration.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.invoke; + +import java.lang.reflect.Method; +import java.util.Properties; +import sun.hotspot.WhiteBox; + +public class StableConfiguration { + static final WhiteBox WB = WhiteBox.getWhiteBox(); + static final boolean isStableEnabled; + static final boolean isServerWithStable; + + static { + Boolean value = WB.getBooleanVMFlag("FoldStableValues"); + isStableEnabled = (value == null ? false : value); + isServerWithStable = isStableEnabled && get(); + System.out.println("@Stable: " + (isStableEnabled ? "enabled" : "disabled")); + System.out.println("Server Compiler: " + get()); + } + + // ::get() is among immediately compiled methods. + static boolean get() { + try { + Method m = StableConfiguration.class.getDeclaredMethod("get"); + int level = WB.getMethodCompilationLevel(m); + if (level > 0) { + return (level == 4); + } else { + String javaVM = System.getProperty("java.vm.name", ""); + if (javaVM.contains("Server")) return true; + if (javaVM.contains("Client")) return false; + throw new Error("Unknown VM type: "+javaVM); + } + } catch (NoSuchMethodException e) { + throw new Error(e); + } + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableBoolean.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableBoolean.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,645 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableBoolean + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableBoolean StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableBoolean + * java/lang/invoke/TestStableBoolean$BooleanStable + * java/lang/invoke/TestStableBoolean$StaticBooleanStable + * java/lang/invoke/TestStableBoolean$VolatileBooleanStable + * java/lang/invoke/TestStableBoolean$BooleanArrayDim1 + * java/lang/invoke/TestStableBoolean$BooleanArrayDim2 + * java/lang/invoke/TestStableBoolean$BooleanArrayDim3 + * java/lang/invoke/TestStableBoolean$BooleanArrayDim4 + * java/lang/invoke/TestStableBoolean$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableBoolean$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableBoolean$NestedStableField + * java/lang/invoke/TestStableBoolean$NestedStableField$A + * java/lang/invoke/TestStableBoolean$NestedStableField1 + * java/lang/invoke/TestStableBoolean$NestedStableField1$A + * java/lang/invoke/TestStableBoolean$NestedStableField2 + * java/lang/invoke/TestStableBoolean$NestedStableField2$A + * java/lang/invoke/TestStableBoolean$NestedStableField3 + * java/lang/invoke/TestStableBoolean$NestedStableField3$A + * java/lang/invoke/TestStableBoolean$DefaultValue + * java/lang/invoke/TestStableBoolean$DefaultStaticValue + * java/lang/invoke/TestStableBoolean$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableBoolean + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableBoolean + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableBoolean + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableBoolean + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableBoolean + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableBoolean + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableBoolean { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(BooleanStable.class); + run(DefaultStaticValue.class); + run(StaticBooleanStable.class); + run(VolatileBooleanStable.class); + + // @Stable arrays: Dim 1-4 + run(BooleanArrayDim1.class); + run(BooleanArrayDim2.class); + run(BooleanArrayDim3.class); + run(BooleanArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable boolean v; + + public static final DefaultValue c = new DefaultValue(); + public static boolean get() { return c.v; } + public static void test() throws Exception { + boolean val1 = get(); + c.v = true; boolean val2 = get(); + assertEquals(val1, false); + assertEquals(val2, true); + } + } + + /* ==================================================== */ + + static class BooleanStable { + public @Stable boolean v; + + public static final BooleanStable c = new BooleanStable(); + public static boolean get() { return c.v; } + public static void test() throws Exception { + c.v = true; boolean val1 = get(); + c.v = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isStableEnabled ? true : false)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable boolean v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static boolean get() { return c.v; } + public static void test() throws Exception { + boolean val1 = get(); + c.v = true; boolean val2 = get(); + assertEquals(val1, false); + assertEquals(val2, true); + } + } + + /* ==================================================== */ + + static class StaticBooleanStable { + public static @Stable boolean v; + + public static final StaticBooleanStable c = new StaticBooleanStable(); + public static boolean get() { return c.v; } + public static void test() throws Exception { + c.v = true; boolean val1 = get(); + c.v = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isStableEnabled ? true : false)); + } + } + + /* ==================================================== */ + + static class VolatileBooleanStable { + public @Stable volatile boolean v; + + public static final VolatileBooleanStable c = new VolatileBooleanStable(); + public static boolean get() { return c.v; } + public static void test() throws Exception { + c.v = true; boolean val1 = get(); + c.v = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isStableEnabled ? true : false)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class BooleanArrayDim1 { + public @Stable boolean[] v; + + public static final BooleanArrayDim1 c = new BooleanArrayDim1(); + public static boolean get() { return c.v[0]; } + public static boolean get1() { return c.v[10]; } + public static boolean[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new boolean[1]; c.v[0] = true; boolean val1 = get(); + c.v[0] = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isServerWithStable ? true : false)); + } + + { + c.v = new boolean[20]; c.v[10] = true; boolean val1 = get1(); + c.v[10] = false; boolean val2 = get1(); + assertEquals(val1, true); + assertEquals(val2, (isServerWithStable ? true : false)); + } + + { + c.v = new boolean[1]; boolean[] val1 = get2(); + c.v = new boolean[1]; boolean[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class BooleanArrayDim2 { + public @Stable boolean[][] v; + + public static final BooleanArrayDim2 c = new BooleanArrayDim2(); + public static boolean get() { return c.v[0][0]; } + public static boolean[] get1() { return c.v[0]; } + public static boolean[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new boolean[1][1]; c.v[0][0] = true; boolean val1 = get(); + c.v[0][0] = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isServerWithStable ? true : false)); + + c.v = new boolean[1][1]; c.v[0][0] = false; boolean val3 = get(); + assertEquals(val3, (isServerWithStable ? true : false)); + + c.v[0] = new boolean[1]; c.v[0][0] = false; boolean val4 = get(); + assertEquals(val4, (isServerWithStable ? true : false)); + } + + { + c.v = new boolean[1][1]; boolean[] val1 = get1(); + c.v[0] = new boolean[1]; boolean[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1]; boolean[][] val1 = get2(); + c.v = new boolean[1][1]; boolean[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class BooleanArrayDim3 { + public @Stable boolean[][][] v; + + public static final BooleanArrayDim3 c = new BooleanArrayDim3(); + public static boolean get() { return c.v[0][0][0]; } + public static boolean[] get1() { return c.v[0][0]; } + public static boolean[][] get2() { return c.v[0]; } + public static boolean[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new boolean[1][1][1]; c.v[0][0][0] = true; boolean val1 = get(); + c.v[0][0][0] = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isServerWithStable ? true : false)); + + c.v = new boolean[1][1][1]; c.v[0][0][0] = false; boolean val3 = get(); + assertEquals(val3, (isServerWithStable ? true : false)); + + c.v[0] = new boolean[1][1]; c.v[0][0][0] = false; boolean val4 = get(); + assertEquals(val4, (isServerWithStable ? true : false)); + + c.v[0][0] = new boolean[1]; c.v[0][0][0] = false; boolean val5 = get(); + assertEquals(val5, (isServerWithStable ? true : false)); + } + + { + c.v = new boolean[1][1][1]; boolean[] val1 = get1(); + c.v[0][0] = new boolean[1]; boolean[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1][1]; boolean[][] val1 = get2(); + c.v[0] = new boolean[1][1]; boolean[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1][1]; boolean[][][] val1 = get3(); + c.v = new boolean[1][1][1]; boolean[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class BooleanArrayDim4 { + public @Stable boolean[][][][] v; + + public static final BooleanArrayDim4 c = new BooleanArrayDim4(); + public static boolean get() { return c.v[0][0][0][0]; } + public static boolean[] get1() { return c.v[0][0][0]; } + public static boolean[][] get2() { return c.v[0][0]; } + public static boolean[][][] get3() { return c.v[0]; } + public static boolean[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new boolean[1][1][1][1]; c.v[0][0][0][0] = true; boolean val1 = get(); + c.v[0][0][0][0] = false; boolean val2 = get(); + assertEquals(val1, true); + assertEquals(val2, (isServerWithStable ? true : false)); + + c.v = new boolean[1][1][1][1]; c.v[0][0][0][0] = false; boolean val3 = get(); + assertEquals(val3, (isServerWithStable ? true : false)); + + c.v[0] = new boolean[1][1][1]; c.v[0][0][0][0] = false; boolean val4 = get(); + assertEquals(val4, (isServerWithStable ? true : false)); + + c.v[0][0] = new boolean[1][1]; c.v[0][0][0][0] = false; boolean val5 = get(); + assertEquals(val5, (isServerWithStable ? true : false)); + + c.v[0][0][0] = new boolean[1]; c.v[0][0][0][0] = false; boolean val6 = get(); + assertEquals(val6, (isServerWithStable ? true : false)); + } + + { + c.v = new boolean[1][1][1][1]; boolean[] val1 = get1(); + c.v[0][0][0] = new boolean[1]; boolean[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1][1][1]; boolean[][] val1 = get2(); + c.v[0][0] = new boolean[1][1]; boolean[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1][1][1]; boolean[][][] val1 = get3(); + c.v[0] = new boolean[1][1][1]; boolean[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1][1][1]; boolean[][][][] val1 = get4(); + c.v = new boolean[1][1][1][1]; boolean[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static boolean get() { return ((boolean[])c.v)[0]; } + public static boolean[] get1() { return (boolean[])c.v; } + public static boolean[] get2() { return (boolean[])c.v; } + + public static void test() throws Exception { + { + c.v = new boolean[1]; ((boolean[])c.v)[0] = true; boolean val1 = get(); + ((boolean[])c.v)[0] = false; boolean val2 = get(); + + assertEquals(val1, true); + assertEquals(val2, false); + } + + { + c.v = new boolean[1]; boolean[] val1 = get1(); + c.v = new boolean[1]; boolean[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static boolean get() { return ((boolean[][])c.v)[0][0]; } + public static boolean[] get1() { return (boolean[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new boolean[1][1]; ((boolean[][])c.v)[0][0] = true; boolean val1 = get(); + ((boolean[][])c.v)[0][0] = false; boolean val2 = get(); + + assertEquals(val1, true); + assertEquals(val2, false); + } + + { + c.v = new boolean[1][1]; c.v[0] = new boolean[0]; boolean[] val1 = get1(); + c.v[0] = new boolean[0]; boolean[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[0][0]; Object[] val1 = get2(); + c.v = new boolean[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static boolean get() { return ((boolean[][][])c.v)[0][0][0]; } + public static boolean[] get1() { return (boolean[])(c.v[0][0]); } + public static boolean[][] get2() { return (boolean[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new boolean[1][1][1]; ((boolean[][][])c.v)[0][0][0] = true; boolean val1 = get(); + ((boolean[][][])c.v)[0][0][0] = false; boolean val2 = get(); + + assertEquals(val1, true); + assertEquals(val2, false); + } + + { + c.v = new boolean[1][1][1]; c.v[0][0] = new boolean[0]; boolean[] val1 = get1(); + c.v[0][0] = new boolean[0]; boolean[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[1][1][1]; c.v[0] = new boolean[0][0]; boolean[][] val1 = get2(); + c.v[0] = new boolean[0][0]; boolean[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new boolean[0][0][0]; Object[][] val1 = get3(); + c.v = new boolean[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable boolean a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static boolean get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = true; A val1 = get(); + c.v.a = false; A val2 = get(); + + assertEquals(val1.a, false); + assertEquals(val2.a, false); + } + + { + c.v = new A(); c.v.a = true; boolean val1 = get1(); + c.v.a = false; boolean val2 = get1(); + c.v = new A(); c.v.a = false; boolean val3 = get1(); + + assertEquals(val1, true); + assertEquals(val2, (isStableEnabled ? true : false)); + assertEquals(val3, (isStableEnabled ? true : false)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable boolean a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static boolean get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = true; c.v.next.a = true; A val1 = get(); + c.v.a = false; c.v.next.a = false; A val2 = get(); + + assertEquals(val1.a, false); + assertEquals(val2.a, false); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = true; boolean val1 = get1(); + c.v.a = false; boolean val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = false; boolean val3 = get1(); + + assertEquals(val1, true); + assertEquals(val2, (isStableEnabled ? true : false)); + assertEquals(val3, (isStableEnabled ? true : false)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable boolean a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static boolean get() { return c.v.left.left.left.a; } + public static boolean get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = true; boolean val1 = get(); boolean val2 = get1(); + c.v.a = false; boolean val3 = get(); boolean val4 = get1(); + + assertEquals(val1, true); + assertEquals(val3, (isStableEnabled ? true : false)); + + assertEquals(val2, true); + assertEquals(val4, false); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable boolean a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static boolean get() { return c.v[0].left[1].left[0].left[1].a; } + public static boolean get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = true; boolean val1 = get(); boolean val2 = get1(); + elem.a = false; boolean val3 = get(); boolean val4 = get1(); + + assertEquals(val1, true); + assertEquals(val3, (isServerWithStable ? true : false)); + + assertEquals(val2, true); + assertEquals(val4, false); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(boolean i, boolean j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableByte.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableByte.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,661 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableByte + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableByte StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableByte + * java/lang/invoke/TestStableByte$ByteStable + * java/lang/invoke/TestStableByte$StaticByteStable + * java/lang/invoke/TestStableByte$VolatileByteStable + * java/lang/invoke/TestStableByte$ByteArrayDim1 + * java/lang/invoke/TestStableByte$ByteArrayDim2 + * java/lang/invoke/TestStableByte$ByteArrayDim3 + * java/lang/invoke/TestStableByte$ByteArrayDim4 + * java/lang/invoke/TestStableByte$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableByte$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableByte$NestedStableField + * java/lang/invoke/TestStableByte$NestedStableField$A + * java/lang/invoke/TestStableByte$NestedStableField1 + * java/lang/invoke/TestStableByte$NestedStableField1$A + * java/lang/invoke/TestStableByte$NestedStableField2 + * java/lang/invoke/TestStableByte$NestedStableField2$A + * java/lang/invoke/TestStableByte$NestedStableField3 + * java/lang/invoke/TestStableByte$NestedStableField3$A + * java/lang/invoke/TestStableByte$DefaultValue + * java/lang/invoke/TestStableByte$DefaultStaticValue + * java/lang/invoke/TestStableByte$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableByte + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableByte + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableByte + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableByte + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableByte + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableByte + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableByte { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(ByteStable.class); + run(DefaultStaticValue.class); + run(StaticByteStable.class); + run(VolatileByteStable.class); + + // @Stable arrays: Dim 1-4 + run(ByteArrayDim1.class); + run(ByteArrayDim2.class); + run(ByteArrayDim3.class); + run(ByteArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable byte v; + + public static final DefaultValue c = new DefaultValue(); + public static byte get() { return c.v; } + public static void test() throws Exception { + byte val1 = get(); + c.v = 1; byte val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1); + } + } + + /* ==================================================== */ + + static class ByteStable { + public @Stable byte v; + + public static final ByteStable c = new ByteStable(); + public static byte get() { return c.v; } + public static void test() throws Exception { + c.v = 5; byte val1 = get(); + c.v = 127; byte val2 = get(); + assertEquals(val1, 5); + assertEquals(val2, (isStableEnabled ? 5 : 127)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable byte v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static byte get() { return c.v; } + public static void test() throws Exception { + byte val1 = get(); + c.v = 1; byte val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1); + } + } + + /* ==================================================== */ + + static class StaticByteStable { + public static @Stable byte v; + + public static final StaticByteStable c = new StaticByteStable(); + public static byte get() { return c.v; } + public static void test() throws Exception { + c.v = 5; byte val1 = get(); + c.v = 127; byte val2 = get(); + assertEquals(val1, 5); + assertEquals(val2, (isStableEnabled ? 5 : 127)); + } + } + + /* ==================================================== */ + + static class VolatileByteStable { + public @Stable volatile byte v; + + public static final VolatileByteStable c = new VolatileByteStable(); + public static byte get() { return c.v; } + public static void test() throws Exception { + c.v = 5; byte val1 = get(); + c.v = 127; byte val2 = get(); + assertEquals(val1, 5); + assertEquals(val2, (isStableEnabled ? 5 : 127)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class ByteArrayDim1 { + public @Stable byte[] v; + + public static final ByteArrayDim1 c = new ByteArrayDim1(); + public static byte get() { return c.v[0]; } + public static byte get1() { return c.v[10]; } + public static byte[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new byte[1]; c.v[0] = 1; byte val1 = get(); + c.v[0] = 2; byte val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new byte[1]; c.v[0] = 3; byte val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new byte[20]; c.v[10] = 1; byte val1 = get1(); + c.v[10] = 2; byte val2 = get1(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new byte[20]; c.v[10] = 3; byte val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new byte[1]; byte[] val1 = get2(); + c.v = new byte[1]; byte[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ByteArrayDim2 { + public @Stable byte[][] v; + + public static final ByteArrayDim2 c = new ByteArrayDim2(); + public static byte get() { return c.v[0][0]; } + public static byte[] get1() { return c.v[0]; } + public static byte[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new byte[1][1]; c.v[0][0] = 1; byte val1 = get(); + c.v[0][0] = 2; byte val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new byte[1][1]; c.v[0][0] = 3; byte val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new byte[1]; c.v[0][0] = 4; byte val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + } + + { + c.v = new byte[1][1]; byte[] val1 = get1(); + c.v[0] = new byte[1]; byte[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1]; byte[][] val1 = get2(); + c.v = new byte[1][1]; byte[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ByteArrayDim3 { + public @Stable byte[][][] v; + + public static final ByteArrayDim3 c = new ByteArrayDim3(); + public static byte get() { return c.v[0][0][0]; } + public static byte[] get1() { return c.v[0][0]; } + public static byte[][] get2() { return c.v[0]; } + public static byte[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new byte[1][1][1]; c.v[0][0][0] = 1; byte val1 = get(); + c.v[0][0][0] = 2; byte val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new byte[1][1][1]; c.v[0][0][0] = 3; byte val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new byte[1][1]; c.v[0][0][0] = 4; byte val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new byte[1]; c.v[0][0][0] = 5; byte val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + } + + { + c.v = new byte[1][1][1]; byte[] val1 = get1(); + c.v[0][0] = new byte[1]; byte[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1][1]; byte[][] val1 = get2(); + c.v[0] = new byte[1][1]; byte[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1][1]; byte[][][] val1 = get3(); + c.v = new byte[1][1][1]; byte[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ByteArrayDim4 { + public @Stable byte[][][][] v; + + public static final ByteArrayDim4 c = new ByteArrayDim4(); + public static byte get() { return c.v[0][0][0][0]; } + public static byte[] get1() { return c.v[0][0][0]; } + public static byte[][] get2() { return c.v[0][0]; } + public static byte[][][] get3() { return c.v[0]; } + public static byte[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new byte[1][1][1][1]; c.v[0][0][0][0] = 1; byte val1 = get(); + c.v[0][0][0][0] = 2; byte val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new byte[1][1][1][1]; c.v[0][0][0][0] = 3; byte val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new byte[1][1][1]; c.v[0][0][0][0] = 4; byte val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new byte[1][1]; c.v[0][0][0][0] = 5; byte val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + + c.v[0][0][0] = new byte[1]; c.v[0][0][0][0] = 6; byte val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 6)); + } + + { + c.v = new byte[1][1][1][1]; byte[] val1 = get1(); + c.v[0][0][0] = new byte[1]; byte[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1][1][1]; byte[][] val1 = get2(); + c.v[0][0] = new byte[1][1]; byte[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1][1][1]; byte[][][] val1 = get3(); + c.v[0] = new byte[1][1][1]; byte[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1][1][1]; byte[][][][] val1 = get4(); + c.v = new byte[1][1][1][1]; byte[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static byte get() { return ((byte[])c.v)[0]; } + public static byte[] get1() { return (byte[])c.v; } + + public static void test() throws Exception { + { + c.v = new byte[1]; ((byte[])c.v)[0] = 1; byte val1 = get(); + ((byte[])c.v)[0] = 2; byte val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new byte[1]; byte[] val1 = get1(); + c.v = new byte[1]; byte[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static byte get() { return ((byte[][])c.v)[0][0]; } + public static byte[] get1() { return (byte[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new byte[1][1]; ((byte[][])c.v)[0][0] = 1; byte val1 = get(); + ((byte[][])c.v)[0][0] = 2; byte val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new byte[1][1]; c.v[0] = new byte[0]; byte[] val1 = get1(); + c.v[0] = new byte[0]; byte[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[0][0]; Object[] val1 = get2(); + c.v = new byte[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static byte get() { return ((byte[][][])c.v)[0][0][0]; } + public static byte[] get1() { return (byte[])(c.v[0][0]); } + public static byte[][] get2() { return (byte[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new byte[1][1][1]; ((byte[][][])c.v)[0][0][0] = 1; byte val1 = get(); + ((byte[][][])c.v)[0][0][0] = 2; byte val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new byte[1][1][1]; c.v[0][0] = new byte[0]; byte[] val1 = get1(); + c.v[0][0] = new byte[0]; byte[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[1][1][1]; c.v[0] = new byte[0][0]; byte[][] val1 = get2(); + c.v[0] = new byte[0][0]; byte[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new byte[0][0][0]; Object[][] val1 = get3(); + c.v = new byte[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable byte a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static byte get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 1; A val1 = get(); + c.v.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.a = 1; byte val1 = get1(); + c.v.a = 2; byte val2 = get1(); + c.v = new A(); c.v.a = 3; byte val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable byte a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static byte get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 1; c.v.next.a = 1; A val1 = get(); + c.v.a = 2; c.v.next.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 1; byte val1 = get1(); + c.v.a = 2; byte val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 3; byte val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable byte a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static byte get() { return c.v.left.left.left.a; } + public static byte get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 1; byte val1 = get(); byte val2 = get1(); + c.v.a = 2; byte val3 = get(); byte val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isStableEnabled ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable byte a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static byte get() { return c.v[0].left[1].left[0].left[1].a; } + public static byte get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 1; byte val1 = get(); byte val2 = get1(); + elem.a = 2; byte val3 = get(); byte val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isServerWithStable ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(int i, int j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableChar.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableChar.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableChar + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableChar StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableChar + * java/lang/invoke/TestStableChar$CharStable + * java/lang/invoke/TestStableChar$StaticCharStable + * java/lang/invoke/TestStableChar$VolatileCharStable + * java/lang/invoke/TestStableChar$CharArrayDim1 + * java/lang/invoke/TestStableChar$CharArrayDim2 + * java/lang/invoke/TestStableChar$CharArrayDim3 + * java/lang/invoke/TestStableChar$CharArrayDim4 + * java/lang/invoke/TestStableChar$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableChar$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableChar$NestedStableField + * java/lang/invoke/TestStableChar$NestedStableField$A + * java/lang/invoke/TestStableChar$NestedStableField1 + * java/lang/invoke/TestStableChar$NestedStableField1$A + * java/lang/invoke/TestStableChar$NestedStableField2 + * java/lang/invoke/TestStableChar$NestedStableField2$A + * java/lang/invoke/TestStableChar$NestedStableField3 + * java/lang/invoke/TestStableChar$NestedStableField3$A + * java/lang/invoke/TestStableChar$DefaultValue + * java/lang/invoke/TestStableChar$DefaultStaticValue + * java/lang/invoke/TestStableChar$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableChar + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableChar + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableChar + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableChar + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableChar + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableChar + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableChar { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(CharStable.class); + run(DefaultStaticValue.class); + run(StaticCharStable.class); + run(VolatileCharStable.class); + + // @Stable arrays: Dim 1-4 + run(CharArrayDim1.class); + run(CharArrayDim2.class); + run(CharArrayDim3.class); + run(CharArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable char v; + + public static final DefaultValue c = new DefaultValue(); + public static char get() { return c.v; } + public static void test() throws Exception { + char val1 = get(); + c.v = 'a'; char val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 'a'); + } + } + + /* ==================================================== */ + + static class CharStable { + public @Stable char v; + + public static final CharStable c = new CharStable(); + public static char get() { return c.v; } + public static void test() throws Exception { + c.v = 'a'; char val1 = get(); + c.v = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isStableEnabled ? 'a' : 'b')); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable char v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static char get() { return c.v; } + public static void test() throws Exception { + char val1 = get(); + c.v = 'a'; char val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 'a'); + } + } + + /* ==================================================== */ + + static class StaticCharStable { + public @Stable char v; + + public static final StaticCharStable c = new StaticCharStable(); + public static char get() { return c.v; } + public static void test() throws Exception { + c.v = 'a'; char val1 = get(); + c.v = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isStableEnabled ? 'a' : 'b')); + } + } + + /* ==================================================== */ + + static class VolatileCharStable { + public @Stable volatile char v; + + public static final VolatileCharStable c = new VolatileCharStable(); + public static char get() { return c.v; } + public static void test() throws Exception { + c.v = 'a'; char val1 = get(); + c.v = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isStableEnabled ? 'a' : 'b')); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class CharArrayDim1 { + public @Stable char[] v; + + public static final CharArrayDim1 c = new CharArrayDim1(); + public static char get() { return c.v[0]; } + public static char get1() { return c.v[10]; } + public static char[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new char[1]; c.v[0] = 'a'; char val1 = get(); + c.v[0] = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isServerWithStable ? 'a' : 'b')); + + c.v = new char[1]; c.v[0] = 'c'; char val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'c')); + } + + { + c.v = new char[20]; c.v[10] = 'a'; char val1 = get1(); + c.v[10] = 'b'; char val2 = get1(); + assertEquals(val1, 'a'); + assertEquals(val2, (isServerWithStable ? 'a' : 'b')); + + c.v = new char[20]; c.v[10] = 'c'; char val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'c')); + } + + { + c.v = new char[1]; char[] val1 = get2(); + c.v = new char[1]; char[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class CharArrayDim2 { + public @Stable char[][] v; + + public static final CharArrayDim2 c = new CharArrayDim2(); + public static char get() { return c.v[0][0]; } + public static char[] get1() { return c.v[0]; } + public static char[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new char[1][1]; c.v[0][0] = 'a'; char val1 = get(); + c.v[0][0] = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isServerWithStable ? 'a' : 'b')); + + c.v = new char[1][1]; c.v[0][0] = 'c'; char val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'c')); + + c.v[0] = new char[1]; c.v[0][0] = 'd'; char val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'd')); + } + + { + c.v = new char[1][1]; char[] val1 = get1(); + c.v[0] = new char[1]; char[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1]; char[][] val1 = get2(); + c.v = new char[1][1]; char[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class CharArrayDim3 { + public @Stable char[][][] v; + + public static final CharArrayDim3 c = new CharArrayDim3(); + public static char get() { return c.v[0][0][0]; } + public static char[] get1() { return c.v[0][0]; } + public static char[][] get2() { return c.v[0]; } + public static char[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new char[1][1][1]; c.v[0][0][0] = 'a'; char val1 = get(); + c.v[0][0][0] = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isServerWithStable ? 'a' : 'b')); + + c.v = new char[1][1][1]; c.v[0][0][0] = 'c'; char val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'c')); + + c.v[0] = new char[1][1]; c.v[0][0][0] = 'd'; char val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'd')); + + c.v[0][0] = new char[1]; c.v[0][0][0] = 'e'; char val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'e')); + } + + { + c.v = new char[1][1][1]; char[] val1 = get1(); + c.v[0][0] = new char[1]; char[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1][1]; char[][] val1 = get2(); + c.v[0] = new char[1][1]; char[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1][1]; char[][][] val1 = get3(); + c.v = new char[1][1][1]; char[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class CharArrayDim4 { + public @Stable char[][][][] v; + + public static final CharArrayDim4 c = new CharArrayDim4(); + public static char get() { return c.v[0][0][0][0]; } + public static char[] get1() { return c.v[0][0][0]; } + public static char[][] get2() { return c.v[0][0]; } + public static char[][][] get3() { return c.v[0]; } + public static char[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new char[1][1][1][1]; c.v[0][0][0][0] = 'a'; char val1 = get(); + c.v[0][0][0][0] = 'b'; char val2 = get(); + assertEquals(val1, 'a'); + assertEquals(val2, (isServerWithStable ? 'a' : 'b')); + + c.v = new char[1][1][1][1]; c.v[0][0][0][0] = 'c'; char val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'c')); + + c.v[0] = new char[1][1][1]; c.v[0][0][0][0] = 'd'; char val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'd')); + + c.v[0][0] = new char[1][1]; c.v[0][0][0][0] = 'e'; char val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'e')); + + c.v[0][0][0] = new char[1]; c.v[0][0][0][0] = 'f'; char val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 'a' : 'b') + : 'f')); + } + + { + c.v = new char[1][1][1][1]; char[] val1 = get1(); + c.v[0][0][0] = new char[1]; char[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1][1][1]; char[][] val1 = get2(); + c.v[0][0] = new char[1][1]; char[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1][1][1]; char[][][] val1 = get3(); + c.v[0] = new char[1][1][1]; char[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1][1][1]; char[][][][] val1 = get4(); + c.v = new char[1][1][1][1]; char[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static char get() { return ((char[])c.v)[0]; } + public static char[] get1() { return (char[])c.v; } + + public static void test() throws Exception { + { + c.v = new char[1]; ((char[])c.v)[0] = 'a'; char val1 = get(); + ((char[])c.v)[0] = 'b'; char val2 = get(); + + assertEquals(val1, 'a'); + assertEquals(val2, 'b'); + } + + { + c.v = new char[1]; char[] val1 = get1(); + c.v = new char[1]; char[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static char get() { return ((char[][])c.v)[0][0]; } + public static char[] get1() { return (char[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new char[1][1]; ((char[][])c.v)[0][0] = 'a'; char val1 = get(); + ((char[][])c.v)[0][0] = 'b'; char val2 = get(); + + assertEquals(val1, 'a'); + assertEquals(val2, 'b'); + } + + { + c.v = new char[1][1]; c.v[0] = new char[0]; char[] val1 = get1(); + c.v[0] = new char[0]; char[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[0][0]; Object[] val1 = get2(); + c.v = new char[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static char get() { return ((char[][][])c.v)[0][0][0]; } + public static char[] get1() { return (char[])(c.v[0][0]); } + public static char[][] get2() { return (char[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new char[1][1][1]; ((char[][][])c.v)[0][0][0] = 'a'; char val1 = get(); + ((char[][][])c.v)[0][0][0] = 'b'; char val2 = get(); + + assertEquals(val1, 'a'); + assertEquals(val2, 'b'); + } + + { + c.v = new char[1][1][1]; c.v[0][0] = new char[0]; char[] val1 = get1(); + c.v[0][0] = new char[0]; char[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[1][1][1]; c.v[0] = new char[0][0]; char[][] val1 = get2(); + c.v[0] = new char[0][0]; char[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new char[0][0][0]; Object[][] val1 = get3(); + c.v = new char[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable char a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static char get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 'a'; A val1 = get(); + c.v.a = 'b'; A val2 = get(); + + assertEquals(val1.a, 'b'); + assertEquals(val2.a, 'b'); + } + + { + c.v = new A(); c.v.a = 'a'; char val1 = get1(); + c.v.a = 'b'; char val2 = get1(); + c.v = new A(); c.v.a = 'c'; char val3 = get1(); + + assertEquals(val1, 'a'); + assertEquals(val2, (isStableEnabled ? 'a' : 'b')); + assertEquals(val3, (isStableEnabled ? 'a' : 'c')); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable char a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static char get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 'a'; c.v.next.a = 'a'; A val1 = get(); + c.v.a = 'b'; c.v.next.a = 'b'; A val2 = get(); + + assertEquals(val1.a, 'b'); + assertEquals(val2.a, 'b'); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 'a'; char val1 = get1(); + c.v.a = 'b'; char val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 'c'; char val3 = get1(); + + assertEquals(val1, 'a'); + assertEquals(val2, (isStableEnabled ? 'a' : 'b')); + assertEquals(val3, (isStableEnabled ? 'a' : 'c')); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable char a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static char get() { return c.v.left.left.left.a; } + public static char get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 'a'; char val1 = get(); char val2 = get1(); + c.v.a = 'b'; char val3 = get(); char val4 = get1(); + + assertEquals(val1, 'a'); + assertEquals(val3, (isStableEnabled ? 'a' : 'b')); + + assertEquals(val2, 'a'); + assertEquals(val4, 'b'); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable char a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static char get() { return c.v[0].left[1].left[0].left[1].a; } + public static char get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 'a'; char val1 = get(); char val2 = get1(); + elem.a = 'b'; char val3 = get(); char val4 = get1(); + + assertEquals(val1, 'a'); + assertEquals(val3, (isServerWithStable ? 'a' : 'b')); + + assertEquals(val2, 'a'); + assertEquals(val4, 'b'); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(int i, int j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableDouble.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableDouble.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableDouble + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableDouble StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableDouble + * java/lang/invoke/TestStableDouble$DoubleStable + * java/lang/invoke/TestStableDouble$StaticDoubleStable + * java/lang/invoke/TestStableDouble$VolatileDoubleStable + * java/lang/invoke/TestStableDouble$DoubleArrayDim1 + * java/lang/invoke/TestStableDouble$DoubleArrayDim2 + * java/lang/invoke/TestStableDouble$DoubleArrayDim3 + * java/lang/invoke/TestStableDouble$DoubleArrayDim4 + * java/lang/invoke/TestStableDouble$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableDouble$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableDouble$NestedStableField + * java/lang/invoke/TestStableDouble$NestedStableField$A + * java/lang/invoke/TestStableDouble$NestedStableField1 + * java/lang/invoke/TestStableDouble$NestedStableField1$A + * java/lang/invoke/TestStableDouble$NestedStableField2 + * java/lang/invoke/TestStableDouble$NestedStableField2$A + * java/lang/invoke/TestStableDouble$NestedStableField3 + * java/lang/invoke/TestStableDouble$NestedStableField3$A + * java/lang/invoke/TestStableDouble$DefaultValue + * java/lang/invoke/TestStableDouble$DefaultStaticValue + * java/lang/invoke/TestStableDouble$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableDouble + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableDouble + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableDouble + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableDouble + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableDouble + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableDouble + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableDouble { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(DoubleStable.class); + run(DefaultStaticValue.class); + run(StaticDoubleStable.class); + run(VolatileDoubleStable.class); + + // @Stable arrays: Dim 1-4 + run(DoubleArrayDim1.class); + run(DoubleArrayDim2.class); + run(DoubleArrayDim3.class); + run(DoubleArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable double v; + + public static final DefaultValue c = new DefaultValue(); + public static double get() { return c.v; } + public static void test() throws Exception { + double val1 = get(); + c.v = 1.0; double val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1.0); + } + } + + /* ==================================================== */ + + static class DoubleStable { + public @Stable double v; + + public static final DoubleStable c = new DoubleStable(); + public static double get() { return c.v; } + public static void test() throws Exception { + c.v = 1.0; double val1 = get(); + c.v = Double.MAX_VALUE; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isStableEnabled ? 1.0 : Double.MAX_VALUE)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable double v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static double get() { return c.v; } + public static void test() throws Exception { + double val1 = get(); + c.v = 1.0; double val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1.0); + } + } + + /* ==================================================== */ + + static class StaticDoubleStable { + public static @Stable double v; + + public static final StaticDoubleStable c = new StaticDoubleStable(); + public static double get() { return c.v; } + public static void test() throws Exception { + c.v = 1.0; double val1 = get(); + c.v = Double.MAX_VALUE; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isStableEnabled ? 1.0 : Double.MAX_VALUE)); + } + } + + /* ==================================================== */ + + static class VolatileDoubleStable { + public @Stable double v; + + public static final VolatileDoubleStable c = new VolatileDoubleStable(); + public static double get() { return c.v; } + public static void test() throws Exception { + c.v = 1.0; double val1 = get(); + c.v = Double.MAX_VALUE; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isStableEnabled ? 1.0 : Double.MAX_VALUE)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class DoubleArrayDim1 { + public @Stable double[] v; + + public static final DoubleArrayDim1 c = new DoubleArrayDim1(); + public static double get() { return c.v[0]; } + public static double get1() { return c.v[10]; } + public static double[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new double[1]; c.v[0] = 1.0; double val1 = get(); + c.v[0] = 2.0; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isServerWithStable ? 1.0 : 2.0)); + + c.v = new double[1]; c.v[0] = 3.0; double val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 3.0)); + } + + { + c.v = new double[20]; c.v[10] = 1.0; double val1 = get1(); + c.v[10] = 2.0; double val2 = get1(); + assertEquals(val1, 1.0); + assertEquals(val2, (isServerWithStable ? 1.0 : 2.0)); + + c.v = new double[20]; c.v[10] = 3.0; double val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 3.0)); + } + + { + c.v = new double[1]; double[] val1 = get2(); + c.v = new double[1]; double[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class DoubleArrayDim2 { + public @Stable double[][] v; + + public static final DoubleArrayDim2 c = new DoubleArrayDim2(); + public static double get() { return c.v[0][0]; } + public static double[] get1() { return c.v[0]; } + public static double[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new double[1][1]; c.v[0][0] = 1.0; double val1 = get(); + c.v[0][0] = 2.0; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isServerWithStable ? 1.0 : 2.0)); + + c.v = new double[1][1]; c.v[0][0] = 3.0; double val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 3.0)); + + c.v[0] = new double[1]; c.v[0][0] = 4.0; double val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 4.0)); + } + + { + c.v = new double[1][1]; double[] val1 = get1(); + c.v[0] = new double[1]; double[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1]; double[][] val1 = get2(); + c.v = new double[1][1]; double[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class DoubleArrayDim3 { + public @Stable double[][][] v; + + public static final DoubleArrayDim3 c = new DoubleArrayDim3(); + public static double get() { return c.v[0][0][0]; } + public static double[] get1() { return c.v[0][0]; } + public static double[][] get2() { return c.v[0]; } + public static double[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new double[1][1][1]; c.v[0][0][0] = 1.0; double val1 = get(); + c.v[0][0][0] = 2.0; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isServerWithStable ? 1.0 : 2.0)); + + c.v = new double[1][1][1]; c.v[0][0][0] = 3.0; double val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 3.0)); + + c.v[0] = new double[1][1]; c.v[0][0][0] = 4.0; double val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 4.0)); + + c.v[0][0] = new double[1]; c.v[0][0][0] = 5.0; double val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 5.0)); + } + + { + c.v = new double[1][1][1]; double[] val1 = get1(); + c.v[0][0] = new double[1]; double[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1][1]; double[][] val1 = get2(); + c.v[0] = new double[1][1]; double[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1][1]; double[][][] val1 = get3(); + c.v = new double[1][1][1]; double[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class DoubleArrayDim4 { + public @Stable double[][][][] v; + + public static final DoubleArrayDim4 c = new DoubleArrayDim4(); + public static double get() { return c.v[0][0][0][0]; } + public static double[] get1() { return c.v[0][0][0]; } + public static double[][] get2() { return c.v[0][0]; } + public static double[][][] get3() { return c.v[0]; } + public static double[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new double[1][1][1][1]; c.v[0][0][0][0] = 1.0; double val1 = get(); + c.v[0][0][0][0] = 2.0; double val2 = get(); + assertEquals(val1, 1.0); + assertEquals(val2, (isServerWithStable ? 1.0 : 2.0)); + + c.v = new double[1][1][1][1]; c.v[0][0][0][0] = 3.0; double val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 3.0)); + + c.v[0] = new double[1][1][1]; c.v[0][0][0][0] = 4.0; double val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 4.0)); + + c.v[0][0] = new double[1][1]; c.v[0][0][0][0] = 5.0; double val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 5.0)); + + c.v[0][0][0] = new double[1]; c.v[0][0][0][0] = 6.0; double val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 1.0 : 2.0) + : 6.0)); + } + + { + c.v = new double[1][1][1][1]; double[] val1 = get1(); + c.v[0][0][0] = new double[1]; double[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1][1][1]; double[][] val1 = get2(); + c.v[0][0] = new double[1][1]; double[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1][1][1]; double[][][] val1 = get3(); + c.v[0] = new double[1][1][1]; double[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1][1][1]; double[][][][] val1 = get4(); + c.v = new double[1][1][1][1]; double[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static double get() { return ((double[])c.v)[0]; } + public static double[] get1() { return (double[])c.v; } + + public static void test() throws Exception { + { + c.v = new double[1]; ((double[])c.v)[0] = 1.0; double val1 = get(); + ((double[])c.v)[0] = 2.0; double val2 = get(); + + assertEquals(val1, 1.0); + assertEquals(val2, 2.0); + } + + { + c.v = new double[1]; double[] val1 = get1(); + c.v = new double[1]; double[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static double get() { return ((double[][])c.v)[0][0]; } + public static double[] get1() { return (double[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new double[1][1]; ((double[][])c.v)[0][0] = 1.0; double val1 = get(); + ((double[][])c.v)[0][0] = 2.0; double val2 = get(); + + assertEquals(val1, 1.0); + assertEquals(val2, 2.0); + } + + { + c.v = new double[1][1]; c.v[0] = new double[0]; double[] val1 = get1(); + c.v[0] = new double[0]; double[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[0][0]; Object[] val1 = get2(); + c.v = new double[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static double get() { return ((double[][][])c.v)[0][0][0]; } + public static double[] get1() { return (double[])(c.v[0][0]); } + public static double[][] get2() { return (double[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new double[1][1][1]; ((double[][][])c.v)[0][0][0] = 1.0; double val1 = get(); + ((double[][][])c.v)[0][0][0] = 2.0; double val2 = get(); + + assertEquals(val1, 1.0); + assertEquals(val2, 2.0); + } + + { + c.v = new double[1][1][1]; c.v[0][0] = new double[0]; double[] val1 = get1(); + c.v[0][0] = new double[0]; double[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[1][1][1]; c.v[0] = new double[0][0]; double[][] val1 = get2(); + c.v[0] = new double[0][0]; double[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new double[0][0][0]; Object[][] val1 = get3(); + c.v = new double[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable double a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static double get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 1.0; A val1 = get(); + c.v.a = 2.0; A val2 = get(); + + assertEquals(val1.a, 2.0); + assertEquals(val2.a, 2.0); + } + + { + c.v = new A(); c.v.a = 1.0; double val1 = get1(); + c.v.a = 2.0; double val2 = get1(); + c.v = new A(); c.v.a = 3.0; double val3 = get1(); + + assertEquals(val1, 1.0); + assertEquals(val2, (isStableEnabled ? 1.0 : 2.0)); + assertEquals(val3, (isStableEnabled ? 1.0 : 3.0)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable double a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static double get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 1.0; c.v.next.a = 1.0; A val1 = get(); + c.v.a = 2.0; c.v.next.a = 2.0; A val2 = get(); + + assertEquals(val1.a, 2.0); + assertEquals(val2.a, 2.0); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 1.0; double val1 = get1(); + c.v.a = 2.0; double val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 3.0; double val3 = get1(); + + assertEquals(val1, 1.0); + assertEquals(val2, (isStableEnabled ? 1.0 : 2.0)); + assertEquals(val3, (isStableEnabled ? 1.0 : 3.0)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable double a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static double get() { return c.v.left.left.left.a; } + public static double get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 1.0; double val1 = get(); double val2 = get1(); + c.v.a = 2.0; double val3 = get(); double val4 = get1(); + + assertEquals(val1, 1.0); + assertEquals(val3, (isStableEnabled ? 1.0 : 2.0)); + + assertEquals(val2, 1.0); + assertEquals(val4, 2.0); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable double a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static double get() { return c.v[0].left[1].left[0].left[1].a; } + public static double get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 1.0; double val1 = get(); double val2 = get1(); + elem.a = 2.0; double val3 = get(); double val4 = get1(); + + assertEquals(val1, 1.0); + assertEquals(val3, (isServerWithStable ? 1.0 : 2.0)); + + assertEquals(val2, 1.0); + assertEquals(val4, 2.0); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(double i, double j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableFloat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableFloat.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableFloat + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableFloat StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableFloat + * java/lang/invoke/TestStableFloat$FloatStable + * java/lang/invoke/TestStableFloat$StaticFloatStable + * java/lang/invoke/TestStableFloat$VolatileFloatStable + * java/lang/invoke/TestStableFloat$FloatArrayDim1 + * java/lang/invoke/TestStableFloat$FloatArrayDim2 + * java/lang/invoke/TestStableFloat$FloatArrayDim3 + * java/lang/invoke/TestStableFloat$FloatArrayDim4 + * java/lang/invoke/TestStableFloat$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableFloat$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableFloat$NestedStableField + * java/lang/invoke/TestStableFloat$NestedStableField$A + * java/lang/invoke/TestStableFloat$NestedStableField1 + * java/lang/invoke/TestStableFloat$NestedStableField1$A + * java/lang/invoke/TestStableFloat$NestedStableField2 + * java/lang/invoke/TestStableFloat$NestedStableField2$A + * java/lang/invoke/TestStableFloat$NestedStableField3 + * java/lang/invoke/TestStableFloat$NestedStableField3$A + * java/lang/invoke/TestStableFloat$DefaultValue + * java/lang/invoke/TestStableFloat$DefaultStaticValue + * java/lang/invoke/TestStableFloat$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableFloat + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableFloat + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableFloat + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableFloat + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableFloat + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableFloat + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableFloat { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(FloatStable.class); + run(DefaultStaticValue.class); + run(StaticFloatStable.class); + run(VolatileFloatStable.class); + + // @Stable arrays: Dim 1-4 + run(FloatArrayDim1.class); + run(FloatArrayDim2.class); + run(FloatArrayDim3.class); + run(FloatArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable float v; + + public static final DefaultValue c = new DefaultValue(); + public static float get() { return c.v; } + public static void test() throws Exception { + float val1 = get(); + c.v = 1.0F; float val2 = get(); + assertEquals(val1, 0F); + assertEquals(val2, 1.0F); + } + } + + /* ==================================================== */ + + static class FloatStable { + public @Stable float v; + + public static final FloatStable c = new FloatStable(); + public static float get() { return c.v; } + public static void test() throws Exception { + c.v = 1.0F; float val1 = get(); + c.v = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isStableEnabled ? 1.0F : 2.0F)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable float v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static float get() { return c.v; } + public static void test() throws Exception { + float val1 = get(); + c.v = 1.0F; float val2 = get(); + assertEquals(val1, 0F); + assertEquals(val2, 1.0F); + } + } + + /* ==================================================== */ + + static class StaticFloatStable { + public static @Stable float v; + + public static final StaticFloatStable c = new StaticFloatStable(); + public static float get() { return c.v; } + public static void test() throws Exception { + c.v = 1.0F; float val1 = get(); + c.v = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isStableEnabled ? 1.0F : 2.0F)); + } + } + + /* ==================================================== */ + + static class VolatileFloatStable { + public @Stable volatile float v; + + public static final VolatileFloatStable c = new VolatileFloatStable(); + public static float get() { return c.v; } + public static void test() throws Exception { + c.v = 1.0F; float val1 = get(); + c.v = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isStableEnabled ? 1.0F : 2.0F)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class FloatArrayDim1 { + public @Stable float[] v; + + public static final FloatArrayDim1 c = new FloatArrayDim1(); + public static float get() { return c.v[0]; } + public static float get1() { return c.v[10]; } + public static float[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new float[1]; c.v[0] = 1.0F; float val1 = get(); + c.v[0] = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isServerWithStable ? 1.0F : 2.0F)); + + c.v = new float[1]; c.v[0] = 3.0F; float val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 3.0F)); + } + + { + c.v = new float[20]; c.v[10] = 1.0F; float val1 = get1(); + c.v[10] = 2.0F; float val2 = get1(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isServerWithStable ? 1.0F : 2.0F)); + + c.v = new float[20]; c.v[10] = 3.0F; float val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 3.0F)); + } + + { + c.v = new float[1]; float[] val1 = get2(); + c.v = new float[1]; float[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class FloatArrayDim2 { + public @Stable float[][] v; + + public static final FloatArrayDim2 c = new FloatArrayDim2(); + public static float get() { return c.v[0][0]; } + public static float[] get1() { return c.v[0]; } + public static float[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new float[1][1]; c.v[0][0] = 1.0F; float val1 = get(); + c.v[0][0] = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isServerWithStable ? 1.0F : 2.0F)); + + c.v = new float[1][1]; c.v[0][0] = 3.0F; float val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 3.0F)); + + c.v[0] = new float[1]; c.v[0][0] = 4.0F; float val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 4.0F)); + } + + { + c.v = new float[1][1]; float[] val1 = get1(); + c.v[0] = new float[1]; float[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1]; float[][] val1 = get2(); + c.v = new float[1][1]; float[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class FloatArrayDim3 { + public @Stable float[][][] v; + + public static final FloatArrayDim3 c = new FloatArrayDim3(); + public static float get() { return c.v[0][0][0]; } + public static float[] get1() { return c.v[0][0]; } + public static float[][] get2() { return c.v[0]; } + public static float[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new float[1][1][1]; c.v[0][0][0] = 1.0F; float val1 = get(); + c.v[0][0][0] = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isServerWithStable ? 1.0F : 2.0F)); + + c.v = new float[1][1][1]; c.v[0][0][0] = 3.0F; float val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 3.0F)); + + c.v[0] = new float[1][1]; c.v[0][0][0] = 4.0F; float val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 4.0F)); + + c.v[0][0] = new float[1]; c.v[0][0][0] = 5.0F; float val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 5.0F)); + } + + { + c.v = new float[1][1][1]; float[] val1 = get1(); + c.v[0][0] = new float[1]; float[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1][1]; float[][] val1 = get2(); + c.v[0] = new float[1][1]; float[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1][1]; float[][][] val1 = get3(); + c.v = new float[1][1][1]; float[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class FloatArrayDim4 { + public @Stable float[][][][] v; + + public static final FloatArrayDim4 c = new FloatArrayDim4(); + public static float get() { return c.v[0][0][0][0]; } + public static float[] get1() { return c.v[0][0][0]; } + public static float[][] get2() { return c.v[0][0]; } + public static float[][][] get3() { return c.v[0]; } + public static float[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new float[1][1][1][1]; c.v[0][0][0][0] = 1.0F; float val1 = get(); + c.v[0][0][0][0] = 2.0F; float val2 = get(); + assertEquals(val1, 1.0F); + assertEquals(val2, (isServerWithStable ? 1.0F : 2.0F)); + + c.v = new float[1][1][1][1]; c.v[0][0][0][0] = 3.0F; float val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 3.0F)); + + c.v[0] = new float[1][1][1]; c.v[0][0][0][0] = 4.0F; float val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 4.0F)); + + c.v[0][0] = new float[1][1]; c.v[0][0][0][0] = 5.0F; float val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 5.0F)); + + c.v[0][0][0] = new float[1]; c.v[0][0][0][0] = 6.0F; float val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 1.0F : 2.0F) + : 6.0F)); + } + + { + c.v = new float[1][1][1][1]; float[] val1 = get1(); + c.v[0][0][0] = new float[1]; float[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1][1][1]; float[][] val1 = get2(); + c.v[0][0] = new float[1][1]; float[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1][1][1]; float[][][] val1 = get3(); + c.v[0] = new float[1][1][1]; float[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1][1][1]; float[][][][] val1 = get4(); + c.v = new float[1][1][1][1]; float[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static float get() { return ((float[])c.v)[0]; } + public static float[] get1() { return (float[])c.v; } + + public static void test() throws Exception { + { + c.v = new float[1]; ((float[])c.v)[0] = 1.0F; float val1 = get(); + ((float[])c.v)[0] = 2.0F; float val2 = get(); + + assertEquals(val1, 1.0F); + assertEquals(val2, 2.0F); + } + + { + c.v = new float[1]; float[] val1 = get1(); + c.v = new float[1]; float[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static float get() { return ((float[][])c.v)[0][0]; } + public static float[] get1() { return (float[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new float[1][1]; ((float[][])c.v)[0][0] = 1.0F; float val1 = get(); + ((float[][])c.v)[0][0] = 2.0F; float val2 = get(); + + assertEquals(val1, 1.0F); + assertEquals(val2, 2.0F); + } + + { + c.v = new float[1][1]; c.v[0] = new float[0]; float[] val1 = get1(); + c.v[0] = new float[0]; float[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[0][0]; Object[] val1 = get2(); + c.v = new float[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static float get() { return ((float[][][])c.v)[0][0][0]; } + public static float[] get1() { return (float[])(c.v[0][0]); } + public static float[][] get2() { return (float[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new float[1][1][1]; ((float[][][])c.v)[0][0][0] = 1.0F; float val1 = get(); + ((float[][][])c.v)[0][0][0] = 2.0F; float val2 = get(); + + assertEquals(val1, 1.0F); + assertEquals(val2, 2.0F); + } + + { + c.v = new float[1][1][1]; c.v[0][0] = new float[0]; float[] val1 = get1(); + c.v[0][0] = new float[0]; float[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[1][1][1]; c.v[0] = new float[0][0]; float[][] val1 = get2(); + c.v[0] = new float[0][0]; float[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new float[0][0][0]; Object[][] val1 = get3(); + c.v = new float[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable float a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static float get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 1.0F; A val1 = get(); + c.v.a = 2.0F; A val2 = get(); + + assertEquals(val1.a, 2.0F); + assertEquals(val2.a, 2.0F); + } + + { + c.v = new A(); c.v.a = 1.0F; float val1 = get1(); + c.v.a = 2.0F; float val2 = get1(); + c.v = new A(); c.v.a = 3.0F; float val3 = get1(); + + assertEquals(val1, 1.0F); + assertEquals(val2, (isStableEnabled ? 1.0F : 2.0F)); + assertEquals(val3, (isStableEnabled ? 1.0F : 3.0F)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable float a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static float get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 1.0F; c.v.next.a = 1.0F; A val1 = get(); + c.v.a = 2.0F; c.v.next.a = 2.0F; A val2 = get(); + + assertEquals(val1.a, 2.0F); + assertEquals(val2.a, 2.0F); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 1.0F; float val1 = get1(); + c.v.a = 2.0F; float val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 3.0F; float val3 = get1(); + + assertEquals(val1, 1.0F); + assertEquals(val2, (isStableEnabled ? 1.0F : 2.0F)); + assertEquals(val3, (isStableEnabled ? 1.0F : 3.0F)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable float a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static float get() { return c.v.left.left.left.a; } + public static float get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 1.0F; float val1 = get(); float val2 = get1(); + c.v.a = 2.0F; float val3 = get(); float val4 = get1(); + + assertEquals(val1, 1.0F); + assertEquals(val3, (isStableEnabled ? 1.0F : 2.0F)); + + assertEquals(val2, 1.0F); + assertEquals(val4, 2.0F); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable float a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static float get() { return c.v[0].left[1].left[0].left[1].a; } + public static float get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 1.0F; float val1 = get(); float val2 = get1(); + elem.a = 2.0F; float val3 = get(); float val4 = get1(); + + assertEquals(val1, 1.0F); + assertEquals(val3, (isServerWithStable ? 1.0F : 2.0F)); + + assertEquals(val2, 1.0F); + assertEquals(val4, 2.0F); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(float i, float j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableInt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableInt.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableInt + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableInt StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableInt + * java/lang/invoke/TestStableInt$IntStable + * java/lang/invoke/TestStableInt$StaticIntStable + * java/lang/invoke/TestStableInt$VolatileIntStable + * java/lang/invoke/TestStableInt$IntArrayDim1 + * java/lang/invoke/TestStableInt$IntArrayDim2 + * java/lang/invoke/TestStableInt$IntArrayDim3 + * java/lang/invoke/TestStableInt$IntArrayDim4 + * java/lang/invoke/TestStableInt$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableInt$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableInt$NestedStableField + * java/lang/invoke/TestStableInt$NestedStableField$A + * java/lang/invoke/TestStableInt$NestedStableField1 + * java/lang/invoke/TestStableInt$NestedStableField1$A + * java/lang/invoke/TestStableInt$NestedStableField2 + * java/lang/invoke/TestStableInt$NestedStableField2$A + * java/lang/invoke/TestStableInt$NestedStableField3 + * java/lang/invoke/TestStableInt$NestedStableField3$A + * java/lang/invoke/TestStableInt$DefaultValue + * java/lang/invoke/TestStableInt$DefaultStaticValue + * java/lang/invoke/TestStableInt$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableInt + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableInt + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableInt + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableInt + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableInt + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableInt + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableInt { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(IntStable.class); + run(DefaultStaticValue.class); + run(StaticIntStable.class); + run(VolatileIntStable.class); + + // @Stable arrays: Dim 1-4 + run(IntArrayDim1.class); + run(IntArrayDim2.class); + run(IntArrayDim3.class); + run(IntArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable int v; + + public static final DefaultValue c = new DefaultValue(); + public static int get() { return c.v; } + public static void test() throws Exception { + int val1 = get(); + c.v = 1; int val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1); + } + } + + /* ==================================================== */ + + static class IntStable { + public @Stable int v; + + public static final IntStable c = new IntStable(); + public static int get() { return c.v; } + public static void test() throws Exception { + c.v = 1; int val1 = get(); + c.v = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable int v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static int get() { return c.v; } + public static void test() throws Exception { + int val1 = get(); + c.v = 1; int val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1); + } + } + + /* ==================================================== */ + + static class StaticIntStable { + public static @Stable int v; + + public static final StaticIntStable c = new StaticIntStable(); + public static int get() { return c.v; } + public static void test() throws Exception { + c.v = 1; int val1 = get(); + c.v = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + } + } + + /* ==================================================== */ + + static class VolatileIntStable { + public @Stable volatile int v; + + public static final VolatileIntStable c = new VolatileIntStable(); + public static int get() { return c.v; } + public static void test() throws Exception { + c.v = 1; int val1 = get(); + c.v = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class IntArrayDim1 { + public @Stable int[] v; + + public static final IntArrayDim1 c = new IntArrayDim1(); + public static int get() { return c.v[0]; } + public static int get1() { return c.v[10]; } + public static int[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new int[1]; c.v[0] = 1; int val1 = get(); + c.v[0] = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new int[1]; c.v[0] = 3; int val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new int[20]; c.v[10] = 1; int val1 = get1(); + c.v[10] = 2; int val2 = get1(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new int[20]; c.v[10] = 3; int val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new int[1]; int[] val1 = get2(); + c.v = new int[1]; int[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class IntArrayDim2 { + public @Stable int[][] v; + + public static final IntArrayDim2 c = new IntArrayDim2(); + public static int get() { return c.v[0][0]; } + public static int[] get1() { return c.v[0]; } + public static int[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new int[1][1]; c.v[0][0] = 1; int val1 = get(); + c.v[0][0] = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new int[1][1]; c.v[0][0] = 3; int val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new int[1]; c.v[0][0] = 4; int val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + } + + { + c.v = new int[1][1]; int[] val1 = get1(); + c.v[0] = new int[1]; int[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1]; int[][] val1 = get2(); + c.v = new int[1][1]; int[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class IntArrayDim3 { + public @Stable int[][][] v; + + public static final IntArrayDim3 c = new IntArrayDim3(); + public static int get() { return c.v[0][0][0]; } + public static int[] get1() { return c.v[0][0]; } + public static int[][] get2() { return c.v[0]; } + public static int[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new int[1][1][1]; c.v[0][0][0] = 1; int val1 = get(); + c.v[0][0][0] = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new int[1][1][1]; c.v[0][0][0] = 3; int val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new int[1][1]; c.v[0][0][0] = 4; int val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new int[1]; c.v[0][0][0] = 5; int val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + } + + { + c.v = new int[1][1][1]; int[] val1 = get1(); + c.v[0][0] = new int[1]; int[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1][1]; int[][] val1 = get2(); + c.v[0] = new int[1][1]; int[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1][1]; int[][][] val1 = get3(); + c.v = new int[1][1][1]; int[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class IntArrayDim4 { + public @Stable int[][][][] v; + + public static final IntArrayDim4 c = new IntArrayDim4(); + public static int get() { return c.v[0][0][0][0]; } + public static int[] get1() { return c.v[0][0][0]; } + public static int[][] get2() { return c.v[0][0]; } + public static int[][][] get3() { return c.v[0]; } + public static int[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new int[1][1][1][1]; c.v[0][0][0][0] = 1; int val1 = get(); + c.v[0][0][0][0] = 2; int val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new int[1][1][1][1]; c.v[0][0][0][0] = 3; int val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new int[1][1][1]; c.v[0][0][0][0] = 4; int val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new int[1][1]; c.v[0][0][0][0] = 5; int val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + + c.v[0][0][0] = new int[1]; c.v[0][0][0][0] = 6; int val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 6)); + } + + { + c.v = new int[1][1][1][1]; int[] val1 = get1(); + c.v[0][0][0] = new int[1]; int[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1][1][1]; int[][] val1 = get2(); + c.v[0][0] = new int[1][1]; int[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1][1][1]; int[][][] val1 = get3(); + c.v[0] = new int[1][1][1]; int[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1][1][1]; int[][][][] val1 = get4(); + c.v = new int[1][1][1][1]; int[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static int get() { return ((int[])c.v)[0]; } + public static int[] get1() { return (int[])c.v; } + + public static void test() throws Exception { + { + c.v = new int[1]; ((int[])c.v)[0] = 1; int val1 = get(); + ((int[])c.v)[0] = 2; int val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new int[1]; int[] val1 = get1(); + c.v = new int[1]; int[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static int get() { return ((int[][])c.v)[0][0]; } + public static int[] get1() { return (int[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new int[1][1]; ((int[][])c.v)[0][0] = 1; int val1 = get(); + ((int[][])c.v)[0][0] = 2; int val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new int[1][1]; c.v[0] = new int[0]; int[] val1 = get1(); + c.v[0] = new int[0]; int[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[0][0]; Object[] val1 = get2(); + c.v = new int[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static int get() { return ((int[][][])c.v)[0][0][0]; } + public static int[] get1() { return (int[])(c.v[0][0]); } + public static int[][] get2() { return (int[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new int[1][1][1]; ((int[][][])c.v)[0][0][0] = 1; int val1 = get(); + ((int[][][])c.v)[0][0][0] = 2; int val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new int[1][1][1]; c.v[0][0] = new int[0]; int[] val1 = get1(); + c.v[0][0] = new int[0]; int[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[1][1][1]; c.v[0] = new int[0][0]; int[][] val1 = get2(); + c.v[0] = new int[0][0]; int[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new int[0][0][0]; Object[][] val1 = get3(); + c.v = new int[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable int a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static int get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 1; A val1 = get(); + c.v.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.a = 1; int val1 = get1(); + c.v.a = 2; int val2 = get1(); + c.v = new A(); c.v.a = 3; int val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable int a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static int get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 1; c.v.next.a = 1; A val1 = get(); + c.v.a = 2; c.v.next.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 1; int val1 = get1(); + c.v.a = 2; int val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 3; int val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable int a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static int get() { return c.v.left.left.left.a; } + public static int get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 1; int val1 = get(); int val2 = get1(); + c.v.a = 2; int val3 = get(); int val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isStableEnabled ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable int a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static int get() { return c.v[0].left[1].left[0].left[1].a; } + public static int get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 1; int val1 = get(); int val2 = get1(); + elem.a = 2; int val3 = get(); int val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isServerWithStable ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(int i, int j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableLong.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableLong.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableLong + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableLong StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableLong + * java/lang/invoke/TestStableLong$LongStable + * java/lang/invoke/TestStableLong$StaticLongStable + * java/lang/invoke/TestStableLong$VolatileLongStable + * java/lang/invoke/TestStableLong$LongArrayDim1 + * java/lang/invoke/TestStableLong$LongArrayDim2 + * java/lang/invoke/TestStableLong$LongArrayDim3 + * java/lang/invoke/TestStableLong$LongArrayDim4 + * java/lang/invoke/TestStableLong$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableLong$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableLong$NestedStableField + * java/lang/invoke/TestStableLong$NestedStableField$A + * java/lang/invoke/TestStableLong$NestedStableField1 + * java/lang/invoke/TestStableLong$NestedStableField1$A + * java/lang/invoke/TestStableLong$NestedStableField2 + * java/lang/invoke/TestStableLong$NestedStableField2$A + * java/lang/invoke/TestStableLong$NestedStableField3 + * java/lang/invoke/TestStableLong$NestedStableField3$A + * java/lang/invoke/TestStableLong$DefaultValue + * java/lang/invoke/TestStableLong$DefaultStaticValue + * java/lang/invoke/TestStableLong$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableLong + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableLong + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableLong + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableLong + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableLong + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableLong + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableLong { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(LongStable.class); + run(DefaultStaticValue.class); + run(StaticLongStable.class); + run(VolatileLongStable.class); + + // @Stable arrays: Dim 1-4 + run(LongArrayDim1.class); + run(LongArrayDim2.class); + run(LongArrayDim3.class); + run(LongArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable long v; + + public static final DefaultValue c = new DefaultValue(); + public static long get() { return c.v; } + public static void test() throws Exception { + long val1 = get(); + c.v = 1L; long val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1L); + } + } + + /* ==================================================== */ + + static class LongStable { + public @Stable long v; + + public static final LongStable c = new LongStable(); + public static long get() { return c.v; } + public static void test() throws Exception { + c.v = 5; long val1 = get(); + c.v = Long.MAX_VALUE; long val2 = get(); + assertEquals(val1, 5); + assertEquals(val2, (isStableEnabled ? 5 : Long.MAX_VALUE)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable long v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static long get() { return c.v; } + public static void test() throws Exception { + long val1 = get(); + c.v = 1L; long val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1L); + } + } + + /* ==================================================== */ + + static class StaticLongStable { + public static @Stable long v; + + public static final StaticLongStable c = new StaticLongStable(); + public static long get() { return c.v; } + public static void test() throws Exception { + c.v = 5; long val1 = get(); + c.v = Long.MAX_VALUE; long val2 = get(); + assertEquals(val1, 5); + assertEquals(val2, (isStableEnabled ? 5 : Long.MAX_VALUE)); + } + } + + /* ==================================================== */ + + static class VolatileLongStable { + public @Stable volatile long v; + + public static final VolatileLongStable c = new VolatileLongStable(); + public static long get() { return c.v; } + public static void test() throws Exception { + c.v = 5; long val1 = get(); + c.v = Long.MAX_VALUE; long val2 = get(); + assertEquals(val1, 5); + assertEquals(val2, (isStableEnabled ? 5 : Long.MAX_VALUE)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class LongArrayDim1 { + public @Stable long[] v; + + public static final LongArrayDim1 c = new LongArrayDim1(); + public static long get() { return c.v[0]; } + public static long get1() { return c.v[10]; } + public static long[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new long[1]; c.v[0] = 1; long val1 = get(); + c.v[0] = 2; long val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new long[1]; c.v[0] = 3; long val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new long[20]; c.v[10] = 1; long val1 = get1(); + c.v[10] = 2; long val2 = get1(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new long[20]; c.v[10] = 3; long val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new long[1]; long[] val1 = get2(); + c.v = new long[1]; long[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class LongArrayDim2 { + public @Stable long[][] v; + + public static final LongArrayDim2 c = new LongArrayDim2(); + public static long get() { return c.v[0][0]; } + public static long[] get1() { return c.v[0]; } + public static long[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new long[1][1]; c.v[0][0] = 1; long val1 = get(); + c.v[0][0] = 2; long val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new long[1][1]; c.v[0][0] = 3; long val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new long[1]; c.v[0][0] = 4; long val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + } + + { + c.v = new long[1][1]; long[] val1 = get1(); + c.v[0] = new long[1]; long[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1]; long[][] val1 = get2(); + c.v = new long[1][1]; long[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class LongArrayDim3 { + public @Stable long[][][] v; + + public static final LongArrayDim3 c = new LongArrayDim3(); + public static long get() { return c.v[0][0][0]; } + public static long[] get1() { return c.v[0][0]; } + public static long[][] get2() { return c.v[0]; } + public static long[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new long[1][1][1]; c.v[0][0][0] = 1; long val1 = get(); + c.v[0][0][0] = 2; long val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new long[1][1][1]; c.v[0][0][0] = 3; long val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new long[1][1]; c.v[0][0][0] = 4; long val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new long[1]; c.v[0][0][0] = 5; long val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + } + + { + c.v = new long[1][1][1]; long[] val1 = get1(); + c.v[0][0] = new long[1]; long[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1][1]; long[][] val1 = get2(); + c.v[0] = new long[1][1]; long[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1][1]; long[][][] val1 = get3(); + c.v = new long[1][1][1]; long[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class LongArrayDim4 { + public @Stable long[][][][] v; + + public static final LongArrayDim4 c = new LongArrayDim4(); + public static long get() { return c.v[0][0][0][0]; } + public static long[] get1() { return c.v[0][0][0]; } + public static long[][] get2() { return c.v[0][0]; } + public static long[][][] get3() { return c.v[0]; } + public static long[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new long[1][1][1][1]; c.v[0][0][0][0] = 1; long val1 = get(); + c.v[0][0][0][0] = 2; long val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new long[1][1][1][1]; c.v[0][0][0][0] = 3; long val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new long[1][1][1]; c.v[0][0][0][0] = 4; long val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new long[1][1]; c.v[0][0][0][0] = 5; long val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + + c.v[0][0][0] = new long[1]; c.v[0][0][0][0] = 6; long val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 6)); + } + + { + c.v = new long[1][1][1][1]; long[] val1 = get1(); + c.v[0][0][0] = new long[1]; long[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1][1][1]; long[][] val1 = get2(); + c.v[0][0] = new long[1][1]; long[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1][1][1]; long[][][] val1 = get3(); + c.v[0] = new long[1][1][1]; long[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1][1][1]; long[][][][] val1 = get4(); + c.v = new long[1][1][1][1]; long[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static long get() { return ((long[])c.v)[0]; } + public static long[] get1() { return (long[])c.v; } + + public static void test() throws Exception { + { + c.v = new long[1]; ((long[])c.v)[0] = 1; long val1 = get(); + ((long[])c.v)[0] = 2; long val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new long[1]; long[] val1 = get1(); + c.v = new long[1]; long[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static long get() { return ((long[][])c.v)[0][0]; } + public static long[] get1() { return (long[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new long[1][1]; ((long[][])c.v)[0][0] = 1; long val1 = get(); + ((long[][])c.v)[0][0] = 2; long val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new long[1][1]; c.v[0] = new long[0]; long[] val1 = get1(); + c.v[0] = new long[0]; long[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[0][0]; Object[] val1 = get2(); + c.v = new long[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static long get() { return ((long[][][])c.v)[0][0][0]; } + public static long[] get1() { return (long[])(c.v[0][0]); } + public static long[][] get2() { return (long[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new long[1][1][1]; ((long[][][])c.v)[0][0][0] = 1L; long val1 = get(); + ((long[][][])c.v)[0][0][0] = 2L; long val2 = get(); + + assertEquals(val1, 1L); + assertEquals(val2, 2L); + } + + { + c.v = new long[1][1][1]; c.v[0][0] = new long[0]; long[] val1 = get1(); + c.v[0][0] = new long[0]; long[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[1][1][1]; c.v[0] = new long[0][0]; long[][] val1 = get2(); + c.v[0] = new long[0][0]; long[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new long[0][0][0]; Object[][] val1 = get3(); + c.v = new long[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable long a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static long get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 1; A val1 = get(); + c.v.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.a = 1; long val1 = get1(); + c.v.a = 2; long val2 = get1(); + c.v = new A(); c.v.a = 3; long val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable long a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static long get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 1; c.v.next.a = 1; A val1 = get(); + c.v.a = 2; c.v.next.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 1; long val1 = get1(); + c.v.a = 2; long val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 3; long val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable long a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static long get() { return c.v.left.left.left.a; } + public static long get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 1; long val1 = get(); long val2 = get1(); + c.v.a = 2; long val3 = get(); long val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isStableEnabled ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable long a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static long get() { return c.v[0].left[1].left[0].left[1].a; } + public static long get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 1; long val1 = get(); long val2 = get1(); + elem.a = 2; long val3 = get(); long val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isServerWithStable ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(long i, long j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableObject.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,662 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableObject + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableObject StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableObject + * java/lang/invoke/TestStableObject$ObjectStable + * java/lang/invoke/TestStableObject$StaticObjectStable + * java/lang/invoke/TestStableObject$VolatileObjectStable + * java/lang/invoke/TestStableObject$ObjectArrayDim1 + * java/lang/invoke/TestStableObject$ObjectArrayDim2 + * java/lang/invoke/TestStableObject$ObjectArrayDim3 + * java/lang/invoke/TestStableObject$ObjectArrayDim4 + * java/lang/invoke/TestStableObject$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableObject$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableObject$NestedStableField + * java/lang/invoke/TestStableObject$NestedStableField$A + * java/lang/invoke/TestStableObject$NestedStableField1 + * java/lang/invoke/TestStableObject$NestedStableField1$A + * java/lang/invoke/TestStableObject$NestedStableField2 + * java/lang/invoke/TestStableObject$NestedStableField2$A + * java/lang/invoke/TestStableObject$NestedStableField3 + * java/lang/invoke/TestStableObject$NestedStableField3$A + * java/lang/invoke/TestStableObject$Values + * java/lang/invoke/TestStableObject$DefaultValue + * java/lang/invoke/TestStableObject$DefaultStaticValue + * java/lang/invoke/TestStableObject$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableObject + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableObject + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableObject + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableObject + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableObject + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableObject + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableObject { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(ObjectStable.class); + run(DefaultStaticValue.class); + run(StaticObjectStable.class); + run(VolatileObjectStable.class); + + // @Stable arrays: Dim 1-4 + run(ObjectArrayDim1.class); + run(ObjectArrayDim2.class); + run(ObjectArrayDim3.class); + run(ObjectArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + enum Values {A, B, C, D, E, F} + + static class DefaultValue { + public @Stable Object v; + + public static final DefaultValue c = new DefaultValue(); + public static Object get() { return c.v; } + public static void test() throws Exception { + Object val1 = get(); + c.v = Values.A; Object val2 = get(); + assertEquals(val1, null); + assertEquals(val2, Values.A); + } + } + + /* ==================================================== */ + + static class ObjectStable { + public @Stable Values v; + + public static final ObjectStable c = new ObjectStable (); + public static Values get() { return c.v; } + public static void test() throws Exception { + c.v = Values.A; Values val1 = get(); + c.v = Values.B; Values val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isStableEnabled ? Values.A : Values.B)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable Object v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static Object get() { return c.v; } + public static void test() throws Exception { + Object val1 = get(); + c.v = Values.A; Object val2 = get(); + assertEquals(val1, null); + assertEquals(val2, Values.A); + } + } + + /* ==================================================== */ + + static class StaticObjectStable { + public static @Stable Values v; + + public static final ObjectStable c = new ObjectStable (); + public static Values get() { return c.v; } + public static void test() throws Exception { + c.v = Values.A; Values val1 = get(); + c.v = Values.B; Values val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isStableEnabled ? Values.A : Values.B)); + } + } + + /* ==================================================== */ + + static class VolatileObjectStable { + public @Stable volatile Values v; + + public static final VolatileObjectStable c = new VolatileObjectStable (); + public static Values get() { return c.v; } + public static void test() throws Exception { + c.v = Values.A; Values val1 = get(); + c.v = Values.B; Values val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isStableEnabled ? Values.A : Values.B)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class ObjectArrayDim1 { + public @Stable Object[] v; + + public static final ObjectArrayDim1 c = new ObjectArrayDim1(); + public static Object get() { return c.v[0]; } + public static Object get1() { return c.v[10]; } + public static Object[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new Object[1]; c.v[0] = Values.A; Object val1 = get(); + c.v[0] = Values.B; Object val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isServerWithStable ? Values.A : Values.B)); + + c.v = new Object[1]; c.v[0] = Values.C; Object val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.C)); + } + + { + c.v = new Object[20]; c.v[10] = Values.A; Object val1 = get1(); + c.v[10] = Values.B; Object val2 = get1(); + assertEquals(val1, Values.A); + assertEquals(val2, (isServerWithStable ? Values.A : Values.B)); + + c.v = new Object[20]; c.v[10] = Values.C; Object val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.C)); + } + + { + c.v = new Object[1]; Object[] val1 = get2(); + c.v = new Object[1]; Object[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayDim2 c = new ObjectArrayDim2(); + public static Object get() { return c.v[0][0]; } + public static Object[] get1() { return c.v[0]; } + public static Object[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new Object[1][1]; c.v[0][0] = Values.A; Object val1 = get(); + c.v[0][0] = Values.B; Object val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isServerWithStable ? Values.A : Values.B)); + + c.v = new Object[1][1]; c.v[0][0] = Values.C; Object val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.C)); + + c.v[0] = new Object[1]; c.v[0][0] = Values.D; Object val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.D)); + } + + { + c.v = new Object[1][1]; Object[] val1 = get1(); + c.v[0] = new Object[1]; Object[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1]; Object[][] val1 = get2(); + c.v = new Object[1][1]; Object[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayDim3 { + public @Stable Object[][][] v; + + public static final ObjectArrayDim3 c = new ObjectArrayDim3(); + public static Object get() { return c.v[0][0][0]; } + public static Object[] get1() { return c.v[0][0]; } + public static Object[][] get2() { return c.v[0]; } + public static Object[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new Object[1][1][1]; c.v[0][0][0] = Values.A; Object val1 = get(); + c.v[0][0][0] = Values.B; Object val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isServerWithStable ? Values.A : Values.B)); + + c.v = new Object[1][1][1]; c.v[0][0][0] = Values.C; Object val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.C)); + + c.v[0] = new Object[1][1]; c.v[0][0][0] = Values.D; Object val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.D)); + + c.v[0][0] = new Object[1]; c.v[0][0][0] = Values.E; Object val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.E)); + } + + { + c.v = new Object[1][1][1]; Object[] val1 = get1(); + c.v[0][0] = new Object[1]; Object[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1][1]; Object[][] val1 = get2(); + c.v[0] = new Object[1][1]; Object[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1][1]; Object[][][] val1 = get3(); + c.v = new Object[1][1][1]; Object[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayDim4 { + public @Stable Object[][][][] v; + + public static final ObjectArrayDim4 c = new ObjectArrayDim4(); + public static Object get() { return c.v[0][0][0][0]; } + public static Object[] get1() { return c.v[0][0][0]; } + public static Object[][] get2() { return c.v[0][0]; } + public static Object[][][] get3() { return c.v[0]; } + public static Object[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new Object[1][1][1][1]; c.v[0][0][0][0] = Values.A; Object val1 = get(); + c.v[0][0][0][0] = Values.B; Object val2 = get(); + assertEquals(val1, Values.A); + assertEquals(val2, (isServerWithStable ? Values.A : Values.B)); + + c.v = new Object[1][1][1][1]; c.v[0][0][0][0] = Values.C; Object val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.C)); + + c.v[0] = new Object[1][1][1]; c.v[0][0][0][0] = Values.D; Object val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.D)); + + c.v[0][0] = new Object[1][1]; c.v[0][0][0][0] = Values.E; Object val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.E)); + + c.v[0][0][0] = new Object[1]; c.v[0][0][0][0] = Values.F; Object val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? Values.A : Values.B) + : Values.F)); + } + + { + c.v = new Object[1][1][1][1]; Object[] val1 = get1(); + c.v[0][0][0] = new Object[1]; Object[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1][1][1]; Object[][] val1 = get2(); + c.v[0][0] = new Object[1][1]; Object[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1][1][1]; Object[][][] val1 = get3(); + c.v[0] = new Object[1][1][1]; Object[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1][1][1]; Object[][][][] val1 = get4(); + c.v = new Object[1][1][1][1]; Object[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static Object get() { return ((Object[])c.v)[0]; } + public static Object[] get1() { return (Object[])c.v; } + + public static void test() throws Exception { + { + c.v = new Object[1]; ((Object[])c.v)[0] = Values.A; Object val1 = get(); + ((Object[])c.v)[0] = Values.B; Object val2 = get(); + + assertEquals(val1, Values.A); + assertEquals(val2, Values.B); + } + + { + c.v = new Object[1]; Object[] val1 = get1(); + c.v = new Object[1]; Object[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static Object get() { return ((Object[][])c.v)[0][0]; } + public static Object[] get1() { return (Object[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new Object[1][1]; ((Object[][])c.v)[0][0] = Values.A; Object val1 = get(); + ((Object[][])c.v)[0][0] = Values.B; Object val2 = get(); + + assertEquals(val1, Values.A); + assertEquals(val2, Values.B); + } + + { + c.v = new Object[1][1]; c.v[0] = new Object[0]; Object[] val1 = get1(); + c.v[0] = new Object[0]; Object[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[0][0]; Object[] val1 = get2(); + c.v = new Object[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static Object get() { return ((Object[][][])c.v)[0][0][0]; } + public static Object[] get1() { return (Object[])(c.v[0][0]); } + public static Object[][] get2() { return (Object[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new Object[1][1][1]; ((Object[][][])c.v)[0][0][0] = Values.A; Object val1 = get(); + ((Object[][][])c.v)[0][0][0] = Values.B; Object val2 = get(); + + assertEquals(val1, Values.A); + assertEquals(val2, Values.B); + } + + { + c.v = new Object[1][1][1]; c.v[0][0] = new Object[0]; Object[] val1 = get1(); + c.v[0][0] = new Object[0]; Object[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[1][1][1]; c.v[0] = new Object[0][0]; Object[][] val1 = get2(); + c.v[0] = new Object[0][0]; Object[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new Object[0][0][0]; Object[][] val1 = get3(); + c.v = new Object[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable Object a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static Object get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = Values.A; A val1 = get(); + c.v.a = Values.B; A val2 = get(); + + assertEquals(val1.a, Values.B); + assertEquals(val2.a, Values.B); + } + + { + c.v = new A(); c.v.a = Values.A; Object val1 = get1(); + c.v.a = Values.B; Object val2 = get1(); + c.v = new A(); c.v.a = Values.C; Object val3 = get1(); + + assertEquals(val1, Values.A); + assertEquals(val2, (isStableEnabled ? Values.A : Values.B)); + assertEquals(val3, (isStableEnabled ? Values.A : Values.C)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable Object a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static Object get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = Values.A; c.v.next.a = Values.A; A val1 = get(); + c.v.a = Values.B; c.v.next.a = Values.B; A val2 = get(); + + assertEquals(val1.a, Values.B); + assertEquals(val2.a, Values.B); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = Values.A; Object val1 = get1(); + c.v.a = Values.B; Object val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = Values.C; Object val3 = get1(); + + assertEquals(val1, Values.A); + assertEquals(val2, (isStableEnabled ? Values.A : Values.B)); + assertEquals(val3, (isStableEnabled ? Values.A : Values.C)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable Object a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static Object get() { return c.v.left.left.left.a; } + public static Object get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = Values.A; Object val1 = get(); Object val2 = get1(); + c.v.a = Values.B; Object val3 = get(); Object val4 = get1(); + + assertEquals(val1, Values.A); + assertEquals(val3, (isStableEnabled ? Values.A : Values.B)); + + assertEquals(val2, Values.A); + assertEquals(val4, Values.B); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable Object a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static Object get() { return c.v[0].left[1].left[0].left[1].a; } + public static Object get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = Values.A; Object val1 = get(); Object val2 = get1(); + elem.a = Values.B; Object val3 = get(); Object val4 = get1(); + + assertEquals(val1, Values.A); + assertEquals(val3, (isServerWithStable ? Values.A : Values.B)); + + assertEquals(val2, Values.A); + assertEquals(val4, Values.B); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(Object i, Object j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/stable/TestStableShort.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/stable/TestStableShort.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,659 @@ +/* + * Copyright (c) 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 + * 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. + */ + +/* + * @test TestStableShort + * @summary tests on stable fields and arrays + * @library /testlibrary /testlibrary/whitebox + * @build TestStableShort StableConfiguration sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller + * java/lang/invoke/StableConfiguration + * java/lang/invoke/TestStableShort + * java/lang/invoke/TestStableShort$ShortStable + * java/lang/invoke/TestStableShort$StaticShortStable + * java/lang/invoke/TestStableShort$VolatileShortStable + * java/lang/invoke/TestStableShort$ShortArrayDim1 + * java/lang/invoke/TestStableShort$ShortArrayDim2 + * java/lang/invoke/TestStableShort$ShortArrayDim3 + * java/lang/invoke/TestStableShort$ShortArrayDim4 + * java/lang/invoke/TestStableShort$ObjectArrayLowerDim0 + * java/lang/invoke/TestStableShort$ObjectArrayLowerDim1 + * java/lang/invoke/TestStableShort$NestedStableField + * java/lang/invoke/TestStableShort$NestedStableField$A + * java/lang/invoke/TestStableShort$NestedStableField1 + * java/lang/invoke/TestStableShort$NestedStableField1$A + * java/lang/invoke/TestStableShort$NestedStableField2 + * java/lang/invoke/TestStableShort$NestedStableField2$A + * java/lang/invoke/TestStableShort$NestedStableField3 + * java/lang/invoke/TestStableShort$NestedStableField3$A + * java/lang/invoke/TestStableShort$DefaultValue + * java/lang/invoke/TestStableShort$DefaultStaticValue + * java/lang/invoke/TestStableShort$ObjectArrayLowerDim2 + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableShort + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableShort + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableShort + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -server -XX:+TieredCompilation -XX:TieredStopAtLevel=1 + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableShort + * + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableShort + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xcomp + * -client -XX:-TieredCompilation + * -XX:-FoldStableValues + * -XX:CompileOnly=::get,::get1,::get2,::get3,::get4 + * java.lang.invoke.TestStableShort + */ +package java.lang.invoke; + +import java.lang.reflect.InvocationTargetException; + +public class TestStableShort { + static final boolean isStableEnabled = StableConfiguration.isStableEnabled; + static final boolean isServerWithStable = StableConfiguration.isServerWithStable; + + public static void main(String[] args) throws Exception { + run(DefaultValue.class); + run(ShortStable.class); + run(DefaultStaticValue.class); + run(StaticShortStable.class); + run(VolatileShortStable.class); + + // @Stable arrays: Dim 1-4 + run(ShortArrayDim1.class); + run(ShortArrayDim2.class); + run(ShortArrayDim3.class); + run(ShortArrayDim4.class); + + // @Stable Object field: dynamic arrays + run(ObjectArrayLowerDim0.class); + run(ObjectArrayLowerDim1.class); + run(ObjectArrayLowerDim2.class); + + // Nested @Stable fields + run(NestedStableField.class); + run(NestedStableField1.class); + run(NestedStableField2.class); + run(NestedStableField3.class); + + if (failed) { + throw new Error("TEST FAILED"); + } + } + + /* ==================================================== */ + + static class DefaultValue { + public @Stable short v; + + public static final DefaultValue c = new DefaultValue(); + public static short get() { return c.v; } + public static void test() throws Exception { + short val1 = get(); + c.v = 1; short val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1); + } + } + + /* ==================================================== */ + + static class ShortStable { + public @Stable short v; + + public static final ShortStable c = new ShortStable(); + public static short get() { return c.v; } + public static void test() throws Exception { + c.v = 1; short val1 = get(); + c.v = 32767; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 32767)); + } + } + + /* ==================================================== */ + + static class DefaultStaticValue { + public static @Stable short v; + + public static final DefaultStaticValue c = new DefaultStaticValue(); + public static short get() { return c.v; } + public static void test() throws Exception { + short val1 = get(); + c.v = 1; short val2 = get(); + assertEquals(val1, 0); + assertEquals(val2, 1); + } + } + + /* ==================================================== */ + + static class StaticShortStable { + public static @Stable short v; + + public static final StaticShortStable c = new StaticShortStable(); + public static short get() { return c.v; } + public static void test() throws Exception { + c.v = 1; short val1 = get(); + c.v = 32767; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 32767)); + } + } + + /* ==================================================== */ + + static class VolatileShortStable { + public @Stable volatile short v; + + public static final VolatileShortStable c = new VolatileShortStable(); + public static short get() { return c.v; } + public static void test() throws Exception { + c.v = 1; short val1 = get(); + c.v = 32767; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 32767)); + } + } + + /* ==================================================== */ + // @Stable array == field && all components are stable + + static class ShortArrayDim1 { + public @Stable short[] v; + + public static final ShortArrayDim1 c = new ShortArrayDim1(); + public static short get() { return c.v[0]; } + public static short get1() { return c.v[10]; } + public static short[] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new short[1]; c.v[0] = 1; short val1 = get(); + c.v[0] = 2; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new short[1]; c.v[0] = 3; short val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new short[20]; c.v[10] = 1; short val1 = get1(); + c.v[10] = 2; short val2 = get1(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new short[20]; c.v[10] = 3; short val3 = get1(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + } + + { + c.v = new short[1]; short[] val1 = get2(); + c.v = new short[1]; short[] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ShortArrayDim2 { + public @Stable short[][] v; + + public static final ShortArrayDim2 c = new ShortArrayDim2(); + public static short get() { return c.v[0][0]; } + public static short[] get1() { return c.v[0]; } + public static short[][] get2() { return c.v; } + public static void test() throws Exception { + { + c.v = new short[1][1]; c.v[0][0] = 1; short val1 = get(); + c.v[0][0] = 2; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new short[1][1]; c.v[0][0] = 3; short val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new short[1]; c.v[0][0] = 4; short val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + } + + { + c.v = new short[1][1]; short[] val1 = get1(); + c.v[0] = new short[1]; short[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1]; short[][] val1 = get2(); + c.v = new short[1][1]; short[][] val2 = get2(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ShortArrayDim3 { + public @Stable short[][][] v; + + public static final ShortArrayDim3 c = new ShortArrayDim3(); + public static short get() { return c.v[0][0][0]; } + public static short[] get1() { return c.v[0][0]; } + public static short[][] get2() { return c.v[0]; } + public static short[][][] get3() { return c.v; } + public static void test() throws Exception { + { + c.v = new short[1][1][1]; c.v[0][0][0] = 1; short val1 = get(); + c.v[0][0][0] = 2; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new short[1][1][1]; c.v[0][0][0] = 3; short val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new short[1][1]; c.v[0][0][0] = 4; short val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new short[1]; c.v[0][0][0] = 5; short val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + } + + { + c.v = new short[1][1][1]; short[] val1 = get1(); + c.v[0][0] = new short[1]; short[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1][1]; short[][] val1 = get2(); + c.v[0] = new short[1][1]; short[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1][1]; short[][][] val1 = get3(); + c.v = new short[1][1][1]; short[][][] val2 = get3(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ShortArrayDim4 { + public @Stable short[][][][] v; + + public static final ShortArrayDim4 c = new ShortArrayDim4(); + public static short get() { return c.v[0][0][0][0]; } + public static short[] get1() { return c.v[0][0][0]; } + public static short[][] get2() { return c.v[0][0]; } + public static short[][][] get3() { return c.v[0]; } + public static short[][][][] get4() { return c.v; } + public static void test() throws Exception { + { + c.v = new short[1][1][1][1]; c.v[0][0][0][0] = 1; short val1 = get(); + c.v[0][0][0][0] = 2; short val2 = get(); + assertEquals(val1, 1); + assertEquals(val2, (isServerWithStable ? 1 : 2)); + + c.v = new short[1][1][1][1]; c.v[0][0][0][0] = 3; short val3 = get(); + assertEquals(val3, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 3)); + + c.v[0] = new short[1][1][1]; c.v[0][0][0][0] = 4; short val4 = get(); + assertEquals(val4, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 4)); + + c.v[0][0] = new short[1][1]; c.v[0][0][0][0] = 5; short val5 = get(); + assertEquals(val5, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 5)); + + c.v[0][0][0] = new short[1]; c.v[0][0][0][0] = 6; short val6 = get(); + assertEquals(val6, (isStableEnabled ? (isServerWithStable ? 1 : 2) + : 6)); + } + + { + c.v = new short[1][1][1][1]; short[] val1 = get1(); + c.v[0][0][0] = new short[1]; short[] val2 = get1(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1][1][1]; short[][] val1 = get2(); + c.v[0][0] = new short[1][1]; short[][] val2 = get2(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1][1][1]; short[][][] val1 = get3(); + c.v[0] = new short[1][1][1]; short[][][] val2 = get3(); + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1][1][1]; short[][][][] val1 = get4(); + c.v = new short[1][1][1][1]; short[][][][] val2 = get4(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + // Dynamic Dim is higher than static + static class ObjectArrayLowerDim0 { + public @Stable Object v; + + public static final ObjectArrayLowerDim0 c = new ObjectArrayLowerDim0(); + public static short get() { return ((short[])c.v)[0]; } + public static short[] get1() { return (short[])c.v; } + + public static void test() throws Exception { + { + c.v = new short[1]; ((short[])c.v)[0] = 1; short val1 = get(); + ((short[])c.v)[0] = 2; short val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new short[1]; short[] val1 = get1(); + c.v = new short[1]; short[] val2 = get1(); + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim1 { + public @Stable Object[] v; + + public static final ObjectArrayLowerDim1 c = new ObjectArrayLowerDim1(); + public static short get() { return ((short[][])c.v)[0][0]; } + public static short[] get1() { return (short[])(c.v[0]); } + public static Object[] get2() { return c.v; } + + public static void test() throws Exception { + { + c.v = new short[1][1]; ((short[][])c.v)[0][0] = 1; short val1 = get(); + ((short[][])c.v)[0][0] = 2; short val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new short[1][1]; c.v[0] = new short[0]; short[] val1 = get1(); + c.v[0] = new short[0]; short[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[0][0]; Object[] val1 = get2(); + c.v = new short[0][0]; Object[] val2 = get2(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class ObjectArrayLowerDim2 { + public @Stable Object[][] v; + + public static final ObjectArrayLowerDim2 c = new ObjectArrayLowerDim2(); + public static short get() { return ((short[][][])c.v)[0][0][0]; } + public static short[] get1() { return (short[])(c.v[0][0]); } + public static short[][] get2() { return (short[][])(c.v[0]); } + public static Object[][] get3() { return c.v; } + + public static void test() throws Exception { + { + c.v = new short[1][1][1]; ((short[][][])c.v)[0][0][0] = 1; short val1 = get(); + ((short[][][])c.v)[0][0][0] = 2; short val2 = get(); + + assertEquals(val1, 1); + assertEquals(val2, 2); + } + + { + c.v = new short[1][1][1]; c.v[0][0] = new short[0]; short[] val1 = get1(); + c.v[0][0] = new short[0]; short[] val2 = get1(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[1][1][1]; c.v[0] = new short[0][0]; short[][] val1 = get2(); + c.v[0] = new short[0][0]; short[][] val2 = get2(); + + assertTrue((isServerWithStable ? (val1 == val2) : (val1 != val2))); + } + + { + c.v = new short[0][0][0]; Object[][] val1 = get3(); + c.v = new short[0][0][0]; Object[][] val2 = get3(); + + assertTrue((isStableEnabled ? (val1 == val2) : (val1 != val2))); + } + } + } + + /* ==================================================== */ + + static class NestedStableField { + static class A { + public @Stable short a; + + } + public @Stable A v; + + public static final NestedStableField c = new NestedStableField(); + public static A get() { return c.v; } + public static short get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.a = 1; A val1 = get(); + c.v.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.a = 1; short val1 = get1(); + c.v.a = 2; short val2 = get1(); + c.v = new A(); c.v.a = 3; short val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + + /* ==================================================== */ + + static class NestedStableField1 { + static class A { + public @Stable short a; + public @Stable A next; + } + public @Stable A v; + + public static final NestedStableField1 c = new NestedStableField1(); + public static A get() { return c.v.next.next.next.next.next.next.next; } + public static short get1() { return get().a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.next = new A(); c.v.next.next = c.v; + c.v.a = 1; c.v.next.a = 1; A val1 = get(); + c.v.a = 2; c.v.next.a = 2; A val2 = get(); + + assertEquals(val1.a, 2); + assertEquals(val2.a, 2); + } + + { + c.v = new A(); c.v.next = c.v; + c.v.a = 1; short val1 = get1(); + c.v.a = 2; short val2 = get1(); + c.v = new A(); c.v.next = c.v; + c.v.a = 3; short val3 = get1(); + + assertEquals(val1, 1); + assertEquals(val2, (isStableEnabled ? 1 : 2)); + assertEquals(val3, (isStableEnabled ? 1 : 3)); + } + } + } + /* ==================================================== */ + + static class NestedStableField2 { + static class A { + public @Stable short a; + public @Stable A left; + public A right; + } + + public @Stable A v; + + public static final NestedStableField2 c = new NestedStableField2(); + public static short get() { return c.v.left.left.left.a; } + public static short get1() { return c.v.left.left.right.left.a; } + + public static void test() throws Exception { + { + c.v = new A(); c.v.left = c.v.right = c.v; + c.v.a = 1; short val1 = get(); short val2 = get1(); + c.v.a = 2; short val3 = get(); short val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isStableEnabled ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + + static class NestedStableField3 { + static class A { + public @Stable short a; + public @Stable A[] left; + public A[] right; + } + + public @Stable A[] v; + + public static final NestedStableField3 c = new NestedStableField3(); + public static short get() { return c.v[0].left[1].left[0].left[1].a; } + public static short get1() { return c.v[1].left[0].left[1].right[0].left[1].a; } + + public static void test() throws Exception { + { + A elem = new A(); + c.v = new A[] { elem, elem }; c.v[0].left = c.v[0].right = c.v; + elem.a = 1; short val1 = get(); short val2 = get1(); + elem.a = 2; short val3 = get(); short val4 = get1(); + + assertEquals(val1, 1); + assertEquals(val3, (isServerWithStable ? 1 : 2)); + + assertEquals(val2, 1); + assertEquals(val4, 2); + } + } + } + + /* ==================================================== */ + // Auxiliary methods + static void assertEquals(int i, int j) { if (i != j) throw new AssertionError(i + " != " + j); } + static void assertTrue(boolean b) { if (!b) throw new AssertionError(); } + + static boolean failed = false; + + public static void run(Class test) { + Throwable ex = null; + System.out.print(test.getName()+": "); + try { + test.getMethod("test").invoke(null); + } catch (InvocationTargetException e) { + ex = e.getCause(); + } catch (Throwable e) { + ex = e; + } finally { + if (ex == null) { + System.out.println("PASSED"); + } else { + failed = true; + System.out.println("FAILED"); + ex.printStackTrace(System.out); + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/startup/SmallCodeCacheStartup.java --- a/test/compiler/startup/SmallCodeCacheStartup.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/startup/SmallCodeCacheStartup.java Wed Oct 15 16:02:50 2014 +0200 @@ -37,7 +37,6 @@ pb = ProcessTools.createJavaProcessBuilder("-XX:ReservedCodeCacheSize=3m", "-XX:CICompilerCount=64", "-version"); out = new OutputAnalyzer(pb.start()); - out.shouldContain("no space to run compiler"); out.shouldHaveExitValue(0); } } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/AbortProvoker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/AbortProvoker.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import java.util.Objects; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Utils; +import sun.misc.Unsafe; + +/** + * Base class for different transactional execution abortion + * provokers aimed to force abort due to specified reason. + */ +public abstract class AbortProvoker implements CompilableTest { + public static final long DEFAULT_ITERATIONS = 10000L; + /** + * Inflates monitor associated with object {@code monitor}. + * Inflation is forced by entering the same monitor from + * two different threads. + * + * @param monitor monitor to be inflated. + * @return inflated monitor. + * @throws Exception if something went wrong. + */ + public static Object inflateMonitor(Object monitor) throws Exception { + Unsafe unsafe = Utils.getUnsafe(); + CyclicBarrier barrier = new CyclicBarrier(2); + + Runnable inflatingRunnable = () -> { + unsafe.monitorEnter(monitor); + try { + barrier.await(); + barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException( + "Synchronization issue occurred.", e); + } finally { + unsafe.monitorExit(monitor); + } + }; + + Thread t = new Thread(inflatingRunnable); + t.start(); + // Wait until thread t enters the monitor. + barrier.await(); + // At this point monitor will be owned by thread t, + // so our attempt to enter the same monitor will force + // monitor inflation. + Asserts.assertFalse(unsafe.tryMonitorEnter(monitor), + "Not supposed to enter the monitor first"); + barrier.await(); + t.join(); + return monitor; + } + + + /** + * Get instance of specified AbortProvoker, inflate associated monitor + * if needed and then invoke forceAbort method in a loop. + * + * Usage: + * AbortProvoker <AbortType name> [<inflate monitor> + * [<iterations> [ <delay>]]] + * + * Default parameters are: + *
    + *
  • inflate monitor = true
  • + *
  • iterations = {@code AbortProvoker.DEFAULT_ITERATIONS}
  • + *
  • delay = 0
  • + *
+ */ + public static void main(String args[]) throws Throwable { + Asserts.assertGT(args.length, 0, "At least one argument is required."); + + AbortType abortType = AbortType.lookup(Integer.valueOf(args[0])); + boolean monitorShouldBeInflated = true; + long iterations = AbortProvoker.DEFAULT_ITERATIONS; + + if (args.length > 1) { + monitorShouldBeInflated = Boolean.valueOf(args[1]); + + if (args.length > 2) { + iterations = Long.valueOf(args[2]); + + if (args.length > 3) { + Thread.sleep(Integer.valueOf(args[3])); + } + } + } + + AbortProvoker provoker = abortType.provoker(); + + if (monitorShouldBeInflated) { + provoker.inflateMonitor(); + } + + for (long i = 0; i < iterations; i++) { + provoker.forceAbort(); + } + } + + protected final Object monitor; + + protected AbortProvoker() { + this(new Object()); + } + + protected AbortProvoker(Object monitor) { + this.monitor = Objects.requireNonNull(monitor); + } + + /** + * Inflates monitor used by this AbortProvoker instance. + * @throws Exception + */ + public void inflateMonitor() throws Exception { + AbortProvoker.inflateMonitor(monitor); + } + + /** + * Forces transactional execution abortion. + */ + public abstract void forceAbort(); + + /** + * Returns names of all methods that have to be compiled + * in order to successfully force transactional execution + * abortion. + * + * @return array with methods' names that have to be compiled. + */ + @Override + public String[] getMethodsToCompileNames() { + return new String[] { getMethodWithLockName() }; + } + + /** + * Returns name of the method that will contain monitor whose locking + * will be elided using transactional execution. + * + * @return name of the method that will contain elided lock. + */ + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::forceAbort"; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/AbortType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/AbortType.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import com.oracle.java.testlibrary.Asserts; + +import java.util.HashMap; +import java.util.Map; + +/** + * Type of transactional execution abort. + * For more details on different abort types please see + * shared/vm/runtime/rtmLocking.hpp + */ +public enum AbortType { + XABORT(0), + RETRIABLE(1), + MEM_CONFLICT(2), + BUF_OVERFLOW(3), + DEBUG_BREAKPOINT(4), + NESTED_ABORT(5); + + private final int type; + private static final Map LOOKUP_MAP = new HashMap<>(); + + static { + for (AbortType abortType : AbortType.values()) { + Asserts.assertFalse(LOOKUP_MAP.containsKey(abortType.type), + "Abort type values should be unique."); + LOOKUP_MAP.put(abortType.type, abortType); + } + } + + private AbortType(int type) { + this.type = type; + } + + /** + * Returns AbortProvoker for aborts represented by this abort type. + * + * @return an AbortProvoker instance + */ + public AbortProvoker provoker() { + return AbortType.createNewProvoker(this); + } + + public static AbortType lookup(int type) { + Asserts.assertLT(type, AbortType.values().length, + "Unknown abort type."); + return LOOKUP_MAP.get(type); + } + + /** + * Returns transaction execution abort provoker for specified abortion type. + * + * @param type a type of abort which will be forced by returned + * AbortProvoker instance. + * @return AbortProvoker instance that will force abort of specified type + * @throws RuntimeException if there is no provoker for specified type + */ + private static AbortProvoker createNewProvoker(AbortType type) { + switch (type) { + case XABORT: + return new XAbortProvoker(); + case MEM_CONFLICT: + return new MemoryConflictProvoker(); + case BUF_OVERFLOW: + return new BufferOverflowProvoker(); + case NESTED_ABORT: + return new NestedAbortProvoker(); + default: + throw new RuntimeException("No provoker exists for type " + + type.name()); + } + } + + @Override + public String toString() { + return Integer.toString(type); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/BufferOverflowProvoker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/BufferOverflowProvoker.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +/** + * In order to provoke transactional execution abort due to + * internal's buffer overflow BufferOverflowProvoker modifies + * 1MB of BYTES during single transaction. + */ +class BufferOverflowProvoker extends AbortProvoker { + /** + * To force buffer overflow abort we modify memory region with + * size more then L1d cache size. + */ + private static final int MORE_THAN_L1D_SIZE = 1024 * 1024; + private static final byte[] DATA = new byte[MORE_THAN_L1D_SIZE]; + + @Override + public void forceAbort() { + synchronized(monitor) { + for (int i = 0; i < BufferOverflowProvoker.DATA.length; i++) { + BufferOverflowProvoker.DATA[i]++; + } + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/BusyLock.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/BusyLock.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import com.oracle.java.testlibrary.Utils; +import sun.misc.Unsafe; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +/** + * Test case for busy lock scenario. + * One thread enters the monitor and sleep for a while. + * Another thread is blocked on the same monitor. + */ +public class BusyLock implements CompilableTest, Runnable { + private static final int DEFAULT_TIMEOUT = 1000; + private final CyclicBarrier barrier; + + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + private static final Unsafe UNSAFE = Utils.getUnsafe(); + protected final Object monitor; + protected final int timeout; + + public BusyLock() { + this(BusyLock.DEFAULT_TIMEOUT); + } + + public BusyLock(int timeout) { + this.timeout = timeout; + this.monitor = new Object(); + this.barrier = new CyclicBarrier(2); + } + + @Override + public void run() { + try { + // wait until forceAbort leave monitor + barrier.await(); + if (UNSAFE.tryMonitorEnter(monitor)) { + try { + barrier.await(); + Thread.sleep(timeout); + } finally { + UNSAFE.monitorExit(monitor); + } + } else { + throw new RuntimeException("Monitor should be entered by " + + "::run() first."); + } + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException("Synchronization error happened.", e); + } + } + + public void test() { + try { + barrier.await(); + // wait until monitor is locked by a ::run method + barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException("Synchronization error happened.", e); + } + synchronized(monitor) { + BusyLock.field++; + } + } + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::test"; + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { getMethodWithLockName() }; + } + + /** + * Usage: + * BusyLock [ <inflate monitor> [ <timeout> ] ] + * + * Default values are: + *
    + *
  • inflate monitor = {@code true}
  • + *
  • timeout = {@code BusyLock.DEFAULT_TIMEOUT}
  • + *
+ */ + public static void main(String args[]) throws Exception { + int timeoutValue = BusyLock.DEFAULT_TIMEOUT; + boolean inflateMonitor = true; + + if (args.length > 0 ) { + inflateMonitor = Boolean.valueOf(args[0]); + + if (args.length > 1) { + timeoutValue = Integer.valueOf(args[1]); + } + } + + BusyLock busyLock = new BusyLock(timeoutValue); + + if (inflateMonitor) { + AbortProvoker.inflateMonitor(busyLock.monitor); + } + + Thread t = new Thread(busyLock); + t.start(); + busyLock.test(); + t.join(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/CompilableTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/CompilableTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +/** + * Interface for test scenarios that contain methods + * that should be compiled. + */ +public interface CompilableTest { + /** + * @return array with methods' names that should be compiled. + */ + String[] getMethodsToCompileNames(); + + /** + * @return name of method with RTM-elided lock. + */ + String getMethodWithLockName(); +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/MemoryConflictProvoker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/MemoryConflictProvoker.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +/** + * To force transactional execution abort due to memory conflict + * one thread should access memory region from transactional region + * while another thread should modify the same memory region. + * Since this scenario is based on the race condition between threads + * you should not expect some particular amount of aborts. + */ +class MemoryConflictProvoker extends AbortProvoker { + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + private static final int INNER_ITERATIONS = 10000; + private final CyclicBarrier barrier; + /** + * This thread will access and modify memory region + * from outside of the transaction. + */ + private final Runnable conflictingThread; + + public MemoryConflictProvoker() { + this(new Object()); + } + + public MemoryConflictProvoker(Object monitor) { + super(monitor); + barrier = new CyclicBarrier(2); + conflictingThread = () -> { + try { + barrier.await(); + } catch (Exception e) { + throw new RuntimeException(e); + } + for (int i = 0; i < MemoryConflictProvoker.INNER_ITERATIONS; i++) { + MemoryConflictProvoker.field++; + } + }; + } + + /** + * Accesses and modifies memory region from within the transaction. + */ + public void transactionalRegion() { + try { + barrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + for (int i = 0; i < MemoryConflictProvoker.INNER_ITERATIONS; i++) { + synchronized(monitor) { + MemoryConflictProvoker.field--; + } + } + } + + @Override + public void forceAbort() { + try { + Thread t = new Thread(conflictingThread); + t.start(); + transactionalRegion(); + t.join(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String getMethodWithLockName() { + return this.getClass().getName() + "::transactionalRegion"; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/NestedAbortProvoker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/NestedAbortProvoker.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import java.util.Arrays; + +/** + * In order to force nested transaction abort NestedAbortProvoker + * invoke BufferOverflowProvoker from transactional region. + */ +class NestedAbortProvoker extends AbortProvoker { + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + private final AbortProvoker nestedAbortProvoker; + + public NestedAbortProvoker() { + this.nestedAbortProvoker = new XAbortProvoker(monitor); + } + + @Override + public void forceAbort() { + synchronized(monitor) { + NestedAbortProvoker.field++; + nestedAbortProvoker.forceAbort(); + NestedAbortProvoker.field--; + } + } + + @Override + public String[] getMethodsToCompileNames() { + String nestedProvokerMethods[] + = nestedAbortProvoker.getMethodsToCompileNames(); + String methods[] = Arrays.copyOf(nestedProvokerMethods, + nestedProvokerMethods.length + 1); + methods[methods.length - 1] = getMethodWithLockName(); + return methods; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/RTMLockingStatistics.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/RTMLockingStatistics.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import java.util.EnumMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * Wrapper for +UsePreciseRTMLockingStatistics output. + * + * Example of locking statistics: + * + * java/lang/ClassLoader.loadClass@7 + * # rtm locks total (estimated): 0 + * # rtm lock aborts : 13 + * # rtm lock aborts 0: 12 + * # rtm lock aborts 1: 0 + * # rtm lock aborts 2: 0 + * # rtm lock aborts 3: 0 + * # rtm lock aborts 4: 0 + * # rtm lock aborts 5: 0 + */ +public class RTMLockingStatistics { + /** + * Pattern for aborts per abort type entries. + */ + private static final Pattern ABORT_PATTERN; + + /** + * Pattern for whole statistics. + */ + private static final Pattern RTM_LOCKING_STATISTICS_PATTERN; + + static { + String abortRe + = "# rtm lock aborts\\s+(?[0-9]+):\\s(?[0-9]+)"; + + ABORT_PATTERN = Pattern.compile(abortRe); + RTM_LOCKING_STATISTICS_PATTERN = Pattern.compile( + "(?[^.\n]+)\\." + + "(?[^@\n]+)@(?[0-9]+)\n" + + "# rtm locks total \\(estimated\\):\\s*" + + "(?[0-9]+)\n" + + "# rtm lock aborts\\s+:\\s*(?[0-9]+)\n" + + "(?(" + abortRe + "\n)+)"); + } + + private final long totalLocks; + private final long totalAborts; + private final String className; + private final String methodName; + private final int bci; + private final Map aborts = new EnumMap<>(AbortType.class); + + /** + * Constructs RTMLockingStatistics from matcher captured statistics entry. + * @param matcher Matcher captured statistics entry. + */ + private RTMLockingStatistics(Matcher matcher) { + className = matcher.group("className"); + methodName = matcher.group("methodName"); + bci = Integer.valueOf(matcher.group("bci")); + totalLocks = Long.valueOf(matcher.group("totalLocks")); + totalAborts = Long.valueOf(matcher.group("totalAborts")); + + Matcher abortMatcher = ABORT_PATTERN.matcher(matcher. + group("abortStats")); + + while (abortMatcher.find()) { + int type = Integer.valueOf(abortMatcher.group("type")); + long count = Long.valueOf(abortMatcher.group("count")); + setAborts(AbortType.lookup(type), count); + } + } + + + /** + * Parses string and return all founded RTM locking statistics entries. + * + * @param str the string to be parsed. + * @return list with all founded RTM locking statistics entries or + * empty list if nothing was found. + */ + public static List fromString(String str) { + List statistics = new LinkedList<>(); + Matcher matcher = RTM_LOCKING_STATISTICS_PATTERN.matcher(str); + + while (matcher.find()) { + RTMLockingStatistics lock = new RTMLockingStatistics(matcher); + statistics.add(lock); + } + + return statistics; + } + + /** + * Parses string and return all founded RTM locking statistics entries + * for locks in method {@code methodName}. + * + * @param methodName a name of the method for locks from which statistics + * should be gathered. + * @param str the string to be parsed. + * @return list with all founded RTM locking statistics entries or + * empty list if nothing was found. + */ + public static List fromString(String methodName, + String str) { + String formattedMethodName = formatMethodName(methodName); + + List statisticsForMethod = new LinkedList<>(); + for (RTMLockingStatistics statistics : fromString(str)) { + if (statistics.getLockName().startsWith(formattedMethodName)) { + statisticsForMethod.add(statistics); + } + } + return statisticsForMethod; + } + + /** + * Formats method's name so it will have the same format as + * in rtm locking statistics. + * + *
+     * Example:
+     * com/example/Klass::method => com/example/Klass.method
+     * com/example/Klass.method  => com/example/Klass.method
+     * com.example.Klass::method => com/example/Klass.method
+     * com.example.Klass.method  => com/example/Klass.method
+     * 
+ * + * @param methodName method's name that should be formatted. + * @return formatted method's name. + */ + private static String formatMethodName(String methodName) { + String m[]; + if (methodName.contains("::")) { + m = methodName.split("::"); + } else { + int splitAt = methodName.lastIndexOf('.'); + m = new String[2]; + m[0] = methodName.substring(0, splitAt); + m[1] = methodName.substring(splitAt + 1); + } + return String.format("%s.%s", m[0].replaceAll("\\.", "/"), m[1]); + } + + /** + * Returns name of lock for which this statistics was collected. + * Lock name has following format: + * <class name>.<method name>@<bci> + * + * @return name of lock. + */ + public String getLockName() { + return String.format("%s.%s@%d", className, methodName, bci); + } + + /** + * Returns aborts count for specified abort type. + * + * @param type an abort type. + * @return count of aborts. + */ + public long getAborts(AbortType type) { + return aborts.getOrDefault(type, 0L); + } + + /** + * Sets aborts count for specified abort type. + * + * @param type an abort type. + * @param count count of aborts. + */ + public void setAborts(AbortType type, long count) { + aborts.put(type, count); + } + + public long getTotalLocks() { + return totalLocks; + } + + public long getTotalAborts() { + return totalAborts; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getLockName()).append('\n'); + builder.append(String.format("# rtm locks total (estimated): %d\n", + getTotalLocks())); + builder.append(String.format("# rtm lock aborts: %d\n", + getTotalLocks())); + + for (AbortType type : AbortType.values()) { + builder.append(String.format("# rtm lock aborts %s %d\n", + type.toString(), getAborts(type))); + } + return builder.toString(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/RTMTestBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/RTMTestBase.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,280 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.LinkedList; +import java.util.Arrays; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.cli.CommandLineOptionTest; + +/** + * Auxiliary methods used for RTM testing. + */ +public class RTMTestBase { + private static final String RTM_STATE_CHANGE_REASON = "rtm_state_change"; + /** + * We don't parse compilation log as XML-document and use regular + * expressions instead, because in some cases it could be + * malformed. + */ + private static final String FIRED_UNCOMMON_TRAP_PATTERN_TEMPLATE + = "rtm_state_change + * installed during compilation. + * + * @param compilationLogFile a path to file with LogCompilation output. + * @return count of installed uncommon traps with reason + * rtm_state_change. + * @throws IOException + */ + public static int installedRTMStateChangeTraps(String compilationLogFile) + throws IOException { + return RTMTestBase.installedUncommonTraps(compilationLogFile, + RTMTestBase.RTM_STATE_CHANGE_REASON); + } + + /** + * Finds count of fired uncommon traps with reason {@code reason}. + * + * @param compilationLogFile a path to file with LogCompilation output. + * @param reason a reason of fired uncommon traps. + * @return count of fired uncommon traps with reason {@code reason}. + * @throws IOException + */ + public static int firedUncommonTraps(String compilationLogFile, + String reason) throws IOException { + String pattern = String.format( + RTMTestBase.FIRED_UNCOMMON_TRAP_PATTERN_TEMPLATE, + reason); + return RTMTestBase.findTraps(compilationLogFile, pattern); + } + + /** + * Finds count of fired uncommon traps with reason rtm_state_change. + * + * @param compilationLogFile a path to file with LogCompilation output. + * @return count of fired uncommon traps with reason + * rtm_state_change. + * @throws IOException + */ + public static int firedRTMStateChangeTraps(String compilationLogFile) + throws IOException { + return RTMTestBase.firedUncommonTraps(compilationLogFile, + RTMTestBase.RTM_STATE_CHANGE_REASON); + } + + /** + * Finds count of uncommon traps that matches regular + * expression in {@code re}. + * + * @param compilationLogFile a path to file with LogCompilation output. + * @param re regular expression to match uncommon traps. + * @throws IOException + */ + private static int findTraps(String compilationLogFile, String re) + throws IOException { + String compilationLog = RTMTestBase.fileAsString(compilationLogFile); + Pattern pattern = Pattern.compile(re); + Matcher matcher = pattern.matcher(compilationLog); + int traps = 0; + while (matcher.find()) { + traps++; + } + return traps; + } + + /** + * Returns file's content as a string. + * + * @param path a path to file to operate on. + * @return string with content of file. + * @throws IOException + */ + private static String fileAsString(String path) throws IOException { + byte[] fileAsBytes = Files.readAllBytes(Paths.get(path)); + return new String(fileAsBytes); + } + + /** + * Prepares VM options for test execution. + * This method get test java options, filter out all RTM-related options, + * adds CompileCommand=compileonly,method_name options for each method + * from {@code methodToCompile} and finally appends all {@code vmOpts}. + * + * @param test test case whose methods that should be compiled. + * If {@code null} then no additional compileonly + * commands will be added to VM options. + * @param vmOpts additional options to pass to VM. + * @return Array with VM options. + */ + private static String[] prepareTestOptions(CompilableTest test, + String... vmOpts) { + return RTMTestBase.prepareFilteredTestOptions(test, null, vmOpts); + } + + /** + * Prepares VM options for test execution. + * This method get test java options, filter out all RTM-related options + * and all options that matches regexps in {@code additionalFilters}, + * adds CompileCommand=compileonly,method_name options for each method + * from {@code methodToCompile} and finally appends all {@code vmOpts}. + * + * @param test test case whose methods that should be compiled. + * If {@code null} then no additional compileonly + * commands will be added to VM options. + * @param additionalFilters array with regular expression that will be + * used to filter out test java options. + * If {@code null} then no additional filters + * will be used. + * @param vmOpts additional options to pass to VM. + * @return array with VM options. + */ + private static String[] prepareFilteredTestOptions(CompilableTest test, + String[] additionalFilters, String... vmOpts) { + List finalVMOpts = new LinkedList<>(); + String[] filters; + + if (additionalFilters != null) { + filters = Arrays.copyOf(additionalFilters, + additionalFilters.length + 1); + } else { + filters = new String[1]; + } + + filters[filters.length - 1] = "RTM"; + String[] filteredVMOpts = Utils.getFilteredTestJavaOpts(filters); + Collections.addAll(finalVMOpts, filteredVMOpts); + Collections.addAll(finalVMOpts, "-Xcomp", "-server", + "-XX:-TieredCompilation", + CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); + + if (test != null) { + for (String method : test.getMethodsToCompileNames()) { + finalVMOpts.add("-XX:CompileCommand=compileonly," + method); + } + } + Collections.addAll(finalVMOpts, vmOpts); + return finalVMOpts.toArray(new String[finalVMOpts.size()]); + } + + /** + * Adds additional options for VM required for successful execution of test. + * + * @param logFileName a name of compilation log file + * @param test a test case to execute + * @param options additional options to VM + * @return an array with VM options + */ + private static String[] prepareTestOptions(String logFileName, + CompilableTest test, String... options) { + String[] preparedOptions = RTMTestBase.prepareFilteredTestOptions( + test, + new String[] { + "LogCompilation", + "LogFile" + }); + List updatedOptions = new LinkedList<>(); + Collections.addAll(updatedOptions, preparedOptions); + Collections.addAll(updatedOptions, + "-XX:+LogCompilation", + "-XX:LogFile=" + logFileName); + Collections.addAll(updatedOptions, options); + + return updatedOptions.toArray(new String[updatedOptions.size()]); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/XAbortProvoker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/XAbortProvoker.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm; + +import com.oracle.java.testlibrary.Utils; +import sun.misc.Unsafe; + +/** + * Current RTM locking implementation force transaction abort + * before native method call by explicit xabort(0) call. + */ +class XAbortProvoker extends AbortProvoker { + // Following field have to be static in order to avoid escape analysis. + @SuppressWarnings("UnsuedDeclaration") + private static int field = 0; + private static final Unsafe UNSAFE = Utils.getUnsafe(); + + public XAbortProvoker() { + this(new Object()); + } + + public XAbortProvoker(Object monitor) { + super(monitor); + } + + @Override + public void forceAbort() { + synchronized(monitor) { + XAbortProvoker.field = UNSAFE.addressSize(); + } + } + + @Override + public String[] getMethodsToCompileNames() { + return new String[] { + getMethodWithLockName(), + Unsafe.class.getName() + "::addressSize" + }; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/predicate/SupportedCPU.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/predicate/SupportedCPU.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm.predicate; + +import sun.hotspot.cpuinfo.CPUInfo; + +import java.util.function.BooleanSupplier; + +public class SupportedCPU implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return CPUInfo.hasFeature("rtm"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/testlibrary/rtm/predicate/SupportedVM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/testlibrary/rtm/predicate/SupportedVM.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package rtm.predicate; + +import com.oracle.java.testlibrary.Platform; + +import java.util.function.BooleanSupplier; + +public class SupportedVM implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return Platform.isServer() && !Platform.isEmbedded(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/tiered/NonTieredLevelsTest.java --- a/test/compiler/tiered/NonTieredLevelsTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/tiered/NonTieredLevelsTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -30,7 +30,7 @@ * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:-TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:CompileCommand=compileonly,TestCase$Helper::* + * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* * NonTieredLevelsTest * @summary Verify that only one level can be used * @author igor.ignatyev@oracle.com @@ -59,9 +59,7 @@ + "TieredCompilation. Skip test."); return; } - for (TestCase test : TestCase.values()) { - new NonTieredLevelsTest(test).runTest(); - } + CompilerWhiteBoxTest.main(NonTieredLevelsTest::new, args); } private NonTieredLevelsTest(TestCase testCase) { @@ -72,6 +70,9 @@ @Override protected void test() throws Exception { + if (skipXcompOSR()) { + return; + } checkNotCompiled(); compile(); checkCompiled(); @@ -80,7 +81,7 @@ checkLevel(AVAILABLE_COMP_LEVEL, compLevel); int bci = WHITE_BOX.getMethodEntryBci(method); deoptimize(); - if (!testCase.isOsr) { + if (!testCase.isOsr()) { for (int level = 1; level <= COMP_LEVEL_MAX; ++level) { if (IS_AVAILABLE_COMPLEVEL.test(level)) { testAvailableLevel(level, bci); @@ -94,3 +95,4 @@ } } } + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/tiered/TieredLevelsTest.java --- a/test/compiler/tiered/TieredLevelsTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/tiered/TieredLevelsTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -28,7 +28,7 @@ * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:CompileCommand=compileonly,TestCase$Helper::* + * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* * TieredLevelsTest * @summary Verify that all levels < 'TieredStopAtLevel' can be used * @author igor.ignatyev@oracle.com @@ -40,9 +40,7 @@ + "TieredCompilation. Skip test."); return; } - for (TestCase test : TestCase.values()) { - new TieredLevelsTest(test).runTest(); - } + CompilerWhiteBoxTest.main(TieredLevelsTest::new, args); } private TieredLevelsTest(TestCase testCase) { @@ -53,6 +51,9 @@ @Override protected void test() throws Exception { + if (skipXcompOSR()) { + return; + } checkNotCompiled(); compile(); checkCompiled(); diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/TestMeetTopArrayExactConstantArray.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/TestMeetTopArrayExactConstantArray.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 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 + * 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 8027571 + * @summary meet of TopPTR exact array with constant array is not symmetric + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseOnStackReplacement -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation -XX:-BackgroundCompilation TestMeetTopArrayExactConstantArray + * + */ + +public class TestMeetTopArrayExactConstantArray { + + static class A { + } + + static class B { + } + + static class C extends A { + } + + static class D extends C { + } + + final static B[] b = new B[10]; + + static void m0(Object[] o) { + if (o.getClass() == Object[].class) { + } + } + + static void m1(Object[] o, boolean cond) { + if (cond) { + o = b; + } + m0(o); + } + + static void m2(Object[] o, boolean cond1, boolean cond2) { + if (cond1) { + m1(o, cond2); + } + } + + static void m3(C[] o, boolean cond1, boolean cond2, boolean cond3) { + if (cond1) { + m2(o, cond2, cond3); + } + } + + static public void main(String[] args) { + A[] a = new A[10]; + D[] d = new D[10]; + Object[] o = new Object[10]; + for (int i = 0; i < 5000; i++) { + // record in profiling that the if in m0 succeeds + m0(o); + // record some profiling for m2 and m1 + m2(a, true, (i%2) == 0); + // record some profiling for m3 and conflicting profile for m2 + m3(d, true, false, (i%2) == 0); + } + + // get m3 compiled. The if in m0 will be optimized because of argument profiling in m3 + C[] c = new C[10]; + for (int i = 0; i < 20000; i++) { + m3(c, true, false, (i%2) == 0); + } + // make m3 not entrant and the if in m0 fail + m3(c, true, true, false); + m3(c, true, true, false); + m3(c, true, true, false); + m3(c, true, true, false); + + // make m3 recompile, this time with if the not optimized + // on entry to m3, argument o is of type C[], profiled C[] + // on entry to m1, argument o is of type C[], speculative C[] exact, profiled A[]. Speculative becomes AnyNull + // after the if in m1, speculative type of o becomes constant from final field b + // the true if branch in m0 does a join between the type of o of speculative type constant from final field b and exact klass Object[] + for (int i = 0; i < 20000; i++) { + m3(c, true, false, (i%2) == 0); + } + + System.out.println("TEST PASSED"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/TestSpeculationFailedHigherEqual.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/TestSpeculationFailedHigherEqual.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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 + * 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 8027422 + * @summary type methods shouldn't always operate on speculative part + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation -XX:-BackgroundCompilation TestSpeculationFailedHigherEqual + * + */ + +public class TestSpeculationFailedHigherEqual { + + static class A { + void m() {} + int i; + } + + static class C extends A { + } + + static C c; + + static A m1(A a, boolean cond) { + // speculative type for a is C not null + if (cond ) { + a = c; + } + // speculative type for a is C (may be null) + int i = a.i; + return a; + } + + static public void main(String[] args) { + C c = new C(); + TestSpeculationFailedHigherEqual.c = c; + for (int i = 0; i < 20000; i++) { + m1(c, i%2 == 0); + } + + System.out.println("TEST PASSED"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/TypeSpeculation.java --- a/test/compiler/types/TypeSpeculation.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/types/TypeSpeculation.java Wed Oct 15 16:02:50 2014 +0200 @@ -25,7 +25,7 @@ * @test * @bug 8024070 * @summary Test that type speculation doesn't cause incorrect execution - * @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:TypeProfileLevel=222 TypeSpeculation + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation TypeSpeculation * */ @@ -398,6 +398,133 @@ return true; } + // java/lang/Object:AnyNull:exact *,iid=top + // meets + // stable:bottom[int:max..0]:NotNull * + static void test10_4(Object o) { + } + + static void test10_3(Object o, boolean b) { + if (b) { + test10_4(o); + } + } + + static void test10_2(Object o, boolean b1, boolean b2) { + if (b1) { + test10_3(o, b2); + } + } + + static void test10_1(B[] b, boolean b1, boolean b2) { + test10_2(b, b1, b2); + } + + static boolean test10() { + Object o = new Object(); + A[] a = new A[10]; + B[] b = new B[10]; + B[] c = new C[10]; + for (int i = 0; i < 20000; i++) { + test10_1(b, false, false); + test10_1(c, false, false); + test10_2(a, true, false); + test10_3(o, true); + } + return true; + } + + // stable:TypeSpeculation$B:TopPTR *,iid=top[int:max..0]:TopPTR *,iid=top + // meets + // java/lang/Object:AnyNull:exact *,iid=top + static void test11_3(Object o) { + } + + static void test11_2(Object o, boolean b) { + if (b) { + test11_3(o); + } + } + + static void test11_1(B[] b, boolean bb) { + test11_2(b, bb); + } + + static boolean test11() { + Object o = new Object(); + B[] b = new B[10]; + B[] c = new C[10]; + for (int i = 0; i < 20000; i++) { + test11_1(b, false); + test11_1(c, false); + test11_2(o, true); + } + return true; + } + + // TypeSpeculation$I * + // meets + // java/lang/Object:AnyNull *,iid=top + static void test12_3(Object o) { + } + + static void test12_2(Object o, boolean b) { + if (b) { + test12_3(o); + } + } + + static void test12_1(I i, boolean b) { + test12_2(i, b); + } + + static boolean test12() { + Object o = new Object(); + B b = new B(); + C c = new C(); + for (int i = 0; i < 20000; i++) { + test12_1(b, false); + test12_1(c, false); + test12_2(o, true); + } + return true; + } + + // stable:bottom[int:max..0]:NotNull * + // meets + // stable:TypeSpeculation$A:TopPTR *,iid=top[int:max..0]:AnyNull:exact *,iid=top + static Object test13_3(Object o, boolean b) { + Object oo; + if (b) { + oo = o; + } else { + oo = new A[10]; + } + return oo; + } + + static void test13_2(Object o, boolean b1, boolean b2) { + if (b1) { + test13_3(o, b2); + } + } + + static void test13_1(B[] b, boolean b1, boolean b2) { + test13_2(b, b1, b2); + } + + static boolean test13() { + A[] a = new A[10]; + B[] b = new B[10]; + B[] c = new C[10]; + for (int i = 0; i < 20000; i++) { + test13_1(b, false, false); + test13_1(c, false, false); + test13_2(a, true, (i%2) == 0); + } + return true; + } + static public void main(String[] args) { boolean success = true; @@ -419,6 +546,14 @@ success = test9() && success; + success = test10() && success; + + success = test11() && success; + + success = test12() && success; + + success = test13() && success; + if (success) { System.out.println("TEST PASSED"); } else { diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/CorrectnessTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/CorrectnessTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,159 @@ +/* + * Copyright (c) 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 + * 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 CorrectnessTest + * @bug 8038418 + * @library /testlibrary /testlibrary/whitebox + * @compile execution/TypeConflict.java execution/TypeProfile.java + * execution/MethodHandleDelegate.java + * @build CorrectnessTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation + * -XX:CompileCommand=exclude,execution/*::methodNotToCompile + * -XX:CompileCommand=dontinline,scenarios/Scenario::collectReturnType + * CorrectnessTest RETURN + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation + * -XX:CompileCommand=exclude,execution/*::methodNotToCompile + * -XX:CompileCommand=dontinline,scenarios/Scenario::collectReturnType + * CorrectnessTest PARAMETERS + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation + * -XX:CompileCommand=exclude,execution/*::methodNotToCompile + * -XX:CompileCommand=dontinline,scenarios/Scenario::collectReturnType + * CorrectnessTest ARGUMENTS + * @summary Tests correctness of type usage with type profiling and speculations + */ + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; +import execution.Execution; +import execution.MethodHandleDelegate; +import execution.TypeConflict; +import execution.TypeProfile; +import hierarchies.*; +import scenarios.*; +import sun.hotspot.WhiteBox; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +public class CorrectnessTest { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + public static void main(String[] args) { + if (!Platform.isServer()) { + System.out.println("ALL TESTS SKIPPED"); + } + Asserts.assertGTE(args.length, 1); + ProfilingType profilingType = ProfilingType.valueOf(args[0]); + if (runTests(profilingType)) { + System.out.println("ALL TESTS PASSED"); + } else { + throw new RuntimeException("SOME TESTS FAILED"); + } + } + + @SuppressWarnings("unchecked") + public static boolean runTests(ProfilingType profilingType) { + boolean result = true; + + List executionList = new ArrayList<>(); + executionList.add(new TypeConflict()); + executionList.add(new TypeProfile()); + for (int i = 0, n = executionList.size(); i < n; i++) { + executionList.add(new MethodHandleDelegate(executionList.get(i))); + } + + List hierarchyList = new ArrayList<>(); + hierarchyList.add(new DefaultMethodInterface.Hierarchy()); + hierarchyList.add(new DefaultMethodInterface2.Hierarchy()); + hierarchyList.add(new Linear.Hierarchy()); + hierarchyList.add(new Linear2.Hierarchy()); + hierarchyList.add(new OneRank.Hierarchy()); + for (int i = 0, n = hierarchyList.size(); i < n; i++) { + hierarchyList.add(new NullableType(hierarchyList.get(i))); + } + + List>> testCasesConstructors + = new ArrayList<>(); + testCasesConstructors.add(ArrayCopy::new); + testCasesConstructors.add(ArrayReferenceStore::new); + testCasesConstructors.add(ClassIdentity::new); + testCasesConstructors.add(ClassInstanceOf::new); + testCasesConstructors.add(ClassIsInstance::new); + testCasesConstructors.add(ReceiverAtInvokes::new); + testCasesConstructors.add(CheckCast::new); + + for (TypeHierarchy hierarchy : hierarchyList) { + for (BiFunction> constructor : testCasesConstructors) { + for (Execution execution : executionList) { + Scenario scenario = constructor.apply(profilingType, hierarchy); + if (scenario.isApplicable()) { + result &= executeTest(hierarchy, execution, scenario); + } + } + } + } + return result; + } + + /** + * Executes test case + * + * @param hierarchy type hierarchy for the test + * @param execution execution scenario + * @param scenario test scenario executed with given Execution + */ + private static boolean executeTest(TypeHierarchy hierarchy, Execution execution, Scenario scenario) { + boolean testCaseResult = false; + String testName = hierarchy.getClass().getName() + " :: " + scenario.getName() + " @ " + execution.getName(); + clearAllMethodsState(scenario.getClass()); + try { + execution.execute(scenario); + testCaseResult = true; + } catch (Exception e) { + System.err.println(testName + " failed with exception " + e); + e.printStackTrace(); + } + System.out.println((testCaseResult ? "PASSED: " : "FAILED: ") + testName); + return testCaseResult; + } + + private static void clearAllMethodsState(Class aClass) { + while (aClass != null) { + for (Method m : aClass.getDeclaredMethods()) { + WHITE_BOX.clearMethodState(m); + } + aClass = aClass.getSuperclass(); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/OffTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/OffTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 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 + * 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 CorrectnessTest + * @bug 8038418 + * @library /testlibrary /testlibrary/whitebox + * @compile execution/TypeConflict.java execution/TypeProfile.java + * execution/MethodHandleDelegate.java + * @build CorrectnessTest + * @build OffTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/timeout=1200 OffTest + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import scenarios.ProfilingType; + +import java.util.Random; + +public class OffTest { + private static final String[] OPTIONS = { + "-Xbootclasspath/a:.", + "-XX:+IgnoreUnrecognizedVMOptions", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:CompileCommand=exclude,execution/*::methodNotToCompile", + "-XX:CompileCommand=dontinline,scenarios/Scenario::collectReturnType", + "", // -XX:TypeProfileLevel=? + "", // -XX:?UseTypeSpeculation + CorrectnessTest.class.getName(), + "", // ProfilingType.name() + }; + + private static final String TYPE_PROFILE_LEVEL = "TypeProfileLevel"; + private static final String USE_TYPE_SPECULATION = "UseTypeSpeculation"; + private static final int TYPE_PROFILE_LEVEL_LENGTH = 3; + private static final int TYPE_PROFILE_LEVEL_BOUND = 3; + private static final int DEFAULT_COUNT = 10; + private static final int PROFILING_TYPE_INDEX = OPTIONS.length - 1; + private static final int TYPE_PROFILE_INDEX = OPTIONS.length - 4; + private static final int USE_TYPE_SPECULATION_INDEX = OPTIONS.length - 3; + private static final Random RNG; + + static { + String str = System.getProperty("seed"); + long seed = str != null ? Long.parseLong(str) : new Random().nextLong(); + RNG = new Random(seed); + System.out.printf("-Dseed=%d%n", seed); + } + + public static void main(String[] args) throws Exception { + int count = DEFAULT_COUNT; + if (args.length > 0) { + count = Integer.parseInt(args[0]) ; + } + for (int i = 0; i < count; ++i) { + runTest(); + } + } + + private static void runTest() throws Exception { + String useTypeSpeculation = "-XX:" + (RNG.nextBoolean() ? "+" : "-") + USE_TYPE_SPECULATION; + String typeProfileLevel = "-XX:" + TYPE_PROFILE_LEVEL + "=" + randomTypeProfileLevel(); + ProfilingType type = randomProfileType(); + OPTIONS[TYPE_PROFILE_INDEX] = typeProfileLevel; + OPTIONS[USE_TYPE_SPECULATION_INDEX] = useTypeSpeculation; + OPTIONS[PROFILING_TYPE_INDEX] = type.name(); + ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder(/* addTestVmOptions= */ true, OPTIONS); + OutputAnalyzer outputAnalyzer = new OutputAnalyzer(processBuilder.start()); + outputAnalyzer.shouldHaveExitValue(0); + } + + private static ProfilingType randomProfileType() { + ProfilingType[] value = ProfilingType.values(); + return value[RNG.nextInt(value.length)]; + } + + private static String randomTypeProfileLevel() { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < TYPE_PROFILE_LEVEL_LENGTH; ++i) { + stringBuilder.append(RNG.nextInt(TYPE_PROFILE_LEVEL_BOUND)); + } + return stringBuilder.toString(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/execution/Execution.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/execution/Execution.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package execution; + +import hierarchies.TypeHierarchy; +import scenarios.Scenario; + +/** + * Execution scenario represents test methods execution type. + * @param parameter type + * @param result Type + */ +public interface Execution { + /** + * Executes the test code of the given scenario + * See {@link scenarios.Scenario#run(T)} + * + * @param scenario test scenario + */ + void execute(Scenario scenario); + + default String getName() { + return this.getClass().getName(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/execution/MethodHandleDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/execution/MethodHandleDelegate.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package execution; + +import hierarchies.TypeHierarchy; +import scenarios.Scenario; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +/** + * Executes test scenario using {@link MethodHandle#invoke(Object...)}. + * Delegates execution to the given {@link Execution} by creating + * new test scenario, see {@link Scenario} + */ +public class MethodHandleDelegate implements Execution { + private final Execution delegate; + + public MethodHandleDelegate(Execution delegate) { + this.delegate = delegate; + } + + @Override + public void execute(Scenario scenario) { + delegate.execute(new MHScenario(scenario)); + } + + @Override + public String getName() { + return "MethodHandleDelegate # " + delegate.getName(); + } + + private static class MHScenario extends Scenario { + private final Scenario scenario; + private static final MethodHandle METHOD_HANDLE_RUN; + + static { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType methodType = MethodType.methodType(Object.class, TypeHierarchy.I.class); + + try { + METHOD_HANDLE_RUN = lookup.findVirtual(Scenario.class, "run", methodType); + } catch (NoSuchMethodException | IllegalAccessException e) { + System.err.println("Failed to get target method run() with " + e); + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + /** + * Constructor + * + * @param scenario test scenario to be executed + */ + private MHScenario(Scenario scenario) { + super("MethodHandle::" + scenario.getName(), scenario.profilingType, scenario.hierarchy); + this.scenario = scenario; + } + + /** + * Runs {@link Scenario#run(T)} with {@link MethodHandle#invoke(Object...)} + * + * @param t subject of the test + * @return result of the underlying {@link Scenario#run(T)} invocation + */ + @SuppressWarnings("unchecked") + @Override + public R run(T t) { + try { + return (R) METHOD_HANDLE_RUN.invoke(scenario, t); + } catch (Throwable thr) { + System.err.println(scenario.getName() + + " failed to invoke target method run() with " + thr); + throw new RuntimeException("Invocation failed", thr); + } + } + + @Override + public void check(R r, T t) { + scenario.check(r, t); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/execution/TypeConflict.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/execution/TypeConflict.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package execution; + +import hierarchies.TypeHierarchy; +import scenarios.Scenario; + +/** + * Type profiling conflict execution scenario. The main goal is + * to make compiler profile and compile methods with different types. + * Scenario tests guards by passing conflicting types (incompatible + * for the profiled data). + */ +public class TypeConflict implements Execution { + /** Test methods execution number to make profile */ + private final static int POLLUTION_THRESHOLD = 5000; + /** Test methods execution number to make it profiled and compiled*/ + private final static int PROFILE_THRESHOLD = 20000; + + @Override + public void execute(Scenario scenario) { + T base = scenario.getProfiled(); + T incompatible = scenario.getConflict(); + + // pollute profile by passing different types + R baseResult = null; + R incResult = null; + for (int i = 0; i < POLLUTION_THRESHOLD; i++) { + baseResult = methodNotToCompile(scenario, base); + incResult = methodNotToCompile(scenario, incompatible); + } + scenario.check(baseResult, base); + scenario.check(incResult, incompatible); + + // profile and compile + R result = null; + for (int i = 0; i < PROFILE_THRESHOLD; i++) { + result = methodNotToCompile(scenario, base); + } + scenario.check(result, base); + + // pass another type to make guard work and recompile + for (int i = 0; i < PROFILE_THRESHOLD; i++) { + result = methodNotToCompile(scenario, incompatible); + } + scenario.check(result, incompatible); + } + + private R methodNotToCompile(Scenario scenario, T t) { + return scenario.run(t); + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/execution/TypeProfile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/execution/TypeProfile.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package execution; + +import hierarchies.TypeHierarchy; +import scenarios.Scenario; + +/** + * Profile type execution scenario. Executes tester method + * in a loop without any manipulation with types or instances. + */ +public class TypeProfile implements Execution { + /** Number of test method execution to make it profiled and compiled */ + private final static int PROFILE_THRESHOLD = 100000; + + /** + * Makes scenario code be profiled and compiled + * @param scenario Test scenario + */ + @Override + public void execute(Scenario scenario) { + R result = null; + T prof = scenario.getProfiled(); + T confl = scenario.getConflict(); + + for (int i = 0; i < PROFILE_THRESHOLD; i++) { + result = methodNotToCompile(scenario, prof); + } + scenario.check(result, prof); + + result = methodNotToCompile(scenario, confl); + scenario.check(result, confl); + } + + protected R methodNotToCompile(Scenario scenario, T t) { + return scenario.run(t); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/DefaultMethodInterface.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/DefaultMethodInterface.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +public class DefaultMethodInterface { + private DefaultMethodInterface() { + } + + public static class Hierarchy + extends TypeHierarchy { + public Hierarchy() { + super(new DefaultMethodInterface.A(), new DefaultMethodInterface.B(), + DefaultMethodInterface.A.class, DefaultMethodInterface.B.class); + } + } + + public static interface I2 extends TypeHierarchy.I { + default int m() { + return TypeHierarchy.ANSWER; + } + } + + public static class A implements I2 { + // use default method from I2 + } + + public static class B extends A { + @Override + public int m() { + return TypeHierarchy.YEAR; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/DefaultMethodInterface2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/DefaultMethodInterface2.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +public class DefaultMethodInterface2 { + private DefaultMethodInterface2() { + } + + public static class Hierarchy + extends TypeHierarchy { + public Hierarchy() { + super(new TypeHierarchy.A(), new DefaultMethodInterface2.B(), + TypeHierarchy.A.class, DefaultMethodInterface2.B.class); + } + } + + public static interface I2 extends TypeHierarchy.I { + default int m() { + return TypeHierarchy.ANSWER; + } + } + + public static class B implements I2 { + // default method I2.m() + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/Linear.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/Linear.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +public class Linear { + private Linear() { + } + + public static class Hierarchy extends TypeHierarchy { + public Hierarchy() { + super(new TypeHierarchy.A(), new Linear.B(), + TypeHierarchy.A.class, Linear.B.class); + } + } + + public static class B extends TypeHierarchy.A { + @Override + public int m() { + return TypeHierarchy.YEAR; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/Linear2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/Linear2.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +public class Linear2 { + private Linear2() { + } + + public static class Hierarchy extends TypeHierarchy { + public Hierarchy() { + super(new A(), new Linear2.B(), + A.class, Linear2.B.class); + } + } + + public static interface I2 { + int m(); + } + + public static class B extends TypeHierarchy.A implements Linear2.I2 { + @Override + public int m() { + return TypeHierarchy.YEAR; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/NullableType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/NullableType.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +public class NullableType + extends TypeHierarchy { + + public NullableType(TypeHierarchy delegate) { + super(delegate.getM(), null, + delegate.getClassM(), delegate.getClassN()); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/OneRank.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/OneRank.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +public class OneRank { + private OneRank() { + } + + public static class Hierarchy extends TypeHierarchy { + public Hierarchy() { + super(new TypeHierarchy.A(), new OneRank.B(), + TypeHierarchy.A.class, OneRank.B.class); + } + } + + public static class B implements TypeHierarchy.I { + @Override + public int m() { + return TypeHierarchy.YEAR; + } + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/hierarchies/TypeHierarchy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/hierarchies/TypeHierarchy.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package hierarchies; + +/** + * Type hierarchy contains classes the type profiling and speculation are tested with + */ +public abstract class TypeHierarchy { + // Magic numbers + public static final int ANSWER = 42; + public static final int TEMP = 451; + public static final int YEAR = 1984; + + private final M m; + private final N n; + private final Class classM; + private final Class classN; + + protected TypeHierarchy(M m, N n, Class classM, Class classN) { + this.m = m; + this.n = n; + this.classM = classM; + this.classN = classN; + } + + public final M getM() { + return m; + } + + public final N getN() { + return n; + } + + public final Class getClassM() { + return classM; + } + + public final Class getClassN() { + return classN; + } + + public interface I { + int m(); + } + + public static class A implements I { + @Override + public int m() { + return TypeHierarchy.ANSWER; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ArrayCopy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ArrayCopy.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import hierarchies.TypeHierarchy; + +import java.util.Arrays; + +/** + * Tests System.arraycopy() + */ +public class ArrayCopy extends ArrayScenario { + public ArrayCopy(ProfilingType profilingType, + TypeHierarchy hierarchy) { + super("ArrayCopy", profilingType, hierarchy); + } + + /** + * @param obj is used to fill arrays + * @return the same obj + */ + @Override + public TypeHierarchy.I run(TypeHierarchy.I obj) { + switch (profilingType) { + case RETURN: + TypeHierarchy.I t = collectReturnType(obj); + Arrays.fill(array, t); + System.arraycopy(array, 0, matrix[0], 0, array.length); + return array[0]; + case ARGUMENTS: + field = obj; + Arrays.fill(array, field); + System.arraycopy(array, 0, matrix[0], 0, array.length); + return array[0]; + case PARAMETERS: + Arrays.fill(array, obj); + System.arraycopy(array, 0, matrix[0], 0, array.length); + return array[0]; + } + throw new RuntimeException("Should not reach here"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ArrayReferenceStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ArrayReferenceStore.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import hierarchies.TypeHierarchy; + +import java.util.Arrays; + +/** + * Tests aastore bytecode + */ +public class ArrayReferenceStore extends ArrayScenario { + public ArrayReferenceStore(ProfilingType profilingType, + TypeHierarchy hierarchy) { + super("ArrayReferenceStore", profilingType, hierarchy); + } + + /** + * @param obj is used to fill arrays + * @return obj + */ + @Override + public TypeHierarchy.I run(TypeHierarchy.I obj) { + switch (profilingType) { + case RETURN: + TypeHierarchy.I t = collectReturnType(obj); + Arrays.fill(array, t); + matrix[0] = array; + return matrix[0][0]; + case ARGUMENTS: + field = obj; + Arrays.fill(array, field); + matrix[0] = array; + return matrix[0][0]; + case PARAMETERS: + Arrays.fill(array, obj); + matrix[0] = array; + return matrix[0][0]; + } + throw new RuntimeException("Should not reach here"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ArrayScenario.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ArrayScenario.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import com.oracle.java.testlibrary.Asserts; +import hierarchies.TypeHierarchy; + +import java.lang.reflect.Array; +import java.util.Arrays; + +/** + * Base class for array scenarios + */ +public abstract class ArrayScenario extends Scenario { + protected final TypeHierarchy.I[] array; + protected final TypeHierarchy.I[][] matrix; + + protected ArrayScenario(String name, ProfilingType profilingType, + TypeHierarchy hierarchy) { + super(name, profilingType, hierarchy); + final int x = 20; + final int y = 10; + + TypeHierarchy.I prof = hierarchy.getM(); + TypeHierarchy.I confl = hierarchy.getN(); + + this.array = (TypeHierarchy.I[]) Array.newInstance(hierarchy.getClassM(), y); + Arrays.fill(array, prof); + + this.matrix = (TypeHierarchy.I[][]) Array.newInstance(hierarchy.getClassM(), x, y); + for (int i = 0; i < x; i++) { + this.matrix[i] = this.array; + } + + Asserts.assertEquals(array.length, matrix[0].length, "Invariant"); + } + + @Override + public boolean isApplicable() { + return hierarchy.getClassM().isAssignableFrom(hierarchy.getClassN()); + } + + @Override + public void check(TypeHierarchy.I res, TypeHierarchy.I orig) { + Asserts.assertEquals(res, orig, "Check failed"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/CheckCast.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/CheckCast.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import com.oracle.java.testlibrary.Asserts; +import hierarchies.TypeHierarchy; + +import java.util.Objects; + +/** + * Checkcast scenario + * @param profiling parameter + */ +public class CheckCast extends Scenario { + public CheckCast(ProfilingType profilingType, TypeHierarchy hierarchy) { + super("CheckCast", profilingType, hierarchy); + } + + /** + * Returns type profiling. + * @param obj is a profiled parameter for the test + * @return parameter casted to the type R + */ + @Override + public Integer run(T obj) { + switch (profilingType) { + case RETURN: + T t = collectReturnType(obj); + if (t != null) { + return t.m(); + } + return null; + case ARGUMENTS: + field = obj; + if (field != null) { + return field.m(); + } + return null; + case PARAMETERS: + if (obj != null) { + return obj.m(); + } + return null; + } + throw new RuntimeException("Should not reach here"); + } + + @Override + public void check(Integer result, T orig) { + if (result != null || orig != null) { + Objects.requireNonNull(result); + Objects.requireNonNull(orig); + Asserts.assertEquals(result, orig.m(), "Results mismatch"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ClassIdentity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ClassIdentity.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import com.oracle.java.testlibrary.Asserts; +import hierarchies.TypeHierarchy; + +/** + * Tests pattern: if (a.getClass() == D.class) + */ +public class ClassIdentity extends Scenario { + public ClassIdentity(ProfilingType profilingType, + TypeHierarchy hierarchy) { + super("ClassIdentity", profilingType, hierarchy); + } + + @Override + public boolean isApplicable() { + return hierarchy.getM() != null && hierarchy.getN() != null; + } + + @Override + public Integer run(T obj) { + switch (profilingType) { + case RETURN: + T t = collectReturnType(obj); + if (t.getClass() == TypeHierarchy.A.class) { + return inlinee(t); + } + return TypeHierarchy.TEMP; + case ARGUMENTS: + field = obj; + if (field.getClass() == TypeHierarchy.A.class) { + return inlinee(field); + } + return TypeHierarchy.TEMP; + case PARAMETERS: + if (obj.getClass() == TypeHierarchy.A.class) { + return inlinee(obj); + } + return TypeHierarchy.TEMP; + } + throw new RuntimeException("Should not reach here"); + } + + public int inlinee(T obj) { + return obj.m(); + } + + @Override + public void check(Integer result, T orig) { + if (orig.getClass() == TypeHierarchy.A.class) { + Asserts.assertEquals(result, orig.m(), + "Results are not equal for TypeHierarchy.A.class"); + } else { + Asserts.assertEquals(result, TypeHierarchy.TEMP, "Result differs from expected"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ClassInstanceOf.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ClassInstanceOf.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import com.oracle.java.testlibrary.Asserts; +import hierarchies.TypeHierarchy; + +/** + * Tests instanceof + */ +public class ClassInstanceOf extends Scenario { + public ClassInstanceOf(ProfilingType profilingType, + TypeHierarchy hierarchy) { + super("ClassInstanceOf", profilingType, hierarchy); + } + + @Override + public Integer run(T obj) { + switch (profilingType) { + case RETURN: + T t = collectReturnType(obj); + if (t instanceof TypeHierarchy.A) { + return inlinee(t); + } + return TypeHierarchy.TEMP; + case ARGUMENTS: + field = obj; + if (field instanceof TypeHierarchy.A) { + return inlinee(field); + } + return TypeHierarchy.TEMP; + case PARAMETERS: + if (obj instanceof TypeHierarchy.A) { + return inlinee(obj); + } + return TypeHierarchy.TEMP; + } + throw new RuntimeException("Should not reach here"); + } + + public int inlinee(T obj) { + return obj.m(); + } + + @Override + public void check(Integer result, T orig) { + if (orig instanceof TypeHierarchy.A) { + Asserts.assertEquals(result, orig.m(), "Results are not equal for TypeHierarchy.A"); + } else { + Asserts.assertEquals(result, TypeHierarchy.TEMP, "Result differs from expected"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ClassIsInstance.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ClassIsInstance.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import com.oracle.java.testlibrary.Asserts; +import hierarchies.TypeHierarchy; + +/** + * Tests {@link Class#isInstance(Object)} + */ +public class ClassIsInstance extends Scenario { + private final Class baseClass; + + public ClassIsInstance(ProfilingType profilingType, + TypeHierarchy hierarchy) { + super("ClassIsInstance", profilingType, hierarchy); + this.baseClass = hierarchy.getClassM(); + } + + @Override + public Integer run(T obj) { + switch (profilingType) { + case RETURN: + T t = collectReturnType(obj); + if (baseClass.isInstance(t)) { + return inlinee(t); + } + return TypeHierarchy.TEMP; + case ARGUMENTS: + field = obj; + if (baseClass.isInstance(field)) { + return inlinee(field); + } + return TypeHierarchy.TEMP; + case PARAMETERS: + if (baseClass.isInstance(obj)) { + return inlinee(obj); + } + return TypeHierarchy.TEMP; + } + throw new RuntimeException("Should not reach here"); + } + + public int inlinee(T obj) { + return obj.m(); + } + + @Override + public void check(Integer result, T orig) { + if (baseClass.isInstance(orig)) { + Asserts.assertEquals(result, orig.m(), "Results are not equal for base class"); + } else { + Asserts.assertEquals(result, TypeHierarchy.TEMP, "Result differs from expected"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ProfilingType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ProfilingType.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +public enum ProfilingType { + /** type profiling of return values of reference types from an invoke */ + RETURN, + /** type profiling for reference parameters on method entries */ + PARAMETERS, + /** type profiling for reference arguments at an invoke */ + ARGUMENTS, +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/ReceiverAtInvokes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/ReceiverAtInvokes.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import com.oracle.java.testlibrary.Asserts; +import hierarchies.TypeHierarchy; + +/** + * Receiver at invokes profiling and speculation + * + * @param parameter to be returned + */ +public class ReceiverAtInvokes extends Scenario { + public ReceiverAtInvokes(ProfilingType profilingType, + TypeHierarchy hierarchy) { + super("ReceiverAtInvokes", profilingType, hierarchy); + } + + @Override + public boolean isApplicable() { + return hierarchy.getM() != null && hierarchy.getN() != null; + } + + /** + * Receiver profiling + * + * @param obj is a profiled parameter for the test + * @return parameter casted to the type R + */ + @Override + public Integer run(T obj) { + switch (profilingType) { + case RETURN: + T t = collectReturnType(obj); + return inlinee(t); + case ARGUMENTS: + field = obj; + return inlinee(field); + case PARAMETERS: + return inlinee(obj); + } + throw new RuntimeException("Should not reach here"); + } + + private Integer inlinee(T obj) { + return obj.m(); // should be inlined + } + + @Override + public void check(Integer result, T orig) { + Asserts.assertEquals(result, orig.m(), "Results mismatch"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/types/correctness/scenarios/Scenario.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/correctness/scenarios/Scenario.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package scenarios; + +import hierarchies.TypeHierarchy; + +/** + * Test scenario + * + * @param parameter type + * @param result type + */ +public abstract class Scenario { + + private final String name; + public final ProfilingType profilingType; + public final TypeHierarchy hierarchy; + protected volatile T field; + + /** + * Constructor + * + * @param name scenario name + * @param profilingType tested profiling type + * @param hierarchy type hierarchy + */ + protected Scenario(String name, ProfilingType profilingType, + TypeHierarchy hierarchy) { + this.profilingType = profilingType; + this.name = name + " # " + profilingType.name(); + this.hierarchy = hierarchy; + } + + /** + * Returns the object which should be used as a parameter + * for the methods used for profile data + * + * @return profiled type object + */ + public T getProfiled() { + return hierarchy.getM(); + } + + /** + * Returns the object which makes a conflict for a profiled data + * when passed instead of {@linkplain Scenario#getProfiled} + * + * @return incompatible to profiled object + */ + public T getConflict() { + return hierarchy.getN(); + } + + /** + * @return scenario name + */ + public String getName() { + return name; + } + + /** Is this scenario applicable for a hierarchy it was constructed with */ + public boolean isApplicable() { + return true; + } + + /** + * Runs test scenario + * + * @param t subject of the test + * @return result of the test invocation + */ + public abstract R run(T t); + + /** Used for a return type profiling */ + protected final T collectReturnType(T t) { + return t; + } + + /** + * Checks the result for R and T + * + * @param r result + * @param t original + * @throws java.lang.RuntimeException on result mismatch + */ + public abstract void check(R r, T t); +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/uncommontrap/StackOverflowGuardPagesOff.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/uncommontrap/StackOverflowGuardPagesOff.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,355 @@ +/* + * Copyright (c) 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 + * 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 8029383 + * @summary stack overflow if callee is marked for deoptimization causes crash + * @run main/othervm -XX:TieredStopAtLevel=1 -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,StackOverflowGuardPagesOff::m1 -XX:CompileCommand=exclude,StackOverflowGuardPagesOff::m2 -Xss256K -XX:-UseOnStackReplacement StackOverflowGuardPagesOff + * + */ + +// This test calls m2 recursively until a stack overflow. Then calls +// m3 that calls m1. m1 triggers B's class loading, as a result m1 and +// m3 needs to be deoptimized. Deoptimization of m1 causes a stack +// overflow exception to be thrown which is propagated to m3 in the +// deopt blob. If the guard pages are no enabled, the stack bang in +// the deopt blob triggers a crash. +public class StackOverflowGuardPagesOff { + + static class A { + void m() {} + } + + static class B extends A { + void m() {} + } + + static void m1(boolean deopt, A a) { + long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, + l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, + l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, + l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, + l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, + l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, + l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, + l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, + l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, + l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, + l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, + l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, + l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, + l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, + l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, + l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, + l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, + l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, + l198, l199, l200, l201, l202, l203, l204, l205, l206, l207, + l208, l209, l210, l211, l212, l213, l214, l215, l216, l217, + l218, l219, l220, l221, l222, l223, l224, l225, l226, l227, + l228, l229, l230, l231, l232, l233, l234, l235, l236, l237, + l238, l239, l240, l241, l242, l243, l244, l245, l246, l247, + l248, l249, l250, l251, l252, l253, l254, l255, l256, l257, + l258, l259, l260, l261, l262, l263, l264, l265, l266, l267, + l268, l269, l270, l271, l272, l273, l274, l275, l276, l277, + l278, l279, l280, l281, l282, l283, l284, l285, l286, l287, + l288, l289, l290, l291, l292, l293, l294, l295, l296, l297, + l298, l299, l300, l301, l302, l303, l304, l305, l306, l307, + l308, l309, l310, l311, l312, l313, l314, l315, l316, l317, + l318, l319, l320, l321, l322, l323, l324, l325, l326, l327, + l328, l329, l330, l331, l332, l333, l334, l335, l336, l337, + l338, l339, l340, l341, l342, l343, l344, l345, l346, l347, + l348, l349, l350, l351, l352, l353, l354, l355, l356, l357, + l358, l359, l360, l361, l362, l363, l364, l365, l366, l367, + l368, l369, l370, l371, l372, l373, l374, l375, l376, l377, + l378, l379, l380, l381, l382, l383, l384, l385, l386, l387, + l388, l389, l390, l391, l392, l393, l394, l395, l396, l397, + l398, l399, l400, l401, l402, l403, l404, l405, l406, l407, + l408, l409, l410, l411, l412, l413, l414, l415, l416, l417, + l418, l419, l420, l421, l422, l423, l424, l425, l426, l427, + l428, l429, l430, l431, l432, l433, l434, l435, l436, l437, + l438, l439, l440, l441, l442, l443, l444, l445, l446, l447, + l448, l449, l450, l451, l452, l453, l454, l455, l456, l457, + l458, l459, l460, l461, l462, l463, l464, l465, l466, l467, + l468, l469, l470, l471, l472, l473, l474, l475, l476, l477, + l478, l479, l480, l481, l482, l483, l484, l485, l486, l487, + l488, l489, l490, l491, l492, l493, l494, l495, l496, l497, + l498, l499, l500, l501, l502, l503, l504, l505, l506, l507, + l508, l509, l510, l511; + + long ll0, ll1, ll2, ll3, ll4, ll5, ll6, ll7, ll8, ll9, ll10, ll11, ll12, + ll13, ll14, ll15, ll16, ll17, ll18, ll19, ll20, ll21, ll22, ll23, ll24, + ll25, ll26, ll27, ll28, ll29, ll30, ll31, ll32, ll33, ll34, ll35, ll36, + ll37, ll38, ll39, ll40, ll41, ll42, ll43, ll44, ll45, ll46, ll47, ll48, + ll49, ll50, ll51, ll52, ll53, ll54, ll55, ll56, ll57, ll58, ll59, ll60, + ll61, ll62, ll63, ll64, ll65, ll66, ll67, ll68, ll69, ll70, ll71, ll72, + ll73, ll74, ll75, ll76, ll77, ll78, ll79, ll80, ll81, ll82, ll83, ll84, + ll85, ll86, ll87, ll88, ll89, ll90, ll91, ll92, ll93, ll94, ll95, ll96, + ll97, ll98, ll99, ll100, ll101, ll102, ll103, ll104, ll105, ll106, ll107, + ll108, ll109, ll110, ll111, ll112, ll113, ll114, ll115, ll116, ll117, + ll118, ll119, ll120, ll121, ll122, ll123, ll124, ll125, ll126, ll127, + ll128, ll129, ll130, ll131, ll132, ll133, ll134, ll135, ll136, ll137, + ll138, ll139, ll140, ll141, ll142, ll143, ll144, ll145, ll146, ll147, + ll148, ll149, ll150, ll151, ll152, ll153, ll154, ll155, ll156, ll157, + ll158, ll159, ll160, ll161, ll162, ll163, ll164, ll165, ll166, ll167, + ll168, ll169, ll170, ll171, ll172, ll173, ll174, ll175, ll176, ll177, + ll178, ll179, ll180, ll181, ll182, ll183, ll184, ll185, ll186, ll187, + ll188, ll189, ll190, ll191, ll192, ll193, ll194, ll195, ll196, ll197, + ll198, ll199, ll200, ll201, ll202, ll203, ll204, ll205, ll206, ll207, + ll208, ll209, ll210, ll211, ll212, ll213, ll214, ll215, ll216, ll217, + ll218, ll219, ll220, ll221, ll222, ll223, ll224, ll225, ll226, ll227, + ll228, ll229, ll230, ll231, ll232, ll233, ll234, ll235, ll236, ll237, + ll238, ll239, ll240, ll241, ll242, ll243, ll244, ll245, ll246, ll247, + ll248, ll249, ll250, ll251, ll252, ll253, ll254, ll255, ll256, ll257, + ll258, ll259, ll260, ll261, ll262, ll263, ll264, ll265, ll266, ll267, + ll268, ll269, ll270, ll271, ll272, ll273, ll274, ll275, ll276, ll277, + ll278, ll279, ll280, ll281, ll282, ll283, ll284, ll285, ll286, ll287, + ll288, ll289, ll290, ll291, ll292, ll293, ll294, ll295, ll296, ll297, + ll298, ll299, ll300, ll301, ll302, ll303, ll304, ll305, ll306, ll307, + ll308, ll309, ll310, ll311, ll312, ll313, ll314, ll315, ll316, ll317, + ll318, ll319, ll320, ll321, ll322, ll323, ll324, ll325, ll326, ll327, + ll328, ll329, ll330, ll331, ll332, ll333, ll334, ll335, ll336, ll337, + ll338, ll339, ll340, ll341, ll342, ll343, ll344, ll345, ll346, ll347, + ll348, ll349, ll350, ll351, ll352, ll353, ll354, ll355, ll356, ll357, + ll358, ll359, ll360, ll361, ll362, ll363, ll364, ll365, ll366, ll367, + ll368, ll369, ll370, ll371, ll372, ll373, ll374, ll375, ll376, ll377, + ll378, ll379, ll380, ll381, ll382, ll383, ll384, ll385, ll386, ll387, + ll388, ll389, ll390, ll391, ll392, ll393, ll394, ll395, ll396, ll397, + ll398, ll399, ll400, ll401, ll402, ll403, ll404, ll405, ll406, ll407, + ll408, ll409, ll410, ll411, ll412, ll413, ll414, ll415, ll416, ll417, + ll418, ll419, ll420, ll421, ll422, ll423, ll424, ll425, ll426, ll427, + ll428, ll429, ll430, ll431, ll432, ll433, ll434, ll435, ll436, ll437, + ll438, ll439, ll440, ll441, ll442, ll443, ll444, ll445, ll446, ll447, + ll448, ll449, ll450, ll451, ll452, ll453, ll454, ll455, ll456, ll457, + ll458, ll459, ll460, ll461, ll462, ll463, ll464, ll465, ll466, ll467, + ll468, ll469, ll470, ll471, ll472, ll473, ll474, ll475, ll476, ll477, + ll478, ll479, ll480, ll481, ll482, ll483, ll484, ll485, ll486, ll487, + ll488, ll489, ll490, ll491, ll492, ll493, ll494, ll495, ll496, ll497, + ll498, ll499, ll500, ll501, ll502, ll503, ll504, ll505, ll506, ll507, + ll508, ll509, ll510, ll511; + + a.m(); + + if (deopt) { + do_load = true; + while (!load_done); + } + } + + static void m2(boolean deopt, A a) { + long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, + l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, + l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, + l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, + l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, + l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, + l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, + l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, + l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, + l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, + l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, + l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, + l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, + l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, + l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, + l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, + l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, + l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, + l198, l199, l200, l201, l202, l203, l204, l205, l206, l207, + l208, l209, l210, l211, l212, l213, l214, l215, l216, l217, + l218, l219, l220, l221, l222, l223, l224, l225, l226, l227, + l228, l229, l230, l231, l232, l233, l234, l235, l236, l237, + l238, l239, l240, l241, l242, l243, l244, l245, l246, l247, + l248, l249, l250, l251, l252, l253, l254, l255, l256, l257, + l258, l259, l260, l261, l262, l263, l264, l265, l266, l267, + l268, l269, l270, l271, l272, l273, l274, l275, l276, l277, + l278, l279, l280, l281, l282, l283, l284, l285, l286, l287, + l288, l289, l290, l291, l292, l293, l294, l295, l296, l297, + l298, l299, l300, l301, l302, l303, l304, l305, l306, l307, + l308, l309, l310, l311, l312, l313, l314, l315, l316, l317, + l318, l319, l320, l321, l322, l323, l324, l325, l326, l327, + l328, l329, l330, l331, l332, l333, l334, l335, l336, l337, + l338, l339, l340, l341, l342, l343, l344, l345, l346, l347, + l348, l349, l350, l351, l352, l353, l354, l355, l356, l357, + l358, l359, l360, l361, l362, l363, l364, l365, l366, l367, + l368, l369, l370, l371, l372, l373, l374, l375, l376, l377, + l378, l379, l380, l381, l382, l383, l384, l385, l386, l387, + l388, l389, l390, l391, l392, l393, l394, l395, l396, l397, + l398, l399, l400, l401, l402, l403, l404, l405, l406, l407, + l408, l409, l410, l411, l412, l413, l414, l415, l416, l417, + l418, l419, l420, l421, l422, l423, l424, l425, l426, l427, + l428, l429, l430, l431, l432, l433, l434, l435, l436, l437, + l438, l439, l440, l441, l442, l443, l444, l445, l446, l447, + l448, l449, l450, l451, l452, l453, l454, l455, l456, l457, + l458, l459, l460, l461, l462, l463, l464, l465, l466, l467, + l468, l469, l470, l471, l472, l473, l474, l475, l476, l477, + l478, l479, l480, l481, l482, l483, l484, l485, l486, l487, + l488, l489, l490, l491, l492, l493, l494, l495, l496, l497, + l498, l499, l500, l501, l502, l503, l504, l505, l506, l507, + l508, l509, l510, l511; + + try { + m2(deopt, a); + } catch (StackOverflowError e) { + m3(deopt, a); + } + } + + static void m3(boolean deopt, A a) { + long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, + l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, + l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, + l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, + l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, + l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, + l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, + l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, + l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, + l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, + l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, + l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, + l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, + l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, + l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, + l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, + l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, + l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, + l198, l199, l200, l201, l202, l203, l204, l205, l206, l207, + l208, l209, l210, l211, l212, l213, l214, l215, l216, l217, + l218, l219, l220, l221, l222, l223, l224, l225, l226, l227, + l228, l229, l230, l231, l232, l233, l234, l235, l236, l237, + l238, l239, l240, l241, l242, l243, l244, l245, l246, l247, + l248, l249, l250, l251, l252, l253, l254, l255, l256, l257, + l258, l259, l260, l261, l262, l263, l264, l265, l266, l267, + l268, l269, l270, l271, l272, l273, l274, l275, l276, l277, + l278, l279, l280, l281, l282, l283, l284, l285, l286, l287, + l288, l289, l290, l291, l292, l293, l294, l295, l296, l297, + l298, l299, l300, l301, l302, l303, l304, l305, l306, l307, + l308, l309, l310, l311, l312, l313, l314, l315, l316, l317, + l318, l319, l320, l321, l322, l323, l324, l325, l326, l327, + l328, l329, l330, l331, l332, l333, l334, l335, l336, l337, + l338, l339, l340, l341, l342, l343, l344, l345, l346, l347, + l348, l349, l350, l351, l352, l353, l354, l355, l356, l357, + l358, l359, l360, l361, l362, l363, l364, l365, l366, l367, + l368, l369, l370, l371, l372, l373, l374, l375, l376, l377, + l378, l379, l380, l381, l382, l383, l384, l385, l386, l387, + l388, l389, l390, l391, l392, l393, l394, l395, l396, l397, + l398, l399, l400, l401, l402, l403, l404, l405, l406, l407, + l408, l409, l410, l411, l412, l413, l414, l415, l416, l417, + l418, l419, l420, l421, l422, l423, l424, l425, l426, l427, + l428, l429, l430, l431, l432, l433, l434, l435, l436, l437, + l438, l439, l440, l441, l442, l443, l444, l445, l446, l447, + l448, l449, l450, l451, l452, l453, l454, l455, l456, l457, + l458, l459, l460, l461, l462, l463, l464, l465, l466, l467, + l468, l469, l470, l471, l472, l473, l474, l475, l476, l477, + l478, l479, l480, l481, l482, l483, l484, l485, l486, l487, + l488, l489, l490, l491, l492, l493, l494, l495, l496, l497, + l498, l499, l500, l501, l502, l503, l504, l505, l506, l507, + l508, l509, l510, l511; + + long ll0, ll1, ll2, ll3, ll4, ll5, ll6, ll7, ll8, ll9, ll10, ll11, ll12, + ll13, ll14, ll15, ll16, ll17, ll18, ll19, ll20, ll21, ll22, ll23, ll24, + ll25, ll26, ll27, ll28, ll29, ll30, ll31, ll32, ll33, ll34, ll35, ll36, + ll37, ll38, ll39, ll40, ll41, ll42, ll43, ll44, ll45, ll46, ll47, ll48, + ll49, ll50, ll51, ll52, ll53, ll54, ll55, ll56, ll57, ll58, ll59, ll60, + ll61, ll62, ll63, ll64, ll65, ll66, ll67, ll68, ll69, ll70, ll71, ll72, + ll73, ll74, ll75, ll76, ll77, ll78, ll79, ll80, ll81, ll82, ll83, ll84, + ll85, ll86, ll87, ll88, ll89, ll90, ll91, ll92, ll93, ll94, ll95, ll96, + ll97, ll98, ll99, ll100, ll101, ll102, ll103, ll104, ll105, ll106, ll107, + ll108, ll109, ll110, ll111, ll112, ll113, ll114, ll115, ll116, ll117, + ll118, ll119, ll120, ll121, ll122, ll123, ll124, ll125, ll126, ll127, + ll128, ll129, ll130, ll131, ll132, ll133, ll134, ll135, ll136, ll137, + ll138, ll139, ll140, ll141, ll142, ll143, ll144, ll145, ll146, ll147, + ll148, ll149, ll150, ll151, ll152, ll153, ll154, ll155, ll156, ll157, + ll158, ll159, ll160, ll161, ll162, ll163, ll164, ll165, ll166, ll167, + ll168, ll169, ll170, ll171, ll172, ll173, ll174, ll175, ll176, ll177, + ll178, ll179, ll180, ll181, ll182, ll183, ll184, ll185, ll186, ll187, + ll188, ll189, ll190, ll191, ll192, ll193, ll194, ll195, ll196, ll197, + ll198, ll199, ll200, ll201, ll202, ll203, ll204, ll205, ll206, ll207, + ll208, ll209, ll210, ll211, ll212, ll213, ll214, ll215, ll216, ll217, + ll218, ll219, ll220, ll221, ll222, ll223, ll224, ll225, ll226, ll227, + ll228, ll229, ll230, ll231, ll232, ll233, ll234, ll235, ll236, ll237, + ll238, ll239, ll240, ll241, ll242, ll243, ll244, ll245, ll246, ll247, + ll248, ll249, ll250, ll251, ll252, ll253, ll254, ll255, ll256, ll257, + ll258, ll259, ll260, ll261, ll262, ll263, ll264, ll265, ll266, ll267, + ll268, ll269, ll270, ll271, ll272, ll273, ll274, ll275, ll276, ll277, + ll278, ll279, ll280, ll281, ll282, ll283, ll284, ll285, ll286, ll287, + ll288, ll289, ll290, ll291, ll292, ll293, ll294, ll295, ll296, ll297, + ll298, ll299, ll300, ll301, ll302, ll303, ll304, ll305, ll306, ll307, + ll308, ll309, ll310, ll311, ll312, ll313, ll314, ll315, ll316, ll317, + ll318, ll319, ll320, ll321, ll322, ll323, ll324, ll325, ll326, ll327, + ll328, ll329, ll330, ll331, ll332, ll333, ll334, ll335, ll336, ll337, + ll338, ll339, ll340, ll341, ll342, ll343, ll344, ll345, ll346, ll347, + ll348, ll349, ll350, ll351, ll352, ll353, ll354, ll355, ll356, ll357, + ll358, ll359, ll360, ll361, ll362, ll363, ll364, ll365, ll366, ll367, + ll368, ll369, ll370, ll371, ll372, ll373, ll374, ll375, ll376, ll377, + ll378, ll379, ll380, ll381, ll382, ll383, ll384, ll385, ll386, ll387, + ll388, ll389, ll390, ll391, ll392, ll393, ll394, ll395, ll396, ll397, + ll398, ll399, ll400, ll401, ll402, ll403, ll404, ll405, ll406, ll407, + ll408, ll409, ll410, ll411, ll412, ll413, ll414, ll415, ll416, ll417, + ll418, ll419, ll420, ll421, ll422, ll423, ll424, ll425, ll426, ll427, + ll428, ll429, ll430, ll431, ll432, ll433, ll434, ll435, ll436, ll437, + ll438, ll439, ll440, ll441, ll442, ll443, ll444, ll445, ll446, ll447, + ll448, ll449, ll450, ll451, ll452, ll453, ll454, ll455, ll456, ll457, + ll458, ll459, ll460, ll461, ll462, ll463, ll464, ll465, ll466, ll467, + ll468, ll469, ll470, ll471, ll472, ll473, ll474, ll475, ll476, ll477, + ll478, ll479, ll480, ll481, ll482, ll483, ll484, ll485, ll486, ll487, + ll488, ll489, ll490, ll491, ll492, ll493, ll494, ll495, ll496, ll497, + ll498, ll499, ll500, ll501, ll502, ll503, ll504, ll505, ll506, ll507, + ll508, ll509, ll510, ll511; + + a.m(); + + m1(deopt, a); + } + + // Used for synchronization betwen main thread and thread + // responsible for class loading + static volatile boolean thread_started = false; + static volatile boolean do_load = false; + static volatile boolean load_done = false; + + static public void main(String[] args) { + // This thread does the loading of B. If m1 does it, the class + // loading can cause stack overflows. + Thread thread = new Thread() { + public void run() { + thread_started = true; + while(!do_load); + new B(); + load_done = true; + } + }; + thread.start(); + while(!thread_started); + // get m3 and m1 compiled + A a = new A(); + for (int i = 0; i < 5000; i++) { + m3(false, a); + m1(false, a); + } + m2(true, a); + + System.out.println("TEST PASSED"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/uncommontrap/TestLockEliminatedAtDeopt.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/uncommontrap/TestLockEliminatedAtDeopt.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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 + * 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 8032011 + * @summary biased locking's revoke_bias locks monitor in compiled frame with eliminated lock + * @run main/othervm -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestLockEliminatedAtDeopt$A.m2 -XX:-BackgroundCompilation -XX:BiasedLockingStartupDelay=0 TestLockEliminatedAtDeopt + * + */ + +public class TestLockEliminatedAtDeopt { + + static class A { + void m() { + } + + // This lock is not eliminated but biased to main thread on + // first call + synchronized void m2(boolean trap) { + if (trap) { + new B(); + } + } + } + + static class B extends A { + void m() { + } + } + + static void m1(boolean trap) { + A a = new A(); + // This lock is eliminated by c2 + synchronized(a) { + a.m2(trap); + a.m(); + } + } + + public static void main(String[] args) { + for (int i = 0; i < 20000; i++) { + m1(false); + } + // Trigger uncommon trap in A.m2() (class unloaded) and + // deoptimization of m1() (CHA invalidated). Uncommon trap + // code locks monitor in m1's frame where's it's eliminated. + m1(true); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/uncommontrap/TestSpecTrapClassUnloading.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/uncommontrap/TestSpecTrapClassUnloading.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 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 + * 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 8031752 + * @summary speculative traps need to be cleaned up at GC + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:+UseTypeSpeculation -XX:TypeProfileLevel=222 -XX:CompileCommand=exclude,java.lang.reflect.Method::invoke -XX:CompileCommand=exclude,sun.reflect.DelegatingMethodAccessorImpl::invoke -Xmx1M TestSpecTrapClassUnloading + * + */ + +import java.lang.reflect.Method; + +public class TestSpecTrapClassUnloading { + static class B { + final public boolean m(Object o) { + if (o.getClass() == B.class) { + return true; + } + return false; + } + } + + static class MemoryChunk { + MemoryChunk other; + long[] array; + MemoryChunk(MemoryChunk other) { + other = other; + array = new long[1024 * 1024 * 1024]; + } + } + + static void m1(B b, Object o) { + b.m(o); + } + + static void m2(B b, Object o) { + b.m(o); + } + + public static void main(String[] args) throws Exception { + Method m = B.class.getMethod("m", Object.class); + Object o = new Object(); + B b = new B(); + + // add speculative trap in B.m() for m1 + for (int i = 0; i < 20000; i++) { + m1(b, b); + } + m1(b, o); + + // add speculative trap in B.m() for code generated by reflection + for (int i = 0; i < 20000; i++) { + m.invoke(b, b); + } + m.invoke(b, o); + + m = null; + + // add speculative trap in B.m() for m2 + for (int i = 0; i < 20000; i++) { + m2(b, b); + } + m2(b, o); + + // Exhaust memory which causes the code generated by + // reflection to be unloaded but B.m() is not. + MemoryChunk root = null; + try { + while (true) { + root = new MemoryChunk(root); + } + } catch(OutOfMemoryError e) { + root = null; + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/uncommontrap/TestStackBangMonitorOwned.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/uncommontrap/TestStackBangMonitorOwned.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,268 @@ +/* + * Copyright (c) 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 + * 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 8032410 + * @summary Stack overflow at deoptimization doesn't release owned monitors + * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestStackBangMonitorOwned::m1 -XX:CompileCommand=exclude,TestStackBangMonitorOwned::m2 -Xss256K -XX:-UseOnStackReplacement TestStackBangMonitorOwned + * + */ +public class TestStackBangMonitorOwned { + + static class UnloadedClass1 { + volatile int field; + } + + static Object m1(boolean deopt) { + long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, + l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, + l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, + l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, + l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, + l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, + l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, + l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, + l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, + l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, + l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, + l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, + l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, + l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, + l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, + l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, + l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, + l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, + l198, l199, l200, l201, l202, l203, l204, l205, l206, l207, + l208, l209, l210, l211, l212, l213, l214, l215, l216, l217, + l218, l219, l220, l221, l222, l223, l224, l225, l226, l227, + l228, l229, l230, l231, l232, l233, l234, l235, l236, l237, + l238, l239, l240, l241, l242, l243, l244, l245, l246, l247, + l248, l249, l250, l251, l252, l253, l254, l255, l256, l257, + l258, l259, l260, l261, l262, l263, l264, l265, l266, l267, + l268, l269, l270, l271, l272, l273, l274, l275, l276, l277, + l278, l279, l280, l281, l282, l283, l284, l285, l286, l287, + l288, l289, l290, l291, l292, l293, l294, l295, l296, l297, + l298, l299, l300, l301, l302, l303, l304, l305, l306, l307, + l308, l309, l310, l311, l312, l313, l314, l315, l316, l317, + l318, l319, l320, l321, l322, l323, l324, l325, l326, l327, + l328, l329, l330, l331, l332, l333, l334, l335, l336, l337, + l338, l339, l340, l341, l342, l343, l344, l345, l346, l347, + l348, l349, l350, l351, l352, l353, l354, l355, l356, l357, + l358, l359, l360, l361, l362, l363, l364, l365, l366, l367, + l368, l369, l370, l371, l372, l373, l374, l375, l376, l377, + l378, l379, l380, l381, l382, l383, l384, l385, l386, l387, + l388, l389, l390, l391, l392, l393, l394, l395, l396, l397, + l398, l399, l400, l401, l402, l403, l404, l405, l406, l407, + l408, l409, l410, l411, l412, l413, l414, l415, l416, l417, + l418, l419, l420, l421, l422, l423, l424, l425, l426, l427, + l428, l429, l430, l431, l432, l433, l434, l435, l436, l437, + l438, l439, l440, l441, l442, l443, l444, l445, l446, l447, + l448, l449, l450, l451, l452, l453, l454, l455, l456, l457, + l458, l459, l460, l461, l462, l463, l464, l465, l466, l467, + l468, l469, l470, l471, l472, l473, l474, l475, l476, l477, + l478, l479, l480, l481, l482, l483, l484, l485, l486, l487, + l488, l489, l490, l491, l492, l493, l494, l495, l496, l497, + l498, l499, l500, l501, l502, l503, l504, l505, l506, l507, + l508, l509, l510, l511; + + long ll0, ll1, ll2, ll3, ll4, ll5, ll6, ll7, ll8, ll9, ll10, ll11, ll12, + ll13, ll14, ll15, ll16, ll17, ll18, ll19, ll20, ll21, ll22, ll23, ll24, + ll25, ll26, ll27, ll28, ll29, ll30, ll31, ll32, ll33, ll34, ll35, ll36, + ll37, ll38, ll39, ll40, ll41, ll42, ll43, ll44, ll45, ll46, ll47, ll48, + ll49, ll50, ll51, ll52, ll53, ll54, ll55, ll56, ll57, ll58, ll59, ll60, + ll61, ll62, ll63, ll64, ll65, ll66, ll67, ll68, ll69, ll70, ll71, ll72, + ll73, ll74, ll75, ll76, ll77, ll78, ll79, ll80, ll81, ll82, ll83, ll84, + ll85, ll86, ll87, ll88, ll89, ll90, ll91, ll92, ll93, ll94, ll95, ll96, + ll97, ll98, ll99, ll100, ll101, ll102, ll103, ll104, ll105, ll106, ll107, + ll108, ll109, ll110, ll111, ll112, ll113, ll114, ll115, ll116, ll117, + ll118, ll119, ll120, ll121, ll122, ll123, ll124, ll125, ll126, ll127, + ll128, ll129, ll130, ll131, ll132, ll133, ll134, ll135, ll136, ll137, + ll138, ll139, ll140, ll141, ll142, ll143, ll144, ll145, ll146, ll147, + ll148, ll149, ll150, ll151, ll152, ll153, ll154, ll155, ll156, ll157, + ll158, ll159, ll160, ll161, ll162, ll163, ll164, ll165, ll166, ll167, + ll168, ll169, ll170, ll171, ll172, ll173, ll174, ll175, ll176, ll177, + ll178, ll179, ll180, ll181, ll182, ll183, ll184, ll185, ll186, ll187, + ll188, ll189, ll190, ll191, ll192, ll193, ll194, ll195, ll196, ll197, + ll198, ll199, ll200, ll201, ll202, ll203, ll204, ll205, ll206, ll207, + ll208, ll209, ll210, ll211, ll212, ll213, ll214, ll215, ll216, ll217, + ll218, ll219, ll220, ll221, ll222, ll223, ll224, ll225, ll226, ll227, + ll228, ll229, ll230, ll231, ll232, ll233, ll234, ll235, ll236, ll237, + ll238, ll239, ll240, ll241, ll242, ll243, ll244, ll245, ll246, ll247, + ll248, ll249, ll250, ll251, ll252, ll253, ll254, ll255, ll256, ll257, + ll258, ll259, ll260, ll261, ll262, ll263, ll264, ll265, ll266, ll267, + ll268, ll269, ll270, ll271, ll272, ll273, ll274, ll275, ll276, ll277, + ll278, ll279, ll280, ll281, ll282, ll283, ll284, ll285, ll286, ll287, + ll288, ll289, ll290, ll291, ll292, ll293, ll294, ll295, ll296, ll297, + ll298, ll299, ll300, ll301, ll302, ll303, ll304, ll305, ll306, ll307, + ll308, ll309, ll310, ll311, ll312, ll313, ll314, ll315, ll316, ll317, + ll318, ll319, ll320, ll321, ll322, ll323, ll324, ll325, ll326, ll327, + ll328, ll329, ll330, ll331, ll332, ll333, ll334, ll335, ll336, ll337, + ll338, ll339, ll340, ll341, ll342, ll343, ll344, ll345, ll346, ll347, + ll348, ll349, ll350, ll351, ll352, ll353, ll354, ll355, ll356, ll357, + ll358, ll359, ll360, ll361, ll362, ll363, ll364, ll365, ll366, ll367, + ll368, ll369, ll370, ll371, ll372, ll373, ll374, ll375, ll376, ll377, + ll378, ll379, ll380, ll381, ll382, ll383, ll384, ll385, ll386, ll387, + ll388, ll389, ll390, ll391, ll392, ll393, ll394, ll395, ll396, ll397, + ll398, ll399, ll400, ll401, ll402, ll403, ll404, ll405, ll406, ll407, + ll408, ll409, ll410, ll411, ll412, ll413, ll414, ll415, ll416, ll417, + ll418, ll419, ll420, ll421, ll422, ll423, ll424, ll425, ll426, ll427, + ll428, ll429, ll430, ll431, ll432, ll433, ll434, ll435, ll436, ll437, + ll438, ll439, ll440, ll441, ll442, ll443, ll444, ll445, ll446, ll447, + ll448, ll449, ll450, ll451, ll452, ll453, ll454, ll455, ll456, ll457, + ll458, ll459, ll460, ll461, ll462, ll463, ll464, ll465, ll466, ll467, + ll468, ll469, ll470, ll471, ll472, ll473, ll474, ll475, ll476, ll477, + ll478, ll479, ll480, ll481, ll482, ll483, ll484, ll485, ll486, ll487, + ll488, ll489, ll490, ll491, ll492, ll493, ll494, ll495, ll496, ll497, + ll498, ll499, ll500, ll501, ll502, ll503, ll504, ll505, ll506, ll507, + ll508, ll509, ll510, ll511; + + if (deopt) { + method_entered = true; + synchronized(monitor) { + do_monitor_acquire = true; + UnloadedClass1 res = new UnloadedClass1(); // forces deopt with c2 + res.field = 0; //forced deopt with c1 + return res; + } + } + return null; + } + + static boolean m2(boolean deopt) { + long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, + l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, + l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, + l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, + l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, + l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, + l73, l74, l75, l76, l77, l78, l79, l80, l81, l82, l83, l84, + l85, l86, l87, l88, l89, l90, l91, l92, l93, l94, l95, l96, + l97, l98, l99, l100, l101, l102, l103, l104, l105, l106, l107, + l108, l109, l110, l111, l112, l113, l114, l115, l116, l117, + l118, l119, l120, l121, l122, l123, l124, l125, l126, l127, + l128, l129, l130, l131, l132, l133, l134, l135, l136, l137, + l138, l139, l140, l141, l142, l143, l144, l145, l146, l147, + l148, l149, l150, l151, l152, l153, l154, l155, l156, l157, + l158, l159, l160, l161, l162, l163, l164, l165, l166, l167, + l168, l169, l170, l171, l172, l173, l174, l175, l176, l177, + l178, l179, l180, l181, l182, l183, l184, l185, l186, l187, + l188, l189, l190, l191, l192, l193, l194, l195, l196, l197, + l198, l199, l200, l201, l202, l203, l204, l205, l206, l207, + l208, l209, l210, l211, l212, l213, l214, l215, l216, l217, + l218, l219, l220, l221, l222, l223, l224, l225, l226, l227, + l228, l229, l230, l231, l232, l233, l234, l235, l236, l237, + l238, l239, l240, l241, l242, l243, l244, l245, l246, l247, + l248, l249, l250, l251, l252, l253, l254, l255, l256, l257, + l258, l259, l260, l261, l262, l263, l264, l265, l266, l267, + l268, l269, l270, l271, l272, l273, l274, l275, l276, l277, + l278, l279, l280, l281, l282, l283, l284, l285, l286, l287, + l288, l289, l290, l291, l292, l293, l294, l295, l296, l297, + l298, l299, l300, l301, l302, l303, l304, l305, l306, l307, + l308, l309, l310, l311, l312, l313, l314, l315, l316, l317, + l318, l319, l320, l321, l322, l323, l324, l325, l326, l327, + l328, l329, l330, l331, l332, l333, l334, l335, l336, l337, + l338, l339, l340, l341, l342, l343, l344, l345, l346, l347, + l348, l349, l350, l351, l352, l353, l354, l355, l356, l357, + l358, l359, l360, l361, l362, l363, l364, l365, l366, l367, + l368, l369, l370, l371, l372, l373, l374, l375, l376, l377, + l378, l379, l380, l381, l382, l383, l384, l385, l386, l387, + l388, l389, l390, l391, l392, l393, l394, l395, l396, l397, + l398, l399, l400, l401, l402, l403, l404, l405, l406, l407, + l408, l409, l410, l411, l412, l413, l414, l415, l416, l417, + l418, l419, l420, l421, l422, l423, l424, l425, l426, l427, + l428, l429, l430, l431, l432, l433, l434, l435, l436, l437, + l438, l439, l440, l441, l442, l443, l444, l445, l446, l447, + l448, l449, l450, l451, l452, l453, l454, l455, l456, l457, + l458, l459, l460, l461, l462, l463, l464, l465, l466, l467, + l468, l469, l470, l471, l472, l473, l474, l475, l476, l477, + l478, l479, l480, l481, l482, l483, l484, l485, l486, l487, + l488, l489, l490, l491, l492, l493, l494, l495, l496, l497, + l498, l499, l500, l501, l502, l503, l504, l505, l506, l507, + l508, l509, l510, l511; + + boolean do_m3 = false; + try { + do_m3 = m2(deopt); + } catch (StackOverflowError e) { + return true; + } + if (do_m3) { + try { + m1(deopt); + } catch (StackOverflowError e) {} + } + return false; + } + + // Used for synchronization betwen threads + static volatile boolean thread_started = false; + static volatile boolean do_monitor_acquire = false; + static volatile boolean monitor_acquired = false; + static volatile boolean method_entered = false; + + static Object monitor = new Object(); + + static public void main(String[] args) { + // get m1 compiled + for (int i = 0; i < 20000; i++) { + m1(false); + } + + Thread thread = new Thread() { + public void run() { + thread_started = true; + while(!do_monitor_acquire); + System.out.println("Ok to try to acquire the lock"); + synchronized(monitor) { + monitor_acquired = true; + } + } + }; + + thread.setDaemon(true); + thread.start(); + + while(!thread_started); + + m2(true); + + if (!method_entered) { + System.out.println("TEST PASSED"); + return; + } + + for (int i = 0; i < 10; i++) { + System.out.println("Is lock acquired?"); + if (monitor_acquired) { + System.out.println("TEST PASSED"); + return; + } + try { + Thread.sleep(10000); + } catch(InterruptedException ie) { + } + } + System.out.println("TEST FAILED"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/ClearMethodStateTest.java --- a/test/compiler/whitebox/ClearMethodStateTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/ClearMethodStateTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -21,25 +21,25 @@ * questions. */ +import java.util.function.Function; + /* * @test ClearMethodStateTest * @bug 8006683 8007288 8022832 * @library /testlibrary /testlibrary/whitebox * @build ClearMethodStateTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* ClearMethodStateTest + * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* ClearMethodStateTest * @summary testing of WB::clearMethodState() * @author igor.ignatyev@oracle.com */ public class ClearMethodStateTest extends CompilerWhiteBoxTest { public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new ClearMethodStateTest(test).runTest(); - } + CompilerWhiteBoxTest.main(ClearMethodStateTest::new, args); } - public ClearMethodStateTest(TestCase testCase) { + private ClearMethodStateTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); @@ -63,7 +63,7 @@ deoptimize(); checkNotCompiled(); - if (testCase.isOsr) { + if (testCase.isOsr()) { // part test isn't applicable for OSR test case return; } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/CompilerWhiteBoxTest.java --- a/test/compiler/whitebox/CompilerWhiteBoxTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/CompilerWhiteBoxTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,6 +24,7 @@ import com.sun.management.HotSpotDiagnosticMXBean; import com.sun.management.VMOption; import sun.hotspot.WhiteBox; +import sun.hotspot.code.NMethod; import sun.management.ManagementFactoryHelper; import java.lang.reflect.Constructor; @@ -31,6 +32,7 @@ import java.lang.reflect.Method; import java.util.Objects; import java.util.concurrent.Callable; +import java.util.function.Function; /** * Abstract class for WhiteBox testing of JIT. @@ -50,7 +52,7 @@ protected static int COMP_LEVEL_FULL_PROFILE = 3; /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4; - /** Maximal value for CompLeveL */ + /** Maximal value for CompLevel */ protected static int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION; /** Instance of WhiteBox */ @@ -75,8 +77,7 @@ /** count of invocation to triger OSR compilation */ protected static final long BACKEDGE_THRESHOLD; /** Value of {@code java.vm.info} (interpreted|mixed|comp mode) */ - protected static final String MODE - = System.getProperty("java.vm.info"); + protected static final String MODE = System.getProperty("java.vm.info"); static { if (TIERED_COMPILATION) { @@ -133,6 +134,20 @@ return compLevel == COMP_LEVEL_FULL_OPTIMIZATION; } + protected static void main( + Function constructor, + String[] args) { + if (args.length == 0) { + for (TestCase test : SimpleTestCase.values()) { + constructor.apply(test).runTest(); + } + } else { + for (String name : args) { + constructor.apply(SimpleTestCase.valueOf(name)).runTest(); + } + } + } + /** tested method */ protected final Executable method; protected final TestCase testCase; @@ -145,7 +160,7 @@ protected CompilerWhiteBoxTest(TestCase testCase) { Objects.requireNonNull(testCase); System.out.println("TEST CASE:" + testCase.name()); - method = testCase.executable; + method = testCase.getExecutable(); this.testCase = testCase; } @@ -182,6 +197,29 @@ } /** + * Checks, that {@linkplain #method} is not compiled at the given compilation + * level or above. + * + * @param compLevel + * + * @throws RuntimeException if {@linkplain #method} is in compiler queue or + * is compiled, or if {@linkplain #method} has zero + * compilation level. + */ + + protected final void checkNotCompiled(int compLevel) { + if (WHITE_BOX.isMethodQueuedForCompilation(method)) { + throw new RuntimeException(method + " must not be in queue"); + } + if (WHITE_BOX.getMethodCompilationLevel(method, false) >= compLevel) { + throw new RuntimeException(method + " comp_level must be >= maxCompLevel"); + } + if (WHITE_BOX.getMethodCompilationLevel(method, true) >= compLevel) { + throw new RuntimeException(method + " osr_comp_level must be >= maxCompLevel"); + } + } + + /** * Checks, that {@linkplain #method} is not compiled. * * @throws RuntimeException if {@linkplain #method} is in compiler queue or @@ -204,7 +242,7 @@ if (WHITE_BOX.getMethodCompilationLevel(method, true) != 0) { throw new RuntimeException(method + " osr_comp_level must be == 0"); } - } + } /** * Checks, that {@linkplain #method} is compiled. @@ -221,44 +259,47 @@ method, System.currentTimeMillis() - start); return; } - if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr)) { + if (!WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) { throw new RuntimeException(method + " must be " - + (testCase.isOsr ? "osr_" : "") + "compiled"); + + (testCase.isOsr() ? "osr_" : "") + "compiled"); } - if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr) == 0) { + if (WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr()) + == 0) { throw new RuntimeException(method - + (testCase.isOsr ? " osr_" : " ") + + (testCase.isOsr() ? " osr_" : " ") + "comp_level must be != 0"); } } protected final void deoptimize() { - WHITE_BOX.deoptimizeMethod(method, testCase.isOsr); - if (testCase.isOsr) { + WHITE_BOX.deoptimizeMethod(method, testCase.isOsr()); + if (testCase.isOsr()) { WHITE_BOX.deoptimizeMethod(method, false); } } protected final int getCompLevel() { - return WHITE_BOX.getMethodCompilationLevel(method, testCase.isOsr); + NMethod nm = NMethod.get(method, testCase.isOsr()); + return nm == null ? COMP_LEVEL_NONE : nm.comp_level; } protected final boolean isCompilable() { return WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, - testCase.isOsr); + testCase.isOsr()); } protected final boolean isCompilable(int compLevel) { - return WHITE_BOX.isMethodCompilable(method, compLevel, testCase.isOsr); + return WHITE_BOX + .isMethodCompilable(method, compLevel, testCase.isOsr()); } protected final void makeNotCompilable() { WHITE_BOX.makeMethodNotCompilable(method, COMP_LEVEL_ANY, - testCase.isOsr); + testCase.isOsr()); } protected final void makeNotCompilable(int compLevel) { - WHITE_BOX.makeMethodNotCompilable(method, compLevel, testCase.isOsr); + WHITE_BOX.makeMethodNotCompilable(method, compLevel, testCase.isOsr()); } /** @@ -298,7 +339,7 @@ WHITE_BOX.isMethodCompiled(method, true)); System.out.printf("\tosr_comp_level:\t%d%n", WHITE_BOX.getMethodCompilationLevel(method, true)); - System.out.printf("\tin_queue:\t%b%n", + System.out.printf("\tin_queue:\t%b%n", WHITE_BOX.isMethodQueuedForCompilation(method)); System.out.printf("compile_queues_size:\t%d%n%n", WHITE_BOX.getCompileQueuesSize()); @@ -311,13 +352,13 @@ /** * Tries to trigger compilation of {@linkplain #method} by call - * {@linkplain #testCase.callable} enough times. + * {@linkplain TestCase#getCallable()} enough times. * * @return accumulated result * @see #compile(int) */ protected final int compile() { - if (testCase.isOsr) { + if (testCase.isOsr()) { return compile(1); } else { return compile(THRESHOLD); @@ -326,7 +367,7 @@ /** * Tries to trigger compilation of {@linkplain #method} by call - * {@linkplain #testCase.callable} specified times. + * {@linkplain TestCase#getCallable()} specified times. * * @param count invocation count * @return accumulated result @@ -336,7 +377,7 @@ Integer tmp; for (int i = 0; i < count; ++i) { try { - tmp = testCase.callable.call(); + tmp = testCase.getCallable().call(); } catch (Exception e) { tmp = null; } @@ -347,19 +388,46 @@ } return result; } + + /** + * Utility interface provides tested method and object to invoke it. + */ + public interface TestCase { + /** the name of test case */ + String name(); + + /** tested method */ + Executable getExecutable(); + + /** object to invoke {@linkplain #getExecutable()} */ + Callable getCallable(); + + /** flag for OSR test case */ + boolean isOsr(); + } + + /** + * @return {@code true} if the current test case is OSR and the mode is + * Xcomp, otherwise {@code false} + */ + protected boolean skipXcompOSR() { + boolean result = testCase.isOsr() + && CompilerWhiteBoxTest.MODE.startsWith("compiled "); + if (result && IS_VERBOSE) { + System.err.printf("Warning: %s is not applicable in %s%n", + testCase.name(), CompilerWhiteBoxTest.MODE); + } + return result; + } } -/** - * Utility structure containing tested method and object to invoke it. - */ -enum TestCase { +enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { /** constructor test case */ CONSTRUCTOR_TEST(Helper.CONSTRUCTOR, Helper.CONSTRUCTOR_CALLABLE, false), /** method test case */ METOD_TEST(Helper.METHOD, Helper.METHOD_CALLABLE, false), /** static method test case */ STATIC_TEST(Helper.STATIC, Helper.STATIC_CALLABLE, false), - /** OSR constructor test case */ OSR_CONSTRUCTOR_TEST(Helper.OSR_CONSTRUCTOR, Helper.OSR_CONSTRUCTOR_CALLABLE, true), @@ -368,20 +436,32 @@ /** OSR static method test case */ OSR_STATIC_TEST(Helper.OSR_STATIC, Helper.OSR_STATIC_CALLABLE, true); - /** tested method */ - final Executable executable; - /** object to invoke {@linkplain #executable} */ - final Callable callable; - /** flag for OSR test case */ - final boolean isOsr; + private final Executable executable; + private final Callable callable; + private final boolean isOsr; - private TestCase(Executable executable, Callable callable, + private SimpleTestCase(Executable executable, Callable callable, boolean isOsr) { this.executable = executable; this.callable = callable; this.isOsr = isOsr; } + @Override + public Executable getExecutable() { + return executable; + } + + @Override + public Callable getCallable() { + return callable; + } + + @Override + public boolean isOsr() { + return isOsr; + } + private static class Helper { private static final Callable CONSTRUCTOR_CALLABLE @@ -436,7 +516,6 @@ } }; - private static final Constructor CONSTRUCTOR; private static final Constructor OSR_CONSTRUCTOR; private static final Method METHOD; diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/DeoptimizeAllTest.java --- a/test/compiler/whitebox/DeoptimizeAllTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/DeoptimizeAllTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -27,19 +27,17 @@ * @library /testlibrary /testlibrary/whitebox * @build DeoptimizeAllTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* DeoptimizeAllTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* DeoptimizeAllTest * @summary testing of WB::deoptimizeAll() * @author igor.ignatyev@oracle.com */ public class DeoptimizeAllTest extends CompilerWhiteBoxTest { public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new DeoptimizeAllTest(test).runTest(); - } + CompilerWhiteBoxTest.main(DeoptimizeAllTest::new, args); } - public DeoptimizeAllTest(TestCase testCase) { + private DeoptimizeAllTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); @@ -53,11 +51,8 @@ */ @Override protected void test() throws Exception { - if (testCase.isOsr && CompilerWhiteBoxTest.MODE.startsWith( - "compiled ")) { - System.err.printf("Warning: %s is not applicable in %s%n", - testCase.name(), CompilerWhiteBoxTest.MODE); - return; + if (skipXcompOSR()) { + return; } compile(); checkCompiled(); diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/DeoptimizeMethodTest.java --- a/test/compiler/whitebox/DeoptimizeMethodTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/DeoptimizeMethodTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -27,19 +27,17 @@ * @library /testlibrary /testlibrary/whitebox * @build DeoptimizeMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* DeoptimizeMethodTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* DeoptimizeMethodTest * @summary testing of WB::deoptimizeMethod() * @author igor.ignatyev@oracle.com */ public class DeoptimizeMethodTest extends CompilerWhiteBoxTest { public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new DeoptimizeMethodTest(test).runTest(); - } + CompilerWhiteBoxTest.main(DeoptimizeMethodTest::new, args); } - public DeoptimizeMethodTest(TestCase testCase) { + private DeoptimizeMethodTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); @@ -53,11 +51,8 @@ */ @Override protected void test() throws Exception { - if (testCase.isOsr && CompilerWhiteBoxTest.MODE.startsWith( - "compiled ")) { - System.err.printf("Warning: %s is not applicable in %s%n", - testCase.name(), CompilerWhiteBoxTest.MODE); - return; + if (skipXcompOSR()) { + return; } compile(); checkCompiled(); diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/EnqueueMethodForCompilationTest.java --- a/test/compiler/whitebox/EnqueueMethodForCompilationTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/EnqueueMethodForCompilationTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,19 +27,17 @@ * @library /testlibrary /testlibrary/whitebox * @build EnqueueMethodForCompilationTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* EnqueueMethodForCompilationTest + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* EnqueueMethodForCompilationTest * @summary testing of WB::enqueueMethodForCompilation() * @author igor.ignatyev@oracle.com */ public class EnqueueMethodForCompilationTest extends CompilerWhiteBoxTest { public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new EnqueueMethodForCompilationTest(test).runTest(); - } + CompilerWhiteBoxTest.main(EnqueueMethodForCompilationTest::new, args); } - public EnqueueMethodForCompilationTest(TestCase testCase) { + private EnqueueMethodForCompilationTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/GetNMethodTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/whitebox/GetNMethodTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 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 + * 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. + * + */ + +import sun.hotspot.code.NMethod; + +/* + * @test GetNMethodTest + * @bug 8038240 + * @library /testlibrary /testlibrary/whitebox + * @build GetNMethodTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* GetNMethodTest + * @summary testing of WB::getNMethod() + * @author igor.ignatyev@oracle.com + */ +public class GetNMethodTest extends CompilerWhiteBoxTest { + public static void main(String[] args) throws Exception { + CompilerWhiteBoxTest.main(GetNMethodTest::new, args); + } + + private GetNMethodTest(TestCase testCase) { + super(testCase); + // to prevent inlining of #method + WHITE_BOX.testSetDontInlineMethod(method, true); + } + + @Override + protected void test() throws Exception { + checkNotCompiled(); + + compile(); + checkCompiled(); + NMethod nmethod = NMethod.get(method, testCase.isOsr()); + if (IS_VERBOSE) { + System.out.println("nmethod = " + nmethod); + } + if (nmethod == null) { + throw new RuntimeException("nmethod of compiled method is null"); + } + if (nmethod.insts.length == 0) { + throw new RuntimeException("compiled method's instructions is empty"); + } + deoptimize(); + checkNotCompiled(); + nmethod = NMethod.get(method, testCase.isOsr()); + if (nmethod != null) { + throw new RuntimeException("nmethod of non-compiled method isn't null"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/IsMethodCompilableTest.java --- a/test/compiler/whitebox/IsMethodCompilableTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/IsMethodCompilableTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,13 +24,17 @@ /* * @test IsMethodCompilableTest * @bug 8007270 8006683 8007288 8022832 - * @library /testlibrary /testlibrary/whitebox + * @library /testlibrary /testlibrary/whitebox /testlibrary/com/oracle/java/testlibrary * @build IsMethodCompilableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* IsMethodCompilableTest + * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform + * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:PerMethodRecompilationCutoff=3 -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* IsMethodCompilableTest * @summary testing of WB::isMethodCompilable() * @author igor.ignatyev@oracle.com */ + +import com.oracle.java.testlibrary.Platform; + public class IsMethodCompilableTest extends CompilerWhiteBoxTest { /** * Value of {@code -XX:PerMethodRecompilationCutoff} @@ -43,17 +47,15 @@ if (tmp == -1) { PER_METHOD_RECOMPILATION_CUTOFF = -1 /* Inf */; } else { - PER_METHOD_RECOMPILATION_CUTOFF = 1 + (0xFFFFFFFFL & tmp); + PER_METHOD_RECOMPILATION_CUTOFF = (0xFFFFFFFFL & tmp); } } public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new IsMethodCompilableTest(test).runTest(); - } + CompilerWhiteBoxTest.main(IsMethodCompilableTest::new, args); } - public IsMethodCompilableTest(TestCase testCase) { + private IsMethodCompilableTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); @@ -62,19 +64,22 @@ /** * Tests {@code WB::isMethodCompilable()} by recompilation of tested method * 'PerMethodRecompilationCutoff' times and checks compilation status. Also - * checks that WB::clearMethodState() clears no-compilable flags. + * checks that WB::clearMethodState() clears no-compilable flags. Only + * applicable to c2 compiled methods. * * @throws Exception if one of the checks fails. */ @Override protected void test() throws Exception { - if (testCase.isOsr && CompilerWhiteBoxTest.MODE.startsWith( - "compiled ")) { - System.err.printf("Warning: %s is not applicable in %s%n", - testCase.name(), CompilerWhiteBoxTest.MODE); + // Only c2 compilations can be disabled through PerMethodRecompilationCutoff + if (!Platform.isServer()) { + return; + } + + if (skipXcompOSR()) { return; } - if (!isCompilable()) { + if (!isCompilable(COMP_LEVEL_FULL_OPTIMIZATION)) { throw new RuntimeException(method + " must be compilable"); } System.out.println("PerMethodRecompilationCutoff = " @@ -85,39 +90,37 @@ return; } - // deoptimize 'PerMethodRecompilationCutoff' times and clear state - for (long i = 0L, n = PER_METHOD_RECOMPILATION_CUTOFF - 1; i < n; ++i) { - compileAndDeoptimize(); + // deoptimize 'PerMethodRecompilationCutoff' times + for (long attempts = 0, successes = 0; + (successes < PER_METHOD_RECOMPILATION_CUTOFF) && + (attempts < PER_METHOD_RECOMPILATION_CUTOFF*2) && + isCompilable(COMP_LEVEL_FULL_OPTIMIZATION); attempts++) { + if (compileAndDeoptimize() == COMP_LEVEL_FULL_OPTIMIZATION) { + successes++; + } } - if (!testCase.isOsr && !isCompilable()) { + + if (!testCase.isOsr() && !isCompilable(COMP_LEVEL_FULL_OPTIMIZATION)) { // in osr test case count of deopt maybe more than iterations throw new RuntimeException(method + " is not compilable after " - + (PER_METHOD_RECOMPILATION_CUTOFF - 1) + " iterations"); + + PER_METHOD_RECOMPILATION_CUTOFF + " iterations"); } - WHITE_BOX.clearMethodState(method); - // deoptimize 'PerMethodRecompilationCutoff' + 1 times - long i; - for (i = 0L; i < PER_METHOD_RECOMPILATION_CUTOFF - && isCompilable(); ++i) { - compileAndDeoptimize(); - } - if (!testCase.isOsr && i != PER_METHOD_RECOMPILATION_CUTOFF) { - // in osr test case count of deopt maybe more than iterations - throw new RuntimeException(method + " is not compilable after " - + i + " iterations, but must only after " - + PER_METHOD_RECOMPILATION_CUTOFF); - } - if (isCompilable()) { + // Now compile once more + compileAndDeoptimize(); + + if (isCompilable(COMP_LEVEL_FULL_OPTIMIZATION)) { throw new RuntimeException(method + " is still compilable after " + PER_METHOD_RECOMPILATION_CUTOFF + " iterations"); } + checkNotCompiled(); compile(); - checkNotCompiled(); + waitBackgroundCompilation(); + checkNotCompiled(COMP_LEVEL_FULL_OPTIMIZATION); // WB.clearMethodState() must reset no-compilable flags WHITE_BOX.clearMethodState(method); - if (!isCompilable()) { + if (!isCompilable(COMP_LEVEL_FULL_OPTIMIZATION)) { throw new RuntimeException(method + " is not compilable after clearMethodState()"); } @@ -125,9 +128,11 @@ checkCompiled(); } - private void compileAndDeoptimize() throws Exception { + private int compileAndDeoptimize() throws Exception { compile(); waitBackgroundCompilation(); + int compLevel = getCompLevel(); deoptimize(); + return compLevel; } } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/MakeMethodNotCompilableTest.java --- a/test/compiler/whitebox/MakeMethodNotCompilableTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/MakeMethodNotCompilableTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -27,26 +27,17 @@ * @library /testlibrary /testlibrary/whitebox * @build MakeMethodNotCompilableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* MakeMethodNotCompilableTest + * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* MakeMethodNotCompilableTest * @summary testing of WB::makeMethodNotCompilable() * @author igor.ignatyev@oracle.com */ public class MakeMethodNotCompilableTest extends CompilerWhiteBoxTest { private int bci; public static void main(String[] args) throws Exception { - if (args.length == 0) { - for (TestCase test : TestCase.values()) { - new MakeMethodNotCompilableTest(test).runTest(); - } - } else { - for (String name : args) { - new MakeMethodNotCompilableTest( - TestCase.valueOf(name)).runTest(); - } - } + CompilerWhiteBoxTest.main(MakeMethodNotCompilableTest::new, args); } - public MakeMethodNotCompilableTest(TestCase testCase) { + private MakeMethodNotCompilableTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); @@ -62,11 +53,8 @@ */ @Override protected void test() throws Exception { - if (testCase.isOsr && CompilerWhiteBoxTest.MODE.startsWith( - "compiled ")) { - System.err.printf("Warning: %s is not applicable in %s%n", - testCase.name(), CompilerWhiteBoxTest.MODE); - return; + if (skipXcompOSR()) { + return; } checkNotCompiled(); if (!isCompilable()) { diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/SetDontInlineMethodTest.java --- a/test/compiler/whitebox/SetDontInlineMethodTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/SetDontInlineMethodTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,19 +27,17 @@ * @library /testlibrary /testlibrary/whitebox * @build SetDontInlineMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* SetDontInlineMethodTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* SetDontInlineMethodTest * @summary testing of WB::testSetDontInlineMethod() * @author igor.ignatyev@oracle.com */ public class SetDontInlineMethodTest extends CompilerWhiteBoxTest { public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new SetDontInlineMethodTest(test).runTest(); - } + CompilerWhiteBoxTest.main(SetDontInlineMethodTest::new, args); } - public SetDontInlineMethodTest(TestCase testCase) { + private SetDontInlineMethodTest(TestCase testCase) { super(testCase); } diff -r 45d7b2c7029d -r 52b4284cb496 test/compiler/whitebox/SetForceInlineMethodTest.java --- a/test/compiler/whitebox/SetForceInlineMethodTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/compiler/whitebox/SetForceInlineMethodTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,19 +27,17 @@ * @library /testlibrary /testlibrary/whitebox * @build SetForceInlineMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,TestCase$Helper::* SetForceInlineMethodTest + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* SetForceInlineMethodTest * @summary testing of WB::testSetForceInlineMethod() * @author igor.ignatyev@oracle.com */ public class SetForceInlineMethodTest extends CompilerWhiteBoxTest { public static void main(String[] args) throws Exception { - for (TestCase test : TestCase.values()) { - new SetForceInlineMethodTest(test).runTest(); - } + CompilerWhiteBoxTest.main(SetForceInlineMethodTest::new, args); } - public SetForceInlineMethodTest(TestCase testCase) { + private SetForceInlineMethodTest(TestCase testCase) { super(testCase); } diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/TestGCLogRotationViaJcmd.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/TestGCLogRotationViaJcmd.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 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 + * 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 TestGCLogRotationViaJcmd.java + * @bug 7090324 + * @summary test for gc log rotation via jcmd + * @library /testlibrary + * @run main/othervm -Xloggc:test.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 TestGCLogRotationViaJcmd + * + */ +import com.oracle.java.testlibrary.*; +import java.io.File; +import java.io.FilenameFilter; + +public class TestGCLogRotationViaJcmd { + + static final File currentDirectory = new File("."); + static final String LOG_FILE_NAME = "test.log"; + static final int NUM_LOGS = 3; + + static FilenameFilter logFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.startsWith(LOG_FILE_NAME); + } + }; + + public static void main(String[] args) throws Exception { + // Grab the pid from the current java process + String pid = Integer.toString(ProcessTools.getProcessId()); + + // Create a JDKToolLauncher + JDKToolLauncher jcmd = JDKToolLauncher.create("jcmd") + .addToolArg(pid) + .addToolArg("GC.rotate_log"); + + for (int times = 1; times < NUM_LOGS; times++) { + // Run jcmd GC.rotate_log + ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand()); + + // Make sure we didn't crash + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + } + + // GC log check + File[] logs = currentDirectory.listFiles(logFilter); + if (logs.length != NUM_LOGS) { + throw new Error("There are only " + logs.length + + " logs instead " + NUM_LOGS); + } + + } + +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/TestVerifySilently.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/TestVerifySilently.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 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 + * 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 TestVerifySilently.java + * @key gc + * @bug 8032771 + * @summary Test silent verification. + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import java.util.ArrayList; +import java.util.Collections; + +class RunSystemGC { + public static void main(String args[]) throws Exception { + System.gc(); + } +} + + +public class TestVerifySilently { + private static String[] getTestJavaOpts() { + String testVmOptsStr = System.getProperty("test.java.opts"); + if (!testVmOptsStr.isEmpty()) { + return testVmOptsStr.split(" "); + } else { + return new String[] {}; + } + } + + private static OutputAnalyzer runTest(boolean verifySilently) throws Exception { + ArrayList vmOpts = new ArrayList(); + + Collections.addAll(vmOpts, getTestJavaOpts()); + Collections.addAll(vmOpts, new String[] {"-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyDuringStartup", + "-XX:+VerifyBeforeGC", + "-XX:+VerifyAfterGC", + "-XX:" + (verifySilently ? "+":"-") + "VerifySilently", + RunSystemGC.class.getName()}); + ProcessBuilder pb = + ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + return output; + } + + + public static void main(String args[]) throws Exception { + + OutputAnalyzer output; + + output = runTest(false); + output.shouldContain("[Verifying"); + output.shouldHaveExitValue(0); + + output = runTest(true); + output.shouldNotContain("[Verifying"); + output.shouldHaveExitValue(0); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/arguments/TestDynMaxHeapFreeRatio.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/arguments/TestDynMaxHeapFreeRatio.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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 + * 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 TestDynMaxHeapFreeRatio + * @bug 8028391 + * @summary Verify that MaxHeapFreeRatio flag is manageable + * @library /testlibrary + * @run main TestDynMaxHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=100 TestDynMaxHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 -XX:-UseAdaptiveSizePolicy TestDynMaxHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 TestDynMaxHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=51 -XX:MaxHeapFreeRatio=52 TestDynMaxHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=75 -XX:MaxHeapFreeRatio=100 TestDynMaxHeapFreeRatio + */ +import com.oracle.java.testlibrary.TestDynamicVMOption; +import com.oracle.java.testlibrary.DynamicVMOptionChecker; + +public class TestDynMaxHeapFreeRatio extends TestDynamicVMOption { + + public static final String MinFreeRatioFlagName = "MinHeapFreeRatio"; + public static final String MaxFreeRatioFlagName = "MaxHeapFreeRatio"; + + public TestDynMaxHeapFreeRatio() { + super(MaxFreeRatioFlagName); + } + + public void test() { + + int minHeapFreeValue = DynamicVMOptionChecker.getIntValue(MinFreeRatioFlagName); + System.out.println(MinFreeRatioFlagName + " = " + minHeapFreeValue); + + testPercentageValues(); + + checkInvalidValue(Integer.toString(minHeapFreeValue - 1)); + checkValidValue(Integer.toString(minHeapFreeValue)); + checkValidValue("100"); + } + + public static void main(String args[]) throws Exception { + new TestDynMaxHeapFreeRatio().test(); + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/arguments/TestDynMinHeapFreeRatio.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/arguments/TestDynMinHeapFreeRatio.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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 + * 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 TestDynMinHeapFreeRatio + * @bug 8028391 + * @summary Verify that MinHeapFreeRatio flag is manageable + * @library /testlibrary + * @run main TestDynMinHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=100 TestDynMinHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 -XX:-UseAdaptiveSizePolicy TestDynMinHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 TestDynMinHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=51 -XX:MaxHeapFreeRatio=52 TestDynMinHeapFreeRatio + * @run main/othervm -XX:MinHeapFreeRatio=75 -XX:MaxHeapFreeRatio=100 TestDynMinHeapFreeRatio + */ +import com.oracle.java.testlibrary.TestDynamicVMOption; +import com.oracle.java.testlibrary.DynamicVMOptionChecker; + +public class TestDynMinHeapFreeRatio extends TestDynamicVMOption { + + public static final String MinFreeRatioFlagName = "MinHeapFreeRatio"; + public static final String MaxFreeRatioFlagName = "MaxHeapFreeRatio"; + + public TestDynMinHeapFreeRatio() { + super(MinFreeRatioFlagName); + } + + public void test() { + int maxHeapFreeValue = DynamicVMOptionChecker.getIntValue(MaxFreeRatioFlagName); + System.out.println(MaxFreeRatioFlagName + " = " + maxHeapFreeValue); + + testPercentageValues(); + + checkInvalidValue(Integer.toString(maxHeapFreeValue + 1)); + checkValidValue(Integer.toString(maxHeapFreeValue)); + checkValidValue("0"); + } + + public static void main(String args[]) throws Exception { + new TestDynMinHeapFreeRatio().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestGCLogMessages.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestGCLogMessages.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 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 + * 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 TestPrintGCDetails + * @bug 8035406 8027295 8035398 + * @summary Ensure that the PrintGCDetails output for a minor GC with G1 + * includes the expected necessary messages. + * @key gc + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.OutputAnalyzer; + +public class TestGCLogMessages { + public static void main(String[] args) throws Exception { + testNormalLogs(); + testWithToSpaceExhaustionLogs(); + } + + private static void testNormalLogs() throws Exception { + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-Xmx10M", + GCTest.class.getName()); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.shouldNotContain("[Redirty Cards"); + output.shouldNotContain("[Code Root Purge"); + output.shouldNotContain("[String Dedup Fixup"); + output.shouldNotContain("[Young Free CSet"); + output.shouldNotContain("[Non-Young Free CSet"); + output.shouldHaveExitValue(0); + + pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-XX:+UseStringDeduplication", + "-Xmx10M", + "-XX:+PrintGCDetails", + GCTest.class.getName()); + + output = new OutputAnalyzer(pb.start()); + + output.shouldContain("[Redirty Cards"); + output.shouldContain("[Code Root Purge"); + output.shouldContain("[String Dedup Fixup"); + output.shouldNotContain("[Young Free CSet"); + output.shouldNotContain("[Non-Young Free CSet"); + output.shouldHaveExitValue(0); + + pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-XX:+UseStringDeduplication", + "-Xmx10M", + "-XX:+PrintGCDetails", + "-XX:+UnlockExperimentalVMOptions", + "-XX:G1LogLevel=finest", + GCTest.class.getName()); + + output = new OutputAnalyzer(pb.start()); + + output.shouldContain("[Redirty Cards"); + output.shouldContain("[Code Root Purge"); + output.shouldContain("[String Dedup Fixup"); + output.shouldContain("[Young Free CSet"); + output.shouldContain("[Non-Young Free CSet"); + + // also check evacuation failure messages once + output.shouldNotContain("[Evacuation Failure"); + output.shouldNotContain("[Recalculate Used"); + output.shouldNotContain("[Remove Self Forwards"); + output.shouldNotContain("[Restore RemSet"); + output.shouldHaveExitValue(0); + } + + private static void testWithToSpaceExhaustionLogs() throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-Xmx10M", + "-Xmn5M", + "-XX:+PrintGCDetails", + GCTestWithToSpaceExhaustion.class.getName()); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("[Evacuation Failure"); + output.shouldNotContain("[Recalculate Used"); + output.shouldNotContain("[Remove Self Forwards"); + output.shouldNotContain("[Restore RemSet"); + output.shouldHaveExitValue(0); + + pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-Xmx10M", + "-Xmn5M", + "-XX:+PrintGCDetails", + "-XX:+UnlockExperimentalVMOptions", + "-XX:G1LogLevel=finest", + GCTestWithToSpaceExhaustion.class.getName()); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[Evacuation Failure"); + output.shouldContain("[Recalculate Used"); + output.shouldContain("[Remove Self Forwards"); + output.shouldContain("[Restore RemSet"); + output.shouldHaveExitValue(0); + } + + static class GCTest { + private static byte[] garbage; + public static void main(String [] args) { + System.out.println("Creating garbage"); + // create 128MB of garbage. This should result in at least one GC + for (int i = 0; i < 1024; i++) { + garbage = new byte[128 * 1024]; + } + System.out.println("Done"); + } + } + + static class GCTestWithToSpaceExhaustion { + private static byte[] garbage; + private static byte[] largeObject; + public static void main(String [] args) { + largeObject = new byte[5*1024*1024]; + System.out.println("Creating garbage"); + // create 128MB of garbage. This should result in at least one GC, + // some of them with to-space exhaustion. + for (int i = 0; i < 1024; i++) { + garbage = new byte[128 * 1024]; + } + System.out.println("Done"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationAgeThreshold.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationAgeThreshold.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationAgeThreshold + * @summary Test string deduplication age threshold + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationAgeThreshold { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testAgeThreshold(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationFullGC.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationFullGC.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationFullGC + * @summary Test string deduplication during full GC + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationFullGC { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testFullGC(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationInterned.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationInterned.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationInterned + * @summary Test string deduplication of interned strings + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationInterned { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testInterned(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationPrintOptions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationPrintOptions.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationPrintOptions + * @summary Test string deduplication print options + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationPrintOptions { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testPrintOptions(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationTableRehash.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationTableRehash.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationTableRehash + * @summary Test string deduplication table rehash + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationTableRehash { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testTableRehash(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationTableResize.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationTableResize.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationTableResize + * @summary Test string deduplication table resize + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationTableResize { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testTableResize(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationTools.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationTools.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,434 @@ +/* + * Copyright (c) 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 + * 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 code for string deduplication tests + */ + +import java.lang.management.*; +import java.lang.reflect.*; +import java.security.*; +import java.util.*; +import com.oracle.java.testlibrary.*; +import sun.misc.*; + +class TestStringDeduplicationTools { + private static final String YoungGC = "YoungGC"; + private static final String FullGC = "FullGC"; + + private static final int Xmn = 50; // MB + private static final int Xms = 100; // MB + private static final int Xmx = 100; // MB + private static final int MB = 1024 * 1024; + private static final int StringLength = 50; + + private static Field valueField; + private static Unsafe unsafe; + private static byte[] dummy; + + static { + try { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + unsafe = (Unsafe)field.get(null); + + valueField = String.class.getDeclaredField("value"); + valueField.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Object getValue(String string) { + try { + return valueField.get(string); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void doFullGc(int numberOfTimes) { + for (int i = 0; i < numberOfTimes; i++) { + System.out.println("Begin: Full GC " + (i + 1) + "/" + numberOfTimes); + System.gc(); + System.out.println("End: Full GC " + (i + 1) + "/" + numberOfTimes); + } + } + + private static void doYoungGc(int numberOfTimes) { + // Provoke at least numberOfTimes young GCs + final int objectSize = 128; + final int maxObjectInYoung = (Xmn * MB) / objectSize; + for (int i = 0; i < numberOfTimes; i++) { + System.out.println("Begin: Young GC " + (i + 1) + "/" + numberOfTimes); + for (int j = 0; j < maxObjectInYoung + 1; j++) { + dummy = new byte[objectSize]; + } + System.out.println("End: Young GC " + (i + 1) + "/" + numberOfTimes); + } + } + + private static void forceDeduplication(int ageThreshold, String gcType) { + // Force deduplication to happen by either causing a FullGC or a YoungGC. + // We do several collections to also provoke a situation where the the + // deduplication thread needs to yield while processing the queue. This + // also tests that the references in the deduplication queue are adjusted + // accordingly. + if (gcType.equals(FullGC)) { + doFullGc(3); + } else { + doYoungGc(ageThreshold + 3); + } + } + + private static String generateString(int id) { + StringBuilder builder = new StringBuilder(StringLength); + + builder.append("DeduplicationTestString:" + id + ":"); + + while (builder.length() < StringLength) { + builder.append('X'); + } + + return builder.toString(); + } + + private static ArrayList createStrings(int total, int unique) { + System.out.println("Creating strings: total=" + total + ", unique=" + unique); + if (total % unique != 0) { + throw new RuntimeException("Total must be divisible by unique"); + } + + ArrayList list = new ArrayList(total); + for (int j = 0; j < total / unique; j++) { + for (int i = 0; i < unique; i++) { + list.add(generateString(i)); + } + } + + return list; + } + + private static void verifyStrings(ArrayList list, int uniqueExpected) { + for (;;) { + // Check number of deduplicated strings + ArrayList unique = new ArrayList(uniqueExpected); + for (String string: list) { + Object value = getValue(string); + boolean uniqueValue = true; + for (Object obj: unique) { + if (obj == value) { + uniqueValue = false; + break; + } + } + + if (uniqueValue) { + unique.add(value); + } + } + + System.out.println("Verifying strings: total=" + list.size() + + ", uniqueFound=" + unique.size() + + ", uniqueExpected=" + uniqueExpected); + + if (unique.size() == uniqueExpected) { + System.out.println("Deduplication completed"); + break; + } else { + System.out.println("Deduplication not completed, waiting..."); + + // Give the deduplication thread time to complete + try { + Thread.sleep(1000); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + + private static OutputAnalyzer runTest(String... extraArgs) throws Exception { + String[] defaultArgs = new String[] { + "-Xmn" + Xmn + "m", + "-Xms" + Xms + "m", + "-Xmx" + Xmx + "m", + "-XX:+UseG1GC", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+VerifyAfterGC" // Always verify after GC + }; + + ArrayList args = new ArrayList(); + args.addAll(Arrays.asList(defaultArgs)); + args.addAll(Arrays.asList(extraArgs)); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args.toArray(new String[args.size()])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.err.println(output.getStderr()); + System.out.println(output.getStdout()); + return output; + } + + private static class DeduplicationTest { + public static void main(String[] args) { + System.out.println("Begin: DeduplicationTest"); + + final int numberOfStrings = Integer.parseUnsignedInt(args[0]); + final int numberOfUniqueStrings = Integer.parseUnsignedInt(args[1]); + final int ageThreshold = Integer.parseUnsignedInt(args[2]); + final String gcType = args[3]; + + ArrayList list = createStrings(numberOfStrings, numberOfUniqueStrings); + forceDeduplication(ageThreshold, gcType); + verifyStrings(list, numberOfUniqueStrings); + + System.out.println("End: DeduplicationTest"); + } + + public static OutputAnalyzer run(int numberOfStrings, int ageThreshold, String gcType, String... extraArgs) throws Exception { + String[] defaultArgs = new String[] { + "-XX:+UseStringDeduplication", + "-XX:StringDeduplicationAgeThreshold=" + ageThreshold, + DeduplicationTest.class.getName(), + "" + numberOfStrings, + "" + numberOfStrings / 2, + "" + ageThreshold, + gcType + }; + + ArrayList args = new ArrayList(); + args.addAll(Arrays.asList(extraArgs)); + args.addAll(Arrays.asList(defaultArgs)); + + return runTest(args.toArray(new String[args.size()])); + } + } + + private static class InternedTest { + public static void main(String[] args) { + // This test verifies that interned strings are always + // deduplicated when being interned, and never after + // being interned. + + System.out.println("Begin: InternedTest"); + + final int ageThreshold = Integer.parseUnsignedInt(args[0]); + final String baseString = "DeduplicationTestString:" + InternedTest.class.getName(); + + // Create duplicate of baseString + StringBuilder sb1 = new StringBuilder(baseString); + String dupString1 = sb1.toString(); + if (getValue(dupString1) == getValue(baseString)) { + throw new RuntimeException("Values should not match"); + } + + // Force baseString to be inspected for deduplication + // and be inserted into the deduplication hashtable. + forceDeduplication(ageThreshold, FullGC); + + // Wait for deduplication to occur + while (getValue(dupString1) != getValue(baseString)) { + System.out.println("Waiting..."); + try { + Thread.sleep(100); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // Create a new duplicate of baseString + StringBuilder sb2 = new StringBuilder(baseString); + String dupString2 = sb2.toString(); + if (getValue(dupString2) == getValue(baseString)) { + throw new RuntimeException("Values should not match"); + } + + // Intern the new duplicate + Object beforeInternedValue = getValue(dupString2); + String internedString = dupString2.intern(); + if (internedString != dupString2) { + throw new RuntimeException("String should match"); + } + if (getValue(internedString) != getValue(baseString)) { + throw new RuntimeException("Values should match"); + } + + // Check original value of interned string, to make sure + // deduplication happened on the interned string and not + // on the base string + if (beforeInternedValue == getValue(baseString)) { + throw new RuntimeException("Values should not match"); + } + + System.out.println("End: InternedTest"); + } + + public static OutputAnalyzer run() throws Exception { + return runTest("-XX:+PrintGC", + "-XX:+PrintGCDetails", + "-XX:+UseStringDeduplication", + "-XX:+PrintStringDeduplicationStatistics", + "-XX:StringDeduplicationAgeThreshold=" + DefaultAgeThreshold, + InternedTest.class.getName(), + "" + DefaultAgeThreshold); + } + } + + /* + * Tests + */ + + private static final int LargeNumberOfStrings = 10000; + private static final int SmallNumberOfStrings = 10; + + private static final int MaxAgeThreshold = 15; + private static final int DefaultAgeThreshold = 3; + private static final int MinAgeThreshold = 1; + + private static final int TooLowAgeThreshold = MinAgeThreshold - 1; + private static final int TooHighAgeThreshold = MaxAgeThreshold + 1; + + public static void testYoungGC() throws Exception { + // Do young GC to age strings to provoke deduplication + OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings, + DefaultAgeThreshold, + YoungGC, + "-XX:+PrintGC", + "-XX:+PrintStringDeduplicationStatistics"); + output.shouldNotContain("Full GC"); + output.shouldContain("GC pause (G1 Evacuation Pause) (young)"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldContain("Deduplicated:"); + output.shouldHaveExitValue(0); + } + + public static void testFullGC() throws Exception { + // Do full GC to age strings to provoke deduplication + OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings, + DefaultAgeThreshold, + FullGC, + "-XX:+PrintGC", + "-XX:+PrintStringDeduplicationStatistics"); + output.shouldNotContain("GC pause (G1 Evacuation Pause) (young)"); + output.shouldContain("Full GC"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldContain("Deduplicated:"); + output.shouldHaveExitValue(0); + } + + public static void testTableResize() throws Exception { + // Test with StringDeduplicationResizeALot + OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings, + DefaultAgeThreshold, + YoungGC, + "-XX:+PrintGC", + "-XX:+PrintStringDeduplicationStatistics", + "-XX:+StringDeduplicationResizeALot"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldContain("Deduplicated:"); + output.shouldNotContain("Resize Count: 0"); + output.shouldHaveExitValue(0); + } + + public static void testTableRehash() throws Exception { + // Test with StringDeduplicationRehashALot + OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings, + DefaultAgeThreshold, + YoungGC, + "-XX:+PrintGC", + "-XX:+PrintStringDeduplicationStatistics", + "-XX:+StringDeduplicationRehashALot"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldContain("Deduplicated:"); + output.shouldNotContain("Rehash Count: 0"); + output.shouldNotContain("Hash Seed: 0x0"); + output.shouldHaveExitValue(0); + } + + public static void testAgeThreshold() throws Exception { + OutputAnalyzer output; + + // Test with max age theshold + output = DeduplicationTest.run(SmallNumberOfStrings, + MaxAgeThreshold, + YoungGC, + "-XX:+PrintGC", + "-XX:+PrintStringDeduplicationStatistics"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldContain("Deduplicated:"); + output.shouldHaveExitValue(0); + + // Test with min age theshold + output = DeduplicationTest.run(SmallNumberOfStrings, + MinAgeThreshold, + YoungGC, + "-XX:+PrintGC", + "-XX:+PrintStringDeduplicationStatistics"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldContain("Deduplicated:"); + output.shouldHaveExitValue(0); + + // Test with too low age threshold + output = DeduplicationTest.run(SmallNumberOfStrings, + TooLowAgeThreshold, + YoungGC); + output.shouldContain("StringDeduplicationAgeThreshold of " + TooLowAgeThreshold + + " is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold); + output.shouldHaveExitValue(1); + + // Test with too high age threshold + output = DeduplicationTest.run(SmallNumberOfStrings, + TooHighAgeThreshold, + YoungGC); + output.shouldContain("StringDeduplicationAgeThreshold of " + TooHighAgeThreshold + + " is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold); + output.shouldHaveExitValue(1); + } + + public static void testPrintOptions() throws Exception { + OutputAnalyzer output; + + // Test without PrintGC and without PrintStringDeduplicationStatistics + output = DeduplicationTest.run(SmallNumberOfStrings, + DefaultAgeThreshold, + YoungGC); + output.shouldNotContain("GC concurrent-string-deduplication"); + output.shouldNotContain("Deduplicated:"); + output.shouldHaveExitValue(0); + + // Test with PrintGC but without PrintStringDeduplicationStatistics + output = DeduplicationTest.run(SmallNumberOfStrings, + DefaultAgeThreshold, + YoungGC, + "-XX:+PrintGC"); + output.shouldContain("GC concurrent-string-deduplication"); + output.shouldNotContain("Deduplicated:"); + output.shouldHaveExitValue(0); + } + + public static void testInterned() throws Exception { + // Test that interned strings are deduplicated before being interned + OutputAnalyzer output = InternedTest.run(); + output.shouldHaveExitValue(0); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringDeduplicationYoungGC.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringDeduplicationYoungGC.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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 + * 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 TestStringDeduplicationYoungGC + * @summary Test string deduplication during young GC + * @bug 8029075 + * @key gc + * @library /testlibrary + */ + +public class TestStringDeduplicationYoungGC { + public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.testYoungGC(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/g1/TestStringSymbolTableStats.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestStringSymbolTableStats.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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 + * 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 TestStringSymbolTableStats.java + * @bug 8027476 8027455 + * @summary Ensure that the G1TraceStringSymbolTableScrubbing prints the expected message. + * @key gc + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.OutputAnalyzer; + +public class TestStringSymbolTableStats { + public static void main(String[] args) throws Exception { + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", + "-XX:+UnlockExperimentalVMOptions", + "-XX:+G1TraceStringSymbolTableScrubbing", + SystemGCTest.class.getName()); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + System.out.println("Output:\n" + output.getOutput()); + + output.shouldContain("Cleaned string and symbol table"); + output.shouldHaveExitValue(0); + } + + static class SystemGCTest { + public static void main(String [] args) { + System.out.println("Calling System.gc()"); + System.gc(); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/gc/metaspace/TestMetaspaceInitialization.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/metaspace/TestMetaspaceInitialization.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.util.ArrayList; + +/* @test TestMetaspaceInitialization + * @bug 8042933 + * @summary Tests to initialize metaspace with a very low MetaspaceSize + * @library /testlibrary + * @run main/othervm -XX:MetaspaceSize=2m TestMetaspaceInitialization + */ +public class TestMetaspaceInitialization { + private class Internal { + public int x; + public Internal(int x) { + this.x = x; + } + } + + private void test() { + ArrayList l = new ArrayList<>(); + l.add(new Internal(17)); + } + + public static void main(String[] args) { + new TestMetaspaceInitialization().test(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/7158988/FieldMonitor.java --- a/test/runtime/7158988/FieldMonitor.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/7158988/FieldMonitor.java Wed Oct 15 16:02:50 2014 +0200 @@ -34,10 +34,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -56,6 +52,7 @@ import com.sun.jdi.event.EventSet; import com.sun.jdi.event.ModificationWatchpointEvent; import com.sun.jdi.event.VMDeathEvent; +import com.sun.jdi.event.VMStartEvent; import com.sun.jdi.event.VMDisconnectEvent; import com.sun.jdi.request.ClassPrepareRequest; import com.sun.jdi.request.EventRequest; @@ -71,24 +68,10 @@ public static void main(String[] args) throws IOException, InterruptedException { - StringBuffer sb = new StringBuffer(); - - for (int i=0; i < args.length; i++) { - sb.append(' '); - sb.append(args[i]); - } //VirtualMachine vm = launchTarget(sb.toString()); VirtualMachine vm = launchTarget(CLASS_NAME); System.out.println("Vm launched"); - // set watch field on already loaded classes - List referenceTypes = vm - .classesByName(CLASS_NAME); - for (ReferenceType refType : referenceTypes) { - addFieldWatch(vm, refType); - } - // watch for loaded classes - addClassWatch(vm); // process events EventQueue eventQueue = vm.eventQueue(); @@ -104,13 +87,15 @@ errThread.start(); outThread.start(); - - vm.resume(); boolean connected = true; + int watched = 0; while (connected) { EventSet eventSet = eventQueue.remove(); for (Event event : eventSet) { - if (event instanceof VMDeathEvent + System.out.println("FieldMonitor-main receives: "+event); + if (event instanceof VMStartEvent) { + addClassWatch(vm); + } else if (event instanceof VMDeathEvent || event instanceof VMDisconnectEvent) { // exit connected = false; @@ -122,17 +107,17 @@ .referenceType(); addFieldWatch(vm, refType); } else if (event instanceof ModificationWatchpointEvent) { + watched++; System.out.println("sleep for 500 ms"); Thread.sleep(500); - System.out.println("resume..."); ModificationWatchpointEvent modEvent = (ModificationWatchpointEvent) event; System.out.println("old=" + modEvent.valueCurrent()); System.out.println("new=" + modEvent.valueToBe()); - System.out.println(); } } + System.out.println("resume..."); eventSet.resume(); } // Shutdown begins when event thread terminates @@ -142,6 +127,10 @@ } catch (InterruptedException exc) { // we don't interrupt } + + if (watched != 11) { // init + 10 modifications in TestPostFieldModification class + throw new Error("Expected to receive 11 times ModificationWatchpointEvent, but got "+watched); + } } /** diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/ClassFile/UnsupportedClassFileVersion.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/ClassFile/UnsupportedClassFileVersion.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 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 + * 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 + * @library /testlibrary + * @compile -XDignore.symbol.file UnsupportedClassFileVersion.java + * @run main UnsupportedClassFileVersion + */ + +import java.io.File; +import java.io.FileOutputStream; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import com.oracle.java.testlibrary.*; + +public class UnsupportedClassFileVersion implements Opcodes { + public static void main(String... args) throws Exception { + writeClassFile(); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "-cp", ".", "ClassFile"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("ClassFile has been compiled by a more recent version of the " + + "Java Runtime (class file version 99.0), this version of " + + "the Java Runtime only recognizes class file versions up to " + + System.getProperty("java.class.version")); + + output.shouldHaveExitValue(1); + } + + public static void writeClassFile() throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(99, ACC_PUBLIC + ACC_SUPER, "ClassFile", null, "java/lang/Object", null); + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + cw.visitEnd(); + + try (FileOutputStream fos = new FileOutputStream(new File("ClassFile.class"))) { + fos.write(cw.toByteArray()); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/CommandLine/CompilerConfigFileWarning.java --- a/test/runtime/CommandLine/CompilerConfigFileWarning.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/CommandLine/CompilerConfigFileWarning.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -33,8 +33,7 @@ public class CompilerConfigFileWarning { public static void main(String[] args) throws Exception { - String vmVersion = System.getProperty("java.vm.version"); - if (vmVersion.toLowerCase().contains("debug") || vmVersion.toLowerCase().contains("jvmg")) { + if (Platform.isDebugBuild()) { System.out.println("Skip on debug builds since we'll always read the file there"); return; } diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/CommandLine/ConfigFileWarning.java --- a/test/runtime/CommandLine/ConfigFileWarning.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/CommandLine/ConfigFileWarning.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -33,8 +33,7 @@ public class ConfigFileWarning { public static void main(String[] args) throws Exception { - String vmVersion = System.getProperty("java.vm.version"); - if (vmVersion.toLowerCase().contains("debug") || vmVersion.toLowerCase().contains("jvmg")) { + if (Platform.isDebugBuild()) { System.out.println("Skip on debug builds since we'll always read the file there"); return; } diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/CommandLine/VMOptionWarning.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/CommandLine/VMOptionWarning.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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 + * 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 8027314 + * @summary Warn if diagnostic or experimental vm option is used and -XX:+UnlockDiagnosticVMOptions or -XX:+UnlockExperimentalVMOptions, respectively, isn't specified. Warn if develop or notproduct vm option is used with product version of VM. + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class VMOptionWarning { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+PredictedLoadedClassCount", "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Error: VM option 'PredictedLoadedClassCount' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions."); + + if (Platform.isDebugBuild()) { + System.out.println("Skip the rest of the tests on debug builds since diagnostic, develop, and notproduct options are available on debug builds."); + return; + } + + pb = ProcessTools.createJavaProcessBuilder("-XX:+PrintInlining", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Error: VM option 'PrintInlining' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); + + pb = ProcessTools.createJavaProcessBuilder("-XX:+TraceJNICalls", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Error: VM option 'TraceJNICalls' is develop and is available only in debug version of VM."); + + pb = ProcessTools.createJavaProcessBuilder("-XX:+TraceJVMCalls", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Error: VM option 'TraceJVMCalls' is notproduct and is available only in debug version of VM."); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/NMT/CommandLineDetail.java --- a/test/runtime/NMT/CommandLineDetail.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/NMT/CommandLineDetail.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,7 +24,7 @@ /* * @test * @key nmt - * @summary Running with NMT detail should not result in an error or warning + * @summary Running with NMT detail should not result in an error * @library /testlibrary */ @@ -39,7 +39,6 @@ "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotContain("error"); - output.shouldNotContain("warning"); output.shouldHaveExitValue(0); } } diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/NMT/CommandLineSummary.java --- a/test/runtime/NMT/CommandLineSummary.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/NMT/CommandLineSummary.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,7 +24,7 @@ /* * @test * @key nmt - * @summary Running with NMT summary should not result in an error or warning + * @summary Running with NMT summary should not result in an error * @library /testlibrary */ @@ -39,7 +39,6 @@ "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotContain("error"); - output.shouldNotContain("warning"); output.shouldHaveExitValue(0); } } diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/NMT/CommandLineTurnOffNMT.java --- a/test/runtime/NMT/CommandLineTurnOffNMT.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/NMT/CommandLineTurnOffNMT.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -24,7 +24,7 @@ /* * @test * @key nmt - * @summary Turning off NMT should not result in an error or warning + * @summary Turning off NMT should not result in an error * @library /testlibrary */ @@ -38,7 +38,6 @@ "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldNotContain("error"); - output.shouldNotContain("warning"); output.shouldHaveExitValue(0); } } diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/NMT/PrintNMTStatistics.java --- a/test/runtime/NMT/PrintNMTStatistics.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/runtime/NMT/PrintNMTStatistics.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -64,7 +64,6 @@ OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldContain("Java Heap (reserved="); output.shouldNotContain("error"); - output.shouldNotContain("warning"); output.shouldHaveExitValue(0); } } diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/PerfMemDestroy/PerfMemDestroy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/PerfMemDestroy/PerfMemDestroy.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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 + * 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 8030955 + * @summary Allow multiple calls to PerfMemory::destroy() without asserting. + * @library /testlibrary + * @run main PerfMemDestroy + */ + +import com.oracle.java.testlibrary.*; + +public class PerfMemDestroy { + public static void main(String args[]) throws Throwable { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+PerfAllowAtExitRegistration", "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/Thread/TestThreadDumpMonitorContention.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/Thread/TestThreadDumpMonitorContention.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,534 @@ +/* + * Copyright (c) 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 + * 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 8036823 + * @bug 8046287 + * @summary Creates two threads contending for the same lock and checks + * whether jstack reports "locked" by more than one thread. + * + * @library /testlibrary + * @run main/othervm TestThreadDumpMonitorContention + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.oracle.java.testlibrary.*; + +public class TestThreadDumpMonitorContention { + // jstack tends to be closely bound to the VM that we are running + // so use getTestJDKTool() instead of getCompileJDKTool() or even + // getJDKTool() which can fall back to "compile.jdk". + final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack"); + final static String PID = getPid(); + + // looking for header lines with these patterns: + // "ContendingThread-1" #19 prio=5 os_prio=64 tid=0x000000000079c000 nid=0x23 runnable [0xffff80ffb8b87000] + // "ContendingThread-2" #21 prio=5 os_prio=64 tid=0x0000000000780000 nid=0x2f waiting for monitor entry [0xfffffd7fc1111000] + // "ContendingThread-2" #24 prio=5 os_prio=64 tid=0x0000000000ec8800 nid=0x31 waiting on condition [0xfffffd7bbfffe000] + final static Pattern HEADER_PREFIX_PATTERN = Pattern.compile( + "^\"ContendingThread-.*"); + final static Pattern HEADER_WAITING_PATTERN1 = Pattern.compile( + "^\"ContendingThread-.* waiting for monitor entry .*"); + final static Pattern HEADER_WAITING_PATTERN2 = Pattern.compile( + "^\"ContendingThread-.* waiting on condition .*"); + final static Pattern HEADER_RUNNABLE_PATTERN = Pattern.compile( + "^\"ContendingThread-.* runnable .*"); + + // looking for thread state lines with these patterns: + // java.lang.Thread.State: RUNNABLE + // java.lang.Thread.State: BLOCKED (on object monitor) + final static Pattern THREAD_STATE_PREFIX_PATTERN = Pattern.compile( + " *java\\.lang\\.Thread\\.State: .*"); + final static Pattern THREAD_STATE_BLOCKED_PATTERN = Pattern.compile( + " *java\\.lang\\.Thread\\.State: BLOCKED \\(on object monitor\\)"); + final static Pattern THREAD_STATE_RUNNABLE_PATTERN = Pattern.compile( + " *java\\.lang\\.Thread\\.State: RUNNABLE"); + + // looking for duplicates of this pattern: + // - locked <0x000000076ac59e20> (a TestThreadDumpMonitorContention$1) + final static Pattern LOCK_PATTERN = Pattern.compile( + ".* locked \\<.*\\(a TestThreadDumpMonitorContention.*"); + + // sanity checking header and thread state lines associated + // with this pattern: + // - waiting to lock <0x000000076ac59e20> (a TestThreadDumpMonitorContention$1) + final static Pattern WAITING_PATTERN = Pattern.compile( + ".* waiting to lock \\<.*\\(a TestThreadDumpMonitorContention.*"); + + final static Object barrier = new Object(); + volatile static boolean done = false; + + static int barrier_cnt = 0; + static int blank_line_match_cnt = 0; + static int error_cnt = 0; + static boolean have_header_line = false; + static boolean have_thread_state_line = false; + static String header_line = null; + static int header_prefix_match_cnt = 0; + static int locked_line_match_cnt = 0; + static String[] locked_match_list = new String[2]; + static int n_samples = 15; + static int sum_both_running_cnt = 0; + static int sum_both_waiting_cnt = 0; + static int sum_contended_cnt = 0; + static int sum_locked_hdr_runnable_cnt = 0; + static int sum_locked_hdr_waiting1_cnt = 0; + static int sum_locked_hdr_waiting2_cnt = 0; + static int sum_locked_thr_state_blocked_cnt = 0; + static int sum_locked_thr_state_runnable_cnt = 0; + static int sum_one_waiting_cnt = 0; + static int sum_uncontended_cnt = 0; + static int sum_waiting_hdr_waiting1_cnt = 0; + static int sum_waiting_thr_state_blocked_cnt = 0; + static String thread_state_line = null; + static boolean verbose = false; + static int waiting_line_match_cnt = 0; + + public static void main(String[] args) throws Exception { + if (args.length != 0) { + int arg_i = 0; + if (args[arg_i].equals("-v")) { + verbose = true; + arg_i++; + } + + try { + n_samples = Integer.parseInt(args[arg_i]); + } catch (NumberFormatException nfe) { + System.err.println(nfe); + usage(); + } + } + + Runnable runnable = new Runnable() { + public void run() { + synchronized (barrier) { + // let the main thread know we're running + barrier_cnt++; + barrier.notify(); + } + while (!done) { + synchronized (this) { } + } + } + }; + Thread[] thread_list = new Thread[2]; + thread_list[0] = new Thread(runnable, "ContendingThread-1"); + thread_list[1] = new Thread(runnable, "ContendingThread-2"); + synchronized (barrier) { + thread_list[0].start(); + thread_list[1].start(); + + // Wait until the contending threads are running so that + // we don't sample any thread init states. + while (barrier_cnt < 2) { + barrier.wait(); + } + } + + doSamples(); + + done = true; + + thread_list[0].join(); + thread_list[1].join(); + + if (error_cnt == 0) { + System.out.println("Test PASSED."); + } else { + System.out.println("Test FAILED."); + throw new AssertionError("error_cnt=" + error_cnt); + } + } + + // Reached a blank line which is the end of the + // stack trace without matching either LOCK_PATTERN + // or WAITING_PATTERN. Rare, but it's not an error. + // + // Example: + // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f runnable [0xfffffd7fc1111000] + // java.lang.Thread.State: RUNNABLE + // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) + // at java.lang.Thread.run(Thread.java:745) + // + static boolean checkBlankLine(String line) { + if (line.length() == 0) { + blank_line_match_cnt++; + have_header_line = false; + have_thread_state_line = false; + return true; + } + + return false; + } + + // Process the locked line here if we found one. + // + // Example 1: + // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f runnable [0xfffffd7fc1111000] + // java.lang.Thread.State: RUNNABLE + // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) + // - locked <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1) + // at java.lang.Thread.run(Thread.java:745) + // + // Example 2: + // "ContendingThread-1" #21 prio=5 os_prio=64 tid=0x00000000007b9000 nid=0x2f waiting for monitor entry [0xfffffd7fc1111000] + // java.lang.Thread.State: BLOCKED (on object monitor) + // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) + // - locked <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1) + // at java.lang.Thread.run(Thread.java:745) + // + // Example 3: + // "ContendingThread-2" #24 prio=5 os_prio=64 tid=0x0000000000ec8800 nid=0x31 waiting on condition [0xfffffd7bbfffe000] + // java.lang.Thread.State: RUNNABLE + // JavaThread state: _thread_blocked + // Thread: 0x0000000000ec8800 [0x31] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0 + // JavaThread state: _thread_blocked + // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) + // - locked <0xfffffd7e6d03eb28> (a TestThreadDumpMonitorContention$1) + // at java.lang.Thread.run(Thread.java:745) + // + static boolean checkLockedLine(String line) { + Matcher matcher = LOCK_PATTERN.matcher(line); + if (matcher.matches()) { + if (verbose) { + System.out.println("locked_line='" + line + "'"); + } + locked_match_list[locked_line_match_cnt] = new String(line); + locked_line_match_cnt++; + + matcher = HEADER_RUNNABLE_PATTERN.matcher(header_line); + if (matcher.matches()) { + sum_locked_hdr_runnable_cnt++; + } else { + // It's strange, but a locked line can also + // match the HEADER_WAITING_PATTERN{1,2}. + matcher = HEADER_WAITING_PATTERN1.matcher(header_line); + if (matcher.matches()) { + sum_locked_hdr_waiting1_cnt++; + } else { + matcher = HEADER_WAITING_PATTERN2.matcher(header_line); + if (matcher.matches()) { + sum_locked_hdr_waiting2_cnt++; + } else { + System.err.println(); + System.err.println("ERROR: header line does " + + "not match runnable or waiting patterns."); + System.err.println("ERROR: header_line='" + + header_line + "'"); + System.err.println("ERROR: locked_line='" + line + + "'"); + error_cnt++; + } + } + } + + matcher = THREAD_STATE_RUNNABLE_PATTERN.matcher(thread_state_line); + if (matcher.matches()) { + sum_locked_thr_state_runnable_cnt++; + } else { + // It's strange, but a locked line can also + // match the THREAD_STATE_BLOCKED_PATTERN. + matcher = THREAD_STATE_BLOCKED_PATTERN.matcher( + thread_state_line); + if (matcher.matches()) { + sum_locked_thr_state_blocked_cnt++; + } else { + System.err.println(); + System.err.println("ERROR: thread state line does not " + + "match runnable or waiting patterns."); + System.err.println("ERROR: " + "thread_state_line='" + + thread_state_line + "'"); + System.err.println("ERROR: locked_line='" + line + "'"); + error_cnt++; + } + } + + // Have everything we need from this thread stack + // that matches the LOCK_PATTERN. + have_header_line = false; + have_thread_state_line = false; + return true; + } + + return false; + } + + // Process the waiting line here if we found one. + // + // Example: + // "ContendingThread-2" #22 prio=5 os_prio=64 tid=0x00000000007b9800 nid=0x30 waiting for monitor entry [0xfffffd7fc1010000] + // java.lang.Thread.State: BLOCKED (on object monitor) + // at TestThreadDumpMonitorContention$1.run(TestThreadDumpMonitorContention.java:140) + // - waiting to lock <0xfffffd7e6a2912f8> (a TestThreadDumpMonitorContention$1) + // at java.lang.Thread.run(Thread.java:745) + // + static boolean checkWaitingLine(String line) { + Matcher matcher = WAITING_PATTERN.matcher(line); + if (matcher.matches()) { + waiting_line_match_cnt++; + if (verbose) { + System.out.println("waiting_line='" + line + "'"); + } + + matcher = HEADER_WAITING_PATTERN1.matcher(header_line); + if (matcher.matches()) { + sum_waiting_hdr_waiting1_cnt++; + } else { + System.err.println(); + System.err.println("ERROR: header line does " + + "not match a waiting pattern."); + System.err.println("ERROR: header_line='" + header_line + "'"); + System.err.println("ERROR: waiting_line='" + line + "'"); + error_cnt++; + } + + matcher = THREAD_STATE_BLOCKED_PATTERN.matcher(thread_state_line); + if (matcher.matches()) { + sum_waiting_thr_state_blocked_cnt++; + } else { + System.err.println(); + System.err.println("ERROR: thread state line " + + "does not match a waiting pattern."); + System.err.println("ERROR: thread_state_line='" + + thread_state_line + "'"); + System.err.println("ERROR: waiting_line='" + line + "'"); + error_cnt++; + } + + // Have everything we need from this thread stack + // that matches the WAITING_PATTERN. + have_header_line = false; + have_thread_state_line = false; + return true; + } + + return false; + } + + static void doSamples() throws Exception { + for (int count = 0; count < n_samples; count++) { + blank_line_match_cnt = 0; + header_prefix_match_cnt = 0; + locked_line_match_cnt = 0; + waiting_line_match_cnt = 0; + // verbose mode or an error has a lot of output so add more space + if (verbose || error_cnt > 0) System.out.println(); + System.out.println("Sample #" + count); + + // We don't use the ProcessTools, OutputBuffer or + // OutputAnalyzer classes from the testlibrary because + // we have a complicated multi-line parse to perform + // on a narrow subset of the JSTACK output. + // + // - we only care about stack traces that match + // HEADER_PREFIX_PATTERN; only two should match + // - we care about at most three lines from each stack trace + // - if both stack traces match LOCKED_PATTERN, then that's + // a failure and we report it + // - for a stack trace that matches LOCKED_PATTERN, we verify: + // - the header line matches HEADER_RUNNABLE_PATTERN + // or HEADER_WAITING_PATTERN{1,2} + // - the thread state line matches THREAD_STATE_BLOCKED_PATTERN + // or THREAD_STATE_RUNNABLE_PATTERN + // - we report any mismatches as failures + // - for a stack trace that matches WAITING_PATTERN, we verify: + // - the header line matches HEADER_WAITING_PATTERN1 + // - the thread state line matches THREAD_STATE_BLOCKED_PATTERN + // - we report any mismatches as failures + // - the stack traces that match HEADER_PREFIX_PATTERN may + // not match either LOCKED_PATTERN or WAITING_PATTERN + // because we might observe the thread outside of + // monitor operations; this is not considered a failure + // + // When we do observe LOCKED_PATTERN or WAITING_PATTERN, + // then we are checking the header and thread state patterns + // that occurred earlier in the current stack trace that + // matched HEADER_PREFIX_PATTERN. We don't use data from + // stack traces that don't match HEADER_PREFIX_PATTERN and + // we don't mix data between the two stack traces that do + // match HEADER_PREFIX_PATTERN. + // + Process process = new ProcessBuilder(JSTACK, PID) + .redirectErrorStream(true).start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader( + process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + Matcher matcher = null; + + // process the header line here + if (!have_header_line) { + matcher = HEADER_PREFIX_PATTERN.matcher(line); + if (matcher.matches()) { + header_prefix_match_cnt++; + if (verbose) { + System.out.println(); + System.out.println("header='" + line + "'"); + } + header_line = new String(line); + have_header_line = true; + continue; + } + continue; // skip until have a header line + } + + // process the thread state line here + if (!have_thread_state_line) { + matcher = THREAD_STATE_PREFIX_PATTERN.matcher(line); + if (matcher.matches()) { + if (verbose) { + System.out.println("thread_state='" + line + "'"); + } + thread_state_line = new String(line); + have_thread_state_line = true; + continue; + } + continue; // skip until we have a thread state line + } + + // process the locked line here if we find one + if (checkLockedLine(line)) { + continue; + } + + // process the waiting line here if we find one + if (checkWaitingLine(line)) { + continue; + } + + // process the blank line here if we find one + if (checkBlankLine(line)) { + continue; + } + } + process.waitFor(); + + if (header_prefix_match_cnt != 2) { + System.err.println(); + System.err.println("ERROR: should match exactly two headers."); + System.err.println("ERROR: header_prefix_match_cnt=" + + header_prefix_match_cnt); + error_cnt++; + } + + if (locked_line_match_cnt == 2) { + if (locked_match_list[0].equals(locked_match_list[1])) { + System.err.println(); + System.err.println("ERROR: matching lock lines:"); + System.err.println("ERROR: line[0]'" + + locked_match_list[0] + "'"); + System.err.println("ERROR: line[1]'" + + locked_match_list[1] + "'"); + error_cnt++; + } + } + + if (locked_line_match_cnt == 1) { + // one thread has the lock + if (waiting_line_match_cnt == 1) { + // and the other contended for it + sum_contended_cnt++; + } else { + // and the other is just running + sum_uncontended_cnt++; + } + } else if (waiting_line_match_cnt == 1) { + // one thread is waiting + sum_one_waiting_cnt++; + } else if (waiting_line_match_cnt == 2) { + // both threads are waiting + sum_both_waiting_cnt++; + } else { + // both threads are running + sum_both_running_cnt++; + } + + // slight delay between jstack launches + Thread.sleep(500); + } + + if (error_cnt != 0) { + // skip summary info since there were errors + return; + } + + System.out.println("INFO: Summary for all samples:"); + System.out.println("INFO: both_running_cnt=" + sum_both_running_cnt); + System.out.println("INFO: both_waiting_cnt=" + sum_both_waiting_cnt); + System.out.println("INFO: contended_cnt=" + sum_contended_cnt); + System.out.println("INFO: one_waiting_cnt=" + sum_one_waiting_cnt); + System.out.println("INFO: uncontended_cnt=" + sum_uncontended_cnt); + System.out.println("INFO: locked_hdr_runnable_cnt=" + + sum_locked_hdr_runnable_cnt); + System.out.println("INFO: locked_hdr_waiting1_cnt=" + + sum_locked_hdr_waiting1_cnt); + System.out.println("INFO: locked_hdr_waiting2_cnt=" + + sum_locked_hdr_waiting2_cnt); + System.out.println("INFO: locked_thr_state_blocked_cnt=" + + sum_locked_thr_state_blocked_cnt); + System.out.println("INFO: locked_thr_state_runnable_cnt=" + + sum_locked_thr_state_runnable_cnt); + System.out.println("INFO: waiting_hdr_waiting1_cnt=" + + sum_waiting_hdr_waiting1_cnt); + System.out.println("INFO: waiting_thr_state_blocked_cnt=" + + sum_waiting_thr_state_blocked_cnt); + + if (sum_contended_cnt == 0) { + System.err.println("WARNING: the primary scenario for 8036823" + + " has not been exercised by this test run."); + } + } + + // This helper relies on RuntimeMXBean.getName() returning a string + // that looks like this: 5436@mt-haku + // + // The testlibrary has tryFindJvmPid(), but that uses a separate + // process which is much more expensive for finding out your own PID. + // + static String getPid() { + RuntimeMXBean runtimebean = ManagementFactory.getRuntimeMXBean(); + String vmname = runtimebean.getName(); + int i = vmname.indexOf('@'); + if (i != -1) { + vmname = vmname.substring(0, i); + } + return vmname; + } + + static void usage() { + System.err.println("Usage: " + + "java TestThreadDumpMonitorContention [-v] [n_samples]"); + System.exit(1); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/classFileParserBug/TestEmptyBootstrapMethodsAttr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/classFileParserBug/TestEmptyBootstrapMethodsAttr.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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 + * 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 TestEmptyBootstrapMethodsAttr + * @bug 8041918 + * @library /testlibrary + * @summary Test empty bootstrap_methods table within BootstrapMethods attribute + * @compile TestEmptyBootstrapMethodsAttr.java + * @run main TestEmptyBootstrapMethodsAttr + */ + +import java.io.File; +import com.oracle.java.testlibrary.*; + +public class TestEmptyBootstrapMethodsAttr { + + public static void main(String args[]) throws Throwable { + System.out.println("Regression test for bug 8041918"); + String jarFile = System.getProperty("test.src") + File.separator + "emptynumbootstrapmethods.jar"; + + // ====== extract the test case + ProcessBuilder pb = new ProcessBuilder(new String[] { JDKToolFinder.getJDKTool("jar"), "xvf", jarFile } ); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + // Test case #1: + // Try loading class with empty bootstrap_methods table where no + // other attributes are following BootstrapMethods in attribute table. + String className = "emptynumbootstrapmethods1"; + + // ======= execute test case #1 + // Expect a lack of main method, this implies that the class loaded correctly + // with an empty bootstrap_methods and did not generate a ClassFormatError. + pb = ProcessTools.createJavaProcessBuilder("-cp", ".", className); + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("java.lang.ClassFormatError"); + output.shouldContain("Main method not found in class " + className); + output.shouldHaveExitValue(1); + + // Test case #2: + // Try loading class with empty bootstrap_methods table where an + // AnnotationDefault attribute follows the BootstrapMethods in the attribute table. + className = "emptynumbootstrapmethods2"; + + // ======= execute test case #2 + // Expect a lack of main method, this implies that the class loaded correctly + // with an empty bootstrap_methods and did not generate ClassFormatError. + pb = ProcessTools.createJavaProcessBuilder("-cp", ".", className); + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("java.lang.ClassFormatError"); + output.shouldContain("Main method not found in class " + className); + output.shouldHaveExitValue(1); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/classFileParserBug/emptynumbootstrapmethods.jar Binary file test/runtime/classFileParserBug/emptynumbootstrapmethods.jar has changed diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/classFileParserBug/emptynumbootstrapmethods1.jcod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/classFileParserBug/emptynumbootstrapmethods1.jcod Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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 + * 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 test contains a BootstrapMethods attribute with an empty + * bootstrap_methods table. This yields a BootstrapMethods + * attribute length of 2 and should not cause a + * java.lang.ClassFormatError to be thrown. + */ +class emptynumbootstrapmethods1 { + 0xCAFEBABE; + 0; // minor version + 51; // version + [12] { // Constant Pool + ; // first element is empty + class #2; // #1 at 0x0A + Utf8 "emptynumbootstrapmethods1"; // #2 at 0x0D + class #4; // #3 at 0x1F + Utf8 "java/lang/Object"; // #4 at 0x22 + MethodHandle 5b #9; // #5 at 0x35 + NameAndType #7 #8; // #6 at 0x39 + Utf8 "equals"; // #7 at 0x3E + Utf8 "(Ljava/lang/Object;)Z"; // #8 at 0x47 + Method #3 #6; // #9 at 0x5F + Utf8 "equalsx"; // #10 at 0x3E + Utf8 "BootstrapMethods"; // #11 at 0x69 + } // Constant Pool + + 0x0001; // access + #1;// this_cpx + #3;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // fields + } // fields + + [0] { // methods + } // methods + + [1] { // Attributes + Attr(#11, 2) { // BootstrapMethods at 0x8A + [0] { // bootstrap_methods + } + } // end BootstrapMethods + } // Attributes +} // end class atrbsm00101m10p diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/classFileParserBug/emptynumbootstrapmethods2.jcod --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/classFileParserBug/emptynumbootstrapmethods2.jcod Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 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 + * 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 test contains a BootstrapMethods attribute with an empty + * bootstrap_methods table. This yields a BootstrapMethods + * attribute length of 2 and should not cause a + * java.lang.ClassFormatError to be thrown. To ensure that an empty + * bootstrap_methods table is parsed correctly, another attribute, + * AnnotationDefault, follows the BootstrapMethods attribute in + * the attribute table. + */ + +class emptynumbootstrapmethods2 { + 0xCAFEBABE; + 0; // minor version + 51; // version + [14] { // Constant Pool + ; // first element is empty + class #2; // #1 at 0x0A + Utf8 "emptynumbootstrapmethods2"; // #2 at 0x0D + class #4; // #3 at 0x1F + Utf8 "java/lang/Object"; // #4 at 0x22 + MethodHandle 5b #9; // #5 at 0x35 + NameAndType #7 #8; // #6 at 0x39 + Utf8 "equals"; // #7 at 0x3E + Utf8 "(Ljava/lang/Object;)Z"; // #8 at 0x47 + Method #3 #6; // #9 at 0x5F + Utf8 "equalsx"; // #10 at 0x3E + Utf8 "BootstrapMethods"; // #11 at 0x69 + Utf8 "AnnotationDefault"; // #12 + Utf8 "LAnnotationDefaultI;"; // #13 + } // Constant Pool + + 0x0001; // access + #1;// this_cpx + #3;// super_cpx + + [0] { // Interfaces + } // Interfaces + + [0] { // fields + } // fields + + [0] { // methods + } // methods + + [2] { // Attributes + Attr(#11, 2) { // BootstrapMethods at 0x8A + [0] { // bootstrap_methods + } + } // end BootstrapMethods + ; + Attr(#12) { // AnnotationDefault + [] { // type annotations + { // type annotation + 0x00; // target_type + 0x00; // type_parameter_index + []b { // type_path + } + + #13; // type_index + [] { // element_value_pairs + } // element_value_pairs + } // type annotation + } // type annotations + } // end AnnotationDefault + } // Attributes +} // end class atrbsm00101m10p diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/finalStatic/FinalStatic.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/finalStatic/FinalStatic.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 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 + * 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 8028553 + * @summary Test that VerifyError is not thrown when 'overriding' a static method. + * @run main FinalStatic + */ + +import java.lang.reflect.*; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; + +/* + * class A { static final int m() {return FAIL; } } + * class B extends A { int m() { return PASS; } } + * class FinalStatic { + * public static void main () { + * Object b = new B(); + * b.m(); + * } + * } + */ +public class FinalStatic { + + static final String CLASS_NAME_A = "A"; + static final String CLASS_NAME_B = "B"; + static final int FAILED = 0; + static final int EXPECTED = 1234; + + static class TestClassLoader extends ClassLoader implements Opcodes { + + @Override + public Class findClass(String name) throws ClassNotFoundException { + byte[] b; + try { + b = loadClassData(name); + } catch (Throwable th) { + // th.printStackTrace(); + throw new ClassNotFoundException("Loading error", th); + } + return defineClass(name, b, 0, b.length); + } + + private byte[] loadClassData(String name) throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + switch (name) { + case CLASS_NAME_A: + cw.visit(52, ACC_SUPER | ACC_PUBLIC, CLASS_NAME_A, null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + + mv = cw.visitMethod(ACC_FINAL | ACC_STATIC, "m", "()I", null, null); + mv.visitCode(); + mv.visitLdcInsn(FAILED); + mv.visitInsn(IRETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + break; + case CLASS_NAME_B: + cw.visit(52, ACC_SUPER | ACC_PUBLIC, CLASS_NAME_B, null, CLASS_NAME_A, null); + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, CLASS_NAME_A, "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + + mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null); + mv.visitCode(); + mv.visitLdcInsn(EXPECTED); + mv.visitInsn(IRETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + + } + break; + default: + break; + } + cw.visitEnd(); + + return cw.toByteArray(); + } + } + + public static void main(String[] args) throws Exception { + TestClassLoader tcl = new TestClassLoader(); + Class a = tcl.loadClass(CLASS_NAME_A); + Class b = tcl.loadClass(CLASS_NAME_B); + Object inst = b.newInstance(); + Method[] meths = b.getDeclaredMethods(); + + Method m = meths[0]; + int mod = m.getModifiers(); + if ((mod & Modifier.FINAL) != 0) { + throw new Exception("FAILED: " + m + " is FINAL"); + } + if ((mod & Modifier.STATIC) != 0) { + throw new Exception("FAILED: " + m + " is STATIC"); + } + + m.setAccessible(true); + if (!m.invoke(inst).equals(EXPECTED)) { + throw new Exception("FAILED: " + EXPECTED + " from " + m); + } + + System.out.println("Passed."); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/lambda-features/InvokespecialInterface.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/lambda-features/InvokespecialInterface.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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 + * 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 8032024 + * @bug 8025937 + * @bug 8033528 + * @summary [JDK 8] Test invokespecial and invokeinterface with the same JVM_CONSTANT_InterfaceMethodref + * @run main/othervm -XX:+StressRewriter InvokespecialInterface + */ +import java.util.function.*; +import java.util.*; + +interface I { + default void imethod() { System.out.println("I::imethod"); } +} + +class C implements I { + public void foo() { I.super.imethod(); } // invokespecial InterfaceMethod + public void bar() { I i = this; i.imethod(); } // invokeinterface same + public void doSomeInvokedynamic() { + String str = "world"; + Supplier foo = ()->"hello, "+str; + String res = foo.get(); + System.out.println(res); + } +} + +public class InvokespecialInterface { + public static void main(java.lang.String[] unused) { + // need to create C and call I::foo() + C c = new C(); + c.foo(); + c.bar(); + c.doSomeInvokedynamic(); + } +}; + + diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/lambda-features/TestConcreteClassWithAbstractMethod.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/lambda-features/TestConcreteClassWithAbstractMethod.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 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 + * 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 8032010 + * @summary method lookup on an abstract method in a concrete class should be successful + * @run main TestConcreteClassWithAbstractMethod + */ + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +/* + * class T1 { public int m() {} } + * class T2 { public abstract int m(); } + * class T3 { public int m() {} } + * + * Call site: T3.test() { invokevirtual T2.m() } + * T3.m() should be invoked + */ +public class TestConcreteClassWithAbstractMethod { + static final String classT1 = "p1.T1"; + static final String classT2 = "p1.T2"; + static final String classT3 = "p1.T3"; + + static final String callerName = classT3; + + public static void main(String[] args) throws Exception { + ClassLoader cl = new ClassLoader() { + public Class loadClass(String name) throws ClassNotFoundException { + if (findLoadedClass(name) != null) { + return findLoadedClass(name); + } + + if (classT1.equals(name)) { + byte[] classFile = dumpT1(); + return defineClass(classT1, classFile, 0, classFile.length); + } + if (classT2.equals(name)) { + byte[] classFile = dumpT2(); + return defineClass(classT2, classFile, 0, classFile.length); + } + if (classT3.equals(name)) { + byte[] classFile = dumpT3(); + return defineClass(classT3, classFile, 0, classFile.length); + } + + return super.loadClass(name); + } + }; + + cl.loadClass(classT1); + cl.loadClass(classT2); + cl.loadClass(classT3); + + //cl.loadClass(callerName).getDeclaredMethod("m"); + cl.loadClass(callerName).newInstance(); + + int result = (Integer)cl.loadClass(callerName).getDeclaredMethod("test").invoke(null); + System.out.println(""+result); + } + + public static byte[] dumpT1() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(52, ACC_PUBLIC | ACC_SUPER, "p1/T1", null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("p1/T1.m()"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V", false); + mv.visitIntInsn(BIPUSH, 3); + mv.visitInsn(IRETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpT2() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(52, ACC_PUBLIC | ACC_SUPER, "p1/T2", null, "p1/T1", null); + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "p1/T1", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpT3() { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(52, ACC_PUBLIC + ACC_SUPER, "p1/T3", null, "p1/T2", null); + + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "p1/T2", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null); + mv.visitCode(); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("p1/T3.m()"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/String;)V", false); + mv.visitIntInsn(BIPUSH, 2); + mv.visitInsn(IRETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "test", "()I", null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, "p1/T3"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "p1/T3", "", "()V", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "p1/T2", "m", "()I", false); + mv.visitInsn(IRETURN); + mv.visitMaxs(3, 2); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/runtime/verifier/TestMultiANewArray.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/verifier/TestMultiANewArray.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.io.File; +import java.io.FileOutputStream; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import com.oracle.java.testlibrary.*; + +/* + * @test TestMultiANewArray + * @bug 8038076 + * @library /testlibrary + * @compile -XDignore.symbol.file TestMultiANewArray.java + * @run main/othervm TestMultiANewArray 49 + * @run main/othervm TestMultiANewArray 50 + * @run main/othervm TestMultiANewArray 51 + * @run main/othervm TestMultiANewArray 52 + */ + +public class TestMultiANewArray { + public static void main(String... args) throws Exception { + int cfv = Integer.parseInt(args[0]); + writeClassFile(cfv); + System.err.println("Running with cfv: " + cfv); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "-cp", ".", "ClassFile"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("VerifyError"); + output.shouldHaveExitValue(1); + } + + public static void writeClassFile(int cfv) throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + cw.visit(cfv, ACC_PUBLIC + ACC_SUPER, "ClassFile", null, "java/lang/Object", null); + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); + mv.visitCode(); + mv.visitInsn(ICONST_1); + mv.visitInsn(ICONST_2); + mv.visitMultiANewArrayInsn("[I", 2); + mv.visitVarInsn(ASTORE, 1); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + + cw.visitEnd(); + + try (FileOutputStream fos = new FileOutputStream(new File("ClassFile.class"))) { + fos.write(cw.toByteArray()); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/ParserTest.java --- a/test/serviceability/ParserTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/serviceability/ParserTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -22,10 +22,10 @@ */ /* - * @test ParserTest + * @test * @summary Test that the diagnostic command arguemnt parser works * @library /testlibrary /testlibrary/whitebox - * @build ParserTest + * @build ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.parser.* * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ParserTest */ diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/attach/AttachWithStalePidFile.java --- a/test/serviceability/attach/AttachWithStalePidFile.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/serviceability/attach/AttachWithStalePidFile.java Wed Oct 15 16:02:50 2014 +0200 @@ -27,7 +27,7 @@ * @key regression * @summary Regression test for attach issue where stale pid files in /tmp lead to connection issues * @library /testlibrary - * @compile AttachWithStalePidFileTarget.java + * @build com.oracle.java.testlibrary.* AttachWithStalePidFileTarget * @run main AttachWithStalePidFile */ diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/jvmti/GetObjectSizeOverflow.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/GetObjectSizeOverflow.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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 + * 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. + */ +import java.io.PrintWriter; +import com.oracle.java.testlibrary.*; + +/* + * Test to verify GetObjectSize does not overflow on a 600M element int[] + * + * @test + * @bug 8027230 + * @library /testlibrary + * @build ClassFileInstaller com.oracle.java.testlibrary.* GetObjectSizeOverflowAgent + * @run main ClassFileInstaller GetObjectSizeOverflowAgent + * @run main GetObjectSizeOverflow + */ +public class GetObjectSizeOverflow { + public static void main(String[] args) throws Exception { + + if (!Platform.is64bit()) { + System.out.println("Test needs a 4GB heap and can only be run as a 64bit process, skipping."); + return; + } + + PrintWriter pw = new PrintWriter("MANIFEST.MF"); + pw.println("Premain-Class: GetObjectSizeOverflowAgent"); + pw.close(); + + ProcessBuilder pb = new ProcessBuilder(); + pb.command(new String[] { JDKToolFinder.getJDKTool("jar"), "cmf", "MANIFEST.MF", "agent.jar", "GetObjectSizeOverflowAgent.class"}); + pb.start().waitFor(); + + ProcessBuilder pt = ProcessTools.createJavaProcessBuilder(true, "-Xmx4000m", "-javaagent:agent.jar", "GetObjectSizeOverflowAgent"); + OutputAnalyzer output = new OutputAnalyzer(pt.start()); + + if (output.getStdout().contains("Could not reserve enough space") || output.getStderr().contains("java.lang.OutOfMemoryError")) { + System.out.println("stdout: " + output.getStdout()); + System.out.println("stderr: " + output.getStderr()); + System.out.println("Test could not reserve or allocate enough space, skipping"); + return; + } + + output.stdoutShouldContain("GetObjectSizeOverflow passed"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/jvmti/GetObjectSizeOverflowAgent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/GetObjectSizeOverflowAgent.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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 + * 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. + */ +import java.lang.instrument.*; + +public class GetObjectSizeOverflowAgent { + + static Instrumentation instrumentation; + + public static void premain(String agentArgs, Instrumentation instrumentation) { + GetObjectSizeOverflowAgent.instrumentation = instrumentation; + } + + public static void main(String[] args) throws Exception { + int[] a = new int[600_000_000]; + long size = instrumentation.getObjectSize(a); + + if (size < 2_400_000_000L) { + throw new RuntimeException("Invalid size of array, expected >= 2400000000, got " + size); + } + + System.out.println("GetObjectSizeOverflow passed"); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/jvmti/TestRedefineWithUnresolvedClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/TestRedefineWithUnresolvedClass.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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 + * 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 + * @summary Redefine a class with an UnresolvedClass reference in the constant pool. + * @bug 8035150 + * @library /testlibrary + * @build com.oracle.java.testlibrary.* UnresolvedClassAgent + * @run main TestRedefineWithUnresolvedClass + */ + +import java.io.File; +import java.util.Arrays; + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +public class TestRedefineWithUnresolvedClass { + + final static String slash = File.separator; + final static String testClasses = System.getProperty("test.classes") + slash; + + public static void main(String... args) throws Throwable { + // delete this class to cause a NoClassDefFoundError + File unresolved = new File(testClasses, "MyUnresolvedClass.class"); + if (unresolved.exists() && !unresolved.delete()) { + throw new Exception("Could not delete: " + unresolved); + } + + // build the javaagent + buildJar("UnresolvedClassAgent"); + + // launch a VM with the javaagent + launchTest(); + } + + private static void buildJar(String jarName) throws Throwable { + String testSrc = System.getProperty("test.src", "?") + slash; + + String jarPath = String.format("%s%s.jar", testClasses, jarName); + String manifestPath = String.format("%s%s.mf", testSrc, jarName); + String className = String.format("%s.class", jarName); + + String[] args = new String[] {"-cfm", jarPath, manifestPath, "-C", testClasses, className}; + + System.out.println("Running jar " + Arrays.toString(args)); + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(args)) { + throw new Exception("jar failed: args=" + Arrays.toString(args)); + } + } + + private static void launchTest() throws Throwable { + String[] args = { + "-javaagent:" + testClasses + "UnresolvedClassAgent.jar", + "-Dtest.classes=" + testClasses, + "UnresolvedClassAgent" }; + OutputAnalyzer output = ProcessTools.executeTestJvm(args); + output.shouldHaveExitValue(0); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/jvmti/UnresolvedClassAgent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/UnresolvedClassAgent.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 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 + * 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. + */ + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; + +/* + * This class is present during compilation, but will be deleted before execution. + */ +class MyUnresolvedClass { + static void bar() { + } +} + +class MyRedefinedClass { + static void foo() { + MyUnresolvedClass.bar(); + } +} + +public class UnresolvedClassAgent { + public static void main(String... args) { + } + + public static void premain(String args, Instrumentation inst) throws Exception { + try { + MyRedefinedClass.foo(); + } catch(NoClassDefFoundError err) { + System.out.println("NoClassDefFoundError (expected)"); + } + + File f = new File(System.getProperty("test.classes"), "MyRedefinedClass.class"); + byte[] buf = new byte[(int)f.length()]; + try (DataInputStream dis = new DataInputStream(new FileInputStream(f))) { + dis.readFully(buf); + } + ClassDefinition cd = new ClassDefinition(MyRedefinedClass.class, buf); + inst.redefineClasses(new ClassDefinition[] {cd}); + + try { + MyRedefinedClass.foo(); + } catch(NoClassDefFoundError err) { + System.out.println("NoClassDefFoundError (expected again)"); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/jvmti/UnresolvedClassAgent.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/jvmti/UnresolvedClassAgent.mf Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Premain-Class: UnresolvedClassAgent +Can-Redefine-Classes: true diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/sa/jmap-hashcode/Test8028623.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/serviceability/sa/jmap-hashcode/Test8028623.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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 + * 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 8028623 + * @summary Test hashing of extended characters in Serviceability Agent. + * @library /testlibrary + * @build com.oracle.java.testlibrary.* + * @compile -encoding utf8 Test8028623.java + * @run main Test8028623 + */ + +import com.oracle.java.testlibrary.JDKToolLauncher; +import com.oracle.java.testlibrary.OutputBuffer; +import com.oracle.java.testlibrary.ProcessTools; + +import java.io.File; + +public class Test8028623 { + + public static int à = 1; + public static String dumpFile = "heap.out"; + + public static void main (String[] args) { + + System.out.println(Ã); + + try { + int pid = ProcessTools.getProcessId(); + JDKToolLauncher jmap = JDKToolLauncher.create("jmap") + .addToolArg("-F") + .addToolArg("-dump:live,format=b,file=" + dumpFile) + .addToolArg(Integer.toString(pid)); + ProcessBuilder pb = new ProcessBuilder(jmap.getCommand()); + OutputBuffer output = ProcessTools.getOutput(pb); + Process p = pb.start(); + int e = p.waitFor(); + System.out.println("stdout:"); + System.out.println(output.getStdout()); + System.out.println("stderr:"); + System.out.println(output.getStderr()); + + if (e != 0) { + throw new RuntimeException("jmap returns: " + e); + } + if (! new File(dumpFile).exists()) { + throw new RuntimeException("dump file NOT created: '" + dumpFile + "'"); + } + } catch (Throwable t) { + t.printStackTrace(); + throw new RuntimeException("Test failed with: " + t); + } + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java --- a/test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -44,7 +44,7 @@ * @key regression * @summary Regression test for hprof export issue due to large heaps (>2G) * @library /testlibrary - * @compile JMapHProfLargeHeapProc.java + * @build com.oracle.java.testlibrary.* JMapHProfLargeHeapProc * @run main JMapHProfLargeHeapTest */ diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/Asserts.java --- a/test/testlibrary/com/oracle/java/testlibrary/Asserts.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/com/oracle/java/testlibrary/Asserts.java Wed Oct 15 16:02:50 2014 +0200 @@ -378,6 +378,64 @@ } } + /** + * Asserts that two strings are equal. + * + * If strings are not equals, then exception message + * will contain {@code msg} followed by list of mismatched lines. + * + * @param str1 First string to compare. + * @param str2 Second string to compare. + * @param msg A description of the assumption. + * @throws RuntimeException if strings are not equal. + */ + public static void assertStringsEqual(String str1, String str2, + String msg) { + String lineSeparator = System.getProperty("line.separator"); + String str1Lines[] = str1.split(lineSeparator); + String str2Lines[] = str2.split(lineSeparator); + + int minLength = Math.min(str1Lines.length, str2Lines.length); + String longestStringLines[] = ((str1Lines.length == minLength) ? + str2Lines : str1Lines); + + boolean stringsAreDifferent = false; + + StringBuilder messageBuilder = new StringBuilder(msg); + + messageBuilder.append("\n"); + + for (int line = 0; line < minLength; line++) { + if (!str1Lines[line].equals(str2Lines[line])) { + messageBuilder.append(String. + format("[line %d] '%s' differs " + + "from '%s'\n", + line, + str1Lines[line], + str2Lines[line])); + stringsAreDifferent = true; + } + } + + if (minLength < longestStringLines.length) { + String stringName = ((longestStringLines == str1Lines) ? + "first" : "second"); + messageBuilder.append(String.format("Only %s string contains " + + "following lines:\n", + stringName)); + stringsAreDifferent = true; + for(int line = minLength; line < longestStringLines.length; line++) { + messageBuilder.append(String. + format("[line %d] '%s'", line, + longestStringLines[line])); + } + } + + if (stringsAreDifferent) { + error(messageBuilder.toString()); + } + } + private static > int compare(T lhs, T rhs, String msg) { assertNotNull(lhs, msg); assertNotNull(rhs, msg); diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/DynamicVMOptionChecker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/DynamicVMOptionChecker.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary; + +import com.sun.management.HotSpotDiagnosticMXBean; +import com.sun.management.VMOption; +import java.lang.management.ManagementFactory; + +/** + * Simple class to check writeability, invalid and valid values for VMOption + */ +public class DynamicVMOptionChecker { + + /** + * Reads VM option from PlatformMXBean and parse it to integer value + * + * @param name of option + * @return parsed value + */ + public static int getIntValue(String name) { + + VMOption option = ManagementFactory. + getPlatformMXBean(HotSpotDiagnosticMXBean.class). + getVMOption(name); + + return Integer.parseInt(option.getValue()); + } + + /** + * Sets VM option value + * + * @param name of option + * @param value to set + */ + public static void setIntValue(String name, int value) { + ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class).setVMOption(name, Integer.toString(value)); + } + + /** + * Checks that VM option is dynamically writable + * + * @param name + * @throws RuntimeException if option if not writable + * @return always true + */ + public static boolean checkIsWritable(String name) { + VMOption option = ManagementFactory. + getPlatformMXBean(HotSpotDiagnosticMXBean.class). + getVMOption(name); + + if (!option.isWriteable()) { + throw new RuntimeException(name + " is not writable"); + } + + return true; + } + + /** + * Checks that value cannot be set + * + * @param name of flag + * @param value string representation of value to set + * @throws RuntimeException on error - when expected exception hasn't been thrown + */ + public static void checkInvalidValue(String name, String value) { + // should throw + try { + ManagementFactory. + getPlatformMXBean(HotSpotDiagnosticMXBean.class). + setVMOption(name, value); + + } catch (IllegalArgumentException e) { + return; + } + + throw new RuntimeException("Expected IllegalArgumentException was not thrown, " + name + "= " + value); + } + + /** + * Checks that value can be set + * + * @param name of flag to set + * @param value string representation of value to set + * @throws RuntimeException on error - when value in VM is not equal to origin + */ + public static void checkValidValue(String name, String value) { + ManagementFactory. + getPlatformMXBean(HotSpotDiagnosticMXBean.class). + setVMOption(name, value); + + VMOption option = ManagementFactory. + getPlatformMXBean(HotSpotDiagnosticMXBean.class). + getVMOption(name); + + if (!option.getValue().equals(value)) { + throw new RuntimeException("Actual value of " + name + " \"" + option.getValue() + + "\" not equal origin \"" + value + "\""); + } + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/ExitCode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/ExitCode.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary; + +/** + * Exit code values that could be returned by the JVM. + */ +public enum ExitCode { + OK(0), + FAIL(1), + CRASH(134); + + public final int value; + + ExitCode(int value) { + this.value = value; + } +} + diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/Platform.java --- a/test/testlibrary/com/oracle/java/testlibrary/Platform.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/com/oracle/java/testlibrary/Platform.java Wed Oct 15 16:02:50 2014 +0200 @@ -28,6 +28,27 @@ private static final String dataModel = System.getProperty("sun.arch.data.model"); private static final String vmVersion = System.getProperty("java.vm.version"); private static final String osArch = System.getProperty("os.arch"); + private static final String vmName = System.getProperty("java.vm.name"); + + public static boolean isClient() { + return vmName.endsWith(" Client VM"); + } + + public static boolean isServer() { + return vmName.endsWith(" Server VM"); + } + + public static boolean isGraal() { + return vmName.endsWith(" Graal VM"); + } + + public static boolean isMinimal() { + return vmName.endsWith(" Minimal VM"); + } + + public static boolean isEmbedded() { + return vmName.contains("Embedded"); + } public static boolean is32bit() { return dataModel.equals("32"); diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java --- a/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/com/oracle/java/testlibrary/ProcessTools.java Wed Oct 15 16:02:50 2014 +0200 @@ -142,20 +142,108 @@ * with any platform specific arguments prepended */ public static ProcessBuilder createJavaProcessBuilder(String... command) throws Exception { + return createJavaProcessBuilder(false, command); + } + + public static ProcessBuilder createJavaProcessBuilder(boolean addTestVmOptions, String... command) throws Exception { String javapath = JDKToolFinder.getJDKTool("java"); ArrayList args = new ArrayList<>(); args.add(javapath); Collections.addAll(args, getPlatformSpecificVMArgs()); + + if (addTestVmOptions) { + String vmopts = System.getProperty("test.vm.opts"); + if (vmopts != null && vmopts.length() > 0) { + Collections.addAll(args, vmopts.split("\\s")); + } + } + Collections.addAll(args, command); // Reporting StringBuilder cmdLine = new StringBuilder(); - for (String cmd : args) - cmdLine.append(cmd).append(' '); + for (String cmd : args) { + cmdLine.append(cmd).append(' '); + } System.out.println("Command line: [" + cmdLine.toString() + "]"); return new ProcessBuilder(args.toArray(new String[args.size()])); } + /** + * Executes a test jvm process, waits for it to finish and returns the process output. + * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added. + * The java from the test.jdk is used to execute the command. + * + * The command line will be like: + * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds + * + * @param cmds User specifed arguments. + * @return The output from the process. + */ + public static OutputAnalyzer executeTestJvm(String... cmds) throws Throwable { + ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(cmds)); + return executeProcess(pb); + } + + /** + * Executes a process, waits for it to finish and returns the process output. + * @param pb The ProcessBuilder to execute. + * @return The output from the process. + */ + public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Throwable { + OutputAnalyzer output = null; + try { + output = new OutputAnalyzer(pb.start()); + return output; + } catch (Throwable t) { + System.out.println("executeProcess() failed: " + t); + throw t; + } finally { + System.out.println(getProcessLog(pb, output)); + } + } + + /** + * Executes a process, waits for it to finish and returns the process output. + * @param cmds The command line to execute. + * @return The output from the process. + */ + public static OutputAnalyzer executeProcess(String... cmds) throws Throwable { + return executeProcess(new ProcessBuilder(cmds)); + } + + /** + * Used to log command line, stdout, stderr and exit code from an executed process. + * @param pb The executed process. + * @param output The output from the process. + */ + public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) { + String stderr = output == null ? "null" : output.getStderr(); + String stdout = output == null ? "null" : output.getStdout(); + String exitValue = output == null ? "null": Integer.toString(output.getExitValue()); + StringBuilder logMsg = new StringBuilder(); + final String nl = System.getProperty("line.separator"); + logMsg.append("--- ProcessLog ---" + nl); + logMsg.append("cmd: " + getCommandLine(pb) + nl); + logMsg.append("exitvalue: " + exitValue + nl); + logMsg.append("stderr: " + stderr + nl); + logMsg.append("stdout: " + stdout + nl); + return logMsg.toString(); + } + + /** + * @return The full command line for the ProcessBuilder. + */ + public static String getCommandLine(ProcessBuilder pb) { + if (pb == null) { + return "null"; + } + StringBuilder cmd = new StringBuilder(); + for (String s : pb.command()) { + cmd.append(s).append(" "); + } + return cmd.toString().trim(); + } } diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/TestDynamicVMOption.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/TestDynamicVMOption.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary; + +/** + * Simple class to check writeability, invalid and valid values for concrete VMOption + */ +public class TestDynamicVMOption { + + private final String name; + private final int value; + + /** + * Constructor + * + * @param name of VM option to test + */ + public TestDynamicVMOption(String name) { + this.name = name; + this.value = DynamicVMOptionChecker.getIntValue(name); + System.out.println(this.name + " = " + this.value); + } + + /** + * Checks that this value can accept valid percentage values and cannot accept invalid percentage values + * + * @throws RuntimeException + */ + public void testPercentageValues() { + checkInvalidValue(Integer.toString(Integer.MIN_VALUE)); + checkInvalidValue(Integer.toString(Integer.MAX_VALUE)); + checkInvalidValue("-10"); + checkInvalidValue("190"); + } + + /** + * Reads VM option from PlatformMXBean and parse it to integer value + * + * @return value + */ + public int getIntValue() { + return DynamicVMOptionChecker.getIntValue(this.name); + } + + /** + * Sets VM option value + * + * @param value to set + */ + public void setIntValue(int value) { + DynamicVMOptionChecker.setIntValue(this.name, value); + } + + /** + * Checks that this VM option is dynamically writable + * + * @throws RuntimeException if option if not writable + * @return true + */ + public boolean checkIsWritable() throws RuntimeException { + return DynamicVMOptionChecker.checkIsWritable(this.name); + } + + /** + * Checks that value for this VM option cannot be set + * + * @param value to check + * @throws RuntimeException on error - when expected exception hasn't been thrown + */ + public void checkInvalidValue(String value) { + DynamicVMOptionChecker.checkInvalidValue(this.name, value); + } + + /** + * Checks that value for this VM option can be set + * + * @param value to check + * @throws RuntimeException on error - when value in VM is not equal to origin + */ + public void checkValidValue(String value) { + DynamicVMOptionChecker.checkValidValue(this.name, value); + } + +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/Utils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/Utils.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary; + +import static com.oracle.java.testlibrary.Asserts.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.Arrays; +import java.util.Collections; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +/** + * Common library for various test helper functions. + */ +public final class Utils { + + /** + * Returns the sequence used by operating system to separate lines. + */ + public static final String NEW_LINE = System.getProperty("line.separator"); + + /** + * Returns the value of 'test.vm.opts'system property. + */ + public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim(); + + /** + * Returns the value of 'test.java.opts'system property. + */ + public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim(); + + private static Unsafe unsafe = null; + + /** + * Returns the value of 'test.timeout.factor' system property + * converted to {@code double}. + */ + public static final double TIMEOUT_FACTOR; + static { + String toFactor = System.getProperty("test.timeout.factor", "1.0"); + TIMEOUT_FACTOR = Double.parseDouble(toFactor); + } + + private Utils() { + // Private constructor to prevent class instantiation + } + + /** + * Returns the list of VM options. + * + * @return List of VM options + */ + public static List getVmOptions() { + return Arrays.asList(safeSplitString(VM_OPTIONS)); + } + + /** + * Returns the list of VM options with -J prefix. + * + * @return The list of VM options with -J prefix + */ + public static List getForwardVmOptions() { + String[] opts = safeSplitString(VM_OPTIONS); + for (int i = 0; i < opts.length; i++) { + opts[i] = "-J" + opts[i]; + } + return Arrays.asList(opts); + } + + /** + * Returns the default JTReg arguments for a jvm running a test. + * This is the combination of JTReg arguments test.vm.opts and test.java.opts. + * @return An array of options, or an empty array if no opptions. + */ + public static String[] getTestJavaOpts() { + List opts = new ArrayList(); + Collections.addAll(opts, safeSplitString(VM_OPTIONS)); + Collections.addAll(opts, safeSplitString(JAVA_OPTIONS)); + return opts.toArray(new String[0]); + } + + /** + * Returns the default JTReg arguments for a jvm running a test without + * options that matches regular expressions in {@code filters}. + * This is the combination of JTReg arguments test.vm.opts and test.java.opts. + * @param filters Regular expressions used to filter out options. + * @return An array of options, or an empty array if no options. + */ + public static String[] getFilteredTestJavaOpts(String... filters) { + String options[] = getTestJavaOpts(); + + if (filters.length == 0) { + return options; + } + + List filteredOptions = new ArrayList(options.length); + Pattern patterns[] = new Pattern[filters.length]; + for (int i = 0; i < filters.length; i++) { + patterns[i] = Pattern.compile(filters[i]); + } + + for (String option : options) { + boolean matched = false; + for (int i = 0; i < patterns.length && !matched; i++) { + Matcher matcher = patterns[i].matcher(option); + matched = matcher.find(); + } + if (!matched) { + filteredOptions.add(option); + } + } + + return filteredOptions.toArray(new String[filteredOptions.size()]); + } + + /** + * Combines given arguments with default JTReg arguments for a jvm running a test. + * This is the combination of JTReg arguments test.vm.opts and test.java.opts + * @return The combination of JTReg test java options and user args. + */ + public static String[] addTestJavaOpts(String... userArgs) { + List opts = new ArrayList(); + Collections.addAll(opts, getTestJavaOpts()); + Collections.addAll(opts, userArgs); + return opts.toArray(new String[0]); + } + + /** + * Splits a string by white space. + * Works like String.split(), but returns an empty array + * if the string is null or empty. + */ + private static String[] safeSplitString(String s) { + if (s == null || s.trim().isEmpty()) { + return new String[] {}; + } + return s.trim().split("\\s+"); + } + + /** + * @return The full command line for the ProcessBuilder. + */ + public static String getCommandLine(ProcessBuilder pb) { + StringBuilder cmd = new StringBuilder(); + for (String s : pb.command()) { + cmd.append(s).append(" "); + } + return cmd.toString(); + } + + /** + * Returns the free port on the local host. + * The function will spin until a valid port number is found. + * + * @return The port number + * @throws InterruptedException if any thread has interrupted the current thread + * @throws IOException if an I/O error occurs when opening the socket + */ + public static int getFreePort() throws InterruptedException, IOException { + int port = -1; + + while (port <= 0) { + Thread.sleep(100); + + ServerSocket serverSocket = null; + try { + serverSocket = new ServerSocket(0); + port = serverSocket.getLocalPort(); + } finally { + serverSocket.close(); + } + } + + return port; + } + + /** + * Returns the name of the local host. + * + * @return The host name + * @throws UnknownHostException if IP address of a host could not be determined + */ + public static String getHostname() throws UnknownHostException { + InetAddress inetAddress = InetAddress.getLocalHost(); + String hostName = inetAddress.getHostName(); + + assertTrue((hostName != null && !hostName.isEmpty()), + "Cannot get hostname"); + + return hostName; + } + + /** + * Uses "jcmd -l" to search for a jvm pid. This function will wait + * forever (until jtreg timeout) for the pid to be found. + * @param key Regular expression to search for + * @return The found pid. + */ + public static int waitForJvmPid(String key) throws Throwable { + final long iterationSleepMillis = 250; + System.out.println("waitForJvmPid: Waiting for key '" + key + "'"); + System.out.flush(); + while (true) { + int pid = tryFindJvmPid(key); + if (pid >= 0) { + return pid; + } + Thread.sleep(iterationSleepMillis); + } + } + + /** + * Searches for a jvm pid in the output from "jcmd -l". + * + * Example output from jcmd is: + * 12498 sun.tools.jcmd.JCmd -l + * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar + * + * @param key A regular expression to search for. + * @return The found pid, or -1 if Enot found. + * @throws Exception If multiple matching jvms are found. + */ + public static int tryFindJvmPid(String key) throws Throwable { + OutputAnalyzer output = null; + try { + JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd"); + jcmdLauncher.addToolArg("-l"); + output = ProcessTools.executeProcess(jcmdLauncher.getCommand()); + output.shouldHaveExitValue(0); + + // Search for a line starting with numbers (pid), follwed by the key. + Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n"); + Matcher matcher = pattern.matcher(output.getStdout()); + + int pid = -1; + if (matcher.find()) { + pid = Integer.parseInt(matcher.group(1)); + System.out.println("findJvmPid.pid: " + pid); + if (matcher.find()) { + throw new Exception("Found multiple JVM pids for key: " + key); + } + } + return pid; + } catch (Throwable t) { + System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t)); + throw t; + } + } + + /** + * Returns file content as a list of strings + * + * @param file File to operate on + * @return List of strings + * @throws IOException + */ + public static List fileAsList(File file) throws IOException { + assertTrue(file.exists() && file.isFile(), + file.getAbsolutePath() + " does not exist or not a file"); + List output = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(new FileReader(file.getAbsolutePath()))) { + while (reader.ready()) { + output.add(reader.readLine().replace(NEW_LINE, "")); + } + } + return output; + } + + /** + * @return Unsafe instance. + */ + public static synchronized Unsafe getUnsafe() { + if (unsafe == null) { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Unable to get Unsafe instance.", e); + } + } + return unsafe; + } + private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Returns hex view of byte array + * + * @param bytes byte array to process + * @return Space separated hexadecimal string representation of bytes + */ + + public static String toHexString(byte[] bytes) { + char[] hexView = new char[bytes.length * 3]; + int i = 0; + for (byte b : bytes) { + hexView[i++] = hexArray[(b >> 4) & 0x0F]; + hexView[i++] = hexArray[b & 0x0F]; + hexView[i++] = ' '; + } + return new String(hexView); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/cli/CPUSpecificCommandLineOptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/cli/CPUSpecificCommandLineOptionTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary.cli; + +import com.oracle.java.testlibrary.cli.predicate.CPUSpecificPredicate; + +/** + * Base class for command line options tests that + * requires specific CPU arch or specific CPU features. + */ +public abstract class CPUSpecificCommandLineOptionTest + extends CommandLineOptionTest { + /** + * Creates new CPU specific test instance that does not + * require any CPU features. + * + * @param cpuArchPattern Regular expression that should + * match os.arch. + */ + public CPUSpecificCommandLineOptionTest(String cpuArchPattern) { + this(cpuArchPattern, null, null); + } + + /** + * Creates new CPU specific test instance that does not + * require from CPU support of {@code supportedCPUFeatures} features + * and no support of {@code unsupportedCPUFeatures}. + * + * @param cpuArchPattern Regular expression that should + * match os.arch. + * @param supportedCPUFeatures Array with names of features that + * should be supported by CPU. If {@code null}, + * then no features have to be supported. + * @param unsupportedCPUFeatures Array with names of features that + * should not be supported by CPU. + * If {@code null}, then CPU may support any + * features. + */ + public CPUSpecificCommandLineOptionTest(String cpuArchPattern, + String supportedCPUFeatures[], String unsupportedCPUFeatures[]) { + super(new CPUSpecificPredicate(cpuArchPattern, supportedCPUFeatures, + unsupportedCPUFeatures)); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/cli/CommandLineOptionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/cli/CommandLineOptionTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,330 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary.cli; + +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.function.BooleanSupplier; + +import com.oracle.java.testlibrary.*; + +/** + * Base class for command line option tests. + */ +public abstract class CommandLineOptionTest { + public static final String UNLOCK_DIAGNOSTIC_VM_OPTIONS + = "-XX:+UnlockDiagnosticVMOptions"; + public static final String UNLOCK_EXPERIMENTAL_VM_OPTIONS + = "-XX:+UnlockExperimentalVMOptions"; + protected static final String UNRECOGNIZED_OPTION_ERROR_FORMAT + = "Unrecognized VM option '[+-]?%s(=.*)?'"; + protected static final String EXPERIMENTAL_OPTION_ERROR_FORMAT + = "VM option '%s' is experimental and must be enabled via " + + "-XX:\\+UnlockExperimentalVMOptions."; + protected static final String DIAGNOSTIC_OPTION_ERROR_FORMAT + = " VM option '%s' is diagnostic and must be enabled via " + + "-XX:\\+UnlockDiagnosticVMOptions."; + private static final String PRINT_FLAGS_FINAL_FORMAT = "%s\\s*:?=\\s*%s"; + + /** + * Verifies that JVM startup behaviour matches our expectations. + * + * @param option an option that should be passed to JVM + * @param expectedMessages an array of patterns that should occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not + * occur in JVM output. If {@code null} then + * JVM output could be empty. + * @param exitCode expected exit code. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyJVMStartup(String option, + String expectedMessages[], String unexpectedMessages[], + ExitCode exitCode) throws Throwable { + CommandLineOptionTest.verifyJVMStartup(expectedMessages, + unexpectedMessages, exitCode, false, option); + } + + /** + * Verifies that JVM startup behaviour matches our expectations. + * + * @param expectedMessages an array of patterns that should occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not + * occur in JVM output. If {@code null} then + * JVM output could be empty. + * @param exitCode expected exit code. + * @param addTestVMOptions if {@code true} then test VM options will be + * passed to VM. + * @param options options that should be passed to VM in addition to mode + * flag. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyJVMStartup(String expectedMessages[], + String unexpectedMessages[], ExitCode exitCode, + boolean addTestVMOptions, String... options) throws Throwable { + List finalOptions = new ArrayList<>(); + if (addTestVMOptions) { + Collections.addAll(finalOptions, Utils.getTestJavaOpts()); + } + Collections.addAll(finalOptions, options); + finalOptions.add("-version"); + + ProcessBuilder processBuilder + = ProcessTools.createJavaProcessBuilder(finalOptions.toArray( + new String[finalOptions.size()])); + OutputAnalyzer outputAnalyzer + = new OutputAnalyzer(processBuilder.start()); + outputAnalyzer.shouldHaveExitValue(exitCode.value); + + if (expectedMessages != null) { + for (String expectedMessage : expectedMessages) { + outputAnalyzer.shouldMatch(expectedMessage); + } + } + + if (unexpectedMessages != null) { + for (String unexpectedMessage : unexpectedMessages) { + outputAnalyzer.shouldNotMatch(unexpectedMessage); + } + } + } + + /** + * Verifies that JVM startup behaviour matches our expectations when type + * of newly started VM is the same as the type of current. + * + * @param expectedMessages an array of patterns that should occur + * in JVM output. If {@code null} then + * JVM output could be empty. + * @param unexpectedMessages an array of patterns that should not + * occur in JVM output. If {@code null} then + * JVM output could be empty. + * @param exitCode expected exit code. + * @param options options that should be passed to VM in addition to mode + * flag. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifySameJVMStartup(String expectedMessages[], + String unexpectedMessages[], ExitCode exitCode, String... options) + throws Throwable { + List finalOptions = new ArrayList<>(); + finalOptions.add(CommandLineOptionTest.getVMTypeOption()); + Collections.addAll(finalOptions, options); + + CommandLineOptionTest.verifyJVMStartup(expectedMessages, + unexpectedMessages, exitCode, false, + finalOptions.toArray(new String[finalOptions.size()])); + } + + /** + * Verifies that value of specified JVM option is the same as + * expected value. + * This method filter out option with {@code optionName} + * name from test java options. + * + * @param optionName a name of tested option. + * @param expectedValue expected value of tested option. + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyOptionValue(String optionName, + String expectedValue, String... additionalVMOpts) throws Throwable { + verifyOptionValue(optionName, expectedValue, true, additionalVMOpts); + } + + /** + * Verifies that value of specified JVM option is the same as + * expected value. + * This method filter out option with {@code optionName} + * name from test java options. + * + * @param optionName a name of tested option. + * @param expectedValue expected value of tested option. + * @param addTestVmOptions if {@code true}, then test VM options + * will be used. + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @throws Throwable if verification fails or some other issues + * occur. + */ + public static void verifyOptionValue(String optionName, + String expectedValue, boolean addTestVmOptions, + String... additionalVMOpts) throws Throwable { + List vmOpts = new ArrayList<>(); + + if (addTestVmOptions) { + Collections.addAll(vmOpts, + Utils.getFilteredTestJavaOpts(optionName)); + } + Collections.addAll(vmOpts, additionalVMOpts); + Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); + + ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( + vmOpts.toArray(new String[vmOpts.size()])); + + OutputAnalyzer outputAnalyzer + = new OutputAnalyzer(processBuilder.start()); + + outputAnalyzer.shouldHaveExitValue(0); + outputAnalyzer.shouldMatch(String.format( + CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, + optionName, expectedValue)); + } + + /** + * Verifies that value of specified JVM when type of newly started VM + * is the same as the type of current. + * This method filter out option with {@code optionName} + * name from test java options. + * Only mode flag will be passed to VM in addition to + * {@code additionalVMOpts} + * + * @param optionName name of tested option. + * @param expectedValue expected value of tested option. + * @param additionalVMOpts additional options that should be + * passed to JVM. + * @throws Throwable if verification fails or some other issues occur. + */ + public static void verifyOptionValueForSameVM(String optionName, + String expectedValue, String... additionalVMOpts) throws Throwable { + List finalOptions = new ArrayList<>(); + finalOptions.add(CommandLineOptionTest.getVMTypeOption()); + Collections.addAll(finalOptions, additionalVMOpts); + + CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, + false, finalOptions.toArray(new String[finalOptions.size()])); + } + + /** + * Prepares boolean command line flag with name {@code name} according + * to it's {@code value}. + * + * @param name the name of option to be prepared + * @param value the value of option + * @return prepared command line flag + */ + public static String prepareBooleanFlag(String name, boolean value) { + return String.format("-XX:%c%s", (value ? '+' : '-'), name); + } + + /** + * Prepares numeric command line flag with name {@code name} by setting + * it's value to {@code value}. + * + * @param name the name of option to be prepared + * @param value the value of option + * @return prepared command line flag + */ + public static String prepareNumericFlag(String name, Number value) { + return String.format("-XX:%s=%s", name, value.toString()); + } + + /** + * Returns message that should occur in VM output if option + * {@code optionName} if unrecognized. + * + * @param optionName the name of option for which message should be returned + * @return message saying that option {@code optionName} is unrecognized + */ + public static String getUnrecognizedOptionErrorMessage(String optionName) { + return String.format( + CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT, + optionName); + } + + /** + * Returns message that should occur in VM output if option + * {@code optionName} is experimental and + * -XX:+UnlockExperimentalVMOptions was not passed to VM. + * + * @param optionName the name of option for which message should be returned + * @return message saying that option {@code optionName} is experimental + */ + public static String getExperimentalOptionErrorMessage(String optionName) { + return String.format( + CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT, + optionName); + } + + /** + * Returns message that should occur in VM output if option + * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions + * was not passed to VM. + * + * @param optionName the name of option for which message should be returned + * @return message saying that option {@code optionName} is diganostic + */ + public static String getDiagnosticOptionErrorMessage(String optionName) { + return String.format( + CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT, + optionName); + } + + /** + * @return option required to start a new VM with the same type as current. + * @throws RuntimeException when VM type is unknown. + */ + private static String getVMTypeOption() { + if (Platform.isServer()) { + return "-server"; + } else if (Platform.isClient()) { + return "-client"; + } else if (Platform.isMinimal()) { + return "-minimal"; + } else if (Platform.isGraal()) { + return "-graal"; + } + throw new RuntimeException("Unknown VM mode."); + } + + private final BooleanSupplier predicate; + + /** + * Constructs new CommandLineOptionTest that will be executed only if + * predicate {@code predicate} return {@code true}. + * @param predicate a predicate responsible for test's preconditions check. + */ + public CommandLineOptionTest(BooleanSupplier predicate) { + this.predicate = predicate; + } + + /** + * Runs command line option test. + */ + public final void test() throws Throwable { + if (predicate.getAsBoolean()) { + runTestCases(); + } + } + + /** + * @throws Throwable if some issue happened during test cases execution. + */ + protected abstract void runTestCases() throws Throwable; +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/cli/predicate/AndPredicate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/AndPredicate.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary.cli.predicate; + +import java.util.function.BooleanSupplier; + +public class AndPredicate implements BooleanSupplier { + private final BooleanSupplier a; + private final BooleanSupplier b; + + public AndPredicate(BooleanSupplier a, BooleanSupplier b) { + this.a = a; + this.b = b; + } + + @Override + public boolean getAsBoolean() { + return a.getAsBoolean() && b.getAsBoolean(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/cli/predicate/CPUSpecificPredicate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/CPUSpecificPredicate.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.java.testlibrary.cli.predicate; + +import com.oracle.java.testlibrary.Platform; +import sun.hotspot.cpuinfo.CPUInfo; + +import java.util.function.BooleanSupplier; + +public class CPUSpecificPredicate implements BooleanSupplier { + private final String cpuArchPattern; + private final String supportedCPUFeatures[]; + private final String unsupportedCPUFeatures[]; + + public CPUSpecificPredicate(String cpuArchPattern, + String supportedCPUFeatures[], + String unsupportedCPUFeatures[]) { + this.cpuArchPattern = cpuArchPattern; + this.supportedCPUFeatures = supportedCPUFeatures; + this.unsupportedCPUFeatures = unsupportedCPUFeatures; + } + + @Override + public boolean getAsBoolean() { + if (!Platform.getOsArch().matches(cpuArchPattern)) { + System.out.println("CPU arch does not match " + cpuArchPattern); + return false; + } + + if (supportedCPUFeatures != null) { + for (String feature : supportedCPUFeatures) { + if (!CPUInfo.hasFeature(feature)) { + System.out.println("CPU does not support " + feature + + " feature"); + return false; + } + } + } + + if (unsupportedCPUFeatures != null) { + for (String feature : unsupportedCPUFeatures) { + if (CPUInfo.hasFeature(feature)) { + System.out.println("CPU support " + feature + " feature"); + return false; + } + } + } + return true; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/cli/predicate/NotPredicate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/NotPredicate.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package com.oracle.java.testlibrary.cli.predicate; + +import java.util.function.BooleanSupplier; + +public class NotPredicate implements BooleanSupplier { + private final BooleanSupplier s; + + public NotPredicate(BooleanSupplier s) { + this.s = s; + } + + @Override + public boolean getAsBoolean() { + return !s.getAsBoolean(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/com/oracle/java/testlibrary/cli/predicate/OrPredicate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/OrPredicate.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package com.oracle.java.testlibrary.cli.predicate; + +import java.util.function.BooleanSupplier; + +public class OrPredicate implements BooleanSupplier { + private final BooleanSupplier a; + private final BooleanSupplier b; + + public OrPredicate(BooleanSupplier a, BooleanSupplier b) { + this.a = a; + this.b = b; + } + + @Override + public boolean getAsBoolean() { + return a.getAsBoolean() || b.getAsBoolean(); + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/ctw/test/ClassesDirTest.java --- a/test/testlibrary/ctw/test/ClassesDirTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/ctw/test/ClassesDirTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -22,10 +22,10 @@ */ /* - * @test ClassesDirTest + * @test * @bug 8012447 * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src - * @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox ClassesDirTest Foo Bar + * @build ClassFileInstaller sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar * @run main ClassesDirTest prepare * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld classes diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/ctw/test/ClassesListTest.java --- a/test/testlibrary/ctw/test/ClassesListTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/ctw/test/ClassesListTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -22,10 +22,10 @@ */ /* - * @test ClassesListTest + * @test * @bug 8012447 * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src - * @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox ClassesListTest Foo Bar + * @build ClassFileInstaller sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar * @run main ClassesListTest prepare * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld classes.lst diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/ctw/test/JarDirTest.java --- a/test/testlibrary/ctw/test/JarDirTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/ctw/test/JarDirTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -22,10 +22,10 @@ */ /* - * @test JarDirTest + * @test * @bug 8012447 * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src - * @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox JarDirTest Foo Bar + * @build ClassFileInstaller com.oracle.java.testlibrary.* sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar * @run main JarDirTest prepare * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld jars/* diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/ctw/test/JarsTest.java --- a/test/testlibrary/ctw/test/JarsTest.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/ctw/test/JarsTest.java Wed Oct 15 16:02:50 2014 +0200 @@ -22,10 +22,10 @@ */ /* - * @test JarsTest + * @test * @bug 8012447 * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src - * @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox JarsTest Foo Bar + * @build ClassFileInstaller com.oracle.java.testlibrary.* sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar * @run main JarsTest prepare * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld foo.jar bar.jar diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/whitebox/sun/hotspot/WhiteBox.java --- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Thu Oct 16 10:21:29 2014 +0200 +++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed Oct 15 16:02:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -135,6 +135,7 @@ public native boolean enqueueMethodForCompilation(Executable method, int compLevel, int entry_bci); public native void clearMethodState(Executable method); public native int getMethodEntryBci(Executable method); + public native Object[] getNMethod(Executable method, boolean isOsr); // Intered strings public native boolean isInStringTable(String str); @@ -150,4 +151,7 @@ public native void runMemoryUnitTests(); public native void readFromNoaccessArea(); + // CPU features + public native String getCPUFeatures(); + } diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/whitebox/sun/hotspot/code/NMethod.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.hotspot.code; + +import java.lang.reflect.Executable; +import sun.hotspot.WhiteBox; + +public class NMethod { + private static final WhiteBox wb = WhiteBox.getWhiteBox(); + public static NMethod get(Executable method, boolean isOsr) { + Object[] obj = wb.getNMethod(method, isOsr); + return obj == null ? null : new NMethod(obj); + } + private NMethod(Object[] obj) { + assert obj.length == 2; + comp_level = (Integer) obj[0]; + insts = (byte[]) obj[1]; + } + public byte[] insts; + public int comp_level; + + @Override + public String toString() { + return "NMethod{" + + "insts=" + insts + + ", comp_level=" + comp_level + + '}'; + } +} diff -r 45d7b2c7029d -r 52b4284cb496 test/testlibrary/whitebox/sun/hotspot/cpuinfo/CPUInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/testlibrary/whitebox/sun/hotspot/cpuinfo/CPUInfo.java Wed Oct 15 16:02:50 2014 +0200 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.hotspot.cpuinfo; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import sun.hotspot.WhiteBox; + +/** + * Information about CPU on test box. + * + * CPUInfo uses WhiteBox to gather information, + * so WhiteBox class should be added to bootclasspath + * and option -XX:+WhiteBoxAPI should expclicetly + * specified on command line. + */ +public class CPUInfo { + + private static final List features; + private static final String additionalCPUInfo; + + static { + WhiteBox wb = WhiteBox.getWhiteBox(); + + Pattern additionalCPUInfoRE = + Pattern.compile("([^(]*\\([^)]*\\)[^,]*),\\s*"); + + String cpuFeaturesString = wb.getCPUFeatures(); + Matcher matcher = additionalCPUInfoRE.matcher(cpuFeaturesString); + if (matcher.find()) { + additionalCPUInfo = matcher.group(1); + } else { + additionalCPUInfo = ""; + } + String splittedFeatures[] = matcher.replaceAll("").split("(, )| "); + + features = Collections.unmodifiableList(Arrays. + asList(splittedFeatures)); + } + + /** + * Get additional information about CPU. + * For example, on X86 in will be family/model/stepping + * and number of cores. + * + * @return additional CPU info + */ + public static String getAdditionalCPUInfo() { + return additionalCPUInfo; + } + + /** + * Get all known features supported by CPU. + * + * @return unmodifiable list with names of all known features + * supported by CPU. + */ + public static List getFeatures() { + return features; + } + + /** + * Check if some feature is supported by CPU. + * + * @param feature Name of feature to be tested. + * @return true if tested feature is supported by CPU. + */ + public static boolean hasFeature(String feature) { + return features.contains(feature.toLowerCase()); + } +}