# HG changeset patch # User dcubed # Date 1269561299 25200 # Node ID 84043c7507b9b464b0c52056ddd98929ac759872 # Parent 5d393243d487fecfe968b00ce86b5fc3fe109223# Parent 39e409a664b340ca0eaaa5a4e55dddfa99c82bdd Merge diff -r 39e409a664b3 -r 84043c7507b9 .hgignore --- a/.hgignore Thu Mar 25 16:27:12 2010 -0700 +++ b/.hgignore Thu Mar 25 16:54:59 2010 -0700 @@ -1,6 +1,6 @@ ^build/ ^dist/ -^nbproject/private/ +/nbproject/private/ ^src/share/tools/hsdis/build/ ^src/share/tools/IdealGraphVisualizer/[a-zA-Z0-9]*/build/ ^src/share/tools/IdealGraphVisualizer/build/ diff -r 39e409a664b3 -r 84043c7507b9 .hgtags --- a/.hgtags Thu Mar 25 16:27:12 2010 -0700 +++ b/.hgtags Thu Mar 25 16:54:59 2010 -0700 @@ -45,3 +45,39 @@ d07e68298d4e17ebf93d8299e43fcc3ded26472a jdk7-b68 54fd4d9232969ea6cd3d236e5ad276183bb0d423 jdk7-b69 0632c3e615a315ff11e2ab1d64f4d82ff9853461 jdk7-b70 +50a95aa4a247f0cbbf66df285a8b1d78ffb153d9 jdk7-b71 +a94714c550658fd6741793ef036cb9625dc2ab1a jdk7-b72 +faf94d94786b621f8e13cbcc941ca69c6d967c3f jdk7-b73 +f4b900403d6e4b0af51447bd13bbe23fe3a1dac7 jdk7-b74 +d8dd291a362acb656026a9c0a9da48501505a1e7 jdk7-b75 +9174bb32e934965288121f75394874eeb1fcb649 jdk7-b76 +455105fc81d941482f8f8056afaa7aa0949c9300 jdk7-b77 +e703499b4b51e3af756ae77c3d5e8b3058a14e4e jdk7-b78 +a5a6adfca6ecefb5894a848debabfe442ff50e25 jdk7-b79 +3003ddd1d4330b06cb4691ae74d600d3685899eb jdk7-b80 +1f9b07674480c224828852ffe137beea36b3cab5 jdk7-b81 +1999f5b12482d66c8b0daf6709daea4f51893a04 jdk7-b82 +a94714c550658fd6741793ef036cb9625dc2ab1a hs17-b01 +faf94d94786b621f8e13cbcc941ca69c6d967c3f hs17-b02 +f4b900403d6e4b0af51447bd13bbe23fe3a1dac7 hs17-b03 +d8dd291a362acb656026a9c0a9da48501505a1e7 hs17-b04 +9174bb32e934965288121f75394874eeb1fcb649 hs17-b05 +a5a6adfca6ecefb5894a848debabfe442ff50e25 hs17-b06 +3003ddd1d4330b06cb4691ae74d600d3685899eb hs17-b07 +1f9b07674480c224828852ffe137beea36b3cab5 hs17-b08 +ff3232b68fbb35185b338d7ff4695b52460243f3 hs17-b09 +981375ca07b7f0605f92f57aad95122e8c385a4d hs16-b01 +f4cbf78110c726919f46b59a3b054c54c7e889b4 hs16-b02 +07c1c01e031513bfe6a7d17c6cf30d2752824ae9 hs16-b03 +08f86fa55a31113df626a75c8a626e66a543a1bd hs16-b04 +32c83fb84370a35344676991a48440378e6b6c8a hs16-b05 +ba313800759b678979434d6da8ed3bf49eb8bea4 hs16-b06 +3c0f729815607e1678bd0c41ae68494c700dcc71 hs16-b07 +ac59d4e6dae51ac5fc31a9a4940d1857f91161b1 hs16-b08 +3f844a28c5f4912bd04043b44f21b25b0805ffc2 hs15-b01 +1605bb4eb5a7a1703b13d5b077a22cc665fe45f7 hs15-b02 +2581d90c6c9b2012da930eb4742add94a03069a0 hs15-b03 +9ab385cb0c42997e16a7761ebcd25c90560a2714 hs15-b04 +fafab5d5349c7c066d677538db67a1ee0fb33bd2 hs15-b05 +3f370a32906eb5ba993fabd7b4279be7f31052b9 jdk7-b83 +ffc8d176b84bcfb5ac21302b4feb3b0c0d69b97c jdk7-b84 diff -r 39e409a664b3 -r 84043c7507b9 agent/make/saenv.sh --- a/agent/make/saenv.sh Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/make/saenv.sh Thu Mar 25 16:54:59 2010 -0700 @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2009 Sun Microsystems, 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 @@ -48,8 +48,16 @@ CPU=i386 fi else - LD_AUDIT_32=$STARTDIR/../src/os/solaris/proc/`uname -p`/libsaproc_audit.so - export LD_AUDIT_32 + # configure audit helper library if SA_ALTROOT is set + if [ -n "$SA_ALTROOT" ]; then + LD_AUDIT_32=$STARTDIR/../src/os/solaris/proc/`uname -p`/libsaproc_audit.so + export LD_AUDIT_32 + if [ ! -f $LD_AUDIT_32 ]; then + echo "SA_ALTROOT is set and can't find libsaproc_audit.so." + echo "Make sure to build it with 'make natives'." + exit 1 + fi + fi SA_LIBPATH=$STARTDIR/../src/os/solaris/proc/`uname -p`:$STARTDIR/solaris/`uname -p` OPTIONS="-Dsa.library.path=$SA_LIBPATH -Dsun.jvm.hotspot.debugger.useProcDebugger" CPU=sparc diff -r 39e409a664b3 -r 84043c7507b9 agent/make/saenv64.sh --- a/agent/make/saenv64.sh Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/make/saenv64.sh Thu Mar 25 16:54:59 2010 -0700 @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2009 Sun Microsystems, 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 @@ -43,8 +43,16 @@ fi fi -LD_AUDIT_64=$STARTDIR/../src/os/solaris/proc/$CPU/libsaproc_audit.so -export LD_AUDIT_64 +# configure audit helper library if SA_ALTROOT is set +if [ -n "$SA_ALTROOT" ]; then + LD_AUDIT_64=$STARTDIR/../src/os/solaris/proc/$CPU/libsaproc_audit.so + export LD_AUDIT_64 + if [ ! -f $LD_AUDIT_64 ]; then + echo "SA_ALTROOT is set and can't find libsaproc_audit.so." + echo "Make sure to build it with 'make natives'." + exit 1 + fi +fi SA_LIBPATH=$STARTDIR/../src/os/solaris/proc/$CPU:$STARTDIR/solaris/$CPU OPTIONS="-Dsa.library.path=$SA_LIBPATH -Dsun.jvm.hotspot.debugger.useProcDebugger" diff -r 39e409a664b3 -r 84043c7507b9 agent/src/os/solaris/proc/Makefile --- a/agent/src/os/solaris/proc/Makefile Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/os/solaris/proc/Makefile Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ # -# Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2002-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 agent/src/os/solaris/proc/mapfile --- a/agent/src/os/solaris/proc/mapfile Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/os/solaris/proc/mapfile Thu Mar 25 16:54:59 2010 -0700 @@ -1,7 +1,7 @@ # # -# Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2003-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java --- a/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java Thu Mar 25 16:54:59 2010 -0700 @@ -926,6 +926,28 @@ } } }, + new Command("dumpcodecache", "dumpcodecache", false) { + public void doit(Tokens t) { + if (t.countTokens() != 0) { + usage(); + } else { + final PrintStream fout = out; + final HTMLGenerator gen = new HTMLGenerator(false); + CodeCacheVisitor v = new CodeCacheVisitor() { + public void prologue(Address start, Address end) { + } + public void visit(CodeBlob blob) { + fout.println(gen.genHTML(blob.instructionsBegin())); + } + public void epilogue() { + } + + + }; + VM.getVM().getCodeCache().iterate(v); + } + } + }, new Command("where", "where { -a | id }", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java --- a/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java Thu Mar 25 16:54:59 2010 -0700 @@ -33,6 +33,7 @@ public class CodeCache { private static AddressField heapField; + private static AddressField scavengeRootNMethodsField; private static VirtualConstructor virtualConstructor; private CodeHeap heap; @@ -49,6 +50,7 @@ Type type = db.lookupType("CodeCache"); heapField = type.getAddressField("_heap"); + scavengeRootNMethodsField = type.getAddressField("_scavenge_root_nmethods"); virtualConstructor = new VirtualConstructor(db); // Add mappings for all possible CodeBlob subclasses @@ -67,6 +69,10 @@ heap = (CodeHeap) VMObjectFactory.newObject(CodeHeap.class, heapField.getValue()); } + public NMethod scavengeRootMethods() { + return (NMethod) VMObjectFactory.newObject(NMethod.class, scavengeRootNMethodsField.getValue()); + } + public boolean contains(Address p) { return getHeap().contains(p); } @@ -167,7 +173,8 @@ CodeBlob lastBlob = null; while (ptr != null && ptr.lessThan(end)) { try { - CodeBlob blob = findBlobUnsafe(ptr); + // Use findStart to get a pointer inside blob other findBlob asserts + CodeBlob blob = findBlobUnsafe(heap.findStart(ptr)); if (blob != null) { visitor.visit(blob); if (blob == lastBlob) { diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java --- a/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java Thu Mar 25 16:54:59 2010 -0700 @@ -81,4 +81,8 @@ Assert.that(false, "should not reach here"); return null; } + + public int readBCI() { + return readInt() + InvocationEntryBCI; + } } diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java --- a/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, 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 @@ -40,7 +40,10 @@ /** != InvocationEntryBci if this nmethod is an on-stack replacement method */ private static CIntegerField entryBCIField; /** To support simple linked-list chaining of nmethods */ - private static AddressField linkField; + private static AddressField osrLinkField; + private static AddressField scavengeRootLinkField; + private static JByteField scavengeRootStateField; + /** Offsets for different nmethod parts */ private static CIntegerField exceptionOffsetField; private static CIntegerField deoptOffsetField; @@ -87,7 +90,10 @@ zombieInstructionSizeField = type.getCIntegerField("_zombie_instruction_size"); methodField = type.getOopField("_method"); entryBCIField = type.getCIntegerField("_entry_bci"); - linkField = type.getAddressField("_link"); + osrLinkField = type.getAddressField("_osr_link"); + scavengeRootLinkField = type.getAddressField("_scavenge_root_link"); + scavengeRootStateField = type.getJByteField("_scavenge_root_state"); + exceptionOffsetField = type.getCIntegerField("_exception_offset"); deoptOffsetField = type.getCIntegerField("_deoptimize_offset"); origPCOffsetField = type.getCIntegerField("_orig_pc_offset"); @@ -219,10 +225,19 @@ return getEntryBCI(); } - public NMethod getLink() { - return (NMethod) VMObjectFactory.newObject(NMethod.class, linkField.getValue(addr)); + public NMethod getOSRLink() { + return (NMethod) VMObjectFactory.newObject(NMethod.class, osrLinkField.getValue(addr)); } + public NMethod getScavengeRootLink() { + return (NMethod) VMObjectFactory.newObject(NMethod.class, scavengeRootLinkField.getValue(addr)); + } + + public int getScavengeRootState() { + return (int) scavengeRootStateField.getValue(addr); + } + + /** Tells whether frames described by this nmethod can be deoptimized. Note: native wrappers cannot be deoptimized. */ public boolean canBeDeoptimized() { return isJavaMethod(); } @@ -259,7 +274,7 @@ if (Assert.ASSERTS_ENABLED) { Assert.that(pd != null, "scope must be present"); } - return new ScopeDesc(this, pd.getScopeDecodeOffset()); + return new ScopeDesc(this, pd.getScopeDecodeOffset(), pd.getObjDecodeOffset(), pd.getReexecute()); } /** This is only for use by the debugging system, and is only @@ -291,11 +306,11 @@ public ScopeDesc getScopeDescNearDbg(Address pc) { PCDesc pd = getPCDescNearDbg(pc); if (pd == null) return null; - return new ScopeDesc(this, pd.getScopeDecodeOffset()); + return new ScopeDesc(this, pd.getScopeDecodeOffset(), pd.getObjDecodeOffset(), pd.getReexecute()); } - public Map/**/ getSafepoints() { - Map safepoints = new HashMap(); // Map + public Map/**/ getSafepoints() { + Map safepoints = new HashMap(); // Map sun.jvm.hotspot.debugger.Address p = null; for (p = scopesPCsBegin(); p.lessThan(scopesPCsEnd()); p = p.addOffsetTo(pcDescSize)) { diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java --- a/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, 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 @@ -36,6 +36,8 @@ public class PCDesc extends VMObject { private static CIntegerField pcOffsetField; private static CIntegerField scopeDecodeOffsetField; + private static CIntegerField objDecodeOffsetField; + private static CIntegerField pcFlagsField; static { VM.registerVMInitializedObserver(new Observer() { @@ -50,6 +52,8 @@ pcOffsetField = type.getCIntegerField("_pc_offset"); scopeDecodeOffsetField = type.getCIntegerField("_scope_decode_offset"); + objDecodeOffsetField = type.getCIntegerField("_obj_decode_offset"); + pcFlagsField = type.getCIntegerField("_flags"); } public PCDesc(Address addr) { @@ -66,10 +70,20 @@ return ((int) scopeDecodeOffsetField.getValue(addr)); } + public int getObjDecodeOffset() { + return ((int) objDecodeOffsetField.getValue(addr)); + } + public Address getRealPC(NMethod code) { return code.instructionsBegin().addOffsetTo(getPCOffset()); } + + public boolean getReexecute() { + int flags = (int)pcFlagsField.getValue(addr); + return ((flags & 0x1)== 1); //first is the reexecute bit + } + public void print(NMethod code) { printOn(System.out, code); } diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java --- a/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java Thu Mar 25 16:54:59 2010 -0700 @@ -51,45 +51,46 @@ /** Scalar replaced bjects pool */ private List objects; // ArrayList - - public ScopeDesc(NMethod code, int decodeOffset) { + private ScopeDesc(NMethod code, int decodeOffset, List objects, boolean reexecute) { this.code = code; this.decodeOffset = decodeOffset; - this.objects = decodeObjectValues(DebugInformationRecorder.SERIALIZED_NULL); + this.objects = objects; + this.reexecute = reexecute; // Decode header DebugInfoReadStream stream = streamAt(decodeOffset); senderDecodeOffset = stream.readInt(); method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); - setBCIAndReexecute(stream.readInt()); + bci = stream.readBCI(); // Decode offsets for body and sender localsDecodeOffset = stream.readInt(); expressionsDecodeOffset = stream.readInt(); monitorsDecodeOffset = stream.readInt(); } - public ScopeDesc(NMethod code, int decodeOffset, int objectDecodeOffset) { + public ScopeDesc(NMethod code, int decodeOffset, int objectDecodeOffset, boolean reexecute) { this.code = code; this.decodeOffset = decodeOffset; this.objects = decodeObjectValues(objectDecodeOffset); + this.reexecute = reexecute; // Decode header DebugInfoReadStream stream = streamAt(decodeOffset); senderDecodeOffset = stream.readInt(); method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); - setBCIAndReexecute(stream.readInt()); + bci = stream.readBCI(); // Decode offsets for body and sender localsDecodeOffset = stream.readInt(); expressionsDecodeOffset = stream.readInt(); monitorsDecodeOffset = stream.readInt(); } - public NMethod getNMethod() { return code; } - public Method getMethod() { return method; } - public int getBCI() { return bci; } - public boolean getReexecute() {return reexecute;} + public NMethod getNMethod() { return code; } + public Method getMethod() { return method; } + public int getBCI() { return bci; } + public boolean getReexecute() { return reexecute;} /** Returns a List<ScopeValue> */ public List getLocals() { @@ -106,7 +107,7 @@ return decodeMonitorValues(monitorsDecodeOffset); } - /** Returns a List<MonitorValue> */ + /** Returns a List<ObjectValue> */ public List getObjects() { return objects; } @@ -117,7 +118,7 @@ return null; } - return new ScopeDesc(code, senderDecodeOffset); + return new ScopeDesc(code, senderDecodeOffset, objects, false); } /** Returns where the scope was decoded */ @@ -151,8 +152,8 @@ public void printValueOn(PrintStream tty) { tty.print("ScopeDesc for "); method.printValueOn(tty); - tty.println(" @bci " + bci); - tty.println(" reexecute: " + reexecute); + tty.print(" @bci " + bci); + tty.println(" reexecute=" + reexecute); } // FIXME: add more accessors @@ -160,12 +161,6 @@ //-------------------------------------------------------------------------------- // Internals only below this point // - private void setBCIAndReexecute(int combination) { - int InvocationEntryBci = VM.getVM().getInvocationEntryBCI(); - bci = (combination >> 1) + InvocationEntryBci; - reexecute = (combination & 1)==1 ? true : false; - } - private DebugInfoReadStream streamAt(int decodeOffset) { return new DebugInfoReadStream(code, decodeOffset, objects); } diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleFreeListSpace.java --- a/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleFreeListSpace.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/memory/CompactibleFreeListSpace.java Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/memory/FreeChunk.java --- a/agent/src/share/classes/sun/jvm/hotspot/memory/FreeChunk.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/memory/FreeChunk.java Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/memory/SystemDictionary.java --- a/agent/src/share/classes/sun/jvm/hotspot/memory/SystemDictionary.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/memory/SystemDictionary.java Thu Mar 25 16:54:59 2010 -0700 @@ -63,12 +63,12 @@ javaSystemLoaderField = type.getOopField("_java_system_loader"); nofBuckets = db.lookupIntConstant("SystemDictionary::_nof_buckets").intValue(); - objectKlassField = type.getOopField(WK_KLASS("object_klass")); - classLoaderKlassField = type.getOopField(WK_KLASS("classloader_klass")); - stringKlassField = type.getOopField(WK_KLASS("string_klass")); - systemKlassField = type.getOopField(WK_KLASS("system_klass")); - threadKlassField = type.getOopField(WK_KLASS("thread_klass")); - threadGroupKlassField = type.getOopField(WK_KLASS("threadGroup_klass")); + objectKlassField = type.getOopField(WK_KLASS("Object_klass")); + classLoaderKlassField = type.getOopField(WK_KLASS("ClassLoader_klass")); + stringKlassField = type.getOopField(WK_KLASS("String_klass")); + systemKlassField = type.getOopField(WK_KLASS("System_klass")); + threadKlassField = type.getOopField(WK_KLASS("Thread_klass")); + threadGroupKlassField = type.getOopField(WK_KLASS("ThreadGroup_klass")); } // This WK functions must follow the definitions in systemDictionary.hpp: diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java --- a/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Thu Mar 25 16:54:59 2010 -0700 @@ -807,6 +807,9 @@ Interpreter interp = VM.getVM().getInterpreter(); if (interp.contains(pc)) { InterpreterCodelet codelet = interp.getCodeletContaining(pc); + if (codelet == null) { + return "Unknown location in the Interpreter: " + pc; + } return genHTML(codelet); } return genHTML(blob); @@ -969,16 +972,24 @@ } protected String genSafepointInfo(NMethod nm, PCDesc pcDesc) { - ScopeDesc sd = nm.getScopeDescAt(pcDesc.getRealPC(nm)); - Formatter buf = new Formatter(genHTML); - Formatter tabs = new Formatter(genHTML); + ScopeDesc sd = nm.getScopeDescAt(pcDesc.getRealPC(nm)); + Formatter buf = new Formatter(genHTML); + Formatter tabs = new Formatter(genHTML); + tabs.append(tab + tab + tab); // Initial indent for debug info + + buf.beginTag("pre"); + genScope(buf, tabs, sd); - buf.beginTag("pre"); - genScope(buf, tabs, sd); - buf.endTag("pre"); - buf.append(genOopMapInfo(nm, pcDesc)); + // Reset indent for scalar replaced objects + tabs = new Formatter(genHTML); + tabs.append(tab + tab + tab); // Initial indent for debug info - return buf.toString(); + genScObjInfo(buf, tabs, sd); + buf.endTag("pre"); + + buf.append(genOopMapInfo(nm, pcDesc)); + + return buf.toString(); } protected void genScope(Formatter buf, Formatter tabs, ScopeDesc sd) { @@ -1022,8 +1033,95 @@ buf.append(genHTMLForMonitors(sd, monitors)); } + buf.br(); tabs.append(tab); - buf.br(); + } + + protected void genScObjInfo(Formatter buf, Formatter tabs, ScopeDesc sd) { + if (sd == null) { + return; + } + + List objects = sd.getObjects(); + if (objects == null) { + return; + } + int length = objects.size(); + for (int i = 0; i < length; i++) { + buf.append(tabs); + ObjectValue ov = (ObjectValue)objects.get(i); + buf.append("ScObj" + i); + ScopeValue sv = ov.getKlass(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(sv.isConstantOop(), "scalar replaced object klass must be constant oop"); + } + ConstantOopReadValue klv = (ConstantOopReadValue)sv; + OopHandle klHandle = klv.getValue(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(klHandle != null, "scalar replaced object klass must be not NULL"); + } + Oop obj = VM.getVM().getObjectHeap().newOop(klHandle); + if (obj instanceof InstanceKlass) { + InstanceKlass kls = (InstanceKlass) obj; + buf.append(" " + kls.getName().asString() + "={"); + int flen = ov.fieldsSize(); + + TypeArray klfields = kls.getFields(); + int klen = (int) klfields.getLength(); + + ConstantPool cp = kls.getConstants(); + int findex = 0; + for (int index = 0; index < klen; index += kls.NEXT_OFFSET) { + int accsFlags = klfields.getShortAt(index + kls.ACCESS_FLAGS_OFFSET); + int nameIndex = klfields.getShortAt(index + kls.NAME_INDEX_OFFSET); + AccessFlags access = new AccessFlags(accsFlags); + if (!access.isStatic()) { + ScopeValue svf = ov.getFieldAt(findex++); + String fstr = scopeValueAsString(sd, svf); + Symbol f_name = cp.getSymbolAt(nameIndex); + buf.append(" [" + f_name.asString() + " :"+ index + "]=(#" + fstr + ")"); + } + } + buf.append(" }"); + } else { + buf.append(" "); + int flen = ov.fieldsSize(); + if (obj instanceof TypeArrayKlass) { + TypeArrayKlass kls = (TypeArrayKlass) obj; + buf.append(kls.getElementTypeName() + "[" + flen + "]"); + } else if (obj instanceof ObjArrayKlass) { + ObjArrayKlass kls = (ObjArrayKlass) obj; + Klass elobj = kls.getBottomKlass(); + if (elobj instanceof InstanceKlass) { + buf.append(elobj.getName().asString()); + } else if (elobj instanceof TypeArrayKlass) { + TypeArrayKlass elkls = (TypeArrayKlass) elobj; + buf.append(elkls.getElementTypeName()); + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "unknown scalar replaced object klass!"); + } + } + buf.append("[" + flen + "]"); + int ndim = (int) kls.getDimension(); + while (--ndim > 0) { + buf.append("[]"); + } + } else { + if (Assert.ASSERTS_ENABLED) { + Assert.that(false, "unknown scalar replaced object klass!"); + } + } + buf.append("={"); + for (int findex = 0; findex < flen; findex++) { + ScopeValue svf = ov.getFieldAt(findex); + String fstr = scopeValueAsString(sd, svf); + buf.append(" [" + findex + "]=(#" + fstr + ")"); + } + buf.append(" }"); + } + buf.br(); + } } protected String genHTMLForOopMap(OopMap map) { @@ -1037,8 +1135,6 @@ tmpBuf.beginTag("tr"); tmpBuf.beginTag("td"); tmpBuf.append(type); - tmpBuf.endTag("td"); - tmpBuf.endTag("tr"); for (; ! oms.isDone(); oms.next()) { OopMapValue omv = oms.getCurrent(); if (omv == null) { @@ -1048,7 +1144,7 @@ VMReg vmReg = omv.getReg(); int reg = vmReg.getValue(); if (reg < stack0) { - tmpBuf.append(VMRegImpl.getRegisterName(vmReg.getValue())); + tmpBuf.append(VMRegImpl.getRegisterName(reg)); } else { tmpBuf.append('['); tmpBuf.append(Integer.toString((reg - stack0) * 4)); @@ -1058,7 +1154,13 @@ tmpBuf.append(" = "); VMReg vmContentReg = omv.getContentReg(); int contentReg = vmContentReg.getValue(); - tmpBuf.append(VMRegImpl.getRegisterName(vmContentReg.getValue())); + if (contentReg < stack0) { + tmpBuf.append(VMRegImpl.getRegisterName(contentReg)); + } else { + tmpBuf.append('['); + tmpBuf.append(Integer.toString((contentReg - stack0) * 4)); + tmpBuf.append(']'); + } } tmpBuf.append(spaces); } @@ -1072,19 +1174,19 @@ OopMapValueIterator omvIterator = new OopMapValueIterator(); OopMapStream oms = new OopMapStream(map, OopMapValue.OopTypes.OOP_VALUE); - buf.append(omvIterator.iterate(oms, "Oop:", false)); + buf.append(omvIterator.iterate(oms, "Oops:", false)); + + oms = new OopMapStream(map, OopMapValue.OopTypes.NARROWOOP_VALUE); + buf.append(omvIterator.iterate(oms, "narrowOops:", false)); oms = new OopMapStream(map, OopMapValue.OopTypes.VALUE_VALUE); - buf.append(omvIterator.iterate(oms, "Value:", false)); - - oms = new OopMapStream(map, OopMapValue.OopTypes.NARROWOOP_VALUE); - buf.append(omvIterator.iterate(oms, "Oop:", false)); + buf.append(omvIterator.iterate(oms, "Values:", false)); oms = new OopMapStream(map, OopMapValue.OopTypes.CALLEE_SAVED_VALUE); buf.append(omvIterator.iterate(oms, "Callee saved:", true)); oms = new OopMapStream(map, OopMapValue.OopTypes.DERIVED_OOP_VALUE); - buf.append(omvIterator.iterate(oms, "Derived oop:", true)); + buf.append(omvIterator.iterate(oms, "Derived oops:", true)); buf.endTag("table"); return buf.toString(); @@ -1093,6 +1195,8 @@ protected String genOopMapInfo(NMethod nmethod, PCDesc pcDesc) { OopMapSet mapSet = nmethod.getOopMaps(); + if (mapSet == null || (mapSet.getSize() <= 0)) + return ""; int pcOffset = pcDesc.getPCOffset(); OopMap map = mapSet.findMapAtOffset(pcOffset, VM.getVM().isDebugging()); if (map == null) { @@ -1106,6 +1210,7 @@ Formatter buf = new Formatter(genHTML); buf.beginTag("pre"); buf.append("OopMap: "); + buf.br(); buf.append(genHTMLForOopMap(map)); buf.endTag("pre"); @@ -1154,7 +1259,7 @@ return buf.toString(); } - private String scopeValueAsString(ScopeValue sv) { + private String scopeValueAsString(ScopeDesc sd, ScopeValue sv) { Formatter buf = new Formatter(genHTML); if (sv.isConstantInt()) { buf.append("int "); @@ -1187,6 +1292,11 @@ } else { buf.append("null"); } + } else if (sv.isObject()) { + ObjectValue ov = (ObjectValue)sv; + buf.append("#ScObj" + sd.getObjects().indexOf(ov)); + } else { + buf.append("unknown scope value " + sv); } return buf.toString(); } @@ -1219,7 +1329,7 @@ } buf.append(", "); - buf.append(scopeValueAsString(sv)); + buf.append(scopeValueAsString(sd, sv)); buf.append(") "); } @@ -1246,7 +1356,7 @@ buf.append("(owner = "); ScopeValue owner = mv.owner(); if (owner != null) { - buf.append(scopeValueAsString(owner)); + buf.append(scopeValueAsString(sd, owner)); } else { buf.append("null"); } @@ -1324,11 +1434,11 @@ buf.append(instr.asString(currentPc, symFinder)); } + buf.br(); if (isSafepoint && !prevWasCall) { - buf.append(genSafepointInfo(nmethod, pcDesc)); + buf.append(genSafepointInfo(nmethod, pcDesc)); } - buf.br(); prevWasCall = instr.isCall(); } diff -r 39e409a664b3 -r 84043c7507b9 agent/src/share/classes/sun/jvm/hotspot/utilities/soql/sa.js --- a/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/sa.js Thu Mar 25 16:27:12 2010 -0700 +++ b/agent/src/share/classes/sun/jvm/hotspot/utilities/soql/sa.js Thu Mar 25 16:54:59 2010 -0700 @@ -1047,7 +1047,7 @@ } else { // some type names have ':'. replace to make it as a // JavaScript identifier - tmp.name = tmp.name.replace(':', '_'); + tmp.name = tmp.name.replace(':', '_').replace('<', '_').replace('>', '_').replace('*', '_').replace(' ', '_'); eval("function read" + tmp.name + "(addr) {" + " return readVMType('" + tmp.name + "', addr);}"); eval("function print" + tmp.name + "(addr) {" + diff -r 39e409a664b3 -r 84043c7507b9 make/Makefile --- a/make/Makefile Thu Mar 25 16:27:12 2010 -0700 +++ b/make/Makefile Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ # -# Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2005-2010 Sun Microsystems, 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 @@ -84,6 +84,7 @@ C1_VM_TARGETS=product1 fastdebug1 optimized1 jvmg1 C2_VM_TARGETS=product fastdebug optimized jvmg KERNEL_VM_TARGETS=productkernel fastdebugkernel optimizedkernel jvmgkernel +ZERO_VM_TARGETS=productzero fastdebugzero optimizedzero jvmgzero # JDK directory list JDK_DIRS=bin include jre lib demo @@ -94,6 +95,12 @@ all_debug: jvmg jvmg1 jvmgkernel docs export_debug all_optimized: optimized optimized1 optimizedkernel docs export_optimized +allzero: all_productzero all_fastdebugzero +all_productzero: productzero docs export_product +all_fastdebugzero: fastdebugzero docs export_fastdebug +all_debugzero: jvmgzero docs export_debug +all_optimizedzero: optimizedzero docs export_optimized + # Do everything world: all create_jdk @@ -120,6 +127,10 @@ $(CD) $(GAMMADIR)/make; \ $(MAKE) VM_TARGET=$@ generic_buildkernel $(ALT_OUT) +$(ZERO_VM_TARGETS): + $(CD) $(GAMMADIR)/make; \ + $(MAKE) VM_TARGET=$@ generic_buildzero $(ALT_OUT) + # Build compiler1 (client) rule, different for platforms generic_build1: $(MKDIR) -p $(OUTPUTDIR) @@ -180,6 +191,12 @@ @$(ECHO) "No kernel ($(VM_TARGET)) for OS_NAME=$(OSNAME)" endif +generic_buildzero: + $(MKDIR) -p $(OUTPUTDIR) + $(CD) $(OUTPUTDIR); \ + $(MAKE) -f $(ABS_OS_MAKEFILE) \ + $(MAKE_ARGS) $(VM_TARGET) + # Export file rule generic_export: $(EXPORT_LIST) export_product: @@ -210,11 +227,17 @@ C1_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_compiler1 C2_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_compiler2 KERNEL_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_kernel +ZERO_BASE_DIR=$(OUTPUTDIR)/$(VM_PLATFORM)_zero C1_DIR=$(C1_BASE_DIR)/$(VM_SUBDIR) C2_DIR=$(C2_BASE_DIR)/$(VM_SUBDIR) KERNEL_DIR=$(KERNEL_BASE_DIR)/$(VM_SUBDIR) +ZERO_DIR=$(ZERO_BASE_DIR)/$(VM_SUBDIR) # Misc files and generated files need to come from C1 or C2 area +ifeq ($(ZERO_BUILD), true) + MISC_DIR=$(ZERO_DIR) + GEN_DIR=$(ZERO_BASE_DIR)/generated +else ifeq ($(ARCH_DATA_MODEL), 32) MISC_DIR=$(C1_DIR) GEN_DIR=$(C1_BASE_DIR)/generated @@ -222,6 +245,7 @@ MISC_DIR=$(C2_DIR) GEN_DIR=$(C2_BASE_DIR)/generated endif +endif # Bin files (windows) ifeq ($(OSNAME),windows) @@ -265,6 +289,12 @@ # Shared Library ifneq ($(OSNAME),windows) + ifeq ($(ZERO_BUILD), true) +$(EXPORT_JRE_LIB_ARCH_DIR)/%.so: $(ZERO_DIR)/%.so + $(install-file) +$(EXPORT_SERVER_DIR)/%.so: $(ZERO_DIR)/%.so + $(install-file) + else $(EXPORT_JRE_LIB_ARCH_DIR)/%.so: $(C2_DIR)/%.so $(install-file) $(EXPORT_CLIENT_DIR)/%.so: $(C1_DIR)/%.so @@ -275,16 +305,20 @@ $(install-file) $(EXPORT_SERVER_DIR)/64/%.so: $(C2_DIR)/%.so $(install-file) + endif endif # Jar file (sa-jdi.jar) $(EXPORT_LIB_DIR)/%.jar: $(GEN_DIR)/%.jar $(install-file) -# Include files (jvmti.h, jni.h, $(JDK_INCLUDE_SUBDIR)/jni_md.h, jmm.h) +# Include files (jvmti.h, jvmticmlr.h, jni.h, $(JDK_INCLUDE_SUBDIR)/jni_md.h, jmm.h) $(EXPORT_INCLUDE_DIR)/%: $(GEN_DIR)/jvmtifiles/% $(install-file) +$(EXPORT_INCLUDE_DIR)/%: $(HS_SRC_DIR)/share/vm/code/% + $(install-file) + $(EXPORT_INCLUDE_DIR)/%: $(HS_SRC_DIR)/share/vm/prims/% $(install-file) @@ -313,6 +347,7 @@ $(RM) -r $(C1_DIR) $(RM) -r $(C2_DIR) $(RM) -r $(KERNEL_DIR) + $(RM) -r $(ZERO_DIR) clean_export: $(RM) -r $(EXPORT_PATH) clean_jdk: @@ -335,8 +370,10 @@ ($(CD) $(JDK_IMAGE_DIR) && $(TAR) -xf -) test_jdk: - ifeq ($(ARCH_DATA_MODEL), 32) + ifneq ($(ZERO_BUILD), true) + ifeq ($(ARCH_DATA_MODEL), 32) $(JDK_IMAGE_DIR)/bin/java -client -version + endif endif $(JDK_IMAGE_DIR)/bin/java -server -version diff -r 39e409a664b3 -r 84043c7507b9 make/defs.make --- a/make/defs.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/defs.make Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ # -# Copyright 2006-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2006-2010 Sun Microsystems, 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 @@ -192,13 +192,14 @@ # 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,$(ARCH))) + SRCARCH = $(ARCH/$(filter sparc sparc64 ia64 amd64 x86_64 zero,$(ARCH))) ARCH/ = x86 ARCH/sparc = sparc ARCH/sparc64= sparc ARCH/ia64 = ia64 ARCH/amd64 = x86 ARCH/x86_64 = x86 + ARCH/zero = zero # BUILDARCH is usually the same as SRCARCH, except for sparcv9 BUILDARCH = $(SRCARCH) @@ -222,8 +223,9 @@ LIBARCH/sparc = sparc LIBARCH/sparcv9 = sparcv9 LIBARCH/ia64 = ia64 + LIBARCH/zero = $(ZERO_LIBARCH) - LP64_ARCH = sparcv9 amd64 ia64 + LP64_ARCH = sparcv9 amd64 ia64 zero endif # Required make macro settings for all platforms @@ -259,6 +261,7 @@ # Common export list of files EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jvmti.h +EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jvmticmlr.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jni.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/$(JDK_INCLUDE_SUBDIR)/jni_md.h EXPORT_LIST += $(EXPORT_INCLUDE_DIR)/jmm.h diff -r 39e409a664b3 -r 84043c7507b9 make/hotspot_version --- a/make/hotspot_version Thu Mar 25 16:27:12 2010 -0700 +++ b/make/hotspot_version Thu Mar 25 16:54:59 2010 -0700 @@ -33,9 +33,9 @@ # Don't put quotes (fail windows build). HOTSPOT_VM_COPYRIGHT=Copyright 2009 -HS_MAJOR_VER=16 +HS_MAJOR_VER=17 HS_MINOR_VER=0 -HS_BUILD_NUMBER=08 +HS_BUILD_NUMBER=10 JDK_MAJOR_VER=1 JDK_MINOR_VER=7 diff -r 39e409a664b3 -r 84043c7507b9 make/jprt.gmk --- a/make/jprt.gmk Thu Mar 25 16:27:12 2010 -0700 +++ b/make/jprt.gmk Thu Mar 25 16:54:59 2010 -0700 @@ -29,17 +29,24 @@ MILESTONE=$(JPRT_BUILD_VERSION) endif +ifeq ($(OSNAME),windows) + ZIPFLAGS=-q +else + # store symbolic links as the link + ZIPFLAGS=-q -y +endif + jprt_build_product: all_product copy_product_jdk export_product_jdk ( $(CD) $(JDK_IMAGE_DIR) && \ - $(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . ) + $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) jprt_build_fastdebug: all_fastdebug copy_fastdebug_jdk export_fastdebug_jdk ( $(CD) $(JDK_IMAGE_DIR)/fastdebug && \ - $(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . ) + $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) jprt_build_debug: all_debug copy_debug_jdk export_debug_jdk ( $(CD) $(JDK_IMAGE_DIR)/debug && \ - $(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . ) + $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) .PHONY: jprt_build_product jprt_build_fastdebug jprt_build_debug diff -r 39e409a664b3 -r 84043c7507b9 make/jprt.properties --- a/make/jprt.properties Thu Mar 25 16:27:12 2010 -0700 +++ b/make/jprt.properties Thu Mar 25 16:54:59 2010 -0700 @@ -40,6 +40,10 @@ jprt.tools.default.release=${jprt.submit.release} +# Disable syncing the source after builds and tests are done. + +jprt.sync.push=false + # Define the Solaris platforms we want for the various releases jprt.my.solaris.sparc.jdk7=solaris_sparc_5.10 diff -r 39e409a664b3 -r 84043c7507b9 make/linux/Makefile --- a/make/linux/Makefile Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/Makefile Thu Mar 25 16:54:59 2010 -0700 @@ -132,6 +132,9 @@ 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: # @@ -158,6 +161,13 @@ # profiledcore core __core/profiled # productcore core __core/product # +# debugzero zero __zero/debug +# fastdebugzero zero __zero/fastdebug +# jvmgzero zero __zero/jvmg +# optimizedzero zero __zero/optimized +# profiledzero zero __zero/profiled +# productzero zero __zero/product +# # What you get with each target: # # debug* - "thin" libjvm_g - debug info linked into the gamma_g launcher @@ -171,16 +181,22 @@ # in the build.sh script: TARGETS = debug jvmg fastdebug optimized profiled product -SUBDIR_DOCS = $(OSNAME)_$(BUILDARCH)_docs +ifeq ($(ZERO_BUILD), 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)) TARGETS_C2 = $(TARGETS) TARGETS_C1 = $(addsuffix 1,$(TARGETS)) TARGETS_TIERED = $(addsuffix tiered,$(TARGETS)) TARGETS_CORE = $(addsuffix core,$(TARGETS)) +TARGETS_ZERO = $(addsuffix zero,$(TARGETS)) BUILDTREE_MAKE = $(GAMMADIR)/make/$(OSNAME)/makefiles/buildtree.make BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) ARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) @@ -196,6 +212,7 @@ @echo " $(TARGETS_C2)" @echo " $(TARGETS_C1)" @echo " $(TARGETS_CORE)" + @echo " $(TARGETS_ZERO)" checks: check_os_version check_j2se_version @@ -245,6 +262,13 @@ $(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) + +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) @@ -275,10 +299,18 @@ cd $(OSNAME)_$(BUILDARCH)_core/$(patsubst %core,%,$@) && $(MAKE) $(MFLAGS) install endif +$(TARGETS_ZERO): $(SUBDIRS_ZERO) + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && ./test_gamma +ifdef INSTALL + cd $(OSNAME)_$(VARIANTARCH)_zero/$(patsubst %zero,%,$@) && $(MAKE) $(MFLAGS) install +endif + # Just build the tree, and nothing else: tree: $(SUBDIRS_C2) tree1: $(SUBDIRS_C1) treecore: $(SUBDIRS_CORE) +treezero: $(SUBDIRS_ZERO) # Doc target. This is the same for all build options. # Hence create a docs directory beside ...$(ARCH)_[...] @@ -293,20 +325,22 @@ core: jvmgcore productcore +zero: jvmgzero productzero + clean_docs: rm -rf $(SUBDIR_DOCS) -clean_compiler1 clean_compiler2 clean_core: +clean_compiler1 clean_compiler2 clean_core clean_zero: rm -rf $(OSNAME)_$(BUILDARCH)_$(subst clean_,,$@) -clean: clean_compiler2 clean_compiler1 clean_core clean_docs +clean: clean_compiler2 clean_compiler1 clean_core clean_zero clean_docs include $(GAMMADIR)/make/$(OSNAME)/makefiles/cscope.make #------------------------------------------------------------------------------- -.PHONY: $(TARGETS_C2) $(TARGETS_C1) $(TARGETS_CORE) -.PHONY: tree tree1 treecore -.PHONY: all compiler1 compiler2 core -.PHONY: clean clean_compiler1 clean_compiler2 clean_core docs clean_docs +.PHONY: $(TARGETS_C2) $(TARGETS_C1) $(TARGETS_CORE) $(TARGETS_ZERO) +.PHONY: tree tree1 treecore treezero +.PHONY: all compiler1 compiler2 core zero +.PHONY: clean clean_compiler1 clean_compiler2 clean_core clean_zero docs clean_docs .PHONY: checks check_os_version check_j2se_version diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/buildtree.make --- a/make/linux/makefiles/buildtree.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/buildtree.make Thu Mar 25 16:54:59 2010 -0700 @@ -63,20 +63,30 @@ # For now, until the compiler is less wobbly: TESTFLAGS = -Xbatch -showversion -ifdef USE_SUNCC -PLATFORM_FILE = $(GAMMADIR)/make/$(OS_FAMILY)/platform_$(BUILDARCH).suncc +ifeq ($(ZERO_BUILD), true) + PLATFORM_FILE = $(shell dirname $(shell dirname $(shell pwd)))/platform_zero else -PLATFORM_FILE = $(GAMMADIR)/make/$(OS_FAMILY)/platform_$(BUILDARCH) + 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)_$(BUILDARCH)_compiler2 +PLATFORM_DIR = $(OS_FAMILY)_$(VARIANTARCH)_compiler2 else -PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_$(VARIANT) +PLATFORM_DIR = $(OS_FAMILY)_$(VARIANTARCH)_$(VARIANT) endif else -PLATFORM_DIR = $(OS_FAMILY)_$(BUILDARCH)_$(VARIANT) +PLATFORM_DIR = $(OS_FAMILY)_$(VARIANTARCH)_$(VARIANT) endif # @@ -321,6 +331,7 @@ DATA_MODE/sparcv9 = 64 DATA_MODE/amd64 = 64 DATA_MODE/ia64 = 64 +DATA_MODE/zero = $(ARCH_DATA_MODEL) JAVA_FLAG/32 = -d32 JAVA_FLAG/64 = -d64 diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/debug.make --- a/make/linux/makefiles/debug.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/debug.make Thu Mar 25 16:54:59 2010 -0700 @@ -38,7 +38,7 @@ "Please use 'make jvmg' to build debug JVM. \n" \ "----------------------------------------------------------------------\n") -G_SUFFIX = +G_SUFFIX = _g VERSION = debug SYSDEFS += -DASSERT -DDEBUG PICFLAGS = DEFAULT diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/defs.make --- a/make/linux/makefiles/defs.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/defs.make Thu Mar 25 16:54:59 2010 -0700 @@ -37,6 +37,17 @@ ARCH_DATA_MODEL ?= 32 endif +# zero +ifeq ($(ZERO_BUILD), true) + ifeq ($(ARCH_DATA_MODEL), 64) + MAKE_ARGS += LP64=1 + endif + PLATFORM = linux-zero + VM_PLATFORM = linux_$(subst i386,i486,$(ZERO_LIBARCH)) + HS_ARCH = zero + ARCH = zero +endif + # ia64 ifeq ($(ARCH), ia64) ARCH_DATA_MODEL = 64 @@ -93,21 +104,25 @@ VM_DEBUG=jvmg EXPORT_LIST += $(EXPORT_DOCS_DIR)/platform/jvmti/jvmti.html + +# client and server subdirectories have symbolic links to ../libjsig.so +EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libjsig.so + EXPORT_SERVER_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/server EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt -EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjsig.so EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.so -ifeq ($(ARCH_DATA_MODEL), 32) - EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client - EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt - EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjsig.so - EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.so - EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so - EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar -else - ifeq ($(ARCH),ia64) - else - EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so - EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar +ifneq ($(ZERO_BUILD), true) + ifeq ($(ARCH_DATA_MODEL), 32) + EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt + EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.so + EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so + EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar + else + ifeq ($(ARCH),ia64) + else + EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libsaproc.so + EXPORT_LIST += $(EXPORT_LIB_DIR)/sa-jdi.jar endif + endif endif diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/fastdebug.make --- a/make/linux/makefiles/fastdebug.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/fastdebug.make Thu Mar 25 16:54:59 2010 -0700 @@ -58,7 +58,7 @@ # Linker mapfile MAPFILE = $(GAMMADIR)/make/linux/makefiles/mapfile-vers-debug -G_SUFFIX = +G_SUFFIX = _g VERSION = optimized SYSDEFS += -DASSERT -DFASTDEBUG PICFLAGS = DEFAULT diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/gcc.make --- a/make/linux/makefiles/gcc.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/gcc.make Thu Mar 25 16:54:59 2010 -0700 @@ -52,6 +52,9 @@ VM_PICFLAG/AOUT = VM_PICFLAG = $(VM_PICFLAG/$(LINK_INTO)) +ifeq ($(ZERO_BUILD), true) +CFLAGS += $(LIBFFI_CFLAGS) +endif CFLAGS += $(VM_PICFLAG) CFLAGS += -fno-rtti CFLAGS += -fno-exceptions @@ -64,6 +67,7 @@ ARCHFLAG/ia64 = ARCHFLAG/sparc = -m32 -mcpu=v9 ARCHFLAG/sparcv9 = -m64 -mcpu=v9 +ARCHFLAG/zero = $(ZERO_ARCHFLAG) CFLAGS += $(ARCHFLAG) AOUT_FLAGS += $(ARCHFLAG) diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/jsig.make --- a/make/linux/makefiles/jsig.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/jsig.make Thu Mar 25 16:54:59 2010 -0700 @@ -25,9 +25,12 @@ # Rules to build signal interposition library, used by vm.make # libjsig[_g].so: signal interposition library -JSIG = jsig$(G_SUFFIX) +JSIG = jsig LIBJSIG = lib$(JSIG).so +JSIG_G = $(JSIG)$(G_SUFFIX) +LIBJSIG_G = lib$(JSIG_G).so + JSIGSRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/vm DEST_JSIG = $(JDK_LIBDIR)/$(LIBJSIG) @@ -50,6 +53,7 @@ @echo Making signal interposition lib... $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ $(LFLAGS_JSIG) $(JSIG_DEBUG_CFLAGS) -o $@ $< -ldl + $(QUIETLY) [ -f $(LIBJSIG_G) ] || { ln -s $@ $(LIBJSIG_G); } install_jsig: $(LIBJSIG) @echo "Copying $(LIBJSIG) to $(DEST_JSIG)" diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/jvmg.make --- a/make/linux/makefiles/jvmg.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/jvmg.make Thu Mar 25 16:54:59 2010 -0700 @@ -35,7 +35,7 @@ # Linker mapfile MAPFILE = $(GAMMADIR)/make/linux/makefiles/mapfile-vers-debug -G_SUFFIX = +G_SUFFIX = _g VERSION = debug SYSDEFS += -DASSERT -DDEBUG PICFLAGS = DEFAULT diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/jvmti.make --- a/make/linux/makefiles/jvmti.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/jvmti.make Thu Mar 25 16:54:59 2010 -0700 @@ -70,10 +70,10 @@ both = $(JvmtiGenClass) $(JvmtiSrcDir)/jvmti.xml $(JvmtiSrcDir)/jvmtiLib.xsl $(JvmtiGenClass): $(JvmtiGenSource) - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiGenSource) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JvmtiOutDir) $(JvmtiGenSource) $(JvmtiEnvFillClass): $(JvmtiEnvFillSource) - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -d $(JvmtiOutDir) $(JvmtiEnvFillSource) $(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl @echo Generating $@ diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/launcher.make --- a/make/linux/makefiles/launcher.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/launcher.make Thu Mar 25 16:54:59 2010 -0700 @@ -25,7 +25,9 @@ # Rules to build gamma launcher, used by vm.make # gamma[_g]: launcher -LAUNCHER = gamma$(G_SUFFIX) + +LAUNCHER = gamma +LAUNCHER_G = $(LAUNCHER)$(G_SUFFIX) LAUNCHERDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/launcher LAUNCHERFLAGS = $(ARCHFLAG) \ @@ -70,4 +72,5 @@ $(LINK_LAUNCHER/PRE_HOOK) \ $(LINK_LAUNCHER) $(LFLAGS_LAUNCHER) -o $@ $(LAUNCHER.o) $(LIBS_LAUNCHER); \ $(LINK_LAUNCHER/POST_HOOK) \ + [ -f $(LAUNCHER_G) ] || { ln -s $@ $(LAUNCHER_G); }; \ } diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/mapfile-vers-debug --- a/make/linux/makefiles/mapfile-vers-debug Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/mapfile-vers-debug Thu Mar 25 16:54:59 2010 -0700 @@ -74,6 +74,7 @@ JVM_CurrentTimeMillis; JVM_DefineClass; JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; JVM_DesiredAssertionStatus; JVM_DisableCompiler; JVM_DoPrivileged; diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/mapfile-vers-product --- a/make/linux/makefiles/mapfile-vers-product Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/mapfile-vers-product Thu Mar 25 16:54:59 2010 -0700 @@ -74,6 +74,7 @@ JVM_CurrentTimeMillis; JVM_DefineClass; JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; JVM_DesiredAssertionStatus; JVM_DisableCompiler; JVM_DoPrivileged; diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/rules.make --- a/make/linux/makefiles/rules.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/rules.make Thu Mar 25 16:54:59 2010 -0700 @@ -122,12 +122,20 @@ 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 $<; } diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/sa.make --- a/make/linux/makefiles/sa.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/sa.make Thu Mar 25 16:54:59 2010 -0700 @@ -52,10 +52,10 @@ 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. +# also, we don't build SA on Itanium or zero. all: - if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" ] ; then \ + if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" -a "$(SRCARCH)" != "zero" ] ; then \ $(MAKE) -f sa.make $(GENERATED)/sa-jdi.jar; \ fi @@ -74,8 +74,8 @@ mkdir -p $(SA_CLASSDIR); \ fi - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -g -d $(SA_CLASSDIR) $(AGENT_FILES1) - $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -g -d $(SA_CLASSDIR) $(AGENT_FILES2) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES1) + $(QUIETLY) $(REMOTE) $(COMPILE.JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES2) $(QUIETLY) $(REMOTE) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/saproc.make --- a/make/linux/makefiles/saproc.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/saproc.make Thu Mar 25 16:54:59 2010 -0700 @@ -25,9 +25,13 @@ # Rules to build serviceability agent library, used by vm.make # libsaproc[_g].so: serviceability agent -SAPROC = saproc$(G_SUFFIX) + +SAPROC = saproc LIBSAPROC = lib$(SAPROC).so +SAPROC_G = $(SAPROC)$(G_SUFFIX) +LIBSAPROC_G = lib$(SAPROC_G).so + AGENT_DIR = $(GAMMADIR)/agent SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family) @@ -49,10 +53,10 @@ endif # if $(AGENT_DIR) does not exist, we don't build SA -# also, we don't build SA on Itanium. +# also, we don't build SA on Itanium or zero. checkAndBuildSA: - $(QUIETLY) if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" ] ; then \ + $(QUIETLY) if [ -d $(AGENT_DIR) -a "$(SRCARCH)" != "ia64" -a "$(SRCARCH)" != "zero" ] ; then \ $(MAKE) -f vm.make $(LIBSAPROC); \ fi @@ -75,6 +79,7 @@ $(SA_DEBUG_CFLAGS) \ -o $@ \ -lthread_db + $(QUIETLY) [ -f $(LIBSAPROC_G) ] || { ln -s $@ $(LIBSAPROC_G); } install_saproc: checkAndBuildSA $(QUIETLY) if [ -e $(LIBSAPROC) ] ; then \ diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/top.make --- a/make/linux/makefiles/top.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/top.make Thu Mar 25 16:54:59 2010 -0700 @@ -24,7 +24,7 @@ # top.make is included in the Makefile in the build directories. # It DOES NOT include the vm dependency info in order to be faster. -# It's main job is to implement the incremental form of make lists. +# 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) @@ -74,6 +74,7 @@ Include_DBs/COMPILER1 = $(Include_DBs/CORE) $(VM)/includeDB_compiler1 Include_DBs/COMPILER2 = $(Include_DBs/CORE) $(VM)/includeDB_compiler2 Include_DBs/TIERED = $(Include_DBs/CORE) $(VM)/includeDB_compiler1 $(VM)/includeDB_compiler2 +Include_DBs/ZERO = $(Include_DBs/CORE) $(VM)/includeDB_zero Include_DBs = $(Include_DBs/$(TYPE)) Cached_plat = $(GENERATED)/platform.current @@ -114,7 +115,7 @@ # make makeDeps: (and zap the cached db files to force a nonincremental run) $(GENERATED)/$(MakeDepsClass): $(MakeDepsSources) - @$(REMOTE) $(COMPILE.JAVAC) -classpath $(GAMMADIR)/src/share/tools/MakeDeps -g -d $(GENERATED) $(MakeDepsSources) + @$(REMOTE) $(COMPILE.JAVAC) -classpath $(GAMMADIR)/src/share/tools/MakeDeps -d $(GENERATED) $(MakeDepsSources) @echo Removing $(Incremental_Lists) to force regeneration. @rm -f $(Incremental_Lists) @$(CDG) echo >$(Cached_plat) diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/vm.make --- a/make/linux/makefiles/vm.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/linux/makefiles/vm.make Thu Mar 25 16:54:59 2010 -0700 @@ -40,7 +40,11 @@ include $(GENERATED)/Dependencies # read machine-specific adjustments (%%% should do this via buildtree.make?) -include $(MAKEFILES_DIR)/$(BUILDARCH).make +ifeq ($(ZERO_BUILD), 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 is everything in src/share/vm/*, plus the right os/*/vm and cpu/*/vm @@ -109,8 +113,9 @@ #---------------------------------------------------------------------- # JVM -JVM = jvm$(G_SUFFIX) -LIBJVM = lib$(JVM).so +JVM = jvm +LIBJVM = lib$(JVM).so +LIBJVM_G = lib$(JVM)$(G_SUFFIX).so JVM_OBJ_FILES = $(Obj_Files) @@ -124,7 +129,11 @@ rm -f $@ cat $^ > $@ -STATIC_CXX = true +ifeq ($(ZERO_LIBARCH), ppc64) + STATIC_CXX = false +else + STATIC_CXX = true +endif ifeq ($(LINK_INTO),AOUT) LIBJVM.o = @@ -148,6 +157,9 @@ LIBS_VM += $(LIBS) endif +ifeq ($(ZERO_BUILD), true) + LIBS_VM += $(LIBFFI_LIBS) +endif LINK_VM = $(LINK_LIB.c) @@ -190,6 +202,7 @@ $(LFLAGS_VM) -o $@ $(LIBJVM.o) $(LIBS_VM); \ $(LINK_LIB.CC/POST_HOOK) \ rm -f $@.1; ln -s $@ $@.1; \ + [ -f $(LIBJVM_G) ] || { ln -s $@ $(LIBJVM_G); ln -s $@.1 $(LIBJVM_G).1; }; \ if [ -x /usr/sbin/selinuxenabled ] ; then \ /usr/sbin/selinuxenabled; \ if [ $$? = 0 ] ; then \ diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/zero.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/makefiles/zero.make Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,32 @@ +# +# Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2009 Red Hat, Inc. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Setup for Zero (non-Shark) version of VM + +# Select which includeDB files to use (in top.make) +TYPE = ZERO + +# Install libjvm.so, etc in in server directory. +VM_SUBDIR = server diff -r 39e409a664b3 -r 84043c7507b9 make/linux/makefiles/zeroshark.make --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/makefiles/zeroshark.make Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,43 @@ +# +# Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2007, 2008 Red Hat, Inc. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Setup common to Zero (non-Shark) and Shark versions of VM + +# 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 +OPT_CFLAGS/sharedRuntimeTrans.o = $(OPT_CFLAGS/NOOPT) + +# Specify that the CPU is little endian, if necessary +ifeq ($(ZERO_ENDIANNESS), little) + CFLAGS += -DVM_LITTLE_ENDIAN +endif + +# Specify that the CPU is 64 bit, if necessary +ifeq ($(ARCH_DATA_MODEL), 64) + CFLAGS += -D_LP64=1 +endif + +OPT_CFLAGS/compactingPermGenGen.o = -O1 diff -r 39e409a664b3 -r 84043c7507b9 make/linux/platform_zero.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/linux/platform_zero.in Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,17 @@ +os_family = linux + +arch = zero + +arch_model = zero + +os_arch = linux_zero + +os_arch_model = linux_zero + +lib_arch = zero + +compiler = gcc + +gnu_dis_arch = zero + +sysdefs = -DLINUX -D_GNU_SOURCE -DCC_INTERP -DZERO -D@ZERO_ARCHDEF@ -DZERO_LIBARCH=\"@ZERO_LIBARCH@\" diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/debug.make --- a/make/solaris/makefiles/debug.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/debug.make Thu Mar 25 16:54:59 2010 -0700 @@ -54,7 +54,7 @@ "Please use 'gnumake jvmg' to build debug JVM. \n" \ "-------------------------------------------------------------------------\n") -G_SUFFIX = +G_SUFFIX = _g VERSION = debug SYSDEFS += -DASSERT -DDEBUG PICFLAGS = DEFAULT diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/defs.make --- a/make/solaris/makefiles/defs.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/defs.make Thu Mar 25 16:54:59 2010 -0700 @@ -65,16 +65,18 @@ VM_DEBUG=jvmg EXPORT_LIST += $(EXPORT_DOCS_DIR)/platform/jvmti/jvmti.html + +# client and server subdirectories have symbolic links to ../libjsig.so +EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libjsig.so + EXPORT_SERVER_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/server EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt -EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjsig.so EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm.so EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm_db.so EXPORT_LIST += $(EXPORT_SERVER_DIR)/libjvm_dtrace.so ifeq ($(ARCH_DATA_MODEL), 32) EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt - EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjsig.so EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.so EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm_db.so EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm_dtrace.so diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/dtrace.make --- a/make/solaris/makefiles/dtrace.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/dtrace.make Thu Mar 25 16:54:59 2010 -0700 @@ -24,8 +24,8 @@ # Rules to build jvm_db/dtrace, used by vm.make -# we build libjvm_dtrace/libjvm_db/dtrace for COMPILER1 and COMPILER2 -# but not for CORE configuration +# We build libjvm_dtrace/libjvm_db/dtrace for COMPILER1 and COMPILER2 +# but not for CORE or KERNEL configurations. ifneq ("${TYPE}", "CORE") ifneq ("${TYPE}", "KERNEL") @@ -37,12 +37,13 @@ else - JVM_DB = libjvm_db -LIBJVM_DB = libjvm$(G_SUFFIX)_db.so +LIBJVM_DB = libjvm_db.so +LIBJVM_DB_G = libjvm$(G_SUFFIX)_db.so JVM_DTRACE = jvm_dtrace -LIBJVM_DTRACE = libjvm$(G_SUFFIX)_dtrace.so +LIBJVM_DTRACE = libjvm_dtrace.so +LIBJVM_DTRACE_G = libjvm$(G_SUFFIX)_dtrace.so JVMOFFS = JvmOffsets JVMOFFS.o = $(JVMOFFS).o @@ -77,7 +78,7 @@ LFLAGS_JVM_DTRACE += -D_REENTRANT $(PICFLAG) else LFLAGS_JVM_DB += -mt $(PICFLAG) -xnolib -LFLAGS_JVM_DTRACE += -mt $(PICFLAG) -xnolib +LFLAGS_JVM_DTRACE += -mt $(PICFLAG) -xnolib -ldl endif ISA = $(subst i386,i486,$(shell isainfo -n)) @@ -86,18 +87,24 @@ ifneq ("${ISA}","${BUILDARCH}") XLIBJVM_DB = 64/$(LIBJVM_DB) +XLIBJVM_DB_G = 64/$(LIBJVM_DB_G) XLIBJVM_DTRACE = 64/$(LIBJVM_DTRACE) +XLIBJVM_DTRACE_G = 64/$(LIBJVM_DTRACE_G) $(XLIBJVM_DB): $(DTRACE_SRCDIR)/$(JVM_DB).c $(JVMOFFS).h $(LIBJVM_DB_MAPFILE) @echo Making $@ $(QUIETLY) mkdir -p 64/ ; \ $(CC) $(SYMFLAG) $(ARCHFLAG/$(ISA)) -D$(TYPE) -I. -I$(GENERATED) \ $(SHARED_FLAG) $(LFLAGS_JVM_DB) -o $@ $(DTRACE_SRCDIR)/$(JVM_DB).c -lc + [ -f $(XLIBJVM_DB_G) ] || { ln -s $(LIBJVM_DB) $(XLIBJVM_DB_G); } + $(XLIBJVM_DTRACE): $(DTRACE_SRCDIR)/$(JVM_DTRACE).c $(DTRACE_SRCDIR)/$(JVM_DTRACE).h $(LIBJVM_DTRACE_MAPFILE) @echo Making $@ $(QUIETLY) mkdir -p 64/ ; \ $(CC) $(SYMFLAG) $(ARCHFLAG/$(ISA)) -D$(TYPE) -I. \ $(SHARED_FLAG) $(LFLAGS_JVM_DTRACE) -o $@ $(DTRACE_SRCDIR)/$(JVM_DTRACE).c -lc -lthread -ldoor + [ -f $(XLIBJVM_DTRACE_G) ] || { ln -s $(LIBJVM_DTRACE) $(XLIBJVM_DTRACE_G); } + endif # ifneq ("${ISA}","${BUILDARCH}") ifdef USE_GCC @@ -142,11 +149,13 @@ @echo Making $@ $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) -D$(TYPE) -I. -I$(GENERATED) \ $(SHARED_FLAG) $(LFLAGS_JVM_DB) -o $@ $(DTRACE_SRCDIR)/$(JVM_DB).c -lc + [ -f $(LIBJVM_DB_G) ] || { ln -s $@ $(LIBJVM_DB_G); } $(LIBJVM_DTRACE): $(DTRACE_SRCDIR)/$(JVM_DTRACE).c $(XLIBJVM_DTRACE) $(DTRACE_SRCDIR)/$(JVM_DTRACE).h $(LIBJVM_DTRACE_MAPFILE) @echo Making $@ $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) -D$(TYPE) -I. \ $(SHARED_FLAG) $(LFLAGS_JVM_DTRACE) -o $@ $(DTRACE_SRCDIR)/$(JVM_DTRACE).c -lc -lthread -ldoor + [ -f $(LIBJVM_DTRACE_G) ] || { ln -s $@ $(LIBJVM_DTRACE_G); } $(DTRACE).d: $(DTRACE_SRCDIR)/hotspot.d $(DTRACE_SRCDIR)/hotspot_jni.d \ $(DTRACE_SRCDIR)/hs_private.d $(DTRACE_SRCDIR)/jhelper.d diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/fastdebug.make --- a/make/solaris/makefiles/fastdebug.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/fastdebug.make Thu Mar 25 16:54:59 2010 -0700 @@ -90,7 +90,6 @@ # for this method for now. (fix this when dtrace bug 6258412 is fixed) OPT_CFLAGS/ciEnv.o = $(OPT_CFLAGS) -xinline=no%__1cFciEnvbFpost_compiled_method_load_event6MpnHnmethod__v_ - # (OPT_CFLAGS/SLOWER is also available, to alter compilation of buggy files) # If you set HOTSPARC_GENERIC=yes, you disable all OPT_CFLAGS settings @@ -115,8 +114,7 @@ # and mustn't be otherwise. MAPFILE_DTRACE = $(GAMMADIR)/make/solaris/makefiles/mapfile-vers-$(TYPE) - -G_SUFFIX = +G_SUFFIX = _g VERSION = optimized SYSDEFS += -DASSERT -DFASTDEBUG -DCHECK_UNHANDLED_OOPS PICFLAGS = DEFAULT diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/jsig.make --- a/make/solaris/makefiles/jsig.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/jsig.make Thu Mar 25 16:54:59 2010 -0700 @@ -25,8 +25,11 @@ # Rules to build signal interposition library, used by vm.make # libjsig[_g].so: signal interposition library -JSIG = jsig$(G_SUFFIX) -LIBJSIG = lib$(JSIG).so +JSIG = jsig +LIBJSIG = lib$(JSIG).so + +JSIG_G = $(JSIG)$(G_SUFFIX) +LIBJSIG_G = lib$(JSIG_G).so JSIGSRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/vm @@ -46,6 +49,7 @@ @echo Making signal interposition lib... $(QUIETLY) $(CC) $(SYMFLAG) $(ARCHFLAG) $(SHARED_FLAG) $(PICFLAG) \ $(LFLAGS_JSIG) -o $@ $< -ldl + [ -f $(LIBJSIG_G) ] || { ln -s $@ $(LIBJSIG_G); } install_jsig: $(LIBJSIG) @echo "Copying $(LIBJSIG) to $(DEST_JSIG)" diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/jvmg.make --- a/make/solaris/makefiles/jvmg.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/jvmg.make Thu Mar 25 16:54:59 2010 -0700 @@ -51,7 +51,7 @@ # and mustn't be otherwise. MAPFILE_DTRACE = $(GAMMADIR)/make/solaris/makefiles/mapfile-vers-$(TYPE) -G_SUFFIX = +G_SUFFIX = _g VERSION = debug SYSDEFS += -DASSERT -DDEBUG PICFLAGS = DEFAULT diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/jvmti.make --- a/make/solaris/makefiles/jvmti.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/jvmti.make Thu Mar 25 16:54:59 2010 -0700 @@ -69,10 +69,10 @@ both = $(JvmtiGenClass) $(JvmtiSrcDir)/jvmti.xml $(JvmtiSrcDir)/jvmtiLib.xsl $(JvmtiGenClass): $(JvmtiGenSource) - $(QUIETLY) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiGenSource) + $(QUIETLY) $(COMPILE.JAVAC) -d $(JvmtiOutDir) $(JvmtiGenSource) $(JvmtiEnvFillClass): $(JvmtiEnvFillSource) - $(QUIETLY) $(COMPILE.JAVAC) -g -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + $(QUIETLY) $(COMPILE.JAVAC) -d $(JvmtiOutDir) $(JvmtiEnvFillSource) $(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl @echo Generating $@ diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/launcher.make --- a/make/solaris/makefiles/launcher.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/launcher.make Thu Mar 25 16:54:59 2010 -0700 @@ -25,7 +25,8 @@ # Rules to build gamma launcher, used by vm.make # gamma[_g]: launcher -LAUNCHER = gamma$(G_SUFFIX) +LAUNCHER = gamma +LAUNCHER_G = $(LAUNCHER)$(G_SUFFIX) LAUNCHERDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/launcher LAUNCHERFLAGS = $(ARCHFLAG) \ @@ -88,5 +89,6 @@ $(LINK_LAUNCHER/PRE_HOOK) \ $(LINK_LAUNCHER) $(LFLAGS_LAUNCHER) -o $@ $(LAUNCHER.o) $(LIBS_LAUNCHER); \ $(LINK_LAUNCHER/POST_HOOK) \ + [ -f $(LAUNCHER_G) ] || { ln -s $@ $(LAUNCHER_G); }; \ ;; \ esac diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/mapfile-vers --- a/make/solaris/makefiles/mapfile-vers Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/mapfile-vers Thu Mar 25 16:54:59 2010 -0700 @@ -74,6 +74,7 @@ JVM_CurrentTimeMillis; JVM_DefineClass; JVM_DefineClassWithSource; + JVM_DefineClassWithSourceCond; JVM_DesiredAssertionStatus; JVM_DisableCompiler; JVM_DoPrivileged; diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/rules.make --- a/make/solaris/makefiles/rules.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/rules.make Thu Mar 25 16:54:59 2010 -0700 @@ -122,12 +122,20 @@ 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 $<; } diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/sa.make --- a/make/solaris/makefiles/sa.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/sa.make Thu Mar 25 16:54:59 2010 -0700 @@ -67,8 +67,8 @@ $(QUIETLY) if [ ! -d $(SA_CLASSDIR) ] ; then \ mkdir -p $(SA_CLASSDIR); \ fi - $(QUIETLY) $(COMPILE.JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -g -d $(SA_CLASSDIR) $(AGENT_FILES1) - $(QUIETLY) $(COMPILE.JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -g -d $(SA_CLASSDIR) $(AGENT_FILES2) + $(QUIETLY) $(COMPILE.JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES1) + $(QUIETLY) $(COMPILE.JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES2) $(QUIETLY) $(COMPILE.RMIC) -classpath $(SA_CLASSDIR) -d $(SA_CLASSDIR) sun.jvm.hotspot.debugger.remote.RemoteDebuggerServer $(QUIETLY) echo "$(SA_BUILD_VERSION_PROP)" > $(SA_PROPERTIES) diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/saproc.make --- a/make/solaris/makefiles/saproc.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/saproc.make Thu Mar 25 16:54:59 2010 -0700 @@ -25,9 +25,13 @@ # Rules to build serviceability agent library, used by vm.make # libsaproc[_g].so: serviceability agent -SAPROC = saproc$(G_SUFFIX) + +SAPROC = saproc LIBSAPROC = lib$(SAPROC).so +SAPROC_G = $(SAPROC)$(G_SUFFIX) +LIBSAPROC_G = lib$(SAPROC_G).so + AGENT_DIR = $(GAMMADIR)/agent SASRCDIR = $(AGENT_DIR)/src/os/$(Platform_os_family)/proc @@ -69,6 +73,7 @@ $(SA_LFLAGS) \ -o $@ \ -ldl -ldemangle -lthread -lc + [ -f $(LIBSAPROC_G) ] || { ln -s $@ $(LIBSAPROC_G); } install_saproc: checkAndBuildSA $(QUIETLY) if [ -f $(LIBSAPROC) ] ; then \ diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/sparcWorks.make --- a/make/solaris/makefiles/sparcWorks.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/sparcWorks.make Thu Mar 25 16:54:59 2010 -0700 @@ -281,8 +281,6 @@ OPT_CFLAGS=-xO4 $(EXTRA_OPT_CFLAGS) endif -CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_sparc/vm/solaris_sparc.il - endif # sparc ifeq ("${Platform_arch_model}", "x86_32") @@ -293,13 +291,14 @@ # [phh] Is this still true for 6.1? OPT_CFLAGS+=-xO3 -CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_x86/vm/solaris_x86_32.il - endif # 32bit x86 # no more exceptions CFLAGS/NOEX=-noex +# Inline functions +CFLAGS += $(GAMMADIR)/src/os_cpu/solaris_${Platform_arch}/vm/solaris_${Platform_arch_model}.il + # Reduce code bloat by reverting back to 5.0 behavior for static initializers CFLAGS += -Qoption ccfe -one_static_init @@ -312,6 +311,15 @@ PICFLAG/BETTER = $(PICFLAG/DEFAULT) PICFLAG/BYFILE = $(PICFLAG/$@)$(PICFLAG/DEFAULT$(PICFLAG/$@)) +# Use $(MAPFLAG:FILENAME=real_file_name) to specify a map file. +MAPFLAG = -M FILENAME + +# Use $(SONAMEFLAG:SONAME=soname) to specify the intrinsic name of a shared obj +SONAMEFLAG = -h SONAME + +# Build shared library +SHARED_FLAG = -G + # Would be better if these weren't needed, since we link with CC, but # at present removing them causes run-time errors LFLAGS += -library=Crun diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/top.make --- a/make/solaris/makefiles/top.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/top.make Thu Mar 25 16:54:59 2010 -0700 @@ -24,7 +24,7 @@ # top.make is included in the Makefile in the build directories. # It DOES NOT include the vm dependency info in order to be faster. -# It's main job is to implement the incremental form of make lists. +# 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) @@ -112,7 +112,7 @@ # make makeDeps: (and zap the cached db files to force a nonincremental run) $(GENERATED)/$(MakeDepsClass): $(MakeDepsSources) - @$(COMPILE.JAVAC) -classpath $(GAMMADIR)/src/share/tools/MakeDeps -g -d $(GENERATED) $(MakeDepsSources) + @$(COMPILE.JAVAC) -classpath $(GAMMADIR)/src/share/tools/MakeDeps -d $(GENERATED) $(MakeDepsSources) @echo Removing $(Incremental_Lists) to force regeneration. @rm -f $(Incremental_Lists) @$(CDG) echo >$(Cached_plat) diff -r 39e409a664b3 -r 84043c7507b9 make/solaris/makefiles/vm.make --- a/make/solaris/makefiles/vm.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/solaris/makefiles/vm.make Thu Mar 25 16:54:59 2010 -0700 @@ -108,11 +108,16 @@ # older libm before libCrun, just to make sure it's found and used first. LIBS += -lsocket -lsched -ldl $(LIBM) -lCrun -lthread -ldoor -lc else +ifeq ($(COMPILER_REV_NUMERIC), 502) +# SC6.1 has it's own libm.so: specifying anything else provokes a name conflict. +LIBS += -ldl -lthread -lsocket -lm -lsched -ldoor +else LIBS += -ldl -lthread -lsocket $(LIBM) -lsched -ldoor -endif +endif # 502 +endif # 505 else LIBS += -lsocket -lsched -ldl $(LIBM) -lthread -lc -endif +endif # sparcWorks # By default, link the *.o into the library, not the executable. LINK_INTO$(LINK_INTO) = LIBJVM @@ -126,8 +131,9 @@ #---------------------------------------------------------------------- # JVM -JVM = jvm$(G_SUFFIX) -LIBJVM = lib$(JVM).so +JVM = jvm +LIBJVM = lib$(JVM).so +LIBJVM_G = lib$(JVM)$(G_SUFFIX).so JVM_OBJ_FILES = $(Obj_Files) $(DTRACE_OBJS) @@ -173,11 +179,12 @@ -sbfast|-xsbfast) \ ;; \ *) \ - echo Linking vm...; \ - $(LINK_LIB.CC/PRE_HOOK) \ - $(LINK_VM) $(LFLAGS_VM) -o $@ $(LIBJVM.o) $(LIBS_VM); \ - $(LINK_LIB.CC/POST_HOOK) \ - rm -f $@.1; ln -s $@ $@.1; \ + echo Linking vm...; \ + $(LINK_LIB.CC/PRE_HOOK) \ + $(LINK_VM) $(LFLAGS_VM) -o $@ $(LIBJVM.o) $(LIBS_VM); \ + $(LINK_LIB.CC/POST_HOOK) \ + rm -f $@.1; ln -s $@ $@.1; \ + [ -f $(LIBJVM_G) ] || { ln -s $@ $(LIBJVM_G); ln -s $@.1 $(LIBJVM_G).1; }; \ ;; \ esac diff -r 39e409a664b3 -r 84043c7507b9 make/windows/makefiles/generated.make --- a/make/windows/makefiles/generated.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/windows/makefiles/generated.make Thu Mar 25 16:54:59 2010 -0700 @@ -91,7 +91,7 @@ classes/MakeDeps.class: $(MakeDepsSources) if exist classes rmdir /s /q classes mkdir classes - $(COMPILE_JAVAC) -classpath $(WorkSpace)\src\share\tools\MakeDeps -g -d classes $(MakeDepsSources) + $(COMPILE_JAVAC) -classpath $(WorkSpace)\src\share\tools\MakeDeps -d classes $(MakeDepsSources) !if ("$(Variant)" == "compiler2") || ("$(Variant)" == "tiered") diff -r 39e409a664b3 -r 84043c7507b9 make/windows/makefiles/jvmti.make --- a/make/windows/makefiles/jvmti.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/windows/makefiles/jvmti.make Thu Mar 25 16:54:59 2010 -0700 @@ -68,10 +68,10 @@ @if not exist $(JvmtiOutDir) mkdir $(JvmtiOutDir) $(JvmtiGenClass): $(JvmtiGenSource) - $(COMPILE_JAVAC) -g -d $(JvmtiOutDir) $(JvmtiGenSource) + $(COMPILE_JAVAC) -d $(JvmtiOutDir) $(JvmtiGenSource) $(JvmtiEnvFillClass): $(JvmtiEnvFillSource) - @$(COMPILE_JAVAC) -g -d $(JvmtiOutDir) $(JvmtiEnvFillSource) + @$(COMPILE_JAVAC) -d $(JvmtiOutDir) $(JvmtiEnvFillSource) $(JvmtiOutDir)/jvmtiEnter.cpp: $(both) $(JvmtiSrcDir)/jvmtiEnter.xsl @echo Generating $@ diff -r 39e409a664b3 -r 84043c7507b9 make/windows/makefiles/rules.make --- a/make/windows/makefiles/rules.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/windows/makefiles/rules.make Thu Mar 25 16:54:59 2010 -0700 @@ -29,7 +29,7 @@ RUN_JAVAP=$(BootStrapDir)\bin\javap RUN_JAVAH=$(BootStrapDir)\bin\javah RUN_JAR=$(BootStrapDir)\bin\jar -COMPILE_JAVAC=$(BootStrapDir)\bin\javac +COMPILE_JAVAC=$(BootStrapDir)\bin\javac $(BOOTSTRAP_JAVAC_FLAGS) COMPILE_RMIC=$(BootStrapDir)\bin\rmic BOOT_JAVA_HOME=$(BootStrapDir) !else @@ -37,11 +37,17 @@ RUN_JAVAP=javap RUN_JAVAH=javah RUN_JAR=jar -COMPILE_JAVAC=javac +COMPILE_JAVAC=javac $(BOOTSTRAP_JAVAC_FLAGS) COMPILE_RMIC=rmic BOOT_JAVA_HOME= !endif +# 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) + ProjectFile=vm.vcproj !if "$(MSC_VER)" == "1200" diff -r 39e409a664b3 -r 84043c7507b9 make/windows/makefiles/sa.make --- a/make/windows/makefiles/sa.make Thu Mar 25 16:27:12 2010 -0700 +++ b/make/windows/makefiles/sa.make Thu Mar 25 16:54:59 2010 -0700 @@ -55,9 +55,9 @@ $(GENERATED)\sa-jdi.jar: $(AGENT_FILES1:/=\) $(AGENT_FILES2:/=\) @if not exist $(SA_CLASSDIR) mkdir $(SA_CLASSDIR) @echo ...Building sa-jdi.jar - @echo ...$(COMPILE_JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -g -d $(SA_CLASSDIR) .... - @$(COMPILE_JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -g -d $(SA_CLASSDIR) $(AGENT_FILES1:/=\) - @$(COMPILE_JAVAC) -source 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -g -d $(SA_CLASSDIR) $(AGENT_FILES2:/=\) + @echo ...$(COMPILE_JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -d $(SA_CLASSDIR) .... + @$(COMPILE_JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES1:/=\) + @$(COMPILE_JAVAC) -source 1.4 -target 1.4 -classpath $(SA_CLASSPATH) -sourcepath $(AGENT_SRC_DIR) -d $(SA_CLASSDIR) $(AGENT_FILES2:/=\) $(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 diff -r 39e409a664b3 -r 84043c7507b9 make/windows/projectfiles/common/Makefile --- a/make/windows/projectfiles/common/Makefile Thu Mar 25 16:27:12 2010 -0700 +++ b/make/windows/projectfiles/common/Makefile Thu Mar 25 16:54:59 2010 -0700 @@ -179,6 +179,6 @@ $(HOTSPOTBUILDSPACE)/classes/MakeDeps.class: $(MakeDepsSources) @if exist $(HOTSPOTBUILDSPACE)\classes rmdir /s /q $(HOTSPOTBUILDSPACE)\classes @mkdir $(HOTSPOTBUILDSPACE)\classes - @$(COMPILE_JAVAC) -classpath $(HOTSPOTWORKSPACE)\src\share\tools\MakeDeps -g -d $(HOTSPOTBUILDSPACE)/classes $(MakeDepsSources) + @$(COMPILE_JAVAC) -classpath $(HOTSPOTWORKSPACE)\src\share\tools\MakeDeps -d $(HOTSPOTBUILDSPACE)/classes $(MakeDepsSources) FORCE: diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/assembler_sparc.cpp --- a/src/cpu/sparc/vm/assembler_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -2631,13 +2631,13 @@ (src.is_register() && src.as_register() == G0)) { // do nothing } else if (dest.is_register()) { - add(dest.as_register(), ensure_rs2(src, temp), dest.as_register()); + add(dest.as_register(), ensure_simm13_or_reg(src, temp), dest.as_register()); } else if (src.is_constant()) { intptr_t res = dest.as_constant() + src.as_constant(); dest = RegisterOrConstant(res); // side effect seen by caller } else { assert(temp != noreg, "cannot handle constant += register"); - add(src.as_register(), ensure_rs2(dest, temp), temp); + add(src.as_register(), ensure_simm13_or_reg(dest, temp), temp); dest = RegisterOrConstant(temp); // side effect seen by caller } } @@ -2710,7 +2710,7 @@ RegisterOrConstant itable_offset = itable_index; regcon_sll_ptr(itable_offset, exact_log2(itableMethodEntry::size() * wordSize)); regcon_inc_ptr(itable_offset, itableMethodEntry::method_offset_in_bytes()); - add(recv_klass, ensure_rs2(itable_offset, sethi_temp), recv_klass); + add(recv_klass, ensure_simm13_or_reg(itable_offset, sethi_temp), recv_klass); // for (scan = klass->itable(); scan->interface() != NULL; scan += scan_step) { // if (scan->interface() == intf) { @@ -4676,3 +4676,50 @@ load_ptr_contents(base, G6_heapbase); } } + +// Compare char[] arrays aligned to 4 bytes. +void MacroAssembler::char_arrays_equals(Register ary1, Register ary2, + Register limit, Register result, + Register chr1, Register chr2, Label& Ldone) { + Label Lvector, Lloop; + assert(chr1 == result, "should be the same"); + + // Note: limit contains number of bytes (2*char_elements) != 0. + andcc(limit, 0x2, chr1); // trailing character ? + br(Assembler::zero, false, Assembler::pt, Lvector); + delayed()->nop(); + + // compare the trailing char + sub(limit, sizeof(jchar), limit); + lduh(ary1, limit, chr1); + lduh(ary2, limit, chr2); + cmp(chr1, chr2); + br(Assembler::notEqual, true, Assembler::pt, Ldone); + delayed()->mov(G0, result); // not equal + + // only one char ? + br_on_reg_cond(rc_z, true, Assembler::pn, limit, Ldone); + delayed()->add(G0, 1, result); // zero-length arrays are equal + + // word by word compare, dont't need alignment check + bind(Lvector); + // Shift ary1 and ary2 to the end of the arrays, negate limit + add(ary1, limit, ary1); + add(ary2, limit, ary2); + neg(limit, limit); + + lduw(ary1, limit, chr1); + bind(Lloop); + lduw(ary2, limit, chr2); + cmp(chr1, chr2); + br(Assembler::notEqual, true, Assembler::pt, Ldone); + delayed()->mov(G0, result); // not equal + inccc(limit, 2*sizeof(jchar)); + // annul LDUW if branch is not taken to prevent access past end of array + br(Assembler::notZero, true, Assembler::pt, Lloop); + delayed()->lduw(ary1, limit, chr1); // hoisted + + // Caller should set it: + // add(G0, 1, result); // equals +} + diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/assembler_sparc.hpp --- a/src/cpu/sparc/vm/assembler_sparc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1279,6 +1279,7 @@ // 171 + inline void ldf(FloatRegisterImpl::Width w, Register s1, RegisterOrConstant s2, FloatRegister d); inline void ldf(FloatRegisterImpl::Width w, Register s1, Register s2, FloatRegister d); inline void ldf(FloatRegisterImpl::Width w, Register s1, int simm13a, FloatRegister d, RelocationHolder const& rspec = RelocationHolder()); @@ -1535,7 +1536,8 @@ // pp 222 - inline void stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, Register s2 ); + inline void stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, RegisterOrConstant s2); + inline void stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, Register s2); inline void stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, int simm13a); inline void stf( FloatRegisterImpl::Width w, FloatRegister d, const Address& a, int offset = 0); @@ -2049,12 +2051,13 @@ Register temp = noreg ); void regcon_sll_ptr( RegisterOrConstant& dest, RegisterOrConstant src, Register temp = noreg ); - RegisterOrConstant ensure_rs2(RegisterOrConstant rs2, Register sethi_temp) { - guarantee(sethi_temp != noreg, "constant offset overflow"); - if (is_simm13(rs2.constant_or_zero())) - return rs2; // register or short constant - set(rs2.as_constant(), sethi_temp); - return sethi_temp; + + RegisterOrConstant ensure_simm13_or_reg(RegisterOrConstant roc, Register Rtemp) { + guarantee(Rtemp != noreg, "constant offset overflow"); + if (is_simm13(roc.constant_or_zero())) + return roc; // register or short constant + set(roc.as_constant(), Rtemp); + return RegisterOrConstant(Rtemp); } // -------------------------------------------------- @@ -2455,6 +2458,11 @@ void inc_counter(address counter_addr, Register Rtmp1, Register Rtmp2); void inc_counter(int* counter_addr, Register Rtmp1, Register Rtmp2); + // Compare char[] arrays aligned to 4 bytes. + void char_arrays_equals(Register ary1, Register ary2, + Register limit, Register result, + Register chr1, Register chr2, Label& Ldone); + #undef VIRTUAL }; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/assembler_sparc.inline.hpp --- a/src/cpu/sparc/vm/assembler_sparc.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -99,6 +99,11 @@ inline void Assembler::jmpl( Register s1, Register s2, Register d ) { emit_long( 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 ) { 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, RegisterOrConstant s2, FloatRegister d) { + if (s2.is_register()) ldf(w, s1, s2.as_register(), d); + else ldf(w, s1, s2.as_constant(), d); +} + inline void Assembler::ldf(FloatRegisterImpl::Width w, Register s1, Register s2, FloatRegister d) { emit_long( 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); } @@ -224,6 +229,11 @@ // pp 222 +inline void Assembler::stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, RegisterOrConstant s2) { + if (s2.is_register()) stf(w, d, s1, s2.as_register()); + else stf(w, d, s1, s2.as_constant()); +} + inline void Assembler::stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, Register s2) { emit_long( op(ldst_op) | fd(d, w) | alt_op3(stf_op3, w) | rs1(s1) | rs2(s2) ); } inline void Assembler::stf( FloatRegisterImpl::Width w, FloatRegister d, Register s1, int simm13a) { emit_data( op(ldst_op) | fd(d, w) | alt_op3(stf_op3, w) | rs1(s1) | immed(true) | simm(simm13a, 13)); } @@ -284,6 +294,7 @@ inline void Assembler::stb(Register d, Register s1, RegisterOrConstant s2) { stb(d, Address(s1, s2)); } inline void Assembler::sth(Register d, Register s1, RegisterOrConstant s2) { sth(d, Address(s1, s2)); } +inline void Assembler::stw(Register d, Register s1, RegisterOrConstant s2) { stw(d, Address(s1, s2)); } inline void Assembler::stx(Register d, Register s1, RegisterOrConstant s2) { stx(d, Address(s1, s2)); } inline void Assembler::std(Register d, Register s1, RegisterOrConstant s2) { std(d, Address(s1, s2)); } inline void Assembler::st( Register d, Register s1, RegisterOrConstant s2) { st( d, Address(s1, s2)); } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/c1_Defs_sparc.hpp --- a/src/cpu/sparc/vm/c1_Defs_sparc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/c1_Defs_sparc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, 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 @@ -38,7 +38,7 @@ // registers enum { pd_nof_cpu_regs_frame_map = 32, // number of registers used during code emission - pd_nof_caller_save_cpu_regs_frame_map = 6, // number of cpu registers killed by calls + pd_nof_caller_save_cpu_regs_frame_map = 10, // number of cpu registers killed by calls pd_nof_cpu_regs_reg_alloc = 20, // number of registers that are visible to register allocator pd_nof_cpu_regs_linearscan = 32,// number of registers visible linear scan pd_first_cpu_reg = 0, diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/c1_FrameMap_sparc.cpp --- a/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/c1_FrameMap_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -320,6 +320,10 @@ _caller_save_cpu_regs[3] = FrameMap::O3_opr; _caller_save_cpu_regs[4] = FrameMap::O4_opr; _caller_save_cpu_regs[5] = FrameMap::O5_opr; + _caller_save_cpu_regs[6] = FrameMap::G1_opr; + _caller_save_cpu_regs[7] = FrameMap::G3_opr; + _caller_save_cpu_regs[8] = FrameMap::G4_opr; + _caller_save_cpu_regs[9] = FrameMap::G5_opr; for (int i = 0; i < nof_caller_save_fpu_regs; i++) { _caller_save_fpu_regs[i] = LIR_OprFact::single_fpu(i); } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp --- a/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 Sun Microsystems, 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 @@ -189,14 +189,17 @@ Register OSR_buf = osrBufferPointer()->as_register(); { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); int monitor_offset = BytesPerWord * method()->max_locals() + - (BasicObjectLock::size() * BytesPerWord) * (number_of_locks - 1); + (2 * BytesPerWord) * (number_of_locks - 1); + // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in + // the OSR buffer using 2 word entries: first the lock and then + // the oop. for (int i = 0; i < number_of_locks; i++) { - int slot_offset = monitor_offset - ((i * BasicObjectLock::size()) * BytesPerWord); + int slot_offset = monitor_offset - ((i * 2) * BytesPerWord); #ifdef ASSERT // verify the interpreter's monitor has a non-null object { Label L; - __ ld_ptr(OSR_buf, slot_offset + BasicObjectLock::obj_offset_in_bytes(), O7); + __ ld_ptr(OSR_buf, slot_offset + 1*BytesPerWord, O7); __ cmp(G0, O7); __ br(Assembler::notEqual, false, Assembler::pt, L); __ delayed()->nop(); @@ -205,9 +208,9 @@ } #endif // ASSERT // Copy the lock field into the compiled activation. - __ ld_ptr(OSR_buf, slot_offset + BasicObjectLock::lock_offset_in_bytes(), O7); + __ ld_ptr(OSR_buf, slot_offset + 0, O7); __ st_ptr(O7, frame_map()->address_for_monitor_lock(i)); - __ ld_ptr(OSR_buf, slot_offset + BasicObjectLock::obj_offset_in_bytes(), O7); + __ ld_ptr(OSR_buf, slot_offset + 1*BytesPerWord, O7); __ st_ptr(O7, frame_map()->address_for_monitor_object(i)); } } @@ -354,7 +357,7 @@ } -void LIR_Assembler::emit_exception_handler() { +int LIR_Assembler::emit_exception_handler() { // if the last instruction is a call (typically to do a throw which // is coming at the end after block reordering) the return address // must still point into the code area in order to avoid assertion @@ -370,15 +373,12 @@ if (handler_base == NULL) { // not enough space left for the handler bailout("exception handler overflow"); - return; + return -1; } -#ifdef ASSERT + int offset = code_offset(); -#endif // ASSERT - compilation()->offsets()->set_value(CodeOffsets::Exceptions, code_offset()); - - - if (compilation()->has_exception_handlers() || compilation()->env()->jvmti_can_post_exceptions()) { + + if (compilation()->has_exception_handlers() || compilation()->env()->jvmti_can_post_on_exceptions()) { __ call(Runtime1::entry_for(Runtime1::handle_exception_id), relocInfo::runtime_call_type); __ delayed()->nop(); } @@ -387,11 +387,13 @@ __ delayed()->nop(); debug_only(__ stop("should have gone to the caller");) assert(code_offset() - offset <= exception_handler_size, "overflow"); - __ end_a_stub(); + + return offset; } -void LIR_Assembler::emit_deopt_handler() { + +int LIR_Assembler::emit_deopt_handler() { // if the last instruction is a call (typically to do a throw which // is coming at the end after block reordering) the return address // must still point into the code area in order to avoid assertion @@ -405,23 +407,18 @@ if (handler_base == NULL) { // not enough space left for the handler bailout("deopt handler overflow"); - return; + return -1; } -#ifdef ASSERT + int offset = code_offset(); -#endif // ASSERT - compilation()->offsets()->set_value(CodeOffsets::Deopt, code_offset()); - AddressLiteral deopt_blob(SharedRuntime::deopt_blob()->unpack()); - __ JUMP(deopt_blob, G3_scratch, 0); // sethi;jmp __ delayed()->nop(); - assert(code_offset() - offset <= deopt_handler_size, "overflow"); - debug_only(__ stop("should have gone to the caller");) - __ end_a_stub(); + + return offset; } @@ -953,9 +950,11 @@ } else { #ifdef _LP64 assert(base != to_reg->as_register_lo(), "can't handle this"); + assert(O7 != to_reg->as_register_lo(), "can't handle this"); __ ld(base, offset + hi_word_offset_in_bytes, to_reg->as_register_lo()); + __ lduw(base, offset + lo_word_offset_in_bytes, O7); // in case O7 is base or offset, use it last __ sllx(to_reg->as_register_lo(), 32, to_reg->as_register_lo()); - __ ld(base, offset + lo_word_offset_in_bytes, to_reg->as_register_lo()); + __ or3(to_reg->as_register_lo(), O7, to_reg->as_register_lo()); #else if (base == to_reg->as_register_lo()) { __ ld(base, offset + hi_word_offset_in_bytes, to_reg->as_register_hi()); @@ -976,8 +975,8 @@ FloatRegister reg = to_reg->as_double_reg(); // split unaligned loads if (unaligned || PatchALot) { - __ ldf(FloatRegisterImpl::S, base, offset + BytesPerWord, reg->successor()); - __ ldf(FloatRegisterImpl::S, base, offset, reg); + __ ldf(FloatRegisterImpl::S, base, offset + 4, reg->successor()); + __ ldf(FloatRegisterImpl::S, base, offset, reg); } else { __ ldf(FloatRegisterImpl::D, base, offset, to_reg->as_double_reg()); } @@ -2171,7 +2170,7 @@ // subtype which we can't check or src is the same array as dst // but not necessarily exactly of type default_type. Label known_ok, halt; - jobject2reg(op->expected_type()->encoding(), tmp); + jobject2reg(op->expected_type()->constant_encoding(), tmp); __ ld_ptr(dst, oopDesc::klass_offset_in_bytes(), tmp2); if (basic_type != T_OBJECT) { __ cmp(tmp, tmp2); @@ -2200,6 +2199,7 @@ Register len = O2; __ add(src, arrayOopDesc::base_offset_in_bytes(basic_type), src_ptr); + LP64_ONLY(__ sra(src_pos, 0, src_pos);) //higher 32bits must be null if (shift == 0) { __ add(src_ptr, src_pos, src_ptr); } else { @@ -2208,6 +2208,7 @@ } __ add(dst, arrayOopDesc::base_offset_in_bytes(basic_type), dst_ptr); + LP64_ONLY(__ sra(dst_pos, 0, dst_pos);) //higher 32bits must be null if (shift == 0) { __ add(dst_ptr, dst_pos, dst_ptr); } else { @@ -2429,7 +2430,7 @@ assert(data->is_BitData(), "need BitData for checkcast"); Register mdo = k_RInfo; Register data_val = Rtmp1; - jobject2reg(md->encoding(), mdo); + jobject2reg(md->constant_encoding(), mdo); int mdo_offset_bias = 0; if (!Assembler::is_simm13(md->byte_offset_of_slot(data, DataLayout::header_offset()) + data->size_in_bytes())) { @@ -2452,7 +2453,7 @@ // patching may screw with our temporaries on sparc, // so let's do it before loading the class if (k->is_loaded()) { - jobject2reg(k->encoding(), k_RInfo); + jobject2reg(k->constant_encoding(), k_RInfo); } else { jobject2reg_with_patching(k_RInfo, op->info_for_patch()); } @@ -2513,7 +2514,7 @@ // patching may screw with our temporaries on sparc, // so let's do it before loading the class if (k->is_loaded()) { - jobject2reg(k->encoding(), k_RInfo); + jobject2reg(k->constant_encoding(), k_RInfo); } else { jobject2reg_with_patching(k_RInfo, op->info_for_patch()); } @@ -2717,7 +2718,7 @@ assert(op->tmp1()->is_single_cpu(), "tmp1 must be allocated"); Register mdo = op->mdo()->as_register(); Register tmp1 = op->tmp1()->as_register(); - jobject2reg(md->encoding(), mdo); + jobject2reg(md->constant_encoding(), mdo); int mdo_offset_bias = 0; if (!Assembler::is_simm13(md->byte_offset_of_slot(data, CounterData::count_offset()) + data->size_in_bytes())) { @@ -2729,9 +2730,6 @@ } Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias); - __ lduw(counter_addr, tmp1); - __ add(tmp1, DataLayout::counter_increment, tmp1); - __ stw(tmp1, counter_addr); Bytecodes::Code bc = method->java_code_at_bci(bci); // Perform additional virtual call profiling for invokevirtual and // invokeinterface bytecodes @@ -2774,7 +2772,7 @@ if (receiver == NULL) { Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)) - mdo_offset_bias); - jobject2reg(known_klass->encoding(), tmp1); + jobject2reg(known_klass->constant_encoding(), tmp1); __ st_ptr(tmp1, recv_addr); Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias); @@ -2821,15 +2819,23 @@ __ set(DataLayout::counter_increment, tmp1); __ st_ptr(tmp1, mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias); - if (i < (VirtualCallData::row_limit() - 1)) { - __ br(Assembler::always, false, Assembler::pt, update_done); - __ delayed()->nop(); - } + __ br(Assembler::always, false, Assembler::pt, update_done); + __ delayed()->nop(); __ bind(next_test); } + // Receiver did not match any saved receiver and there is no empty row for it. + // Increment total counter to indicate polymorphic case. + __ lduw(counter_addr, tmp1); + __ add(tmp1, DataLayout::counter_increment, tmp1); + __ stw(tmp1, counter_addr); __ bind(update_done); } + } else { + // Static call + __ lduw(counter_addr, tmp1); + __ add(tmp1, DataLayout::counter_increment, tmp1); + __ stw(tmp1, counter_addr); } } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp --- a/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -144,17 +144,17 @@ if (index->is_register()) { // apply the shift and accumulate the displacement if (shift > 0) { - LIR_Opr tmp = new_register(T_INT); + LIR_Opr tmp = new_pointer_register(); __ shift_left(index, shift, tmp); index = tmp; } if (disp != 0) { - LIR_Opr tmp = new_register(T_INT); + LIR_Opr tmp = new_pointer_register(); if (Assembler::is_simm13(disp)) { - __ add(tmp, LIR_OprFact::intConst(disp), tmp); + __ add(tmp, LIR_OprFact::intptrConst(disp), tmp); index = tmp; } else { - __ move(LIR_OprFact::intConst(disp), tmp); + __ move(LIR_OprFact::intptrConst(disp), tmp); __ add(tmp, index, tmp); index = tmp; } @@ -162,8 +162,8 @@ } } else if (disp != 0 && !Assembler::is_simm13(disp)) { // index is illegal so replace it with the displacement loaded into a register - index = new_register(T_INT); - __ move(LIR_OprFact::intConst(disp), index); + index = new_pointer_register(); + __ move(LIR_OprFact::intptrConst(disp), index); disp = 0; } @@ -668,7 +668,7 @@ __ add(obj.result(), offset.result(), addr); if (type == objectType) { // Write-barrier needed for Object fields. - pre_barrier(obj.result(), false, NULL); + pre_barrier(addr, false, NULL); } if (type == objectType) @@ -749,6 +749,10 @@ void LIRGenerator::do_ArrayCopy(Intrinsic* x) { assert(x->number_of_arguments() == 5, "wrong type"); + + // Make all state_for calls early since they can emit code + CodeEmitInfo* info = state_for(x, x->state()); + // Note: spill caller save before setting the item LIRItem src (x->argument_at(0), this); LIRItem src_pos (x->argument_at(1), this); @@ -767,7 +771,6 @@ ciArrayKlass* expected_type; arraycopy_helper(x, &flags, &expected_type); - CodeEmitInfo* info = state_for(x, x->state()); __ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), length.result(), rlock_callee_saved(T_INT), expected_type, flags, info); @@ -878,6 +881,9 @@ void LIRGenerator::do_NewTypeArray(NewTypeArray* x) { + // Evaluate state_for early since it may emit code + CodeEmitInfo* info = state_for(x, x->state()); + LIRItem length(x->length(), this); length.load_item(); @@ -890,9 +896,8 @@ LIR_Opr len = length.result(); BasicType elem_type = x->elt_type(); - __ oop2reg(ciTypeArrayKlass::make(elem_type)->encoding(), klass_reg); + __ oop2reg(ciTypeArrayKlass::make(elem_type)->constant_encoding(), klass_reg); - CodeEmitInfo* info = state_for(x, x->state()); CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info); __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path); @@ -902,7 +907,8 @@ void LIRGenerator::do_NewObjectArray(NewObjectArray* x) { - LIRItem length(x->length(), this); + // Evaluate state_for early since it may emit code. + CodeEmitInfo* info = state_for(x, x->state()); // in case of patching (i.e., object class is not yet loaded), we need to reexecute the instruction // and therefore provide the state before the parameters have been consumed CodeEmitInfo* patching_info = NULL; @@ -910,6 +916,7 @@ patching_info = state_for(x, x->state_before()); } + LIRItem length(x->length(), this); length.load_item(); const LIR_Opr reg = result_register_for(x->type()); @@ -919,7 +926,6 @@ LIR_Opr tmp4 = FrameMap::O1_oop_opr; LIR_Opr klass_reg = FrameMap::G5_oop_opr; LIR_Opr len = length.result(); - CodeEmitInfo* info = state_for(x, x->state()); CodeStub* slow_path = new NewObjectArrayStub(klass_reg, len, reg, info); ciObject* obj = (ciObject*) ciObjArrayKlass::make(x->klass()); @@ -943,25 +949,22 @@ items->at_put(i, size); } - // need to get the info before, as the items may become invalid through item_free + // Evaluate state_for early since it may emit code. CodeEmitInfo* patching_info = NULL; if (!x->klass()->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); // cannot re-use same xhandlers for multiple CodeEmitInfos, so - // clone all handlers + // clone all handlers. This is handled transparently in other + // places by the CodeEmitInfo cloning logic but is handled + // specially here because a stub isn't being used. x->set_exception_handlers(new XHandlers(x->exception_handlers())); } + CodeEmitInfo* info = state_for(x, x->state()); i = dims->length(); while (i-- > 0) { LIRItem* size = items->at(i); - // if a patching_info was generated above then debug information for the state before - // the call is going to be emitted. The LIRGenerator calls above may have left some values - // in registers and that's been recorded in the CodeEmitInfo. In that case the items - // for those values can't simply be freed if they are registers because the values - // might be destroyed by store_stack_parameter. So in the case of patching, delay the - // freeing of the items that already were in registers size->load_item(); store_stack_parameter (size->result(), in_ByteSize(STACK_BIAS + @@ -972,8 +975,6 @@ // This instruction can be deoptimized in the slow path : use // O0 as result register. const LIR_Opr reg = result_register_for(x->type()); - CodeEmitInfo* info = state_for(x, x->state()); - jobject2reg_with_patching(reg, x->klass(), patching_info); LIR_Opr rank = FrameMap::O1_opr; __ move(LIR_OprFact::intConst(x->rank()), rank); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/c1_globals_sparc.hpp --- a/src/cpu/sparc/vm/c1_globals_sparc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/c1_globals_sparc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,10 +22,9 @@ * */ -// // Sets the default values for platform dependent flags used by the client compiler. // (see c1_globals.hpp) -// + #ifndef TIERED define_pd_global(bool, BackgroundCompilation, true ); define_pd_global(bool, CICompileOSR, true ); @@ -48,27 +47,24 @@ define_pd_global(bool, UseTLAB, true ); define_pd_global(bool, ProfileInterpreter, false); define_pd_global(intx, FreqInlineSize, 325 ); -define_pd_global(intx, NewRatio, 8 ); // Design center runs on 1.3.1 define_pd_global(bool, ResizeTLAB, true ); define_pd_global(intx, ReservedCodeCacheSize, 32*M ); define_pd_global(intx, CodeCacheExpansionSize, 32*K ); define_pd_global(uintx,CodeCacheMinBlockLength, 1); -define_pd_global(uintx, PermSize, 12*M ); -define_pd_global(uintx, MaxPermSize, 64*M ); -define_pd_global(bool, NeverActAsServerClassMachine, true); +define_pd_global(uintx,PermSize, 12*M ); +define_pd_global(uintx,MaxPermSize, 64*M ); +define_pd_global(bool, NeverActAsServerClassMachine, true ); define_pd_global(intx, NewSizeThreadIncrease, 16*K ); -define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(uint64_t,MaxRAM, 1ULL*G); define_pd_global(intx, InitialCodeCacheSize, 160*K); -#endif // TIERED +#endif // !TIERED define_pd_global(bool, UseTypeProfile, false); define_pd_global(bool, RoundFPResults, false); - -define_pd_global(bool, LIRFillDelaySlots, true); +define_pd_global(bool, LIRFillDelaySlots, true ); define_pd_global(bool, OptimizeSinglePrecision, false); -define_pd_global(bool, CSEArrayLength, true); +define_pd_global(bool, CSEArrayLength, true ); define_pd_global(bool, TwoOperandLIRForm, false); - -define_pd_global(intx, SafepointPollOffset, 0); +define_pd_global(intx, SafepointPollOffset, 0 ); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/c2_globals_sparc.hpp --- a/src/cpu/sparc/vm/c2_globals_sparc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/c2_globals_sparc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -59,7 +59,6 @@ define_pd_global(intx, FreqInlineSize, 175); define_pd_global(intx, INTPRESSURE, 48); // large register set define_pd_global(intx, InteriorEntryAlignment, 16); // = CodeEntryAlignment -define_pd_global(intx, NewRatio, 2); define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); // The default setting 16/16 seems to work best. // (For _228_jack 16/16 is 2% better than 4/4, 16/4, 32/32, 32/16, or 16/32.) @@ -83,25 +82,25 @@ // sequence of instructions to load a 64 bit pointer. // // InitialCodeCacheSize derived from specjbb2000 run. -define_pd_global(intx, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize -define_pd_global(intx, ReservedCodeCacheSize, 48*M); -define_pd_global(intx, CodeCacheExpansionSize, 64*K); +define_pd_global(intx, InitialCodeCacheSize, 2048*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, ReservedCodeCacheSize, 48*M); +define_pd_global(intx, CodeCacheExpansionSize, 64*K); // Ergonomics related flags -define_pd_global(uintx, DefaultMaxRAM, 32*G); +define_pd_global(uint64_t,MaxRAM, 128ULL*G); #else // InitialCodeCacheSize derived from specjbb2000 run. -define_pd_global(intx, InitialCodeCacheSize, 1536*K); // Integral multiple of CodeCacheExpansionSize -define_pd_global(intx, ReservedCodeCacheSize, 32*M); -define_pd_global(intx, CodeCacheExpansionSize, 32*K); +define_pd_global(intx, InitialCodeCacheSize, 1536*K); // Integral multiple of CodeCacheExpansionSize +define_pd_global(intx, ReservedCodeCacheSize, 32*M); +define_pd_global(intx, CodeCacheExpansionSize, 32*K); // Ergonomics related flags -define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(uint64_t,MaxRAM, 4ULL*G); #endif -define_pd_global(uintx,CodeCacheMinBlockLength, 4); +define_pd_global(uintx,CodeCacheMinBlockLength, 4); // Heap related flags -define_pd_global(uintx, PermSize, ScaleForWordSize(16*M)); -define_pd_global(uintx, MaxPermSize, ScaleForWordSize(64*M)); +define_pd_global(uintx,PermSize, ScaleForWordSize(16*M)); +define_pd_global(uintx,MaxPermSize, ScaleForWordSize(64*M)); // Ergonomics related flags define_pd_global(bool, NeverActAsServerClassMachine, false); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/frame_sparc.cpp --- a/src/cpu/sparc/vm/frame_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/frame_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -366,8 +366,9 @@ // as get_original_pc() needs correct value for unextended_sp() if (_pc != NULL) { _cb = CodeCache::find_blob(_pc); - if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { - _pc = ((nmethod*)_cb)->get_original_pc(this); + 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; @@ -519,9 +520,9 @@ _cb = CodeCache::find_blob(pc); *O7_addr() = pc - pc_return_offset; _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"); + address original_pc = nmethod::get_deopt_original_pc(this); + if (original_pc != NULL) { + assert(original_pc == _pc, "expected original to be stored before patching"); _deopt_state = is_deoptimized; } else { _deopt_state = not_deoptimized; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/globals_sparc.hpp --- a/src/cpu/sparc/vm/globals_sparc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/globals_sparc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,10 +22,8 @@ * */ -// // Sets the default values for platform dependent flags used by the runtime system. // (see globals.hpp) -// // For sparc we do not do call backs when a thread is in the interpreter, because the // interpreter dispatch needs at least two instructions - first to load the dispatch address @@ -41,26 +39,23 @@ 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(intx, CodeEntryAlignment, 32); -define_pd_global(uintx, TLABSize, 0); -define_pd_global(uintx, NewSize, ScaleForWordSize((2048 * K) + (2 * (64 * K)))); -define_pd_global(intx, SurvivorRatio, 8); -define_pd_global(intx, InlineFrequencyCount, 50); // we can use more inlining on the SPARC -define_pd_global(intx, InlineSmallCode, 1500); +define_pd_global(intx, CodeEntryAlignment, 32); +define_pd_global(intx, InlineFrequencyCount, 50); // we can use more inlining on the SPARC +define_pd_global(intx, InlineSmallCode, 1500); #ifdef _LP64 // Stack slots are 2X larger in LP64 than in the 32 bit VM. -define_pd_global(intx, ThreadStackSize, 1024); -define_pd_global(intx, VMThreadStackSize, 1024); +define_pd_global(intx, ThreadStackSize, 1024); +define_pd_global(intx, VMThreadStackSize, 1024); #else -define_pd_global(intx, ThreadStackSize, 512); -define_pd_global(intx, VMThreadStackSize, 512); +define_pd_global(intx, ThreadStackSize, 512); +define_pd_global(intx, VMThreadStackSize, 512); #endif define_pd_global(intx, StackYellowPages, 2); define_pd_global(intx, StackRedPages, 1); define_pd_global(intx, StackShadowPages, 3 DEBUG_ONLY(+1)); -define_pd_global(intx, PreInflateSpin, 40); // Determined by running design center +define_pd_global(intx, PreInflateSpin, 40); // Determined by running design center define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteFrequentPairs, true); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/interp_masm_sparc.cpp --- a/src/cpu/sparc/vm/interp_masm_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/interp_masm_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -1681,11 +1681,8 @@ // 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()), scratch); - // Record the receiver type. - record_klass_in_profile(receiver, scratch); + record_klass_in_profile(receiver, scratch, true); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size())); @@ -1695,7 +1692,14 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper( Register receiver, Register scratch, - int start_row, Label& done) { + 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()), scratch); + } + 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. @@ -1711,6 +1715,7 @@ // 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, scratch); + // delayed()->tst(scratch); // The receiver is receiver[n]. Increment count[n]. int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row)); @@ -1720,20 +1725,31 @@ 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. - brx(Assembler::notZero, false, Assembler::pt, done); - delayed()->nop(); + if (is_virtual_call) { + brx(Assembler::zero, false, Assembler::pn, found_null); + delayed()->nop(); + // 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()), scratch); + ba(false, done); + delayed()->nop(); + bind(found_null); + } else { + brx(Assembler::notZero, false, Assembler::pt, done); + delayed()->nop(); + } break; } // Since null is rare, make it be the branch-taken case. - Label found_null; brx(Assembler::zero, false, Assembler::pn, found_null); delayed()->nop(); // Put all the "Case 3" tests here. - record_klass_in_profile_helper(receiver, scratch, start_row + 1, done); + record_klass_in_profile_helper(receiver, scratch, 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. @@ -1750,16 +1766,18 @@ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); mov(DataLayout::counter_increment, scratch); set_mdp_data_at(count_offset, scratch); - ba(false, done); - delayed()->nop(); + if (start_row > 0) { + ba(false, done); + delayed()->nop(); + } } void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register scratch) { + Register scratch, bool is_virtual_call) { assert(ProfileInterpreter, "must be profiling"); Label done; - record_klass_in_profile_helper(receiver, scratch, 0, done); + record_klass_in_profile_helper(receiver, scratch, 0, done, is_virtual_call); bind (done); } @@ -1837,7 +1855,7 @@ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, scratch); + record_klass_in_profile(klass, scratch, false); } // The method data pointer needs to be updated. diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/interp_masm_sparc.hpp --- a/src/cpu/sparc/vm/interp_masm_sparc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/interp_masm_sparc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -290,9 +290,9 @@ void test_mdp_data_at(int offset, Register value, Label& not_equal_continue, Register scratch); - void record_klass_in_profile(Register receiver, Register scratch); + void record_klass_in_profile(Register receiver, Register scratch, bool is_virtual_call); void record_klass_in_profile_helper(Register receiver, Register scratch, - int start_row, Label& done); + int start_row, Label& done, bool is_virtual_call); void update_mdp_by_offset(int offset_of_disp, Register scratch); void update_mdp_by_offset(Register reg, int offset_of_disp, diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/interpreter_sparc.cpp --- a/src/cpu/sparc/vm/interpreter_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/interpreter_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -394,6 +394,11 @@ } +bool AbstractInterpreter::can_be_compiled(methodHandle m) { + // No special entry points that preclude compilation + return true; +} + // This method tells the deoptimizer how big an interpreted frame must be: int AbstractInterpreter::size_activation(methodOop method, int tempcount, diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/sharedRuntime_sparc.cpp --- a/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -107,7 +107,7 @@ // are saved in register windows - I's and L's in the caller's frame and O's in the stub frame // (as the stub's I's) when the runtime routine called by the stub creates its frame. int i; - // Always make the frame size 16 bytr aligned. + // Always make the frame size 16 byte aligned. int frame_size = round_to(additional_frame_words + register_save_size, 16); // OopMap frame size is in c2 stack slots (sizeof(jint)) not bytes or words int frame_size_in_slots = frame_size / sizeof(jint); @@ -201,15 +201,14 @@ __ stx(G5, SP, ccr_offset+STACK_BIAS); __ stxfsr(SP, fsr_offset+STACK_BIAS); - // Save all the FP registers + // Save all the FP registers: 32 doubles (32 floats correspond to the 2 halves of the first 16 doubles) int offset = d00_offset; - for( int i=0; i<64; i+=2 ) { + for( int i=0; iset_callee_saved(VMRegImpl::stack2reg(offset>>2), f->as_VMReg()); - if (true) { - map->set_callee_saved(VMRegImpl::stack2reg((offset + sizeof(float))>>2), f->as_VMReg()->next()); - } + map->set_callee_saved(VMRegImpl::stack2reg((offset + sizeof(float))>>2), f->as_VMReg()->next()); offset += sizeof(double); } @@ -224,7 +223,7 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm) { // Restore all the FP registers - for( int i=0; i<64; i+=2 ) { + for( int i=0; ias_VMReg();// as part of the load/store shuffle if (!r_2->is_valid()) __ ld (base, ld_off, G1_scratch); else __ ldx(base, ld_off, G1_scratch); @@ -867,9 +865,11 @@ if (sig_bt[i] == T_OBJECT || sig_bt[i] == T_ARRAY) { store_c2i_object(r, base, st_off); } else if (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) { +#ifndef _LP64 if (TieredCompilation) { assert(G1_forced || sig_bt[i] != T_LONG, "should not use register args for longs"); } +#endif // _LP64 store_c2i_long(r, base, st_off, r_2->is_stack()); } else { store_c2i_int(r, base, st_off); @@ -1052,9 +1052,7 @@ // Load in argument order going down. const int ld_off = (total_args_passed-i)*Interpreter::stackElementSize(); -#ifdef _LP64 set_Rdisp(G1_scratch); -#endif // _LP64 VMReg r_1 = regs[i].first(); VMReg r_2 = regs[i].second(); @@ -1074,7 +1072,7 @@ #ifdef _LP64 // In V9, longs are given 2 64-bit slots in the interpreter, but the // data is passed in only 1 slot. - Register slot = (sig_bt[i]==T_LONG) ? + RegisterOrConstant slot = (sig_bt[i] == T_LONG) ? next_arg_slot(ld_off) : arg_slot(ld_off); __ ldx(Gargs, slot, r); #else @@ -1092,7 +1090,7 @@ // data is passed in only 1 slot. This code also handles longs that // are passed on the stack, but need a stack-to-stack move through a // spare float register. - Register slot = (sig_bt[i]==T_LONG || sig_bt[i] == T_DOUBLE) ? + RegisterOrConstant slot = (sig_bt[i] == T_LONG || sig_bt[i] == T_DOUBLE) ? next_arg_slot(ld_off) : arg_slot(ld_off); __ ldf(FloatRegisterImpl::D, Gargs, slot, r_1->as_FloatRegister()); #else @@ -1109,8 +1107,9 @@ // Convert stack slot to an SP offset int st_off = reg2offset(regs[i].first()) + STACK_BIAS; // Store down the shuffled stack word. Target address _is_ aligned. - if (!r_2->is_valid()) __ stf(FloatRegisterImpl::S, r_1->as_FloatRegister(), SP, st_off); - else __ stf(FloatRegisterImpl::D, r_1->as_FloatRegister(), SP, st_off); + RegisterOrConstant slot = __ ensure_simm13_or_reg(st_off, Rdisp); + if (!r_2->is_valid()) __ stf(FloatRegisterImpl::S, r_1->as_FloatRegister(), SP, slot); + else __ stf(FloatRegisterImpl::D, r_1->as_FloatRegister(), SP, slot); } } bool made_space = false; @@ -1192,7 +1191,8 @@ // VMReg max_arg, int comp_args_on_stack, // VMRegStackSlots const BasicType *sig_bt, - const VMRegPair *regs) { + const VMRegPair *regs, + AdapterFingerPrint* fingerprint) { address i2c_entry = __ pc(); AdapterGenerator agen(masm); @@ -1261,7 +1261,7 @@ agen.gen_c2i_adapter(total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); __ flush(); - return new AdapterHandlerEntry(i2c_entry, c2i_entry, c2i_unverified_entry); + return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); } @@ -3216,9 +3216,8 @@ Register Oreturn0 = O0; Register Oreturn1 = O1; Register O2UnrollBlock = O2; - Register O3tmp = O3; - Register I5exception_tmp = I5; - Register G4exception_tmp = G4_scratch; + Register L0deopt_mode = L0; + Register G4deopt_mode = G4_scratch; int frame_size_words; Address saved_Freturn0_addr(FP, -sizeof(double) + STACK_BIAS); #if !defined(_LP64) && defined(COMPILER2) @@ -3268,7 +3267,7 @@ map = RegisterSaver::save_live_registers(masm, 0, &frame_size_words); __ ba(false, cont); - __ delayed()->mov(Deoptimization::Unpack_deopt, I5exception_tmp); + __ delayed()->mov(Deoptimization::Unpack_deopt, L0deopt_mode); int exception_offset = __ offset() - start; @@ -3319,7 +3318,7 @@ #endif __ ba(false, cont); - __ delayed()->mov(Deoptimization::Unpack_exception, I5exception_tmp);; + __ delayed()->mov(Deoptimization::Unpack_exception, L0deopt_mode);; // // Reexecute entry, similar to c2 uncommon trap @@ -3329,7 +3328,7 @@ // No need to update oop_map as each call to save_live_registers will produce identical oopmap (void) RegisterSaver::save_live_registers(masm, 0, &frame_size_words); - __ mov(Deoptimization::Unpack_reexecute, I5exception_tmp); + __ mov(Deoptimization::Unpack_reexecute, L0deopt_mode); __ bind(cont); @@ -3352,14 +3351,14 @@ // NOTE: we know that only O0/O1 will be reloaded by restore_result_registers // so this move will survive - __ mov(I5exception_tmp, G4exception_tmp); + __ mov(L0deopt_mode, G4deopt_mode); __ mov(O0, O2UnrollBlock->after_save()); RegisterSaver::restore_result_registers(masm); Label noException; - __ cmp(G4exception_tmp, Deoptimization::Unpack_exception); // Was exception pending? + __ cmp(G4deopt_mode, Deoptimization::Unpack_exception); // Was exception pending? __ br(Assembler::notEqual, false, Assembler::pt, noException); __ delayed()->nop(); @@ -3393,10 +3392,10 @@ } #endif __ set_last_Java_frame(SP, noreg); - __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), G2_thread, G4exception_tmp); + __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), G2_thread, G4deopt_mode); #else // LP64 uses g4 in set_last_Java_frame - __ mov(G4exception_tmp, O1); + __ mov(G4deopt_mode, O1); __ set_last_Java_frame(SP, G0); __ call_VM_leaf(L7_thread_cache, CAST_FROM_FN_PTR(address, Deoptimization::unpack_frames), G2_thread, O1); #endif @@ -3449,7 +3448,6 @@ #endif MacroAssembler* masm = new MacroAssembler(&buffer); Register O2UnrollBlock = O2; - Register O3tmp = O3; Register O2klass_index = O2; // diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/sparc.ad Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ // -// Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. +// Copyright 1998-2010 Sun Microsystems, 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 @@ -193,38 +193,38 @@ // I believe we can't handle callee-save doubles D32 and up until // the place in the sparc stack crawler that asserts on the 255 is // fixed up. -reg_def R_D32x(SOC, SOC, Op_RegD,255, F32->as_VMReg()); -reg_def R_D32 (SOC, SOC, Op_RegD, 1, F32->as_VMReg()->next()); -reg_def R_D34x(SOC, SOC, Op_RegD,255, F34->as_VMReg()); -reg_def R_D34 (SOC, SOC, Op_RegD, 3, F34->as_VMReg()->next()); -reg_def R_D36x(SOC, SOC, Op_RegD,255, F36->as_VMReg()); -reg_def R_D36 (SOC, SOC, Op_RegD, 5, F36->as_VMReg()->next()); -reg_def R_D38x(SOC, SOC, Op_RegD,255, F38->as_VMReg()); -reg_def R_D38 (SOC, SOC, Op_RegD, 7, F38->as_VMReg()->next()); -reg_def R_D40x(SOC, SOC, Op_RegD,255, F40->as_VMReg()); -reg_def R_D40 (SOC, SOC, Op_RegD, 9, F40->as_VMReg()->next()); -reg_def R_D42x(SOC, SOC, Op_RegD,255, F42->as_VMReg()); -reg_def R_D42 (SOC, SOC, Op_RegD, 11, F42->as_VMReg()->next()); -reg_def R_D44x(SOC, SOC, Op_RegD,255, F44->as_VMReg()); -reg_def R_D44 (SOC, SOC, Op_RegD, 13, F44->as_VMReg()->next()); -reg_def R_D46x(SOC, SOC, Op_RegD,255, F46->as_VMReg()); -reg_def R_D46 (SOC, SOC, Op_RegD, 15, F46->as_VMReg()->next()); -reg_def R_D48x(SOC, SOC, Op_RegD,255, F48->as_VMReg()); -reg_def R_D48 (SOC, SOC, Op_RegD, 17, F48->as_VMReg()->next()); -reg_def R_D50x(SOC, SOC, Op_RegD,255, F50->as_VMReg()); -reg_def R_D50 (SOC, SOC, Op_RegD, 19, F50->as_VMReg()->next()); -reg_def R_D52x(SOC, SOC, Op_RegD,255, F52->as_VMReg()); -reg_def R_D52 (SOC, SOC, Op_RegD, 21, F52->as_VMReg()->next()); -reg_def R_D54x(SOC, SOC, Op_RegD,255, F54->as_VMReg()); -reg_def R_D54 (SOC, SOC, Op_RegD, 23, F54->as_VMReg()->next()); -reg_def R_D56x(SOC, SOC, Op_RegD,255, F56->as_VMReg()); -reg_def R_D56 (SOC, SOC, Op_RegD, 25, F56->as_VMReg()->next()); -reg_def R_D58x(SOC, SOC, Op_RegD,255, F58->as_VMReg()); -reg_def R_D58 (SOC, SOC, Op_RegD, 27, F58->as_VMReg()->next()); -reg_def R_D60x(SOC, SOC, Op_RegD,255, F60->as_VMReg()); -reg_def R_D60 (SOC, SOC, Op_RegD, 29, F60->as_VMReg()->next()); -reg_def R_D62x(SOC, SOC, Op_RegD,255, F62->as_VMReg()); -reg_def R_D62 (SOC, SOC, Op_RegD, 31, F62->as_VMReg()->next()); +reg_def R_D32 (SOC, SOC, Op_RegD, 1, F32->as_VMReg()); +reg_def R_D32x(SOC, SOC, Op_RegD,255, F32->as_VMReg()->next()); +reg_def R_D34 (SOC, SOC, Op_RegD, 3, F34->as_VMReg()); +reg_def R_D34x(SOC, SOC, Op_RegD,255, F34->as_VMReg()->next()); +reg_def R_D36 (SOC, SOC, Op_RegD, 5, F36->as_VMReg()); +reg_def R_D36x(SOC, SOC, Op_RegD,255, F36->as_VMReg()->next()); +reg_def R_D38 (SOC, SOC, Op_RegD, 7, F38->as_VMReg()); +reg_def R_D38x(SOC, SOC, Op_RegD,255, F38->as_VMReg()->next()); +reg_def R_D40 (SOC, SOC, Op_RegD, 9, F40->as_VMReg()); +reg_def R_D40x(SOC, SOC, Op_RegD,255, F40->as_VMReg()->next()); +reg_def R_D42 (SOC, SOC, Op_RegD, 11, F42->as_VMReg()); +reg_def R_D42x(SOC, SOC, Op_RegD,255, F42->as_VMReg()->next()); +reg_def R_D44 (SOC, SOC, Op_RegD, 13, F44->as_VMReg()); +reg_def R_D44x(SOC, SOC, Op_RegD,255, F44->as_VMReg()->next()); +reg_def R_D46 (SOC, SOC, Op_RegD, 15, F46->as_VMReg()); +reg_def R_D46x(SOC, SOC, Op_RegD,255, F46->as_VMReg()->next()); +reg_def R_D48 (SOC, SOC, Op_RegD, 17, F48->as_VMReg()); +reg_def R_D48x(SOC, SOC, Op_RegD,255, F48->as_VMReg()->next()); +reg_def R_D50 (SOC, SOC, Op_RegD, 19, F50->as_VMReg()); +reg_def R_D50x(SOC, SOC, Op_RegD,255, F50->as_VMReg()->next()); +reg_def R_D52 (SOC, SOC, Op_RegD, 21, F52->as_VMReg()); +reg_def R_D52x(SOC, SOC, Op_RegD,255, F52->as_VMReg()->next()); +reg_def R_D54 (SOC, SOC, Op_RegD, 23, F54->as_VMReg()); +reg_def R_D54x(SOC, SOC, Op_RegD,255, F54->as_VMReg()->next()); +reg_def R_D56 (SOC, SOC, Op_RegD, 25, F56->as_VMReg()); +reg_def R_D56x(SOC, SOC, Op_RegD,255, F56->as_VMReg()->next()); +reg_def R_D58 (SOC, SOC, Op_RegD, 27, F58->as_VMReg()); +reg_def R_D58x(SOC, SOC, Op_RegD,255, F58->as_VMReg()->next()); +reg_def R_D60 (SOC, SOC, Op_RegD, 29, F60->as_VMReg()); +reg_def R_D60x(SOC, SOC, Op_RegD,255, F60->as_VMReg()->next()); +reg_def R_D62 (SOC, SOC, Op_RegD, 31, F62->as_VMReg()); +reg_def R_D62x(SOC, SOC, Op_RegD,255, F62->as_VMReg()->next()); // ---------------------------- @@ -1803,8 +1803,9 @@ // to implement the UseStrictFP mode. const bool Matcher::strict_fp_requires_explicit_rounding = false; -// Do floats take an entire double register or just half? -const bool Matcher::float_in_double = false; +// Are floats conerted to double when stored to stack during deoptimization? +// Sparc does not handle callee-save floats. +bool Matcher::float_in_double() { return false; } // Do ints take an entire long register or just half? // Note that we if-def off of _LP64. @@ -1885,6 +1886,10 @@ return RegMask(); } +const RegMask Matcher::method_handle_invoke_SP_save_mask() { + return RegMask(); +} + %} @@ -2838,63 +2843,41 @@ %} - enc_class enc_String_Compare(o0RegP str1, o1RegP str2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result) %{ + enc_class enc_String_Compare(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result) %{ Label Ldone, Lloop; MacroAssembler _masm(&cbuf); Register str1_reg = reg_to_register_object($str1$$reg); Register str2_reg = reg_to_register_object($str2$$reg); - Register tmp1_reg = reg_to_register_object($tmp1$$reg); - Register tmp2_reg = reg_to_register_object($tmp2$$reg); + Register cnt1_reg = reg_to_register_object($cnt1$$reg); + Register cnt2_reg = reg_to_register_object($cnt2$$reg); Register result_reg = reg_to_register_object($result$$reg); - // Get the first character position in both strings - // [8] char array, [12] offset, [16] count - int value_offset = java_lang_String:: value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String:: count_offset_in_bytes(); - - // load str1 (jchar*) base address into tmp1_reg - __ load_heap_oop(str1_reg, value_offset, tmp1_reg); - __ ld(str1_reg, offset_offset, result_reg); - __ add(tmp1_reg, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp1_reg); - __ ld(str1_reg, count_offset, str1_reg); // hoisted - __ sll(result_reg, exact_log2(sizeof(jchar)), result_reg); - __ load_heap_oop(str2_reg, value_offset, tmp2_reg); // hoisted - __ add(result_reg, tmp1_reg, tmp1_reg); - - // load str2 (jchar*) base address into tmp2_reg - // __ ld_ptr(str2_reg, value_offset, tmp2_reg); // hoisted - __ ld(str2_reg, offset_offset, result_reg); - __ add(tmp2_reg, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp2_reg); - __ ld(str2_reg, count_offset, str2_reg); // hoisted - __ sll(result_reg, exact_log2(sizeof(jchar)), result_reg); - __ subcc(str1_reg, str2_reg, O7); // hoisted - __ add(result_reg, tmp2_reg, tmp2_reg); + assert(result_reg != str1_reg && + result_reg != str2_reg && + result_reg != cnt1_reg && + result_reg != cnt2_reg , + "need different registers"); // Compute the minimum of the string lengths(str1_reg) and the // difference of the string lengths (stack) - // discard string base pointers, after loading up the lengths - // __ ld(str1_reg, count_offset, str1_reg); // hoisted - // __ ld(str2_reg, count_offset, str2_reg); // hoisted - // See if the lengths are different, and calculate min in str1_reg. // Stash diff in O7 in case we need it for a tie-breaker. Label Lskip; - // __ subcc(str1_reg, str2_reg, O7); // hoisted - __ sll(str1_reg, exact_log2(sizeof(jchar)), str1_reg); // scale the limit + __ subcc(cnt1_reg, cnt2_reg, O7); + __ sll(cnt1_reg, exact_log2(sizeof(jchar)), cnt1_reg); // scale the limit __ br(Assembler::greater, true, Assembler::pt, Lskip); - // str2 is shorter, so use its count: - __ delayed()->sll(str2_reg, exact_log2(sizeof(jchar)), str1_reg); // scale the limit + // cnt2 is shorter, so use its count: + __ delayed()->sll(cnt2_reg, exact_log2(sizeof(jchar)), cnt1_reg); // scale the limit __ bind(Lskip); - // reallocate str1_reg, str2_reg, result_reg + // reallocate cnt1_reg, cnt2_reg, result_reg // Note: limit_reg holds the string length pre-scaled by 2 - Register limit_reg = str1_reg; - Register chr2_reg = str2_reg; + Register limit_reg = cnt1_reg; + Register chr2_reg = cnt2_reg; Register chr1_reg = result_reg; - // tmp{12} are the base pointers + // str{12} are the base pointers // Is the minimum length zero? __ cmp(limit_reg, (int)(0 * sizeof(jchar))); // use cast to resolve overloading ambiguity @@ -2902,8 +2885,8 @@ __ delayed()->mov(O7, result_reg); // result is difference in lengths // Load first characters - __ lduh(tmp1_reg, 0, chr1_reg); - __ lduh(tmp2_reg, 0, chr2_reg); + __ lduh(str1_reg, 0, chr1_reg); + __ lduh(str2_reg, 0, chr2_reg); // Compare first characters __ subcc(chr1_reg, chr2_reg, chr1_reg); @@ -2915,7 +2898,7 @@ // Check after comparing first character to see if strings are equivalent Label LSkip2; // Check if the strings start at same location - __ cmp(tmp1_reg, tmp2_reg); + __ cmp(str1_reg, str2_reg); __ brx(Assembler::notEqual, true, Assembler::pt, LSkip2); __ delayed()->nop(); @@ -2932,23 +2915,23 @@ __ br(Assembler::equal, true, Assembler::pn, Ldone); __ delayed()->mov(O7, result_reg); // result is difference in lengths - // Shift tmp1_reg and tmp2_reg to the end of the arrays, negate limit - __ add(tmp1_reg, limit_reg, tmp1_reg); - __ add(tmp2_reg, limit_reg, tmp2_reg); + // Shift str1_reg and str2_reg to the end of the arrays, negate limit + __ add(str1_reg, limit_reg, str1_reg); + __ add(str2_reg, limit_reg, str2_reg); __ neg(chr1_reg, limit_reg); // limit = -(limit-2) // Compare the rest of the characters - __ lduh(tmp1_reg, limit_reg, chr1_reg); + __ lduh(str1_reg, limit_reg, chr1_reg); __ bind(Lloop); - // __ lduh(tmp1_reg, limit_reg, chr1_reg); // hoisted - __ lduh(tmp2_reg, limit_reg, chr2_reg); + // __ lduh(str1_reg, limit_reg, chr1_reg); // hoisted + __ lduh(str2_reg, limit_reg, chr2_reg); __ subcc(chr1_reg, chr2_reg, chr1_reg); __ br(Assembler::notZero, false, Assembler::pt, Ldone); assert(chr1_reg == result_reg, "result must be pre-placed"); __ delayed()->inccc(limit_reg, sizeof(jchar)); // annul LDUH if branch is not taken to prevent access past end of string __ br(Assembler::notZero, true, Assembler::pt, Lloop); - __ delayed()->lduh(tmp1_reg, limit_reg, chr1_reg); // hoisted + __ delayed()->lduh(str1_reg, limit_reg, chr1_reg); // hoisted // If strings are equal up to min length, return the length difference. __ mov(O7, result_reg); @@ -2957,125 +2940,80 @@ __ bind(Ldone); %} -enc_class enc_String_Equals(o0RegP str1, o1RegP str2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result) %{ - Label Lword, Lword_loop, Lpost_word, Lchar, Lchar_loop, Ldone; +enc_class enc_String_Equals(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result) %{ + Label Lword_loop, Lpost_word, Lchar, Lchar_loop, Ldone; MacroAssembler _masm(&cbuf); Register str1_reg = reg_to_register_object($str1$$reg); Register str2_reg = reg_to_register_object($str2$$reg); - Register tmp1_reg = reg_to_register_object($tmp1$$reg); - Register tmp2_reg = reg_to_register_object($tmp2$$reg); + Register cnt_reg = reg_to_register_object($cnt$$reg); + Register tmp1_reg = O7; Register result_reg = reg_to_register_object($result$$reg); - // Get the first character position in both strings - // [8] char array, [12] offset, [16] count - int value_offset = java_lang_String:: value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String:: count_offset_in_bytes(); - - // load str1 (jchar*) base address into tmp1_reg - __ load_heap_oop(Address(str1_reg, value_offset), tmp1_reg); - __ ld(Address(str1_reg, offset_offset), result_reg); - __ add(tmp1_reg, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp1_reg); - __ ld(Address(str1_reg, count_offset), str1_reg); // hoisted - __ sll(result_reg, exact_log2(sizeof(jchar)), result_reg); - __ load_heap_oop(Address(str2_reg, value_offset), tmp2_reg); // hoisted - __ add(result_reg, tmp1_reg, tmp1_reg); - - // load str2 (jchar*) base address into tmp2_reg - // __ ld_ptr(Address(str2_reg, value_offset), tmp2_reg); // hoisted - __ ld(Address(str2_reg, offset_offset), result_reg); - __ add(tmp2_reg, arrayOopDesc::base_offset_in_bytes(T_CHAR), tmp2_reg); - __ ld(Address(str2_reg, count_offset), str2_reg); // hoisted - __ sll(result_reg, exact_log2(sizeof(jchar)), result_reg); - __ cmp(str1_reg, str2_reg); // hoisted - __ add(result_reg, tmp2_reg, tmp2_reg); - - __ sll(str1_reg, exact_log2(sizeof(jchar)), str1_reg); - __ br(Assembler::notEqual, true, Assembler::pt, Ldone); - __ delayed()->mov(G0, result_reg); // not equal - - __ br_zero(Assembler::equal, true, Assembler::pn, str1_reg, Ldone); - __ delayed()->add(G0, 1, result_reg); //equals - - __ cmp(tmp1_reg, tmp2_reg); //same string ? + assert(result_reg != str1_reg && + result_reg != str2_reg && + result_reg != cnt_reg && + result_reg != tmp1_reg , + "need different registers"); + + __ cmp(str1_reg, str2_reg); //same char[] ? __ brx(Assembler::equal, true, Assembler::pn, Ldone); __ delayed()->add(G0, 1, result_reg); + __ br_on_reg_cond(Assembler::rc_z, true, Assembler::pn, cnt_reg, Ldone); + __ delayed()->add(G0, 1, result_reg); // count == 0 + //rename registers - Register limit_reg = str1_reg; - Register chr2_reg = str2_reg; + Register limit_reg = cnt_reg; Register chr1_reg = result_reg; - // tmp{12} are the base pointers + Register chr2_reg = tmp1_reg; //check for alignment and position the pointers to the ends - __ or3(tmp1_reg, tmp2_reg, chr1_reg); - __ andcc(chr1_reg, 0x3, chr1_reg); // notZero means at least one not 4-byte aligned - __ br(Assembler::notZero, false, Assembler::pn, Lchar); - __ delayed()->nop(); - - __ bind(Lword); - __ and3(limit_reg, 0x2, O7); //remember the remainder (either 0 or 2) - __ andn(limit_reg, 0x3, limit_reg); - __ br_zero(Assembler::zero, false, Assembler::pn, limit_reg, Lpost_word); - __ delayed()->nop(); - - __ add(tmp1_reg, limit_reg, tmp1_reg); - __ add(tmp2_reg, limit_reg, tmp2_reg); - __ neg(limit_reg); - - __ lduw(tmp1_reg, limit_reg, chr1_reg); - __ bind(Lword_loop); - __ lduw(tmp2_reg, limit_reg, chr2_reg); - __ cmp(chr1_reg, chr2_reg); - __ br(Assembler::notEqual, true, Assembler::pt, Ldone); - __ delayed()->mov(G0, result_reg); - __ inccc(limit_reg, 2*sizeof(jchar)); - // annul LDUW if branch i s not taken to prevent access past end of string - __ br(Assembler::notZero, true, Assembler::pt, Lword_loop); //annul on taken - __ delayed()->lduw(tmp1_reg, limit_reg, chr1_reg); // hoisted - - __ bind(Lpost_word); - __ br_zero(Assembler::zero, true, Assembler::pt, O7, Ldone); - __ delayed()->add(G0, 1, result_reg); - - __ lduh(tmp1_reg, 0, chr1_reg); - __ lduh(tmp2_reg, 0, chr2_reg); - __ cmp (chr1_reg, chr2_reg); - __ br(Assembler::notEqual, true, Assembler::pt, Ldone); - __ delayed()->mov(G0, result_reg); + __ or3(str1_reg, str2_reg, chr1_reg); + __ andcc(chr1_reg, 0x3, chr1_reg); + // notZero means at least one not 4-byte aligned. + // We could optimize the case when both arrays are not aligned + // but it is not frequent case and it requires additional checks. + __ br(Assembler::notZero, false, Assembler::pn, Lchar); // char by char compare + __ delayed()->sll(limit_reg, exact_log2(sizeof(jchar)), limit_reg); // set byte count + + // Compare char[] arrays aligned to 4 bytes. + __ char_arrays_equals(str1_reg, str2_reg, limit_reg, result_reg, + chr1_reg, chr2_reg, Ldone); __ ba(false,Ldone); __ delayed()->add(G0, 1, result_reg); + // char by char compare __ bind(Lchar); - __ add(tmp1_reg, limit_reg, tmp1_reg); - __ add(tmp2_reg, limit_reg, tmp2_reg); + __ add(str1_reg, limit_reg, str1_reg); + __ add(str2_reg, limit_reg, str2_reg); __ neg(limit_reg); //negate count - __ lduh(tmp1_reg, limit_reg, chr1_reg); + __ lduh(str1_reg, limit_reg, chr1_reg); + // Lchar_loop __ bind(Lchar_loop); - __ lduh(tmp2_reg, limit_reg, chr2_reg); + __ lduh(str2_reg, limit_reg, chr2_reg); __ cmp(chr1_reg, chr2_reg); __ br(Assembler::notEqual, true, Assembler::pt, Ldone); __ delayed()->mov(G0, result_reg); //not equal __ inccc(limit_reg, sizeof(jchar)); // annul LDUH if branch is not taken to prevent access past end of string - __ br(Assembler::notZero, true, Assembler::pt, Lchar_loop); //annul on taken - __ delayed()->lduh(tmp1_reg, limit_reg, chr1_reg); // hoisted + __ br(Assembler::notZero, true, Assembler::pt, Lchar_loop); + __ delayed()->lduh(str1_reg, limit_reg, chr1_reg); // hoisted __ add(G0, 1, result_reg); //equal __ bind(Ldone); %} -enc_class enc_Array_Equals(o0RegP ary1, o1RegP ary2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result) %{ +enc_class enc_Array_Equals(o0RegP ary1, o1RegP ary2, g3RegP tmp1, notemp_iRegI result) %{ Label Lvector, Ldone, Lloop; MacroAssembler _masm(&cbuf); Register ary1_reg = reg_to_register_object($ary1$$reg); Register ary2_reg = reg_to_register_object($ary2$$reg); Register tmp1_reg = reg_to_register_object($tmp1$$reg); - Register tmp2_reg = reg_to_register_object($tmp2$$reg); + Register tmp2_reg = O7; Register result_reg = reg_to_register_object($result$$reg); int length_offset = arrayOopDesc::length_offset_in_bytes(); @@ -3083,7 +3021,7 @@ // return true if the same array __ cmp(ary1_reg, ary2_reg); - __ br(Assembler::equal, true, Assembler::pn, Ldone); + __ brx(Assembler::equal, true, Assembler::pn, Ldone); __ delayed()->add(G0, 1, result_reg); // equal __ br_null(ary1_reg, true, Assembler::pn, Ldone); @@ -3101,7 +3039,7 @@ __ br(Assembler::notEqual, true, Assembler::pn, Ldone); __ delayed()->mov(G0, result_reg); // not equal - __ br_zero(Assembler::zero, true, Assembler::pn, tmp1_reg, Ldone); + __ br_on_reg_cond(Assembler::rc_z, true, Assembler::pn, tmp1_reg, Ldone); __ delayed()->add(G0, 1, result_reg); // zero-length arrays are equal // load array addresses @@ -3109,45 +3047,16 @@ __ add(ary2_reg, base_offset, ary2_reg); // renaming registers - Register chr1_reg = tmp2_reg; // for characters in ary1 - Register chr2_reg = result_reg; // for characters in ary2 + Register chr1_reg = result_reg; // for characters in ary1 + Register chr2_reg = tmp2_reg; // for characters in ary2 Register limit_reg = tmp1_reg; // length // set byte count __ sll(limit_reg, exact_log2(sizeof(jchar)), limit_reg); - __ andcc(limit_reg, 0x2, chr1_reg); //trailing character ? - __ br(Assembler::zero, false, Assembler::pt, Lvector); - __ delayed()->nop(); - - //compare the trailing char - __ sub(limit_reg, sizeof(jchar), limit_reg); - __ lduh(ary1_reg, limit_reg, chr1_reg); - __ lduh(ary2_reg, limit_reg, chr2_reg); - __ cmp(chr1_reg, chr2_reg); - __ br(Assembler::notEqual, true, Assembler::pt, Ldone); - __ delayed()->mov(G0, result_reg); // not equal - - // only one char ? - __ br_zero(Assembler::zero, true, Assembler::pn, limit_reg, Ldone); - __ delayed()->add(G0, 1, result_reg); // zero-length arrays are equal - - __ bind(Lvector); - // Shift ary1_reg and ary2_reg to the end of the arrays, negate limit - __ add(ary1_reg, limit_reg, ary1_reg); - __ add(ary2_reg, limit_reg, ary2_reg); - __ neg(limit_reg, limit_reg); - - __ lduw(ary1_reg, limit_reg, chr1_reg); - __ bind(Lloop); - __ lduw(ary2_reg, limit_reg, chr2_reg); - __ cmp(chr1_reg, chr2_reg); - __ br(Assembler::notEqual, false, Assembler::pt, Ldone); - __ delayed()->mov(G0, result_reg); // not equal - __ inccc(limit_reg, 2*sizeof(jchar)); - // annul LDUW if branch is not taken to prevent access past end of string - __ br(Assembler::notZero, true, Assembler::pt, Lloop); //annul on taken - __ delayed()->lduw(ary1_reg, limit_reg, chr1_reg); // hoisted - + + // Compare char[] arrays aligned to 4 bytes. + __ char_arrays_equals(ary1_reg, ary2_reg, limit_reg, result_reg, + chr1_reg, chr2_reg, Ldone); __ add(G0, 1, result_reg); // equals __ bind(Ldone); @@ -5707,7 +5616,7 @@ effect(TEMP dst, TEMP tmp); ins_cost(MEMORY_REF_COST + 2*DEFAULT_COST); - size(3*4); + size((3+1)*4); // set may use two instructions. format %{ "LDUH $mem,$dst\t! ushort/char & 16-bit mask -> long\n\t" "SET $mask,$tmp\n\t" "AND $dst,$tmp,$dst" %} @@ -5851,7 +5760,7 @@ effect(TEMP dst, TEMP tmp); ins_cost(MEMORY_REF_COST + 2*DEFAULT_COST); - size(3*4); + size((3+1)*4); // set may use two instructions. format %{ "LDUW $mem,$dst\t! int & 32-bit mask -> long\n\t" "SET $mask,$tmp\n\t" "AND $dst,$tmp,$dst" %} @@ -6760,7 +6669,7 @@ ins_pipe(ialu_imm); %} -instruct cmovII_U_reg(cmpOpU cmp, flagsRegU icc, iRegI dst, iRegI src) %{ +instruct cmovIIu_reg(cmpOpU cmp, flagsRegU icc, iRegI dst, iRegI src) %{ match(Set dst (CMoveI (Binary cmp icc) (Binary dst src))); ins_cost(150); size(4); @@ -6769,7 +6678,7 @@ ins_pipe(ialu_reg); %} -instruct cmovII_U_imm(cmpOpU cmp, flagsRegU icc, iRegI dst, immI11 src) %{ +instruct cmovIIu_imm(cmpOpU cmp, flagsRegU icc, iRegI dst, immI11 src) %{ match(Set dst (CMoveI (Binary cmp icc) (Binary dst src))); ins_cost(140); size(4); @@ -6815,6 +6724,16 @@ ins_pipe(ialu_reg); %} +// This instruction also works with CmpN so we don't need cmovNN_reg. +instruct cmovNIu_reg(cmpOpU cmp, flagsRegU icc, iRegN dst, iRegN src) %{ + match(Set dst (CMoveN (Binary cmp icc) (Binary dst src))); + ins_cost(150); + size(4); + format %{ "MOV$cmp $icc,$src,$dst" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + instruct cmovNF_reg(cmpOpF cmp, flagsRegF fcc, iRegN dst, iRegN src) %{ match(Set dst (CMoveN (Binary cmp fcc) (Binary dst src))); ins_cost(150); @@ -6852,6 +6771,16 @@ ins_pipe(ialu_reg); %} +instruct cmovPIu_reg(cmpOpU cmp, flagsRegU icc, iRegP dst, iRegP src) %{ + match(Set dst (CMoveP (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + instruct cmovPI_imm(cmpOp cmp, flagsReg icc, iRegP dst, immP0 src) %{ match(Set dst (CMoveP (Binary cmp icc) (Binary dst src))); ins_cost(140); @@ -6862,6 +6791,16 @@ ins_pipe(ialu_imm); %} +instruct cmovPIu_imm(cmpOpU cmp, flagsRegU icc, iRegP dst, immP0 src) %{ + match(Set dst (CMoveP (Binary cmp icc) (Binary dst src))); + ins_cost(140); + + size(4); + format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %} + ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_imm); +%} + instruct cmovPF_reg(cmpOpF cmp, flagsRegF fcc, iRegP dst, iRegP src) %{ match(Set dst (CMoveP (Binary cmp fcc) (Binary dst src))); ins_cost(150); @@ -6901,6 +6840,17 @@ ins_pipe(int_conditional_float_move); %} +instruct cmovFIu_reg(cmpOpU cmp, flagsRegU icc, regF dst, regF src) %{ + match(Set dst (CMoveF (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "FMOVS$cmp $icc,$src,$dst" %} + opcode(0x101); + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(int_conditional_float_move); +%} + // Conditional move, instruct cmovFF_reg(cmpOpF cmp, flagsRegF fcc, regF dst, regF src) %{ match(Set dst (CMoveF (Binary cmp fcc) (Binary dst src))); @@ -6934,6 +6884,17 @@ ins_pipe(int_conditional_double_move); %} +instruct cmovDIu_reg(cmpOpU cmp, flagsRegU icc, regD dst, regD src) %{ + match(Set dst (CMoveD (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "FMOVD$cmp $icc,$src,$dst" %} + opcode(0x102); + ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(int_conditional_double_move); +%} + // Conditional move, instruct cmovDF_reg(cmpOpF cmp, flagsRegF fcc, regD dst, regD src) %{ match(Set dst (CMoveD (Binary cmp fcc) (Binary dst src))); @@ -6973,6 +6934,17 @@ %} +instruct cmovLIu_reg(cmpOpU cmp, flagsRegU icc, iRegL dst, iRegL src) %{ + match(Set dst (CMoveL (Binary cmp icc) (Binary dst src))); + ins_cost(150); + + size(4); + format %{ "MOV$cmp $icc,$src,$dst\t! long" %} + ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) ); + ins_pipe(ialu_reg); +%} + + instruct cmovLF_reg(cmpOpF cmp, flagsRegF fcc, iRegL dst, iRegL src) %{ match(Set dst (CMoveL (Binary cmp fcc) (Binary dst src))); ins_cost(150); @@ -9471,33 +9443,33 @@ ins_pipe(long_memory_op); %} -instruct string_compare(o0RegP str1, o1RegP str2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result, - o7RegI tmp3, flagsReg ccr) %{ - match(Set result (StrComp str1 str2)); - effect(USE_KILL str1, USE_KILL str2, KILL tmp1, KILL tmp2, KILL ccr, KILL tmp3); +instruct string_compare(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result, + o7RegI tmp, flagsReg ccr) %{ + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp); ins_cost(300); - format %{ "String Compare $str1,$str2 -> $result" %} - ins_encode( enc_String_Compare(str1, str2, tmp1, tmp2, result) ); + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %} + ins_encode( enc_String_Compare(str1, str2, cnt1, cnt2, result) ); ins_pipe(long_memory_op); %} -instruct string_equals(o0RegP str1, o1RegP str2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result, - o7RegI tmp3, flagsReg ccr) %{ - match(Set result (StrEquals str1 str2)); - effect(USE_KILL str1, USE_KILL str2, KILL tmp1, KILL tmp2, KILL ccr, KILL tmp3); +instruct string_equals(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result, + o7RegI tmp, flagsReg ccr) %{ + match(Set result (StrEquals (Binary str1 str2) cnt)); + effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp, KILL ccr); ins_cost(300); - format %{ "String Equals $str1,$str2 -> $result" %} - ins_encode( enc_String_Equals(str1, str2, tmp1, tmp2, result) ); + format %{ "String Equals $str1,$str2,$cnt -> $result // KILL $tmp" %} + ins_encode( enc_String_Equals(str1, str2, cnt, result) ); ins_pipe(long_memory_op); %} -instruct array_equals(o0RegP ary1, o1RegP ary2, g3RegP tmp1, g4RegP tmp2, notemp_iRegI result, - flagsReg ccr) %{ +instruct array_equals(o0RegP ary1, o1RegP ary2, g3RegI tmp1, notemp_iRegI result, + o7RegI tmp2, flagsReg ccr) %{ match(Set result (AryEq ary1 ary2)); effect(USE_KILL ary1, USE_KILL ary2, KILL tmp1, KILL tmp2, KILL ccr); ins_cost(300); - format %{ "Array Equals $ary1,$ary2 -> $result" %} - ins_encode( enc_Array_Equals(ary1, ary2, tmp1, tmp2, result)); + format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1,$tmp2" %} + ins_encode( enc_Array_Equals(ary1, ary2, tmp1, result)); ins_pipe(long_memory_op); %} @@ -9515,8 +9487,9 @@ // x |= (x >> 8); // x |= (x >> 16); // return (WORDBITS - popc(x)); - format %{ "SRL $src,1,$dst\t! count leading zeros (int)\n\t" - "OR $src,$tmp,$dst\n\t" + format %{ "SRL $src,1,$tmp\t! count leading zeros (int)\n\t" + "SRL $src,0,$dst\t! 32-bit zero extend\n\t" + "OR $dst,$tmp,$dst\n\t" "SRL $dst,2,$tmp\n\t" "OR $dst,$tmp,$dst\n\t" "SRL $dst,4,$tmp\n\t" @@ -9533,7 +9506,8 @@ Register Rsrc = $src$$Register; Register Rtmp = $tmp$$Register; __ srl(Rsrc, 1, Rtmp); - __ or3(Rsrc, Rtmp, Rdst); + __ srl(Rsrc, 0, Rdst); + __ or3(Rdst, Rtmp, Rdst); __ srl(Rdst, 2, Rtmp); __ or3(Rdst, Rtmp, Rdst); __ srl(Rdst, 4, Rtmp); @@ -9561,7 +9535,7 @@ // x |= (x >> 16); // x |= (x >> 32); // return (WORDBITS - popc(x)); - format %{ "SRLX $src,1,$dst\t! count leading zeros (long)\n\t" + format %{ "SRLX $src,1,$tmp\t! count leading zeros (long)\n\t" "OR $src,$tmp,$dst\n\t" "SRLX $dst,2,$tmp\n\t" "OR $dst,$tmp,$dst\n\t" diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/stubGenerator_sparc.cpp --- a/src/cpu/sparc/vm/stubGenerator_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/stubGenerator_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -2862,6 +2862,9 @@ // arraycopy stubs used by compilers generate_arraycopy_stubs(); + + // Don't initialize the platform math functions since sparc + // doesn't have intrinsics for these operations. } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/sparc/vm/templateInterpreter_sparc.cpp --- a/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -150,8 +150,7 @@ } -address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, bool unbox) { - assert(!unbox, "NYI");//6815692// +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { address compiled_entry = __ pc(); Label cont; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/assembler_x86.cpp --- a/src/cpu/x86/vm/assembler_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/assembler_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -2251,6 +2251,7 @@ emit_byte(0x9D); } +#ifndef _LP64 // no 32bit push/pop on amd64 void Assembler::popl(Address dst) { // NOTE: this will adjust stack by 8byte on 64bits InstructionMark im(this); @@ -2258,6 +2259,7 @@ emit_byte(0x8F); emit_operand(rax, dst); } +#endif void Assembler::prefetch_prefix(Address src) { prefix(src); @@ -2428,6 +2430,7 @@ emit_byte(0x9C); } +#ifndef _LP64 // no 32bit push/pop on amd64 void Assembler::pushl(Address src) { // Note this will push 64bit on 64bit InstructionMark im(this); @@ -2435,6 +2438,7 @@ emit_byte(0xFF); emit_operand(rsi, src); } +#endif void Assembler::pxor(XMMRegister dst, Address src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); @@ -5591,7 +5595,12 @@ } void MacroAssembler::andpd(XMMRegister dst, AddressLiteral src) { - andpd(dst, as_Address(src)); + if (reachable(src)) { + andpd(dst, as_Address(src)); + } else { + lea(rscratch1, src); + andpd(dst, Address(rscratch1, 0)); + } } void MacroAssembler::andptr(Register dst, int32_t imm32) { @@ -6078,11 +6087,21 @@ } void MacroAssembler::comisd(XMMRegister dst, AddressLiteral src) { - comisd(dst, as_Address(src)); + if (reachable(src)) { + comisd(dst, as_Address(src)); + } else { + lea(rscratch1, src); + comisd(dst, Address(rscratch1, 0)); + } } void MacroAssembler::comiss(XMMRegister dst, AddressLiteral src) { - comiss(dst, as_Address(src)); + if (reachable(src)) { + comiss(dst, as_Address(src)); + } else { + lea(rscratch1, src); + comiss(dst, Address(rscratch1, 0)); + } } @@ -7647,7 +7666,7 @@ #ifdef ASSERT Label L; - testl(tmp, tmp); + testptr(tmp, tmp); jccb(Assembler::notZero, L); hlt(); bind(L); @@ -8214,6 +8233,15 @@ } } +// Used for storing NULLs. +void MacroAssembler::store_heap_oop_null(Address dst) { + if (UseCompressedOops) { + movl(dst, (int32_t)NULL_WORD); + } else { + movslq(dst, (int32_t)NULL_WORD); + } +} + // Algorithm must match oop.inline.hpp encode_heap_oop. void MacroAssembler::encode_heap_oop(Register r) { assert (UseCompressedOops, "should be compressed"); @@ -8404,6 +8432,321 @@ } #endif // _LP64 +// IndexOf substring. +void MacroAssembler::string_indexof(Register str1, Register str2, + Register cnt1, Register cnt2, Register result, + XMMRegister vec, Register tmp) { + assert(UseSSE42Intrinsics, "SSE4.2 is required"); + + Label RELOAD_SUBSTR, PREP_FOR_SCAN, SCAN_TO_SUBSTR, + SCAN_SUBSTR, RET_NOT_FOUND, CLEANUP; + + push(str1); // string addr + push(str2); // substr addr + push(cnt2); // substr count + jmpb(PREP_FOR_SCAN); + + // Substr count saved at sp + // Substr saved at sp+1*wordSize + // String saved at sp+2*wordSize + + // Reload substr for rescan + bind(RELOAD_SUBSTR); + movl(cnt2, Address(rsp, 0)); + movptr(str2, Address(rsp, wordSize)); + // We came here after the beginninig of the substring was + // matched but the rest of it was not so we need to search + // again. Start from the next element after the previous match. + subptr(str1, result); // Restore counter + shrl(str1, 1); + addl(cnt1, str1); + lea(str1, Address(result, 2)); // Reload string + + // Load substr + bind(PREP_FOR_SCAN); + movdqu(vec, Address(str2, 0)); + addl(cnt1, 8); // prime the loop + subptr(str1, 16); + + // Scan string for substr in 16-byte vectors + bind(SCAN_TO_SUBSTR); + subl(cnt1, 8); + addptr(str1, 16); + + // pcmpestri + // inputs: + // xmm - substring + // rax - substring length (elements count) + // mem - scaned string + // rdx - string length (elements count) + // 0xd - mode: 1100 (substring search) + 01 (unsigned shorts) + // outputs: + // rcx - matched index in string + assert(cnt1 == rdx && cnt2 == rax && tmp == rcx, "pcmpestri"); + + pcmpestri(vec, Address(str1, 0), 0x0d); + jcc(Assembler::above, SCAN_TO_SUBSTR); // CF == 0 && ZF == 0 + jccb(Assembler::aboveEqual, RET_NOT_FOUND); // CF == 0 + + // Fallthrough: found a potential substr + + // Make sure string is still long enough + subl(cnt1, tmp); + cmpl(cnt1, cnt2); + jccb(Assembler::negative, RET_NOT_FOUND); + // Compute start addr of substr + lea(str1, Address(str1, tmp, Address::times_2)); + movptr(result, str1); // save + + // Compare potential substr + addl(cnt1, 8); // prime the loop + addl(cnt2, 8); + subptr(str1, 16); + subptr(str2, 16); + + // Scan 16-byte vectors of string and substr + bind(SCAN_SUBSTR); + subl(cnt1, 8); + subl(cnt2, 8); + addptr(str1, 16); + addptr(str2, 16); + movdqu(vec, Address(str2, 0)); + pcmpestri(vec, Address(str1, 0), 0x0d); + jcc(Assembler::noOverflow, RELOAD_SUBSTR); // OF == 0 + jcc(Assembler::positive, SCAN_SUBSTR); // SF == 0 + + // Compute substr offset + subptr(result, Address(rsp, 2*wordSize)); + shrl(result, 1); // index + jmpb(CLEANUP); + + bind(RET_NOT_FOUND); + movl(result, -1); + + bind(CLEANUP); + addptr(rsp, 3*wordSize); +} + +// Compare strings. +void MacroAssembler::string_compare(Register str1, Register str2, + Register cnt1, Register cnt2, Register result, + XMMRegister vec1, XMMRegister vec2) { + Label LENGTH_DIFF_LABEL, POP_LABEL, DONE_LABEL, WHILE_HEAD_LABEL; + + // Compute the minimum of the string lengths and the + // difference of the string lengths (stack). + // Do the conditional move stuff + movl(result, cnt1); + subl(cnt1, cnt2); + push(cnt1); + if (VM_Version::supports_cmov()) { + cmovl(Assembler::lessEqual, cnt2, result); + } else { + Label GT_LABEL; + jccb(Assembler::greater, GT_LABEL); + movl(cnt2, result); + bind(GT_LABEL); + } + + // Is the minimum length zero? + testl(cnt2, cnt2); + jcc(Assembler::zero, LENGTH_DIFF_LABEL); + + // Load first characters + load_unsigned_short(result, Address(str1, 0)); + load_unsigned_short(cnt1, Address(str2, 0)); + + // Compare first characters + subl(result, cnt1); + jcc(Assembler::notZero, POP_LABEL); + decrementl(cnt2); + jcc(Assembler::zero, LENGTH_DIFF_LABEL); + + { + // Check after comparing first character to see if strings are equivalent + Label LSkip2; + // Check if the strings start at same location + cmpptr(str1, str2); + jccb(Assembler::notEqual, LSkip2); + + // Check if the length difference is zero (from stack) + cmpl(Address(rsp, 0), 0x0); + jcc(Assembler::equal, LENGTH_DIFF_LABEL); + + // Strings might not be equivalent + bind(LSkip2); + } + + // Advance to next character + addptr(str1, 2); + addptr(str2, 2); + + if (UseSSE42Intrinsics) { + // With SSE4.2, use double quad vector compare + Label COMPARE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_TAIL; + // Setup to compare 16-byte vectors + movl(cnt1, cnt2); + andl(cnt2, 0xfffffff8); // cnt2 holds the vector count + andl(cnt1, 0x00000007); // cnt1 holds the tail count + testl(cnt2, cnt2); + jccb(Assembler::zero, COMPARE_TAIL); + + lea(str2, Address(str2, cnt2, Address::times_2)); + lea(str1, Address(str1, cnt2, Address::times_2)); + negptr(cnt2); + + bind(COMPARE_VECTORS); + movdqu(vec1, Address(str1, cnt2, Address::times_2)); + movdqu(vec2, Address(str2, cnt2, Address::times_2)); + pxor(vec1, vec2); + ptest(vec1, vec1); + jccb(Assembler::notZero, VECTOR_NOT_EQUAL); + addptr(cnt2, 8); + jcc(Assembler::notZero, COMPARE_VECTORS); + jmpb(COMPARE_TAIL); + + // Mismatched characters in the vectors + bind(VECTOR_NOT_EQUAL); + lea(str1, Address(str1, cnt2, Address::times_2)); + lea(str2, Address(str2, cnt2, Address::times_2)); + movl(cnt1, 8); + + // Compare tail (< 8 chars), or rescan last vectors to + // find 1st mismatched characters + bind(COMPARE_TAIL); + testl(cnt1, cnt1); + jccb(Assembler::zero, LENGTH_DIFF_LABEL); + movl(cnt2, cnt1); + // Fallthru to tail compare + } + + // Shift str2 and str1 to the end of the arrays, negate min + lea(str1, Address(str1, cnt2, Address::times_2, 0)); + lea(str2, Address(str2, cnt2, Address::times_2, 0)); + negptr(cnt2); + + // Compare the rest of the characters + bind(WHILE_HEAD_LABEL); + load_unsigned_short(result, Address(str1, cnt2, Address::times_2, 0)); + load_unsigned_short(cnt1, Address(str2, cnt2, Address::times_2, 0)); + subl(result, cnt1); + jccb(Assembler::notZero, POP_LABEL); + increment(cnt2); + jcc(Assembler::notZero, WHILE_HEAD_LABEL); + + // Strings are equal up to min length. Return the length difference. + bind(LENGTH_DIFF_LABEL); + pop(result); + jmpb(DONE_LABEL); + + // Discard the stored length difference + bind(POP_LABEL); + addptr(rsp, wordSize); + + // That's it + bind(DONE_LABEL); +} + +// Compare char[] arrays aligned to 4 bytes or substrings. +void MacroAssembler::char_arrays_equals(bool is_array_equ, Register ary1, Register ary2, + Register limit, Register result, Register chr, + XMMRegister vec1, XMMRegister vec2) { + Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR; + + int length_offset = arrayOopDesc::length_offset_in_bytes(); + int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); + + // Check the input args + cmpptr(ary1, ary2); + jcc(Assembler::equal, TRUE_LABEL); + + if (is_array_equ) { + // Need additional checks for arrays_equals. + testptr(ary1, ary1); + jcc(Assembler::zero, FALSE_LABEL); + testptr(ary2, ary2); + jcc(Assembler::zero, FALSE_LABEL); + + // Check the lengths + movl(limit, Address(ary1, length_offset)); + cmpl(limit, Address(ary2, length_offset)); + jcc(Assembler::notEqual, FALSE_LABEL); + } + + // count == 0 + testl(limit, limit); + jcc(Assembler::zero, TRUE_LABEL); + + if (is_array_equ) { + // Load array address + lea(ary1, Address(ary1, base_offset)); + lea(ary2, Address(ary2, base_offset)); + } + + shll(limit, 1); // byte count != 0 + movl(result, limit); // copy + + if (UseSSE42Intrinsics) { + // With SSE4.2, use double quad vector compare + Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; + // Compare 16-byte vectors + andl(result, 0x0000000e); // tail count (in bytes) + andl(limit, 0xfffffff0); // vector count (in bytes) + jccb(Assembler::zero, COMPARE_TAIL); + + lea(ary1, Address(ary1, limit, Address::times_1)); + lea(ary2, Address(ary2, limit, Address::times_1)); + negptr(limit); + + bind(COMPARE_WIDE_VECTORS); + movdqu(vec1, Address(ary1, limit, Address::times_1)); + movdqu(vec2, Address(ary2, limit, Address::times_1)); + pxor(vec1, vec2); + ptest(vec1, vec1); + jccb(Assembler::notZero, FALSE_LABEL); + addptr(limit, 16); + jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); + + bind(COMPARE_TAIL); // limit is zero + movl(limit, result); + // Fallthru to tail compare + } + + // Compare 4-byte vectors + andl(limit, 0xfffffffc); // vector count (in bytes) + jccb(Assembler::zero, COMPARE_CHAR); + + lea(ary1, Address(ary1, limit, Address::times_1)); + lea(ary2, Address(ary2, limit, Address::times_1)); + negptr(limit); + + bind(COMPARE_VECTORS); + movl(chr, Address(ary1, limit, Address::times_1)); + cmpl(chr, Address(ary2, limit, Address::times_1)); + jccb(Assembler::notEqual, FALSE_LABEL); + addptr(limit, 4); + jcc(Assembler::notZero, COMPARE_VECTORS); + + // Compare trailing char (final 2 bytes), if any + bind(COMPARE_CHAR); + testl(result, 0x2); // tail char + jccb(Assembler::zero, TRUE_LABEL); + load_unsigned_short(chr, Address(ary1, 0)); + load_unsigned_short(limit, Address(ary2, 0)); + cmpl(chr, limit); + jccb(Assembler::notEqual, FALSE_LABEL); + + bind(TRUE_LABEL); + movl(result, 1); // return true + jmpb(DONE); + + bind(FALSE_LABEL); + xorl(result, result); // return false + + // That's it + bind(DONE); +} + Assembler::Condition MacroAssembler::negate_condition(Assembler::Condition cond) { switch (cond) { // Note some conditions are synonyms for others diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/assembler_x86.hpp --- a/src/cpu/x86/vm/assembler_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/assembler_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1244,7 +1244,9 @@ void pcmpestri(XMMRegister xmm1, XMMRegister xmm2, int imm8); void pcmpestri(XMMRegister xmm1, Address src, int imm8); +#ifndef _LP64 // no 32bit push/pop on amd64 void popl(Address dst); +#endif #ifdef _LP64 void popq(Address dst); @@ -1285,7 +1287,9 @@ // Interleave Low Bytes void punpcklbw(XMMRegister dst, XMMRegister src); +#ifndef _LP64 // no 32bit push/pop on amd64 void pushl(Address src); +#endif void pushq(Address src); @@ -1682,6 +1686,17 @@ void load_heap_oop(Register dst, Address src); void store_heap_oop(Address dst, Register src); + + // This dummy is to prevent a call to store_heap_oop from + // converting a zero (like NULL) into a Register by giving + // the compiler two choices it can't resolve + + void store_heap_oop(Address dst, void* dummy); + + // Used for storing NULL. All other oop constants should be + // stored using routines that take a jobject. + void store_heap_oop_null(Address dst); + void encode_heap_oop(Register r); void decode_heap_oop(Register r); void encode_heap_oop_not_null(Register r); @@ -2206,6 +2221,20 @@ void movl2ptr(Register dst, Address src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(movl(dst, src)); } void movl2ptr(Register dst, Register src) { LP64_ONLY(movslq(dst, src)) NOT_LP64(if (dst != src) movl(dst, src)); } + // IndexOf strings. + void string_indexof(Register str1, Register str2, + Register cnt1, Register cnt2, Register result, + XMMRegister vec, Register tmp); + + // Compare strings. + void string_compare(Register str1, Register str2, + Register cnt1, Register cnt2, Register result, + XMMRegister vec1, XMMRegister vec2); + + // Compare char[] arrays. + void char_arrays_equals(bool is_array_equ, Register ary1, Register ary2, + Register limit, Register result, Register chr, + XMMRegister vec1, XMMRegister vec2); #undef VIRTUAL diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/c1_LIRAssembler_x86.cpp --- a/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 Sun Microsystems, 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 @@ -301,22 +301,25 @@ Register OSR_buf = osrBufferPointer()->as_pointer_register(); { assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); int monitor_offset = BytesPerWord * method()->max_locals() + - (BasicObjectLock::size() * BytesPerWord) * (number_of_locks - 1); + (2 * BytesPerWord) * (number_of_locks - 1); + // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in + // the OSR buffer using 2 word entries: first the lock and then + // the oop. for (int i = 0; i < number_of_locks; i++) { - int slot_offset = monitor_offset - ((i * BasicObjectLock::size()) * BytesPerWord); + int slot_offset = monitor_offset - ((i * 2) * BytesPerWord); #ifdef ASSERT // verify the interpreter's monitor has a non-null object { Label L; - __ cmpptr(Address(OSR_buf, slot_offset + BasicObjectLock::obj_offset_in_bytes()), (int32_t)NULL_WORD); + __ cmpptr(Address(OSR_buf, slot_offset + 1*BytesPerWord), (int32_t)NULL_WORD); __ jcc(Assembler::notZero, L); __ stop("locked object is NULL"); __ bind(L); } #endif - __ movptr(rbx, Address(OSR_buf, slot_offset + BasicObjectLock::lock_offset_in_bytes())); + __ movptr(rbx, Address(OSR_buf, slot_offset + 0)); __ movptr(frame_map()->address_for_monitor_lock(i), rbx); - __ movptr(rbx, Address(OSR_buf, slot_offset + BasicObjectLock::obj_offset_in_bytes())); + __ movptr(rbx, Address(OSR_buf, slot_offset + 1*BytesPerWord)); __ movptr(frame_map()->address_for_monitor_object(i), rbx); } } @@ -415,13 +418,12 @@ } -void LIR_Assembler::emit_exception_handler() { +int LIR_Assembler::emit_exception_handler() { // if the last instruction is a call (typically to do a throw which // is coming at the end after block reordering) the return address // must still point into the code area in order to avoid assertion // failures when searching for the corresponding bci => add a nop // (was bug 5/14/1999 - gri) - __ nop(); // generate code for exception handler @@ -429,17 +431,14 @@ if (handler_base == NULL) { // not enough space left for the handler bailout("exception handler overflow"); - return; + return -1; } -#ifdef ASSERT + int offset = code_offset(); -#endif // ASSERT - - compilation()->offsets()->set_value(CodeOffsets::Exceptions, code_offset()); // if the method does not have an exception handler, then there is // no reason to search for one - if (compilation()->has_exception_handlers() || compilation()->env()->jvmti_can_post_exceptions()) { + if (compilation()->has_exception_handlers() || compilation()->env()->jvmti_can_post_on_exceptions()) { // the exception oop and pc are in rax, and rdx // no other registers need to be preserved, so invalidate them __ invalidate_registers(false, true, true, false, true, true); @@ -471,19 +470,19 @@ // unwind activation and forward exception to caller // rax,: exception __ jump(RuntimeAddress(Runtime1::entry_for(Runtime1::unwind_exception_id))); - assert(code_offset() - offset <= exception_handler_size, "overflow"); - __ end_a_stub(); + + return offset; } -void LIR_Assembler::emit_deopt_handler() { + +int LIR_Assembler::emit_deopt_handler() { // if the last instruction is a call (typically to do a throw which // is coming at the end after block reordering) the return address // must still point into the code area in order to avoid assertion // failures when searching for the corresponding bci => add a nop // (was bug 5/14/1999 - gri) - __ nop(); // generate code for exception handler @@ -491,23 +490,17 @@ if (handler_base == NULL) { // not enough space left for the handler bailout("deopt handler overflow"); - return; + return -1; } -#ifdef ASSERT + int offset = code_offset(); -#endif // ASSERT - - compilation()->offsets()->set_value(CodeOffsets::Deopt, code_offset()); - InternalAddress here(__ pc()); __ pushptr(here.addr()); - __ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); - assert(code_offset() - offset <= deopt_handler_size, "overflow"); - __ end_a_stub(); + return offset; } @@ -785,7 +778,13 @@ ShouldNotReachHere(); __ movoop(as_Address(addr, noreg), c->as_jobject()); } else { +#ifdef _LP64 + __ movoop(rscratch1, c->as_jobject()); + null_check_here = code_offset(); + __ movptr(as_Address_lo(addr), rscratch1); +#else __ movoop(as_Address(addr), c->as_jobject()); +#endif } } break; @@ -1118,8 +1117,14 @@ __ pushptr(frame_map()->address_for_slot(src ->single_stack_ix())); __ popptr (frame_map()->address_for_slot(dest->single_stack_ix())); } else { +#ifndef _LP64 __ pushl(frame_map()->address_for_slot(src ->single_stack_ix())); __ popl (frame_map()->address_for_slot(dest->single_stack_ix())); +#else + //no pushl on 64bits + __ movl(rscratch1, frame_map()->address_for_slot(src ->single_stack_ix())); + __ movl(frame_map()->address_for_slot(dest->single_stack_ix()), rscratch1); +#endif } } else if (src->is_double_stack()) { @@ -1638,7 +1643,7 @@ jobject2reg_with_patching(k_RInfo, op->info_for_patch()); } else { #ifdef _LP64 - __ movoop(k_RInfo, k->encoding()); + __ movoop(k_RInfo, k->constant_encoding()); #else k_RInfo = noreg; #endif // _LP64 @@ -1661,7 +1666,7 @@ assert(data != NULL, "need data for checkcast"); assert(data->is_BitData(), "need BitData for checkcast"); Register mdo = klass_RInfo; - __ movoop(mdo, md->encoding()); + __ movoop(mdo, md->constant_encoding()); Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::header_offset())); int header_bits = DataLayout::flag_mask_to_header_mask(BitData::null_seen_byte_constant()); __ orl(data_addr, header_bits); @@ -1679,7 +1684,7 @@ #ifdef _LP64 __ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); #else - __ cmpoop(Address(obj, oopDesc::klass_offset_in_bytes()), k->encoding()); + __ cmpoop(Address(obj, oopDesc::klass_offset_in_bytes()), k->constant_encoding()); #endif // _LP64 } else { __ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); @@ -1696,7 +1701,7 @@ #ifdef _LP64 __ cmpptr(k_RInfo, Address(klass_RInfo, k->super_check_offset())); #else - __ cmpoop(Address(klass_RInfo, k->super_check_offset()), k->encoding()); + __ cmpoop(Address(klass_RInfo, k->super_check_offset()), k->constant_encoding()); #endif // _LP64 if (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() != k->super_check_offset()) { __ jcc(Assembler::notEqual, *stub->entry()); @@ -1707,7 +1712,7 @@ #ifdef _LP64 __ cmpptr(klass_RInfo, k_RInfo); #else - __ cmpoop(klass_RInfo, k->encoding()); + __ cmpoop(klass_RInfo, k->constant_encoding()); #endif // _LP64 __ jcc(Assembler::equal, done); @@ -1715,7 +1720,7 @@ #ifdef _LP64 __ push(k_RInfo); #else - __ pushoop(k->encoding()); + __ pushoop(k->constant_encoding()); #endif // _LP64 __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); __ pop(klass_RInfo); @@ -1763,7 +1768,7 @@ if (!k->is_loaded()) { jobject2reg_with_patching(k_RInfo, op->info_for_patch()); } else { - LP64_ONLY(__ movoop(k_RInfo, k->encoding())); + LP64_ONLY(__ movoop(k_RInfo, k->constant_encoding())); } assert(obj != k_RInfo, "must be different"); @@ -1774,7 +1779,7 @@ // get object class // not a safepoint as obj null check happens earlier if (LP64_ONLY(false &&) k->is_loaded()) { - NOT_LP64(__ cmpoop(Address(obj, oopDesc::klass_offset_in_bytes()), k->encoding())); + NOT_LP64(__ cmpoop(Address(obj, oopDesc::klass_offset_in_bytes()), k->constant_encoding())); k_RInfo = noreg; } else { __ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes())); @@ -1791,14 +1796,14 @@ #ifndef _LP64 if (k->is_loaded()) { // See if we get an immediate positive hit - __ cmpoop(Address(klass_RInfo, k->super_check_offset()), k->encoding()); + __ cmpoop(Address(klass_RInfo, k->super_check_offset()), k->constant_encoding()); __ jcc(Assembler::equal, one); if (sizeof(oopDesc) + Klass::secondary_super_cache_offset_in_bytes() == k->super_check_offset()) { // check for self - __ cmpoop(klass_RInfo, k->encoding()); + __ cmpoop(klass_RInfo, k->constant_encoding()); __ jcc(Assembler::equal, one); __ push(klass_RInfo); - __ pushoop(k->encoding()); + __ pushoop(k->constant_encoding()); __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); __ pop(klass_RInfo); __ pop(dst); @@ -3112,7 +3117,7 @@ // subtype which we can't check or src is the same array as dst // but not necessarily exactly of type default_type. Label known_ok, halt; - __ movoop(tmp, default_type->encoding()); + __ movoop(tmp, default_type->constant_encoding()); if (basic_type != T_OBJECT) { __ cmpptr(tmp, dst_klass_addr); __ jcc(Assembler::notEqual, halt); @@ -3136,8 +3141,10 @@ #ifdef _LP64 assert_different_registers(c_rarg0, dst, dst_pos, length); + __ movl2ptr(src_pos, src_pos); //higher 32bits must be null __ lea(c_rarg0, Address(src, src_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); assert_different_registers(c_rarg1, length); + __ movl2ptr(dst_pos, dst_pos); //higher 32bits must be null __ lea(c_rarg1, Address(dst, dst_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); __ mov(c_rarg2, length); @@ -3200,9 +3207,8 @@ assert(data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); - __ movoop(mdo, md->encoding()); + __ movoop(mdo, md->constant_encoding()); Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ addl(counter_addr, DataLayout::counter_increment); Bytecodes::Code bc = method->java_code_at_bci(bci); // Perform additional virtual call profiling for invokevirtual and // invokeinterface bytecodes @@ -3240,7 +3246,7 @@ ciKlass* receiver = vc_data->receiver(i); if (receiver == NULL) { Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i))); - __ movoop(recv_addr, known_klass->encoding()); + __ movoop(recv_addr, known_klass->constant_encoding()); Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); __ addl(data_addr, DataLayout::counter_increment); return; @@ -3269,14 +3275,18 @@ __ jcc(Assembler::notEqual, next_test); __ movptr(recv_addr, recv); __ movl(Address(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))), DataLayout::counter_increment); - if (i < (VirtualCallData::row_limit() - 1)) { - __ jmp(update_done); - } + __ jmp(update_done); __ bind(next_test); } + // Receiver did not match any saved receiver and there is no empty row for it. + // Increment total counter to indicate polymorphic case. + __ addl(counter_addr, DataLayout::counter_increment); __ bind(update_done); } + } else { + // Static call + __ addl(counter_addr, DataLayout::counter_increment); } } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/c1_LIRGenerator_x86.cpp --- a/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, 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 @@ -755,8 +755,19 @@ } LIR_Opr addr = new_pointer_register(); - __ move(obj.result(), addr); - __ add(addr, offset.result(), addr); + LIR_Address* a; + if(offset.result()->is_constant()) { + a = new LIR_Address(obj.result(), + NOT_LP64(offset.result()->as_constant_ptr()->as_jint()) LP64_ONLY((int)offset.result()->as_constant_ptr()->as_jlong()), + as_BasicType(type)); + } else { + a = new LIR_Address(obj.result(), + offset.result(), + LIR_Address::times_1, + 0, + as_BasicType(type)); + } + __ leal(LIR_OprFact::address(a), addr); if (type == objectType) { // Write-barrier needed for Object fields. // Do the pre-write barrier, if any. @@ -827,8 +838,8 @@ case vmIntrinsics::_dsin: __ sin (calc_input, calc_result, tmp1, tmp2); break; case vmIntrinsics::_dcos: __ cos (calc_input, calc_result, tmp1, tmp2); break; case vmIntrinsics::_dtan: __ tan (calc_input, calc_result, tmp1, tmp2); break; - case vmIntrinsics::_dlog: __ log (calc_input, calc_result, LIR_OprFact::illegalOpr); break; - case vmIntrinsics::_dlog10: __ log10(calc_input, calc_result, LIR_OprFact::illegalOpr); break; + case vmIntrinsics::_dlog: __ log (calc_input, calc_result, tmp1); break; + case vmIntrinsics::_dlog10: __ log10(calc_input, calc_result, tmp1); break; default: ShouldNotReachHere(); } @@ -994,7 +1005,7 @@ LIR_Opr len = length.result(); BasicType elem_type = x->elt_type(); - __ oop2reg(ciTypeArrayKlass::make(elem_type)->encoding(), klass_reg); + __ oop2reg(ciTypeArrayKlass::make(elem_type)->constant_encoding(), klass_reg); CodeStub* slow_path = new NewTypeArrayStub(klass_reg, len, reg, info); __ allocate_array(reg, len, tmp1, tmp2, tmp3, tmp4, elem_type, klass_reg, slow_path); @@ -1047,16 +1058,17 @@ items->at_put(i, size); } - // need to get the info before, as the items may become invalid through item_free + // Evaluate state_for early since it may emit code. CodeEmitInfo* patching_info = NULL; if (!x->klass()->is_loaded() || PatchALot) { patching_info = state_for(x, x->state_before()); // cannot re-use same xhandlers for multiple CodeEmitInfos, so - // clone all handlers. + // clone all handlers. This is handled transparently in other + // places by the CodeEmitInfo cloning logic but is handled + // specially here because a stub isn't being used. x->set_exception_handlers(new XHandlers(x->exception_handlers())); } - CodeEmitInfo* info = state_for(x, x->state()); i = dims->length(); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/c1_LinearScan_x86.cpp --- a/src/cpu/x86/vm/c1_LinearScan_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/c1_LinearScan_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -764,8 +764,6 @@ break; } - case lir_log: - case lir_log10: case lir_abs: case lir_sqrt: { // Right argument appears to be unused @@ -785,6 +783,30 @@ break; } + case lir_log: + case lir_log10: { + // log and log10 needs one temporary fpu stack slot, so there is ontemporary + // registers stored in temp of the operation. + // the stack allocator must guarantee that the stack slots are really free, + // otherwise there might be a stack overflow. + assert(right->is_illegal(), "must be"); + assert(left->is_fpu_register(), "must be"); + assert(res->is_fpu_register(), "must be"); + assert(op2->tmp_opr()->is_fpu_register(), "must be"); + + insert_free_if_dead(op2->tmp_opr()); + insert_free_if_dead(res, left); + insert_exchange(left); + do_rename(left, res); + + new_left = to_fpu_stack_top(res); + new_res = new_left; + + op2->set_fpu_stack_size(sim()->stack_size()); + assert(sim()->stack_size() <= 7, "at least one stack slot must be free"); + break; + } + case lir_tan: case lir_sin: diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/c1_globals_x86.hpp --- a/src/cpu/x86/vm/c1_globals_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/c1_globals_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,10 +22,8 @@ * */ -// // Sets the default values for platform dependent flags used by the client compiler. // (see c1_globals.hpp) -// #ifndef TIERED define_pd_global(bool, BackgroundCompilation, true ); @@ -48,27 +46,24 @@ define_pd_global(intx, OnStackReplacePercentage, 933 ); define_pd_global(intx, FreqInlineSize, 325 ); -define_pd_global(intx, NewRatio, 12 ); define_pd_global(intx, NewSizeThreadIncrease, 4*K ); define_pd_global(intx, InitialCodeCacheSize, 160*K); define_pd_global(intx, ReservedCodeCacheSize, 32*M ); define_pd_global(bool, ProfileInterpreter, false); define_pd_global(intx, CodeCacheExpansionSize, 32*K ); define_pd_global(uintx,CodeCacheMinBlockLength, 1); -define_pd_global(uintx, PermSize, 12*M ); -define_pd_global(uintx, MaxPermSize, 64*M ); -define_pd_global(bool, NeverActAsServerClassMachine, true); -define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(uintx,PermSize, 12*M ); +define_pd_global(uintx,MaxPermSize, 64*M ); +define_pd_global(bool, NeverActAsServerClassMachine, true ); +define_pd_global(uint64_t,MaxRAM, 1ULL*G); define_pd_global(bool, CICompileOSR, true ); -#endif // TIERED +#endif // !TIERED define_pd_global(bool, UseTypeProfile, false); define_pd_global(bool, RoundFPResults, true ); - define_pd_global(bool, LIRFillDelaySlots, false); -define_pd_global(bool, OptimizeSinglePrecision, true); +define_pd_global(bool, OptimizeSinglePrecision, true ); define_pd_global(bool, CSEArrayLength, false); -define_pd_global(bool, TwoOperandLIRForm, true); +define_pd_global(bool, TwoOperandLIRForm, true ); - -define_pd_global(intx, SafepointPollOffset, 256); +define_pd_global(intx, SafepointPollOffset, 256 ); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/c2_globals_x86.hpp --- a/src/cpu/x86/vm/c2_globals_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/c2_globals_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,7 +22,6 @@ * */ -// // Sets the default values for platform dependent flags used by the server compiler. // (see c2_globals.hpp). Alpha-sorted. @@ -46,8 +45,8 @@ define_pd_global(intx, CompileThreshold, 10000); #endif // TIERED define_pd_global(intx, Tier2CompileThreshold, 10000); -define_pd_global(intx, Tier3CompileThreshold, 20000 ); -define_pd_global(intx, Tier4CompileThreshold, 40000 ); +define_pd_global(intx, Tier3CompileThreshold, 20000); +define_pd_global(intx, Tier4CompileThreshold, 40000); define_pd_global(intx, BackEdgeThreshold, 100000); define_pd_global(intx, Tier2BackEdgeThreshold, 100000); @@ -61,7 +60,6 @@ #ifdef AMD64 define_pd_global(intx, INTPRESSURE, 13); define_pd_global(intx, InteriorEntryAlignment, 16); -define_pd_global(intx, NewRatio, 2); define_pd_global(intx, NewSizeThreadIncrease, ScaleForWordSize(4*K)); define_pd_global(intx, LoopUnrollLimit, 60); // InitialCodeCacheSize derived from specjbb2000 run. @@ -69,19 +67,18 @@ define_pd_global(intx, CodeCacheExpansionSize, 64*K); // Ergonomics related flags -define_pd_global(uintx, DefaultMaxRAM, 32*G); +define_pd_global(uint64_t,MaxRAM, 128ULL*G); #else define_pd_global(intx, INTPRESSURE, 6); define_pd_global(intx, InteriorEntryAlignment, 4); -define_pd_global(intx, NewRatio, 8); // Design center runs on 1.3.1 define_pd_global(intx, NewSizeThreadIncrease, 4*K); -define_pd_global(intx, LoopUnrollLimit, 50); // Design center runs on 1.3.1 +define_pd_global(intx, LoopUnrollLimit, 50); // Design center runs on 1.3.1 // InitialCodeCacheSize derived from specjbb2000 run. define_pd_global(intx, InitialCodeCacheSize, 2304*K); // Integral multiple of CodeCacheExpansionSize define_pd_global(intx, CodeCacheExpansionSize, 32*K); // Ergonomics related flags -define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(uint64_t,MaxRAM, 4ULL*G); #endif // AMD64 define_pd_global(intx, OptoLoopAlignment, 16); define_pd_global(intx, RegisterCostAreaRatio, 16000); @@ -97,8 +94,8 @@ define_pd_global(uintx,CodeCacheMinBlockLength, 4); // Heap related flags -define_pd_global(uintx, PermSize, ScaleForWordSize(16*M)); -define_pd_global(uintx, MaxPermSize, ScaleForWordSize(64*M)); +define_pd_global(uintx,PermSize, ScaleForWordSize(16*M)); +define_pd_global(uintx,MaxPermSize, ScaleForWordSize(64*M)); // Ergonomics related flags define_pd_global(bool, NeverActAsServerClassMachine, false); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/frame_x86.cpp --- a/src/cpu/x86/vm/frame_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/frame_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -222,9 +222,9 @@ } ((address *)sp())[-1] = 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"); + address original_pc = nmethod::get_deopt_original_pc(this); + if (original_pc != NULL) { + assert(original_pc == _pc, "expected original PC to be stored before patching"); _deopt_state = is_deoptimized; // leave _pc as is } else { @@ -323,13 +323,63 @@ return fr; } + +//------------------------------------------------------------------------------ +// frame::verify_deopt_original_pc +// +// Verifies the calculated original PC of a deoptimization PC for the +// given unextended SP. The unextended SP might also be the saved SP +// for MethodHandle call sites. +#if ASSERT +void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return) { + frame fr; + + // This is ugly but it's better than to change {get,set}_original_pc + // to take an SP value as argument. And it's only a debugging + // method anyway. + fr._unextended_sp = unextended_sp; + + address original_pc = nm->get_original_pc(&fr); + assert(nm->code_contains(original_pc), "original PC must be in nmethod"); + assert(nm->is_method_handle_return(original_pc) == is_method_handle_return, "must be"); +} +#endif + + +//------------------------------------------------------------------------------ +// frame::sender_for_interpreter_frame frame frame::sender_for_interpreter_frame(RegisterMap* map) const { - // sp is the raw sp from the sender after adapter or interpreter extension - intptr_t* sp = (intptr_t*) addr_at(sender_sp_offset); + // SP is the raw SP from the sender after adapter or interpreter + // extension. + intptr_t* sender_sp = this->sender_sp(); // This is the sp before any possible extension (adapter/locals). intptr_t* unextended_sp = interpreter_frame_sender_sp(); + // Stored FP. + intptr_t* saved_fp = link(); + + address sender_pc = this->sender_pc(); + CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc); + assert(sender_cb, "sanity"); + nmethod* sender_nm = sender_cb->as_nmethod_or_null(); + + if (sender_nm != NULL) { + // If the sender PC is a deoptimization point, get the original + // PC. For MethodHandle call site the unextended_sp is stored in + // saved_fp. + if (sender_nm->is_deopt_mh_entry(sender_pc)) { + DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp)); + unextended_sp = saved_fp; + } + else if (sender_nm->is_deopt_entry(sender_pc)) { + DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp)); + } + else if (sender_nm->is_method_handle_return(sender_pc)) { + unextended_sp = saved_fp; + } + } + // The interpreter and compiler(s) always save EBP/RBP in a known // location on entry. We must record where that location is // so this if EBP/RBP was live on callout from c2 we can find @@ -351,29 +401,52 @@ } #endif // AMD64 } -#endif /* COMPILER2 */ - return frame(sp, unextended_sp, link(), sender_pc()); +#endif // COMPILER2 + + return frame(sender_sp, unextended_sp, saved_fp, sender_pc); } -//------------------------------sender_for_compiled_frame----------------------- +//------------------------------------------------------------------------------ +// frame::sender_for_compiled_frame frame frame::sender_for_compiled_frame(RegisterMap* map) const { assert(map != NULL, "map must be set"); - const bool c1_compiled = _cb->is_compiled_by_c1(); // frame owned by optimizing compiler - intptr_t* sender_sp = NULL; - assert(_cb->frame_size() >= 0, "must have non-zero frame size"); - sender_sp = unextended_sp() + _cb->frame_size(); + intptr_t* sender_sp = unextended_sp() + _cb->frame_size(); + intptr_t* unextended_sp = sender_sp; // On Intel the return_address is always the word on the stack address sender_pc = (address) *(sender_sp-1); - // This is the saved value of ebp which may or may not really be an fp. - // it is only an fp if the sender is an interpreter frame (or c1?) + // This is the saved value of EBP which may or may not really be an FP. + // It is only an FP if the sender is an interpreter frame (or C1?). + intptr_t* saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset); + + // 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: + CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc); + assert(sender_cb, "sanity"); + nmethod* sender_nm = sender_cb->as_nmethod_or_null(); - intptr_t *saved_fp = (intptr_t*)*(sender_sp - frame::sender_sp_offset); + if (sender_nm != NULL) { + // If the sender PC is a deoptimization point, get the original + // PC. For MethodHandle call site the unextended_sp is stored in + // saved_fp. + if (sender_nm->is_deopt_mh_entry(sender_pc)) { + DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp)); + unextended_sp = saved_fp; + } + else if (sender_nm->is_deopt_entry(sender_pc)) { + DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp)); + } + else if (sender_nm->is_method_handle_return(sender_pc)) { + unextended_sp = saved_fp; + } + } if (map->update_map()) { // Tell GC to use argument oopmaps for some runtime stubs that need it. @@ -383,7 +456,7 @@ if (_cb->oop_maps() != NULL) { OopMapSet::update_register_map(this, map); } - // Since the prolog does the save and restore of epb there is no oopmap + // Since the prolog does the save and restore of EBP there is no oopmap // for it so we must fill in its location as if there was an oopmap entry // since if our caller was compiled code there could be live jvm state in it. map->set_location(rbp->as_VMReg(), (address) (sender_sp - frame::sender_sp_offset)); @@ -399,9 +472,12 @@ } assert(sender_sp != sp(), "must have changed"); - return frame(sender_sp, saved_fp, sender_pc); + return frame(sender_sp, unextended_sp, saved_fp, sender_pc); } + +//------------------------------------------------------------------------------ +// frame::sender frame frame::sender(RegisterMap* map) const { // Default is we done have to follow them. The sender_for_xxx will // update it accordingly diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/frame_x86.hpp --- a/src/cpu/x86/vm/frame_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/frame_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -163,6 +163,14 @@ return (intptr_t*) addr_at(offset); } +#if ASSERT + // Used in frame::sender_for_{interpreter,compiled}_frame + static void verify_deopt_original_pc( nmethod* nm, intptr_t* unextended_sp, bool is_method_handle_return = false); + static void verify_deopt_mh_original_pc(nmethod* nm, intptr_t* unextended_sp) { + verify_deopt_original_pc(nm, unextended_sp, true); + } +#endif + public: // Constructors diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/frame_x86.inline.hpp --- a/src/cpu/x86/vm/frame_x86.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/frame_x86.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -35,32 +35,35 @@ _deopt_state = unknown; } -inline frame:: frame(intptr_t* sp, intptr_t* fp, address pc) { +inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) { _sp = sp; _unextended_sp = sp; _fp = fp; _pc = pc; assert(pc != NULL, "no pc?"); _cb = CodeCache::find_blob(pc); - _deopt_state = not_deoptimized; - if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { - _pc = (((nmethod*)_cb)->get_original_pc(this)); + + 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; } } -inline frame:: frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc) { +inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc) { _sp = sp; _unextended_sp = unextended_sp; _fp = fp; _pc = pc; assert(pc != NULL, "no pc?"); _cb = CodeCache::find_blob(pc); - _deopt_state = not_deoptimized; - if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { - _pc = (((nmethod*)_cb)->get_original_pc(this)); + + address original_pc = nmethod::get_deopt_original_pc(this); + if (original_pc != NULL) { + _pc = original_pc; + assert(((nmethod*)_cb)->code_contains(_pc), "original PC must be in nmethod"); _deopt_state = is_deoptimized; } else { _deopt_state = not_deoptimized; @@ -86,9 +89,9 @@ _cb = CodeCache::find_blob(_pc); - _deopt_state = not_deoptimized; - if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { - _pc = (((nmethod*)_cb)->get_original_pc(this)); + 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; @@ -225,11 +228,13 @@ // top of expression stack inline intptr_t* frame::interpreter_frame_tos_address() const { intptr_t* last_sp = interpreter_frame_last_sp(); - if (last_sp == NULL ) { + if (last_sp == NULL) { return sp(); } else { - // sp() may have been extended by an adapter - assert(last_sp < fp() && last_sp >= sp(), "bad tos"); + // sp() may have been extended or shrunk by an adapter. At least + // check that we don't fall behind the legal region. + // For top deoptimized frame last_sp == interpreter_frame_monitor_end. + assert(last_sp <= (intptr_t*) interpreter_frame_monitor_end(), "bad tos"); return last_sp; } } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/globals_x86.hpp --- a/src/cpu/x86/vm/globals_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/globals_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,17 +22,16 @@ * */ -// // 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, true); -define_pd_global(bool, CountInterpCalls, true); +define_pd_global(bool, ConvertSleepToYield, true); +define_pd_global(bool, ShareVtableStubs, true); +define_pd_global(bool, CountInterpCalls, true); +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, ImplicitNullChecks, true); // Generate code for implicit null checks +define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NULLs past 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 @@ -42,29 +41,24 @@ // the uep and the vep doesn't get real alignment but just slops on by // only assured that the entry instruction meets the 5 byte size requirement. #ifdef COMPILER2 -define_pd_global(intx, CodeEntryAlignment, 32); +define_pd_global(intx, CodeEntryAlignment, 32); #else -define_pd_global(intx, CodeEntryAlignment, 16); +define_pd_global(intx, CodeEntryAlignment, 16); #endif // COMPILER2 +define_pd_global(intx, InlineFrequencyCount, 100); +define_pd_global(intx, InlineSmallCode, 1000); -define_pd_global(bool, NeedsDeoptSuspend, false); // only register window machines need this - -define_pd_global(uintx, TLABSize, 0); +define_pd_global(intx, StackYellowPages, 2); +define_pd_global(intx, StackRedPages, 1); #ifdef AMD64 -define_pd_global(uintx, NewSize, ScaleForWordSize(2048 * K)); // Very large C++ stack frames using solaris-amd64 optimized builds // due to lack of optimization caused by C++ compiler bugs define_pd_global(intx, StackShadowPages, SOLARIS_ONLY(20) NOT_SOLARIS(6) DEBUG_ONLY(+2)); #else -define_pd_global(uintx, NewSize, 1024 * K); define_pd_global(intx, StackShadowPages, 3 DEBUG_ONLY(+1)); #endif // AMD64 -define_pd_global(intx, InlineFrequencyCount, 100); -define_pd_global(intx, InlineSmallCode, 1000); -define_pd_global(intx, PreInflateSpin, 10); -define_pd_global(intx, StackYellowPages, 2); -define_pd_global(intx, StackRedPages, 1); +define_pd_global(intx, PreInflateSpin, 10); define_pd_global(bool, RewriteBytecodes, true); define_pd_global(bool, RewriteFrequentPairs, true); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/interp_masm_x86_32.cpp --- a/src/cpu/x86/vm/interp_masm_x86_32.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_32.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -196,6 +196,9 @@ } else { assert(EnableInvokeDynamic, "giant index used only for EnableInvokeDynamic"); movl(reg, Address(rsi, bcp_offset)); + // Check if the secondary index definition is still ~x, otherwise + // we have to change the following assembler code to calculate the + // plain index. assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line"); notl(reg); // convert to plain index } @@ -1236,17 +1239,19 @@ // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - // We are making a call. Increment the count. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - Label skip_receiver_profile; if (receiver_can_be_null) { + Label not_null; testptr(receiver, receiver); - jcc(Assembler::zero, skip_receiver_profile); + jccb(Assembler::notZero, not_null); + // We are making a call. Increment the count for null receiver. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + jmp(skip_receiver_profile); + bind(not_null); } // Record the receiver type. - record_klass_in_profile(receiver, mdp, reg2); + record_klass_in_profile(receiver, mdp, reg2, true); bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. @@ -1260,8 +1265,15 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper( Register receiver, Register mdp, - Register reg2, - int start_row, Label& done) { + Register reg2, int start_row, + Label& done, bool is_virtual_call) { + if (TypeProfileWidth == 0) { + if (is_virtual_call) { + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + } + 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. @@ -1288,19 +1300,28 @@ bind(next_test); if (row == start_row) { + Label found_null; // Failed the equality check on receiver[n]... Test for null. testptr(reg2, reg2); if (start_row == last_row) { // The only thing left to do is handle the null case. - jcc(Assembler::notZero, done); + if (is_virtual_call) { + jccb(Assembler::zero, 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(mdp, in_bytes(CounterData::count_offset())); + jmp(done); + bind(found_null); + } else { + jcc(Assembler::notZero, done); + } break; } // Since null is rare, make it be the branch-taken case. - Label found_null; jcc(Assembler::zero, found_null); // Put all the "Case 3" tests here. - record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done); + record_klass_in_profile_helper(receiver, mdp, reg2, 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. @@ -1317,16 +1338,18 @@ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); movptr(reg2, (int32_t)DataLayout::counter_increment); set_mdp_data_at(mdp, count_offset, reg2); - jmp(done); + if (start_row > 0) { + jmp(done); + } } void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register mdp, - Register reg2) { + Register mdp, Register reg2, + bool is_virtual_call) { assert(ProfileInterpreter, "must be profiling"); Label done; - record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); + record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call); bind (done); } @@ -1419,7 +1442,7 @@ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, mdp, reg2); + record_klass_in_profile(klass, mdp, reg2, false); assert(reg2 == rdi, "we know how to fix this blown reg"); restore_locals(); // Restore EDI } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/interp_masm_x86_32.hpp --- a/src/cpu/x86/vm/interp_masm_x86_32.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_32.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -213,10 +213,10 @@ Label& not_equal_continue); void record_klass_in_profile(Register receiver, Register mdp, - Register reg2); + Register reg2, bool is_virtual_call); void record_klass_in_profile_helper(Register receiver, Register mdp, - Register reg2, - int start_row, Label& done); + Register reg2, int start_row, + Label& done, bool is_virtual_call); void update_mdp_by_offset(Register mdp_in, int offset_of_offset); void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/interp_masm_x86_64.cpp --- a/src/cpu/x86/vm/interp_masm_x86_64.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -185,12 +185,30 @@ } +void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index, + int bcp_offset, + bool giant_index) { + assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + if (!giant_index) { + load_unsigned_short(index, Address(r13, bcp_offset)); + } else { + assert(EnableInvokeDynamic, "giant index used only for EnableInvokeDynamic"); + movl(index, Address(r13, bcp_offset)); + // Check if the secondary index definition is still ~x, otherwise + // we have to change the following assembler code to calculate the + // plain index. + assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line"); + notl(index); // convert to plain index + } +} + + void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, Register index, - int bcp_offset) { - assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + int bcp_offset, + bool giant_index) { assert(cache != index, "must use different registers"); - load_unsigned_short(index, Address(r13, bcp_offset)); + get_cache_index_at_bcp(index, bcp_offset, giant_index); movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize)); assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); // convert from field index to ConstantPoolCacheEntry index @@ -200,10 +218,10 @@ void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache, Register tmp, - int bcp_offset) { - assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + int bcp_offset, + bool giant_index) { assert(cache != tmp, "must use different register"); - load_unsigned_short(tmp, Address(r13, bcp_offset)); + get_cache_index_at_bcp(tmp, bcp_offset, giant_index); assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below"); // convert from field index to ConstantPoolCacheEntry index // and from word offset to byte offset @@ -1236,18 +1254,28 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2) { + Register reg2, + bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; // If no method data exists, go to profile_continue. test_method_data_pointer(mdp, profile_continue); - // We are making a call. Increment the count. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + Label skip_receiver_profile; + if (receiver_can_be_null) { + Label not_null; + testptr(receiver, receiver); + jccb(Assembler::notZero, not_null); + // We are making a call. Increment the count for null receiver. + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + jmp(skip_receiver_profile); + bind(not_null); + } // Record the receiver type. - record_klass_in_profile(receiver, mdp, reg2); + record_klass_in_profile(receiver, mdp, reg2, true); + bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. update_mdp_by_constant(mdp, @@ -1270,8 +1298,15 @@ // See below for example code. void InterpreterMacroAssembler::record_klass_in_profile_helper( Register receiver, Register mdp, - Register reg2, - int start_row, Label& done) { + Register reg2, int start_row, + Label& done, bool is_virtual_call) { + if (TypeProfileWidth == 0) { + if (is_virtual_call) { + increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); + } + 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. @@ -1298,19 +1333,28 @@ bind(next_test); if (test_for_null_also) { + Label found_null; // Failed the equality check on receiver[n]... Test for null. testptr(reg2, reg2); if (start_row == last_row) { // The only thing left to do is handle the null case. - jcc(Assembler::notZero, done); + if (is_virtual_call) { + jccb(Assembler::zero, 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(mdp, in_bytes(CounterData::count_offset())); + jmp(done); + bind(found_null); + } else { + jcc(Assembler::notZero, done); + } break; } // Since null is rare, make it be the branch-taken case. - Label found_null; jcc(Assembler::zero, found_null); // Put all the "Case 3" tests here. - record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done); + record_klass_in_profile_helper(receiver, mdp, reg2, 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. @@ -1327,7 +1371,9 @@ int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row)); movl(reg2, DataLayout::counter_increment); set_mdp_data_at(mdp, count_offset, reg2); - jmp(done); + if (start_row > 0) { + jmp(done); + } } // Example state machine code for three profile rows: @@ -1339,7 +1385,7 @@ // if (row[1].rec != NULL) { // // degenerate decision tree, rooted at row[2] // if (row[2].rec == rec) { row[2].incr(); goto done; } -// if (row[2].rec != NULL) { goto done; } // overflow +// if (row[2].rec != NULL) { count.incr(); goto done; } // overflow // row[2].init(rec); goto done; // } else { // // remember row[1] is empty @@ -1352,14 +1398,15 @@ // if (row[2].rec == rec) { row[2].incr(); goto done; } // row[0].init(rec); goto done; // } +// done: void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register mdp, - Register reg2) { + Register mdp, Register reg2, + bool is_virtual_call) { assert(ProfileInterpreter, "must be profiling"); Label done; - record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); + record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call); bind (done); } @@ -1455,7 +1502,7 @@ mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, mdp, reg2); + record_klass_in_profile(klass, mdp, reg2, false); } update_mdp_by_constant(mdp, mdp_delta); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/interp_masm_x86_64.hpp --- a/src/cpu/x86/vm/interp_masm_x86_64.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_64.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -95,9 +95,10 @@ void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset); void get_cache_and_index_at_bcp(Register cache, Register index, - int bcp_offset); + int bcp_offset, bool giant_index = false); void get_cache_entry_pointer_at_bcp(Register cache, Register tmp, - int bcp_offset); + int bcp_offset, bool giant_index = false); + void get_cache_index_at_bcp(Register index, int bcp_offset, bool giant_index = false); void pop_ptr(Register r = rax); @@ -221,10 +222,10 @@ Label& not_equal_continue); void record_klass_in_profile(Register receiver, Register mdp, - Register reg2); + Register reg2, bool is_virtual_call); void record_klass_in_profile_helper(Register receiver, Register mdp, - Register reg2, - int start_row, Label& done); + Register reg2, int start_row, + Label& done, bool is_virtual_call); void update_mdp_by_offset(Register mdp_in, int offset_of_offset); void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); @@ -236,7 +237,8 @@ void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register scratch2); + Register scratch2, + bool receiver_can_be_null = false); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); void profile_typecheck(Register mdp, Register klass, Register scratch); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/interpreter_x86_64.cpp --- a/src/cpu/x86/vm/interpreter_x86_64.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/interpreter_x86_64.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -277,12 +277,11 @@ address entry_point = __ pc(); // abstract method entry - // remove return address. Not really needed, since exception - // handling throws away expression stack - __ pop(rbx); - // adjust stack to what a normal return would do - __ mov(rsp, r13); + // pop return address, reset last_sp to NULL + __ empty_expression_stack(); + __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) + __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) // throw exception __ call_VM(noreg, CAST_FROM_FN_PTR(address, @@ -300,7 +299,10 @@ if (!EnableMethodHandles) { return generate_abstract_entry(); } - return generate_abstract_entry(); //6815692// + + address entry_point = MethodHandles::generate_method_handle_interpreter_entry(_masm); + + return entry_point; } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/methodHandles_x86.cpp --- a/src/cpu/x86/vm/methodHandles_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/methodHandles_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -65,9 +65,9 @@ // Verify that argslot lies within (rsp, rbp]. Label L_ok, L_bad; __ cmpptr(rax_argslot, rbp); - __ jcc(Assembler::above, L_bad); + __ jccb(Assembler::above, L_bad); __ cmpptr(rsp, rax_argslot); - __ jcc(Assembler::below, L_ok); + __ jccb(Assembler::below, L_ok); __ bind(L_bad); __ stop(error_message); __ bind(L_ok); @@ -136,9 +136,9 @@ if (arg_slots.is_register()) { Label L_ok, L_bad; __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); - __ jcc(Assembler::greater, L_bad); + __ jccb(Assembler::greater, L_bad); __ testl(arg_slots.as_register(), -stack_move_unit() - 1); - __ jcc(Assembler::zero, L_ok); + __ jccb(Assembler::zero, L_ok); __ bind(L_bad); __ stop("assert arg_slots <= 0 and clear low bits"); __ bind(L_ok); @@ -173,7 +173,7 @@ __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp); __ addptr(rdx_temp, wordSize); __ cmpptr(rdx_temp, rax_argslot); - __ jcc(Assembler::less, loop); + __ jccb(Assembler::less, loop); } // Now move the argslot down, to point to the opened-up space. @@ -211,9 +211,9 @@ Label L_ok, L_bad; __ lea(rbx_temp, Address(rax_argslot, arg_slots, Address::times_ptr)); __ cmpptr(rbx_temp, rbp); - __ jcc(Assembler::above, L_bad); + __ jccb(Assembler::above, L_bad); __ cmpptr(rsp, rax_argslot); - __ jcc(Assembler::below, L_ok); + __ jccb(Assembler::below, L_ok); __ bind(L_bad); __ stop("deleted argument(s) must fall within current frame"); __ bind(L_ok); @@ -221,9 +221,9 @@ if (arg_slots.is_register()) { Label L_ok, L_bad; __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); - __ jcc(Assembler::less, L_bad); + __ jccb(Assembler::less, L_bad); __ testl(arg_slots.as_register(), -stack_move_unit() - 1); - __ jcc(Assembler::zero, L_ok); + __ jccb(Assembler::zero, L_ok); __ bind(L_bad); __ stop("assert arg_slots >= 0 and clear low bits"); __ bind(L_ok); @@ -258,7 +258,7 @@ __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp); __ addptr(rdx_temp, -wordSize); __ cmpptr(rdx_temp, rsp); - __ jcc(Assembler::greaterEqual, loop); + __ jccb(Assembler::greaterEqual, loop); } // Now move the argslot up, to point to the just-copied block. @@ -268,12 +268,20 @@ } #ifndef PRODUCT +extern "C" void print_method_handle(oop mh); void trace_method_handle_stub(const char* adaptername, - oopDesc* mh, + oop mh, intptr_t* entry_sp, - intptr_t* saved_sp) { + intptr_t* saved_sp, + intptr_t* saved_bp) { // called as a leaf from native code: do not block the JVM! - printf("MH %s "PTR_FORMAT" "PTR_FORMAT" "INTX_FORMAT"\n", adaptername, (void*)mh, entry_sp, entry_sp - saved_sp); + intptr_t* last_sp = (intptr_t*) saved_bp[frame::interpreter_frame_last_sp_offset]; + intptr_t* base_sp = (intptr_t*) saved_bp[frame::interpreter_frame_monitor_block_top_offset]; + printf("MH %s mh="INTPTR_FORMAT" sp=("INTPTR_FORMAT"+"INTX_FORMAT") stack_size="INTX_FORMAT" bp="INTPTR_FORMAT"\n", + adaptername, (intptr_t)mh, (intptr_t)entry_sp, (intptr_t)(saved_sp - entry_sp), (intptr_t)(base_sp - last_sp), (intptr_t)saved_bp); + if (last_sp != saved_sp) + printf("*** last_sp="INTPTR_FORMAT"\n", (intptr_t)last_sp); + if (Verbose) print_method_handle(mh); } #endif //PRODUCT @@ -293,6 +301,10 @@ Register rbx_temp = rbx; Register rdx_temp = rdx; + // This guy is set up by prepare_to_jump_from_interpreted (from interpreted calls) + // and gen_c2i_adapter (from compiled calls): + Register saved_last_sp = LP64_ONLY(r13) NOT_LP64(rsi); + guarantee(java_dyn_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets"); // some handy addresses @@ -315,6 +327,8 @@ assert(tag_offset = wordSize, "stack grows as expected"); } + const int java_mirror_offset = klassOopDesc::klass_part_offset_in_bytes() + Klass::java_mirror_offset_in_bytes(); + if (have_entry(ek)) { __ nop(); // empty stubs make SG sick return; @@ -328,45 +342,65 @@ __ push(rax); __ push(rbx); __ push(rcx); __ push(rdx); __ push(rsi); __ push(rdi); __ lea(rax, Address(rsp, wordSize*6)); // entry_sp // arguments: + __ push(rbp); // interpreter frame pointer __ push(rsi); // saved_sp __ push(rax); // entry_sp __ push(rcx); // mh __ push(rcx); __ movptr(Address(rsp, 0), (intptr_t)entry_name(ek)); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), 4); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), 5); __ pop(rdi); __ pop(rsi); __ pop(rdx); __ pop(rcx); __ pop(rbx); __ pop(rax); } #endif //PRODUCT switch ((int) ek) { - case _check_mtype: + case _raise_exception: { - // this stub is special, because it requires a live mtype argument - Register rax_mtype = rax; + // Not a real MH entry, but rather shared code for raising an exception. + // Extra local arguments are pushed on stack, as required type at TOS+8, + // failing object (or NULL) at TOS+4, failing bytecode type at TOS. + // Beyond those local arguments are the PC, of course. + Register rdx_code = rdx_temp; + Register rcx_fail = rcx_recv; + Register rax_want = rax_argslot; + Register rdi_pc = rdi; + __ pop(rdx_code); // TOS+0 + __ pop(rcx_fail); // TOS+4 + __ pop(rax_want); // TOS+8 + __ pop(rdi_pc); // caller PC - // emit WrongMethodType path first, to enable jccb back-branch - Label wrong_method_type; - __ bind(wrong_method_type); - __ movptr(rdx_temp, ExternalAddress((address) &_entries[_wrong_method_type])); - __ jmp(Address(rdx_temp, MethodHandleEntry::from_interpreted_entry_offset_in_bytes())); - __ hlt(); + __ mov(rsp, rsi); // cut the stack back to where the caller started - interp_entry = __ pc(); - __ check_method_handle_type(rax_mtype, rcx_recv, rdx_temp, wrong_method_type); - // now rax_mtype is dead; subsequent stubs will use it as a temp - - __ jump_to_method_handle_entry(rcx_recv, rdx_temp); - } - break; + // Repush the arguments as if coming from the interpreter. + if (TaggedStackInterpreter) __ push(frame::tag_for_basic_type(T_INT)); + __ push(rdx_code); + if (TaggedStackInterpreter) __ push(frame::tag_for_basic_type(T_OBJECT)); + __ push(rcx_fail); + if (TaggedStackInterpreter) __ push(frame::tag_for_basic_type(T_OBJECT)); + __ push(rax_want); - case _wrong_method_type: - { - // this stub is special, because it requires a live mtype argument - Register rax_mtype = rax; + Register rbx_method = rbx_temp; + Label no_method; + // FIXME: fill in _raise_exception_method with a suitable sun.dyn method + __ movptr(rbx_method, ExternalAddress((address) &_raise_exception_method)); + __ testptr(rbx_method, rbx_method); + __ jccb(Assembler::zero, no_method); + int jobject_oop_offset = 0; + __ movptr(rbx_method, Address(rbx_method, jobject_oop_offset)); // dereference the jobject + __ testptr(rbx_method, rbx_method); + __ jccb(Assembler::zero, no_method); + __ verify_oop(rbx_method); + __ push(rdi_pc); // and restore caller PC + __ jmp(rbx_method_fie); - interp_entry = __ pc(); - __ push(rax_mtype); // required mtype - __ push(rcx_recv); // random mh (1st stacked argument) + // If we get here, the Java runtime did not do its job of creating the exception. + // Do something that is at least causes a valid throw from the interpreter. + __ bind(no_method); + __ pop(rax_want); + if (TaggedStackInterpreter) __ pop(rcx_fail); + __ pop(rcx_fail); + __ push(rax_want); + __ push(rcx_fail); __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry())); } break; @@ -416,7 +450,7 @@ rbx_index, Address::times_ptr, base + vtableEntry::method_offset_in_bytes()); Register rbx_method = rbx_temp; - __ movl(rbx_method, vtable_entry_addr); + __ movptr(rbx_method, vtable_entry_addr); __ verify_oop(rbx_method); __ jmp(rbx_method_fie); @@ -442,7 +476,7 @@ __ load_klass(rax_klass, rcx_recv); __ verify_oop(rax_klass); - Register rcx_temp = rcx_recv; + Register rdi_temp = rdi; Register rbx_method = rbx_index; // get interface klass @@ -451,7 +485,7 @@ __ lookup_interface_method(rax_klass, rdx_intf, // note: next two args must be the same: rbx_index, rbx_method, - rcx_temp, + rdi_temp, no_such_interface); __ verify_oop(rbx_method); @@ -461,7 +495,10 @@ __ bind(no_such_interface); // Throw an exception. // For historical reasons, it will be IncompatibleClassChangeError. - __ should_not_reach_here(); // %%% FIXME NYI + __ pushptr(Address(rdx_intf, java_mirror_offset)); // required interface + __ push(rcx_recv); // bad receiver + __ push((int)Bytecodes::_invokeinterface); // who is complaining? + __ jump(ExternalAddress(from_interpreted_entry(_raise_exception))); } break; @@ -498,16 +535,15 @@ if (arg_type == T_OBJECT) { __ movptr(Address(rax_argslot, 0), rbx_temp); } else { - __ load_sized_value(rbx_temp, prim_value_addr, + __ load_sized_value(rdx_temp, prim_value_addr, type2aelembytes(arg_type), is_signed_subword_type(arg_type)); - __ movptr(Address(rax_argslot, 0), rbx_temp); + __ movptr(Address(rax_argslot, 0), rdx_temp); #ifndef _LP64 if (arg_slots == 2) { - __ movl(rbx_temp, prim_value_addr.plus_disp(wordSize)); - __ movl(Address(rax_argslot, Interpreter::stackElementSize()), rbx_temp); + __ movl(rdx_temp, prim_value_addr.plus_disp(wordSize)); + __ movl(Address(rax_argslot, Interpreter::stackElementSize()), rdx_temp); } #endif //_LP64 - break; } if (direct_to_method) { @@ -524,6 +560,7 @@ break; case _adapter_retype_only: + case _adapter_retype_raw: // immediately jump to the next MH layer: __ movptr(rcx_recv, rcx_mh_vmtarget); __ verify_oop(rcx_recv); @@ -545,30 +582,32 @@ __ movptr(rbx_klass, rcx_amh_argument); // this is a Class object! __ movptr(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes())); - // get the new MH: - __ movptr(rcx_recv, rcx_mh_vmtarget); - // (now we are done with the old MH) - Label done; __ movptr(rdx_temp, vmarg); - __ testl(rdx_temp, rdx_temp); - __ jcc(Assembler::zero, done); // no cast if null + __ testptr(rdx_temp, rdx_temp); + __ jccb(Assembler::zero, done); // no cast if null __ load_klass(rdx_temp, rdx_temp); // live at this point: // - rbx_klass: klass required by the target method // - rdx_temp: argument klass to test - // - rcx_recv: method handle to invoke (after cast succeeds) + // - rcx_recv: adapter method handle __ check_klass_subtype(rdx_temp, rbx_klass, rax_argslot, done); // If we get here, the type check failed! // Call the wrong_method_type stub, passing the failing argument type in rax. Register rax_mtype = rax_argslot; - __ push(rbx_klass); // missed klass (required type) - __ push(rdx_temp); // bad actual type (1st stacked argument) - __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry())); + __ movl(rax_argslot, rcx_amh_vmargslot); // reload argslot field + __ movptr(rdx_temp, vmarg); + + __ pushptr(rcx_amh_argument); // required class + __ push(rdx_temp); // bad object + __ push((int)Bytecodes::_checkcast); // who is complaining? + __ jump(ExternalAddress(from_interpreted_entry(_raise_exception))); __ bind(done); + // get the new MH: + __ movptr(rcx_recv, rcx_mh_vmtarget); __ jump_to_method_handle_entry(rcx_recv, rdx_temp); } break; @@ -637,24 +676,24 @@ // (now we are done with the old MH) // original 32-bit vmdata word must be of this form: - // | MBZ:16 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 | - __ xchgl(rcx, rbx_vminfo); // free rcx for shifts + // | MBZ:6 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 | + __ xchgptr(rcx, rbx_vminfo); // free rcx for shifts __ shll(rdx_temp /*, rcx*/); Label zero_extend, done; __ testl(rcx, CONV_VMINFO_SIGN_FLAG); - __ jcc(Assembler::zero, zero_extend); + __ jccb(Assembler::zero, zero_extend); // this path is taken for int->byte, int->short __ sarl(rdx_temp /*, rcx*/); - __ jmp(done); + __ jmpb(done); __ bind(zero_extend); // this is taken for int->char __ shrl(rdx_temp /*, rcx*/); __ bind(done); - __ movptr(vmarg, rdx_temp); - __ xchgl(rcx, rbx_vminfo); // restore rcx_recv + __ movl(vmarg, rdx_temp); + __ xchgptr(rcx, rbx_vminfo); // restore rcx_recv __ jump_to_method_handle_entry(rcx_recv, rdx_temp); } @@ -823,7 +862,7 @@ // Verify that argslot > destslot, by at least swap_bytes. Label L_ok; __ cmpptr(rax_argslot, rbx_destslot); - __ jcc(Assembler::aboveEqual, L_ok); + __ jccb(Assembler::aboveEqual, L_ok); __ stop("source must be above destination (upward rotation)"); __ bind(L_ok); } @@ -839,7 +878,7 @@ __ movptr(Address(rax_argslot, swap_bytes), rdx_temp); __ addptr(rax_argslot, -wordSize); __ cmpptr(rax_argslot, rbx_destslot); - __ jcc(Assembler::aboveEqual, loop); + __ jccb(Assembler::aboveEqual, loop); } else { __ addptr(rax_argslot, swap_bytes); #ifdef ASSERT @@ -847,7 +886,7 @@ // Verify that argslot < destslot, by at least swap_bytes. Label L_ok; __ cmpptr(rax_argslot, rbx_destslot); - __ jcc(Assembler::belowEqual, L_ok); + __ jccb(Assembler::belowEqual, L_ok); __ stop("source must be below destination (downward rotation)"); __ bind(L_ok); } @@ -863,7 +902,7 @@ __ movptr(Address(rax_argslot, -swap_bytes), rdx_temp); __ addptr(rax_argslot, wordSize); __ cmpptr(rax_argslot, rbx_destslot); - __ jcc(Assembler::belowEqual, loop); + __ jccb(Assembler::belowEqual, loop); } // pop the original first chunk into the destination slot, now free @@ -929,7 +968,7 @@ __ addptr(rax_argslot, wordSize); __ addptr(rdx_newarg, wordSize); __ cmpptr(rdx_newarg, rbx_oldarg); - __ jcc(Assembler::less, loop); + __ jccb(Assembler::less, loop); __ pop(rdi); // restore temp @@ -1081,7 +1120,7 @@ } __ addptr(rax_argslot, Interpreter::stackElementSize()); __ cmpptr(rax_argslot, rdx_argslot_limit); - __ jcc(Assembler::less, loop); + __ jccb(Assembler::less, loop); } else if (length_constant == 0) { __ bind(skip_array_check); // nothing to copy @@ -1107,11 +1146,17 @@ __ bind(bad_array_klass); UNPUSH_RSI_RDI; - __ stop("bad array klass NYI"); + __ pushptr(Address(rdx_array_klass, java_mirror_offset)); // required type + __ pushptr(vmarg); // bad array + __ push((int)Bytecodes::_aaload); // who is complaining? + __ jump(ExternalAddress(from_interpreted_entry(_raise_exception))); __ bind(bad_array_length); UNPUSH_RSI_RDI; - __ stop("bad array length NYI"); + __ push(rcx_recv); // AMH requiring a certain length + __ pushptr(vmarg); // bad array + __ push((int)Bytecodes::_arraylength); // who is complaining? + __ jump(ExternalAddress(from_interpreted_entry(_raise_exception))); #undef UNPUSH_RSI_RDI } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/runtime_x86_32.cpp --- a/src/cpu/x86/vm/runtime_x86_32.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/runtime_x86_32.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -43,11 +43,11 @@ // This code is entered with a jmp. // // Arguments: -// rax,: exception oop +// rax: exception oop // rdx: exception pc // // Results: -// rax,: exception oop +// rax: exception oop // rdx: exception pc in caller or ??? // destination: exception handler of caller // @@ -113,17 +113,17 @@ __ addptr(rsp, return_off * wordSize); // Epilog! __ pop(rdx); // Exception pc + // rax: exception handler for given - // rax,: exception handler for given + // Restore SP from BP if the exception PC is a MethodHandle call. + __ cmpl(Address(rcx, JavaThread::is_method_handle_exception_offset()), 0); + __ cmovptr(Assembler::notEqual, rsp, rbp); // We have a handler in rax, (could be deopt blob) // rdx - throwing pc, deopt blob will need it. __ push(rax); - // rcx contains handler address - - __ get_thread(rcx); // TLS // Get the exception __ movptr(rax, Address(rcx, JavaThread::exception_oop_offset())); // Get the exception pc in case we are deoptimized @@ -137,7 +137,7 @@ __ pop(rcx); - // rax,: exception oop + // rax: exception oop // rcx: exception handler // rdx: exception pc __ jmp (rcx); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/sharedRuntime_x86_32.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -907,7 +907,8 @@ int total_args_passed, int comp_args_on_stack, const BasicType *sig_bt, - const VMRegPair *regs) { + const VMRegPair *regs, + AdapterFingerPrint* fingerprint) { address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -954,7 +955,7 @@ gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); __ flush(); - return new AdapterHandlerEntry(i2c_entry, c2i_entry, c2i_unverified_entry); + return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); } int SharedRuntime::c_calling_convention(const BasicType *sig_bt, @@ -2381,7 +2382,7 @@ // Save everything in sight. - map = RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); + map = RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words, false); // Normal deoptimization __ push(Deoptimization::Unpack_deopt); __ jmp(cont); @@ -2392,7 +2393,7 @@ // return address is the pc describes what bci to do re-execute at // No need to update map as each call to save_live_registers will produce identical oopmap - (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); + (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words, false); __ push(Deoptimization::Unpack_reexecute); __ jmp(cont); @@ -2428,7 +2429,7 @@ // Save everything in sight. // No need to update map as each call to save_live_registers will produce identical oopmap - (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words); + (void) RegisterSaver::save_live_registers(masm, additional_words, &frame_size_in_words, false); // Now it is safe to overwrite any register @@ -2515,6 +2516,11 @@ RegisterSaver::restore_result_registers(masm); + // Non standard control word may be leaked out through a safepoint blob, and we can + // deopt at a poll point with the non standard control word. However, we should make + // sure the control word is correct after restore_result_registers. + __ fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); + // All of the register save area has been popped of the stack. Only the // return address remains. diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/sharedRuntime_x86_64.cpp --- a/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -638,6 +638,10 @@ __ movptr(rax, Address(rsp, 0)); + // Must preserve original SP for loading incoming arguments because + // we need to align the outgoing SP for compiled code. + __ movptr(r11, rsp); + // 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; @@ -661,6 +665,10 @@ // as far as the placement of the call instruction __ push(rax); + // Put saved SP in another register + const Register saved_sp = rax; + __ movptr(saved_sp, r11); + // Will jump to the compiled code just as if compiled code was doing it. // Pre-load the register-jump target early, to schedule it better. __ movptr(r11, Address(rbx, in_bytes(methodOopDesc::from_compiled_offset()))); @@ -680,11 +688,7 @@ assert(!regs[i].second()->is_valid() || regs[i].first()->next() == regs[i].second(), "scrambled load targets?"); // Load in argument order going down. - // int ld_off = (total_args_passed + comp_words_on_stack -i)*wordSize; - // base ld_off on r13 (sender_sp) as the stack alignment makes offsets from rsp - // unpredictable - int ld_off = ((total_args_passed - 1) - i)*Interpreter::stackElementSize(); - + int ld_off = (total_args_passed - i)*Interpreter::stackElementSize() + Interpreter::value_offset_in_bytes(); // Point to interpreter value (vs. tag) int next_off = ld_off - Interpreter::stackElementSize(); // @@ -699,10 +703,14 @@ if (r_1->is_stack()) { // Convert stack slot to an SP offset (+ wordSize to account for return address ) int st_off = regs[i].first()->reg2stack()*VMRegImpl::stack_slot_size + wordSize; + + // We can use r13 as a temp here because compiled code doesn't need r13 as an input + // and if we end up going thru a c2i because of a miss a reasonable value of r13 + // will be generated. if (!r_2->is_valid()) { // sign extend??? - __ movl(rax, Address(r13, ld_off)); - __ movptr(Address(rsp, st_off), rax); + __ movl(r13, Address(saved_sp, ld_off)); + __ movptr(Address(rsp, st_off), r13); } else { // // We are using two optoregs. This can be either T_OBJECT, T_ADDRESS, T_LONG, or T_DOUBLE @@ -715,9 +723,9 @@ // ld_off is MSW so get LSW const int offset = (sig_bt[i]==T_LONG||sig_bt[i]==T_DOUBLE)? next_off : ld_off; - __ movq(rax, Address(r13, offset)); + __ movq(r13, Address(saved_sp, offset)); // st_off is LSW (i.e. reg.first()) - __ movq(Address(rsp, st_off), rax); + __ movq(Address(rsp, st_off), r13); } } else if (r_1->is_Register()) { // Register argument Register r = r_1->as_Register(); @@ -732,16 +740,16 @@ next_off : ld_off; // this can be a misaligned move - __ movq(r, Address(r13, offset)); + __ movq(r, Address(saved_sp, offset)); } else { // sign extend and use a full word? - __ movl(r, Address(r13, ld_off)); + __ movl(r, Address(saved_sp, ld_off)); } } else { if (!r_2->is_valid()) { - __ movflt(r_1->as_XMMRegister(), Address(r13, ld_off)); + __ movflt(r_1->as_XMMRegister(), Address(saved_sp, ld_off)); } else { - __ movdbl(r_1->as_XMMRegister(), Address(r13, next_off)); + __ movdbl(r_1->as_XMMRegister(), Address(saved_sp, next_off)); } } } @@ -770,7 +778,8 @@ int total_args_passed, int comp_args_on_stack, const BasicType *sig_bt, - const VMRegPair *regs) { + const VMRegPair *regs, + AdapterFingerPrint* fingerprint) { address i2c_entry = __ pc(); gen_i2c_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs); @@ -816,7 +825,7 @@ gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup); __ flush(); - return new AdapterHandlerEntry(i2c_entry, c2i_entry, c2i_unverified_entry); + return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry); } int SharedRuntime::c_calling_convention(const BasicType *sig_bt, @@ -3319,6 +3328,10 @@ // rax: exception handler + // Restore SP from BP if the exception PC is a MethodHandle call. + __ cmpl(Address(r15_thread, JavaThread::is_method_handle_exception_offset()), 0); + __ cmovptr(Assembler::notEqual, rsp, rbp); + // We have a handler in rax (could be deopt blob). __ mov(r8, rax); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/stubGenerator_x86_32.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_32.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/stubGenerator_x86_32.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -718,10 +718,8 @@ case BarrierSet::G1SATBCTLogging: { __ pusha(); // push registers - __ push(count); - __ push(start); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre))); - __ addptr(rsp, 2*wordSize); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre), + start, count); __ popa(); } break; @@ -752,10 +750,8 @@ case BarrierSet::G1SATBCTLogging: { __ pusha(); // push registers - __ push(count); - __ push(start); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post))); - __ addptr(rsp, 2*wordSize); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post), + start, count); __ popa(); } break; @@ -2030,6 +2026,54 @@ entry_checkcast_arraycopy); } + void generate_math_stubs() { + { + StubCodeMark mark(this, "StubRoutines", "log"); + StubRoutines::_intrinsic_log = (double (*)(double)) __ pc(); + + __ fld_d(Address(rsp, 4)); + __ flog(); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "log10"); + StubRoutines::_intrinsic_log10 = (double (*)(double)) __ pc(); + + __ fld_d(Address(rsp, 4)); + __ flog10(); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "sin"); + StubRoutines::_intrinsic_sin = (double (*)(double)) __ pc(); + + __ fld_d(Address(rsp, 4)); + __ trigfunc('s'); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "cos"); + StubRoutines::_intrinsic_cos = (double (*)(double)) __ pc(); + + __ fld_d(Address(rsp, 4)); + __ trigfunc('c'); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "tan"); + StubRoutines::_intrinsic_tan = (double (*)(double)) __ pc(); + + __ fld_d(Address(rsp, 4)); + __ trigfunc('t'); + __ ret(0); + } + + // The intrinsic version of these seem to return the same value as + // the strict version. + StubRoutines::_intrinsic_exp = SharedRuntime::dexp; + StubRoutines::_intrinsic_pow = SharedRuntime::dpow; + } + public: // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since @@ -2228,6 +2272,8 @@ MethodHandles::generate_method_handle_stub(_masm, ek); } } + + generate_math_stubs(); } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/stubGenerator_x86_64.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_64.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/stubGenerator_x86_64.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -1172,7 +1172,7 @@ __ movptr(c_rarg0, addr); __ movptr(c_rarg1, count); } - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre))); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_pre), 2); __ popa(); } break; @@ -1212,7 +1212,7 @@ __ shrptr(scratch, LogBytesPerHeapOop); // convert to element count __ mov(c_rarg0, start); __ mov(c_rarg1, scratch); - __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post))); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSet::static_write_ref_array_post), 2); __ popa(); } break; @@ -2731,6 +2731,79 @@ StubRoutines::_arrayof_oop_arraycopy = StubRoutines::_oop_arraycopy; } + void generate_math_stubs() { + { + StubCodeMark mark(this, "StubRoutines", "log"); + StubRoutines::_intrinsic_log = (double (*)(double)) __ pc(); + + __ subq(rsp, 8); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ flog(); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ addq(rsp, 8); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "log10"); + StubRoutines::_intrinsic_log10 = (double (*)(double)) __ pc(); + + __ subq(rsp, 8); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ flog10(); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ addq(rsp, 8); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "sin"); + StubRoutines::_intrinsic_sin = (double (*)(double)) __ pc(); + + __ subq(rsp, 8); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ trigfunc('s'); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ addq(rsp, 8); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "cos"); + StubRoutines::_intrinsic_cos = (double (*)(double)) __ pc(); + + __ subq(rsp, 8); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ trigfunc('c'); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ addq(rsp, 8); + __ ret(0); + } + { + StubCodeMark mark(this, "StubRoutines", "tan"); + StubRoutines::_intrinsic_tan = (double (*)(double)) __ pc(); + + __ subq(rsp, 8); + __ movdbl(Address(rsp, 0), xmm0); + __ fld_d(Address(rsp, 0)); + __ trigfunc('t'); + __ fstp_d(Address(rsp, 0)); + __ movdbl(xmm0, Address(rsp, 0)); + __ addq(rsp, 8); + __ ret(0); + } + + // The intrinsic version of these seem to return the same value as + // the strict version. + StubRoutines::_intrinsic_exp = SharedRuntime::dexp; + StubRoutines::_intrinsic_pow = SharedRuntime::dpow; + } + #undef __ #define __ masm-> @@ -2935,6 +3008,18 @@ // arraycopy stubs used by compilers generate_arraycopy_stubs(); + + // generic method handle stubs + if (EnableMethodHandles && SystemDictionary::MethodHandle_klass() != NULL) { + for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST; + ek < MethodHandles::_EK_LIMIT; + ek = MethodHandles::EntryKind(1 + (int)ek)) { + StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek)); + MethodHandles::generate_method_handle_stub(_masm, ek); + } + } + + generate_math_stubs(); } public: diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/templateInterpreter_x86_32.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -92,8 +92,7 @@ return entry; } -// Arguments are: required type at TOS+8, failing object (or NULL) at TOS+4. -// pc at TOS (just for debugging) +// Arguments are: required type at TOS+4, failing object (or NULL) at TOS. address TemplateInterpreterGenerator::generate_WrongMethodType_handler() { address entry = __ pc(); @@ -156,15 +155,8 @@ } -address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, bool unbox) { +address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step) { TosState incoming_state = state; - if (EnableInvokeDynamic) { - if (unbox) { - incoming_state = atos; - } - } else { - assert(!unbox, "old behavior"); - } Label interpreter_entry; address compiled_entry = __ pc(); @@ -217,46 +209,6 @@ __ restore_bcp(); __ restore_locals(); - Label L_fail; - - if (unbox && state != atos) { - // cast and unbox - BasicType type = as_BasicType(state); - if (type == T_BYTE) type = T_BOOLEAN; // FIXME - KlassHandle boxk = SystemDictionaryHandles::box_klass(type); - __ mov32(rbx, ExternalAddress((address) boxk.raw_value())); - __ testl(rax, rax); - Label L_got_value, L_get_value; - // convert nulls to zeroes (avoid NPEs here) - if (!(type == T_FLOAT || type == T_DOUBLE)) { - // if rax already contains zero bits, forge ahead - __ jcc(Assembler::zero, L_got_value); - } else { - __ jcc(Assembler::notZero, L_get_value); - __ fldz(); - __ jmp(L_got_value); - } - __ bind(L_get_value); - __ cmp32(rbx, Address(rax, oopDesc::klass_offset_in_bytes())); - __ jcc(Assembler::notEqual, L_fail); - int offset = java_lang_boxing_object::value_offset_in_bytes(type); - // Cf. TemplateTable::getfield_or_static - switch (type) { - case T_BYTE: // fall through: - case T_BOOLEAN: __ load_signed_byte(rax, Address(rax, offset)); break; - case T_CHAR: __ load_unsigned_short(rax, Address(rax, offset)); break; - case T_SHORT: __ load_signed_short(rax, Address(rax, offset)); break; - case T_INT: __ movl(rax, Address(rax, offset)); break; - case T_FLOAT: __ fld_s(Address(rax, offset)); break; - case T_DOUBLE: __ fld_d(Address(rax, offset)); break; - // Access to java.lang.Double.value does not need to be atomic: - case T_LONG: { __ movl(rdx, Address(rax, offset + 4)); - __ movl(rax, Address(rax, offset + 0)); } break; - default: ShouldNotReachHere(); - } - __ bind(L_got_value); - } - Label L_got_cache, L_giant_index; if (EnableInvokeDynamic) { __ cmpb(Address(rsi, 0), Bytecodes::_invokedynamic); @@ -264,32 +216,6 @@ } __ get_cache_and_index_at_bcp(rbx, rcx, 1, false); __ bind(L_got_cache); - if (unbox && state == atos) { - // insert a casting conversion, to keep verifier sane - Label L_ok, L_ok_pops; - __ testl(rax, rax); - __ jcc(Assembler::zero, L_ok); - __ push(rax); // save the object to check - __ push(rbx); // save CP cache reference - __ movl(rdx, Address(rax, oopDesc::klass_offset_in_bytes())); - __ movl(rbx, Address(rbx, rcx, - Address::times_4, constantPoolCacheOopDesc::base_offset() + - ConstantPoolCacheEntry::f1_offset())); - __ movl(rbx, Address(rbx, __ delayed_value(sun_dyn_CallSiteImpl::type_offset_in_bytes, rcx))); - __ movl(rbx, Address(rbx, __ delayed_value(java_dyn_MethodType::rtype_offset_in_bytes, rcx))); - __ movl(rax, Address(rbx, __ delayed_value(java_lang_Class::klass_offset_in_bytes, rcx))); - __ check_klass_subtype(rdx, rax, rbx, L_ok_pops); - __ pop(rcx); // pop and discard CP cache - __ mov(rbx, rax); // target supertype into rbx for L_fail - __ pop(rax); // failed object into rax for L_fail - __ jmp(L_fail); - - __ bind(L_ok_pops); - // restore pushed temp regs: - __ pop(rbx); - __ pop(rax); - __ bind(L_ok); - } __ movl(rbx, Address(rbx, rcx, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::flags_offset())); @@ -302,14 +228,6 @@ __ bind(L_giant_index); __ get_cache_and_index_at_bcp(rbx, rcx, 1, true); __ jmp(L_got_cache); - - if (unbox) { - __ bind(L_fail); - __ push(rbx); // missed klass (required) - __ push(rax); // bad object (actual) - __ movptr(rdx, ExternalAddress((address) &Interpreter::_throw_WrongMethodType_entry)); - __ call(rdx); - } } return entry; @@ -1513,6 +1431,23 @@ } +// These should never be compiled since the interpreter will prefer +// the compiled version to the intrinsic version. +bool AbstractInterpreter::can_be_compiled(methodHandle m) { + switch (method_kind(m)) { + 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 : + return false; + default: + return true; + } +} + // How much stack a method activation needs in words. int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { @@ -1570,7 +1505,10 @@ if (interpreter_frame != NULL) { #ifdef ASSERT - assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), "Frame not properly walkable"); + if (!EnableMethodHandles) + // @@@ 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 diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/templateInterpreter_x86_64.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -100,21 +100,26 @@ return entry; } -// Arguments are: required type in rarg1, failing object (or NULL) in rarg2 +// Arguments are: required type at TOS+8, failing object (or NULL) at TOS+4. address TemplateInterpreterGenerator::generate_WrongMethodType_handler() { address entry = __ pc(); __ pop(c_rarg2); // failing object is at TOS __ pop(c_rarg1); // required type is at TOS+8 - // expression stack must be empty before entering the VM if an - // exception happened + __ verify_oop(c_rarg1); + __ verify_oop(c_rarg2); + + // Various method handle types use interpreter registers as temps. + __ restore_bcp(); + __ restore_locals(); + + // Expression stack must be empty before entering the VM for an exception. __ empty_expression_stack(); __ call_VM(noreg, CAST_FROM_FN_PTR(address, - InterpreterRuntime:: - throw_WrongMethodTypeException), + InterpreterRuntime::throw_WrongMethodTypeException), // pass required type, failing object (or NULL) c_rarg1, c_rarg2); return entry; @@ -166,8 +171,7 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, - int step, bool unbox) { - assert(!unbox, "NYI");//6815692// + int step) { // amd64 doesn't need to do anything special about compiled returns // to the interpreter so the code that exists on x86 to place a sentinel @@ -183,15 +187,29 @@ __ restore_bcp(); __ restore_locals(); - __ get_cache_and_index_at_bcp(rbx, rcx, 1); + Label L_got_cache, L_giant_index; + if (EnableInvokeDynamic) { + __ cmpb(Address(r13, 0), Bytecodes::_invokedynamic); + __ jcc(Assembler::equal, L_giant_index); + } + __ get_cache_and_index_at_bcp(rbx, rcx, 1, false); + __ bind(L_got_cache); __ movl(rbx, Address(rbx, rcx, - Address::times_8, + Address::times_ptr, in_bytes(constantPoolCacheOopDesc::base_offset()) + 3 * wordSize)); __ andl(rbx, 0xFF); if (TaggedStackInterpreter) __ shll(rbx, 1); // 2 slots per parameter. __ lea(rsp, Address(rsp, rbx, Address::times_8)); __ dispatch_next(state, step); + + // out of the main line of code... + if (EnableInvokeDynamic) { + __ bind(L_giant_index); + __ get_cache_and_index_at_bcp(rbx, rcx, 1, true); + __ jmp(L_got_cache); + } + return entry; } @@ -431,8 +449,12 @@ __ addptr(rax, stack_base); __ subptr(rax, stack_size); + // Use the maximum number of pages we might bang. + const int max_pages = StackShadowPages > (StackRedPages+StackYellowPages) ? StackShadowPages : + (StackRedPages+StackYellowPages); + // add in the red and yellow zone sizes - __ addptr(rax, (StackRedPages + StackYellowPages) * page_size); + __ addptr(rax, max_pages * page_size); // check against the current stack bottom __ cmpptr(rsp, rax); @@ -1434,6 +1456,23 @@ 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) { + switch (method_kind(m)) { + 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 : + return false; + default: + return true; + } +} + // How much stack a method activation needs in words. int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { const int entry_size = frame::interpreter_frame_monitor_size(); @@ -1484,8 +1523,10 @@ tempcount* Interpreter::stackElementWords() + popframe_extra_args; if (interpreter_frame != NULL) { #ifdef ASSERT - assert(caller->unextended_sp() == interpreter_frame->interpreter_frame_sender_sp(), - "Frame not properly walkable"); + if (!EnableMethodHandles) + // @@@ 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 diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/templateTable_x86_32.cpp --- a/src/cpu/x86/vm/templateTable_x86_32.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/templateTable_x86_32.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -2890,9 +2890,6 @@ void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) { - bool is_invdyn_bootstrap = (byte_no < 0); - if (is_invdyn_bootstrap) byte_no = -byte_no; - // determine flags Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; @@ -2907,8 +2904,6 @@ const Register flags = rdx; assert_different_registers(method, index, recv, flags); - assert(!is_invdyn_bootstrap || is_invokedynamic, "byte_no<0 hack only for invdyn"); - // save 'interpreter return address' __ save_bcp(); @@ -2944,9 +2939,7 @@ // load return address { address table_addr; - if (is_invdyn_bootstrap) - table_addr = (address)Interpreter::return_5_unbox_addrs_by_index_table(); - else if (is_invokeinterface || is_invokedynamic) + if (is_invokeinterface || is_invokedynamic) table_addr = (address)Interpreter::return_5_addrs_by_index_table(); else table_addr = (address)Interpreter::return_3_addrs_by_index_table(); @@ -3153,54 +3146,10 @@ __ profile_call(rsi); } - Label handle_unlinked_site; - __ movptr(rcx, Address(rax, __ delayed_value(sun_dyn_CallSiteImpl::target_offset_in_bytes, rcx))); - __ testptr(rcx, rcx); - __ jcc(Assembler::zero, handle_unlinked_site); - + __ movptr(rcx, Address(rax, __ delayed_value(java_dyn_CallSite::target_offset_in_bytes, rcx))); + __ null_check(rcx); __ prepare_to_jump_from_interpreted(); __ jump_to_method_handle_entry(rcx, rdx); - - // Initial calls come here... - __ bind(handle_unlinked_site); - __ pop(rcx); // remove return address pushed by prepare_invoke - - // box stacked arguments into an array for the bootstrap method - address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::bootstrap_invokedynamic); - __ restore_bcp(); // rsi must be correct for call_VM - __ call_VM(rax, entry, rax); - __ movl(rdi, rax); // protect bootstrap MH from prepare_invoke - - // recompute return address - __ restore_bcp(); // rsi must be correct for prepare_invoke - prepare_invoke(rax, rbx, -byte_no); // smashes rcx, rdx - // rax: CallSite object (f1) - // rbx: unused (f2) - // rdi: bootstrap MH - // rdx: flags - - // now load up the arglist, which has been neatly boxed - __ get_thread(rcx); - __ movptr(rdx, Address(rcx, JavaThread::vm_result_2_offset())); - __ movptr(Address(rcx, JavaThread::vm_result_2_offset()), NULL_WORD); - __ verify_oop(rdx); - // rdx = arglist - - // save SP now, before we add the bootstrap call to the stack - // We must preserve a fiction that the original arguments are outgoing, - // because the return sequence will reset the stack to this point - // and then pop all those arguments. It seems error-prone to use - // a different argument list size just for bootstrapping. - __ prepare_to_jump_from_interpreted(); - - // Now let's play adapter, pushing the real arguments on the stack. - __ pop(rbx); // return PC - __ push(rdi); // boot MH - __ push(rax); // call site - __ push(rdx); // arglist - __ push(rbx); // return PC, again - __ mov(rcx, rdi); - __ jump_to_method_handle_entry(rcx, rdx); } //---------------------------------------------------------------------------------------------------- diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/templateTable_x86_64.cpp --- a/src/cpu/x86/vm/templateTable_x86_64.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/templateTable_x86_64.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -139,7 +139,7 @@ } __ g1_write_barrier_pre(rdx, r8, rbx, val != noreg); if (val == noreg) { - __ store_heap_oop(Address(rdx, 0), NULL_WORD); + __ store_heap_oop_null(Address(rdx, 0)); } else { __ store_heap_oop(Address(rdx, 0), val); __ g1_write_barrier_post(rdx, val, r8, rbx); @@ -152,7 +152,7 @@ case BarrierSet::CardTableExtension: { if (val == noreg) { - __ store_heap_oop(obj, NULL_WORD); + __ store_heap_oop_null(obj); } else { __ store_heap_oop(obj, val); // flatten object address if needed @@ -168,7 +168,7 @@ case BarrierSet::ModRef: case BarrierSet::Other: if (val == noreg) { - __ store_heap_oop(obj, NULL_WORD); + __ store_heap_oop_null(obj); } else { __ store_heap_oop(obj, val); } @@ -203,18 +203,15 @@ __ jcc(Assembler::notEqual, fast_patch); __ get_method(scratch); // Let breakpoint table handling rewrite to quicker bytecode - __ call_VM(noreg, - CAST_FROM_FN_PTR(address, - InterpreterRuntime::set_original_bytecode_at), - scratch, r13, bc); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), scratch, r13, bc); #ifndef ASSERT __ jmpb(patch_done); +#else + __ jmp(patch_done); +#endif __ bind(fast_patch); } -#else - __ jmp(patch_done); - __ bind(fast_patch); - } +#ifdef ASSERT Label okay; __ load_unsigned_byte(scratch, at_bcp(0)); __ cmpl(scratch, (int) Bytecodes::java_code(bytecode)); @@ -2054,26 +2051,28 @@ } } -void TemplateTable::resolve_cache_and_index(int byte_no, - Register Rcache, - Register index) { +void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register index) { assert(byte_no == 1 || byte_no == 2, "byte_no out of range"); + bool is_invokedynamic = (bytecode() == Bytecodes::_invokedynamic); const Register temp = rbx; assert_different_registers(Rcache, index, temp); const int shift_count = (1 + byte_no) * BitsPerByte; Label resolved; - __ get_cache_and_index_at_bcp(Rcache, index, 1); - __ movl(temp, Address(Rcache, - index, Address::times_8, - constantPoolCacheOopDesc::base_offset() + - ConstantPoolCacheEntry::indices_offset())); - __ shrl(temp, shift_count); - // have we resolved this bytecode? - __ andl(temp, 0xFF); - __ cmpl(temp, (int) bytecode()); - __ jcc(Assembler::equal, resolved); + __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic); + if (is_invokedynamic) { + // we are resolved if the f1 field contains a non-null CallSite object + __ cmpptr(Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()), (int32_t) NULL_WORD); + __ jcc(Assembler::notEqual, resolved); + } else { + __ movl(temp, Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset())); + __ shrl(temp, shift_count); + // have we resolved this bytecode? + __ andl(temp, 0xFF); + __ cmpl(temp, (int) bytecode()); + __ jcc(Assembler::equal, resolved); + } // resolve first time through address entry; @@ -2090,6 +2089,9 @@ case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break; + case Bytecodes::_invokedynamic: + entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); + break; default: ShouldNotReachHere(); break; @@ -2098,7 +2100,7 @@ __ call_VM(noreg, entry, temp); // Update registers with resolved info - __ get_cache_and_index_at_bcp(Rcache, index, 1); + __ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic); __ bind(resolved); } @@ -2832,15 +2834,14 @@ ShouldNotReachHere(); } -void TemplateTable::prepare_invoke(Register method, - Register index, - int byte_no, - Bytecodes::Code code) { +void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) { // determine flags + Bytecodes::Code code = bytecode(); const bool is_invokeinterface = code == Bytecodes::_invokeinterface; + const bool is_invokedynamic = code == Bytecodes::_invokedynamic; const bool is_invokevirtual = code == Bytecodes::_invokevirtual; const bool is_invokespecial = code == Bytecodes::_invokespecial; - const bool load_receiver = code != Bytecodes::_invokestatic; + const bool load_receiver = (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic); const bool receiver_null_check = is_invokespecial; const bool save_flags = is_invokeinterface || is_invokevirtual; // setup registers & access constant pool cache @@ -2858,9 +2859,13 @@ __ movl(recv, flags); __ andl(recv, 0xFF); if (TaggedStackInterpreter) __ shll(recv, 1); // index*2 - __ movptr(recv, Address(rsp, recv, Address::times_8, - -Interpreter::expr_offset_in_bytes(1))); - __ verify_oop(recv); + Address recv_addr(rsp, recv, Address::times_8, -Interpreter::expr_offset_in_bytes(1)); + if (is_invokedynamic) { + __ lea(recv, recv_addr); + } else { + __ movptr(recv, recv_addr); + __ verify_oop(recv); + } } // do null check if needed @@ -2878,10 +2883,14 @@ ConstantPoolCacheEntry::verify_tosBits(); // load return address { - ExternalAddress return_5((address)Interpreter::return_5_addrs_by_index_table()); - ExternalAddress return_3((address)Interpreter::return_3_addrs_by_index_table()); - __ lea(rscratch1, (is_invokeinterface ? return_5 : return_3)); - __ movptr(flags, Address(rscratch1, flags, Address::times_8)); + address table_addr; + if (is_invokeinterface || is_invokedynamic) + table_addr = (address)Interpreter::return_5_addrs_by_index_table(); + else + table_addr = (address)Interpreter::return_3_addrs_by_index_table(); + ExternalAddress table(table_addr); + __ lea(rscratch1, table); + __ movptr(flags, Address(rscratch1, flags, Address::times_ptr)); } // push return address @@ -2947,7 +2956,7 @@ void TemplateTable::invokevirtual(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // rbx: index // rcx: receiver @@ -2959,7 +2968,7 @@ void TemplateTable::invokespecial(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // do the call __ verify_oop(rbx); __ profile_call(rax); @@ -2969,7 +2978,7 @@ void TemplateTable::invokestatic(int byte_no) { transition(vtos, vtos); - prepare_invoke(rbx, noreg, byte_no, bytecode()); + prepare_invoke(rbx, noreg, byte_no); // do the call __ verify_oop(rbx); __ profile_call(rax); @@ -2983,7 +2992,7 @@ void TemplateTable::invokeinterface(int byte_no) { transition(vtos, vtos); - prepare_invoke(rax, rbx, byte_no, bytecode()); + prepare_invoke(rax, rbx, byte_no); // rax: Interface // rbx: index @@ -3072,7 +3081,24 @@ return; } - __ stop("invokedynamic NYI");//6815692// + prepare_invoke(rax, rbx, byte_no); + + // rax: CallSite object (f1) + // rbx: unused (f2) + // rcx: receiver address + // rdx: flags (unused) + + if (ProfileInterpreter) { + Label L; + // %%% should make a type profile for any invokedynamic that takes a ref argument + // profile this call + __ profile_call(r13); + } + + __ movptr(rcx, Address(rax, __ delayed_value(java_dyn_CallSite::target_offset_in_bytes, rcx))); + __ null_check(rcx); + __ prepare_to_jump_from_interpreted(); + __ jump_to_method_handle_entry(rcx, rdx); } @@ -3212,17 +3238,19 @@ __ xorl(rcx, rcx); // use zero reg to clear memory (shorter code) __ store_klass_gap(rax, rcx); // zero klass gap for compressed oops __ store_klass(rax, rsi); // store klass last + + { + SkipIfEqual skip(_masm, &DTraceAllocProbes, false); + // Trigger dtrace event for fastpath + __ push(atos); // save the return value + __ call_VM_leaf( + CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), rax); + __ pop(atos); // restore the return value + + } __ jmp(done); } - { - SkipIfEqual skip(_masm, &DTraceAllocProbes, false); - // Trigger dtrace event for fastpath - __ push(atos); // save the return value - __ call_VM_leaf( - CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), rax); - __ pop(atos); // restore the return value - } // slow case __ bind(slow_case); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/templateTable_x86_64.hpp --- a/src/cpu/x86/vm/templateTable_x86_64.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/templateTable_x86_64.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,8 +22,7 @@ * */ - static void prepare_invoke(Register method, Register index, int byte_no, - Bytecodes::Code code); + static void prepare_invoke(Register method, Register index, int byte_no); static void invokevirtual_helper(Register index, Register recv, Register flags); static void volatile_barrier(Assembler::Membar_mask_bits order_constraint); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/vm_version_x86.cpp --- a/src/cpu/x86/vm/vm_version_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/vm_version_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -255,6 +255,8 @@ if (!VM_Version::supports_sse2()) { vm_exit_during_initialization("Unknown x64 processor: SSE2 not supported"); } + // in 64 bit the use of SSE2 is the minimum + if (UseSSE < 2) UseSSE = 2; #endif // If the OS doesn't support SSE, we can't use this feature even if the HW does diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/x86_32.ad Thu Mar 25 16:54:59 2010 -0700 @@ -235,6 +235,11 @@ //----------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 %{ +// Must be visible to the DFA in dfa_x86_32.cpp +extern bool is_operand_hi32_zero(Node* n); +%} + source %{ #define RELOC_IMM32 Assembler::imm_operand #define RELOC_DISP32 Assembler::disp32_operand @@ -268,22 +273,36 @@ static jlong *float_signflip_pool = double_quadword(&fp_signmask_pool[3*2], CONST64(0x8000000080000000), CONST64(0x8000000080000000)); static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], CONST64(0x8000000000000000), CONST64(0x8000000000000000)); +// Offset hacking within calls. +static int pre_call_FPU_size() { + if (Compile::current()->in_24_bit_fp_mode()) + return 6; // fldcw + return 0; +} + +static int preserve_SP_size() { + return LP64_ONLY(1 +) 2; // [rex,] op, rm(reg/reg) +} + // !!!!! 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. int MachCallStaticJavaNode::ret_addr_offset() { - return 5 + (Compile::current()->in_24_bit_fp_mode() ? 6 : 0); // 5 bytes from start of call to where return address points + int offset = 5 + pre_call_FPU_size(); // 5 bytes from start of call to where return address points + if (_method_handle_invoke) + offset += preserve_SP_size(); + return offset; } int MachCallDynamicJavaNode::ret_addr_offset() { - return 10 + (Compile::current()->in_24_bit_fp_mode() ? 6 : 0); // 10 bytes from start of call to where return address points + return 10 + pre_call_FPU_size(); // 10 bytes from start of call to where return address points } static int sizeof_FFree_Float_Stack_All = -1; int MachCallRuntimeNode::ret_addr_offset() { assert(sizeof_FFree_Float_Stack_All != -1, "must have been emitted already"); - return sizeof_FFree_Float_Stack_All + 5 + (Compile::current()->in_24_bit_fp_mode() ? 6 : 0); + return sizeof_FFree_Float_Stack_All + 5 + pre_call_FPU_size(); } // Indicate if the safepoint node needs the polling page as an input. @@ -299,8 +318,16 @@ // The address of the call instruction needs to be 4-byte aligned to // ensure that it does not span a cache line so that it can be patched. int CallStaticJavaDirectNode::compute_padding(int current_offset) const { - if (Compile::current()->in_24_bit_fp_mode()) - current_offset += 6; // skip fldcw in pre_call_FPU, if any + current_offset += pre_call_FPU_size(); // skip fldcw, if any + current_offset += 1; // skip call opcode byte + return round_to(current_offset, alignment_required()) - current_offset; +} + +// The address of the call instruction needs to be 4-byte aligned to +// ensure that it does not span a cache line so that it can be patched. +int CallStaticJavaHandleNode::compute_padding(int current_offset) const { + current_offset += pre_call_FPU_size(); // skip fldcw, if any + current_offset += preserve_SP_size(); // skip mov rbp, rsp current_offset += 1; // skip call opcode byte return round_to(current_offset, alignment_required()) - current_offset; } @@ -308,8 +335,7 @@ // The address of the call instruction needs to be 4-byte aligned to // ensure that it does not span a cache line so that it can be patched. int CallDynamicJavaDirectNode::compute_padding(int current_offset) const { - if (Compile::current()->in_24_bit_fp_mode()) - current_offset += 6; // skip fldcw in pre_call_FPU, if any + current_offset += pre_call_FPU_size(); // skip fldcw, if any current_offset += 5; // skip MOV instruction current_offset += 1; // skip call opcode byte return round_to(current_offset, alignment_required()) - current_offset; @@ -379,7 +405,7 @@ int format) { #ifdef ASSERT if (rspec.reloc()->type() == relocInfo::oop_type && d32 != 0 && d32 != (int)Universe::non_oop_word()) { - assert(oop(d32)->is_oop() && oop(d32)->is_perm(), "cannot embed non-perm oops in code"); + assert(oop(d32)->is_oop() && (ScavengeRootsInCode || !oop(d32)->is_scavengable()), "cannot embed scavengable oops in code"); } #endif cbuf.relocate(cbuf.inst_mark(), rspec, format); @@ -1418,8 +1444,10 @@ // to implement the UseStrictFP mode. const bool Matcher::strict_fp_requires_explicit_rounding = true; -// Do floats take an entire double register or just half? -const bool Matcher::float_in_double = true; +// Are floats conerted to double when stored to stack during deoptimization? +// On x32 it is stored with convertion only when FPU is used for floats. +bool Matcher::float_in_double() { return (UseSSE == 0); } + // Do ints take an entire long register or just half? const bool Matcher::int_in_long = false; @@ -1460,6 +1488,25 @@ return RegMask(); } +const RegMask Matcher::method_handle_invoke_SP_save_mask() { + return EBP_REG_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(); + if (opc == Op_LoadUI2L) { + return true; + } + if (opc == Op_AndL) { + Node* o2 = n->in(2); + if (o2->is_Con() && (o2->get_long() & 0xFFFFFFFF00000000LL) == 0LL) { + return true; + } + } + return false; +} + %} //----------ENCODING BLOCK----------------------------------------------------- @@ -1772,10 +1819,13 @@ enc_class pre_call_FPU %{ // If method sets FPU control word restore it here + debug_only(int off0 = cbuf.code_size()); if( Compile::current()->in_24_bit_fp_mode() ) { MacroAssembler masm(&cbuf); masm.fldcw(ExternalAddress(StubRoutines::addr_fpu_cntrl_wrd_std())); } + debug_only(int off1 = cbuf.code_size()); + assert(off1 - off0 == pre_call_FPU_size(), "correct size prediction"); %} enc_class post_call_FPU %{ @@ -1786,6 +1836,21 @@ } %} + enc_class preserve_SP %{ + debug_only(int off0 = cbuf.code_size()); + MacroAssembler _masm(&cbuf); + // RBP is preserved across all calls, even compiled calls. + // Use it to preserve RSP in places where the callee might change the SP. + __ movptr(rbp, rsp); + debug_only(int off1 = cbuf.code_size()); + assert(off1 - off0 == preserve_SP_size(), "correct size prediction"); + %} + + enc_class restore_SP %{ + MacroAssembler _masm(&cbuf); + __ movptr(rsp, rbp); + %} + enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL // CALL to fixup routine. Fixup routine uses ScopeDesc info to determine // who we intended to call. @@ -3701,458 +3766,6 @@ } %} - enc_class enc_String_Compare(eDIRegP str1, eSIRegP str2, regXD tmp1, regXD tmp2, - eAXRegI tmp3, eBXRegI tmp4, eCXRegI result) %{ - Label ECX_GOOD_LABEL, LENGTH_DIFF_LABEL, - POP_LABEL, DONE_LABEL, CONT_LABEL, - WHILE_HEAD_LABEL; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - XMMRegister tmp2Reg = as_XMMRegister($tmp2$$reg); - - // Get the first character position in both strings - // [8] char array, [12] offset, [16] count - int value_offset = java_lang_String::value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String::count_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - masm.movptr(rax, Address(rsi, value_offset)); - masm.movl(rcx, Address(rsi, offset_offset)); - masm.lea(rax, Address(rax, rcx, Address::times_2, base_offset)); - masm.movptr(rbx, Address(rdi, value_offset)); - masm.movl(rcx, Address(rdi, offset_offset)); - masm.lea(rbx, Address(rbx, rcx, Address::times_2, base_offset)); - - // Compute the minimum of the string lengths(rsi) and the - // difference of the string lengths (stack) - - if (VM_Version::supports_cmov()) { - masm.movl(rdi, Address(rdi, count_offset)); - masm.movl(rsi, Address(rsi, count_offset)); - masm.movl(rcx, rdi); - masm.subl(rdi, rsi); - masm.push(rdi); - masm.cmovl(Assembler::lessEqual, rsi, rcx); - } else { - masm.movl(rdi, Address(rdi, count_offset)); - masm.movl(rcx, Address(rsi, count_offset)); - masm.movl(rsi, rdi); - masm.subl(rdi, rcx); - masm.push(rdi); - masm.jccb(Assembler::lessEqual, ECX_GOOD_LABEL); - masm.movl(rsi, rcx); - // rsi holds min, rcx is unused - } - - // Is the minimum length zero? - masm.bind(ECX_GOOD_LABEL); - masm.testl(rsi, rsi); - masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); - - // Load first characters - masm.load_unsigned_short(rcx, Address(rbx, 0)); - masm.load_unsigned_short(rdi, Address(rax, 0)); - - // Compare first characters - masm.subl(rcx, rdi); - masm.jcc(Assembler::notZero, POP_LABEL); - masm.decrementl(rsi); - masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); - - { - // Check after comparing first character to see if strings are equivalent - Label LSkip2; - // Check if the strings start at same location - masm.cmpptr(rbx,rax); - masm.jccb(Assembler::notEqual, LSkip2); - - // Check if the length difference is zero (from stack) - masm.cmpl(Address(rsp, 0), 0x0); - masm.jcc(Assembler::equal, LENGTH_DIFF_LABEL); - - // Strings might not be equivalent - masm.bind(LSkip2); - } - - // Advance to next character - masm.addptr(rax, 2); - masm.addptr(rbx, 2); - - if (UseSSE42Intrinsics) { - // With SSE4.2, use double quad vector compare - Label COMPARE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_TAIL; - // Setup to compare 16-byte vectors - masm.movl(rdi, rsi); - masm.andl(rsi, 0xfffffff8); // rsi holds the vector count - masm.andl(rdi, 0x00000007); // rdi holds the tail count - masm.testl(rsi, rsi); - masm.jccb(Assembler::zero, COMPARE_TAIL); - - masm.lea(rax, Address(rax, rsi, Address::times_2)); - masm.lea(rbx, Address(rbx, rsi, Address::times_2)); - masm.negl(rsi); - - masm.bind(COMPARE_VECTORS); - masm.movdqu(tmp1Reg, Address(rax, rsi, Address::times_2)); - masm.movdqu(tmp2Reg, Address(rbx, rsi, Address::times_2)); - masm.pxor(tmp1Reg, tmp2Reg); - masm.ptest(tmp1Reg, tmp1Reg); - masm.jccb(Assembler::notZero, VECTOR_NOT_EQUAL); - masm.addl(rsi, 8); - masm.jcc(Assembler::notZero, COMPARE_VECTORS); - masm.jmpb(COMPARE_TAIL); - - // Mismatched characters in the vectors - masm.bind(VECTOR_NOT_EQUAL); - masm.lea(rax, Address(rax, rsi, Address::times_2)); - masm.lea(rbx, Address(rbx, rsi, Address::times_2)); - masm.movl(rdi, 8); - - // Compare tail (< 8 chars), or rescan last vectors to - // find 1st mismatched characters - masm.bind(COMPARE_TAIL); - masm.testl(rdi, rdi); - masm.jccb(Assembler::zero, LENGTH_DIFF_LABEL); - masm.movl(rsi, rdi); - // Fallthru to tail compare - } - - //Shift rax, and rbx, to the end of the arrays, negate min - masm.lea(rax, Address(rax, rsi, Address::times_2, 0)); - masm.lea(rbx, Address(rbx, rsi, Address::times_2, 0)); - masm.negl(rsi); - - // Compare the rest of the characters - masm.bind(WHILE_HEAD_LABEL); - masm.load_unsigned_short(rcx, Address(rbx, rsi, Address::times_2, 0)); - masm.load_unsigned_short(rdi, Address(rax, rsi, Address::times_2, 0)); - masm.subl(rcx, rdi); - masm.jccb(Assembler::notZero, POP_LABEL); - masm.incrementl(rsi); - masm.jcc(Assembler::notZero, WHILE_HEAD_LABEL); - - // Strings are equal up to min length. Return the length difference. - masm.bind(LENGTH_DIFF_LABEL); - masm.pop(rcx); - masm.jmpb(DONE_LABEL); - - // Discard the stored length difference - masm.bind(POP_LABEL); - masm.addptr(rsp, 4); - - // That's it - masm.bind(DONE_LABEL); - %} - - enc_class enc_String_Equals(eDIRegP str1, eSIRegP str2, regXD tmp1, regXD tmp2, - eBXRegI tmp3, eCXRegI tmp4, eAXRegI result) %{ - Label RET_TRUE, RET_FALSE, DONE, COMPARE_VECTORS, COMPARE_CHAR; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - XMMRegister tmp2Reg = as_XMMRegister($tmp2$$reg); - - int value_offset = java_lang_String::value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String::count_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // does source == target string? - masm.cmpptr(rdi, rsi); - masm.jcc(Assembler::equal, RET_TRUE); - - // get and compare counts - masm.movl(rcx, Address(rdi, count_offset)); - masm.movl(rax, Address(rsi, count_offset)); - masm.cmpl(rcx, rax); - masm.jcc(Assembler::notEqual, RET_FALSE); - masm.testl(rax, rax); - masm.jcc(Assembler::zero, RET_TRUE); - - // get source string offset and value - masm.movptr(rbx, Address(rsi, value_offset)); - masm.movl(rax, Address(rsi, offset_offset)); - masm.leal(rsi, Address(rbx, rax, Address::times_2, base_offset)); - - // get compare string offset and value - masm.movptr(rbx, Address(rdi, value_offset)); - masm.movl(rax, Address(rdi, offset_offset)); - masm.leal(rdi, Address(rbx, rax, Address::times_2, base_offset)); - - // Set byte count - masm.shll(rcx, 1); - masm.movl(rax, rcx); - - if (UseSSE42Intrinsics) { - // With SSE4.2, use double quad vector compare - Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; - // Compare 16-byte vectors - masm.andl(rcx, 0xfffffff0); // vector count (in bytes) - masm.andl(rax, 0x0000000e); // tail count (in bytes) - masm.testl(rcx, rcx); - masm.jccb(Assembler::zero, COMPARE_TAIL); - masm.lea(rdi, Address(rdi, rcx, Address::times_1)); - masm.lea(rsi, Address(rsi, rcx, Address::times_1)); - masm.negl(rcx); - - masm.bind(COMPARE_WIDE_VECTORS); - masm.movdqu(tmp1Reg, Address(rdi, rcx, Address::times_1)); - masm.movdqu(tmp2Reg, Address(rsi, rcx, Address::times_1)); - masm.pxor(tmp1Reg, tmp2Reg); - masm.ptest(tmp1Reg, tmp1Reg); - masm.jccb(Assembler::notZero, RET_FALSE); - masm.addl(rcx, 16); - masm.jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); - masm.bind(COMPARE_TAIL); - masm.movl(rcx, rax); - // Fallthru to tail compare - } - - // Compare 4-byte vectors - masm.andl(rcx, 0xfffffffc); // vector count (in bytes) - masm.andl(rax, 0x00000002); // tail char (in bytes) - masm.testl(rcx, rcx); - masm.jccb(Assembler::zero, COMPARE_CHAR); - masm.lea(rdi, Address(rdi, rcx, Address::times_1)); - masm.lea(rsi, Address(rsi, rcx, Address::times_1)); - masm.negl(rcx); - - masm.bind(COMPARE_VECTORS); - masm.movl(rbx, Address(rdi, rcx, Address::times_1)); - masm.cmpl(rbx, Address(rsi, rcx, Address::times_1)); - masm.jccb(Assembler::notEqual, RET_FALSE); - masm.addl(rcx, 4); - masm.jcc(Assembler::notZero, COMPARE_VECTORS); - - // Compare trailing char (final 2 bytes), if any - masm.bind(COMPARE_CHAR); - masm.testl(rax, rax); - masm.jccb(Assembler::zero, RET_TRUE); - masm.load_unsigned_short(rbx, Address(rdi, 0)); - masm.load_unsigned_short(rcx, Address(rsi, 0)); - masm.cmpl(rbx, rcx); - masm.jccb(Assembler::notEqual, RET_FALSE); - - masm.bind(RET_TRUE); - masm.movl(rax, 1); // return true - masm.jmpb(DONE); - - masm.bind(RET_FALSE); - masm.xorl(rax, rax); // return false - - masm.bind(DONE); - %} - - enc_class enc_String_IndexOf(eSIRegP str1, eDIRegP str2, regXD tmp1, eAXRegI tmp2, - eCXRegI tmp3, eDXRegI tmp4, eBXRegI result) %{ - // SSE4.2 version - Label LOAD_SUBSTR, PREP_FOR_SCAN, SCAN_TO_SUBSTR, - SCAN_SUBSTR, RET_NEG_ONE, RET_NOT_FOUND, CLEANUP, DONE; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - - // Get the first character position in both strings - // [8] char array, [12] offset, [16] count - int value_offset = java_lang_String::value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String::count_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // Get counts for string and substr - masm.movl(rdx, Address(rsi, count_offset)); - masm.movl(rax, Address(rdi, count_offset)); - // Check for substr count > string count - masm.cmpl(rax, rdx); - masm.jcc(Assembler::greater, RET_NEG_ONE); - - // Start the indexOf operation - // Get start addr of string - masm.movptr(rbx, Address(rsi, value_offset)); - masm.movl(rcx, Address(rsi, offset_offset)); - masm.lea(rsi, Address(rbx, rcx, Address::times_2, base_offset)); - masm.push(rsi); - - // Get start addr of substr - masm.movptr(rbx, Address(rdi, value_offset)); - masm.movl(rcx, Address(rdi, offset_offset)); - masm.lea(rdi, Address(rbx, rcx, Address::times_2, base_offset)); - masm.push(rdi); - masm.push(rax); - masm.jmpb(PREP_FOR_SCAN); - - // Substr count saved at sp - // Substr saved at sp+4 - // String saved at sp+8 - - // Prep to load substr for scan - masm.bind(LOAD_SUBSTR); - masm.movptr(rdi, Address(rsp, 4)); - masm.movl(rax, Address(rsp, 0)); - - // Load substr - masm.bind(PREP_FOR_SCAN); - masm.movdqu(tmp1Reg, Address(rdi, 0)); - masm.addl(rdx, 8); // prime the loop - masm.subptr(rsi, 16); - - // Scan string for substr in 16-byte vectors - masm.bind(SCAN_TO_SUBSTR); - masm.subl(rdx, 8); - masm.addptr(rsi, 16); - masm.pcmpestri(tmp1Reg, Address(rsi, 0), 0x0d); - masm.jcc(Assembler::above, SCAN_TO_SUBSTR); // CF == 0 && ZF == 0 - masm.jccb(Assembler::aboveEqual, RET_NOT_FOUND); // CF == 0 - - // Fallthru: found a potential substr - - // Make sure string is still long enough - masm.subl(rdx, rcx); - masm.cmpl(rdx, rax); - masm.jccb(Assembler::negative, RET_NOT_FOUND); - // Compute start addr of substr - masm.lea(rsi, Address(rsi, rcx, Address::times_2)); - masm.movptr(rbx, rsi); - - // Compare potential substr - masm.addl(rdx, 8); // prime the loop - masm.addl(rax, 8); - masm.subptr(rsi, 16); - masm.subptr(rdi, 16); - - // Scan 16-byte vectors of string and substr - masm.bind(SCAN_SUBSTR); - masm.subl(rax, 8); - masm.subl(rdx, 8); - masm.addptr(rsi, 16); - masm.addptr(rdi, 16); - masm.movdqu(tmp1Reg, Address(rdi, 0)); - masm.pcmpestri(tmp1Reg, Address(rsi, 0), 0x0d); - masm.jcc(Assembler::noOverflow, LOAD_SUBSTR); // OF == 0 - masm.jcc(Assembler::positive, SCAN_SUBSTR); // SF == 0 - - // Compute substr offset - masm.movptr(rsi, Address(rsp, 8)); - masm.subptr(rbx, rsi); - masm.shrl(rbx, 1); - masm.jmpb(CLEANUP); - - masm.bind(RET_NEG_ONE); - masm.movl(rbx, -1); - masm.jmpb(DONE); - - masm.bind(RET_NOT_FOUND); - masm.movl(rbx, -1); - - masm.bind(CLEANUP); - masm.addptr(rsp, 12); - - masm.bind(DONE); - %} - - enc_class enc_Array_Equals(eDIRegP ary1, eSIRegP ary2, regXD tmp1, regXD tmp2, - eBXRegI tmp3, eDXRegI tmp4, eAXRegI result) %{ - Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - XMMRegister tmp2Reg = as_XMMRegister($tmp2$$reg); - Register ary1Reg = as_Register($ary1$$reg); - Register ary2Reg = as_Register($ary2$$reg); - Register tmp3Reg = as_Register($tmp3$$reg); - Register tmp4Reg = as_Register($tmp4$$reg); - Register resultReg = as_Register($result$$reg); - - int length_offset = arrayOopDesc::length_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // Check the input args - masm.cmpptr(ary1Reg, ary2Reg); - masm.jcc(Assembler::equal, TRUE_LABEL); - masm.testptr(ary1Reg, ary1Reg); - masm.jcc(Assembler::zero, FALSE_LABEL); - masm.testptr(ary2Reg, ary2Reg); - masm.jcc(Assembler::zero, FALSE_LABEL); - - // Check the lengths - masm.movl(tmp4Reg, Address(ary1Reg, length_offset)); - masm.movl(resultReg, Address(ary2Reg, length_offset)); - masm.cmpl(tmp4Reg, resultReg); - masm.jcc(Assembler::notEqual, FALSE_LABEL); - masm.testl(resultReg, resultReg); - masm.jcc(Assembler::zero, TRUE_LABEL); - - // Load array addrs - masm.lea(ary1Reg, Address(ary1Reg, base_offset)); - masm.lea(ary2Reg, Address(ary2Reg, base_offset)); - - // Set byte count - masm.shll(tmp4Reg, 1); - masm.movl(resultReg, tmp4Reg); - - if (UseSSE42Intrinsics) { - // With SSE4.2, use double quad vector compare - Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; - // Compare 16-byte vectors - masm.andl(tmp4Reg, 0xfffffff0); // vector count (in bytes) - masm.andl(resultReg, 0x0000000e); // tail count (in bytes) - masm.testl(tmp4Reg, tmp4Reg); - masm.jccb(Assembler::zero, COMPARE_TAIL); - masm.lea(ary1Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.lea(ary2Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.negl(tmp4Reg); - - masm.bind(COMPARE_WIDE_VECTORS); - masm.movdqu(tmp1Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.movdqu(tmp2Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.pxor(tmp1Reg, tmp2Reg); - masm.ptest(tmp1Reg, tmp1Reg); - - masm.jccb(Assembler::notZero, FALSE_LABEL); - masm.addl(tmp4Reg, 16); - masm.jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); - masm.bind(COMPARE_TAIL); - masm.movl(tmp4Reg, resultReg); - // Fallthru to tail compare - } - - // Compare 4-byte vectors - masm.andl(tmp4Reg, 0xfffffffc); // vector count (in bytes) - masm.andl(resultReg, 0x00000002); // tail char (in bytes) - masm.testl(tmp4Reg, tmp4Reg); - masm.jccb(Assembler::zero, COMPARE_CHAR); - masm.lea(ary1Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.lea(ary2Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.negl(tmp4Reg); - - masm.bind(COMPARE_VECTORS); - masm.movl(tmp3Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.cmpl(tmp3Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.jccb(Assembler::notEqual, FALSE_LABEL); - masm.addl(tmp4Reg, 4); - masm.jcc(Assembler::notZero, COMPARE_VECTORS); - - // Compare trailing char (final 2 bytes), if any - masm.bind(COMPARE_CHAR); - masm.testl(resultReg, resultReg); - masm.jccb(Assembler::zero, TRUE_LABEL); - masm.load_unsigned_short(tmp3Reg, Address(ary1Reg, 0)); - masm.load_unsigned_short(tmp4Reg, Address(ary2Reg, 0)); - masm.cmpl(tmp3Reg, tmp4Reg); - masm.jccb(Assembler::notEqual, FALSE_LABEL); - - masm.bind(TRUE_LABEL); - masm.movl(resultReg, 1); // return true - masm.jmpb(DONE); - - masm.bind(FALSE_LABEL); - masm.xorl(resultReg, resultReg); // return false - - // That's it - masm.bind(DONE); - %} enc_class enc_pop_rdx() %{ emit_opcode(cbuf,0x5A); @@ -9008,6 +8621,63 @@ ins_pipe( pipe_slow ); %} +// Multiply Register Long where the left operand's high 32 bits are zero +instruct mulL_eReg_lhi0(eADXRegL dst, eRegL src, eRegI tmp, eFlagsReg cr) %{ + predicate(is_operand_hi32_zero(n->in(1))); + match(Set dst (MulL dst src)); + effect(KILL cr, TEMP tmp); + ins_cost(2*100+2*400); +// Basic idea: lo(result) = lo(x_lo * y_lo) +// hi(result) = hi(x_lo * y_lo) + lo(x_lo * y_hi) where lo(x_hi * y_lo) = 0 because x_hi = 0 + format %{ "MOV $tmp,$src.hi\n\t" + "IMUL $tmp,EAX\n\t" + "MUL EDX:EAX,$src.lo\n\t" + "ADD EDX,$tmp" %} + ins_encode %{ + __ movl($tmp$$Register, HIGH_FROM_LOW($src$$Register)); + __ imull($tmp$$Register, rax); + __ mull($src$$Register); + __ addl(rdx, $tmp$$Register); + %} + ins_pipe( pipe_slow ); +%} + +// Multiply Register Long where the right operand's high 32 bits are zero +instruct mulL_eReg_rhi0(eADXRegL dst, eRegL src, eRegI tmp, eFlagsReg cr) %{ + predicate(is_operand_hi32_zero(n->in(2))); + match(Set dst (MulL dst src)); + effect(KILL cr, TEMP tmp); + ins_cost(2*100+2*400); +// Basic idea: lo(result) = lo(x_lo * y_lo) +// hi(result) = hi(x_lo * y_lo) + lo(x_hi * y_lo) where lo(x_lo * y_hi) = 0 because y_hi = 0 + format %{ "MOV $tmp,$src.lo\n\t" + "IMUL $tmp,EDX\n\t" + "MUL EDX:EAX,$src.lo\n\t" + "ADD EDX,$tmp" %} + ins_encode %{ + __ movl($tmp$$Register, $src$$Register); + __ imull($tmp$$Register, rdx); + __ mull($src$$Register); + __ addl(rdx, $tmp$$Register); + %} + ins_pipe( pipe_slow ); +%} + +// Multiply Register Long where the left and the right operands' high 32 bits are zero +instruct mulL_eReg_hi0(eADXRegL dst, eRegL src, eFlagsReg cr) %{ + predicate(is_operand_hi32_zero(n->in(1)) && is_operand_hi32_zero(n->in(2))); + match(Set dst (MulL dst src)); + effect(KILL cr); + ins_cost(1*400); +// Basic idea: lo(result) = lo(x_lo * y_lo) +// hi(result) = hi(x_lo * y_lo) where lo(x_hi * y_lo) = 0 and lo(x_lo * y_hi) = 0 because x_hi = 0 and y_hi = 0 + format %{ "MUL EDX:EAX,$src.lo\n\t" %} + ins_encode %{ + __ mull($src$$Register); + %} + ins_pipe( pipe_slow ); +%} + // Multiply Register Long by small constant instruct mulL_eReg_con(eADXRegL dst, immL_127 src, eRegI tmp, eFlagsReg cr) %{ match(Set dst (MulL dst src)); @@ -12718,48 +12388,64 @@ ins_pipe( pipe_slow ); %} -instruct string_compare(eDIRegP str1, eSIRegP str2, regXD tmp1, regXD tmp2, - eAXRegI tmp3, eBXRegI tmp4, eCXRegI result, eFlagsReg cr) %{ - match(Set result (StrComp str1 str2)); - effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, KILL tmp3, KILL tmp4, KILL cr); - //ins_cost(300); - - format %{ "String Compare $str1,$str2 -> $result // KILL EAX, EBX" %} - ins_encode( enc_String_Compare(str1, str2, tmp1, tmp2, tmp3, tmp4, result) ); +instruct string_compare(eDIRegP str1, eCXRegI cnt1, eSIRegP str2, eBXRegI cnt2, + eAXRegI result, regXD tmp1, regXD tmp2, eFlagsReg cr) %{ + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1, $tmp2" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} ins_pipe( pipe_slow ); %} // fast string equals -instruct string_equals(eDIRegP str1, eSIRegP str2, regXD tmp1, regXD tmp2, - eBXRegI tmp3, eCXRegI tmp4, eAXRegI result, eFlagsReg cr) %{ - match(Set result (StrEquals str1 str2)); - effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, KILL tmp3, KILL tmp4, KILL cr); - - format %{ "String Equals $str1,$str2 -> $result // KILL EBX, ECX" %} - ins_encode( enc_String_Equals(tmp1, tmp2, str1, str2, tmp3, tmp4, result) ); - ins_pipe( pipe_slow ); -%} - -instruct string_indexof(eSIRegP str1, eDIRegP str2, regXD tmp1, eAXRegI tmp2, - eCXRegI tmp3, eDXRegI tmp4, eBXRegI result, eFlagsReg cr) %{ +instruct string_equals(eDIRegP str1, eSIRegP str2, eCXRegI cnt, eAXRegI result, + regXD tmp1, regXD tmp2, eBXRegI tmp3, eFlagsReg cr) %{ + match(Set result (StrEquals (Binary str1 str2) cnt)); + effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp3, KILL cr); + + format %{ "String Equals $str1,$str2,$cnt -> $result // KILL $tmp1, $tmp2, $tmp3" %} + ins_encode %{ + __ char_arrays_equals(false, $str1$$Register, $str2$$Register, + $cnt$$Register, $result$$Register, $tmp3$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct string_indexof(eDIRegP str1, eDXRegI cnt1, eSIRegP str2, eAXRegI cnt2, + eBXRegI result, regXD tmp1, eCXRegI tmp2, eFlagsReg cr) %{ predicate(UseSSE42Intrinsics); - match(Set result (StrIndexOf str1 str2)); - effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, KILL tmp2, KILL tmp3, KILL tmp4, KILL cr); - - format %{ "String IndexOf $str1,$str2 -> $result // KILL EAX, ECX, EDX" %} - ins_encode( enc_String_IndexOf(str1, str2, tmp1, tmp2, tmp3, tmp4, result) ); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp2, KILL cr); + + format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp2, $tmp1" %} + ins_encode %{ + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, $tmp2$$Register); + %} ins_pipe( pipe_slow ); %} // fast array equals -instruct array_equals(eDIRegP ary1, eSIRegP ary2, regXD tmp1, regXD tmp2, eBXRegI tmp3, - eDXRegI tmp4, eAXRegI result, eFlagsReg cr) %{ +instruct array_equals(eDIRegP ary1, eSIRegP ary2, eAXRegI result, + regXD tmp1, regXD tmp2, eCXRegI tmp3, eBXRegI tmp4, eFlagsReg cr) +%{ match(Set result (AryEq ary1 ary2)); effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL ary2, KILL tmp3, KILL tmp4, KILL cr); //ins_cost(300); - format %{ "Array Equals $ary1,$ary2 -> $result // KILL EBX, EDX" %} - ins_encode( enc_Array_Equals(ary1, ary2, tmp1, tmp2, tmp3, tmp4, result) ); + format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + ins_encode %{ + __ char_arrays_equals(true, $ary1$$Register, $ary2$$Register, + $tmp3$$Register, $result$$Register, $tmp4$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} ins_pipe( pipe_slow ); %} @@ -13842,6 +13528,7 @@ // compute_padding() functions will have to be adjusted. instruct CallStaticJavaDirect(method meth) %{ match(CallStaticJava); + predicate(! ((CallStaticJavaNode*)n)->is_method_handle_invoke()); effect(USE meth); ins_cost(300); @@ -13856,6 +13543,30 @@ ins_alignment(4); %} +// Call Java Static Instruction (method handle version) +// Note: If this code changes, the corresponding ret_addr_offset() and +// compute_padding() functions will have to be adjusted. +instruct CallStaticJavaHandle(method meth, eBPRegP ebp) %{ + match(CallStaticJava); + predicate(((CallStaticJavaNode*)n)->is_method_handle_invoke()); + effect(USE meth); + // EBP is saved by all callees (for interpreter stack correction). + // We use it here for a similar purpose, in {preserve,restore}_SP. + + ins_cost(300); + format %{ "CALL,static/MethodHandle " %} + opcode(0xE8); /* E8 cd */ + ins_encode( pre_call_FPU, + preserve_SP, + Java_Static_Call( meth ), + restore_SP, + call_epilog, + post_call_FPU ); + ins_pipe( pipe_slow ); + ins_pc_relative(1); + ins_alignment(4); +%} + // Call Java Dynamic Instruction // Note: If this code changes, the corresponding ret_addr_offset() and // compute_padding() functions will have to be adjusted. diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Thu Mar 25 16:27:12 2010 -0700 +++ b/src/cpu/x86/vm/x86_64.ad Thu Mar 25 16:54:59 2010 -0700 @@ -551,12 +551,19 @@ #define __ _masm. +static int preserve_SP_size() { + return LP64_ONLY(1 +) 2; // [rex,] op, rm(reg/reg) +} + // !!!!! Special hack to get all types of calls to specify the byte offset // from the start of the call to the point where the return address // will point. int MachCallStaticJavaNode::ret_addr_offset() { - return 5; // 5 bytes from start of call to where return address points + int offset = 5; // 5 bytes from start of call to where return address points + if (_method_handle_invoke) + offset += preserve_SP_size(); + return offset; } int MachCallDynamicJavaNode::ret_addr_offset() @@ -589,6 +596,15 @@ // The address of the call instruction needs to be 4-byte aligned to // ensure that it does not span a cache line so that it can be patched. +int CallStaticJavaHandleNode::compute_padding(int current_offset) const +{ + current_offset += preserve_SP_size(); // skip mov rbp, rsp + current_offset += 1; // skip call opcode byte + return round_to(current_offset, alignment_required()) - current_offset; +} + +// The address of the call instruction needs to be 4-byte aligned to +// ensure that it does not span a cache line so that it can be patched. int CallDynamicJavaDirectNode::compute_padding(int current_offset) const { current_offset += 11; // skip movq instruction + call opcode byte @@ -683,7 +699,7 @@ #ifdef ASSERT if (rspec.reloc()->type() == relocInfo::oop_type && d32 != 0 && d32 != (intptr_t) Universe::non_oop_word()) { - assert(oop((intptr_t)d32)->is_oop() && oop((intptr_t)d32)->is_perm(), "cannot embed non-perm oops in code"); + assert(oop((intptr_t)d32)->is_oop() && (ScavengeRootsInCode || !oop((intptr_t)d32)->is_scavengable()), "cannot embed scavengable oops in code"); } #endif cbuf.relocate(cbuf.inst_mark(), rspec, format); @@ -721,8 +737,8 @@ #ifdef ASSERT if (rspec.reloc()->type() == relocInfo::oop_type && d64 != 0 && d64 != (int64_t) Universe::non_oop_word()) { - assert(oop(d64)->is_oop() && oop(d64)->is_perm(), - "cannot embed non-perm oops in code"); + assert(oop(d64)->is_oop() && (ScavengeRootsInCode || !oop(d64)->is_scavengable()), + "cannot embed scavengable oops in code"); } #endif cbuf.relocate(cbuf.inst_mark(), rspec, format); @@ -2058,8 +2074,10 @@ // implement the UseStrictFP mode. const bool Matcher::strict_fp_requires_explicit_rounding = true; -// Do floats take an entire double register or just half? -const bool Matcher::float_in_double = true; +// Are floats conerted to double when stored to stack during deoptimization? +// On x64 it is stored without convertion so we can use normal access. +bool Matcher::float_in_double() { return false; } + // Do ints take an entire long register or just half? const bool Matcher::int_in_long = true; @@ -2113,6 +2131,10 @@ return LONG_RDX_REG_mask; } +const RegMask Matcher::method_handle_invoke_SP_save_mask() { + return PTR_RBP_REG_mask; +} + static Address build_address(int b, int i, int s, int d) { Register index = as_Register(i); Address::ScaleFactor scale = (Address::ScaleFactor)s; @@ -2608,6 +2630,21 @@ RELOC_DISP32); %} + enc_class preserve_SP %{ + debug_only(int off0 = cbuf.code_size()); + MacroAssembler _masm(&cbuf); + // RBP is preserved across all calls, even compiled calls. + // Use it to preserve RSP in places where the callee might change the SP. + __ movptr(rbp, rsp); + debug_only(int off1 = cbuf.code_size()); + assert(off1 - off0 == preserve_SP_size(), "correct size prediction"); + %} + + enc_class restore_SP %{ + MacroAssembler _masm(&cbuf); + __ movptr(rsp, rbp); + %} + enc_class Java_Static_Call(method meth) %{ // JAVA STATIC CALL @@ -3701,448 +3738,6 @@ } %} - enc_class enc_String_Compare(rdi_RegP str1, rsi_RegP str2, regD tmp1, regD tmp2, - rax_RegI tmp3, rbx_RegI tmp4, rcx_RegI result) %{ - Label RCX_GOOD_LABEL, LENGTH_DIFF_LABEL, - POP_LABEL, DONE_LABEL, CONT_LABEL, - WHILE_HEAD_LABEL; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - XMMRegister tmp2Reg = as_XMMRegister($tmp2$$reg); - - // Get the first character position in both strings - // [8] char array, [12] offset, [16] count - int value_offset = java_lang_String::value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String::count_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - masm.load_heap_oop(rax, Address(rsi, value_offset)); - masm.movl(rcx, Address(rsi, offset_offset)); - masm.lea(rax, Address(rax, rcx, Address::times_2, base_offset)); - masm.load_heap_oop(rbx, Address(rdi, value_offset)); - masm.movl(rcx, Address(rdi, offset_offset)); - masm.lea(rbx, Address(rbx, rcx, Address::times_2, base_offset)); - - // Compute the minimum of the string lengths(rsi) and the - // difference of the string lengths (stack) - - // do the conditional move stuff - masm.movl(rdi, Address(rdi, count_offset)); - masm.movl(rsi, Address(rsi, count_offset)); - masm.movl(rcx, rdi); - masm.subl(rdi, rsi); - masm.push(rdi); - masm.cmov(Assembler::lessEqual, rsi, rcx); - - // Is the minimum length zero? - masm.bind(RCX_GOOD_LABEL); - masm.testl(rsi, rsi); - masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); - - // Load first characters - masm.load_unsigned_short(rcx, Address(rbx, 0)); - masm.load_unsigned_short(rdi, Address(rax, 0)); - - // Compare first characters - masm.subl(rcx, rdi); - masm.jcc(Assembler::notZero, POP_LABEL); - masm.decrementl(rsi); - masm.jcc(Assembler::zero, LENGTH_DIFF_LABEL); - - { - // Check after comparing first character to see if strings are equivalent - Label LSkip2; - // Check if the strings start at same location - masm.cmpptr(rbx, rax); - masm.jccb(Assembler::notEqual, LSkip2); - - // Check if the length difference is zero (from stack) - masm.cmpl(Address(rsp, 0), 0x0); - masm.jcc(Assembler::equal, LENGTH_DIFF_LABEL); - - // Strings might not be equivalent - masm.bind(LSkip2); - } - - // Advance to next character - masm.addptr(rax, 2); - masm.addptr(rbx, 2); - - if (UseSSE42Intrinsics) { - // With SSE4.2, use double quad vector compare - Label COMPARE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_TAIL; - // Setup to compare 16-byte vectors - masm.movl(rdi, rsi); - masm.andl(rsi, 0xfffffff8); // rsi holds the vector count - masm.andl(rdi, 0x00000007); // rdi holds the tail count - masm.testl(rsi, rsi); - masm.jccb(Assembler::zero, COMPARE_TAIL); - - masm.lea(rax, Address(rax, rsi, Address::times_2)); - masm.lea(rbx, Address(rbx, rsi, Address::times_2)); - masm.negptr(rsi); - - masm.bind(COMPARE_VECTORS); - masm.movdqu(tmp1Reg, Address(rax, rsi, Address::times_2)); - masm.movdqu(tmp2Reg, Address(rbx, rsi, Address::times_2)); - masm.pxor(tmp1Reg, tmp2Reg); - masm.ptest(tmp1Reg, tmp1Reg); - masm.jccb(Assembler::notZero, VECTOR_NOT_EQUAL); - masm.addptr(rsi, 8); - masm.jcc(Assembler::notZero, COMPARE_VECTORS); - masm.jmpb(COMPARE_TAIL); - - // Mismatched characters in the vectors - masm.bind(VECTOR_NOT_EQUAL); - masm.lea(rax, Address(rax, rsi, Address::times_2)); - masm.lea(rbx, Address(rbx, rsi, Address::times_2)); - masm.movl(rdi, 8); - - // Compare tail (< 8 chars), or rescan last vectors to - // find 1st mismatched characters - masm.bind(COMPARE_TAIL); - masm.testl(rdi, rdi); - masm.jccb(Assembler::zero, LENGTH_DIFF_LABEL); - masm.movl(rsi, rdi); - // Fallthru to tail compare - } - - // Shift RAX and RBX to the end of the arrays, negate min - masm.lea(rax, Address(rax, rsi, Address::times_2, 0)); - masm.lea(rbx, Address(rbx, rsi, Address::times_2, 0)); - masm.negptr(rsi); - - // Compare the rest of the characters - masm.bind(WHILE_HEAD_LABEL); - masm.load_unsigned_short(rcx, Address(rbx, rsi, Address::times_2, 0)); - masm.load_unsigned_short(rdi, Address(rax, rsi, Address::times_2, 0)); - masm.subl(rcx, rdi); - masm.jccb(Assembler::notZero, POP_LABEL); - masm.increment(rsi); - masm.jcc(Assembler::notZero, WHILE_HEAD_LABEL); - - // Strings are equal up to min length. Return the length difference. - masm.bind(LENGTH_DIFF_LABEL); - masm.pop(rcx); - masm.jmpb(DONE_LABEL); - - // Discard the stored length difference - masm.bind(POP_LABEL); - masm.addptr(rsp, 8); - - // That's it - masm.bind(DONE_LABEL); - %} - - enc_class enc_String_IndexOf(rsi_RegP str1, rdi_RegP str2, regD tmp1, rax_RegI tmp2, - rcx_RegI tmp3, rdx_RegI tmp4, rbx_RegI result) %{ - // SSE4.2 version - Label LOAD_SUBSTR, PREP_FOR_SCAN, SCAN_TO_SUBSTR, - SCAN_SUBSTR, RET_NEG_ONE, RET_NOT_FOUND, CLEANUP, DONE; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - - // Get the first character position in both strings - // [8] char array, [12] offset, [16] count - int value_offset = java_lang_String::value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String::count_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // Get counts for string and substr - masm.movl(rdx, Address(rsi, count_offset)); - masm.movl(rax, Address(rdi, count_offset)); - // Check for substr count > string count - masm.cmpl(rax, rdx); - masm.jcc(Assembler::greater, RET_NEG_ONE); - - // Start the indexOf operation - // Get start addr of string - masm.load_heap_oop(rbx, Address(rsi, value_offset)); - masm.movl(rcx, Address(rsi, offset_offset)); - masm.lea(rsi, Address(rbx, rcx, Address::times_2, base_offset)); - masm.push(rsi); - - // Get start addr of substr - masm.load_heap_oop(rbx, Address(rdi, value_offset)); - masm.movl(rcx, Address(rdi, offset_offset)); - masm.lea(rdi, Address(rbx, rcx, Address::times_2, base_offset)); - masm.push(rdi); - masm.push(rax); - masm.jmpb(PREP_FOR_SCAN); - - // Substr count saved at sp - // Substr saved at sp+8 - // String saved at sp+16 - - // Prep to load substr for scan - masm.bind(LOAD_SUBSTR); - masm.movptr(rdi, Address(rsp, 8)); - masm.movl(rax, Address(rsp, 0)); - - // Load substr - masm.bind(PREP_FOR_SCAN); - masm.movdqu(tmp1Reg, Address(rdi, 0)); - masm.addq(rdx, 8); // prime the loop - masm.subptr(rsi, 16); - - // Scan string for substr in 16-byte vectors - masm.bind(SCAN_TO_SUBSTR); - masm.subq(rdx, 8); - masm.addptr(rsi, 16); - masm.pcmpestri(tmp1Reg, Address(rsi, 0), 0x0d); - masm.jcc(Assembler::above, SCAN_TO_SUBSTR); - masm.jccb(Assembler::aboveEqual, RET_NOT_FOUND); - - // Fallthru: found a potential substr - - //Make sure string is still long enough - masm.subl(rdx, rcx); - masm.cmpl(rdx, rax); - masm.jccb(Assembler::negative, RET_NOT_FOUND); - // Compute start addr of substr - masm.lea(rsi, Address(rsi, rcx, Address::times_2)); - masm.movptr(rbx, rsi); - - // Compare potential substr - masm.addq(rdx, 8); // prime the loop - masm.addq(rax, 8); - masm.subptr(rsi, 16); - masm.subptr(rdi, 16); - - // Scan 16-byte vectors of string and substr - masm.bind(SCAN_SUBSTR); - masm.subq(rax, 8); - masm.subq(rdx, 8); - masm.addptr(rsi, 16); - masm.addptr(rdi, 16); - masm.movdqu(tmp1Reg, Address(rdi, 0)); - masm.pcmpestri(tmp1Reg, Address(rsi, 0), 0x0d); - masm.jcc(Assembler::noOverflow, LOAD_SUBSTR); // OF == 0 - masm.jcc(Assembler::positive, SCAN_SUBSTR); // SF == 0 - - // Compute substr offset - masm.movptr(rsi, Address(rsp, 16)); - masm.subptr(rbx, rsi); - masm.shrl(rbx, 1); - masm.jmpb(CLEANUP); - - masm.bind(RET_NEG_ONE); - masm.movl(rbx, -1); - masm.jmpb(DONE); - - masm.bind(RET_NOT_FOUND); - masm.movl(rbx, -1); - - masm.bind(CLEANUP); - masm.addptr(rsp, 24); - - masm.bind(DONE); - %} - - enc_class enc_String_Equals(rdi_RegP str1, rsi_RegP str2, regD tmp1, regD tmp2, - rbx_RegI tmp3, rcx_RegI tmp2, rax_RegI result) %{ - Label RET_TRUE, RET_FALSE, DONE, COMPARE_VECTORS, COMPARE_CHAR; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - XMMRegister tmp2Reg = as_XMMRegister($tmp2$$reg); - - int value_offset = java_lang_String::value_offset_in_bytes(); - int offset_offset = java_lang_String::offset_offset_in_bytes(); - int count_offset = java_lang_String::count_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // does source == target string? - masm.cmpptr(rdi, rsi); - masm.jcc(Assembler::equal, RET_TRUE); - - // get and compare counts - masm.movl(rcx, Address(rdi, count_offset)); - masm.movl(rax, Address(rsi, count_offset)); - masm.cmpl(rcx, rax); - masm.jcc(Assembler::notEqual, RET_FALSE); - masm.testl(rax, rax); - masm.jcc(Assembler::zero, RET_TRUE); - - // get source string offset and value - masm.load_heap_oop(rbx, Address(rsi, value_offset)); - masm.movl(rax, Address(rsi, offset_offset)); - masm.lea(rsi, Address(rbx, rax, Address::times_2, base_offset)); - - // get compare string offset and value - masm.load_heap_oop(rbx, Address(rdi, value_offset)); - masm.movl(rax, Address(rdi, offset_offset)); - masm.lea(rdi, Address(rbx, rax, Address::times_2, base_offset)); - - // Set byte count - masm.shll(rcx, 1); - masm.movl(rax, rcx); - - if (UseSSE42Intrinsics) { - // With SSE4.2, use double quad vector compare - Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; - // Compare 16-byte vectors - masm.andl(rcx, 0xfffffff0); // vector count (in bytes) - masm.andl(rax, 0x0000000e); // tail count (in bytes) - masm.testl(rcx, rcx); - masm.jccb(Assembler::zero, COMPARE_TAIL); - masm.lea(rdi, Address(rdi, rcx, Address::times_1)); - masm.lea(rsi, Address(rsi, rcx, Address::times_1)); - masm.negptr(rcx); - - masm.bind(COMPARE_WIDE_VECTORS); - masm.movdqu(tmp1Reg, Address(rdi, rcx, Address::times_1)); - masm.movdqu(tmp2Reg, Address(rsi, rcx, Address::times_1)); - masm.pxor(tmp1Reg, tmp2Reg); - masm.ptest(tmp1Reg, tmp1Reg); - masm.jccb(Assembler::notZero, RET_FALSE); - masm.addptr(rcx, 16); - masm.jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); - masm.bind(COMPARE_TAIL); - masm.movl(rcx, rax); - // Fallthru to tail compare - } - - // Compare 4-byte vectors - masm.andl(rcx, 0xfffffffc); // vector count (in bytes) - masm.andl(rax, 0x00000002); // tail char (in bytes) - masm.testl(rcx, rcx); - masm.jccb(Assembler::zero, COMPARE_CHAR); - masm.lea(rdi, Address(rdi, rcx, Address::times_1)); - masm.lea(rsi, Address(rsi, rcx, Address::times_1)); - masm.negptr(rcx); - - masm.bind(COMPARE_VECTORS); - masm.movl(rbx, Address(rdi, rcx, Address::times_1)); - masm.cmpl(rbx, Address(rsi, rcx, Address::times_1)); - masm.jccb(Assembler::notEqual, RET_FALSE); - masm.addptr(rcx, 4); - masm.jcc(Assembler::notZero, COMPARE_VECTORS); - - // Compare trailing char (final 2 bytes), if any - masm.bind(COMPARE_CHAR); - masm.testl(rax, rax); - masm.jccb(Assembler::zero, RET_TRUE); - masm.load_unsigned_short(rbx, Address(rdi, 0)); - masm.load_unsigned_short(rcx, Address(rsi, 0)); - masm.cmpl(rbx, rcx); - masm.jccb(Assembler::notEqual, RET_FALSE); - - masm.bind(RET_TRUE); - masm.movl(rax, 1); // return true - masm.jmpb(DONE); - - masm.bind(RET_FALSE); - masm.xorl(rax, rax); // return false - - masm.bind(DONE); - %} - - enc_class enc_Array_Equals(rdi_RegP ary1, rsi_RegP ary2, regD tmp1, regD tmp2, - rax_RegI tmp3, rbx_RegI tmp4, rcx_RegI result) %{ - Label TRUE_LABEL, FALSE_LABEL, DONE, COMPARE_VECTORS, COMPARE_CHAR; - MacroAssembler masm(&cbuf); - - XMMRegister tmp1Reg = as_XMMRegister($tmp1$$reg); - XMMRegister tmp2Reg = as_XMMRegister($tmp2$$reg); - Register ary1Reg = as_Register($ary1$$reg); - Register ary2Reg = as_Register($ary2$$reg); - Register tmp3Reg = as_Register($tmp3$$reg); - Register tmp4Reg = as_Register($tmp4$$reg); - Register resultReg = as_Register($result$$reg); - - int length_offset = arrayOopDesc::length_offset_in_bytes(); - int base_offset = arrayOopDesc::base_offset_in_bytes(T_CHAR); - - // Check the input args - masm.cmpq(ary1Reg, ary2Reg); - masm.jcc(Assembler::equal, TRUE_LABEL); - masm.testq(ary1Reg, ary1Reg); - masm.jcc(Assembler::zero, FALSE_LABEL); - masm.testq(ary2Reg, ary2Reg); - masm.jcc(Assembler::zero, FALSE_LABEL); - - // Check the lengths - masm.movl(tmp4Reg, Address(ary1Reg, length_offset)); - masm.movl(resultReg, Address(ary2Reg, length_offset)); - masm.cmpl(tmp4Reg, resultReg); - masm.jcc(Assembler::notEqual, FALSE_LABEL); - masm.testl(resultReg, resultReg); - masm.jcc(Assembler::zero, TRUE_LABEL); - - //load array address - masm.lea(ary1Reg, Address(ary1Reg, base_offset)); - masm.lea(ary2Reg, Address(ary2Reg, base_offset)); - - //set byte count - masm.shll(tmp4Reg, 1); - masm.movl(resultReg,tmp4Reg); - - if (UseSSE42Intrinsics){ - // With SSE4.2, use double quad vector compare - Label COMPARE_WIDE_VECTORS, COMPARE_TAIL; - // Compare 16-byte vectors - masm.andl(tmp4Reg, 0xfffffff0); // vector count (in bytes) - masm.andl(resultReg, 0x0000000e); // tail count (in bytes) - masm.testl(tmp4Reg, tmp4Reg); - masm.jccb(Assembler::zero, COMPARE_TAIL); - masm.lea(ary1Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.lea(ary2Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.negptr(tmp4Reg); - - masm.bind(COMPARE_WIDE_VECTORS); - masm.movdqu(tmp1Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.movdqu(tmp2Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.pxor(tmp1Reg, tmp2Reg); - masm.ptest(tmp1Reg, tmp1Reg); - - masm.jccb(Assembler::notZero, FALSE_LABEL); - masm.addptr(tmp4Reg, 16); - masm.jcc(Assembler::notZero, COMPARE_WIDE_VECTORS); - masm.bind(COMPARE_TAIL); - masm.movl(tmp4Reg, resultReg); - // Fallthru to tail compare - } - - // Compare 4-byte vectors - masm.andl(tmp4Reg, 0xfffffffc); // vector count (in bytes) - masm.andl(resultReg, 0x00000002); // tail char (in bytes) - masm.testl(tmp4Reg, tmp4Reg); //if tmp2 == 0, only compare char - masm.jccb(Assembler::zero, COMPARE_CHAR); - masm.lea(ary1Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.lea(ary2Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.negptr(tmp4Reg); - - masm.bind(COMPARE_VECTORS); - masm.movl(tmp3Reg, Address(ary1Reg, tmp4Reg, Address::times_1)); - masm.cmpl(tmp3Reg, Address(ary2Reg, tmp4Reg, Address::times_1)); - masm.jccb(Assembler::notEqual, FALSE_LABEL); - masm.addptr(tmp4Reg, 4); - masm.jcc(Assembler::notZero, COMPARE_VECTORS); - - // Compare trailing char (final 2 bytes), if any - masm.bind(COMPARE_CHAR); - masm.testl(resultReg, resultReg); - masm.jccb(Assembler::zero, TRUE_LABEL); - masm.load_unsigned_short(tmp3Reg, Address(ary1Reg, 0)); - masm.load_unsigned_short(tmp4Reg, Address(ary2Reg, 0)); - masm.cmpl(tmp3Reg, tmp4Reg); - masm.jccb(Assembler::notEqual, FALSE_LABEL); - - masm.bind(TRUE_LABEL); - masm.movl(resultReg, 1); // return true - masm.jmpb(DONE); - - masm.bind(FALSE_LABEL); - masm.xorl(resultReg, resultReg); // return false - - // That's it - masm.bind(DONE); - %} enc_class enc_rethrow() %{ @@ -12096,52 +11691,67 @@ ins_pipe(pipe_slow); %} -instruct string_compare(rdi_RegP str1, rsi_RegP str2, regD tmp1, regD tmp2, - rax_RegI tmp3, rbx_RegI tmp4, rcx_RegI result, rFlagsReg cr) -%{ - match(Set result (StrComp str1 str2)); - effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, KILL tmp3, KILL tmp4, KILL cr); - //ins_cost(300); - - format %{ "String Compare $str1, $str2 -> $result // XXX KILL RAX, RBX" %} - ins_encode( enc_String_Compare(str1, str2, tmp1, tmp2, tmp3, tmp4, result) ); +instruct string_compare(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rbx_RegI cnt2, + rax_RegI result, regD tmp1, regD tmp2, rFlagsReg cr) +%{ + match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); + + format %{ "String Compare $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1, $tmp2" %} + ins_encode %{ + __ string_compare($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} ins_pipe( pipe_slow ); %} -instruct string_indexof(rsi_RegP str1, rdi_RegP str2, regD tmp1, rax_RegI tmp2, - rcx_RegI tmp3, rdx_RegI tmp4, rbx_RegI result, rFlagsReg cr) +instruct string_indexof(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2, + rbx_RegI result, regD tmp1, rcx_RegI tmp2, rFlagsReg cr) %{ predicate(UseSSE42Intrinsics); - match(Set result (StrIndexOf str1 str2)); - effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, KILL tmp2, KILL tmp3, KILL tmp4, KILL cr); - - format %{ "String IndexOf $str1,$str2 -> $result // KILL RAX, RCX, RDX" %} - ins_encode( enc_String_IndexOf(str1, str2, tmp1, tmp2, tmp3, tmp4, result) ); + match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); + effect(TEMP tmp1, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL tmp2, KILL cr); + + format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1, $tmp2" %} + ins_encode %{ + __ string_indexof($str1$$Register, $str2$$Register, + $cnt1$$Register, $cnt2$$Register, $result$$Register, + $tmp1$$XMMRegister, $tmp2$$Register); + %} ins_pipe( pipe_slow ); %} // fast string equals -instruct string_equals(rdi_RegP str1, rsi_RegP str2, regD tmp1, regD tmp2, rbx_RegI tmp3, - rcx_RegI tmp4, rax_RegI result, rFlagsReg cr) -%{ - match(Set result (StrEquals str1 str2)); - effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, KILL tmp3, KILL tmp4, KILL cr); - - format %{ "String Equals $str1,$str2 -> $result // KILL RBX, RCX" %} - ins_encode( enc_String_Equals(str1, str2, tmp1, tmp2, tmp3, tmp4, result) ); +instruct string_equals(rdi_RegP str1, rsi_RegP str2, rcx_RegI cnt, rax_RegI result, + regD tmp1, regD tmp2, rbx_RegI tmp3, rFlagsReg cr) +%{ + match(Set result (StrEquals (Binary str1 str2) cnt)); + effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp3, KILL cr); + + format %{ "String Equals $str1,$str2,$cnt -> $result // KILL $tmp1, $tmp2, $tmp3" %} + ins_encode %{ + __ char_arrays_equals(false, $str1$$Register, $str2$$Register, + $cnt$$Register, $result$$Register, $tmp3$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} ins_pipe( pipe_slow ); %} // fast array equals -instruct array_equals(rdi_RegP ary1, rsi_RegP ary2, regD tmp1, regD tmp2, rax_RegI tmp3, - rbx_RegI tmp4, rcx_RegI result, rFlagsReg cr) +instruct array_equals(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, + regD tmp1, regD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr) %{ match(Set result (AryEq ary1 ary2)); effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL ary2, KILL tmp3, KILL tmp4, KILL cr); //ins_cost(300); - format %{ "Array Equals $ary1,$ary2 -> $result // KILL RAX, RBX" %} - ins_encode( enc_Array_Equals(ary1, ary2, tmp1, tmp2, tmp3, tmp4, result) ); + format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1, $tmp2, $tmp3, $tmp4" %} + ins_encode %{ + __ char_arrays_equals(true, $ary1$$Register, $ary2$$Register, + $tmp3$$Register, $result$$Register, $tmp4$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister); + %} ins_pipe( pipe_slow ); %} @@ -12953,9 +12563,9 @@ // Call Java Static Instruction // Note: If this code changes, the corresponding ret_addr_offset() and // compute_padding() functions will have to be adjusted. -instruct CallStaticJavaDirect(method meth) -%{ +instruct CallStaticJavaDirect(method meth) %{ match(CallStaticJava); + predicate(!((CallStaticJavaNode*) n)->is_method_handle_invoke()); effect(USE meth); ins_cost(300); @@ -12967,6 +12577,28 @@ ins_alignment(4); %} +// Call Java Static Instruction (method handle version) +// Note: If this code changes, the corresponding ret_addr_offset() and +// compute_padding() functions will have to be adjusted. +instruct CallStaticJavaHandle(method meth, rbp_RegP rbp) %{ + match(CallStaticJava); + predicate(((CallStaticJavaNode*) n)->is_method_handle_invoke()); + effect(USE meth); + // RBP is saved by all callees (for interpreter stack correction). + // We use it here for a similar purpose, in {preserve,restore}_SP. + + ins_cost(300); + format %{ "call,static/MethodHandle " %} + opcode(0xE8); /* E8 cd */ + ins_encode(preserve_SP, + Java_Static_Call(meth), + restore_SP, + call_epilog); + ins_pipe(pipe_slow); + ins_pc_relative(1); + ins_alignment(4); +%} + // Call Java Dynamic Instruction // Note: If this code changes, the corresponding ret_addr_offset() and // compute_padding() functions will have to be adjusted. diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/assembler_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/assembler_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_assembler_zero.cpp.incl" + +int AbstractAssembler::code_fill_byte() { + return 0; +} + +void Assembler::pd_patch_instruction(address branch, address target) { + ShouldNotCallThis(); +} + +#ifndef PRODUCT +void Assembler::pd_print_patched_instruction(address branch) { + ShouldNotCallThis(); +} +#endif // PRODUCT + +void MacroAssembler::align(int modulus) { + while (offset() % modulus != 0) + emit_byte(AbstractAssembler::code_fill_byte()); +} + +void MacroAssembler::bang_stack_with_offset(int offset) { + ShouldNotCallThis(); +} + +void MacroAssembler::advance(int bytes) { + _code_pos += bytes; + sync(); +} + +RegisterOrConstant MacroAssembler::delayed_value_impl( + intptr_t* delayed_value_addr, Register tmpl, int offset) { + ShouldNotCallThis(); +} + +void MacroAssembler::store_oop(jobject obj) { + code_section()->relocate(pc(), oop_Relocation::spec_for_immediate()); + emit_address((address) obj); +} + +static void should_not_call() { + report_should_not_call(__FILE__, __LINE__); +} + +address ShouldNotCallThisStub() { + return (address) should_not_call; +} + +address ShouldNotCallThisEntry() { + return (address) should_not_call; +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/assembler_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/assembler_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// In normal, CPU-specific ports of HotSpot these two classes are used +// for generating assembly language. We don't do any of this in zero, +// of course, but we do sneak entry points around in CodeBuffers so we +// generate those here. + +class Assembler : public AbstractAssembler { + public: + Assembler(CodeBuffer* code) : AbstractAssembler(code) {} + + public: + void pd_patch_instruction(address branch, address target); +#ifndef PRODUCT + static void pd_print_patched_instruction(address branch); +#endif // PRODUCT +}; + +class MacroAssembler : public Assembler { + public: + MacroAssembler(CodeBuffer* code) : Assembler(code) {} + + public: + void align(int modulus); + void bang_stack_with_offset(int offset); + bool needs_explicit_null_check(intptr_t offset); + RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, + Register tmp, int offset); + public: + void advance(int bytes); + void store_oop(jobject obj); +}; + +#ifdef ASSERT +inline bool AbstractAssembler::pd_check_instruction_mark() { + ShouldNotCallThis(); +} +#endif + +address ShouldNotCallThisStub(); +address ShouldNotCallThisEntry(); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/assembler_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/assembler_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/bytecodeInterpreter_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/bytecodeInterpreter_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cppInterpreter_zero.cpp.incl" + +#ifdef CC_INTERP + +const char *BytecodeInterpreter::name_of_field_at_address(address addr) { +#define DO(member) {if (addr == (address) &(member)) return XSTR(member);} + DO(_thread); + DO(_bcp); + DO(_locals); + DO(_constants); + DO(_method); + DO(_mdx); + DO(_stack); + DO(_msg); + DO(_result); + DO(_prev_link); + DO(_oop_temp); + DO(_stack_base); + DO(_stack_limit); + DO(_monitor_base); + DO(_self_link); +#undef DO + if (addr > (address) &_result && addr < (address) (&_result + 1)) + return "_result)"; + return NULL; +} + +#endif // CC_INTERP diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/bytecodeInterpreter_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/bytecodeInterpreter_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,148 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Platform specific for C++ based Interpreter + +#if defined(PPC) || defined(SPARC) || defined(IA64) +#define LOTS_OF_REGS // Use plenty of registers +#else +#undef LOTS_OF_REGS // Loser platforms +#endif + + private: + interpreterState _self_link; + + public: + inline void set_locals(intptr_t* new_locals) { + _locals = new_locals; + } + inline void set_method(methodOop new_method) { + _method = new_method; + } + inline interpreterState self_link() { + return _self_link; + } + inline void set_self_link(interpreterState new_self_link) { + _self_link = new_self_link; + } + inline interpreterState prev_link() { + return _prev_link; + } + inline void set_prev_link(interpreterState new_prev_link) { + _prev_link = new_prev_link; + } + inline void set_stack_limit(intptr_t* new_stack_limit) { + _stack_limit = new_stack_limit; + } + inline void set_stack_base(intptr_t* new_stack_base) { + _stack_base = new_stack_base; + } + inline void set_monitor_base(BasicObjectLock *new_monitor_base) { + _monitor_base = new_monitor_base; + } + inline void set_thread(JavaThread* new_thread) { + _thread = new_thread; + } + inline void set_constants(constantPoolCacheOop new_constants) { + _constants = new_constants; + } + inline oop oop_temp() { + return _oop_temp; + } + inline oop *oop_temp_addr() { + return &_oop_temp; + } + inline void set_oop_temp(oop new_oop_temp) { + _oop_temp = new_oop_temp; + } + inline address callee_entry_point() { + return _result._to_call._callee_entry_point; + } + inline address osr_buf() { + return _result._osr._osr_buf; + } + inline address osr_entry() { + return _result._osr._osr_entry; + } + + public: + const char *name_of_field_at_address(address addr); + +// The frame manager handles this +#define SET_LAST_JAVA_FRAME() +#define RESET_LAST_JAVA_FRAME() + +// ZeroStack Implementation + +#undef STACK_INT +#undef STACK_FLOAT +#undef STACK_ADDR +#undef STACK_OBJECT +#undef STACK_DOUBLE +#undef STACK_LONG + +#define GET_STACK_SLOT(offset) (*((intptr_t*) &topOfStack[-(offset)])) +#define STACK_SLOT(offset) ((address) &topOfStack[-(offset)]) +#define STACK_ADDR(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_FLOAT(offset) (*((jfloat*)&locals[-(offset)])) +#define LOCALS_OBJECT(offset) ((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)])) +#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_ADDR(value, offset) (*((address *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_INT(value, offset) (*((jint *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_FLOAT(value, offset) (*((jfloat *)&locals[-(offset)]) = (value)) +#define SET_LOCALS_OBJECT(value, offset) (*((oop *)&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) +#define SET_LOCALS_LONG_FROM_ADDR(addr, offset) (((VMJavaVal64*)&locals[-((offset)+1)])->l = \ + ((VMJavaVal64*)(addr))->l) diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,301 @@ +/* + * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline interpreter functions for zero + +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 fmod(op1, 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]) { + *(uint64_t *) to = *(uint64_t *) from; +} + +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) { + /* it's possible we could catch this special case implicitly */ + if (op1 == (jlong) 0x8000000000000000LL && op2 == -1) return op1; + else 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) { + /* it's possible we could catch this special case implicitly */ + if (op1 == (jlong) 0x8000000000000000LL && op2 == -1) return 0; + else return op1 % op2; +} + +inline jlong BytecodeInterpreter::VMlongUshr(jlong op1, jint op2) { + return ((unsigned long long) 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) { + // Divide by zero... QQQ + 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 (op1 == (jint) 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 (op1 == (jint) 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 jint 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; +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/bytecodes_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/bytecodes_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_bytecodes_zero.cpp.incl" + +void Bytecodes::pd_initialize() { + // No zero specific initialization +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/bytecodes_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/bytecodes_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/bytes_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/bytes_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,164 @@ +/* + * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +typedef union unaligned { + u4 u; + u2 us; + u8 ul; +} __attribute__((packed)) unaligned; + +class Bytes: AllStatic { + public: + // Returns true if the byte ordering used by Java is different + // from the native byte ordering of the underlying machine. + static inline bool is_Java_byte_ordering_different() { +#ifdef VM_LITTLE_ENDIAN + return true; +#else + return false; +#endif + } + + // Efficient reading and writing of unaligned unsigned data in + // platform-specific byte ordering. + static inline u2 get_native_u2(address p){ + unaligned *up = (unaligned *) p; + return up->us; + } + + static inline u4 get_native_u4(address p) { + unaligned *up = (unaligned *) p; + return up->u; + } + + static inline u8 get_native_u8(address p) { + unaligned *up = (unaligned *) p; + return up->ul; + } + + static inline void put_native_u2(address p, u2 x) { + unaligned *up = (unaligned *) p; + up->us = x; + } + + static inline void put_native_u4(address p, u4 x) { + unaligned *up = (unaligned *) p; + up->u = x; + } + + static inline void put_native_u8(address p, u8 x) { + unaligned *up = (unaligned *) p; + up->ul = x; + } + + // Efficient reading and writing of unaligned unsigned data in Java + // byte ordering (i.e. big-endian ordering). +#ifdef VM_LITTLE_ENDIAN + // Byte-order reversal is needed + static inline u2 get_Java_u2(address p) { + return (u2(p[0]) << 8) | + (u2(p[1]) ); + } + static inline u4 get_Java_u4(address p) { + return (u4(p[0]) << 24) | + (u4(p[1]) << 16) | + (u4(p[2]) << 8) | + (u4(p[3]) ); + } + static inline u8 get_Java_u8(address p) { + u4 hi, lo; + hi = (u4(p[0]) << 24) | + (u4(p[1]) << 16) | + (u4(p[2]) << 8) | + (u4(p[3]) ); + lo = (u4(p[4]) << 24) | + (u4(p[5]) << 16) | + (u4(p[6]) << 8) | + (u4(p[7]) ); + return u8(lo) | (u8(hi) << 32); + } + + static inline void put_Java_u2(address p, u2 x) { + p[0] = x >> 8; + p[1] = x; + } + static inline void put_Java_u4(address p, u4 x) { + p[0] = x >> 24; + p[1] = x >> 16; + p[2] = x >> 8; + p[3] = x; + } + static inline void put_Java_u8(address p, u8 x) { + u4 hi, lo; + lo = x; + hi = x >> 32; + p[0] = hi >> 24; + p[1] = hi >> 16; + p[2] = hi >> 8; + p[3] = hi; + p[4] = lo >> 24; + p[5] = lo >> 16; + p[6] = lo >> 8; + p[7] = lo; + } + + // Efficient swapping of byte ordering + static inline u2 swap_u2(u2 x); + static inline u4 swap_u4(u4 x); + static inline u8 swap_u8(u8 x); +#else + // No byte-order reversal is needed + 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); + } + + // No byte-order reversal is needed + 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; } +#endif // VM_LITTLE_ENDIAN +}; + +#ifdef VM_LITTLE_ENDIAN +// The following header contains the implementations of swap_u2, +// swap_u4, and swap_u8 +#include "incls/_bytes_pd.inline.hpp.incl" +#endif // VM_LITTLE_ENDIAN diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/codeBuffer_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/codeBuffer_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,27 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + void pd_initialize() {} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/copy_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/copy_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,178 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Inline functions for memory copy and fill. + +static void pd_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + 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: + 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) { + 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) { + memmove(to, from, count); +} + +static void pd_conjoint_bytes_atomic(void* from, void* to, size_t count) { + memmove(to, from, count); +} + +static void pd_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + _Copy_conjoint_jshorts_atomic(from, to, count); +} + +static void pd_conjoint_jints_atomic(jint* from, jint* to, size_t count) { + _Copy_conjoint_jints_atomic(from, to, count); +} + +static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { + _Copy_conjoint_jlongs_atomic(from, to, count); +} + +static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { +#ifdef _LP64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + _Copy_conjoint_jlongs_atomic((jlong*)from, (jlong*)to, count); +#else + assert(BytesPerInt == BytesPerOop, "jints and oops must be the same size"); + _Copy_conjoint_jints_atomic((jint*)from, (jint*)to, count); +#endif // _LP64 +} + +static void pd_arrayof_conjoint_bytes(HeapWord* from, + HeapWord* to, + size_t count) { + _Copy_arrayof_conjoint_bytes(from, to, count); +} + +static void pd_arrayof_conjoint_jshorts(HeapWord* from, + HeapWord* to, + size_t count) { + _Copy_arrayof_conjoint_jshorts(from, to, count); +} + +static void pd_arrayof_conjoint_jints(HeapWord* from, + HeapWord* to, + size_t count) { + _Copy_arrayof_conjoint_jints(from, to, count); +} + +static void pd_arrayof_conjoint_jlongs(HeapWord* from, + HeapWord* to, + size_t count) { + _Copy_arrayof_conjoint_jlongs(from, to, count); +} + +static void pd_arrayof_conjoint_oops(HeapWord* from, + HeapWord* to, + size_t count) { +#ifdef _LP64 + assert(BytesPerLong == BytesPerOop, "jlongs and oops must be the same size"); + _Copy_arrayof_conjoint_jlongs(from, to, count); +#else + assert(BytesPerInt == BytesPerOop, "jints and oops must be the same size"); + _Copy_arrayof_conjoint_jints(from, to, count); +#endif // _LP64 +} + +static void pd_fill_to_words(HeapWord* tohw, size_t count, juint value) { +#ifdef _LP64 + julong* to = (julong*) tohw; + julong v = ((julong) value << 32) | value; +#else + juint* to = (juint*) tohw; + juint v = value; +#endif // _LP64 + + 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) { + 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) { + memset(to, 0, count); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/cppInterpreterGenerator_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/cppInterpreterGenerator_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + protected: + MacroAssembler* assembler() const { + return _masm; + } + + protected: + address generate_entry(address entry_point) { + ZeroEntry *entry = (ZeroEntry *) assembler()->pc(); + assembler()->advance(sizeof(ZeroEntry)); + entry->set_entry_point(entry_point); + return (address) entry; + } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/cppInterpreter_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/cppInterpreter_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,981 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_cppInterpreter_zero.cpp.incl" + +#ifdef CC_INTERP + +#define fixup_after_potential_safepoint() \ + method = istate->method() + +#define CALL_VM_NOCHECK(func) \ + thread->set_last_Java_frame(); \ + func; \ + thread->reset_last_Java_frame(); \ + fixup_after_potential_safepoint() + +void CppInterpreter::normal_entry(methodOop method, intptr_t UNUSED, TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + ZeroStack *stack = thread->zero_stack(); + + // Adjust the caller's stack frame to accomodate any additional + // local variables we have contiguously with our parameters. + int extra_locals = method->max_locals() - method->size_of_parameters(); + if (extra_locals > 0) { + if (extra_locals > stack->available_words()) { + Unimplemented(); + } + for (int i = 0; i < extra_locals; i++) + stack->push(0); + } + + // Allocate and initialize our frame. + InterpreterFrame *frame = InterpreterFrame::build(stack, method, thread); + thread->push_zero_frame(frame); + + // Execute those bytecodes! + main_loop(0, THREAD); +} + +void CppInterpreter::main_loop(int recurse, TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + ZeroStack *stack = thread->zero_stack(); + + // If we are entering from a deopt we may need to call + // ourself a few times in order to get to our frame. + if (recurse) + main_loop(recurse - 1, THREAD); + + InterpreterFrame *frame = thread->top_zero_frame()->as_interpreter_frame(); + interpreterState istate = frame->interpreter_state(); + methodOop method = istate->method(); + + intptr_t *result = NULL; + int result_slots = 0; + + // Check we're not about to run out of stack + if (stack_overflow_imminent(thread)) { + CALL_VM_NOCHECK(InterpreterRuntime::throw_StackOverflowError(thread)); + goto unwind_and_return; + } + + while (true) { + // We can set up the frame anchor with everything we want at + // this point as we are thread_in_Java and no safepoints can + // occur until we go to vm mode. We do have to clear flags + // on return from vm but that is it. + thread->set_last_Java_frame(); + + // Call the interpreter + if (JvmtiExport::can_post_interpreter_events()) + BytecodeInterpreter::runWithChecks(istate); + else + BytecodeInterpreter::run(istate); + fixup_after_potential_safepoint(); + + // Clear the frame anchor + thread->reset_last_Java_frame(); + + // Examine the message from the interpreter to decide what to do + if (istate->msg() == BytecodeInterpreter::call_method) { + methodOop callee = istate->callee(); + + // Trim back the stack to put the parameters at the top + stack->set_sp(istate->stack() + 1); + + // Make the call + Interpreter::invoke_method(callee, istate->callee_entry_point(), THREAD); + fixup_after_potential_safepoint(); + + // Convert the result + istate->set_stack(stack->sp() - 1); + + // Restore the stack + stack->set_sp(istate->stack_limit() + 1); + + // Resume the interpreter + istate->set_msg(BytecodeInterpreter::method_resume); + } + else if (istate->msg() == BytecodeInterpreter::more_monitors) { + int monitor_words = frame::interpreter_frame_monitor_size(); + + // Allocate the space + if (monitor_words > stack->available_words()) { + Unimplemented(); + } + stack->alloc(monitor_words * wordSize); + + // Move the expression stack contents + for (intptr_t *p = istate->stack() + 1; p < istate->stack_base(); p++) + *(p - monitor_words) = *p; + + // Move the expression stack pointers + istate->set_stack_limit(istate->stack_limit() - monitor_words); + istate->set_stack(istate->stack() - monitor_words); + istate->set_stack_base(istate->stack_base() - monitor_words); + + // Zero the new monitor so the interpreter can find it. + ((BasicObjectLock *) istate->stack_base())->set_obj(NULL); + + // Resume the interpreter + istate->set_msg(BytecodeInterpreter::got_monitors); + } + else if (istate->msg() == BytecodeInterpreter::return_from_method) { + // Copy the result into the caller's frame + result_slots = type2size[result_type_of(method)]; + assert(result_slots >= 0 && result_slots <= 2, "what?"); + result = istate->stack() + result_slots; + break; + } + else if (istate->msg() == BytecodeInterpreter::throwing_exception) { + assert(HAS_PENDING_EXCEPTION, "should do"); + break; + } + else if (istate->msg() == BytecodeInterpreter::do_osr) { + // Unwind the current frame + thread->pop_zero_frame(); + + // Remove any extension of the previous frame + int extra_locals = method->max_locals() - method->size_of_parameters(); + stack->set_sp(stack->sp() + extra_locals); + + // Jump into the OSR method + Interpreter::invoke_osr( + method, istate->osr_entry(), istate->osr_buf(), THREAD); + return; + } + else { + ShouldNotReachHere(); + } + } + + unwind_and_return: + + // Unwind the current frame + thread->pop_zero_frame(); + + // Pop our local variables + stack->set_sp(stack->sp() + method->max_locals()); + + // Push our result + for (int i = 0; i < result_slots; i++) + stack->push(result[-i]); +} + +void CppInterpreter::native_entry(methodOop method, intptr_t UNUSED, TRAPS) { + // Make sure method is native and not abstract + assert(method->is_native() && !method->is_abstract(), "should be"); + + JavaThread *thread = (JavaThread *) THREAD; + ZeroStack *stack = thread->zero_stack(); + + // Allocate and initialize our frame + InterpreterFrame *frame = InterpreterFrame::build(stack, method, thread); + thread->push_zero_frame(frame); + interpreterState istate = frame->interpreter_state(); + intptr_t *locals = istate->locals(); + + // Check we're not about to run out of stack + if (stack_overflow_imminent(thread)) { + CALL_VM_NOCHECK(InterpreterRuntime::throw_StackOverflowError(thread)); + goto unwind_and_return; + } + + // Update the invocation counter + if ((UseCompiler || CountCompiledCalls) && !method->is_synchronized()) { + thread->set_do_not_unlock(); + InvocationCounter *counter = method->invocation_counter(); + counter->increment(); + if (counter->reached_InvocationLimit()) { + CALL_VM_NOCHECK( + InterpreterRuntime::frequency_counter_overflow(thread, NULL)); + if (HAS_PENDING_EXCEPTION) + goto unwind_and_return; + } + thread->clr_do_not_unlock(); + } + + // Lock if necessary + BasicObjectLock *monitor; + monitor = NULL; + if (method->is_synchronized()) { + monitor = (BasicObjectLock*) istate->stack_base(); + oop lockee = monitor->obj(); + markOop disp = lockee->mark()->set_unlocked(); + + monitor->lock()->set_displaced_header(disp); + if (Atomic::cmpxchg_ptr(monitor, lockee->mark_addr(), disp) != disp) { + if (thread->is_lock_owned((address) disp->clear_lock_bits())) { + monitor->lock()->set_displaced_header(NULL); + } + else { + CALL_VM_NOCHECK(InterpreterRuntime::monitorenter(thread, monitor)); + if (HAS_PENDING_EXCEPTION) + goto unwind_and_return; + } + } + } + + // Get the signature handler + InterpreterRuntime::SignatureHandler *handler; { + address handlerAddr = method->signature_handler(); + if (handlerAddr == NULL) { + CALL_VM_NOCHECK(InterpreterRuntime::prepare_native_call(thread, method)); + if (HAS_PENDING_EXCEPTION) + goto unlock_unwind_and_return; + + handlerAddr = method->signature_handler(); + assert(handlerAddr != NULL, "eh?"); + } + if (handlerAddr == (address) InterpreterRuntime::slow_signature_handler) { + CALL_VM_NOCHECK(handlerAddr = + InterpreterRuntime::slow_signature_handler(thread, method, NULL,NULL)); + if (HAS_PENDING_EXCEPTION) + goto unlock_unwind_and_return; + } + handler = \ + InterpreterRuntime::SignatureHandler::from_handlerAddr(handlerAddr); + } + + // Get the native function entry point + address function; + function = method->native_function(); + assert(function != NULL, "should be set if signature handler is"); + + // Build the argument list + if (handler->argument_count() * 2 > stack->available_words()) { + Unimplemented(); + } + void **arguments; + void *mirror; { + arguments = + (void **) stack->alloc(handler->argument_count() * sizeof(void **)); + void **dst = arguments; + + void *env = thread->jni_environment(); + *(dst++) = &env; + + if (method->is_static()) { + istate->set_oop_temp( + method->constants()->pool_holder()->klass_part()->java_mirror()); + mirror = istate->oop_temp_addr(); + *(dst++) = &mirror; + } + + intptr_t *src = locals; + for (int i = dst - arguments; i < handler->argument_count(); i++) { + ffi_type *type = handler->argument_type(i); + if (type == &ffi_type_pointer) { + if (*src) { + stack->push((intptr_t) src); + *(dst++) = stack->sp(); + } + else { + *(dst++) = src; + } + src--; + } + else if (type->size == 4) { + *(dst++) = src--; + } + else if (type->size == 8) { + src--; + *(dst++) = src--; + } + else { + ShouldNotReachHere(); + } + } + } + + // Set up the Java frame anchor + thread->set_last_Java_frame(); + + // Change the thread state to _thread_in_native + ThreadStateTransition::transition_from_java(thread, _thread_in_native); + + // Make the call + intptr_t result[4 - LogBytesPerWord]; + ffi_call(handler->cif(), (void (*)()) function, result, arguments); + + // Change the thread state back to _thread_in_Java. + // ThreadStateTransition::transition_from_native() cannot be used + // here because it does not check for asynchronous exceptions. + // We have to manage the transition ourself. + thread->set_thread_state(_thread_in_native_trans); + + // Make sure new state is visible in the GC thread + if (os::is_MP()) { + if (UseMembar) { + OrderAccess::fence(); + } + else { + InterfaceSupport::serialize_memory(thread); + } + } + + // Handle safepoint operations, pending suspend requests, + // and pending asynchronous exceptions. + if (SafepointSynchronize::do_call_back() || + thread->has_special_condition_for_native_trans()) { + JavaThread::check_special_condition_for_native_trans(thread); + CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops()); + } + + // Finally we can change the thread state to _thread_in_Java. + thread->set_thread_state(_thread_in_Java); + fixup_after_potential_safepoint(); + + // Clear the frame anchor + thread->reset_last_Java_frame(); + + // If the result was an oop then unbox it and store it in + // oop_temp where the garbage collector can see it before + // we release the handle it might be protected by. + if (handler->result_type() == &ffi_type_pointer) { + if (result[0]) + istate->set_oop_temp(*(oop *) result[0]); + else + istate->set_oop_temp(NULL); + } + + // Reset handle block + thread->active_handles()->clear(); + + unlock_unwind_and_return: + + // Unlock if necessary + if (monitor) { + BasicLock *lock = monitor->lock(); + markOop header = lock->displaced_header(); + oop rcvr = monitor->obj(); + monitor->set_obj(NULL); + + if (header != NULL) { + if (Atomic::cmpxchg_ptr(header, rcvr->mark_addr(), lock) != lock) { + monitor->set_obj(rcvr); { + HandleMark hm(thread); + CALL_VM_NOCHECK(InterpreterRuntime::monitorexit(thread, monitor)); + } + } + } + } + + unwind_and_return: + + // Unwind the current activation + thread->pop_zero_frame(); + + // Pop our parameters + stack->set_sp(stack->sp() + method->size_of_parameters()); + + // Push our result + if (!HAS_PENDING_EXCEPTION) { + BasicType type = result_type_of(method); + stack->set_sp(stack->sp() - type2size[type]); + + switch (type) { + case T_VOID: + break; + + case T_BOOLEAN: +#ifndef VM_LITTLE_ENDIAN + result[0] <<= (BitsPerWord - BitsPerByte); +#endif + SET_LOCALS_INT(*(jboolean *) result != 0, 0); + break; + + case T_CHAR: +#ifndef VM_LITTLE_ENDIAN + result[0] <<= (BitsPerWord - BitsPerShort); +#endif + SET_LOCALS_INT(*(jchar *) result, 0); + break; + + case T_BYTE: +#ifndef VM_LITTLE_ENDIAN + result[0] <<= (BitsPerWord - BitsPerByte); +#endif + SET_LOCALS_INT(*(jbyte *) result, 0); + break; + + case T_SHORT: +#ifndef VM_LITTLE_ENDIAN + result[0] <<= (BitsPerWord - BitsPerShort); +#endif + SET_LOCALS_INT(*(jshort *) result, 0); + break; + + case T_INT: +#ifndef VM_LITTLE_ENDIAN + result[0] <<= (BitsPerWord - BitsPerInt); +#endif + SET_LOCALS_INT(*(jint *) result, 0); + break; + + case T_LONG: + SET_LOCALS_LONG(*(jlong *) result, 0); + break; + + case T_FLOAT: + SET_LOCALS_FLOAT(*(jfloat *) result, 0); + break; + + case T_DOUBLE: + SET_LOCALS_DOUBLE(*(jdouble *) result, 0); + break; + + case T_OBJECT: + case T_ARRAY: + SET_LOCALS_OBJECT(istate->oop_temp(), 0); + break; + + default: + ShouldNotReachHere(); + } + } +} + +void CppInterpreter::accessor_entry(methodOop method, intptr_t UNUSED, TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + ZeroStack *stack = thread->zero_stack(); + intptr_t *locals = stack->sp(); + + // Drop into the slow path if we need a safepoint check + if (SafepointSynchronize::do_call_back()) { + normal_entry(method, 0, THREAD); + return; + } + + // Load the object pointer and drop into the slow path + // if we have a NullPointerException + oop object = LOCALS_OBJECT(0); + if (object == NULL) { + normal_entry(method, 0, THREAD); + return; + } + + // Read the field index from the bytecode, which looks like this: + // 0: aload_0 + // 1: getfield + // 2: index + // 3: index + // 4: ireturn/areturn + // NB this is not raw bytecode: index is in machine order + u1 *code = method->code_base(); + assert(code[0] == Bytecodes::_aload_0 && + code[1] == Bytecodes::_getfield && + (code[4] == Bytecodes::_ireturn || + code[4] == Bytecodes::_areturn), "should do"); + u2 index = Bytes::get_native_u2(&code[2]); + + // Get the entry from the constant pool cache, and drop into + // the slow path if it has not been resolved + constantPoolCacheOop cache = method->constants()->cache(); + ConstantPoolCacheEntry* entry = cache->entry_at(index); + if (!entry->is_resolved(Bytecodes::_getfield)) { + normal_entry(method, 0, THREAD); + return; + } + + // Get the result and push it onto the stack + switch (entry->flag_state()) { + case ltos: + case dtos: + if (stack->available_words() < 1) { + Unimplemented(); + } + stack->alloc(wordSize); + break; + } + if (entry->is_volatile()) { + switch (entry->flag_state()) { + case ctos: + SET_LOCALS_INT(object->char_field_acquire(entry->f2()), 0); + break; + + case btos: + SET_LOCALS_INT(object->byte_field_acquire(entry->f2()), 0); + break; + + case stos: + SET_LOCALS_INT(object->short_field_acquire(entry->f2()), 0); + break; + + case itos: + SET_LOCALS_INT(object->int_field_acquire(entry->f2()), 0); + break; + + case ltos: + SET_LOCALS_LONG(object->long_field_acquire(entry->f2()), 0); + break; + + case ftos: + SET_LOCALS_FLOAT(object->float_field_acquire(entry->f2()), 0); + break; + + case dtos: + SET_LOCALS_DOUBLE(object->double_field_acquire(entry->f2()), 0); + break; + + case atos: + SET_LOCALS_OBJECT(object->obj_field_acquire(entry->f2()), 0); + break; + + default: + ShouldNotReachHere(); + } + } + else { + switch (entry->flag_state()) { + case ctos: + SET_LOCALS_INT(object->char_field(entry->f2()), 0); + break; + + case btos: + SET_LOCALS_INT(object->byte_field(entry->f2()), 0); + break; + + case stos: + SET_LOCALS_INT(object->short_field(entry->f2()), 0); + break; + + case itos: + SET_LOCALS_INT(object->int_field(entry->f2()), 0); + break; + + case ltos: + SET_LOCALS_LONG(object->long_field(entry->f2()), 0); + break; + + case ftos: + SET_LOCALS_FLOAT(object->float_field(entry->f2()), 0); + break; + + case dtos: + SET_LOCALS_DOUBLE(object->double_field(entry->f2()), 0); + break; + + case atos: + SET_LOCALS_OBJECT(object->obj_field(entry->f2()), 0); + break; + + default: + ShouldNotReachHere(); + } + } +} + +void CppInterpreter::empty_entry(methodOop method, intptr_t UNUSED, TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + ZeroStack *stack = thread->zero_stack(); + + // Drop into the slow path if we need a safepoint check + if (SafepointSynchronize::do_call_back()) { + normal_entry(method, 0, THREAD); + return; + } + + // Pop our parameters + stack->set_sp(stack->sp() + method->size_of_parameters()); +} + +bool CppInterpreter::stack_overflow_imminent(JavaThread *thread) { + // How is the ABI stack? + address stack_top = thread->stack_base() - thread->stack_size(); + int free_stack = os::current_stack_pointer() - stack_top; + if (free_stack < StackShadowPages * os::vm_page_size()) { + return true; + } + + // How is the Zero stack? + // Throwing a StackOverflowError involves a VM call, which means + // we need a frame on the stack. We should be checking here to + // ensure that methods we call have enough room to install the + // largest possible frame, but that's more than twice the size + // of the entire Zero stack we get by default, so we just check + // we have *some* space instead... + free_stack = thread->zero_stack()->available_words() * wordSize; + if (free_stack < StackShadowPages * os::vm_page_size()) { + return true; + } + + return false; +} + +InterpreterFrame *InterpreterFrame::build(ZeroStack* stack, + const methodOop method, + JavaThread* thread) { + int monitor_words = + method->is_synchronized() ? frame::interpreter_frame_monitor_size() : 0; + int stack_words = method->is_native() ? 0 : method->max_stack(); + + if (header_words + monitor_words + stack_words > stack->available_words()) { + Unimplemented(); + } + + intptr_t *locals; + if (method->is_native()) + locals = stack->sp() + (method->size_of_parameters() - 1); + else + locals = stack->sp() + (method->max_locals() - 1); + + stack->push(0); // next_frame, filled in later + intptr_t *fp = stack->sp(); + assert(fp - stack->sp() == next_frame_off, "should be"); + + stack->push(INTERPRETER_FRAME); + assert(fp - stack->sp() == frame_type_off, "should be"); + + interpreterState istate = + (interpreterState) stack->alloc(sizeof(BytecodeInterpreter)); + assert(fp - stack->sp() == istate_off, "should be"); + + istate->set_locals(locals); + istate->set_method(method); + istate->set_self_link(istate); + istate->set_prev_link(NULL); + istate->set_thread(thread); + istate->set_bcp(method->is_native() ? NULL : method->code_base()); + istate->set_constants(method->constants()->cache()); + istate->set_msg(BytecodeInterpreter::method_entry); + istate->set_oop_temp(NULL); + istate->set_mdx(NULL); + istate->set_callee(NULL); + + istate->set_monitor_base((BasicObjectLock *) stack->sp()); + if (method->is_synchronized()) { + BasicObjectLock *monitor = + (BasicObjectLock *) stack->alloc(monitor_words * wordSize); + oop object; + if (method->is_static()) + object = method->constants()->pool_holder()->klass_part()->java_mirror(); + else + object = (oop) locals[0]; + monitor->set_obj(object); + } + + istate->set_stack_base(stack->sp()); + istate->set_stack(stack->sp() - 1); + if (stack_words) + stack->alloc(stack_words * wordSize); + istate->set_stack_limit(stack->sp() - 1); + + return (InterpreterFrame *) fp; +} + +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; +} + +BasicType CppInterpreter::result_type_of(methodOop method) { + BasicType t; + switch (method->result_index()) { + case 0 : t = T_BOOLEAN; break; + case 1 : t = T_CHAR; break; + case 2 : t = T_BYTE; break; + case 3 : t = T_SHORT; break; + case 4 : t = T_INT; break; + case 5 : t = T_LONG; break; + case 6 : t = T_VOID; break; + case 7 : t = T_FLOAT; break; + case 8 : t = T_DOUBLE; break; + case 9 : t = T_OBJECT; break; + default: ShouldNotReachHere(); + } + assert(AbstractInterpreter::BasicType_as_index(t) == method->result_index(), + "out of step with AbstractInterpreter::BasicType_as_index"); + return t; +} + +address InterpreterGenerator::generate_empty_entry() { + if (!UseFastEmptyMethods) + return NULL; + + return generate_entry((address) CppInterpreter::empty_entry); +} + +address InterpreterGenerator::generate_accessor_entry() { + if (!UseFastAccessorMethods) + return NULL; + + return generate_entry((address) CppInterpreter::accessor_entry); +} + +address InterpreterGenerator::generate_native_entry(bool synchronized) { + assert(synchronized == false, "should be"); + + return generate_entry((address) CppInterpreter::native_entry); +} + +address InterpreterGenerator::generate_normal_entry(bool synchronized) { + assert(synchronized == false, "should be"); + + return generate_entry((address) CppInterpreter::normal_entry); +} + +address AbstractInterpreterGenerator::generate_method_entry( + AbstractInterpreter::MethodKind kind) { + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals: + case Interpreter::zerolocals_synchronized: + break; + + case Interpreter::native: + entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); + break; + + case Interpreter::native_synchronized: + entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); + 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::method_handle: + entry_point = ((InterpreterGenerator*) this)->generate_method_handle_entry(); + break; + + case Interpreter::java_lang_math_sin: + case Interpreter::java_lang_math_cos: + case Interpreter::java_lang_math_tan: + case Interpreter::java_lang_math_abs: + case Interpreter::java_lang_math_log: + case Interpreter::java_lang_math_log10: + case Interpreter::java_lang_math_sqrt: + entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); + break; + + default: + ShouldNotReachHere(); + } + + if (entry_point == NULL) + entry_point = ((InterpreterGenerator*) this)->generate_normal_entry(false); + + return entry_point; +} + +InterpreterGenerator::InterpreterGenerator(StubQueue* code) + : CppInterpreterGenerator(code) { + generate_all(); +} + +// Deoptimization helpers + +InterpreterFrame *InterpreterFrame::build(ZeroStack* stack, int size) { + int size_in_words = size >> LogBytesPerWord; + assert(size_in_words * wordSize == size, "unaligned"); + assert(size_in_words >= header_words, "too small"); + + if (size_in_words > stack->available_words()) { + Unimplemented(); + } + + stack->push(0); // next_frame, filled in later + intptr_t *fp = stack->sp(); + assert(fp - stack->sp() == next_frame_off, "should be"); + + stack->push(INTERPRETER_FRAME); + assert(fp - stack->sp() == frame_type_off, "should be"); + + interpreterState istate = + (interpreterState) stack->alloc(sizeof(BytecodeInterpreter)); + assert(fp - stack->sp() == istate_off, "should be"); + istate->set_self_link(NULL); // mark invalid + + stack->alloc((size_in_words - header_words) * wordSize); + + return (InterpreterFrame *) fp; +} + +int AbstractInterpreter::layout_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + frame* caller, + frame* interpreter_frame, + bool is_top_frame) { + assert(popframe_extra_args == 0, "what to do?"); + assert(!is_top_frame || (!callee_locals && !callee_param_count), + "top frame should have no caller") + + // This code must exactly match what InterpreterFrame::build + // 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. + // + // 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->sp() + 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; +} + +void BytecodeInterpreter::layout_interpreterState(interpreterState istate, + frame* caller, + frame* current, + methodOop method, + intptr_t* locals, + intptr_t* stack, + intptr_t* stack_base, + intptr_t* monitor_base, + intptr_t* frame_bottom, + bool is_top_frame) { + istate->set_locals(locals); + istate->set_method(method); + istate->set_self_link(istate); + istate->set_prev_link(NULL); + // thread will be set by a hacky repurposing of frame::patch_pc() + // bcp will be set by vframeArrayElement::unpack_on_stack() + istate->set_constants(method->constants()->cache()); + istate->set_msg(BytecodeInterpreter::method_resume); + istate->set_bcp_advance(0); + istate->set_oop_temp(NULL); + istate->set_mdx(NULL); + if (caller->is_interpreted_frame()) { + interpreterState prev = caller->get_interpreterState(); + prev->set_callee(method); + if (*prev->bcp() == Bytecodes::_invokeinterface) + prev->set_bcp_advance(5); + else + prev->set_bcp_advance(3); + } + istate->set_callee(NULL); + istate->set_monitor_base((BasicObjectLock *) monitor_base); + istate->set_stack_base(stack_base); + istate->set_stack(stack); + istate->set_stack_limit(stack_base - method->max_stack() - 1); +} + +address CppInterpreter::return_entry(TosState state, int length) { + ShouldNotCallThis(); +} + +address CppInterpreter::deopt_entry(TosState state, int length) { + return NULL; +} + +// Helper for (runtime) stack overflow checks + +int AbstractInterpreter::size_top_interpreter_activation(methodOop method) { + return 0; +} + +// Helper for figuring out if frames are interpreter frames + +bool CppInterpreter::contains(address pc) { +#ifdef PRODUCT + ShouldNotCallThis(); +#else + return false; // make frame::print_value_on work +#endif // !PRODUCT +} + +// Result handlers and convertors + +address CppInterpreterGenerator::generate_result_handler_for( + BasicType type) { + assembler()->advance(1); + return ShouldNotCallThisStub(); +} + +address CppInterpreterGenerator::generate_tosca_to_stack_converter( + BasicType type) { + assembler()->advance(1); + return ShouldNotCallThisStub(); +} + +address CppInterpreterGenerator::generate_stack_to_stack_converter( + BasicType type) { + assembler()->advance(1); + return ShouldNotCallThisStub(); +} + +address CppInterpreterGenerator::generate_stack_to_native_abi_converter( + BasicType type) { + assembler()->advance(1); + return ShouldNotCallThisStub(); +} + +#endif // CC_INTERP diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/cppInterpreter_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/cppInterpreter_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,47 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + protected: + // Size of interpreter code + const static int InterpreterCodeSize = 6 * K; + + public: + // Method entries + static void normal_entry(methodOop method, intptr_t UNUSED, TRAPS); + static void native_entry(methodOop method, intptr_t UNUSED, TRAPS); + static void accessor_entry(methodOop method, intptr_t UNUSED, TRAPS); + static void empty_entry(methodOop method, intptr_t UNUSED, TRAPS); + + public: + // Main loop of normal_entry + static void main_loop(int recurse, TRAPS); + + private: + // Stack overflow checks + static bool stack_overflow_imminent(JavaThread *thread); + + private: + // Fast result type determination + static BasicType result_type_of(methodOop method); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/debug_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/debug_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_debug_zero.cpp.incl" + +void pd_ps(frame f) { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/depChecker_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/depChecker_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/depChecker_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/depChecker_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/disassembler_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/disassembler_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/disassembler_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/disassembler_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// The disassembler prints out zero code annotated +// with Java specific information. + + static int pd_instruction_alignment() { + ShouldNotCallThis(); + } + + static const char* pd_cpu_opts() { + ShouldNotCallThis(); + } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/dump_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/dump_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_dump_zero.cpp.incl" + +void CompactingPermGenGen::generate_vtable_methods(void** vtbl_list, + void** vtable, + char** md_top, + char* md_end, + char** mc_top, + char* mc_end) { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/entryFrame_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/entryFrame_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// | ... | +// +--------------------+ ------------------ +// | parameter n-1 | low addresses +// | ... | +// | parameter 0 | +// | call_wrapper | +// | frame_type | +// | next_frame | high addresses +// +--------------------+ ------------------ +// | ... | + +class EntryFrame : public ZeroFrame { + private: + EntryFrame() : ZeroFrame() { + ShouldNotCallThis(); + } + + protected: + enum Layout { + call_wrapper_off = jf_header_words, + header_words + }; + + public: + static EntryFrame *build(ZeroStack* stack, + const intptr_t* parameters, + int parameter_words, + JavaCallWrapper* call_wrapper); + public: + JavaCallWrapper *call_wrapper() const { + return (JavaCallWrapper *) value_of_word(call_wrapper_off); + } + + public: + void identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const; +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/entry_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/entry_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ZeroEntry { + public: + ZeroEntry() { + ShouldNotCallThis(); + } + + private: + address _entry_point; + + public: + address entry_point() const { + return _entry_point; + } + void set_entry_point(address entry_point) { + _entry_point = entry_point; + } + + private: + typedef void (*NormalEntryFunc)(methodOop method, + intptr_t base_pc, + TRAPS); + typedef void (*OSREntryFunc)(methodOop method, + address osr_buf, + intptr_t base_pc, + TRAPS); + + public: + void invoke(methodOop method, TRAPS) const { + ((NormalEntryFunc) entry_point())(method, (intptr_t) this, THREAD); + } + void invoke_osr(methodOop method, address osr_buf, TRAPS) const { + ((OSREntryFunc) entry_point())(method, osr_buf, (intptr_t) this, THREAD); + } + + public: + static ByteSize entry_point_offset() { + return byte_offset_of(ZeroEntry, _entry_point); + } +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/fakeStubFrame_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/fakeStubFrame_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,53 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// | ... | +// +--------------------+ ------------------ +// | frame_type | low addresses +// | next_frame | high addresses +// +--------------------+ ------------------ +// | ... | + +class FakeStubFrame : public ZeroFrame { + private: + FakeStubFrame() : ZeroFrame() { + ShouldNotCallThis(); + } + + protected: + enum Layout { + header_words = jf_header_words + }; + + public: + static FakeStubFrame *build(ZeroStack* stack); + + public: + void identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const {} +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/frame_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/frame_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,397 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_frame_zero.cpp.incl" + +#ifdef ASSERT +void RegisterMap::check_location_valid() { + ShouldNotCallThis(); +} +#endif + +bool frame::is_interpreted_frame() const { + return zeroframe()->is_interpreter_frame(); +} + +frame frame::sender_for_entry_frame(RegisterMap *map) const { + assert(zeroframe()->is_entry_frame(), "wrong type of frame"); + assert(map != NULL, "map must be set"); + assert(!entry_frame_is_first(), "next Java fp must be non zero"); + assert(entry_frame_call_wrapper()->anchor()->last_Java_sp() == sender_sp(), + "sender should be next Java frame"); + map->clear(); + assert(map->include_argument_oops(), "should be set by clear"); + return frame(sender_sp(), sp() + 1); +} + +frame frame::sender_for_nonentry_frame(RegisterMap *map) const { + assert(zeroframe()->is_interpreter_frame() || + zeroframe()->is_shark_frame() || + zeroframe()->is_fake_stub_frame(), "wrong type of frame"); + return frame(sender_sp(), sp() + 1); +} + +frame frame::sender(RegisterMap* map) const { + // Default is not to follow arguments; the various + // sender_for_xxx methods update this accordingly. + map->set_include_argument_oops(false); + + if (is_entry_frame()) + return sender_for_entry_frame(map); + else + return sender_for_nonentry_frame(map); +} + +#ifdef CC_INTERP +BasicObjectLock* frame::interpreter_frame_monitor_begin() const { + return get_interpreterState()->monitor_base(); +} + +BasicObjectLock* frame::interpreter_frame_monitor_end() const { + return (BasicObjectLock*) get_interpreterState()->stack_base(); +} +#endif // CC_INTERP + +void frame::patch_pc(Thread* thread, address pc) { + // We borrow this call to set the thread pointer in the interpreter + // state; the hook to set up deoptimized frames isn't supplied it. + assert(pc == NULL, "should be"); + get_interpreterState()->set_thread((JavaThread *) thread); +} + +bool frame::safe_for_sender(JavaThread *thread) { + ShouldNotCallThis(); +} + +void frame::pd_gc_epilog() { +} + +bool frame::is_interpreted_frame_valid(JavaThread *thread) const { + ShouldNotCallThis(); +} + +BasicType frame::interpreter_frame_result(oop* oop_result, + jvalue* value_result) { + assert(is_interpreted_frame(), "interpreted frame expected"); + methodOop method = interpreter_frame_method(); + BasicType type = method->result_type(); + intptr_t* tos_addr = (intptr_t *) interpreter_frame_tos_address(); + oop obj; + + switch (type) { + case T_VOID: + break; + case T_BOOLEAN: + value_result->z = *(jboolean *) tos_addr; + break; + case T_BYTE: + value_result->b = *(jbyte *) tos_addr; + break; + case T_CHAR: + value_result->c = *(jchar *) tos_addr; + break; + case T_SHORT: + value_result->s = *(jshort *) 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_OBJECT: + case T_ARRAY: + if (method->is_native()) { + obj = get_interpreterState()->oop_temp(); + } + else { + oop* obj_p = (oop *) tos_addr; + obj = (obj_p == NULL) ? (oop) NULL : *obj_p; + } + assert(obj == NULL || Universe::heap()->is_in(obj), "sanity check"); + *oop_result = obj; + break; + + default: + ShouldNotReachHere(); + } + + return type; +} + +int frame::frame_size(RegisterMap* map) const { +#ifdef PRODUCT + ShouldNotCallThis(); +#else + return 0; // make javaVFrame::print_value work +#endif // PRODUCT +} + +intptr_t* frame::interpreter_frame_tos_at(jint offset) const { + int index = (Interpreter::expr_offset_in_bytes(offset) / wordSize); + return &interpreter_frame_tos_address()[index]; +} + +void frame::zero_print_on_error(int frame_index, + outputStream* st, + char* buf, + int buflen) const { + // Divide the buffer between the field and the value + buflen >>= 1; + char *fieldbuf = buf; + char *valuebuf = buf + buflen; + + // Print each word of the frame + for (intptr_t *addr = fp(); addr <= sp(); addr++) { + int offset = sp() - addr; + + // Fill in default values, then try and improve them + snprintf(fieldbuf, buflen, "word[%d]", offset); + snprintf(valuebuf, buflen, PTR_FORMAT, *addr); + zeroframe()->identify_word(frame_index, offset, fieldbuf, valuebuf, buflen); + fieldbuf[buflen - 1] = '\0'; + valuebuf[buflen - 1] = '\0'; + + // Print the result + st->print_cr(" " PTR_FORMAT ": %-21s = %s", addr, fieldbuf, valuebuf); + } +} + +void ZeroFrame::identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const { + switch (offset) { + case next_frame_off: + strncpy(fieldbuf, "next_frame", buflen); + break; + + case frame_type_off: + strncpy(fieldbuf, "frame_type", buflen); + if (is_entry_frame()) + strncpy(valuebuf, "ENTRY_FRAME", buflen); + else if (is_interpreter_frame()) + strncpy(valuebuf, "INTERPRETER_FRAME", buflen); + else if (is_shark_frame()) + strncpy(valuebuf, "SHARK_FRAME", buflen); + else if (is_fake_stub_frame()) + strncpy(valuebuf, "FAKE_STUB_FRAME", buflen); + break; + + default: + if (is_entry_frame()) { + as_entry_frame()->identify_word( + frame_index, offset, fieldbuf, valuebuf, buflen); + } + else if (is_interpreter_frame()) { + as_interpreter_frame()->identify_word( + frame_index, offset, fieldbuf, valuebuf, buflen); + } + else if (is_shark_frame()) { + as_shark_frame()->identify_word( + frame_index, offset, fieldbuf, valuebuf, buflen); + } + else if (is_fake_stub_frame()) { + as_fake_stub_frame()->identify_word( + frame_index, offset, fieldbuf, valuebuf, buflen); + } + } +} + +void EntryFrame::identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const { + switch (offset) { + case call_wrapper_off: + strncpy(fieldbuf, "call_wrapper", buflen); + break; + + default: + snprintf(fieldbuf, buflen, "local[%d]", offset - 3); + } +} + +void InterpreterFrame::identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const { + interpreterState istate = interpreter_state(); + bool is_valid = istate->self_link() == istate; + intptr_t *addr = addr_of_word(offset); + + // Fixed part + if (addr >= (intptr_t *) istate) { + const char *field = istate->name_of_field_at_address((address) addr); + if (field) { + if (is_valid && !strcmp(field, "_method")) { + istate->method()->name_and_sig_as_C_string(valuebuf, buflen); + } + else if (is_valid && !strcmp(field, "_bcp") && istate->bcp()) { + snprintf(valuebuf, buflen, PTR_FORMAT " (bci %d)", + (intptr_t) istate->bcp(), + istate->method()->bci_from(istate->bcp())); + } + snprintf(fieldbuf, buflen, "%sistate->%s", + field[strlen(field) - 1] == ')' ? "(": "", field); + } + else if (addr == (intptr_t *) istate) { + strncpy(fieldbuf, "(vtable for istate)", buflen); + } + return; + } + + // Variable part + if (!is_valid) + return; + + // JNI stuff + if (istate->method()->is_native() && addr < istate->stack_base()) { + address hA = istate->method()->signature_handler(); + if (hA != NULL) { + if (hA != (address) InterpreterRuntime::slow_signature_handler) { + InterpreterRuntime::SignatureHandler *handler = + InterpreterRuntime::SignatureHandler::from_handlerAddr(hA); + + intptr_t *params = istate->stack_base() - handler->argument_count(); + if (addr >= params) { + int param = addr - params; + const char *desc = ""; + if (param == 0) + desc = " (JNIEnv)"; + else if (param == 1) { + if (istate->method()->is_static()) + desc = " (mirror)"; + else + desc = " (this)"; + } + snprintf(fieldbuf, buflen, "parameter[%d]%s", param, desc); + return; + } + + for (int i = 0; i < handler->argument_count(); i++) { + if (params[i] == (intptr_t) addr) { + snprintf(fieldbuf, buflen, "unboxed parameter[%d]", i); + return; + } + } + } + } + return; + } + + // Monitors and stack + identify_vp_word(frame_index, addr, + (intptr_t *) istate->monitor_base(), + istate->stack_base(), + fieldbuf, buflen); +} + +void SharkFrame::identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const { + // Fixed part + switch (offset) { + case pc_off: + strncpy(fieldbuf, "pc", buflen); + if (method()->is_oop()) { + nmethod *code = method()->code(); + if (code && code->pc_desc_at(pc())) { + SimpleScopeDesc ssd(code, pc()); + snprintf(valuebuf, buflen, PTR_FORMAT " (bci %d)", + (intptr_t) pc(), ssd.bci()); + } + } + return; + + case unextended_sp_off: + strncpy(fieldbuf, "unextended_sp", buflen); + return; + + case method_off: + strncpy(fieldbuf, "method", buflen); + if (method()->is_oop()) { + method()->name_and_sig_as_C_string(valuebuf, buflen); + } + return; + + case oop_tmp_off: + strncpy(fieldbuf, "oop_tmp", buflen); + return; + } + + // Variable part + if (method()->is_oop()) { + identify_vp_word(frame_index, addr_of_word(offset), + addr_of_word(header_words + 1), + unextended_sp() + method()->max_stack(), + fieldbuf, buflen); + } +} + +void ZeroFrame::identify_vp_word(int frame_index, + intptr_t* addr, + intptr_t* monitor_base, + intptr_t* stack_base, + char* fieldbuf, + int buflen) const { + // Monitors + if (addr >= stack_base && addr < monitor_base) { + int monitor_size = frame::interpreter_frame_monitor_size(); + int last_index = (monitor_base - stack_base) / monitor_size - 1; + int index = last_index - (addr - stack_base) / monitor_size; + intptr_t monitor = (intptr_t) ( + (BasicObjectLock *) monitor_base - 1 - index); + intptr_t offset = (intptr_t) addr - monitor; + + if (offset == BasicObjectLock::obj_offset_in_bytes()) + snprintf(fieldbuf, buflen, "monitor[%d]->_obj", index); + else if (offset == BasicObjectLock::lock_offset_in_bytes()) + snprintf(fieldbuf, buflen, "monitor[%d]->_lock", index); + + return; + } + + // Expression stack + if (addr < stack_base) { + snprintf(fieldbuf, buflen, "%s[%d]", + frame_index == 0 ? "stack_word" : "local", + (int) (stack_base - addr - 1)); + return; + } +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/frame_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/frame_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// A frame represents a physical stack frame on the Zero stack. + + public: + enum { + pc_return_offset = 0 + }; + + // Constructor + public: + frame(intptr_t* sp, intptr_t* fp); + + // The sp of a Zero frame is the address of the highest word in + // that frame. We keep track of the lowest address too, so the + // boundaries of the frame are available for debug printing. + private: + intptr_t* _fp; + + public: + intptr_t* fp() const { + return _fp; + } + +#ifdef CC_INTERP + inline interpreterState get_interpreterState() const; +#endif // CC_INTERP + + public: + const ZeroFrame *zeroframe() const { + return (ZeroFrame *) sp(); + } + + const EntryFrame *zero_entryframe() const { + return zeroframe()->as_entry_frame(); + } + const InterpreterFrame *zero_interpreterframe() const { + return zeroframe()->as_interpreter_frame(); + } + const SharkFrame *zero_sharkframe() const { + return zeroframe()->as_shark_frame(); + } + + public: + frame sender_for_nonentry_frame(RegisterMap* map) const; + + public: + void zero_print_on_error(int index, + outputStream* st, + char* buf, + int buflen) const; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/frame_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/frame_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,151 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Constructors + +inline frame::frame() { + _sp = NULL; + _fp = NULL; + _pc = NULL; + _cb = NULL; + _deopt_state = unknown; +} + +inline frame::frame(intptr_t* sp, intptr_t* fp) { + _sp = sp; + _fp = fp; + switch (zeroframe()->type()) { + case ZeroFrame::ENTRY_FRAME: + _pc = StubRoutines::call_stub_return_pc(); + _cb = NULL; + break; + + case ZeroFrame::INTERPRETER_FRAME: + _pc = NULL; + _cb = NULL; + break; + + case ZeroFrame::SHARK_FRAME: + _pc = zero_sharkframe()->pc(); + _cb = CodeCache::find_blob_unsafe(pc()); + break; + + case ZeroFrame::FAKE_STUB_FRAME: + _pc = NULL; + _cb = NULL; + break; + + default: + ShouldNotReachHere(); + } + _deopt_state = not_deoptimized; +} + +// Accessors + +inline intptr_t* frame::sender_sp() const { + return (intptr_t *) zeroframe()->next(); +} + +inline intptr_t* frame::link() const { + ShouldNotCallThis(); +} + +#ifdef CC_INTERP +inline interpreterState frame::get_interpreterState() const { + return zero_interpreterframe()->interpreter_state(); +} + +inline intptr_t** frame::interpreter_frame_locals_addr() const { + return &(get_interpreterState()->_locals); +} + +inline intptr_t* frame::interpreter_frame_bcx_addr() const { + return (intptr_t*) &(get_interpreterState()->_bcp); +} + +inline constantPoolCacheOop* frame::interpreter_frame_cache_addr() const { + return &(get_interpreterState()->_constants); +} + +inline methodOop* frame::interpreter_frame_method_addr() const { + return &(get_interpreterState()->_method); +} + +inline intptr_t* frame::interpreter_frame_mdx_addr() const { + return (intptr_t*) &(get_interpreterState()->_mdx); +} + +inline intptr_t* frame::interpreter_frame_tos_address() const { + return get_interpreterState()->_stack + 1; +} +#endif // CC_INTERP + +inline int frame::interpreter_frame_monitor_size() { + return BasicObjectLock::size(); +} + +inline intptr_t* frame::interpreter_frame_expression_stack() const { + intptr_t* monitor_end = (intptr_t*) interpreter_frame_monitor_end(); + return monitor_end - 1; +} + +inline jint frame::interpreter_frame_expression_stack_direction() { + return -1; +} + +// Return a 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() const { + return sp(); +} + +inline JavaCallWrapper* frame::entry_frame_call_wrapper() const { + return zero_entryframe()->call_wrapper(); +} + +inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { + ShouldNotCallThis(); +} + +inline oop frame::saved_oop_result(RegisterMap* map) const { + ShouldNotCallThis(); +} + +inline bool frame::is_older(intptr_t* id) const { + ShouldNotCallThis(); +} + +inline intptr_t* frame::entry_frame_argument_at(int offset) const { + ShouldNotCallThis(); +} + +inline intptr_t* frame::unextended_sp() const { + if (zeroframe()->is_shark_frame()) + return zero_sharkframe()->unextended_sp(); + else + return (intptr_t *) -1; +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/globalDefinitions_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/globalDefinitions_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/globals_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/globals_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Set the default values for platform dependent flags used by the +// runtime system. See globals.hpp for details of what they do. + +define_pd_global(bool, ConvertSleepToYield, true); +define_pd_global(bool, ShareVtableStubs, true); +define_pd_global(bool, CountInterpCalls, true); +define_pd_global(bool, NeedsDeoptSuspend, false); + +define_pd_global(bool, ImplicitNullChecks, true); +define_pd_global(bool, UncommonNullCast, true); + +define_pd_global(intx, CodeEntryAlignment, 32); +define_pd_global(intx, InlineFrequencyCount, 100); +define_pd_global(intx, PreInflateSpin, 10); + +define_pd_global(intx, StackYellowPages, 2); +define_pd_global(intx, StackRedPages, 1); +define_pd_global(intx, StackShadowPages, 5 LP64_ONLY(+1) DEBUG_ONLY(+3)); + +define_pd_global(bool, RewriteBytecodes, true); +define_pd_global(bool, RewriteFrequentPairs, true); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/icBuffer_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/icBuffer_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icBuffer_zero.cpp.incl" + +int InlineCacheBuffer::ic_stub_code_size() { + // NB set this once the functions below are implemented + return 4; +} + +void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, + oop cached_oop, + address entry_point) { + // NB ic_stub_code_size() must return the size of the code we generate + ShouldNotCallThis(); +} + +address InlineCacheBuffer::ic_buffer_entry_point(address code_begin) { + // NB ic_stub_code_size() must return the size of the code we generate + ShouldNotCallThis(); +} + +oop InlineCacheBuffer::ic_buffer_cached_oop(address code_begin) { + // NB ic_stub_code_size() must return the size of the code we generate + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/icache_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/icache_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_icache_zero.cpp.incl" + +void ICacheStubGenerator::generate_icache_flush( + ICache::flush_icache_stub_t* flush_icache_stub) { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/icache_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/icache_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Interface for updating the instruction cache. Whenever the VM +// modifies code, part of the processor instruction cache potentially +// has to be flushed. This implementation is empty: Zero never deals +// with code, and LLVM handles cache flushing for Shark. + +class ICache : public AbstractICache { + public: + static void initialize() {} + static void invalidate_word(address addr) {} + static void invalidate_range(address start, int nbytes) {} +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interp_masm_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interp_masm_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interp_masm_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interp_masm_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file specializes the assember with interpreter-specific macros + +class InterpreterMacroAssembler : public MacroAssembler { + public: + InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} + + public: + RegisterOrConstant delayed_value_impl(intptr_t* delayed_value_addr, + Register tmp, + int offset) { + ShouldNotCallThis(); + } +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interpreterFrame_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interpreterFrame_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifdef CC_INTERP +// | ... | +// +--------------------+ ------------------ +// | stack slot n-1 | low addresses +// | ... | +// | stack slot 0 | +// | monitor 0 (maybe) | +// | ... | +// | interpreter state | +// | ... | +// | frame_type | +// | next_frame | high addresses +// +--------------------+ ------------------ +// | ... | + +class InterpreterFrame : public ZeroFrame { + friend class AbstractInterpreter; + + private: + InterpreterFrame() : ZeroFrame() { + ShouldNotCallThis(); + } + + protected: + enum Layout { + istate_off = jf_header_words + + (align_size_up_(sizeof(BytecodeInterpreter), + wordSize) >> LogBytesPerWord) - 1, + header_words + }; + + public: + static InterpreterFrame *build(ZeroStack* stack, + const methodOop method, + JavaThread* thread); + static InterpreterFrame *build(ZeroStack* stack, int size); + + public: + interpreterState interpreter_state() const { + return (interpreterState) addr_of_word(istate_off); + } + + public: + void identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const; +}; +#endif // CC_INTERP diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interpreterGenerator_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interpreterGenerator_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // Generation of Interpreter + // + friend class AbstractInterpreterGenerator; + + private: + address generate_normal_entry(bool synchronized); + address generate_native_entry(bool synchronized); + address generate_abstract_entry(); + address generate_math_entry(AbstractInterpreter::MethodKind kind); + address generate_empty_entry(); + address generate_accessor_entry(); + address generate_method_handle_entry(); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interpreterRT_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interpreterRT_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,162 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreterRT_zero.cpp.incl" + +void InterpreterRuntime::SignatureHandlerGeneratorBase::pass_int() { + push(T_INT); + _cif->nargs++; +} + +void InterpreterRuntime::SignatureHandlerGeneratorBase::pass_long() { + push(T_LONG); + _cif->nargs++; +} + +void InterpreterRuntime::SignatureHandlerGeneratorBase::pass_float() { + push(T_FLOAT); + _cif->nargs++; +} + +void InterpreterRuntime::SignatureHandlerGeneratorBase::pass_double() { + push(T_DOUBLE); + _cif->nargs++; +} + +void InterpreterRuntime::SignatureHandlerGeneratorBase::pass_object() { + push(T_OBJECT); + _cif->nargs++; +} + +void InterpreterRuntime::SignatureHandlerGeneratorBase::push(BasicType type) { + ffi_type *ftype; + switch (type) { + case T_VOID: + ftype = &ffi_type_void; + break; + + case T_BOOLEAN: + ftype = &ffi_type_uint8; + break; + + case T_CHAR: + ftype = &ffi_type_uint16; + break; + + case T_BYTE: + ftype = &ffi_type_sint8; + break; + + case T_SHORT: + ftype = &ffi_type_sint16; + break; + + case T_INT: + ftype = &ffi_type_sint32; + break; + + case T_LONG: + ftype = &ffi_type_sint64; + break; + + case T_FLOAT: + ftype = &ffi_type_float; + break; + + case T_DOUBLE: + ftype = &ffi_type_double; + break; + + case T_OBJECT: + case T_ARRAY: + ftype = &ffi_type_pointer; + break; + + default: + ShouldNotReachHere(); + } + push((intptr_t) ftype); +} + +// For fast signature handlers the "signature handler" is generated +// into a temporary buffer. It is then copied to its final location, +// and pd_set_handler is called on it. We have this two stage thing +// to accomodate this. + +void InterpreterRuntime::SignatureHandlerGeneratorBase::generate( + uint64_t fingerprint) { + + // Build the argument types list + pass_object(); + if (method()->is_static()) + pass_object(); + iterate(fingerprint); + + // Tack on the result type + push(method()->result_type()); +} + +void InterpreterRuntime::SignatureHandler::finalize() { + ffi_status status = + ffi_prep_cif(cif(), + FFI_DEFAULT_ABI, + argument_count(), + result_type(), + argument_types()); + + assert(status == FFI_OK, "should be"); +} + +IRT_ENTRY(address, + InterpreterRuntime::slow_signature_handler(JavaThread* thread, + methodOop method, + intptr_t* unused1, + intptr_t* unused2)) + ZeroStack *stack = thread->zero_stack(); + + int required_words = + (align_size_up(sizeof(ffi_cif), wordSize) >> LogBytesPerWord) + + (method->is_static() ? 2 : 1) + method->size_of_parameters() + 1; + if (required_words > stack->available_words()) { + Unimplemented(); + } + + intptr_t *buf = (intptr_t *) stack->alloc(required_words * wordSize); + SlowSignatureHandlerGenerator sshg(methodHandle(thread, method), buf); + sshg.generate(UCONST64(-1)); + + SignatureHandler *handler = sshg.handler(); + handler->finalize(); + + return (address) handler; +IRT_END + +void SignatureHandlerLibrary::pd_set_handler(address handlerAddr) { + InterpreterRuntime::SignatureHandler *handler = + InterpreterRuntime::SignatureHandler::from_handlerAddr(handlerAddr); + + handler->finalize(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interpreterRT_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interpreterRT_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,127 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class SignatureHandler { + public: + static SignatureHandler *from_handlerAddr(address handlerAddr) { + return (SignatureHandler *) handlerAddr; + } + + public: + ffi_cif* cif() const { + return (ffi_cif *) this; + } + + int argument_count() const { + return cif()->nargs; + } + + ffi_type** argument_types() const { + return (ffi_type**) (cif() + 1); + } + + ffi_type* argument_type(int i) const { + return argument_types()[i]; + } + + ffi_type* result_type() const { + return *(argument_types() + argument_count()); + } + + protected: + friend class InterpreterRuntime; + friend class SignatureHandlerLibrary; + + void finalize(); +}; + +class SignatureHandlerGeneratorBase : public NativeSignatureIterator { + private: + ffi_cif* _cif; + + protected: + SignatureHandlerGeneratorBase(methodHandle method, ffi_cif *cif) + : NativeSignatureIterator(method), _cif(cif) { + _cif->nargs = 0; + } + + ffi_cif *cif() const { + return _cif; + } + + public: + void generate(uint64_t fingerprint); + + private: + void pass_int(); + void pass_long(); + void pass_float(); + void pass_double(); + void pass_object(); + + private: + void push(BasicType type); + virtual void push(intptr_t value) = 0; +}; + +class SignatureHandlerGenerator : public SignatureHandlerGeneratorBase { + private: + CodeBuffer* _cb; + + public: + SignatureHandlerGenerator(methodHandle method, CodeBuffer* buffer) + : SignatureHandlerGeneratorBase(method, (ffi_cif *) buffer->code_end()), + _cb(buffer) { + _cb->set_code_end((address) (cif() + 1)); + } + + private: + void push(intptr_t value) { + intptr_t *dst = (intptr_t *) _cb->code_end(); + _cb->set_code_end((address) (dst + 1)); + *dst = value; + } +}; + +class SlowSignatureHandlerGenerator : public SignatureHandlerGeneratorBase { + private: + intptr_t *_dst; + + public: + SlowSignatureHandlerGenerator(methodHandle method, intptr_t* buf) + : SignatureHandlerGeneratorBase(method, (ffi_cif *) buf) { + _dst = (intptr_t *) (cif() + 1); + } + + private: + void push(intptr_t value) { + *(_dst++) = value; + } + + public: + SignatureHandler *handler() const { + return (SignatureHandler *) cif(); + } +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interpreter_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interpreter_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_interpreter_zero.cpp.incl" + +address AbstractInterpreterGenerator::generate_slow_signature_handler() { + _masm->advance(1); + return (address) InterpreterRuntime::slow_signature_handler; +} + +address InterpreterGenerator::generate_math_entry( + AbstractInterpreter::MethodKind kind) { + if (!InlineIntrinsics) + return NULL; + + Unimplemented(); +} + +address InterpreterGenerator::generate_abstract_entry() { + return ShouldNotCallThisEntry(); +} + +address InterpreterGenerator::generate_method_handle_entry() { + return ShouldNotCallThisEntry(); +} + +bool AbstractInterpreter::can_be_compiled(methodHandle m) { + return true; +} + +int AbstractInterpreter::size_activation(methodOop method, + int tempcount, + int popframe_extra_args, + int moncount, + int callee_param_count, + int callee_locals, + bool is_top_frame) { + return layout_activation(method, + tempcount, + popframe_extra_args, + moncount, + callee_param_count, + callee_locals, + (frame*) NULL, + (frame*) NULL, + is_top_frame); +} + +void Deoptimization::unwind_callee_save_values(frame* f, + vframeArray* vframe_array) { +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/interpreter_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/interpreter_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + public: + static void invoke_method(methodOop method, address entry_point, TRAPS) { + ((ZeroEntry *) entry_point)->invoke(method, THREAD); + } + static void invoke_osr(methodOop method, + address entry_point, + address osr_buf, + TRAPS) { + ((ZeroEntry *) entry_point)->invoke_osr(method, osr_buf, THREAD); + } + + public: + static int expr_index_at(int i) { + return stackElementWords() * i; + } + static int expr_tag_index_at(int i) { + assert(TaggedStackInterpreter, "should not call this"); + Unimplemented(); + } + + static int expr_offset_in_bytes(int i) { + return stackElementSize() * i; + } + static int expr_tag_offset_in_bytes(int i) { + assert(TaggedStackInterpreter, "should not call this"); + Unimplemented(); + } + + static int local_index_at(int i) { + assert(i <= 0, "local direction already negated"); + return stackElementWords() * i + (value_offset_in_bytes() / wordSize); + } + static int local_tag_index_at(int i) { + assert(TaggedStackInterpreter, "should not call this"); + Unimplemented(); + } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/javaFrameAnchor_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/javaFrameAnchor_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + 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) + + void clear() { + // clearing _last_Java_sp must be first + _last_Java_sp = NULL; + // fence? + _last_Java_pc = NULL; + } + + 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; + + _last_Java_pc = src->_last_Java_pc; + // Must be last so profiler will always see valid frame if + // has_last_frame() is true + _last_Java_sp = src->_last_Java_sp; + } + + bool walkable() { + return true; + } + + void make_walkable(JavaThread* thread) { + // nothing to do + } + + intptr_t* last_Java_sp() const { + return _last_Java_sp; + } + + void set_last_Java_sp(intptr_t* sp) { + _last_Java_sp = sp; + } diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/jniFastGetField_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/jniFastGetField_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_jniFastGetField_zero.cpp.incl" + +address JNI_FastGetField::generate_fast_get_boolean_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_byte_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_char_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_short_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_int_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_long_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_float_field() { + return (address) -1; +} + +address JNI_FastGetField::generate_fast_get_double_field() { + return (address) -1; +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/jniTypes_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/jniTypes_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,108 @@ +/* + * Copyright 1998-2002 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// 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_zero.cpp) + // reverse the argument list constructed by JavaCallArguments (see + // javaCalls.hpp). + +private: + // Helper routines. + static inline void put_int2 (jint *from, jint *to) { to[0] = from[0]; to[1] = from[1]; } + static inline void put_int2 (jint *from, jint *to, int& pos) { put_int2 (from, (jint *)((intptr_t *)to + pos)); pos += 2; } + static inline void put_int2r(jint *from, jint *to) { to[0] = from[1]; to[1] = from[0]; } + static inline void put_int2r(jint *from, jint *to, int& pos) { put_int2r(from, (jint *)((intptr_t *)to + pos)); pos += 2; } + +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; } + +#ifdef _LP64 + // 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 + 0) = 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; } +#else + // Longs are stored in reversed native word format in two JavaCallArgument slots at *to. + // The high half is in *(to+1) and the low half in *to. + static inline void put_long(jlong from, intptr_t *to) { put_int2r((jint *)&from, (jint *)to); } + static inline void put_long(jlong from, intptr_t *to, int& pos) { put_int2r((jint *)&from, (jint *)to, pos); } + static inline void put_long(jlong *from, intptr_t *to, int& pos) { put_int2r((jint *) from, (jint *)to, pos); } +#endif + + // 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; } + +#ifdef _LP64 + // 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 + 0) = 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; } +#else + // Doubles are stored in reversed native word format in two JavaCallArgument slots at *to. + static inline void put_double(jdouble from, intptr_t *to) { put_int2r((jint *)&from, (jint *)to); } + static inline void put_double(jdouble from, intptr_t *to, int& pos) { put_int2r((jint *)&from, (jint *)to, pos); } + static inline void put_double(jdouble *from, intptr_t *to, int& pos) { put_int2r((jint *) from, (jint *)to, pos); } +#endif + + // The get_xxx routines, on the other hand, actually _do_ fetch + // java primitive types from the interpreter stack. + static inline jint get_int(intptr_t *from) { return *(jint *)from; } + +#ifdef _LP64 + static inline jlong get_long(intptr_t *from) { return *(jlong *)from; } +#else + static inline jlong get_long(intptr_t *from) { return ((jlong)(*( signed int *)((jint *)from )) << 32) | + ((jlong)(*(unsigned int *)((jint *)from + 1)) << 0); } +#endif + + static inline oop get_obj(intptr_t *from) { return *(oop *)from; } + static inline jfloat get_float(intptr_t *from) { return *(jfloat *)from; } + +#ifdef _LP64 + static inline jdouble get_double(intptr_t *from) { return *(jdouble *)from; } +#else + static inline jdouble get_double(intptr_t *from) { jlong jl = ((jlong)(*( signed int *)((jint *)from )) << 32) | + ((jlong)(*(unsigned int *)((jint *)from + 1)) << 0); + return *(jdouble *)&jl; } +#endif + +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/jni_zero.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/jni_zero.h Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#define JNIEXPORT +#define JNIIMPORT +#define JNICALL + +typedef int jint; +typedef signed char jbyte; + +#ifdef _LP64 +typedef long jlong; +#else +typedef long long jlong; +#endif diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/methodHandles_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/methodHandles_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/nativeInst_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/nativeInst_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_nativeInst_zero.cpp.incl" + +// This method is called by nmethod::make_not_entrant_or_zombie to +// insert a jump to SharedRuntime::get_handle_wrong_method_stub() +// (dest) at the start of a compiled method (verified_entry) to avoid +// a race where a method is invoked while being made non-entrant. +// +// In Shark, verified_entry is a pointer to a SharkEntry. We can +// handle this simply by changing it's entry point to point at the +// interpreter. This only works because the interpreter and Shark +// calling conventions are the same. + +void NativeJump::patch_verified_entry(address entry, + address verified_entry, + address dest) { + assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "should be"); + +#ifdef CC_INTERP + ((ZeroEntry*) verified_entry)->set_entry_point( + (address) CppInterpreter::normal_entry); +#else + Unimplemented(); +#endif // CC_INTERP +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/nativeInst_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/nativeInst_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,185 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// We have interfaces for the following instructions: +// - NativeInstruction +// - - NativeCall +// - - NativeMovConstReg +// - - NativeMovConstRegPatching +// - - NativeJump +// - - NativeIllegalOpCode +// - - NativeReturn +// - - NativeReturnX (return with argument) +// - - NativePushConst +// - - NativeTstRegMem + +// The base class for different kinds of native instruction abstractions. +// Provides the primitive operations to manipulate code relative to this. + +class NativeInstruction VALUE_OBJ_CLASS_SPEC { + public: + bool is_jump() { + ShouldNotCallThis(); + } + + bool is_safepoint_poll() { + ShouldNotCallThis(); + } +}; + +inline NativeInstruction* nativeInstruction_at(address address) { + ShouldNotCallThis(); +} + +class NativeCall : public NativeInstruction { + public: + enum zero_specific_constants { + instruction_size = 0 // not used within the interpreter + }; + + address instruction_address() const { + ShouldNotCallThis(); + } + + address next_instruction_address() const { + ShouldNotCallThis(); + } + + address return_address() const { + ShouldNotCallThis(); + } + + address destination() const { + ShouldNotCallThis(); + } + + void set_destination_mt_safe(address dest) { + ShouldNotCallThis(); + } + + void verify_alignment() { + ShouldNotCallThis(); + } + + void verify() { + ShouldNotCallThis(); + } + + static bool is_call_before(address return_address) { + ShouldNotCallThis(); + } +}; + +inline NativeCall* nativeCall_before(address return_address) { + ShouldNotCallThis(); +} + +inline NativeCall* nativeCall_at(address address) { + ShouldNotCallThis(); +} + +class NativeMovConstReg : public NativeInstruction { + public: + address next_instruction_address() const { + ShouldNotCallThis(); + } + + intptr_t data() const { + ShouldNotCallThis(); + } + + void set_data(intptr_t x) { + ShouldNotCallThis(); + } +}; + +inline NativeMovConstReg* nativeMovConstReg_at(address address) { + ShouldNotCallThis(); +} + +class NativeMovRegMem : public NativeInstruction { + public: + int offset() const { + ShouldNotCallThis(); + } + + void set_offset(intptr_t x) { + ShouldNotCallThis(); + } + + void add_offset_in_bytes(int add_offset) { + ShouldNotCallThis(); + } +}; + +inline NativeMovRegMem* nativeMovRegMem_at(address address) { + ShouldNotCallThis(); +} + +class NativeJump : public NativeInstruction { + public: + enum zero_specific_constants { + instruction_size = 0 // not used within the interpreter + }; + + address jump_destination() const { + ShouldNotCallThis(); + } + + void set_jump_destination(address dest) { + ShouldNotCallThis(); + } + + static void check_verified_entry_alignment(address entry, + address verified_entry) { + } + + static void patch_verified_entry(address entry, + address verified_entry, + address dest); +}; + +inline NativeJump* nativeJump_at(address address) { + ShouldNotCallThis(); +} + +class NativeGeneralJump : public NativeInstruction { + public: + address jump_destination() const { + ShouldNotCallThis(); + } + + static void insert_unconditional(address code_pos, address entry) { + ShouldNotCallThis(); + } + + static void replace_mt_safe(address instr_addr, address code_buffer) { + ShouldNotCallThis(); + } +}; + +inline NativeGeneralJump* nativeGeneralJump_at(address address) { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/registerMap_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/registerMap_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright 1998-2007 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // 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) {} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/register_definitions_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/register_definitions_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/register_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/register_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_register_zero.cpp.incl" + +const int ConcreteRegisterImpl::max_gpr = RegisterImpl::number_of_registers; +const int ConcreteRegisterImpl::max_fpr = + ConcreteRegisterImpl::max_gpr + FloatRegisterImpl::number_of_registers; + +const char* RegisterImpl::name() const { + ShouldNotCallThis(); +} + +const char* FloatRegisterImpl::name() const { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/register_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/register_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VMRegImpl; +typedef VMRegImpl* VMReg; + +// Use Register as shortcut +class RegisterImpl; +typedef RegisterImpl* Register; + +inline Register as_Register(int encoding) { + return (Register)(intptr_t) encoding; +} + +// The implementation of integer registers for the zero architecture +class RegisterImpl : public AbstractRegisterImpl { + public: + enum { + number_of_registers = 0 + }; + + // construction + inline friend Register as_Register(int encoding); + VMReg as_VMReg(); + + // derived registers, offsets, and addresses + Register successor() const { + return as_Register(encoding() + 1); + } + + // accessors + int encoding() const { + assert(is_valid(), "invalid register"); + return (intptr_t)this; + } + bool is_valid() const { + return 0 <= (intptr_t) this && (intptr_t)this < number_of_registers; + } + const char* name() const; +}; + +// Use FloatRegister as shortcut +class FloatRegisterImpl; +typedef FloatRegisterImpl* FloatRegister; + +inline FloatRegister as_FloatRegister(int encoding) { + return (FloatRegister)(intptr_t) encoding; +} + +// The implementation of floating point registers for the zero architecture +class FloatRegisterImpl : public AbstractRegisterImpl { + public: + enum { + number_of_registers = 0 + }; + + // construction + inline friend FloatRegister as_FloatRegister(int encoding); + VMReg as_VMReg(); + + // derived registers, offsets, and addresses + FloatRegister successor() const { + return as_FloatRegister(encoding() + 1); + } + + // accessors + int encoding() const { + assert(is_valid(), "invalid register"); + return (intptr_t)this; + } + bool is_valid() const { + return 0 <= (intptr_t) this && (intptr_t)this < number_of_registers; + } + const char* name() const; +}; + +class ConcreteRegisterImpl : public AbstractRegisterImpl { + public: + enum { + number_of_registers = RegisterImpl::number_of_registers + + FloatRegisterImpl::number_of_registers + }; + + static const int max_gpr; + static const int max_fpr; +}; + +CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/relocInfo_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/relocInfo_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,74 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_relocInfo_zero.cpp.incl" + +void Relocation::pd_set_data_value(address x, intptr_t o) { + ShouldNotCallThis(); +} + +address Relocation::pd_call_destination(address orig_addr) { + ShouldNotCallThis(); +} + +void Relocation::pd_set_call_destination(address x) { + ShouldNotCallThis(); +} + +address Relocation::pd_get_address_from_code() { + ShouldNotCallThis(); +} + +address* Relocation::pd_address_in_code() { + // Relocations in Shark are just stored directly + return (address *) addr(); +} + +int Relocation::pd_breakpoint_size() { + ShouldNotCallThis(); +} + +void Relocation::pd_swap_in_breakpoint(address x, + short* instrs, + int instrlen) { + ShouldNotCallThis(); +} + +void Relocation::pd_swap_out_breakpoint(address x, + short* instrs, + int instrlen) { + ShouldNotCallThis(); +} + +void poll_Relocation::fix_relocation_after_move(const CodeBuffer* src, + CodeBuffer* dst) { + ShouldNotCallThis(); +} + +void poll_return_Relocation::fix_relocation_after_move(const CodeBuffer* src, + CodeBuffer* dst) { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/relocInfo_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/relocInfo_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // machine-dependent parts of class relocInfo + private: + enum { + // these constants mean nothing without an assembler + offset_unit = 1, + format_width = 1 + }; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/sharedRuntime_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/sharedRuntime_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,117 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_sharedRuntime_zero.cpp.incl" + +DeoptimizationBlob *SharedRuntime::_deopt_blob; +SafepointBlob *SharedRuntime::_polling_page_safepoint_handler_blob; +SafepointBlob *SharedRuntime::_polling_page_return_handler_blob; +RuntimeStub *SharedRuntime::_wrong_method_blob; +RuntimeStub *SharedRuntime::_ic_miss_blob; +RuntimeStub *SharedRuntime::_resolve_opt_virtual_call_blob; +RuntimeStub *SharedRuntime::_resolve_virtual_call_blob; +RuntimeStub *SharedRuntime::_resolve_static_call_blob; + +int SharedRuntime::java_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed, + int is_outgoing) { + return 0; +} + +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) { + return AdapterHandlerLibrary::new_entry( + fingerprint, + ShouldNotCallThisStub(), + ShouldNotCallThisStub(), + ShouldNotCallThisStub()); +} + +nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, + methodHandle method, + int total_in_args, + int comp_args_on_stack, + BasicType *in_sig_bt, + VMRegPair *in_regs, + BasicType ret_type) { +#ifdef SHARK + return SharkCompiler::compiler()->generate_native_wrapper(masm, + method, + in_sig_bt, + ret_type); +#else + ShouldNotCallThis(); +#endif // SHARK +} + +int Deoptimization::last_frame_adjust(int callee_parameters, + int callee_locals) { + return 0; +} + +uint SharedRuntime::out_preserve_stack_slots() { + ShouldNotCallThis(); +} + +static RuntimeStub* generate_empty_runtime_stub(const char* name) { + CodeBuffer buffer(name, 0, 0); + return RuntimeStub::new_runtime_stub(name, &buffer, 0, 0, NULL, false); +} + +static SafepointBlob* generate_empty_safepoint_blob() { + CodeBuffer buffer("handler_blob", 0, 0); + return SafepointBlob::create(&buffer, NULL, 0); +} + +void SharedRuntime::generate_stubs() { + _wrong_method_blob = + generate_empty_runtime_stub("wrong_method_stub"); + _ic_miss_blob = + generate_empty_runtime_stub("ic_miss_stub"); + _resolve_opt_virtual_call_blob = + generate_empty_runtime_stub("resolve_opt_virtual_call"); + _resolve_virtual_call_blob = + generate_empty_runtime_stub("resolve_virtual_call"); + _resolve_static_call_blob = + generate_empty_runtime_stub("resolve_static_call"); + + _polling_page_safepoint_handler_blob = + generate_empty_safepoint_blob(); + _polling_page_return_handler_blob = + generate_empty_safepoint_blob(); +} + +int SharedRuntime::c_calling_convention(const BasicType *sig_bt, + VMRegPair *regs, + int total_args_passed) { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/sharkFrame_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/sharkFrame_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// | ... | +// +--------------------+ ------------------ +// | stack slot n-1 | low addresses +// | ... | +// | stack slot 0 | +// | monitor m-1 | +// | ... | +// | monitor 0 | +// | oop_tmp | +// | method | +// | unextended_sp | +// | pc | +// | frame_type | +// | next_frame | high addresses +// +--------------------+ ------------------ +// | ... | + +class SharkFrame : public ZeroFrame { + friend class SharkStack; + + private: + SharkFrame() : ZeroFrame() { + ShouldNotCallThis(); + } + + protected: + enum Layout { + pc_off = jf_header_words, + unextended_sp_off, + method_off, + oop_tmp_off, + header_words + }; + + public: + address pc() const { + return (address) value_of_word(pc_off); + } + + intptr_t* unextended_sp() const { + return (intptr_t *) value_of_word(unextended_sp_off); + } + + methodOop method() const { + return (methodOop) value_of_word(method_off); + } + + public: + void identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const; +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/stack_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/stack_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,197 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class ZeroStack { + private: + intptr_t *_base; // the last available word + intptr_t *_top; // the word past the end of the stack + intptr_t *_sp; // the top word on the stack + + public: + ZeroStack() + : _base(NULL), _top(NULL), _sp(NULL) {} + + bool needs_setup() const { + return _base == NULL; + } + + void setup(void *mem, size_t size) { + assert(needs_setup(), "already set up"); + assert(!(size & WordAlignmentMask), "unaligned"); + + _base = (intptr_t *) mem; + _top = _base + (size >> LogBytesPerWord); + _sp = _top; + } + void teardown() { + assert(!needs_setup(), "not set up"); + assert(_sp == _top, "stuff on stack at teardown"); + + _base = NULL; + _top = NULL; + _sp = NULL; + } + + intptr_t *sp() const { + return _sp; + } + void set_sp(intptr_t *new_sp) { + assert(_top >= new_sp && new_sp >= _base, "bad stack pointer"); + _sp = new_sp; + } + + int available_words() const { + return _sp - _base; + } + + void push(intptr_t value) { + assert(_sp > _base, "stack overflow"); + *(--_sp) = value; + } + intptr_t pop() { + assert(_sp < _top, "stack underflow"); + return *(_sp++); + } + + void *alloc(size_t size) { + int count = align_size_up(size, wordSize) >> LogBytesPerWord; + assert(count <= available_words(), "stack overflow"); + return _sp -= count; + } + + public: + static ByteSize base_offset() { + return byte_offset_of(ZeroStack, _base); + } + static ByteSize top_offset() { + return byte_offset_of(ZeroStack, _top); + } + static ByteSize sp_offset() { + return byte_offset_of(ZeroStack, _sp); + } +}; + + +class EntryFrame; +class InterpreterFrame; +class SharkFrame; +class FakeStubFrame; + +// +// | ... | +// +--------------------+ ------------------ +// | ... | low addresses +// | frame_type | +// | next_frame | high addresses +// +--------------------+ ------------------ +// | ... | + +class ZeroFrame { + friend class frame; + friend class ZeroStackPrinter; + + protected: + ZeroFrame() { + ShouldNotCallThis(); + } + + enum Layout { + next_frame_off, + frame_type_off, + jf_header_words + }; + + enum FrameType { + ENTRY_FRAME = 1, + INTERPRETER_FRAME, + SHARK_FRAME, + FAKE_STUB_FRAME + }; + + protected: + intptr_t *addr_of_word(int offset) const { + return (intptr_t *) this - offset; + } + intptr_t value_of_word(int offset) const { + return *addr_of_word(offset); + } + + public: + ZeroFrame *next() const { + return (ZeroFrame *) value_of_word(next_frame_off); + } + + protected: + FrameType type() const { + return (FrameType) value_of_word(frame_type_off); + } + + public: + bool is_entry_frame() const { + return type() == ENTRY_FRAME; + } + bool is_interpreter_frame() const { + return type() == INTERPRETER_FRAME; + } + bool is_shark_frame() const { + return type() == SHARK_FRAME; + } + bool is_fake_stub_frame() const { + return type() == FAKE_STUB_FRAME; + } + + public: + EntryFrame *as_entry_frame() const { + assert(is_entry_frame(), "should be"); + return (EntryFrame *) this; + } + InterpreterFrame *as_interpreter_frame() const { + assert(is_interpreter_frame(), "should be"); + return (InterpreterFrame *) this; + } + SharkFrame *as_shark_frame() const { + assert(is_shark_frame(), "should be"); + return (SharkFrame *) this; + } + FakeStubFrame *as_fake_stub_frame() const { + assert(is_fake_stub_frame(), "should be"); + return (FakeStubFrame *) this; + } + + public: + void identify_word(int frame_index, + int offset, + char* fieldbuf, + char* valuebuf, + int buflen) const; + + protected: + void identify_vp_word(int frame_index, + intptr_t* addr, + intptr_t* monitor_base, + intptr_t* stack_base, + char* fieldbuf, + int buflen) const; +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/stubGenerator_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/stubGenerator_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,251 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubGenerator_zero.cpp.incl" + +// Declaration and definition of StubGenerator (no .hpp file). +// For a more detailed description of the stub routine structure +// see the comment in stubRoutines.hpp + +class StubGenerator: public StubCodeGenerator { + private: + // The call stub is used to call Java from C + static void call_stub( + JavaCallWrapper *call_wrapper, + intptr_t* result, + BasicType result_type, + methodOop method, + address entry_point, + intptr_t* parameters, + int parameter_words, + TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + ZeroStack *stack = thread->zero_stack(); + + // Make sure we have no pending exceptions + assert(!HAS_PENDING_EXCEPTION, "call_stub called with pending exception"); + + // Set up the stack if necessary + bool stack_needs_teardown = false; + if (stack->needs_setup()) { + size_t stack_used = thread->stack_base() - (address) &stack_used; + size_t stack_free = thread->stack_size() - stack_used; + size_t zero_stack_size = align_size_down(stack_free / 2, wordSize); + + stack->setup(alloca(zero_stack_size), zero_stack_size); + stack_needs_teardown = true; + } + + // Allocate and initialize our frame + thread->push_zero_frame( + EntryFrame::build(stack, parameters, parameter_words, call_wrapper)); + + // Make the call + Interpreter::invoke_method(method, entry_point, THREAD); + + // Store result depending on type + if (!HAS_PENDING_EXCEPTION) { + switch (result_type) { + case T_INT: + *(jint *) result = *(jint *) stack->sp(); + break; + case T_LONG: + *(jlong *) result = *(jlong *) stack->sp(); + break; + case T_FLOAT: + *(jfloat *) result = *(jfloat *) stack->sp(); + break; + case T_DOUBLE: + *(jdouble *) result = *(jdouble *) stack->sp(); + break; + case T_OBJECT: + *(oop *) result = *(oop *) stack->sp(); + break; + default: + ShouldNotReachHere(); + } + } + + // Unwind our frame + thread->pop_zero_frame(); + + // Tear down the stack if necessary + if (stack_needs_teardown) + stack->teardown(); + } + + // These stubs get called from some dumb test routine. + // I'll write them properly when they're called from + // something that's actually doing something. + static void fake_arraycopy_stub(address src, address dst, int count) { + assert(count == 0, "huh?"); + } + + void generate_arraycopy_stubs() { + // Call the conjoint generation methods immediately after + // the disjoint ones so that short branches from the former + // to the latter can be generated. + StubRoutines::_jbyte_disjoint_arraycopy = (address) fake_arraycopy_stub; + StubRoutines::_jbyte_arraycopy = (address) fake_arraycopy_stub; + + StubRoutines::_jshort_disjoint_arraycopy = (address) fake_arraycopy_stub; + StubRoutines::_jshort_arraycopy = (address) fake_arraycopy_stub; + + StubRoutines::_jint_disjoint_arraycopy = (address) fake_arraycopy_stub; + StubRoutines::_jint_arraycopy = (address) fake_arraycopy_stub; + + StubRoutines::_jlong_disjoint_arraycopy = (address) fake_arraycopy_stub; + StubRoutines::_jlong_arraycopy = (address) fake_arraycopy_stub; + + StubRoutines::_oop_disjoint_arraycopy = ShouldNotCallThisStub(); + StubRoutines::_oop_arraycopy = ShouldNotCallThisStub(); + + StubRoutines::_checkcast_arraycopy = ShouldNotCallThisStub(); + StubRoutines::_unsafe_arraycopy = ShouldNotCallThisStub(); + StubRoutines::_generic_arraycopy = ShouldNotCallThisStub(); + + // We don't generate specialized code for HeapWord-aligned source + // arrays, so just use the code we've already generated + StubRoutines::_arrayof_jbyte_disjoint_arraycopy = + StubRoutines::_jbyte_disjoint_arraycopy; + StubRoutines::_arrayof_jbyte_arraycopy = + StubRoutines::_jbyte_arraycopy; + + StubRoutines::_arrayof_jshort_disjoint_arraycopy = + StubRoutines::_jshort_disjoint_arraycopy; + StubRoutines::_arrayof_jshort_arraycopy = + StubRoutines::_jshort_arraycopy; + + StubRoutines::_arrayof_jint_disjoint_arraycopy = + StubRoutines::_jint_disjoint_arraycopy; + StubRoutines::_arrayof_jint_arraycopy = + StubRoutines::_jint_arraycopy; + + StubRoutines::_arrayof_jlong_disjoint_arraycopy = + StubRoutines::_jlong_disjoint_arraycopy; + StubRoutines::_arrayof_jlong_arraycopy = + StubRoutines::_jlong_arraycopy; + + StubRoutines::_arrayof_oop_disjoint_arraycopy = + StubRoutines::_oop_disjoint_arraycopy; + StubRoutines::_arrayof_oop_arraycopy = + StubRoutines::_oop_arraycopy; + } + + 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 = ShouldNotCallThisStub(); + StubRoutines::_call_stub_entry = (address) call_stub; + StubRoutines::_catch_exception_entry = ShouldNotCallThisStub(); + + // atomic calls + StubRoutines::_atomic_xchg_entry = ShouldNotCallThisStub(); + StubRoutines::_atomic_xchg_ptr_entry = ShouldNotCallThisStub(); + StubRoutines::_atomic_cmpxchg_entry = ShouldNotCallThisStub(); + StubRoutines::_atomic_cmpxchg_ptr_entry = ShouldNotCallThisStub(); + StubRoutines::_atomic_cmpxchg_long_entry = ShouldNotCallThisStub(); + StubRoutines::_atomic_add_entry = ShouldNotCallThisStub(); + StubRoutines::_atomic_add_ptr_entry = ShouldNotCallThisStub(); + StubRoutines::_fence_entry = ShouldNotCallThisStub(); + + // amd64 does this here, sparc does it in generate_all() + StubRoutines::_handler_for_unsafe_access_entry = + ShouldNotCallThisStub(); + } + + 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 and need to be relocatable, so they each + // fabricate a RuntimeStub internally. + StubRoutines::_throw_AbstractMethodError_entry = + ShouldNotCallThisStub(); + + StubRoutines::_throw_ArithmeticException_entry = + ShouldNotCallThisStub(); + + StubRoutines::_throw_NullPointerException_entry = + ShouldNotCallThisStub(); + + StubRoutines::_throw_NullPointerException_at_call_entry = + ShouldNotCallThisStub(); + + StubRoutines::_throw_StackOverflowError_entry = + ShouldNotCallThisStub(); + + // support for verify_oop (must happen after universe_init) + StubRoutines::_verify_oop_subroutine_entry = + ShouldNotCallThisStub(); + + // arraycopy stubs used by compilers + generate_arraycopy_stubs(); + } + + public: + StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) { + if (all) { + generate_all(); + } else { + generate_initial(); + } + } +}; + +void StubGenerator_generate(CodeBuffer* code, bool all) { + StubGenerator g(code, all); +} + +EntryFrame *EntryFrame::build(ZeroStack* stack, + const intptr_t* parameters, + int parameter_words, + JavaCallWrapper* call_wrapper) { + if (header_words + parameter_words > stack->available_words()) { + Unimplemented(); + } + + stack->push(0); // next_frame, filled in later + intptr_t *fp = stack->sp(); + assert(fp - stack->sp() == next_frame_off, "should be"); + + stack->push(ENTRY_FRAME); + assert(fp - stack->sp() == frame_type_off, "should be"); + + stack->push((intptr_t) call_wrapper); + assert(fp - stack->sp() == call_wrapper_off, "should be"); + + for (int i = 0; i < parameter_words; i++) + stack->push(parameters[i]); + + return (EntryFrame *) fp; +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/stubRoutines_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/stubRoutines_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stubRoutines_zero.cpp.incl" + +#ifdef IA32 +address StubRoutines::x86::_call_stub_compiled_return = NULL; +#endif // IA32 diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/stubRoutines_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/stubRoutines_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + // This file holds the platform specific parts of the StubRoutines + // definition. See stubRoutines.hpp for a description on how to + // extend it. + + public: + static address call_stub_return_pc() { + return (address) -1; + } + + static bool returns_to_call_stub(address return_pc) { + return return_pc == call_stub_return_pc(); + } + + enum platform_dependent_constants { + code_size1 = 0, // The assembler will fail with a guarantee + code_size2 = 0 // if these are too small. Simply increase + }; // them if that happens. + +#ifdef IA32 + class x86 { + friend class VMStructs; + + private: + static address _call_stub_compiled_return; + }; +#endif // IA32 diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/templateInterpreterGenerator_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/templateInterpreterGenerator_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/templateInterpreter_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/templateInterpreter_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/templateInterpreter_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/templateInterpreter_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/templateTable_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/templateTable_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/templateTable_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/templateTable_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vmStructs_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vmStructs_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ + /* be present there) */ + +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ + /* be present there) */ + +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ + /* be present there) */ + +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + + /* NOTE that we do not use the last_entry() macro here; it is used */ + /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ + /* be present there) */ diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vm_version_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vm_version_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vm_version_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vm_version_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,31 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class VM_Version : public Abstract_VM_Version { + public: + static const char* cpu_features() { + return ""; + } +}; diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vmreg_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vmreg_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,62 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vmreg_zero.cpp.incl" + +void VMRegImpl::set_regName() { + int i = 0; + Register reg = ::as_Register(0); + for ( ; i < ConcreteRegisterImpl::max_gpr ; ) { + regName[i++] = reg->name(); + reg = reg->successor(); + } + FloatRegister freg = ::as_FloatRegister(0); + for ( ; i < ConcreteRegisterImpl::max_fpr ; ) { + regName[i++] = freg->name(); + freg = freg->successor(); + } + assert(i == ConcreteRegisterImpl::number_of_registers, "fix this"); +} + +bool VMRegImpl::is_Register() { + return value() >= 0 && + value() < ConcreteRegisterImpl::max_gpr; +} + +bool VMRegImpl::is_FloatRegister() { + return value() >= ConcreteRegisterImpl::max_gpr && + value() < ConcreteRegisterImpl::max_fpr; +} + +Register VMRegImpl::as_Register() { + assert(is_Register(), "must be"); + return ::as_Register(value()); +} + +FloatRegister VMRegImpl::as_FloatRegister() { + assert(is_FloatRegister(), "must be" ); + return ::as_FloatRegister(value() - ConcreteRegisterImpl::max_gpr); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vmreg_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vmreg_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,29 @@ +/* + * Copyright 2006 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + bool is_Register(); + Register as_Register(); + + bool is_FloatRegister(); + FloatRegister as_FloatRegister(); diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vmreg_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vmreg_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline VMReg RegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg(encoding()); +} + +inline VMReg FloatRegisterImpl::as_VMReg() { + return VMRegImpl::as_VMReg(encoding() + ConcreteRegisterImpl::max_gpr); +} diff -r 39e409a664b3 -r 84043c7507b9 src/cpu/zero/vm/vtableStubs_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/zero/vm/vtableStubs_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_vtableStubs_zero.cpp.incl" + +VtableStub* VtableStubs::create_vtable_stub(int vtable_index) { + ShouldNotCallThis(); +} + +VtableStub* VtableStubs::create_itable_stub(int vtable_index) { + ShouldNotCallThis(); +} + +int VtableStub::pd_code_size_limit(bool is_vtable_stub) { + ShouldNotCallThis(); +} + +int VtableStub::pd_code_alignment() { + ShouldNotCallThis(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/os/linux/vm/os_linux.cpp --- a/src/os/linux/vm/os_linux.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os/linux/vm/os_linux.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,6 +22,8 @@ * */ +# define __STDC_FORMAT_MACROS + // do not include precompiled header file # include "incls/_os_linux.cpp.incl" @@ -53,6 +55,8 @@ # include # include # include +# include +# include #define MAX_PATH (2 * K) @@ -176,7 +180,9 @@ #endif // Cpu architecture string -#if defined(IA64) +#if defined(ZERO) +static char cpu_arch[] = ZERO_LIBARCH; +#elif defined(IA64) static char cpu_arch[] = "ia64"; #elif defined(IA32) static char cpu_arch[] = "i386"; @@ -221,8 +227,8 @@ "environment on Linux when /proc filesystem is not mounted."; void os::Linux::initialize_system_info() { - _processor_count = sysconf(_SC_NPROCESSORS_CONF); - if (_processor_count == 1) { + set_processor_count(sysconf(_SC_NPROCESSORS_CONF)); + if (processor_count() == 1) { pid_t pid = os::Linux::gettid(); char fname[32]; jio_snprintf(fname, sizeof(fname), "/proc/%d", pid); @@ -234,7 +240,7 @@ } } _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE); - assert(_processor_count > 0, "linux error"); + assert(processor_count() > 0, "linux error"); } void os::init_system_properties_values() { @@ -1743,7 +1749,14 @@ {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"}, - {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"} + {EM_PPC64, EM_PPC64, ELFCLASS64, ELFDATA2MSB, (char*)"Power PC 64"}, + {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"}, + {EM_MIPS_RS3_LE, EM_MIPS_RS3_LE, ELFCLASS32, ELFDATA2LSB, (char*)"MIPSel"}, + {EM_MIPS, EM_MIPS, ELFCLASS32, ELFDATA2MSB, (char*)"MIPS"}, + {EM_PARISC, EM_PARISC, ELFCLASS32, ELFDATA2MSB, (char*)"PARISC"}, + {EM_68K, EM_68K, ELFCLASS32, ELFDATA2MSB, (char*)"M68k"} }; #if (defined IA32) @@ -1760,9 +1773,23 @@ static Elf32_Half running_arch_code=EM_PPC64; #elif (defined __powerpc__) static Elf32_Half running_arch_code=EM_PPC; + #elif (defined ARM) + static Elf32_Half running_arch_code=EM_ARM; + #elif (defined S390) + static Elf32_Half running_arch_code=EM_S390; + #elif (defined ALPHA) + static Elf32_Half running_arch_code=EM_ALPHA; + #elif (defined MIPSEL) + static Elf32_Half running_arch_code=EM_MIPS_RS3_LE; + #elif (defined PARISC) + static Elf32_Half running_arch_code=EM_PARISC; + #elif (defined MIPS) + static Elf32_Half running_arch_code=EM_MIPS; + #elif (defined M68K) + static Elf32_Half running_arch_code=EM_68K; #else #error Method os::dll_load requires that one of following is defined:\ - IA32, AMD64, IA64, __sparc, __powerpc__ + IA32, AMD64, IA64, __sparc, __powerpc__, ARM, S390, ALPHA, MIPS, MIPSEL, PARISC, M68K #endif // Identify compatability class for VM's architecture and library's architecture @@ -1794,10 +1821,12 @@ return NULL; } +#ifndef S390 if (lib_arch.elf_class != arch_array[running_arch_index].elf_class) { ::snprintf(diag_msg_buf, diag_msg_max_length-1," (Possible cause: architecture word width mismatch)"); return NULL; } +#endif // !S390 if (lib_arch.compat_class != arch_array[running_arch_index].compat_class) { if ( lib_arch.name!=NULL ) { @@ -2467,6 +2496,91 @@ != MAP_FAILED; } +// Linux uses a growable mapping for the stack, and if the mapping for +// the stack guard pages is not removed when we detach a thread the +// stack cannot grow beyond the pages where the stack guard was +// mapped. If at some point later in the process the stack expands to +// that point, the Linux kernel cannot expand the stack any further +// because the guard pages are in the way, and a segfault occurs. +// +// However, it's essential not to split the stack region by unmapping +// a region (leaving a hole) that's already part of the stack mapping, +// so if the stack mapping has already grown beyond the guard pages at +// the time we create them, we have to truncate the stack mapping. +// So, we need to know the extent of the stack mapping when +// create_stack_guard_pages() is called. + +// Find the bounds of the stack mapping. Return true for success. +// +// We only need this for stacks that are growable: at the time of +// writing thread stacks don't use growable mappings (i.e. those +// creeated with MAP_GROWSDOWN), and aren't marked "[stack]", so this +// only applies to the main thread. +static bool +get_stack_bounds(uintptr_t *bottom, uintptr_t *top) +{ + FILE *f = fopen("/proc/self/maps", "r"); + if (f == NULL) + return false; + + while (!feof(f)) { + size_t dummy; + char *str = NULL; + ssize_t len = getline(&str, &dummy, f); + if (len == -1) { + fclose(f); + return false; + } + + if (len > 0 && str[len-1] == '\n') { + str[len-1] = 0; + len--; + } + + static const char *stack_str = "[stack]"; + if (len > (ssize_t)strlen(stack_str) + && (strcmp(str + len - strlen(stack_str), stack_str) == 0)) { + if (sscanf(str, "%" SCNxPTR "-%" SCNxPTR, bottom, top) == 2) { + uintptr_t sp = (uintptr_t)__builtin_frame_address(0); + if (sp >= *bottom && sp <= *top) { + free(str); + fclose(f); + return true; + } + } + } + free(str); + } + fclose(f); + return false; +} + +// If the (growable) stack mapping already extends beyond the point +// where we're going to put our guard pages, truncate the mapping at +// that point by munmap()ping it. This ensures that when we later +// munmap() the guard pages we don't leave a hole in the stack +// mapping. +bool os::create_stack_guard_pages(char* addr, size_t size) { + uintptr_t stack_extent, stack_base; + if (get_stack_bounds(&stack_extent, &stack_base)) { + if (stack_extent < (uintptr_t)addr) + ::munmap((void*)stack_extent, (uintptr_t)addr - stack_extent); + } + + return os::commit_memory(addr, size); +} + +// If this is a growable mapping, remove the guard pages entirely by +// munmap()ping them. If not, just call uncommit_memory(). +bool os::remove_stack_guard_pages(char* addr, size_t size) { + uintptr_t stack_extent, stack_base; + if (get_stack_bounds(&stack_extent, &stack_base)) { + return ::munmap(addr, size) == 0; + } + + return os::uncommit_memory(addr, size); +} + static address _highest_vm_reserved_address = NULL; // If 'fixed' is true, anon_mmap() will attempt to reserve anonymous memory @@ -2586,7 +2700,9 @@ // format has been changed), we'll use the largest page size supported by // the processor. +#ifndef ZERO _large_page_size = IA32_ONLY(4 * M) AMD64_ONLY(2 * M) IA64_ONLY(256 * M) SPARC_ONLY(4 * M); +#endif // ZERO FILE *fp = fopen("/proc/meminfo", "r"); if (fp) { @@ -4656,6 +4772,7 @@ // Return immediately if a permit is available. if (_counter > 0) { _counter = 0 ; + OrderAccess::fence(); return ; } @@ -4698,6 +4815,7 @@ _counter = 0; status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; + OrderAccess::fence(); return; } @@ -4738,6 +4856,7 @@ jt->java_suspend_self(); } + OrderAccess::fence(); } void Parker::unpark() { diff -r 39e409a664b3 -r 84043c7507b9 src/os/solaris/dtrace/hotspot.d --- a/src/os/solaris/dtrace/hotspot.d Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os/solaris/dtrace/hotspot.d Thu Mar 25 16:54:59 2010 -0700 @@ -25,9 +25,20 @@ provider hotspot { probe class__loaded(char*, uintptr_t, void*, uintptr_t); probe class__unloaded(char*, uintptr_t, void*, uintptr_t); + probe class__initialization__required(char*, uintptr_t, void*, intptr_t,int); + probe class__initialization__recursive(char*, uintptr_t, void*, intptr_t,int); + probe class__initialization__concurrent(char*, uintptr_t, void*, intptr_t,int); + probe class__initialization__erroneous(char*, uintptr_t, void*, intptr_t, int); + probe class__initialization__super__failed(char*, uintptr_t, void*, intptr_t,int); + probe class__initialization__clinit(char*, uintptr_t, void*, intptr_t,int); + probe class__initialization__error(char*, uintptr_t, void*, intptr_t,int); + probe class__initialization__end(char*, uintptr_t, void*, intptr_t,int); probe vm__init__begin(); probe vm__init__end(); probe vm__shutdown(); + probe vmops__request(char*, uintptr_t, int); + probe vmops__begin(char*, uintptr_t, int); + probe vmops__end(char*, uintptr_t, int); probe gc__begin(uintptr_t); probe gc__end(); probe mem__pool__gc__begin( @@ -38,6 +49,12 @@ uintptr_t, uintptr_t, uintptr_t, uintptr_t); probe thread__start(char*, uintptr_t, uintptr_t, uintptr_t, uintptr_t); probe thread__stop(char*, uintptr_t, uintptr_t, uintptr_t, uintptr_t); + probe thread__sleep__begin(long long); + probe thread__sleep__end(int); + probe thread__yield(); + probe thread__park__begin(uintptr_t, int, long long); + probe thread__park__end(uintptr_t); + probe thread__unpark(uintptr_t); probe method__compile__begin( char*, uintptr_t, char*, uintptr_t, char*, uintptr_t, char*, uintptr_t); probe method__compile__end( diff -r 39e409a664b3 -r 84043c7507b9 src/os/solaris/dtrace/libjvm_db.c --- a/src/os/solaris/dtrace/libjvm_db.c Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os/solaris/dtrace/libjvm_db.c Thu Mar 25 16:54:59 2010 -0700 @@ -937,54 +937,56 @@ return err; } -static int -scopeDesc_chain(Nmethod_t *N) -{ +static int scopeDesc_chain(Nmethod_t *N) { int32_t decode_offset = 0; int32_t err; - if (debug > 2) - fprintf(stderr, "\t scopeDesc_chain: BEGIN\n"); + if (debug > 2) { + fprintf(stderr, "\t scopeDesc_chain: BEGIN\n"); + } err = ps_pread(N->J->P, N->pc_desc + OFFSET_PcDesc_scope_decode_offset, &decode_offset, SZ32); CHECK_FAIL(err); while (decode_offset > 0) { - if (debug > 2) - fprintf(stderr, "\t scopeDesc_chain: decode_offset: %#x\n", decode_offset); + Vframe_t *vf = &N->vframes[N->vf_cnt]; - Vframe_t *vf = &N->vframes[N->vf_cnt]; + if (debug > 2) { + fprintf(stderr, "\t scopeDesc_chain: decode_offset: %#x\n", decode_offset); + } + + err = scope_desc_at(N, decode_offset, vf); + CHECK_FAIL(err); - err = scope_desc_at(N, decode_offset, vf); - CHECK_FAIL(err); + if (vf->methodIdx > N->oops_len) { + fprintf(stderr, "\t scopeDesc_chain: (methodIdx > oops_len) !\n"); + return -1; + } + err = read_pointer(N->J, N->nm + N->oops_beg + (vf->methodIdx-1)*POINTER_SIZE, + &vf->methodOop); + CHECK_FAIL(err); - if (vf->methodIdx > N->oops_len) { - fprintf(stderr, "\t scopeDesc_chain: (methodIdx > oops_len) !\n"); - return -1; - } - err = read_pointer(N->J, N->nm + N->oops_beg + (vf->methodIdx-1)*POINTER_SIZE, - &vf->methodOop); + if (vf->methodOop) { + N->vf_cnt++; + err = line_number_from_bci(N->J, vf); CHECK_FAIL(err); - - if (vf->methodOop) { - N->vf_cnt++; - err = line_number_from_bci(N->J, vf); - CHECK_FAIL(err); - if (debug > 2) { - fprintf(stderr, "\t scopeDesc_chain: methodOop: %#8llx, line: %ld\n", - vf->methodOop, vf->line); - } + if (debug > 2) { + fprintf(stderr, "\t scopeDesc_chain: methodOop: %#8llx, line: %ld\n", + vf->methodOop, vf->line); } - decode_offset = vf->sender_decode_offset; + } + decode_offset = vf->sender_decode_offset; } - if (debug > 2) - fprintf(stderr, "\t scopeDesc_chain: END \n\n"); + if (debug > 2) { + fprintf(stderr, "\t scopeDesc_chain: END \n\n"); + } return PS_OK; fail: - if (debug) - fprintf(stderr, "\t scopeDesc_chain: FAIL \n\n"); + if (debug) { + fprintf(stderr, "\t scopeDesc_chain: FAIL \n\n"); + } return err; } diff -r 39e409a664b3 -r 84043c7507b9 src/os/solaris/vm/attachListener_solaris.cpp --- a/src/os/solaris/vm/attachListener_solaris.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os/solaris/vm/attachListener_solaris.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -668,13 +668,18 @@ } } - if (strcmp(name, "ExtendedDTraceProbes") != 0) { - out->print_cr("flag '%s' cannot be changed", name); - return JNI_ERR; + if (strcmp(name, "ExtendedDTraceProbes") == 0) { + DTrace::set_extended_dprobes(flag); + return JNI_OK; } - DTrace::set_extended_dprobes(flag); - return JNI_OK; + if (strcmp(name, "DTraceMonitorProbes") == 0) { + DTrace::set_monitor_dprobes(flag); + return JNI_OK; + } + + out->print_cr("flag '%s' cannot be changed", name); + return JNI_ERR; } void AttachListener::pd_detachall() { diff -r 39e409a664b3 -r 84043c7507b9 src/os/solaris/vm/os_solaris.cpp --- a/src/os/solaris/vm/os_solaris.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os/solaris/vm/os_solaris.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -457,7 +457,7 @@ void os::Solaris::initialize_system_info() { - _processor_count = sysconf(_SC_NPROCESSORS_CONF); + set_processor_count(sysconf(_SC_NPROCESSORS_CONF)); _processors_online = sysconf (_SC_NPROCESSORS_ONLN); _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE); } @@ -2698,6 +2698,14 @@ } } +bool os::create_stack_guard_pages(char* addr, size_t size) { + return os::commit_memory(addr, size); +} + +bool os::remove_stack_guard_pages(char* addr, size_t size) { + return os::uncommit_memory(addr, size); +} + // Change the page size in a given range. void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) { assert((intptr_t)addr % alignment_hint == 0, "Address should be aligned."); @@ -5803,6 +5811,7 @@ // Return immediately if a permit is available. if (_counter > 0) { _counter = 0 ; + OrderAccess::fence(); return ; } @@ -5846,6 +5855,7 @@ _counter = 0; status = os::Solaris::mutex_unlock(_mutex); assert (status == 0, "invariant") ; + OrderAccess::fence(); return; } @@ -5892,6 +5902,7 @@ jt->java_suspend_self(); } + OrderAccess::fence(); } void Parker::unpark() { diff -r 39e409a664b3 -r 84043c7507b9 src/os/windows/vm/os_windows.cpp --- a/src/os/windows/vm/os_windows.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os/windows/vm/os_windows.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -142,6 +142,9 @@ } #ifndef _WIN64 +// previous UnhandledExceptionFilter, if there is one +static LPTOP_LEVEL_EXCEPTION_FILTER prev_uef_handler = NULL; + LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo); #endif void os::init_system_properties_values() { @@ -260,7 +263,8 @@ } #ifndef _WIN64 - SetUnhandledExceptionFilter(Handle_FLT_Exception); + // set our UnhandledExceptionFilter and save any previous one + prev_uef_handler = SetUnhandledExceptionFilter(Handle_FLT_Exception); #endif // Done @@ -1526,7 +1530,8 @@ case 5000: st->print(" Windows 2000"); break; case 5001: st->print(" Windows XP"); break; case 5002: - case 6000: { + case 6000: + case 6001: { // Retrieve SYSTEM_INFO from GetNativeSystemInfo call so that we could // find out whether we are running on 64 bit processor or not. SYSTEM_INFO si; @@ -1549,13 +1554,27 @@ st->print(" Windows XP x64 Edition"); else st->print(" Windows Server 2003 family"); - } else { // os_vers == 6000 + } else if (os_vers == 6000) { if (osvi.wProductType == VER_NT_WORKSTATION) st->print(" Windows Vista"); else st->print(" Windows Server 2008"); if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) st->print(" , 64 bit"); + } else if (os_vers == 6001) { + if (osvi.wProductType == VER_NT_WORKSTATION) { + st->print(" Windows 7"); + } else { + // Unrecognized windows, print out its major and minor versions + st->print(" Windows NT %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion); + } + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + st->print(" , 64 bit"); + } else { // future os + // Unrecognized windows, print out its major and minor versions + st->print(" Windows NT %d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion); + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + st->print(" , 64 bit"); } break; } @@ -1954,7 +1973,7 @@ #ifndef _WIN64 //----------------------------------------------------------------------------- LONG WINAPI Handle_FLT_Exception(struct _EXCEPTION_POINTERS* exceptionInfo) { - // handle exception caused by native mothod modifying control word + // handle exception caused by native method modifying control word PCONTEXT ctx = exceptionInfo->ContextRecord; DWORD exception_code = exceptionInfo->ExceptionRecord->ExceptionCode; @@ -1975,6 +1994,13 @@ return EXCEPTION_CONTINUE_EXECUTION; } } + + if (prev_uef_handler != NULL) { + // We didn't handle this exception so pass it to the previous + // UnhandledExceptionFilter. + return (prev_uef_handler)(exceptionInfo); + } + return EXCEPTION_CONTINUE_SEARCH; } #else //_WIN64 @@ -2777,6 +2803,14 @@ return VirtualFree(addr, 0, MEM_RELEASE) != 0; } +bool os::create_stack_guard_pages(char* addr, size_t size) { + return os::commit_memory(addr, size); +} + +bool os::remove_stack_guard_pages(char* addr, size_t size) { + return os::uncommit_memory(addr, size); +} + // Set protections specified bool os::protect_memory(char* addr, size_t bytes, ProtType prot, bool is_committed) { @@ -3135,7 +3169,7 @@ _vm_allocation_granularity = si.dwAllocationGranularity; _processor_type = si.dwProcessorType; _processor_level = si.wProcessorLevel; - _processor_count = si.dwNumberOfProcessors; + set_processor_count(si.dwNumberOfProcessors); MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_x86/vm/globals_linux_x86.hpp --- a/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,10 +22,9 @@ * */ -// // Sets the default values for platform dependent flags used by the runtime system. // (see globals.hpp) -// + define_pd_global(bool, DontYieldALot, false); #ifdef AMD64 define_pd_global(intx, ThreadStackSize, 1024); // 0 => use system default @@ -39,11 +38,10 @@ #endif // AMD64 define_pd_global(intx, CompilerThreadStackSize, 0); -define_pd_global(intx, SurvivorRatio, 8); -define_pd_global(uintx, JVMInvokeMethodSlack, 8192); +define_pd_global(uintx,JVMInvokeMethodSlack, 8192); // Only used on 64 bit platforms -define_pd_global(uintx, HeapBaseMinAddress, 2*G); +define_pd_global(uintx,HeapBaseMinAddress, 2*G); // Only used on 64 bit Windows platforms define_pd_global(bool, UseVectoredExceptions, false); diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/assembler_linux_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/assembler_linux_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/atomic_linux_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/atomic_linux_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,293 @@ +/* + * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Implementation of class atomic + +#ifdef M68K + +/* + * __m68k_cmpxchg + * + * Atomically store newval in *ptr if *ptr is equal to oldval for user space. + * Returns newval on success and oldval if no exchange happened. + * This implementation is processor specific and works on + * 68020 68030 68040 and 68060. + * + * It will not work on ColdFire, 68000 and 68010 since they lack the CAS + * instruction. + * Using a kernelhelper would be better for arch complete implementation. + * + */ + +static inline int __m68k_cmpxchg(int oldval, int newval, volatile int *ptr) { + int ret; + __asm __volatile ("cas%.l %0,%2,%1" + : "=d" (ret), "+m" (*(ptr)) + : "d" (newval), "0" (oldval)); + return ret; +} + +/* Perform an atomic compare and swap: if the current value of `*PTR' + is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of + `*PTR' before the operation.*/ +static inline int m68k_compare_and_swap(volatile int *ptr, + int oldval, + int newval) { + for (;;) { + int prev = *ptr; + if (prev != oldval) + return prev; + + if (__m68k_cmpxchg (prev, newval, ptr) == newval) + // Success. + return prev; + + // We failed even though prev == oldval. Try again. + } +} + +/* Atomically add an int to memory. */ +static inline int m68k_add_and_fetch(volatile int *ptr, int add_value) { + for (;;) { + // Loop until success. + + int prev = *ptr; + + if (__m68k_cmpxchg (prev, prev + add_value, ptr) == prev + add_value) + return prev + add_value; + } +} + +/* Atomically write VALUE into `*PTR' and returns the previous + contents of `*PTR'. */ +static inline int m68k_lock_test_and_set(volatile int *ptr, int newval) { + for (;;) { + // Loop until success. + int prev = *ptr; + + if (__m68k_cmpxchg (prev, newval, ptr) == prev) + return prev; + } +} +#endif // M68K + +#ifdef ARM + +/* + * __kernel_cmpxchg + * + * Atomically store newval in *ptr if *ptr is equal to oldval for user space. + * Return zero if *ptr was changed or non-zero if no exchange happened. + * The C flag is also set if *ptr was changed to allow for assembly + * optimization in the calling code. + * + */ + +typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); +#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *) 0xffff0fc0) + + + +/* Perform an atomic compare and swap: if the current value of `*PTR' + is OLDVAL, then write NEWVAL into `*PTR'. Return the contents of + `*PTR' before the operation.*/ +static inline int arm_compare_and_swap(volatile int *ptr, + int oldval, + int newval) { + for (;;) { + int prev = *ptr; + if (prev != oldval) + return prev; + + if (__kernel_cmpxchg (prev, newval, ptr) == 0) + // Success. + return prev; + + // We failed even though prev == oldval. Try again. + } +} + +/* Atomically add an int to memory. */ +static inline int arm_add_and_fetch(volatile int *ptr, int add_value) { + for (;;) { + // Loop until a __kernel_cmpxchg succeeds. + + int prev = *ptr; + + if (__kernel_cmpxchg (prev, prev + add_value, ptr) == 0) + return prev + add_value; + } +} + +/* Atomically write VALUE into `*PTR' and returns the previous + contents of `*PTR'. */ +static inline int arm_lock_test_and_set(volatile int *ptr, int newval) { + for (;;) { + // Loop until a __kernel_cmpxchg succeeds. + int prev = *ptr; + + if (__kernel_cmpxchg (prev, newval, ptr) == 0) + return prev; + } +} +#endif // ARM + +inline void Atomic::store(jint store_value, volatile jint* dest) { + *dest = store_value; +} + +inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { + *dest = store_value; +} + +inline jint Atomic::add(jint add_value, volatile jint* dest) { +#ifdef ARM + return arm_add_and_fetch(dest, add_value); +#else +#ifdef M68K + return m68k_add_and_fetch(dest, add_value); +#else + return __sync_add_and_fetch(dest, add_value); +#endif // M68K +#endif // ARM +} + +inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) { +#ifdef ARM + return arm_add_and_fetch(dest, add_value); +#else +#ifdef M68K + return m68k_add_and_fetch(dest, add_value); +#else + return __sync_add_and_fetch(dest, add_value); +#endif // M68K +#endif // ARM +} + +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) { + add(1, dest); +} + +inline void Atomic::inc_ptr(volatile intptr_t* dest) { + add_ptr(1, dest); +} + +inline void Atomic::inc_ptr(volatile void* dest) { + add_ptr(1, dest); +} + +inline void Atomic::dec(volatile jint* dest) { + add(-1, dest); +} + +inline void Atomic::dec_ptr(volatile intptr_t* dest) { + add_ptr(-1, dest); +} + +inline void Atomic::dec_ptr(volatile void* dest) { + add_ptr(-1, dest); +} + +inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) { +#ifdef ARM + return arm_lock_test_and_set(dest, exchange_value); +#else +#ifdef M68K + return m68k_lock_test_and_set(dest, exchange_value); +#else + // __sync_lock_test_and_set is a bizarrely named atomic exchange + // operation. Note that some platforms only support this with the + // limitation that the only valid value to store is the immediate + // constant 1. There is a test for this in JNI_CreateJavaVM(). + return __sync_lock_test_and_set (dest, exchange_value); +#endif // M68K +#endif // ARM +} + +inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, + volatile intptr_t* dest) { +#ifdef ARM + return arm_lock_test_and_set(dest, exchange_value); +#else +#ifdef M68K + return m68k_lock_test_and_set(dest, exchange_value); +#else + return __sync_lock_test_and_set (dest, exchange_value); +#endif // M68K +#endif // ARM +} + +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) { +#ifdef ARM + return arm_compare_and_swap(dest, compare_value, exchange_value); +#else +#ifdef M68K + return m68k_compare_and_swap(dest, compare_value, exchange_value); +#else + return __sync_val_compare_and_swap(dest, compare_value, exchange_value); +#endif // M68K +#endif // ARM +} + +inline jlong Atomic::cmpxchg(jlong exchange_value, + volatile jlong* dest, + jlong compare_value) { + + return __sync_val_compare_and_swap(dest, compare_value, exchange_value); +} + +inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, + volatile intptr_t* dest, + intptr_t compare_value) { +#ifdef ARM + return arm_compare_and_swap(dest, compare_value, exchange_value); +#else +#ifdef M68K + return m68k_compare_and_swap(dest, compare_value, exchange_value); +#else + return __sync_val_compare_and_swap(dest, compare_value, exchange_value); +#endif // M68K +#endif // ARM +} + +inline void* Atomic::cmpxchg_ptr(void* exchange_value, + volatile void* dest, + void* compare_value) { + + return (void *) cmpxchg_ptr((intptr_t) exchange_value, + (volatile intptr_t*) dest, + (intptr_t) compare_value); +} diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/bytes_linux_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/bytes_linux_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright 2003 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Efficient swapping of data bytes from Java byte +// ordering to native byte ordering and vice versa. + +#include + +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); +} diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/globals_linux_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/globals_linux_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// +// Set the default values for platform dependent flags used by the +// runtime system. See globals.hpp for details of what they do. +// + +define_pd_global(bool, DontYieldALot, false); +#ifdef _LP64 +define_pd_global(intx, ThreadStackSize, 1536); +define_pd_global(intx, VMThreadStackSize, 1024); +#else +define_pd_global(intx, ThreadStackSize, 1024); +define_pd_global(intx, VMThreadStackSize, 512); +#endif // _LP64 +define_pd_global(intx, SurvivorRatio, 8); +define_pd_global(intx, CompilerThreadStackSize, 0); +define_pd_global(uintx, JVMInvokeMethodSlack, 8192); + +define_pd_global(bool, UseVectoredExceptions, false); +// Only used on 64 bit platforms +define_pd_global(uintx, HeapBaseMinAddress, 2*G); diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/orderAccess_linux_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/orderAccess_linux_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,167 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#ifdef ARM + +/* + * ARM Kernel helper for memory barrier. + * Using __asm __volatile ("":::"memory") does not work reliable on ARM + * and gcc __sync_synchronize(); implementation does not use the kernel + * helper for all gcc versions so it is unreliable to use as well. + */ +typedef void (__kernel_dmb_t) (void); +#define __kernel_dmb (*(__kernel_dmb_t *) 0xffff0fa0) + +#define FULL_MEM_BARRIER __kernel_dmb() +#define READ_MEM_BARRIER __kernel_dmb() +#define WRITE_MEM_BARRIER __kernel_dmb() + +#else // ARM + +#define FULL_MEM_BARRIER __sync_synchronize() + +#ifdef PPC + +#define READ_MEM_BARRIER __asm __volatile ("isync":::"memory") +#ifdef __NO_LWSYNC__ +#define WRITE_MEM_BARRIER __asm __volatile ("sync":::"memory") +#else +#define WRITE_MEM_BARRIER __asm __volatile ("lwsync":::"memory") +#endif + +#else // PPC + +#define READ_MEM_BARRIER __asm __volatile ("":::"memory") +#define WRITE_MEM_BARRIER __asm __volatile ("":::"memory") + +#endif // PPC + +#endif // ARM + + +inline void OrderAccess::loadload() { acquire(); } +inline void OrderAccess::storestore() { release(); } +inline void OrderAccess::loadstore() { acquire(); } +inline void OrderAccess::storeload() { fence(); } + +inline void OrderAccess::acquire() { + READ_MEM_BARRIER; +} + +inline void OrderAccess::release() { + WRITE_MEM_BARRIER; +} + +inline void OrderAccess::fence() { + FULL_MEM_BARRIER; +} + +inline jbyte OrderAccess::load_acquire(volatile jbyte* p) { jbyte data = *p; acquire(); return data; } +inline jshort OrderAccess::load_acquire(volatile jshort* p) { jshort data = *p; acquire(); return data; } +inline jint OrderAccess::load_acquire(volatile jint* p) { jint data = *p; acquire(); return data; } +inline jlong OrderAccess::load_acquire(volatile jlong* p) { + jlong tmp; + os::atomic_copy64(p, &tmp); + acquire(); + return tmp; +} +inline jubyte OrderAccess::load_acquire(volatile jubyte* p) { jubyte data = *p; acquire(); return data; } +inline jushort OrderAccess::load_acquire(volatile jushort* p) { jushort data = *p; acquire(); return data; } +inline juint OrderAccess::load_acquire(volatile juint* p) { juint data = *p; acquire(); return data; } +inline julong OrderAccess::load_acquire(volatile julong* p) { + julong tmp; + os::atomic_copy64(p, &tmp); + acquire(); + return tmp; +} +inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { jfloat data = *p; acquire(); return data; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { + jdouble tmp; + os::atomic_copy64(p, &tmp); + acquire(); + return tmp; +} + +inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { + intptr_t data = *p; + acquire(); + return data; +} +inline void* OrderAccess::load_ptr_acquire(volatile void* p) { + void *data = *(void* volatile *)p; + acquire(); + return data; +} +inline void* OrderAccess::load_ptr_acquire(const volatile void* p) { + void *data = *(void* const volatile *)p; + acquire(); + return data; +} + +inline void OrderAccess::release_store(volatile jbyte* p, jbyte v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile jshort* p, jshort v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile jint* p, jint v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile jlong* p, jlong v) +{ release(); os::atomic_copy64(&v, p); } +inline void OrderAccess::release_store(volatile jubyte* p, jubyte v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile jushort* p, jushort v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile juint* p, juint v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile julong* p, julong v) +{ release(); os::atomic_copy64(&v, p); } +inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { release(); *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) +{ release(); os::atomic_copy64(&v, p); } + +inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { release(); *p = v; } +inline void OrderAccess::release_store_ptr(volatile void* p, void* v) +{ release(); *(void* volatile *)p = v; } + +inline void OrderAccess::store_fence(jbyte* p, jbyte v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jshort* p, jshort v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jint* p, jint v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jlong* p, jlong v) { os::atomic_copy64(&v, p); fence(); } +inline void OrderAccess::store_fence(jubyte* p, jubyte v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jushort* p, jushort v) { *p = v; fence(); } +inline void OrderAccess::store_fence(juint* p, juint v) { *p = v; fence(); } +inline void OrderAccess::store_fence(julong* p, julong v) { os::atomic_copy64(&v, p); fence(); } +inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { os::atomic_copy64(&v, p); fence(); } + +inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { *p = v; fence(); } +inline void OrderAccess::store_ptr_fence(void** p, void* v) { *p = v; fence(); } + +inline void OrderAccess::release_store_fence(volatile jbyte* p, jbyte v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jshort* p, jshort v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jint* p, jint v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store(p, v); fence(); } + +inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { release_store_ptr(p, v); fence(); } +inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { release_store_ptr(p, v); fence(); } diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/os_linux_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/os_linux_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,470 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// do not include precompiled header file +#include "incls/_os_linux_zero.cpp.incl" + +address os::current_stack_pointer() { + address dummy = (address) &dummy; + return dummy; +} + +frame os::get_sender_for_C_frame(frame* fr) { + ShouldNotCallThis(); +} + +frame os::current_frame() { + // The only thing that calls this is the stack printing code in + // VMError::report: + // - Step 110 (printing stack bounds) uses the sp in the frame + // to determine the amount of free space on the stack. We + // set the sp to a close approximation of the real value in + // order to allow this step to complete. + // - Step 120 (printing native stack) tries to walk the stack. + // The frame we create has a NULL pc, which is ignored as an + // invalid frame. + frame dummy = frame(); + dummy.set_sp((intptr_t *) current_stack_pointer()); + return dummy; +} + +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). +#ifdef SPARC + // On SPARC, 0 != %hi(any real address), because there is no + // allocation in the first 1Kb of the virtual address space. + return (char *) 0; +#else + // This is the value for x86; works pretty well for PPC too. + return (char *) -1; +#endif // SPARC +} + +void os::initialize_thread() { + // Nothing to do. +} + +address os::Linux::ucontext_get_pc(ucontext_t* uc) { + ShouldNotCallThis(); +} + +ExtendedPC os::fetch_frame_from_context(void* ucVoid, + intptr_t** ret_sp, + intptr_t** ret_fp) { + ShouldNotCallThis(); +} + +frame os::fetch_frame_from_context(void* ucVoid) { + ShouldNotCallThis(); +} + +extern "C" 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 || sig == SIGXFSZ) { + // allow chained handler to go first + if (os::Linux::chained_handler(sig, info, ucVoid)) { + return true; + } else { + if (PrintMiscellaneous && (WizardMode || Verbose)) { + char buf[64]; + warning("Ignoring %s - see bugs 4229104 or 646499219", + os::exception_name(sig, buf, sizeof(buf))); + } + 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; + } + } + } + + if (info != NULL && thread != NULL) { + // Handle ALL stack overflow variations here + if (sig == SIGSEGV) { + address addr = (address) info->si_addr; + + // 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(); + ShouldNotCallThis(); + } + else if (thread->in_stack_red_zone(addr)) { + thread->disable_stack_red_zone(); + ShouldNotCallThis(); + } + 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 true; + } + thread->osthread()->clear_expanding_stack(); + } + else { + fatal("recursive segv. expanding stack."); + } + } + } + } + + /*if (thread->thread_state() == _thread_in_Java) { + ShouldNotCallThis(); + } + else*/ if (thread->thread_state() == _thread_in_vm && + sig == SIGBUS && thread->doing_unsafe_access()) { + ShouldNotCallThis(); + } + + // jni_fast_GetField can trap at certain pc's if a GC + // kicks in and the heap gets shrunk before the field access. + /*if (sig == SIGSEGV || sig == SIGBUS) { + address addr = JNI_FastGetField::find_slowcase_pc(pc); + if (addr != (address)-1) { + stub = addr; + } + }*/ + + // 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, (address) info->si_addr)) { + // Block current thread until permission is restored. + os::block_on_serialize_page_trap(); + 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; + } + +#ifndef PRODUCT + if (sig == SIGSEGV) { + fatal("\n#" + "\n# /--------------------\\" + "\n# | segmentation fault |" + "\n# \\---\\ /--------------/" + "\n# /" + "\n# [-] |\\_/| " + "\n# (+)=C |o o|__ " + "\n# | | =-*-=__\\ " + "\n# OOO c_c_(___)"); + } +#endif // !PRODUCT + + const char *fmt = "caught unhandled signal %d"; + char buf[64]; + + sprintf(buf, fmt, sig); + fatal(buf); +} + +void os::Linux::init_thread_fpu_state(void) { + // Nothing to do +} + +int os::Linux::get_fpu_control_word() { + ShouldNotCallThis(); +} + +void os::Linux::set_fpu_control_word(int fpu) { + ShouldNotCallThis(); +} + +bool os::is_allocatable(size_t bytes) { +#ifdef _LP64 + return true; +#else + if (bytes < 2 * G) { + return true; + } + + char* addr = reserve_memory(bytes, NULL); + + if (addr != NULL) { + release_memory(addr, bytes); + } + + return addr != NULL; +#endif // _LP64 +} + +/////////////////////////////////////////////////////////////////////////////// +// thread stack + +size_t os::Linux::min_stack_allowed = 64 * K; + +bool os::Linux::supports_variable_stack_size() { + return true; +} + +size_t os::Linux::default_stack_size(os::ThreadType thr_type) { +#ifdef _LP64 + size_t s = (thr_type == os::compiler_thread ? 4 * M : 1 * M); +#else + size_t s = (thr_type == os::compiler_thread ? 2 * M : 512 * K); +#endif // _LP64 + return s; +} + +size_t os::Linux::default_guard_size(os::ThreadType thr_type) { + // Only enable glibc guard pages for non-Java threads + // (Java threads have HotSpot guard pages) + return (thr_type == java_thread ? 0 : page_size()); +} + +static void current_stack_region(address *bottom, size_t *size) { + pthread_attr_t attr; + int res = pthread_getattr_np(pthread_self(), &attr); + if (res != 0) { + if (res == ENOMEM) { + vm_exit_out_of_memory(0, "pthread_getattr_np"); + } + else { + fatal1("pthread_getattr_np failed with errno = %d", res); + } + } + + address stack_bottom; + size_t stack_bytes; + res = pthread_attr_getstack(&attr, (void **) &stack_bottom, &stack_bytes); + if (res != 0) { + fatal1("pthread_attr_getstack failed with errno = %d", res); + } + address stack_top = stack_bottom + stack_bytes; + + // The block of memory returned by pthread_attr_getstack() includes + // guard pages where present. We need to trim these off. + size_t page_bytes = os::Linux::page_size(); + assert(((intptr_t) stack_bottom & (page_bytes - 1)) == 0, "unaligned stack"); + + size_t guard_bytes; + res = pthread_attr_getguardsize(&attr, &guard_bytes); + if (res != 0) { + fatal1("pthread_attr_getguardsize failed with errno = %d", res); + } + int guard_pages = align_size_up(guard_bytes, page_bytes) / page_bytes; + assert(guard_bytes == guard_pages * page_bytes, "unaligned guard"); + +#ifdef IA64 + // IA64 has two stacks sharing the same area of memory, a normal + // stack growing downwards and a register stack growing upwards. + // Guard pages, if present, are in the centre. This code splits + // the stack in two even without guard pages, though in theory + // there's nothing to stop us allocating more to the normal stack + // or more to the register stack if one or the other were found + // to grow faster. + int total_pages = align_size_down(stack_bytes, page_bytes) / page_bytes; + stack_bottom += (total_pages - guard_pages) / 2 * page_bytes; +#endif // IA64 + + stack_bottom += guard_bytes; + + pthread_attr_destroy(&attr); + + // The initial thread has a growable stack, and the size reported + // by pthread_attr_getstack is the maximum size it could possibly + // be given what currently mapped. This can be huge, so we cap it. + if (os::Linux::is_initial_thread()) { + stack_bytes = stack_top - stack_bottom; + + if (stack_bytes > JavaThread::stack_size_at_create()) + stack_bytes = JavaThread::stack_size_at_create(); + + stack_bottom = stack_top - stack_bytes; + } + + assert(os::current_stack_pointer() >= stack_bottom, "should do"); + assert(os::current_stack_pointer() < stack_top, "should do"); + + *bottom = stack_bottom; + *size = stack_top - stack_bottom; +} + +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) { + ShouldNotCallThis(); +} + +///////////////////////////////////////////////////////////////////////////// +// Stubs for things that would be in linux_zero.s if it existed. +// You probably want to disassemble these monkeys to check they're ok. + +extern "C" { + int SpinPause() { + } + + int SafeFetch32(int *adr, int errValue) { + int value = errValue; + value = *adr; + return value; + } + intptr_t SafeFetchN(intptr_t *adr, intptr_t errValue) { + intptr_t value = errValue; + value = *adr; + return value; + } + + void _Copy_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + if (from > to) { + jshort *end = from + count; + while (from < end) + *(to++) = *(from++); + } + else if (from < to) { + jshort *end = from; + from += count - 1; + to += count - 1; + while (from >= end) + *(to--) = *(from--); + } + } + void _Copy_conjoint_jints_atomic(jint* from, jint* to, size_t count) { + if (from > to) { + jint *end = from + count; + while (from < end) + *(to++) = *(from++); + } + else if (from < to) { + jint *end = from; + from += count - 1; + to += count - 1; + while (from >= end) + *(to--) = *(from--); + } + } + void _Copy_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { + if (from > to) { + jlong *end = from + count; + while (from < end) + os::atomic_copy64(from++, to++); + } + else if (from < to) { + jlong *end = from; + from += count - 1; + to += count - 1; + while (from >= end) + os::atomic_copy64(from--, to--); + } + } + + void _Copy_arrayof_conjoint_bytes(HeapWord* from, + HeapWord* to, + size_t count) { + ShouldNotCallThis(); + } + void _Copy_arrayof_conjoint_jshorts(HeapWord* from, + HeapWord* to, + size_t count) { + ShouldNotCallThis(); + } + void _Copy_arrayof_conjoint_jints(HeapWord* from, + HeapWord* to, + size_t count) { + ShouldNotCallThis(); + } + void _Copy_arrayof_conjoint_jlongs(HeapWord* from, + HeapWord* to, + size_t count) { + ShouldNotCallThis(); + } +}; + +///////////////////////////////////////////////////////////////////////////// +// Implementations of atomic operations not supported by processors. +// -- http://gcc.gnu.org/onlinedocs/gcc-4.2.1/gcc/Atomic-Builtins.html + +#ifndef _LP64 +extern "C" { + long long unsigned int __sync_val_compare_and_swap_8( + volatile void *ptr, + long long unsigned int oldval, + long long unsigned int newval) { + ShouldNotCallThis(); + } +}; +#endif // !_LP64 diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/os_linux_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/os_linux_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2010 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + static void setup_fpu() {} + + static bool is_allocatable(size_t bytes); + + // 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; } + + // Atomically copy 64 bits of data + static void atomic_copy64(volatile void *src, volatile void *dst) { +#if defined(PPC) && !defined(_LP64) + double tmp; + asm volatile ("lfd %0, 0(%1)\n" + "stfd %0, 0(%2)\n" + : "=f"(tmp) + : "b"(src), "b"(dst)); +#elif defined(S390) && !defined(_LP64) + double tmp; + asm volatile ("ld %0, 0(%1)\n" + "std %0, 0(%2)\n" + : "=r"(tmp) + : "a"(src), "a"(dst)); +#else + *(jlong *) dst = *(jlong *) src; +#endif + } diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/prefetch_linux_zero.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/prefetch_linux_zero.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +inline void Prefetch::read(void* loc, intx interval) { +} + +inline void Prefetch::write(void* loc, intx interval) { +} diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/threadLS_linux_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/threadLS_linux_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_threadLS_linux_zero.cpp.incl" + +void ThreadLocalStorage::generate_code_for_get_thread() { + // nothing to do +} + +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 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/threadLS_linux_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/threadLS_linux_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright 2003 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// Processor dependent parts of ThreadLocalStorage + + public: + static Thread* thread() { + return (Thread*) os::thread_local_storage_at(thread_index()); + } diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/thread_linux_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/thread_linux_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/thread_linux_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007, 2008, 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + + private: + ZeroStack _zero_stack; + ZeroFrame* _top_zero_frame; + + void pd_initialize() { + _top_zero_frame = NULL; + } + + public: + ZeroStack *zero_stack() { + return &_zero_stack; + } + + public: + ZeroFrame *top_zero_frame() { + return _top_zero_frame; + } + void push_zero_frame(ZeroFrame *frame) { + *(ZeroFrame **) frame = _top_zero_frame; + _top_zero_frame = frame; + } + void pop_zero_frame() { + zero_stack()->set_sp((intptr_t *) _top_zero_frame + 1); + _top_zero_frame = *(ZeroFrame **) _top_zero_frame; + } + + public: + static ByteSize zero_stack_offset() { + return byte_offset_of(JavaThread, _zero_stack); + } + static ByteSize top_zero_frame_offset() { + return byte_offset_of(JavaThread, _top_zero_frame); + } + + public: + void record_base_of_stack_pointer() { + assert(top_zero_frame() == NULL, "junk on stack prior to Java call"); + } + void set_base_of_stack_pointer(intptr_t* base_sp) { + assert(base_sp == NULL, "should be"); + assert(top_zero_frame() == NULL, "junk on stack after Java call"); + } + + public: + void set_last_Java_frame() { + JavaFrameAnchor *jfa = frame_anchor(); + jfa->set_last_Java_sp((intptr_t *) top_zero_frame()); + } + void reset_last_Java_frame() { + JavaFrameAnchor *jfa = frame_anchor(); + jfa->set_last_Java_sp(NULL); + } + + private: + frame pd_last_frame() { + assert(has_last_Java_frame(), "must have last_Java_sp() when suspended"); + return frame(last_Java_sp(), zero_stack()->sp()); + } + + public: + // Check for pending suspend requests and pending asynchronous + // exceptions. There are separate accessors for these, but + // _suspend_flags is volatile so using them would be unsafe. + bool has_special_condition_for_native_trans() { + return _suspend_flags != 0; + } + + public: + bool pd_get_top_frame_for_signal_handler(frame* fr_addr, + void* ucontext, + bool isInJava) { + ShouldNotCallThis(); + } + + // 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() {} diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/vmStructs_linux_zero.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/vmStructs_linux_zero.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// These are the OS and CPU-specific fields, types and integer +// constants required by the Serviceability Agent. This file is +// referenced by vmStructs.cpp. + +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ + /* This must be the last entry, and must be present */ \ + last_entry() + + +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + /* This must be the last entry, and must be present */ \ + last_entry() + +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ + /* This must be the last entry, and must be present */ \ + last_entry() diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/linux_zero/vm/vm_version_linux_zero.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os_cpu/linux_zero/vm/vm_version_linux_zero.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,26 @@ +/* + * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2009 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// This file is intentionally empty diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.inline.hpp --- a/src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os_cpu/solaris_sparc/vm/atomic_solaris_sparc.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/solaris_x86/vm/atomic_solaris_x86.inline.hpp --- a/src/os_cpu/solaris_x86/vm/atomic_solaris_x86.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os_cpu/solaris_x86/vm/atomic_solaris_x86.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp --- a/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,31 +22,25 @@ * */ -// // Sets the default values for platform dependent flags used by the runtime system. // (see globals.hpp) -// + define_pd_global(bool, DontYieldALot, true); // Determined in the design center #ifdef AMD64 define_pd_global(intx, ThreadStackSize, 1024); // 0 => use system default define_pd_global(intx, VMThreadStackSize, 1024); -define_pd_global(intx, SurvivorRatio, 6); -define_pd_global(uintx, JVMInvokeMethodSlack, 8*K); +define_pd_global(uintx,JVMInvokeMethodSlack, 8*K); #else -// UseStackBanging is not pd -// define_pd_global(bool, UseStackBanging, true); - // ThreadStackSize 320 allows TaggedStackInterpreter and a couple of test cases // to run while keeping the number of threads that can be created high. define_pd_global(intx, ThreadStackSize, 320); define_pd_global(intx, VMThreadStackSize, 512); -define_pd_global(intx, SurvivorRatio, 8); -define_pd_global(uintx, JVMInvokeMethodSlack, 10*K); +define_pd_global(uintx,JVMInvokeMethodSlack, 10*K); #endif // AMD64 define_pd_global(intx, CompilerThreadStackSize, 0); // Only used on 64 bit platforms -define_pd_global(uintx, HeapBaseMinAddress, 256*M); +define_pd_global(uintx,HeapBaseMinAddress, 256*M); // Only used on 64 bit Windows platforms define_pd_global(bool, UseVectoredExceptions, false); diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp --- a/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -730,11 +730,12 @@ st->print(", RSI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RSI]); st->print(", RDI=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_RDI]); st->cr(); - st->print(", R8=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R8]); + st->print( "R8=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R8]); st->print(", R9=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R9]); st->print(", R10=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R10]); st->print(", R11=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R11]); - st->print(", R12=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R12]); + st->cr(); + st->print( "R12=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R12]); st->print(", R13=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R13]); st->print(", R14=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R14]); st->print(", R15=" INTPTR_FORMAT, uc->uc_mcontext.gregs[REG_R15]); diff -r 39e409a664b3 -r 84043c7507b9 src/os_cpu/windows_x86/vm/globals_windows_x86.hpp --- a/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -22,10 +22,9 @@ * */ -// // Sets the default values for platform dependent flags used by the runtime system. // (see globals.hpp) -// + define_pd_global(bool, DontYieldALot, false); // Default stack size on Windows is determined by the executable (java.exe @@ -35,8 +34,6 @@ define_pd_global(intx, ThreadStackSize, 0); // 0 => use system default define_pd_global(intx, VMThreadStackSize, 0); // 0 => use system default -define_pd_global(intx, SurvivorRatio, 8); - #ifdef ASSERT define_pd_global(intx, CompilerThreadStackSize, 1024); #else diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/adlc/archDesc.cpp --- a/src/share/vm/adlc/archDesc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/adlc/archDesc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1031,7 +1031,8 @@ //---------------------------addSUNcopyright------------------------------- // output SUN copyright info void ArchDesc::addSunCopyright(char* legal, int size, FILE *fp) { - fwrite(legal, size, 1, fp); + size_t count = fwrite(legal, 1, size, fp); + assert(count == (size_t) size, "copyright info truncated"); fprintf(fp,"\n"); fprintf(fp,"// Machine Generated File. Do Not Edit!\n"); fprintf(fp,"\n"); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/adlc/formssel.cpp --- a/src/share/vm/adlc/formssel.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/adlc/formssel.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -828,11 +828,13 @@ return AdlcVMDeps::Parms; // Skip the machine-state edges if( _matrule->_rChild && - ( strcmp(_matrule->_rChild->_opType,"StrComp" )==0 || + ( strcmp(_matrule->_rChild->_opType,"AryEq" )==0 || + strcmp(_matrule->_rChild->_opType,"StrComp" )==0 || strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 || strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 )) { - // String.(compareTo/equals/indexOf) take 1 control and 4 memory edges. - return 5; + // String.(compareTo/equals/indexOf) and Arrays.equals + // take 1 control and 1 memory edges. + return 2; } // Check for handling of 'Memory' input/edge in the ideal world. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/adlc/output_c.cpp --- a/src/share/vm/adlc/output_c.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/adlc/output_c.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -1496,7 +1496,7 @@ unsigned i; // Generate Expand function header - fprintf(fp,"MachNode *%sNode::Expand(State *state, Node_List &proj_list) {\n", node->_ident); + fprintf(fp,"MachNode *%sNode::Expand(State *state, Node_List &proj_list, Node* mem) {\n", node->_ident); fprintf(fp,"Compile* C = Compile::current();\n"); // Generate expand code if( node->expands() ) { @@ -1546,15 +1546,16 @@ // Build a mapping from operand index to input edges fprintf(fp," unsigned idx0 = oper_input_base();\n"); - // The order in which inputs are added to a node is very + // The order in which the memory input is added to a node is very // strange. Store nodes get a memory input before Expand is - // called and all other nodes get it afterwards so - // oper_input_base is wrong during expansion. This code adjusts - // is so that expansion will work correctly. - bool missing_memory_edge = node->_matrule->needs_ideal_memory_edge(_globalNames) && - node->is_ideal_store() == Form::none; - if (missing_memory_edge) { - fprintf(fp," idx0--; // Adjust base because memory edge hasn't been inserted yet\n"); + // called and other nodes get it afterwards or before depending on + // match order so oper_input_base is wrong during expansion. This + // code adjusts it so that expansion will work correctly. + int has_memory_edge = node->_matrule->needs_ideal_memory_edge(_globalNames); + if (has_memory_edge) { + fprintf(fp," if (mem == (Node*)1) {\n"); + fprintf(fp," idx0--; // Adjust base because memory edge hasn't been inserted yet\n"); + fprintf(fp," }\n"); } for( i = 0; i < node->num_opnds(); i++ ) { @@ -1611,9 +1612,11 @@ int node_mem_op = node->memory_operand(_globalNames); assert( node_mem_op != InstructForm::NO_MEMORY_OPERAND, "expand rule member needs memory but top-level inst doesn't have any" ); - if (!missing_memory_edge) { + if (has_memory_edge) { // Copy memory edge - fprintf(fp," n%d->add_req(_in[1]);\t// Add memory edge\n", cnt); + fprintf(fp," if (mem != (Node*)1) {\n"); + fprintf(fp," n%d->add_req(_in[1]);\t// Add memory edge\n", cnt); + fprintf(fp," }\n"); } } @@ -1689,7 +1692,7 @@ } // done iterating over a new instruction's operands // Invoke Expand() for the newly created instruction. - fprintf(fp," result = n%d->Expand( state, proj_list );\n", cnt); + fprintf(fp," result = n%d->Expand( state, proj_list, mem );\n", cnt); assert( !new_inst->expands(), "Do not have complete support for recursive expansion"); } // done iterating over new instructions fprintf(fp,"\n"); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/adlc/output_h.cpp --- a/src/share/vm/adlc/output_h.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/adlc/output_h.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -1754,7 +1754,7 @@ instr->has_temps() || instr->_matrule != NULL && instr->num_opnds() != instr->num_unique_opnds() ) { - fprintf(fp," virtual MachNode *Expand(State *state, Node_List &proj_list);\n"); + fprintf(fp," virtual MachNode *Expand(State *state, Node_List &proj_list, Node* mem);\n"); } if (instr->is_pinned(_globalNames)) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/asm/codeBuffer.hpp --- a/src/share/vm/asm/codeBuffer.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/asm/codeBuffer.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -39,6 +39,7 @@ Dtrace_trap = OSR_Entry, // dtrace probes can never have an OSR entry so reuse it Exceptions, // Offset where exception handler lives Deopt, // Offset where deopt handler lives + DeoptMH, // Offset where MethodHandle deopt handler lives max_Entries }; // special value to note codeBlobs where profile (forte) stack walking is @@ -51,12 +52,13 @@ public: CodeOffsets() { - _values[Entry] = 0; + _values[Entry ] = 0; _values[Verified_Entry] = 0; _values[Frame_Complete] = frame_never_safe; - _values[OSR_Entry] = 0; - _values[Exceptions] = -1; - _values[Deopt] = -1; + _values[OSR_Entry ] = 0; + _values[Exceptions ] = -1; + _values[Deopt ] = -1; + _values[DeoptMH ] = -1; } int value(Entries e) { return _values[e]; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_Compilation.cpp --- a/src/share/vm/c1/c1_Compilation.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_Compilation.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -205,6 +205,8 @@ void Compilation::emit_code_epilog(LIR_Assembler* assembler) { CHECK_BAILOUT(); + CodeOffsets* code_offsets = assembler->offsets(); + // generate code or slow cases assembler->emit_slow_case_stubs(); CHECK_BAILOUT(); @@ -213,10 +215,18 @@ assembler->emit_exception_entries(exception_info_list()); CHECK_BAILOUT(); - // generate code for exception handler - assembler->emit_exception_handler(); + // Generate code for exception handler. + code_offsets->set_value(CodeOffsets::Exceptions, assembler->emit_exception_handler()); CHECK_BAILOUT(); - assembler->emit_deopt_handler(); + + // Generate code for deopt handler. + code_offsets->set_value(CodeOffsets::Deopt, assembler->emit_deopt_handler()); + CHECK_BAILOUT(); + + // Generate code for MethodHandle deopt handler. 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, assembler->emit_deopt_handler()); CHECK_BAILOUT(); // done diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_GraphBuilder.cpp --- a/src/share/vm/c1/c1_GraphBuilder.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_GraphBuilder.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -365,7 +365,7 @@ if (_next_loop_index < 31) _next_loop_index++; } else { // block already marked as loop header - assert(is_power_of_2(_loop_map.at(block->block_id())), "exactly one bit must be set"); + assert(is_power_of_2((unsigned int)_loop_map.at(block->block_id())), "exactly one bit must be set"); } } @@ -1442,7 +1442,7 @@ switch (field_type) { case T_ARRAY: case T_OBJECT: - if (field_val.as_object()->has_encoding()) { + if (field_val.as_object()->should_be_constant()) { constant = new Constant(as_ValueType(field_val)); } break; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_IR.cpp --- a/src/share/vm/c1/c1_IR.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_IR.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_IR.hpp --- a/src/share/vm/c1/c1_IR.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_IR.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -251,8 +251,10 @@ DebugToken* expvals = recorder->create_scope_values(expressions()); DebugToken* monvals = recorder->create_monitor_values(monitors()); // reexecute allowed only for the topmost frame - bool reexecute = topmost ? should_reexecute() : false; - recorder->describe_scope(pc_offset, scope()->method(), bci(), reexecute, locvals, expvals, monvals); + bool reexecute = topmost ? should_reexecute() : false; + bool is_method_handle_invoke = false; + bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis. + recorder->describe_scope(pc_offset, scope()->method(), bci(), reexecute, is_method_handle_invoke, return_oop, locvals, expvals, monvals); } }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_InstructionPrinter.cpp --- a/src/share/vm/c1/c1_InstructionPrinter.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_InstructionPrinter.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -133,12 +133,12 @@ ciMethod* m = (ciMethod*)value; output()->print("", m->holder()->name()->as_utf8(), m->name()->as_utf8()); } else { - output()->print("", value->encoding()); + output()->print("", value->constant_encoding()); } } else if (type->as_InstanceConstant() != NULL) { - output()->print("", type->as_InstanceConstant()->value()->encoding()); + output()->print("", type->as_InstanceConstant()->value()->constant_encoding()); } else if (type->as_ArrayConstant() != NULL) { - output()->print("", type->as_ArrayConstant()->value()->encoding()); + output()->print("", type->as_ArrayConstant()->value()->constant_encoding()); } else if (type->as_ClassConstant() != NULL) { ciInstanceKlass* klass = type->as_ClassConstant()->value(); if (!klass->is_loaded()) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_LIR.cpp --- a/src/share/vm/c1/c1_LIR.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_LIR.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -567,8 +567,6 @@ case lir_rem: case lir_sqrt: case lir_abs: - case lir_log: - case lir_log10: case lir_logic_and: case lir_logic_or: case lir_logic_xor: @@ -644,13 +642,16 @@ case lir_tan: case lir_sin: - case lir_cos: { + case lir_cos: + case lir_log: + case lir_log10: { assert(op->as_Op2() != NULL, "must be"); LIR_Op2* op2 = (LIR_Op2*)op; - // sin and cos need two temporary fpu stack slots, so register - // two temp operands. Register input operand as temp to - // guarantee that they do not overlap + // On x86 tan/sin/cos need two temporary fpu stack slots and + // log/log10 need one so handle opr2 and tmp as temp inputs. + // Register input operand as temp to guarantee that it doesn't + // overlap with the input. assert(op2->_info == NULL, "not used"); assert(op2->_opr1->is_valid(), "used"); do_input(op2->_opr1); do_temp(op2->_opr1); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_LIR.hpp --- a/src/share/vm/c1/c1_LIR.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_LIR.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 Sun Microsystems, 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 @@ -1840,8 +1840,8 @@ void abs (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_abs , from, tmp, to)); } void sqrt(LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_sqrt, from, tmp, to)); } - void log (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log, from, tmp, to)); } - void log10 (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log10, from, tmp, to)); } + void log (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log, from, LIR_OprFact::illegalOpr, to, tmp)); } + void log10 (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_log10, from, LIR_OprFact::illegalOpr, to, tmp)); } void sin (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_sin , from, tmp1, to, tmp2)); } void cos (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_cos , from, tmp1, to, tmp2)); } void tan (LIR_Opr from, LIR_Opr to, LIR_Opr tmp1, LIR_Opr tmp2) { append(new LIR_Op2(lir_tan , from, tmp1, to, tmp2)); } @@ -2000,7 +2000,7 @@ typedef enum { inputMode, firstMode = inputMode, tempMode, outputMode, numModes, invalidMode = -1 } OprMode; enum { - maxNumberOfOperands = 14, + maxNumberOfOperands = 16, maxNumberOfInfos = 4 }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_LIRAssembler.cpp --- a/src/share/vm/c1/c1_LIRAssembler.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_LIRAssembler.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_LIRAssembler.hpp --- a/src/share/vm/c1/c1_LIRAssembler.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_LIRAssembler.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 Sun Microsystems, 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 @@ -133,9 +133,9 @@ void add_call_info_here(CodeEmitInfo* info) { add_call_info(code_offset(), info); } // code patterns - void emit_exception_handler(); + int emit_exception_handler(); void emit_exception_entries(ExceptionInfoList* info_list); - void emit_deopt_handler(); + int emit_deopt_handler(); void emit_code(BlockList* hir); void emit_block(BlockBegin* block); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_LIRGenerator.cpp --- a/src/share/vm/c1/c1_LIRGenerator.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_LIRGenerator.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2010 Sun Microsystems, 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 @@ -440,7 +440,7 @@ __ oop2reg_patch(NULL, r, info); } else { // no patching needed - __ oop2reg(obj->encoding(), r); + __ oop2reg(obj->constant_encoding(), r); } } @@ -831,7 +831,7 @@ int taken_count_offset = md->byte_offset_of_slot(data, BranchData::taken_offset()); int not_taken_count_offset = md->byte_offset_of_slot(data, BranchData::not_taken_offset()); LIR_Opr md_reg = new_register(T_OBJECT); - __ move(LIR_OprFact::oopConst(md->encoding()), md_reg); + __ move(LIR_OprFact::oopConst(md->constant_encoding()), md_reg); LIR_Opr data_offset_reg = new_register(T_INT); __ cmove(lir_cond(cond), LIR_OprFact::intConst(taken_count_offset), @@ -1071,7 +1071,7 @@ LIR_OprList* args = new LIR_OprList(); args->append(getThreadPointer()); LIR_Opr meth = new_register(T_OBJECT); - __ oop2reg(method()->encoding(), meth); + __ oop2reg(method()->constant_encoding(), meth); args->append(meth); call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), voidType, NULL); } @@ -1765,7 +1765,7 @@ __ null_check(exception_opr, new CodeEmitInfo(info, true)); } - if (compilation()->env()->jvmti_can_post_exceptions() && + if (compilation()->env()->jvmti_can_post_on_exceptions() && !block()->is_set(BlockBegin::default_exception_handler_flag)) { // we need to go through the exception lookup path to get JVMTI // notification done @@ -1784,7 +1784,7 @@ LIR_OprList* args = new LIR_OprList(); args->append(getThreadPointer()); LIR_Opr meth = new_register(T_OBJECT); - __ oop2reg(method()->encoding(), meth); + __ oop2reg(method()->constant_encoding(), meth); args->append(meth); call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit), voidType, NULL); } @@ -1855,12 +1855,26 @@ addr = new LIR_Address(base_op, index_op->as_jint(), dst_type); } else { #ifdef X86 +#ifdef _LP64 + if (!index_op->is_illegal() && index_op->type() == T_INT) { + LIR_Opr tmp = new_pointer_register(); + __ convert(Bytecodes::_i2l, index_op, tmp); + index_op = tmp; + } +#endif addr = new LIR_Address(base_op, index_op, LIR_Address::Scale(log2_scale), 0, dst_type); #else if (index_op->is_illegal() || log2_scale == 0) { +#ifdef _LP64 + if (!index_op->is_illegal() && index_op->type() == T_INT) { + LIR_Opr tmp = new_pointer_register(); + __ convert(Bytecodes::_i2l, index_op, tmp); + index_op = tmp; + } +#endif addr = new LIR_Address(base_op, index_op, dst_type); } else { - LIR_Opr tmp = new_register(T_INT); + LIR_Opr tmp = new_pointer_register(); __ shift_left(index_op, log2_scale, tmp); addr = new LIR_Address(base_op, tmp, dst_type); } @@ -1915,10 +1929,25 @@ LIR_Opr index_op = idx.result(); if (log2_scale != 0) { // temporary fix (platform dependent code without shift on Intel would be better) - index_op = new_register(T_INT); - __ move(idx.result(), index_op); + index_op = new_pointer_register(); +#ifdef _LP64 + if(idx.result()->type() == T_INT) { + __ convert(Bytecodes::_i2l, idx.result(), index_op); + } else { +#endif + __ move(idx.result(), index_op); +#ifdef _LP64 + } +#endif __ shift_left(index_op, log2_scale, index_op); } +#ifdef _LP64 + else if(!index_op->is_illegal() && index_op->type() == T_INT) { + LIR_Opr tmp = new_pointer_register(); + __ convert(Bytecodes::_i2l, index_op, tmp); + index_op = tmp; + } +#endif LIR_Address* addr = new LIR_Address(base_op, index_op, x->basic_type()); __ move(value.result(), addr); @@ -2207,7 +2236,7 @@ LIR_OprList* args = new LIR_OprList(); args->append(getThreadPointer()); LIR_Opr meth = new_register(T_OBJECT); - __ oop2reg(method()->encoding(), meth); + __ oop2reg(method()->constant_encoding(), meth); args->append(meth); call_runtime(&signature, args, CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry), voidType, NULL); } @@ -2216,7 +2245,7 @@ LIR_Opr obj; if (method()->is_static()) { obj = new_register(T_OBJECT); - __ oop2reg(method()->holder()->java_mirror()->encoding(), obj); + __ oop2reg(method()->holder()->java_mirror()->constant_encoding(), obj); } else { Local* receiver = x->state()->local_at(0)->as_Local(); assert(receiver != NULL, "must already exist"); @@ -2660,7 +2689,7 @@ } LIR_Opr meth = new_register(T_OBJECT); - __ oop2reg(method()->encoding(), meth); + __ oop2reg(method()->constant_encoding(), meth); LIR_Opr result = increment_and_return_counter(meth, offset, InvocationCounter::count_increment); __ cmp(lir_cond_aboveEqual, result, LIR_OprFact::intConst(limit)); CodeStub* overflow = new CounterOverflowStub(info, info->bci()); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_LinearScan.cpp --- a/src/share/vm/c1/c1_LinearScan.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_LinearScan.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -2464,6 +2464,10 @@ case T_LONG: // fall through case T_DOUBLE: { +#ifdef _LP64 + scope_values->append(&_int_0_scope_value); + scope_values->append(new ConstantLongValue(c->as_jlong_bits())); +#else if (hi_word_offset_in_bytes > lo_word_offset_in_bytes) { scope_values->append(new ConstantIntValue(c->as_jint_hi_bits())); scope_values->append(new ConstantIntValue(c->as_jint_lo_bits())); @@ -2471,7 +2475,7 @@ scope_values->append(new ConstantIntValue(c->as_jint_lo_bits())); scope_values->append(new ConstantIntValue(c->as_jint_hi_bits())); } - +#endif return 2; } @@ -2503,17 +2507,18 @@ } else if (opr->is_single_cpu()) { bool is_oop = opr->is_oop_register(); int cache_idx = opr->cpu_regnr() * 2 + (is_oop ? 1 : 0); + Location::Type int_loc_type = NOT_LP64(Location::normal) LP64_ONLY(Location::int_in_long); ScopeValue* sv = _scope_value_cache.at(cache_idx); if (sv == NULL) { - Location::Type loc_type = is_oop ? Location::oop : Location::normal; + Location::Type loc_type = is_oop ? Location::oop : int_loc_type; VMReg rname = frame_map()->regname(opr); sv = new LocationValue(Location::new_reg_loc(loc_type, rname)); _scope_value_cache.at_put(cache_idx, sv); } // check if cached value is correct - DEBUG_ONLY(assert_equal(sv, new LocationValue(Location::new_reg_loc(is_oop ? Location::oop : Location::normal, frame_map()->regname(opr))))); + DEBUG_ONLY(assert_equal(sv, new LocationValue(Location::new_reg_loc(is_oop ? Location::oop : int_loc_type, frame_map()->regname(opr))))); scope_values->append(sv); return 1; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_Runtime1.cpp --- a/src/share/vm/c1/c1_Runtime1.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_Runtime1.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -110,8 +110,8 @@ RegisterMap reg_map(thread, false); frame runtime_frame = thread->last_frame(); frame caller_frame = runtime_frame.sender(®_map); - VM_DeoptimizeFrame deopt(thread, caller_frame.id()); - VMThread::execute(&deopt); + // bypass VM_DeoptimizeFrame and deoptimize the frame directly + Deoptimization::deoptimize_frame(thread, caller_frame.id()); assert(caller_is_deopted(), "Must be deoptimized"); } } @@ -354,7 +354,7 @@ JRT_ENTRY(void, Runtime1::post_jvmti_exception_throw(JavaThread* thread)) - if (JvmtiExport::can_post_exceptions()) { + if (JvmtiExport::can_post_on_exceptions()) { vframeStream vfst(thread, true); address bcp = vfst.method()->bcp_from(vfst.bci()); JvmtiExport::post_exception_throw(thread, vfst.method(), bcp, thread->exception_oop()); @@ -425,7 +425,7 @@ assert(exception.not_null(), "NULL exceptions should be handled by throw_exception"); assert(exception->is_oop(), "just checking"); // Check that exception is a subclass of Throwable, otherwise we have a VerifyError - if (!(exception->is_a(SystemDictionary::throwable_klass()))) { + if (!(exception->is_a(SystemDictionary::Throwable_klass()))) { if (ExitVMOnVerifyError) vm_exit(-1); ShouldNotReachHere(); } @@ -437,7 +437,7 @@ bool guard_pages_enabled = thread->stack_yellow_zone_enabled(); if (!guard_pages_enabled) guard_pages_enabled = thread->reguard_stack(); - if (JvmtiExport::can_post_exceptions()) { + if (JvmtiExport::can_post_on_exceptions()) { // To ensure correct notification of exception catches and throws // we have to deoptimize here. If we attempted to notify the // catches and throws during this exception lookup it's possible @@ -1075,6 +1075,7 @@ }; +// Below length is the # elements copied. template int obj_arraycopy_work(oopDesc* src, T* src_addr, oopDesc* dst, T* dst_addr, int length) { @@ -1083,22 +1084,22 @@ // barrier. The assert will fail if this is not the case. // Note that we use the non-virtual inlineable variant of write_ref_array. BarrierSet* bs = Universe::heap()->barrier_set(); - assert(bs->has_write_ref_array_opt(), - "Barrier set must have ref array opt"); + assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); + assert(bs->has_write_ref_array_pre_opt(), "For pre-barrier as well."); if (src == dst) { // same object, no check + bs->write_ref_array_pre(dst_addr, length); Copy::conjoint_oops_atomic(src_addr, dst_addr, length); - bs->write_ref_array(MemRegion((HeapWord*)dst_addr, - (HeapWord*)(dst_addr + length))); + bs->write_ref_array((HeapWord*)dst_addr, length); return ac_ok; } else { klassOop bound = objArrayKlass::cast(dst->klass())->element_klass(); klassOop stype = objArrayKlass::cast(src->klass())->element_klass(); if (stype == bound || Klass::cast(stype)->is_subtype_of(bound)) { // Elements are guaranteed to be subtypes, so no check necessary + bs->write_ref_array_pre(dst_addr, length); Copy::conjoint_oops_atomic(src_addr, dst_addr, length); - bs->write_ref_array(MemRegion((HeapWord*)dst_addr, - (HeapWord*)(dst_addr + length))); + bs->write_ref_array((HeapWord*)dst_addr, length); return ac_ok; } } @@ -1162,9 +1163,16 @@ #endif if (num == 0) return; + BarrierSet* bs = Universe::heap()->barrier_set(); + assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); + assert(bs->has_write_ref_array_pre_opt(), "For pre-barrier as well."); + if (UseCompressedOops) { + bs->write_ref_array_pre((narrowOop*)dst, num); + } else { + bs->write_ref_array_pre((oop*)dst, num); + } Copy::conjoint_oops_atomic((oop*) src, (oop*) dst, num); - BarrierSet* bs = Universe::heap()->barrier_set(); - bs->write_ref_array(MemRegion(dst, dst + num)); + bs->write_ref_array(dst, num); JRT_END diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/c1/c1_ValueType.cpp --- a/src/share/vm/c1/c1_ValueType.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/c1/c1_ValueType.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -86,7 +86,7 @@ jobject ObjectType::encoding() const { assert(is_constant(), "must be"); - return constant_value()->encoding(); + return constant_value()->constant_encoding(); } bool ObjectType::is_loaded() const { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/bcEscapeAnalyzer.hpp --- a/src/share/vm/ci/bcEscapeAnalyzer.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/bcEscapeAnalyzer.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -61,9 +61,11 @@ BCEscapeAnalyzer* _parent; int _level; + public: class ArgumentMap; class StateInfo; + private: // helper functions bool is_argument(int i) { return i >= 0 && i < _arg_size; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciCPCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/ci/ciCPCache.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,49 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciCPCache.cpp.incl" + +// ciCPCache + +// ------------------------------------------------------------------ +// ciCPCache::get_f1_offset +size_t ciCPCache::get_f1_offset(int index) { + // Calculate the offset from the constantPoolCacheOop to the f1 + // field. + ByteSize f1_offset = + constantPoolCacheOopDesc::entry_offset(index) + + ConstantPoolCacheEntry::f1_offset(); + + return in_bytes(f1_offset); +} + + +// ------------------------------------------------------------------ +// ciCPCache::print +// +// Print debugging information about the cache. +void ciCPCache::print() { + Unimplemented(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciCPCache.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/ci/ciCPCache.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciCPCache +// +// This class represents a constant pool cache. +// +// Note: This class is called ciCPCache as ciConstantPoolCache is used +// for something different. +class ciCPCache : public ciObject { +public: + ciCPCache(constantPoolCacheHandle cpcache) : ciObject(cpcache) {} + + // What kind of ciObject is this? + bool is_cpcache() const { return true; } + + // Get the offset in bytes from the oop to the f1 field of the + // requested entry. + size_t get_f1_offset(int index); + + void print(); +}; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciCallSite.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/ci/ciCallSite.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciCallSite.cpp.incl" + +// ciCallSite + +// ------------------------------------------------------------------ +// ciCallSite::get_target +// +// Return the target MethodHandle of this CallSite. +ciMethodHandle* ciCallSite::get_target() const { + VM_ENTRY_MARK; + oop method_handle_oop = java_dyn_CallSite::target(get_oop()); + return CURRENT_ENV->get_object(method_handle_oop)->as_method_handle(); +} + +// ------------------------------------------------------------------ +// ciCallSite::print +// +// Print debugging information about the CallSite. +void ciCallSite::print() { + Unimplemented(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciCallSite.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/ci/ciCallSite.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciCallSite +// +// The class represents a java.dyn.CallSite object. +class ciCallSite : public ciInstance { +public: + ciCallSite(instanceHandle h_i) : ciInstance(h_i) {} + + // What kind of ciObject is this? + bool is_call_site() const { return true; } + + // Return the target MethodHandle of this CallSite. + ciMethodHandle* get_target() const; + + void print(); +}; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciClassList.hpp --- a/src/share/vm/ci/ciClassList.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciClassList.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,6 +25,7 @@ class ciEnv; class ciObjectFactory; class ciConstantPoolCache; +class ciCPCache; class ciField; class ciConstant; @@ -42,6 +43,8 @@ class ciObject; class ciNullObject; class ciInstance; +class ciCallSite; +class ciMethodHandle; class ciMethod; class ciMethodData; class ciReceiverTypeData; // part of ciMethodData @@ -78,6 +81,7 @@ // Any more access must be given explicitly. #define CI_PACKAGE_ACCESS_TO \ friend class ciObjectFactory; \ +friend class ciCallSite; \ friend class ciConstantPoolCache; \ friend class ciField; \ friend class ciConstant; \ @@ -93,6 +97,7 @@ friend class ciInstance; \ friend class ciMethod; \ friend class ciMethodData; \ +friend class ciMethodHandle; \ friend class ciReceiverTypeData; \ friend class ciSymbol; \ friend class ciArray; \ diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciEnv.cpp --- a/src/share/vm/ci/ciEnv.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciEnv.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -38,14 +38,9 @@ ciTypeArrayKlassKlass* ciEnv::_type_array_klass_klass_instance; ciObjArrayKlassKlass* ciEnv::_obj_array_klass_klass_instance; -ciInstanceKlass* ciEnv::_ArrayStoreException; -ciInstanceKlass* ciEnv::_Class; -ciInstanceKlass* ciEnv::_ClassCastException; -ciInstanceKlass* ciEnv::_Object; -ciInstanceKlass* ciEnv::_Throwable; -ciInstanceKlass* ciEnv::_Thread; -ciInstanceKlass* ciEnv::_OutOfMemoryError; -ciInstanceKlass* ciEnv::_String; +#define WK_KLASS_DEFN(name, ignore_s, ignore_o) ciInstanceKlass* ciEnv::_##name = NULL; +WK_KLASSES_DO(WK_KLASS_DEFN) +#undef WK_KLASS_DEFN ciSymbol* ciEnv::_unloaded_cisymbol = NULL; ciInstanceKlass* ciEnv::_unloaded_ciinstance_klass = NULL; @@ -110,6 +105,8 @@ _ArrayIndexOutOfBoundsException_instance = NULL; _ArrayStoreException_instance = NULL; _ClassCastException_instance = NULL; + _the_null_string = NULL; + _the_min_jint_string = NULL; } ciEnv::ciEnv(Arena* arena) { @@ -163,6 +160,8 @@ _ArrayIndexOutOfBoundsException_instance = NULL; _ArrayStoreException_instance = NULL; _ClassCastException_instance = NULL; + _the_null_string = NULL; + _the_min_jint_string = NULL; } ciEnv::~ciEnv() { @@ -179,7 +178,7 @@ _jvmti_can_hotswap_or_post_breakpoint = JvmtiExport::can_hotswap_or_post_breakpoint(); _jvmti_can_examine_or_deopt_anywhere = JvmtiExport::can_examine_or_deopt_anywhere(); _jvmti_can_access_local_variables = JvmtiExport::can_access_local_variables(); - _jvmti_can_post_exceptions = JvmtiExport::can_post_exceptions(); + _jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions(); } // ------------------------------------------------------------------ @@ -248,6 +247,22 @@ return _ClassCastException_instance; } +ciInstance* ciEnv::the_null_string() { + if (_the_null_string == NULL) { + VM_ENTRY_MARK; + _the_null_string = get_object(Universe::the_null_string())->as_instance(); + } + return _the_null_string; +} + +ciInstance* ciEnv::the_min_jint_string() { + if (_the_min_jint_string == NULL) { + VM_ENTRY_MARK; + _the_min_jint_string = get_object(Universe::the_min_jint_string())->as_instance(); + } + return _the_min_jint_string; +} + // ------------------------------------------------------------------ // ciEnv::get_method_from_handle ciMethod* ciEnv::get_method_from_handle(jobject method) { @@ -257,7 +272,7 @@ // ------------------------------------------------------------------ // ciEnv::make_array -ciArray* ciEnv::make_array(GrowableArray* objects) { +ciArray* ciEnv::make_system_array(GrowableArray* objects) { VM_ENTRY_MARK; int length = objects->length(); objArrayOop a = oopFactory::new_system_objArray(length, THREAD); @@ -419,12 +434,11 @@ // ciEnv::get_klass_by_index_impl // // Implementation of get_klass_by_index. -ciKlass* ciEnv::get_klass_by_index_impl(ciInstanceKlass* accessor, +ciKlass* ciEnv::get_klass_by_index_impl(constantPoolHandle cpool, int index, - bool& is_accessible) { - assert(accessor->get_instanceKlass()->is_linked(), "must be linked before accessing constant pool"); + bool& is_accessible, + ciInstanceKlass* accessor) { EXCEPTION_CONTEXT; - constantPoolHandle cpool(THREAD, accessor->get_instanceKlass()->constants()); KlassHandle klass (THREAD, constantPoolOopDesc::klass_at_if_loaded(cpool, index)); symbolHandle klass_name; if (klass.is_null()) { @@ -486,22 +500,21 @@ // ciEnv::get_klass_by_index // // Get a klass from the constant pool. -ciKlass* ciEnv::get_klass_by_index(ciInstanceKlass* accessor, +ciKlass* ciEnv::get_klass_by_index(constantPoolHandle cpool, int index, - bool& is_accessible) { - GUARDED_VM_ENTRY(return get_klass_by_index_impl(accessor, index, is_accessible);) + bool& is_accessible, + ciInstanceKlass* accessor) { + GUARDED_VM_ENTRY(return get_klass_by_index_impl(cpool, index, is_accessible, accessor);) } // ------------------------------------------------------------------ // ciEnv::get_constant_by_index_impl // // Implementation of get_constant_by_index(). -ciConstant ciEnv::get_constant_by_index_impl(ciInstanceKlass* accessor, - int index) { +ciConstant ciEnv::get_constant_by_index_impl(constantPoolHandle cpool, + int index, + ciInstanceKlass* accessor) { EXCEPTION_CONTEXT; - instanceKlass* ik_accessor = accessor->get_instanceKlass(); - assert(ik_accessor->is_linked(), "must be linked before accessing constant pool"); - constantPoolOop cpool = ik_accessor->constants(); constantTag tag = cpool->tag_at(index); if (tag.is_int()) { return ciConstant(T_INT, (jint)cpool->int_at(index)); @@ -529,7 +542,7 @@ } else if (tag.is_klass() || tag.is_unresolved_klass()) { // 4881222: allow ldc to take a class type bool ignore; - ciKlass* klass = get_klass_by_index_impl(accessor, index, ignore); + ciKlass* klass = get_klass_by_index_impl(cpool, index, ignore, accessor); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; record_out_of_memory_failure(); @@ -538,6 +551,11 @@ assert (klass->is_instance_klass() || klass->is_array_klass(), "must be an instance or array klass "); return ciConstant(T_OBJECT, klass); + } else if (tag.is_object()) { + oop obj = cpool->object_at(index); + assert(obj->is_instance(), "must be an instance"); + ciObject* ciobj = get_object(obj); + return ciConstant(T_OBJECT, ciobj); } else { ShouldNotReachHere(); return ciConstant(); @@ -574,9 +592,10 @@ // Pull a constant out of the constant pool. How appropriate. // // Implementation note: this query is currently in no way cached. -ciConstant ciEnv::get_constant_by_index(ciInstanceKlass* accessor, - int index) { - GUARDED_VM_ENTRY(return get_constant_by_index_impl(accessor, index); ) +ciConstant ciEnv::get_constant_by_index(constantPoolHandle cpool, + int index, + ciInstanceKlass* accessor) { + GUARDED_VM_ENTRY(return get_constant_by_index_impl(cpool, index, accessor);) } // ------------------------------------------------------------------ @@ -586,7 +605,7 @@ // // Implementation note: this query is currently in no way cached. bool ciEnv::is_unresolved_string(ciInstanceKlass* accessor, - int index) const { + int index) const { GUARDED_VM_ENTRY(return is_unresolved_string_impl(accessor->get_instanceKlass(), index); ) } @@ -597,7 +616,7 @@ // // Implementation note: this query is currently in no way cached. bool ciEnv::is_unresolved_klass(ciInstanceKlass* accessor, - int index) const { + int index) const { GUARDED_VM_ENTRY(return is_unresolved_klass_impl(accessor->get_instanceKlass(), index); ) } @@ -678,22 +697,17 @@ // ------------------------------------------------------------------ // ciEnv::get_method_by_index_impl -ciMethod* ciEnv::get_method_by_index_impl(ciInstanceKlass* accessor, - int index, Bytecodes::Code bc) { - // Get the method's declared holder. - - assert(accessor->get_instanceKlass()->is_linked(), "must be linked before accessing constant pool"); - constantPoolHandle cpool = accessor->get_instanceKlass()->constants(); +ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool, + int index, Bytecodes::Code bc, + ciInstanceKlass* accessor) { int holder_index = cpool->klass_ref_index_at(index); bool holder_is_accessible; - ciKlass* holder = get_klass_by_index_impl(accessor, holder_index, holder_is_accessible); + ciKlass* holder = get_klass_by_index_impl(cpool, holder_index, holder_is_accessible, accessor); ciInstanceKlass* declared_holder = get_instance_klass_for_declared_method_holder(holder); // Get the method's name and signature. - int nt_index = cpool->name_and_type_ref_index_at(index); - int sig_index = cpool->signature_ref_index_at(nt_index); symbolOop name_sym = cpool->name_ref_at(index); - symbolOop sig_sym = cpool->symbol_at(sig_index); + symbolOop sig_sym = cpool->signature_ref_at(index); if (holder_is_accessible) { // Our declared holder is loaded. instanceKlass* lookup = declared_holder->get_instanceKlass(); @@ -715,6 +729,33 @@ // ------------------------------------------------------------------ +// ciEnv::get_fake_invokedynamic_method_impl +ciMethod* ciEnv::get_fake_invokedynamic_method_impl(constantPoolHandle cpool, + int index, Bytecodes::Code bc) { + assert(bc == Bytecodes::_invokedynamic, "must be invokedynamic"); + + // Get the CallSite from the constant pool cache. + ConstantPoolCacheEntry* cpc_entry = cpool->cache()->secondary_entry_at(index); + assert(cpc_entry != NULL && cpc_entry->is_secondary_entry(), "sanity"); + Handle call_site = cpc_entry->f1(); + + // Call site might not be linked yet. + if (call_site.is_null()) { + ciInstanceKlass* mh_klass = get_object(SystemDictionary::MethodHandle_klass())->as_instance_klass(); + ciSymbol* sig_sym = get_object(cpool->signature_ref_at(index))->as_symbol(); + return get_unloaded_method(mh_klass, ciSymbol::invoke_name(), sig_sym); + } + + // Get the methodOop from the CallSite. + methodOop method_oop = (methodOop) java_dyn_CallSite::vmmethod(call_site()); + assert(method_oop != NULL, "sanity"); + assert(method_oop->is_method_handle_invoke(), "consistent"); + + return get_object(method_oop)->as_method(); +} + + +// ------------------------------------------------------------------ // ciEnv::get_instance_klass_for_declared_method_holder ciInstanceKlass* ciEnv::get_instance_klass_for_declared_method_holder(ciKlass* method_holder) { // For the case of .clone(), the method holder can be a ciArrayKlass @@ -736,15 +777,19 @@ } - - // ------------------------------------------------------------------ // ciEnv::get_method_by_index -ciMethod* ciEnv::get_method_by_index(ciInstanceKlass* accessor, - int index, Bytecodes::Code bc) { - GUARDED_VM_ENTRY(return get_method_by_index_impl(accessor, index, bc);) +ciMethod* ciEnv::get_method_by_index(constantPoolHandle cpool, + int index, Bytecodes::Code bc, + ciInstanceKlass* accessor) { + if (bc == Bytecodes::_invokedynamic) { + GUARDED_VM_ENTRY(return get_fake_invokedynamic_method_impl(cpool, index, bc);) + } else { + GUARDED_VM_ENTRY(return get_method_by_index_impl(cpool, index, bc, accessor);) + } } + // ------------------------------------------------------------------ // ciEnv::name_buffer char *ciEnv::name_buffer(int req_len) { @@ -846,8 +891,8 @@ JvmtiExport::can_examine_or_deopt_anywhere()) || (!jvmti_can_access_local_variables() && JvmtiExport::can_access_local_variables()) || - (!jvmti_can_post_exceptions() && - JvmtiExport::can_post_exceptions()) )) { + (!jvmti_can_post_on_exceptions() && + JvmtiExport::can_post_on_exceptions()) )) { record_failure("Jvmti state change invalidated dependencies"); } @@ -917,18 +962,10 @@ if (nm == NULL) { // The CodeCache is full. Print out warning and disable compilation. record_failure("code cache is full"); - UseInterpreter = true; - if (UseCompiler || AlwaysCompileLoopMethods ) { -#ifndef PRODUCT - warning("CodeCache is full. Compiler has been disabled"); - if (CompileTheWorld || ExitOnFullCodeCache) { - before_exit(JavaThread::current()); - exit_globals(); // will delete tty - vm_direct_exit(CompileTheWorld ? 0 : 1); - } -#endif - UseCompiler = false; - AlwaysCompileLoopMethods = false; + { + MutexUnlocker ml(Compile_lock); + MutexUnlocker locker(MethodCompileQueue_lock); + CompileBroker::handle_full_code_cache(); } } else { NOT_PRODUCT(nm->set_has_debug_info(has_debug_info); ) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciEnv.hpp --- a/src/share/vm/ci/ciEnv.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciEnv.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -57,7 +57,7 @@ bool _jvmti_can_hotswap_or_post_breakpoint; bool _jvmti_can_examine_or_deopt_anywhere; bool _jvmti_can_access_local_variables; - bool _jvmti_can_post_exceptions; + bool _jvmti_can_post_on_exceptions; // Cache DTrace flags bool _dtrace_extended_probes; @@ -74,14 +74,9 @@ static ciTypeArrayKlassKlass* _type_array_klass_klass_instance; static ciObjArrayKlassKlass* _obj_array_klass_klass_instance; - static ciInstanceKlass* _ArrayStoreException; - static ciInstanceKlass* _Class; - static ciInstanceKlass* _ClassCastException; - static ciInstanceKlass* _Object; - static ciInstanceKlass* _Throwable; - static ciInstanceKlass* _Thread; - static ciInstanceKlass* _OutOfMemoryError; - static ciInstanceKlass* _String; +#define WK_KLASS_DECL(name, ignore_s, ignore_o) static ciInstanceKlass* _##name; + WK_KLASSES_DO(WK_KLASS_DECL) +#undef WK_KLASS_DECL static ciSymbol* _unloaded_cisymbol; static ciInstanceKlass* _unloaded_ciinstance_klass; @@ -97,6 +92,9 @@ ciInstance* _ArrayStoreException_instance; ciInstance* _ClassCastException_instance; + ciInstance* _the_null_string; // The Java string "null" + ciInstance* _the_min_jint_string; // The Java string "-2147483648" + // Look up a klass by name from a particular class loader (the accessor's). // If require_local, result must be defined in that class loader, or NULL. // If !require_local, a result from remote class loader may be reported, @@ -114,37 +112,45 @@ bool require_local); // Constant pool access. - ciKlass* get_klass_by_index(ciInstanceKlass* loading_klass, + ciKlass* get_klass_by_index(constantPoolHandle cpool, int klass_index, - bool& is_accessible); - ciConstant get_constant_by_index(ciInstanceKlass* loading_klass, - int constant_index); + bool& is_accessible, + ciInstanceKlass* loading_klass); + ciConstant get_constant_by_index(constantPoolHandle cpool, + int constant_index, + ciInstanceKlass* accessor); bool is_unresolved_string(ciInstanceKlass* loading_klass, int constant_index) const; bool is_unresolved_klass(ciInstanceKlass* loading_klass, int constant_index) const; ciField* get_field_by_index(ciInstanceKlass* loading_klass, int field_index); - ciMethod* get_method_by_index(ciInstanceKlass* loading_klass, - int method_index, Bytecodes::Code bc); + ciMethod* get_method_by_index(constantPoolHandle cpool, + int method_index, Bytecodes::Code bc, + ciInstanceKlass* loading_klass); // Implementation methods for loading and constant pool access. ciKlass* get_klass_by_name_impl(ciKlass* accessing_klass, ciSymbol* klass_name, bool require_local); - ciKlass* get_klass_by_index_impl(ciInstanceKlass* loading_klass, + ciKlass* get_klass_by_index_impl(constantPoolHandle cpool, int klass_index, - bool& is_accessible); - ciConstant get_constant_by_index_impl(ciInstanceKlass* loading_klass, - int constant_index); + bool& is_accessible, + ciInstanceKlass* loading_klass); + ciConstant get_constant_by_index_impl(constantPoolHandle cpool, + int constant_index, + ciInstanceKlass* loading_klass); bool is_unresolved_string_impl (instanceKlass* loading_klass, int constant_index) const; bool is_unresolved_klass_impl (instanceKlass* loading_klass, int constant_index) const; ciField* get_field_by_index_impl(ciInstanceKlass* loading_klass, int field_index); - ciMethod* get_method_by_index_impl(ciInstanceKlass* loading_klass, - int method_index, Bytecodes::Code bc); + ciMethod* get_method_by_index_impl(constantPoolHandle cpool, + int method_index, Bytecodes::Code bc, + ciInstanceKlass* loading_klass); + ciMethod* get_fake_invokedynamic_method_impl(constantPoolHandle cpool, + int index, Bytecodes::Code bc); // Helper methods bool check_klass_accessibility(ciKlass* accessing_klass, @@ -253,7 +259,7 @@ bool jvmti_can_hotswap_or_post_breakpoint() const { return _jvmti_can_hotswap_or_post_breakpoint; } bool jvmti_can_examine_or_deopt_anywhere() const { return _jvmti_can_examine_or_deopt_anywhere; } bool jvmti_can_access_local_variables() const { return _jvmti_can_access_local_variables; } - bool jvmti_can_post_exceptions() const { return _jvmti_can_post_exceptions; } + bool jvmti_can_post_on_exceptions() const { return _jvmti_can_post_on_exceptions; } // Cache DTrace flags void cache_dtrace_flags(); @@ -286,30 +292,13 @@ // Access to certain well known ciObjects. - ciInstanceKlass* ArrayStoreException_klass() { - return _ArrayStoreException; - } - ciInstanceKlass* Class_klass() { - return _Class; - } - ciInstanceKlass* ClassCastException_klass() { - return _ClassCastException; - } - ciInstanceKlass* Object_klass() { - return _Object; +#define WK_KLASS_FUNC(name, ignore_s, ignore_o) \ + ciInstanceKlass* name() { \ + return _##name;\ } - ciInstanceKlass* Throwable_klass() { - return _Throwable; - } - ciInstanceKlass* Thread_klass() { - return _Thread; - } - ciInstanceKlass* OutOfMemoryError_klass() { - return _OutOfMemoryError; - } - ciInstanceKlass* String_klass() { - return _String; - } + WK_KLASSES_DO(WK_KLASS_FUNC) +#undef WK_KLASS_FUNC + ciInstance* NullPointerException_instance() { assert(_NullPointerException_instance != NULL, "initialization problem"); return _NullPointerException_instance; @@ -324,6 +313,9 @@ ciInstance* ArrayStoreException_instance(); ciInstance* ClassCastException_instance(); + ciInstance* the_null_string(); + ciInstance* the_min_jint_string(); + static ciSymbol* unloaded_cisymbol() { return _unloaded_cisymbol; } @@ -339,8 +331,8 @@ // but consider adding to vmSymbols.hpp instead. // Use this to make a holder for non-perm compile time constants. - // The resulting array is guaranteed to satisfy "has_encoding". - ciArray* make_array(GrowableArray* objects); + // The resulting array is guaranteed to satisfy "can_be_constant". + ciArray* make_system_array(GrowableArray* objects); // converts the ciKlass* representing the holder of a method into a // ciInstanceKlass*. This is needed since the holder of a method in diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciExceptionHandler.cpp --- a/src/share/vm/ci/ciExceptionHandler.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciExceptionHandler.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -34,12 +34,16 @@ // // Get the exception klass that this handler catches. ciInstanceKlass* ciExceptionHandler::catch_klass() { + VM_ENTRY_MARK; assert(!is_catch_all(), "bad index"); if (_catch_klass == NULL) { bool will_link; - ciKlass* k = CURRENT_ENV->get_klass_by_index(_loading_klass, + assert(_loading_klass->get_instanceKlass()->is_linked(), "must be linked before accessing constant pool"); + constantPoolHandle cpool(_loading_klass->get_instanceKlass()->constants()); + ciKlass* k = CURRENT_ENV->get_klass_by_index(cpool, _catch_klass_index, - will_link); + will_link, + _loading_klass); if (!will_link && k->is_loaded()) { GUARDED_VM_ENTRY( k = CURRENT_ENV->get_unloaded_klass(_loading_klass, k->name()); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciField.cpp --- a/src/share/vm/ci/ciField.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciField.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -86,7 +86,7 @@ bool ignore; // This is not really a class reference; the index always refers to the // field's type signature, as a symbol. Linkage checks do not apply. - _type = ciEnv::current(thread)->get_klass_by_index(klass, sig_index, ignore); + _type = ciEnv::current(thread)->get_klass_by_index(cpool, sig_index, ignore, klass); } else { _type = ciType::make(field_type); } @@ -100,9 +100,9 @@ int holder_index = cpool->klass_ref_index_at(index); bool holder_is_accessible; ciInstanceKlass* declared_holder = - ciEnv::current(thread)->get_klass_by_index(klass, holder_index, - holder_is_accessible) - ->as_instance_klass(); + ciEnv::current(thread)->get_klass_by_index(cpool, holder_index, + holder_is_accessible, + klass)->as_instance_klass(); // The declared holder of this field may not have been loaded. // Bail out with partial field information. @@ -161,6 +161,18 @@ "bootstrap classes must not create & cache unshared fields"); } +static bool trust_final_non_static_fields(ciInstanceKlass* holder) { + if (holder == NULL) + return false; + if (holder->name() == ciSymbol::java_lang_System()) + // Never trust strangely unstable finals: System.out, etc. + return false; + // Even if general trusting is disabled, trust system-built closures in these packages. + if (holder->is_in_package("java/dyn") || holder->is_in_package("sun/dyn")) + return true; + return TrustFinalNonStaticFields; +} + void ciField::initialize_from(fieldDescriptor* fd) { // Get the flags, offset, and canonical holder of the field. _flags = ciFlags(fd->access_flags()); @@ -168,8 +180,18 @@ _holder = CURRENT_ENV->get_object(fd->field_holder())->as_instance_klass(); // Check to see if the field is constant. - if (_holder->is_initialized() && - this->is_final() && this->is_static()) { + if (_holder->is_initialized() && this->is_final()) { + if (!this->is_static()) { + // A field can be constant if it's a final static field or if it's + // a final non-static field of a trusted class ({java,sun}.dyn). + if (trust_final_non_static_fields(_holder)) { + _is_constant = true; + return; + } + _is_constant = false; + return; + } + // This field just may be constant. The only cases where it will // not be constant are: // @@ -182,8 +204,8 @@ // java.lang.System.out, and java.lang.System.err. klassOop k = _holder->get_klassOop(); - assert( SystemDictionary::system_klass() != NULL, "Check once per vm"); - if( k == SystemDictionary::system_klass() ) { + assert( SystemDictionary::System_klass() != NULL, "Check once per vm"); + if( k == SystemDictionary::System_klass() ) { // Check offsets for case 2: System.in, System.out, or System.err if( _offset == java_lang_System::in_offset_in_bytes() || _offset == java_lang_System::out_offset_in_bytes() || diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciField.hpp --- a/src/share/vm/ci/ciField.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciField.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -138,10 +138,18 @@ // Get the constant value of this field. ciConstant constant_value() { - assert(is_constant(), "illegal call to constant_value()"); + assert(is_static() && is_constant(), "illegal call to constant_value()"); return _constant_value; } + // Get the constant value of non-static final field in the given + // object. + ciConstant constant_value_of(ciObject* object) { + assert(!is_static() && is_constant(), "only if field is non-static constant"); + assert(object->is_instance(), "must be instance"); + return object->as_instance()->field_value(this); + } + // Check for link time errors. Accessing a field from a // certain class via a certain bytecode may or may not be legal. // This call checks to see if an exception may be raised by diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciInstance.cpp --- a/src/share/vm/ci/ciInstance.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciInstance.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -36,7 +36,7 @@ VM_ENTRY_MARK; oop m = get_oop(); // Return NULL if it is not java.lang.Class. - if (m == NULL || m->klass() != SystemDictionary::class_klass()) { + if (m == NULL || m->klass() != SystemDictionary::Class_klass()) { return NULL; } // Return either a primitive type or a klass. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciInstanceKlass.cpp --- a/src/share/vm/ci/ciInstanceKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciInstanceKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -75,7 +75,7 @@ _java_mirror = NULL; if (is_shared()) { - if (h_k() != SystemDictionary::object_klass()) { + if (h_k() != SystemDictionary::Object_klass()) { super(); } java_mirror(); @@ -232,8 +232,48 @@ // ------------------------------------------------------------------ // ciInstanceKlass::uses_default_loader bool ciInstanceKlass::uses_default_loader() { - VM_ENTRY_MARK; - return loader() == NULL; + // Note: We do not need to resolve the handle or enter the VM + // in order to test null-ness. + return _loader == NULL; +} + +// ------------------------------------------------------------------ +// ciInstanceKlass::is_in_package +// +// Is this klass in the given package? +bool ciInstanceKlass::is_in_package(const char* packagename, int len) { + // To avoid class loader mischief, this test always rejects application classes. + if (!uses_default_loader()) + return false; + GUARDED_VM_ENTRY( + return is_in_package_impl(packagename, len); + ) +} + +bool ciInstanceKlass::is_in_package_impl(const char* packagename, int len) { + ASSERT_IN_VM; + + // If packagename contains trailing '/' exclude it from the + // prefix-test since we test for it explicitly. + if (packagename[len - 1] == '/') + len--; + + if (!name()->starts_with(packagename, len)) + return false; + + // Test if the class name is something like "java/lang". + if ((len + 1) > name()->utf8_length()) + return false; + + // Test for trailing '/' + if ((char) name()->byte_at(len) != '/') + return false; + + // Make sure it's not actually in a subpackage: + if (name()->index_of_at(len+1, "/", 1) >= 0) + return false; + + return true; } // ------------------------------------------------------------------ @@ -341,6 +381,20 @@ } // ------------------------------------------------------------------ +// ciInstanceKlass::get_field_by_name +ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static) { + VM_ENTRY_MARK; + instanceKlass* k = get_instanceKlass(); + fieldDescriptor fd; + klassOop def = k->find_field(name->get_symbolOop(), signature->get_symbolOop(), is_static, &fd); + if (def == NULL) { + return NULL; + } + ciField* field = new (CURRENT_THREAD_ENV->arena()) ciField(&fd); + return field; +} + +// ------------------------------------------------------------------ // ciInstanceKlass::non_static_fields. class NonStaticFieldFiller: public FieldClosure { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciInstanceKlass.hpp --- a/src/share/vm/ci/ciInstanceKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciInstanceKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -29,10 +29,11 @@ // be loaded. class ciInstanceKlass : public ciKlass { CI_PACKAGE_ACCESS + friend class ciBytecodeStream; friend class ciEnv; + friend class ciExceptionHandler; friend class ciMethod; friend class ciField; - friend class ciBytecodeStream; private: jobject _loader; @@ -78,6 +79,8 @@ const char* type_string() { return "ciInstanceKlass"; } + bool is_in_package_impl(const char* packagename, int len); + void print_impl(outputStream* st); ciConstantPoolCache* field_cache(); @@ -148,6 +151,7 @@ ciInstanceKlass* get_canonical_holder(int offset); ciField* get_field_by_offset(int field_offset, bool is_static); + ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static); GrowableArray* non_static_fields(); @@ -195,6 +199,12 @@ bool is_java_lang_Object(); + // Is this klass in the given package? + bool is_in_package(const char* packagename) { + return is_in_package(packagename, (int) strlen(packagename)); + } + bool is_in_package(const char* packagename, int len); + // What kind of ciObject is this? bool is_instance_klass() { return true; } bool is_java_klass() { return true; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciKlass.cpp --- a/src/share/vm/ci/ciKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciKlass.hpp --- a/src/share/vm/ci/ciKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -69,7 +69,7 @@ ciKlass(KlassHandle k_h); // What is the name of this klass? - ciSymbol* name() { return _name; } + ciSymbol* name() const { return _name; } // What is its layout helper value? jint layout_helper() { return _layout_helper; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciMethod.cpp --- a/src/share/vm/ci/ciMethod.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciMethod.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -325,10 +325,10 @@ } // ------------------------------------------------------------------ -// ciMethod::liveness_at_bci +// ciMethod::raw_liveness_at_bci // // Which local variables are live at a specific bci? -MethodLivenessResult ciMethod::liveness_at_bci(int bci) { +MethodLivenessResult ciMethod::raw_liveness_at_bci(int bci) { check_is_loaded(); if (_liveness == NULL) { // Create the liveness analyzer. @@ -336,7 +336,17 @@ _liveness = new (arena) MethodLiveness(arena, this); _liveness->compute_liveness(); } - MethodLivenessResult result = _liveness->get_liveness_at(bci); + return _liveness->get_liveness_at(bci); +} + +// ------------------------------------------------------------------ +// ciMethod::liveness_at_bci +// +// Which local variables are live at a specific bci? When debugging +// will return true for all locals in some cases to improve debug +// information. +MethodLivenessResult ciMethod::liveness_at_bci(int bci) { + MethodLivenessResult result = raw_liveness_at_bci(bci); if (CURRENT_ENV->jvmti_can_access_local_variables() || DeoptimizeALot || CompileTheWorld) { // Keep all locals live for the user's edification and amusement. result.at_put_range(0, result.size(), true); @@ -426,15 +436,21 @@ // we will set result._method also. } // Determine call site's morphism. - // The call site count could be == (receivers_count_total + 1) - // not only in the case of a polymorphic call but also in the case - // when a method data snapshot is taken after the site count was updated - // but before receivers counters were updated. - if (morphism == result._limit) { - // There were no array klasses and morphism <= MorphismLimit. - if (morphism < ciCallProfile::MorphismLimit || - morphism == ciCallProfile::MorphismLimit && - (receivers_count_total+1) >= count) { + // The call site count is 0 with known morphism (onlt 1 or 2 receivers) + // or < 0 in the case of a type check failured for checkcast, aastore, instanceof. + // The call site count is > 0 in the case of a polymorphic virtual call. + if (morphism > 0 && morphism == result._limit) { + // The morphism <= MorphismLimit. + if ((morphism < ciCallProfile::MorphismLimit) || + (morphism == ciCallProfile::MorphismLimit && count == 0)) { +#ifdef ASSERT + if (count > 0) { + this->print_short_name(tty); + tty->print_cr(" @ bci:%d", bci); + this->print_codes(); + assert(false, "this call site should not be polymorphic"); + } +#endif result._morphism = morphism; } } @@ -442,10 +458,8 @@ // zero or less, presume that this is a typecheck profile and // do nothing. Otherwise, increase count to be the sum of all // receiver's counts. - if (count > 0) { - if (count < receivers_count_total) { - count = receivers_count_total; - } + if (count >= 0) { + count += receivers_count_total; } } result._count = count; @@ -677,7 +691,7 @@ // ------------------------------------------------------------------ // invokedynamic support // -bool ciMethod::is_method_handle_invoke() { +bool ciMethod::is_method_handle_invoke() const { check_is_loaded(); bool flag = ((flags().as_int() & JVM_MH_INVOKE_BITS) == JVM_MH_INVOKE_BITS); #ifdef ASSERT @@ -690,6 +704,12 @@ return flag; } +bool ciMethod::is_method_handle_adapter() const { + check_is_loaded(); + VM_ENTRY_MARK; + return get_methodOop()->is_method_handle_adapter(); +} + ciInstance* ciMethod::method_handle_type() { check_is_loaded(); VM_ENTRY_MARK; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciMethod.hpp --- a/src/share/vm/ci/ciMethod.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciMethod.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -38,6 +38,8 @@ CI_PACKAGE_ACCESS friend class ciEnv; friend class ciExceptionHandlerStream; + friend class ciBytecodeStream; + friend class ciMethodHandle; private: // General method information. @@ -149,6 +151,12 @@ bool has_monitor_bytecodes() const { return _uses_monitors; } bool has_balanced_monitors(); + // Returns a bitmap indicating which locals are required to be + // maintained as live for deopt. raw_liveness_at_bci is always the + // direct output of the liveness computation while liveness_at_bci + // may mark all locals as live to improve support for debugging Java + // code by maintaining the state of as many locals as possible. + MethodLivenessResult raw_liveness_at_bci(int bci); MethodLivenessResult liveness_at_bci(int bci); // Get the interpreters viewpoint on oop liveness. MethodLiveness is @@ -207,7 +215,10 @@ bool check_call(int refinfo_index, bool is_static) const; void build_method_data(); // make sure it exists in the VM also int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC - bool is_method_handle_invoke(); + + // JSR 292 support + bool is_method_handle_invoke() const; + bool is_method_handle_adapter() const; ciInstance* method_handle_type(); // What kind of ciObject is this? @@ -245,4 +256,10 @@ // Print the name of this method in various incarnations. void print_name(outputStream* st = tty); void print_short_name(outputStream* st = tty); + + methodOop get_method_handle_target() { + klassOop receiver_limit_oop = NULL; + int flags = 0; + return MethodHandles::decode_method(get_oop(), receiver_limit_oop, flags); + } }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciMethodHandle.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/ci/ciMethodHandle.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,52 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_ciMethodHandle.cpp.incl" + +// ciMethodHandle + +// ------------------------------------------------------------------ +// ciMethodHandle::get_adapter +// +// Return an adapter for this MethodHandle. +ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const { + VM_ENTRY_MARK; + + Handle h(get_oop()); + methodHandle callee(_callee->get_methodOop()); + MethodHandleCompiler mhc(h, callee, is_invokedynamic, THREAD); + methodHandle m = mhc.compile(CHECK_NULL); + return CURRENT_ENV->get_object(m())->as_method(); +} + + +// ------------------------------------------------------------------ +// ciMethodHandle::print_impl +// +// Implementation of the print method. +void ciMethodHandle::print_impl(outputStream* st) { + st->print(" type="); + get_oop()->print(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciMethodHandle.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/ci/ciMethodHandle.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,56 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +// ciMethodHandle +// +// The class represents a java.dyn.MethodHandle object. +class ciMethodHandle : public ciInstance { +private: + ciMethod* _callee; + + // Return an adapter for this MethodHandle. + ciMethod* get_adapter(bool is_invokedynamic) const; + +protected: + void print_impl(outputStream* st); + +public: + ciMethodHandle(instanceHandle h_i) : ciInstance(h_i) {}; + + // What kind of ciObject is this? + bool is_method_handle() const { return true; } + + ciMethod* callee() const { return _callee; } + void set_callee(ciMethod* m) { _callee = m; } + + // Return an adapter for a MethodHandle call. + ciMethod* get_method_handle_adapter() const { + return get_adapter(false); + } + + // Return an adapter for an invokedynamic call. + ciMethod* get_invokedynamic_adapter() const { + return get_adapter(true); + } +}; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciObject.cpp --- a/src/share/vm/ci/ciObject.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciObject.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -55,6 +55,7 @@ } _klass = NULL; _ident = 0; + init_flags_from(o); } // ------------------------------------------------------------------ @@ -69,6 +70,7 @@ } _klass = NULL; _ident = 0; + init_flags_from(h()); } // ------------------------------------------------------------------ @@ -158,7 +160,7 @@ } // ------------------------------------------------------------------ -// ciObject::encoding +// ciObject::constant_encoding // // The address which the compiler should embed into the // generated code to represent this oop. This address @@ -172,16 +174,24 @@ // // This method should be changed to return an generified address // to discourage use of the JNI handle. -jobject ciObject::encoding() { +jobject ciObject::constant_encoding() { assert(is_null_object() || handle() != NULL, "cannot embed null pointer"); - assert(has_encoding(), "oop must be NULL or perm"); + assert(can_be_constant(), "oop must be NULL or perm"); return handle(); } // ------------------------------------------------------------------ -// ciObject::has_encoding -bool ciObject::has_encoding() { - return handle() == NULL || is_perm(); +// ciObject::can_be_constant +bool ciObject::can_be_constant() { + if (ScavengeRootsInCode >= 1) return true; // now everybody can encode as a constant + return handle() == NULL || !is_scavengable(); +} + +// ------------------------------------------------------------------ +// ciObject::should_be_constant() +bool ciObject::should_be_constant() { + if (ScavengeRootsInCode >= 2) return true; // force everybody to be a constant + return handle() == NULL || !is_scavengable(); } @@ -195,8 +205,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%s address=0x%x>", ident(), is_perm() ? "PERM" : "", + is_scavengable() ? "SCAVENGABLE" : "", (address)this); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciObject.hpp --- a/src/share/vm/ci/ciObject.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciObject.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -51,9 +51,10 @@ ciKlass* _klass; uint _ident; - enum { FLAG_BITS = 1}; + enum { FLAG_BITS = 2 }; enum { - PERM_FLAG = 1 + PERM_FLAG = 1, + SCAVENGABLE_FLAG = 2 }; protected: ciObject(); @@ -68,8 +69,15 @@ return JNIHandles::resolve_non_null(_handle); } - void set_perm() { - _ident |= PERM_FLAG; + void init_flags_from(oop x) { + int flags = 0; + if (x != NULL) { + if (x->is_perm()) + flags |= PERM_FLAG; + if (x->is_scavengable()) + flags |= SCAVENGABLE_FLAG; + } + _ident |= flags; } // Virtual behavior of the print() method. @@ -91,17 +99,27 @@ // A hash value for the convenience of compilers. int hash(); - // Tells if this oop has an encoding. (I.e., is it null or perm?) + // Tells if this oop has an encoding as a constant. + // True if is_scavengable is false. + // Also true if ScavengeRootsInCode is non-zero. // If it does not have an encoding, the compiler is responsible for // making other arrangements for dealing with the oop. - // See ciEnv::make_perm_array - bool has_encoding(); + // See ciEnv::make_array + bool can_be_constant(); + + // Tells if this oop should be made a constant. + // True if is_scavengable is false or ScavengeRootsInCode > 1. + bool should_be_constant(); // Is this object guaranteed to be in the permanent part of the heap? // If so, CollectedHeap::can_elide_permanent_oop_store_barriers is relevant. // If the answer is false, no guarantees are made. bool is_perm() { return (_ident & PERM_FLAG) != 0; } + // Might this object possibly move during a scavenge operation? + // If the answer is true and ScavengeRootsInCode==0, the oop cannot be embedded in code. + bool is_scavengable() { return (_ident & SCAVENGABLE_FLAG) != 0; } + // The address which the compiler should embed into the // generated code to represent this oop. This address // is not the true address of the oop -- it will get patched @@ -109,13 +127,16 @@ // // Usage note: no address arithmetic allowed. Oop must // be registered with the oopRecorder. - jobject encoding(); + jobject constant_encoding(); // What kind of ciObject is this? virtual bool is_null_object() const { return false; } + virtual bool is_call_site() const { return false; } + virtual bool is_cpcache() const { return false; } virtual bool is_instance() { return false; } virtual bool is_method() { return false; } virtual bool is_method_data() { return false; } + virtual bool is_method_handle() const { return false; } virtual bool is_array() { return false; } virtual bool is_obj_array() { return false; } virtual bool is_type_array() { return false; } @@ -167,6 +188,14 @@ assert(is_null_object(), "bad cast"); return (ciNullObject*)this; } + ciCallSite* as_call_site() { + assert(is_call_site(), "bad cast"); + return (ciCallSite*) this; + } + ciCPCache* as_cpcache() { + assert(is_cpcache(), "bad cast"); + return (ciCPCache*) this; + } ciInstance* as_instance() { assert(is_instance(), "bad cast"); return (ciInstance*)this; @@ -179,6 +208,10 @@ assert(is_method_data(), "bad cast"); return (ciMethodData*)this; } + ciMethodHandle* as_method_handle() { + assert(is_method_handle(), "bad cast"); + return (ciMethodHandle*) this; + } ciArray* as_array() { assert(is_array(), "bad cast"); return (ciArray*)this; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciObjectFactory.cpp --- a/src/share/vm/ci/ciObjectFactory.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciObjectFactory.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -144,30 +144,13 @@ ciEnv::_obj_array_klass_klass_instance = get(Universe::objArrayKlassKlassObj()) ->as_obj_array_klass_klass(); - ciEnv::_ArrayStoreException = - get(SystemDictionary::ArrayStoreException_klass()) - ->as_instance_klass(); - ciEnv::_Class = - get(SystemDictionary::class_klass()) - ->as_instance_klass(); - ciEnv::_ClassCastException = - get(SystemDictionary::ClassCastException_klass()) - ->as_instance_klass(); - ciEnv::_Object = - get(SystemDictionary::object_klass()) - ->as_instance_klass(); - ciEnv::_Throwable = - get(SystemDictionary::throwable_klass()) - ->as_instance_klass(); - ciEnv::_Thread = - get(SystemDictionary::thread_klass()) - ->as_instance_klass(); - ciEnv::_OutOfMemoryError = - get(SystemDictionary::OutOfMemoryError_klass()) - ->as_instance_klass(); - ciEnv::_String = - get(SystemDictionary::string_klass()) - ->as_instance_klass(); + +#define WK_KLASS_DEFN(name, ignore_s, opt) \ + if (SystemDictionary::name() != NULL) \ + ciEnv::_##name = get(SystemDictionary::name())->as_instance_klass(); + + WK_KLASSES_DO(WK_KLASS_DEFN) +#undef WK_KLASS_DEFN for (int len = -1; len != _ci_objects->length(); ) { len = _ci_objects->length(); @@ -261,12 +244,11 @@ ciObject* new_object = create_new_object(keyHandle()); assert(keyHandle() == new_object->get_oop(), "must be properly recorded"); init_ident_of(new_object); - if (!keyHandle->is_perm()) { + if (!new_object->is_perm()) { // Not a perm-space object. insert_non_perm(bucket, keyHandle(), new_object); return new_object; } - new_object->set_perm(); if (len != _ci_objects->length()) { // creating the new object has recursively entered new objects // into the table. We need to recompute our index. @@ -325,13 +307,21 @@ return new (arena()) ciMethodData(h_md); } else if (o->is_instance()) { instanceHandle h_i(THREAD, (instanceOop)o); - return new (arena()) ciInstance(h_i); + if (java_dyn_CallSite::is_instance(o)) + return new (arena()) ciCallSite(h_i); + else if (java_dyn_MethodHandle::is_instance(o)) + return new (arena()) ciMethodHandle(h_i); + else + return new (arena()) ciInstance(h_i); } else if (o->is_objArray()) { objArrayHandle h_oa(THREAD, (objArrayOop)o); return new (arena()) ciObjArray(h_oa); } else if (o->is_typeArray()) { typeArrayHandle h_ta(THREAD, (typeArrayOop)o); return new (arena()) ciTypeArray(h_ta); + } else if (o->is_constantPoolCache()) { + constantPoolCacheHandle h_cpc(THREAD, (constantPoolCacheOop) o); + return new (arena()) ciCPCache(h_cpc); } // The oop is of some type not supported by the compiler interface. @@ -568,7 +558,7 @@ if (key->is_perm() && _non_perm_count == 0) { return emptyBucket; } else if (key->is_instance()) { - if (key->klass() == SystemDictionary::class_klass()) { + if (key->klass() == SystemDictionary::Class_klass()) { // class mirror instances are always perm return emptyBucket; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciStreams.cpp --- a/src/share/vm/ci/ciStreams.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciStreams.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -186,8 +186,9 @@ // If this bytecode is a new, newarray, multianewarray, instanceof, // or checkcast, get the referenced klass. ciKlass* ciBytecodeStream::get_klass(bool& will_link) { - return CURRENT_ENV->get_klass_by_index(_holder, get_klass_index(), - will_link); + VM_ENTRY_MARK; + constantPoolHandle cpool(_method->get_methodOop()->constants()); + return CURRENT_ENV->get_klass_by_index(cpool, get_klass_index(), will_link, _holder); } // ------------------------------------------------------------------ @@ -213,7 +214,9 @@ // If this bytecode is one of the ldc variants, get the referenced // constant. ciConstant ciBytecodeStream::get_constant() { - return CURRENT_ENV->get_constant_by_index(_holder, get_constant_index()); + VM_ENTRY_MARK; + constantPoolHandle cpool(_method->get_methodOop()->constants()); + return CURRENT_ENV->get_constant_by_index(cpool, get_constant_index(), _holder); } // ------------------------------------------------------------------ @@ -264,9 +267,11 @@ // There is no "will_link" result passed back. The user is responsible // for checking linkability when retrieving the associated field. ciInstanceKlass* ciBytecodeStream::get_declared_field_holder() { + VM_ENTRY_MARK; + constantPoolHandle cpool(_method->get_methodOop()->constants()); int holder_index = get_field_holder_index(); bool ignore; - return CURRENT_ENV->get_klass_by_index(_holder, holder_index, ignore) + return CURRENT_ENV->get_klass_by_index(cpool, holder_index, ignore, _holder) ->as_instance_klass(); } @@ -277,9 +282,10 @@ // referenced by the current bytecode. Used for generating // deoptimization information. int ciBytecodeStream::get_field_holder_index() { - VM_ENTRY_MARK; - constantPoolOop cpool = _holder->get_instanceKlass()->constants(); - return cpool->klass_ref_index_at(get_field_index()); + GUARDED_VM_ENTRY( + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + return cpool->klass_ref_index_at(get_field_index()); + ) } // ------------------------------------------------------------------ @@ -321,7 +327,9 @@ // // If this is a method invocation bytecode, get the invoked method. ciMethod* ciBytecodeStream::get_method(bool& will_link) { - ciMethod* m = CURRENT_ENV->get_method_by_index(_holder, get_method_index(),cur_bc()); + VM_ENTRY_MARK; + constantPoolHandle cpool(_method->get_methodOop()->constants()); + ciMethod* m = CURRENT_ENV->get_method_by_index(cpool, get_method_index(), cur_bc(), _holder); will_link = m->is_loaded(); return m; } @@ -338,11 +346,13 @@ // There is no "will_link" result passed back. The user is responsible // for checking linkability when retrieving the associated method. ciKlass* ciBytecodeStream::get_declared_method_holder() { + VM_ENTRY_MARK; + constantPoolHandle cpool(_method->get_methodOop()->constants()); bool ignore; - // report as Dynamic for invokedynamic, which is syntactically classless + // report as InvokeDynamic for invokedynamic, which is syntactically classless if (cur_bc() == Bytecodes::_invokedynamic) - return CURRENT_ENV->get_klass_by_name(_holder, ciSymbol::java_dyn_Dynamic(), false); - return CURRENT_ENV->get_klass_by_index(_holder, get_method_holder_index(), ignore); + return CURRENT_ENV->get_klass_by_name(_holder, ciSymbol::java_dyn_InvokeDynamic(), false); + return CURRENT_ENV->get_klass_by_index(cpool, get_method_holder_index(), ignore, _holder); } // ------------------------------------------------------------------ @@ -352,8 +362,7 @@ // referenced by the current bytecode. Used for generating // deoptimization information. int ciBytecodeStream::get_method_holder_index() { - VM_ENTRY_MARK; - constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + constantPoolOop cpool = _method->get_methodOop()->constants(); return cpool->klass_ref_index_at(get_method_index()); } @@ -370,3 +379,31 @@ int name_and_type_index = cpool->name_and_type_ref_index_at(method_index); return cpool->signature_ref_index_at(name_and_type_index); } + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_cpcache +ciCPCache* ciBytecodeStream::get_cpcache() { + VM_ENTRY_MARK; + // Get the constant pool. + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + constantPoolCacheOop cpcache = cpool->cache(); + + return CURRENT_ENV->get_object(cpcache)->as_cpcache(); +} + +// ------------------------------------------------------------------ +// ciBytecodeStream::get_call_site +ciCallSite* ciBytecodeStream::get_call_site() { + VM_ENTRY_MARK; + // Get the constant pool. + constantPoolOop cpool = _holder->get_instanceKlass()->constants(); + constantPoolCacheOop cpcache = cpool->cache(); + + // Get the CallSite from the constant pool cache. + int method_index = get_method_index(); + ConstantPoolCacheEntry* cpcache_entry = cpcache->secondary_entry_at(method_index); + oop call_site_oop = cpcache_entry->f1(); + + // Create a CallSite object and return it. + return CURRENT_ENV->get_object(call_site_oop)->as_call_site(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciStreams.hpp --- a/src/share/vm/ci/ciStreams.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciStreams.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -232,6 +232,9 @@ int get_method_holder_index(); int get_method_signature_index(); + ciCPCache* get_cpcache(); + ciCallSite* get_call_site(); + private: void assert_index_size(int required_size) const { #ifdef ASSERT diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciSymbol.cpp --- a/src/share/vm/ci/ciSymbol.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciSymbol.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -60,6 +60,22 @@ } // ------------------------------------------------------------------ +// ciSymbol::starts_with +// +// Tests if the symbol starts with the given prefix. +bool ciSymbol::starts_with(const char* prefix, int len) const { + GUARDED_VM_ENTRY(return get_symbolOop()->starts_with(prefix, len);) +} + +// ------------------------------------------------------------------ +// ciSymbol::index_of +// +// Determines where the symbol contains the given substring. +int ciSymbol::index_of_at(int i, const char* str, int len) const { + GUARDED_VM_ENTRY(return get_symbolOop()->index_of_at(i, str, len);) +} + +// ------------------------------------------------------------------ // ciSymbol::utf8_length int ciSymbol::utf8_length() { GUARDED_VM_ENTRY(return get_symbolOop()->utf8_length();) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciSymbol.hpp --- a/src/share/vm/ci/ciSymbol.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciSymbol.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -28,6 +28,7 @@ // machine. class ciSymbol : public ciObject { CI_PACKAGE_ACCESS + // These friends all make direct use of get_symbolOop: friend class ciEnv; friend class ciInstanceKlass; friend class ciSignature; @@ -38,13 +39,13 @@ ciSymbol(symbolOop s) : ciObject(s) {} ciSymbol(symbolHandle s); // for use with vmSymbolHandles - symbolOop get_symbolOop() { return (symbolOop)get_oop(); } + symbolOop get_symbolOop() const { return (symbolOop)get_oop(); } const char* type_string() { return "ciSymbol"; } void print_impl(outputStream* st); - int byte_at(int i); + // This is public in symbolOop but private here, because the base can move: jbyte* base(); // Make a ciSymbol from a C string (implementation). @@ -55,6 +56,15 @@ const char* as_utf8(); int utf8_length(); + // Return the i-th utf8 byte, where i < utf8_length + int byte_at(int i); + + // Tests if the symbol starts with the given prefix. + bool starts_with(const char* prefix, int len) const; + + // Determines where the symbol contains the given substring. + int index_of_at(int i, const char* str, int len) const; + // What kind of ciObject is this? bool is_symbol() { return true; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciType.cpp --- a/src/share/vm/ci/ciType.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciType.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -111,7 +111,7 @@ // short, etc. // Note: Bare T_ADDRESS means a raw pointer type, not a return_address. assert((uint)t < T_CONFLICT+1, "range check"); - if (t == T_OBJECT) return ciEnv::_Object; // java/lang/Object + if (t == T_OBJECT) return ciEnv::_Object_klass; // java/lang/Object assert(_basic_types[t] != NULL, "domain check"); return _basic_types[t]; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciTypeFlow.cpp --- a/src/share/vm/ci/ciTypeFlow.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciTypeFlow.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -635,8 +635,15 @@ ciMethod* method = str->get_method(will_link); if (!will_link) { // We weren't able to find the method. - ciKlass* unloaded_holder = method->holder(); - trap(str, unloaded_holder, str->get_method_holder_index()); + if (str->cur_bc() == Bytecodes::_invokedynamic) { + trap(str, NULL, + Deoptimization::make_trap_request + (Deoptimization::Reason_uninitialized, + Deoptimization::Action_reinterpret)); + } else { + ciKlass* unloaded_holder = method->holder(); + trap(str, unloaded_holder, str->get_method_holder_index()); + } } else { ciSignature* signature = method->signature(); ciSignatureStream sigstr(signature); @@ -1292,8 +1299,8 @@ case Bytecodes::_invokeinterface: do_invoke(str, true); break; case Bytecodes::_invokespecial: do_invoke(str, true); break; case Bytecodes::_invokestatic: do_invoke(str, false); break; - case Bytecodes::_invokevirtual: do_invoke(str, true); break; + case Bytecodes::_invokedynamic: do_invoke(str, false); break; case Bytecodes::_istore: store_local_int(str->get_index()); break; case Bytecodes::_istore_0: store_local_int(0); break; @@ -2486,8 +2493,13 @@ // Assume irreducible entries need more data flow add_to_work_list(succ); } - lp = lp->parent(); - assert(lp != NULL, "nested loop must have parent by now"); + Loop* plp = lp->parent(); + if (plp == NULL) { + // This only happens for some irreducible cases. The parent + // will be updated during a later pass. + break; + } + lp = plp; } // Merge loop tree branch for all successors. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/ci/ciUtilities.hpp --- a/src/share/vm/ci/ciUtilities.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/ci/ciUtilities.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -79,7 +79,7 @@ THREAD); \ if (HAS_PENDING_EXCEPTION) { \ if (PENDING_EXCEPTION->klass() == \ - SystemDictionary::threaddeath_klass()) { \ + SystemDictionary::ThreadDeath_klass()) { \ /* Kill the compilation. */ \ fatal("unhandled ci exception"); \ return (result); \ diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/classFileParser.cpp --- a/src/share/vm/classfile/classFileParser.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/classFileParser.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -430,7 +430,7 @@ case JVM_CONSTANT_UnresolvedClass : // Patching a class means pre-resolving it. // The name in the constant pool is ignored. - if (patch->klass() == SystemDictionary::class_klass()) { // %%% java_lang_Class::is_instance + if (patch->klass() == SystemDictionary::Class_klass()) { // %%% java_lang_Class::is_instance guarantee_property(!java_lang_Class::is_primitive(patch()), "Illegal class patch at %d in class file %s", index, CHECK); @@ -643,7 +643,7 @@ guarantee_property(value_type.is_int(), "Inconsistent constant value type in class file %s", CHECK); break; case T_OBJECT: - guarantee_property((cp->symbol_at(signature_index)->equals("Ljava/lang/String;", 18) + guarantee_property((cp->symbol_at(signature_index)->equals("Ljava/lang/String;") && (value_type.is_string() || value_type.is_unresolved_string())), "Bad string initial value in class file %s", CHECK); break; @@ -766,16 +766,16 @@ struct FieldAllocationCount { - int static_oop_count; - int static_byte_count; - int static_short_count; - int static_word_count; - int static_double_count; - int nonstatic_oop_count; - int nonstatic_byte_count; - int nonstatic_short_count; - int nonstatic_word_count; - int nonstatic_double_count; + unsigned int static_oop_count; + unsigned int static_byte_count; + unsigned int static_short_count; + unsigned int static_word_count; + unsigned int static_double_count; + unsigned int nonstatic_oop_count; + unsigned int nonstatic_byte_count; + unsigned int nonstatic_short_count; + unsigned int nonstatic_word_count; + unsigned int nonstatic_double_count; }; typeArrayHandle ClassFileParser::parse_fields(constantPoolHandle cp, bool is_interface, @@ -1718,9 +1718,7 @@ m->set_exception_table(exception_handlers()); // Copy byte codes - if (code_length > 0) { - memcpy(m->code_base(), code_start, code_length); - } + m->set_code(code_start); // Copy line number table if (linenumber_table != NULL) { @@ -2511,23 +2509,12 @@ fac_ptr->nonstatic_byte_count -= 1; (*fields_ptr)->ushort_at_put(i + instanceKlass::signature_index_offset, word_sig_index); - if (wordSize == jintSize) { - fac_ptr->nonstatic_word_count += 1; - } else { - fac_ptr->nonstatic_double_count += 1; - } - - FieldAllocationType atype = (FieldAllocationType) (*fields_ptr)->ushort_at(i+4); + fac_ptr->nonstatic_word_count += 1; + + FieldAllocationType atype = (FieldAllocationType) (*fields_ptr)->ushort_at(i + instanceKlass::low_offset); assert(atype == NONSTATIC_BYTE, ""); FieldAllocationType new_atype = NONSTATIC_WORD; - if (wordSize > jintSize) { - if (Universe::field_type_should_be_aligned(T_LONG)) { - atype = NONSTATIC_ALIGNED_DOUBLE; - } else { - atype = NONSTATIC_DOUBLE; - } - } - (*fields_ptr)->ushort_at_put(i+4, new_atype); + (*fields_ptr)->ushort_at_put(i + instanceKlass::low_offset, new_atype); found_vmentry = true; break; @@ -2547,6 +2534,7 @@ KlassHandle host_klass, GrowableArray* cp_patches, symbolHandle& parsed_name, + bool verify, TRAPS) { // So that JVMTI can cache class file in the state before retransformable agents // have modified it @@ -2591,7 +2579,7 @@ instanceKlassHandle nullHandle; // Figure out whether we can skip format checking (matching classic VM behavior) - _need_verify = Verifier::should_verify_for(class_loader()); + _need_verify = Verifier::should_verify_for(class_loader(), verify); // Set the verify flag in stream cfs->set_verify(_need_verify); @@ -2908,11 +2896,11 @@ } // end of "discovered" field compactibility fix - int nonstatic_double_count = fac.nonstatic_double_count; - int nonstatic_word_count = fac.nonstatic_word_count; - int nonstatic_short_count = fac.nonstatic_short_count; - int nonstatic_byte_count = fac.nonstatic_byte_count; - int nonstatic_oop_count = fac.nonstatic_oop_count; + unsigned int nonstatic_double_count = fac.nonstatic_double_count; + unsigned int nonstatic_word_count = fac.nonstatic_word_count; + unsigned int nonstatic_short_count = fac.nonstatic_short_count; + unsigned int nonstatic_byte_count = fac.nonstatic_byte_count; + unsigned int nonstatic_oop_count = fac.nonstatic_oop_count; bool super_has_nonstatic_fields = (super_klass() != NULL && super_klass->has_nonstatic_fields()); @@ -2922,26 +2910,26 @@ nonstatic_oop_count) != 0); - // Prepare list of oops for oop maps generation. - u2* nonstatic_oop_offsets; - u2* nonstatic_oop_length; - int nonstatic_oop_map_count = 0; + // Prepare list of oops for oop map generation. + int* nonstatic_oop_offsets; + unsigned int* nonstatic_oop_counts; + unsigned int nonstatic_oop_map_count = 0; nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD( - THREAD, u2, nonstatic_oop_count+1); - nonstatic_oop_length = NEW_RESOURCE_ARRAY_IN_THREAD( - THREAD, u2, nonstatic_oop_count+1); + THREAD, int, nonstatic_oop_count + 1); + nonstatic_oop_counts = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, unsigned int, nonstatic_oop_count + 1); // Add fake fields for java.lang.Class instances (also see above). // FieldsAllocationStyle and CompactFields values will be reset to default. if(class_name() == vmSymbols::java_lang_Class() && class_loader.is_null()) { java_lang_Class_fix_post(&next_nonstatic_field_offset); - nonstatic_oop_offsets[0] = (u2)first_nonstatic_field_offset; - int fake_oop_count = (( next_nonstatic_field_offset - - first_nonstatic_field_offset ) / heapOopSize); - nonstatic_oop_length [0] = (u2)fake_oop_count; - nonstatic_oop_map_count = 1; - nonstatic_oop_count -= fake_oop_count; + nonstatic_oop_offsets[0] = first_nonstatic_field_offset; + const uint fake_oop_count = (next_nonstatic_field_offset - + first_nonstatic_field_offset) / heapOopSize; + nonstatic_oop_counts[0] = fake_oop_count; + nonstatic_oop_map_count = 1; + nonstatic_oop_count -= fake_oop_count; first_nonstatic_oop_offset = first_nonstatic_field_offset; } else { first_nonstatic_oop_offset = 0; // will be set for first oop field @@ -3084,7 +3072,7 @@ int len = fields->length(); for (int i = 0; i < len; i += instanceKlass::next_offset) { int real_offset; - FieldAllocationType atype = (FieldAllocationType) fields->ushort_at(i+4); + FieldAllocationType atype = (FieldAllocationType) fields->ushort_at(i + instanceKlass::low_offset); switch (atype) { case STATIC_OOP: real_offset = next_static_oop_offset; @@ -3119,13 +3107,15 @@ // Update oop maps if( nonstatic_oop_map_count > 0 && nonstatic_oop_offsets[nonstatic_oop_map_count - 1] == - (u2)(real_offset - nonstatic_oop_length[nonstatic_oop_map_count - 1] * heapOopSize) ) { + real_offset - + int(nonstatic_oop_counts[nonstatic_oop_map_count - 1]) * + heapOopSize ) { // Extend current oop map - nonstatic_oop_length[nonstatic_oop_map_count - 1] += 1; + nonstatic_oop_counts[nonstatic_oop_map_count - 1] += 1; } else { // Create new oop map - nonstatic_oop_offsets[nonstatic_oop_map_count] = (u2)real_offset; - nonstatic_oop_length [nonstatic_oop_map_count] = 1; + nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset; + nonstatic_oop_counts [nonstatic_oop_map_count] = 1; nonstatic_oop_map_count += 1; if( first_nonstatic_oop_offset == 0 ) { // Undefined first_nonstatic_oop_offset = real_offset; @@ -3170,8 +3160,8 @@ default: ShouldNotReachHere(); } - fields->short_at_put(i+4, extract_low_short_from_int(real_offset) ); - fields->short_at_put(i+5, extract_high_short_from_int(real_offset) ); + fields->short_at_put(i + instanceKlass::low_offset, extract_low_short_from_int(real_offset)); + fields->short_at_put(i + instanceKlass::high_offset, extract_high_short_from_int(real_offset)); } // Size of instances @@ -3182,8 +3172,10 @@ assert(instance_size == align_object_size(align_size_up((instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size*heapOopSize), wordSize) / wordSize), "consistent layout helper value"); - // Size of non-static oop map blocks (in words) allocated at end of klass - int nonstatic_oop_map_size = compute_oop_map_size(super_klass, nonstatic_oop_map_count, first_nonstatic_oop_offset); + // Number of non-static oop map blocks allocated at end of klass. + const unsigned int total_oop_map_count = + compute_oop_map_count(super_klass, nonstatic_oop_map_count, + first_nonstatic_oop_offset); // Compute reference type ReferenceType rt; @@ -3194,17 +3186,19 @@ } // We can now create the basic klassOop for this klass - klassOop ik = oopFactory::new_instanceKlass( - vtable_size, itable_size, - static_field_size, nonstatic_oop_map_size, - rt, CHECK_(nullHandle)); + klassOop ik = oopFactory::new_instanceKlass(vtable_size, itable_size, + static_field_size, + total_oop_map_count, + rt, CHECK_(nullHandle)); instanceKlassHandle this_klass (THREAD, ik); - assert(this_klass->static_field_size() == static_field_size && - this_klass->nonstatic_oop_map_size() == nonstatic_oop_map_size, "sanity check"); + assert(this_klass->static_field_size() == static_field_size, "sanity"); + assert(this_klass->nonstatic_oop_map_count() == total_oop_map_count, + "sanity"); // Fill in information already parsed this_klass->set_access_flags(access_flags); + this_klass->set_should_verify_class(verify); jint lh = Klass::instance_layout_helper(instance_size, false); this_klass->set_layout_helper(lh); assert(this_klass->oop_is_instance(), "layout is correct"); @@ -3224,6 +3218,12 @@ this_klass->set_has_final_method(); } this_klass->set_method_ordering(method_ordering()); + // The instanceKlass::_methods_jmethod_ids cache and the + // instanceKlass::_methods_cached_itable_indices cache are + // both managed on the assumption that the initial cache + // size is equal to the number of methods in the class. If + // that changes, then instanceKlass::idnum_can_increment() + // has to be changed accordingly. this_klass->set_initial_method_idnum(methods->length()); this_klass->set_name(cp->klass_name_at(this_class_index)); if (LinkWellKnownClasses || is_anonymous()) // I am well known to myself @@ -3282,7 +3282,7 @@ klassItable::setup_itable_offset_table(this_klass); // Do final class setup - fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_length); + fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts); set_precomputed_flags(this_klass); @@ -3375,66 +3375,73 @@ } -int ClassFileParser::compute_oop_map_size(instanceKlassHandle super, int nonstatic_oop_map_count, int first_nonstatic_oop_offset) { - int map_size = super.is_null() ? 0 : super->nonstatic_oop_map_size(); +unsigned int +ClassFileParser::compute_oop_map_count(instanceKlassHandle super, + unsigned int nonstatic_oop_map_count, + int first_nonstatic_oop_offset) { + unsigned int map_count = + super.is_null() ? 0 : super->nonstatic_oop_map_count(); if (nonstatic_oop_map_count > 0) { // We have oops to add to map - if (map_size == 0) { - map_size = nonstatic_oop_map_count; + if (map_count == 0) { + map_count = nonstatic_oop_map_count; } else { - // Check whether we should add a new map block or whether the last one can be extended - OopMapBlock* first_map = super->start_of_nonstatic_oop_maps(); - OopMapBlock* last_map = first_map + map_size - 1; - - int next_offset = last_map->offset() + (last_map->length() * heapOopSize); + // Check whether we should add a new map block or whether the last one can + // be extended + OopMapBlock* const first_map = super->start_of_nonstatic_oop_maps(); + OopMapBlock* const last_map = first_map + map_count - 1; + + int next_offset = last_map->offset() + last_map->count() * heapOopSize; if (next_offset == first_nonstatic_oop_offset) { // There is no gap bettwen superklass's last oop field and first // local oop field, merge maps. nonstatic_oop_map_count -= 1; } else { // Superklass didn't end with a oop field, add extra maps - assert(next_offsetstart_of_nonstatic_oop_maps(); - OopMapBlock* last_oop_map = this_oop_map + k->nonstatic_oop_map_size(); - instanceKlass* super = k->superklass(); - if (super != NULL) { - int super_oop_map_size = super->nonstatic_oop_map_size(); + const instanceKlass* const super = k->superklass(); + const unsigned int super_count = super ? super->nonstatic_oop_map_count() : 0; + if (super_count > 0) { + // Copy maps from superklass OopMapBlock* super_oop_map = super->start_of_nonstatic_oop_maps(); - // Copy maps from superklass - while (super_oop_map_size-- > 0) { + for (unsigned int i = 0; i < super_count; ++i) { *this_oop_map++ = *super_oop_map++; } } + if (nonstatic_oop_map_count > 0) { - if (this_oop_map + nonstatic_oop_map_count > last_oop_map) { - // Calculated in compute_oop_map_size() number of oop maps is less then - // collected oop maps since there is no gap between superklass's last oop - // field and first local oop field. Extend the last oop map copied + if (super_count + nonstatic_oop_map_count > k->nonstatic_oop_map_count()) { + // The counts differ because there is no gap between superklass's last oop + // field and the first local oop field. Extend the last oop map copied // from the superklass instead of creating new one. nonstatic_oop_map_count--; nonstatic_oop_offsets++; this_oop_map--; - this_oop_map->set_length(this_oop_map->length() + *nonstatic_oop_length++); + this_oop_map->set_count(this_oop_map->count() + *nonstatic_oop_counts++); this_oop_map++; } - assert((this_oop_map + nonstatic_oop_map_count) == last_oop_map, "just checking"); + // Add new map blocks, fill them while (nonstatic_oop_map_count-- > 0) { this_oop_map->set_offset(*nonstatic_oop_offsets++); - this_oop_map->set_length(*nonstatic_oop_length++); + this_oop_map->set_count(*nonstatic_oop_counts++); this_oop_map++; } + assert(k->start_of_nonstatic_oop_maps() + k->nonstatic_oop_map_count() == + this_oop_map, "sanity"); } } @@ -3462,8 +3469,8 @@ #endif // Check if this klass supports the java.lang.Cloneable interface - if (SystemDictionary::cloneable_klass_loaded()) { - if (k->is_subtype_of(SystemDictionary::cloneable_klass())) { + if (SystemDictionary::Cloneable_klass_loaded()) { + if (k->is_subtype_of(SystemDictionary::Cloneable_klass())) { k->set_is_cloneable(); } } @@ -3746,8 +3753,9 @@ } bool ClassFileParser::is_supported_version(u2 major, u2 minor) { - u2 max_version = JDK_Version::is_gte_jdk17x_version() ? - JAVA_MAX_SUPPORTED_VERSION : JAVA_6_VERSION; + u2 max_version = + JDK_Version::is_gte_jdk17x_version() ? JAVA_MAX_SUPPORTED_VERSION : + (JDK_Version::is_gte_jdk16x_version() ? JAVA_6_VERSION : JAVA_1_5_VERSION); return (major >= JAVA_MIN_SUPPORTED_VERSION) && (major <= max_version) && ((major != max_version) || @@ -4168,7 +4176,7 @@ // Check if ch is Java identifier start or is Java identifier part // 4672820: call java.lang.Character methods directly without generating separate tables. EXCEPTION_MARK; - instanceKlassHandle klass (THREAD, SystemDictionary::char_klass()); + instanceKlassHandle klass (THREAD, SystemDictionary::Character_klass()); // return value JavaValue result(T_BOOLEAN); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/classFileParser.hpp --- a/src/share/vm/classfile/classFileParser.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/classFileParser.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -125,10 +125,13 @@ int runtime_invisible_annotations_length, TRAPS); // Final setup - int compute_oop_map_size(instanceKlassHandle super, int nonstatic_oop_count, - int first_nonstatic_oop_offset); - void fill_oop_maps(instanceKlassHandle k, int nonstatic_oop_map_count, - u2* nonstatic_oop_offsets, u2* nonstatic_oop_length); + unsigned int compute_oop_map_count(instanceKlassHandle super, + unsigned int nonstatic_oop_count, + int first_nonstatic_oop_offset); + void fill_oop_maps(instanceKlassHandle k, + unsigned int nonstatic_oop_map_count, + int* nonstatic_oop_offsets, + unsigned int* nonstatic_oop_counts); void set_precomputed_flags(instanceKlassHandle k); objArrayHandle compute_transitive_interfaces(instanceKlassHandle super, objArrayHandle local_ifs, TRAPS); @@ -257,9 +260,10 @@ Handle class_loader, Handle protection_domain, symbolHandle& parsed_name, + bool verify, TRAPS) { KlassHandle no_host_klass; - return parseClassFile(name, class_loader, protection_domain, no_host_klass, NULL, parsed_name, THREAD); + return parseClassFile(name, class_loader, protection_domain, no_host_klass, NULL, parsed_name, verify, THREAD); } instanceKlassHandle parseClassFile(symbolHandle name, Handle class_loader, @@ -267,6 +271,7 @@ KlassHandle host_klass, GrowableArray* cp_patches, symbolHandle& parsed_name, + bool verify, TRAPS); // Verifier checks diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/classLoader.cpp --- a/src/share/vm/classfile/classLoader.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/classLoader.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -819,7 +819,7 @@ _package_hash_table->copy_pkgnames(packages); } // Allocate objArray and fill with java.lang.String - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), nof_entries, CHECK_0); objArrayHandle result(THREAD, r); for (int i = 0; i < nof_entries; i++) { @@ -874,6 +874,7 @@ class_loader, protection_domain, parsed_name, + false, CHECK_(h)); // add to package table @@ -1248,6 +1249,7 @@ } int ClassLoader::_compile_the_world_counter = 0; +static int _codecache_sweep_counter = 0; void ClassLoader::compile_the_world_in(char* name, Handle loader, TRAPS) { int len = (int)strlen(name); @@ -1292,6 +1294,13 @@ for (int n = 0; n < k->methods()->length(); n++) { methodHandle m (THREAD, methodOop(k->methods()->obj_at(n))); if (CompilationPolicy::canBeCompiled(m)) { + + if (++_codecache_sweep_counter == CompileTheWorldSafepointInterval) { + // Give sweeper a chance to keep up with CTW + VM_ForceSafepoint op; + VMThread::execute(&op); + _codecache_sweep_counter = 0; + } // Force compilation CompileBroker::compile_method(m, InvocationEntryBci, methodHandle(), 0, "CTW", THREAD); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/classLoader.hpp --- a/src/share/vm/classfile/classLoader.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/classLoader.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/javaAssertions.cpp --- a/src/share/vm/classfile/javaAssertions.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/javaAssertions.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -95,14 +95,14 @@ int len; typeArrayOop t; len = OptionList::count(_packages); - objArrayOop pn = oopFactory::new_objArray(SystemDictionary::string_klass(), len, CHECK_NULL); + objArrayOop pn = oopFactory::new_objArray(SystemDictionary::String_klass(), len, CHECK_NULL); objArrayHandle pkgNames (THREAD, pn); t = oopFactory::new_typeArray(T_BOOLEAN, len, CHECK_NULL); typeArrayHandle pkgEnabled(THREAD, t); fillJavaArrays(_packages, len, pkgNames, pkgEnabled, CHECK_NULL); len = OptionList::count(_classes); - objArrayOop cn = oopFactory::new_objArray(SystemDictionary::string_klass(), len, CHECK_NULL); + objArrayOop cn = oopFactory::new_objArray(SystemDictionary::String_klass(), len, CHECK_NULL); objArrayHandle classNames (THREAD, cn); t = oopFactory::new_typeArray(T_BOOLEAN, len, CHECK_NULL); typeArrayHandle classEnabled(THREAD, t); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/javaClasses.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -68,9 +68,9 @@ // and the char array it points to end up in the same cache line. oop obj; if (tenured) { - obj = instanceKlass::cast(SystemDictionary::string_klass())->allocate_permanent_instance(CHECK_NH); + obj = instanceKlass::cast(SystemDictionary::String_klass())->allocate_permanent_instance(CHECK_NH); } else { - obj = instanceKlass::cast(SystemDictionary::string_klass())->allocate_instance(CHECK_NH); + obj = instanceKlass::cast(SystemDictionary::String_klass())->allocate_instance(CHECK_NH); } // Create the char array. The String object must be handlized here @@ -252,7 +252,7 @@ typeArrayOop value = java_lang_String::value(obj); int offset = java_lang_String::offset(obj); int length = java_lang_String::length(obj); - jchar* base = value->char_at_addr(offset); + jchar* base = (length == 0) ? NULL : value->char_at_addr(offset); symbolOop sym = SymbolTable::lookup_unicode(base, length, THREAD); return symbolHandle(THREAD, sym); } @@ -261,7 +261,7 @@ typeArrayOop value = java_lang_String::value(java_string); int offset = java_lang_String::offset(java_string); int length = java_lang_String::length(java_string); - jchar* base = value->char_at_addr(offset); + jchar* base = (length == 0) ? NULL : value->char_at_addr(offset); return SymbolTable::probe_unicode(base, length); } @@ -293,7 +293,7 @@ bool java_lang_String::equals(oop java_string, jchar* chars, int len) { assert(SharedSkipVerify || - java_string->klass() == SystemDictionary::string_klass(), + java_string->klass() == SystemDictionary::String_klass(), "must be java_string"); typeArrayOop value = java_lang_String::value(java_string); int offset = java_lang_String::offset(java_string); @@ -311,7 +311,7 @@ void java_lang_String::print(Handle java_string, outputStream* st) { oop obj = java_string(); - assert(obj->klass() == SystemDictionary::string_klass(), "must be java_string"); + assert(obj->klass() == SystemDictionary::String_klass(), "must be java_string"); typeArrayOop value = java_lang_String::value(obj); int offset = java_lang_String::offset(obj); int length = java_lang_String::length(obj); @@ -339,9 +339,9 @@ // class is put into the system dictionary. int computed_modifiers = k->compute_modifier_flags(CHECK_0); k->set_modifier_flags(computed_modifiers); - if (SystemDictionary::class_klass_loaded()) { + if (SystemDictionary::Class_klass_loaded()) { // Allocate mirror (java.lang.Class instance) - Handle mirror = instanceKlass::cast(SystemDictionary::class_klass())->allocate_permanent_instance(CHECK_0); + Handle mirror = instanceKlass::cast(SystemDictionary::Class_klass())->allocate_permanent_instance(CHECK_0); // Setup indirections mirror->obj_field_put(klass_offset, k()); k->set_java_mirror(mirror()); @@ -378,7 +378,7 @@ oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) { // This should be improved by adding a field at the Java level or by // introducing a new VM klass (see comment in ClassFileParser) - oop java_class = instanceKlass::cast(SystemDictionary::class_klass())->allocate_permanent_instance(CHECK_0); + oop java_class = instanceKlass::cast(SystemDictionary::Class_klass())->allocate_permanent_instance(CHECK_0); if (type != T_VOID) { klassOop aklass = Universe::typeArrayKlassObj(type); assert(aklass != NULL, "correct bootstrap"); @@ -502,7 +502,7 @@ oop java_lang_Class::primitive_mirror(BasicType t) { oop mirror = Universe::java_mirror(t); - assert(mirror != NULL && mirror->is_a(SystemDictionary::class_klass()), "must be a Class"); + assert(mirror != NULL && mirror->is_a(SystemDictionary::Class_klass()), "must be a Class"); assert(java_lang_Class::is_primitive(mirror), "must be primitive"); return mirror; } @@ -515,14 +515,14 @@ assert(!offsets_computed, "offsets should be initialized only once"); offsets_computed = true; - klassOop k = SystemDictionary::class_klass(); + klassOop k = SystemDictionary::Class_klass(); // The classRedefinedCount field is only present starting in 1.5, // so don't go fatal. compute_optional_offset(classRedefinedCount_offset, k, vmSymbols::classRedefinedCount_name(), vmSymbols::int_signature()); // The field indicating parallelCapable (parallelLockMap) is only present starting in 7, - klassOop k1 = SystemDictionary::classloader_klass(); + klassOop k1 = SystemDictionary::ClassLoader_klass(); compute_optional_offset(parallelCapable_offset, k1, vmSymbols::parallelCapable_name(), vmSymbols::concurrenthashmap_signature()); } @@ -588,7 +588,7 @@ void java_lang_Thread::compute_offsets() { assert(_group_offset == 0, "offsets should be initialized only once"); - klassOop k = SystemDictionary::thread_klass(); + klassOop k = SystemDictionary::Thread_klass(); compute_offset(_name_offset, k, vmSymbols::name_name(), vmSymbols::char_array_signature()); compute_offset(_group_offset, k, vmSymbols::group_name(), vmSymbols::threadgroup_signature()); compute_offset(_contextClassLoader_offset, k, vmSymbols::contextClassLoader_name(), vmSymbols::classloader_signature()); @@ -847,7 +847,7 @@ void java_lang_ThreadGroup::compute_offsets() { assert(_parent_offset == 0, "offsets should be initialized only once"); - klassOop k = SystemDictionary::threadGroup_klass(); + klassOop k = SystemDictionary::ThreadGroup_klass(); compute_offset(_parent_offset, k, vmSymbols::parent_name(), vmSymbols::threadgroup_signature()); compute_offset(_name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); @@ -1121,11 +1121,23 @@ } void flush() { + // The following appears to have been an optimization to save from + // doing a barrier for each individual store into the _methods array, + // but rather to do it for the entire array after the series of writes. + // That optimization seems to have been lost when compressed oops was + // implemented. However, the extra card-marks below was left in place, + // but is now redundant because the individual stores into the + // _methods array already execute the barrier code. CR 6918185 has + // been filed so the original code may be restored by deferring the + // barriers until after the entire sequence of stores, thus re-enabling + // the intent of the original optimization. In the meantime the redundant + // card mark below is now disabled. if (_dirty && _methods != NULL) { +#if 0 BarrierSet* bs = Universe::heap()->barrier_set(); assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt"); - bs->write_ref_array(MemRegion((HeapWord*)_methods->base(), - _methods->array_size())); + bs->write_ref_array((HeapWord*)_methods->base(), _methods->length()); +#endif _dirty = false; } } @@ -1169,9 +1181,7 @@ method = mhandle(); } - _methods->obj_at_put(_index, method); - // bad for UseCompressedOops - // *_methods->obj_at_addr(_index) = method; + _methods->obj_at_put(_index, method); _bcis->ushort_at_put(_index, bci); _index++; _dirty = true; @@ -1229,13 +1239,10 @@ // Compiled java method case. if (decode_offset != 0) { - bool dummy_reexecute = false; DebugInfoReadStream stream(nm, decode_offset); decode_offset = stream.read_int(); method = (methodOop)nm->oop_at(stream.read_int()); - //fill_in_stack_trace does not need the reexecute information which is designed - //for the deopt to reexecute - bci = stream.read_bci_and_reexecute(dummy_reexecute); + bci = stream.read_bci(); } else { if (fr.is_first_frame()) break; address pc = fr.pc(); @@ -1348,7 +1355,7 @@ // No-op if stack trace is disabled if (!StackTraceInThrowable) return; - assert(throwable->is_a(SystemDictionary::throwable_klass()), "sanity check"); + assert(throwable->is_a(SystemDictionary::Throwable_klass()), "sanity check"); oop backtrace = java_lang_Throwable::backtrace(throwable()); assert(backtrace != NULL, "backtrace not preallocated"); @@ -1453,7 +1460,7 @@ assert(JDK_Version::is_gte_jdk14x_version(), "should only be called in >= 1.4"); // Allocate java.lang.StackTraceElement instance - klassOop k = SystemDictionary::stackTraceElement_klass(); + klassOop k = SystemDictionary::StackTraceElement_klass(); assert(k != NULL, "must be loaded in 1.4+"); instanceKlassHandle ik (THREAD, k); if (ik->should_be_initialized()) { @@ -1491,7 +1498,7 @@ void java_lang_reflect_AccessibleObject::compute_offsets() { - klassOop k = SystemDictionary::reflect_accessible_object_klass(); + klassOop k = SystemDictionary::reflect_AccessibleObject_klass(); compute_offset(override_offset, k, vmSymbols::override_name(), vmSymbols::bool_signature()); } @@ -1506,7 +1513,7 @@ } void java_lang_reflect_Method::compute_offsets() { - klassOop k = SystemDictionary::reflect_method_klass(); + klassOop k = SystemDictionary::reflect_Method_klass(); compute_offset(clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); compute_offset(name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); compute_offset(returnType_offset, k, vmSymbols::returnType_name(), vmSymbols::class_signature()); @@ -1527,7 +1534,7 @@ Handle java_lang_reflect_Method::create(TRAPS) { assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); - klassOop klass = SystemDictionary::reflect_method_klass(); + klassOop klass = SystemDictionary::reflect_Method_klass(); // This class is eagerly initialized during VM initialization, since we keep a refence // to one of the methods assert(instanceKlass::cast(klass)->is_initialized(), "must be initialized"); @@ -1669,7 +1676,7 @@ } void java_lang_reflect_Constructor::compute_offsets() { - klassOop k = SystemDictionary::reflect_constructor_klass(); + klassOop k = SystemDictionary::reflect_Constructor_klass(); compute_offset(clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); compute_offset(parameterTypes_offset, k, vmSymbols::parameterTypes_name(), vmSymbols::class_array_signature()); compute_offset(exceptionTypes_offset, k, vmSymbols::exceptionTypes_name(), vmSymbols::class_array_signature()); @@ -1793,7 +1800,7 @@ } void java_lang_reflect_Field::compute_offsets() { - klassOop k = SystemDictionary::reflect_field_klass(); + klassOop k = SystemDictionary::reflect_Field_klass(); compute_offset(clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); compute_offset(name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); compute_offset(type_offset, k, vmSymbols::type_name(), vmSymbols::class_signature()); @@ -1900,7 +1907,7 @@ void sun_reflect_ConstantPool::compute_offsets() { - klassOop k = SystemDictionary::reflect_constant_pool_klass(); + klassOop k = SystemDictionary::reflect_ConstantPool_klass(); // This null test can be removed post beta if (k != NULL) { compute_offset(_cp_oop_offset, k, vmSymbols::constantPoolOop_name(), vmSymbols::object_signature()); @@ -1910,7 +1917,7 @@ Handle sun_reflect_ConstantPool::create(TRAPS) { assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); - klassOop k = SystemDictionary::reflect_constant_pool_klass(); + klassOop k = SystemDictionary::reflect_ConstantPool_klass(); instanceKlassHandle klass (THREAD, k); // Ensure it is initialized klass->initialize(CHECK_NH); @@ -1930,7 +1937,7 @@ } void sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets() { - klassOop k = SystemDictionary::reflect_unsafe_static_field_accessor_impl_klass(); + klassOop k = SystemDictionary::reflect_UnsafeStaticFieldAccessorImpl_klass(); // This null test can be removed post beta if (k != NULL) { compute_offset(_base_offset, k, @@ -2076,7 +2083,7 @@ // Support for java_lang_ref_Reference oop java_lang_ref_Reference::pending_list_lock() { - instanceKlass* ik = instanceKlass::cast(SystemDictionary::reference_klass()); + instanceKlass* ik = instanceKlass::cast(SystemDictionary::Reference_klass()); char *addr = (((char *)ik->start_of_static_fields()) + static_lock_offset); if (UseCompressedOops) { return oopDesc::load_decode_heap_oop((narrowOop *)addr); @@ -2086,7 +2093,7 @@ } HeapWord *java_lang_ref_Reference::pending_list_addr() { - instanceKlass* ik = instanceKlass::cast(SystemDictionary::reference_klass()); + instanceKlass* ik = instanceKlass::cast(SystemDictionary::Reference_klass()); char *addr = (((char *)ik->start_of_static_fields()) + static_pending_offset); // XXX This might not be HeapWord aligned, almost rather be char *. return (HeapWord*)addr; @@ -2109,17 +2116,17 @@ } jlong java_lang_ref_SoftReference::clock() { - instanceKlass* ik = instanceKlass::cast(SystemDictionary::soft_reference_klass()); + instanceKlass* ik = instanceKlass::cast(SystemDictionary::SoftReference_klass()); int offset = ik->offset_of_static_fields() + static_clock_offset; - return SystemDictionary::soft_reference_klass()->long_field(offset); + return SystemDictionary::SoftReference_klass()->long_field(offset); } void java_lang_ref_SoftReference::set_clock(jlong value) { - instanceKlass* ik = instanceKlass::cast(SystemDictionary::soft_reference_klass()); + instanceKlass* ik = instanceKlass::cast(SystemDictionary::SoftReference_klass()); int offset = ik->offset_of_static_fields() + static_clock_offset; - SystemDictionary::soft_reference_klass()->long_field_put(offset, value); + SystemDictionary::SoftReference_klass()->long_field_put(offset, value); } @@ -2407,6 +2414,10 @@ return ptypes(mt)->obj_at(idx); } +int java_dyn_MethodType::ptype_count(oop mt) { + return ptypes(mt)->length(); +} + // Support for java_dyn_MethodTypeForm @@ -2433,15 +2444,15 @@ } -// Support for sun_dyn_CallSiteImpl - -int sun_dyn_CallSiteImpl::_type_offset; -int sun_dyn_CallSiteImpl::_target_offset; -int sun_dyn_CallSiteImpl::_vmmethod_offset; - -void sun_dyn_CallSiteImpl::compute_offsets() { +// Support for java_dyn_CallSite + +int java_dyn_CallSite::_type_offset; +int java_dyn_CallSite::_target_offset; +int java_dyn_CallSite::_vmmethod_offset; + +void java_dyn_CallSite::compute_offsets() { if (!EnableInvokeDynamic) return; - klassOop k = SystemDictionary::CallSiteImpl_klass(); + klassOop k = SystemDictionary::CallSite_klass(); if (k != NULL) { compute_offset(_type_offset, k, vmSymbols::type_name(), vmSymbols::java_dyn_MethodType_signature(), true); compute_offset(_target_offset, k, vmSymbols::target_name(), vmSymbols::java_dyn_MethodHandle_signature(), true); @@ -2449,23 +2460,23 @@ } } -oop sun_dyn_CallSiteImpl::type(oop site) { +oop java_dyn_CallSite::type(oop site) { return site->obj_field(_type_offset); } -oop sun_dyn_CallSiteImpl::target(oop site) { +oop java_dyn_CallSite::target(oop site) { return site->obj_field(_target_offset); } -void sun_dyn_CallSiteImpl::set_target(oop site, oop target) { +void java_dyn_CallSite::set_target(oop site, oop target) { site->obj_field_put(_target_offset, target); } -oop sun_dyn_CallSiteImpl::vmmethod(oop site) { +oop java_dyn_CallSite::vmmethod(oop site) { return site->obj_field(_vmmethod_offset); } -void sun_dyn_CallSiteImpl::set_vmmethod(oop site, oop ref) { +void java_dyn_CallSite::set_vmmethod(oop site, oop ref) { site->obj_field_put(_vmmethod_offset, ref); } @@ -2538,7 +2549,7 @@ // the generated bytecodes for reflection, and if so, "magically" // delegate to its parent to prevent class loading from occurring // in places where applications using reflection didn't expect it. - klassOop delegating_cl_class = SystemDictionary::reflect_delegating_classloader_klass(); + klassOop delegating_cl_class = SystemDictionary::reflect_DelegatingClassLoader_klass(); // This might be null in non-1.4 JDKs if (delegating_cl_class != NULL && loader->is_a(delegating_cl_class)) { return parent(loader); @@ -2553,7 +2564,7 @@ void java_lang_System::compute_offsets() { assert(offset_of_static_fields == 0, "offsets should be initialized only once"); - instanceKlass* ik = instanceKlass::cast(SystemDictionary::system_klass()); + instanceKlass* ik = instanceKlass::cast(SystemDictionary::System_klass()); offset_of_static_fields = ik->offset_of_static_fields(); } @@ -2814,7 +2825,7 @@ java_dyn_MethodTypeForm::compute_offsets(); } if (EnableInvokeDynamic) { - sun_dyn_CallSiteImpl::compute_offsets(); + java_dyn_CallSite::compute_offsets(); } java_security_AccessControlContext::compute_offsets(); // Initialize reflection classes. The layouts of these classes diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/javaClasses.hpp --- a/src/share/vm/classfile/javaClasses.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/javaClasses.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -111,7 +111,7 @@ // Testers static bool is_instance(oop obj) { - return obj != NULL && obj->klass() == SystemDictionary::string_klass(); + return obj != NULL && obj->klass() == SystemDictionary::String_klass(); } // Debugging @@ -161,7 +161,7 @@ static void print_signature(oop java_class, outputStream *st); // Testing static bool is_instance(oop obj) { - return obj != NULL && obj->klass() == SystemDictionary::class_klass(); + return obj != NULL && obj->klass() == SystemDictionary::Class_klass(); } static bool is_primitive(oop java_class); static BasicType primitive_type(oop java_class); @@ -903,19 +903,20 @@ // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants): enum { OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype - OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument - OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another - OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive - OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI) - OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg) - OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg) - OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS) - OP_DROP_ARGS = 0x8, // remove one or more argument slots - OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI) - OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size) - OP_FLYBY = 0xB, // operate first on reified argument list (NYI) - OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI) - CONV_OP_LIMIT = 0xD, // limit of CONV_OP enumeration + OP_RETYPE_RAW = 0x1, // straight retype, trusted (void->int, Object->T) + OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument + OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another + OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive + OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI) + OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg) + OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg) + OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS) + OP_DROP_ARGS = 0x9, // remove one or more argument slots + OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI) + OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size) + OP_FLYBY = 0xC, // operate first on reified argument list (NYI) + OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI) + CONV_OP_LIMIT = 0xE, // limit of CONV_OP enumeration CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use @@ -1026,6 +1027,7 @@ static oop form(oop mt); static oop ptype(oop mt, int index); + static int ptype_count(oop mt); static symbolOop as_signature(oop mt, bool intern_if_not_found, TRAPS); static void print_signature(oop mt, outputStream* st); @@ -1060,9 +1062,9 @@ }; -// Interface to sun.dyn.CallSiteImpl objects +// Interface to java.dyn.CallSite objects -class sun_dyn_CallSiteImpl: AllStatic { +class java_dyn_CallSite: AllStatic { friend class JavaClasses; private: @@ -1082,6 +1084,14 @@ static oop vmmethod(oop site); static void set_vmmethod(oop site, oop ref); + // Testers + static bool is_subclass(klassOop klass) { + return Klass::cast(klass)->is_subclass_of(SystemDictionary::CallSite_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + // Accessors for code generation: static int target_offset_in_bytes() { return _target_offset; } static int type_offset_in_bytes() { return _type_offset; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/loaderConstraints.cpp --- a/src/share/vm/classfile/loaderConstraints.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/loaderConstraints.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -457,7 +457,8 @@ } -void LoaderConstraintTable::verify(Dictionary* dictionary) { +void LoaderConstraintTable::verify(Dictionary* dictionary, + PlaceholderTable* placeholders) { Thread *thread = Thread::current(); for (int cindex = 0; cindex < _loader_constraint_size; cindex++) { for (LoaderConstraintEntry* probe = bucket(cindex); @@ -472,7 +473,23 @@ unsigned int d_hash = dictionary->compute_hash(name, loader); int d_index = dictionary->hash_to_index(d_hash); klassOop k = dictionary->find_class(d_index, d_hash, name, loader); - guarantee(k == probe->klass(), "klass should be in dictionary"); + if (k != NULL) { + // We found the class in the system dictionary, so we should + // make sure that the klassOop matches what we already have. + guarantee(k == probe->klass(), "klass should be in dictionary"); + } else { + // If we don't find the class in the system dictionary, it + // has to be in the placeholders table. + unsigned int p_hash = placeholders->compute_hash(name, loader); + int p_index = placeholders->hash_to_index(p_hash); + PlaceholderEntry* entry = placeholders->get_entry(p_index, p_hash, + name, loader); + + // The instanceKlass might not be on the entry, so the only + // thing we can check here is whether we were successful in + // finding the class in the placeholders table. + guarantee(entry != NULL, "klass should be in the placeholders"); + } } for (int n = 0; n< probe->num_loaders(); n++) { guarantee(probe->loader(n)->is_oop_or_null(), "should be oop"); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/loaderConstraints.hpp --- a/src/share/vm/classfile/loaderConstraints.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/loaderConstraints.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -84,7 +84,7 @@ void purge_loader_constraints(BoolObjectClosure* is_alive); - void verify(Dictionary* dictionary); + void verify(Dictionary* dictionary, PlaceholderTable* placeholders); #ifndef PRODUCT void print(); #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/systemDictionary.cpp --- a/src/share/vm/classfile/systemDictionary.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/systemDictionary.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -60,10 +60,10 @@ } void SystemDictionary::compute_java_system_loader(TRAPS) { - KlassHandle system_klass(THREAD, WK_KLASS(classloader_klass)); + KlassHandle system_klass(THREAD, WK_KLASS(ClassLoader_klass)); JavaValue result(T_OBJECT); JavaCalls::call_static(&result, - KlassHandle(THREAD, WK_KLASS(classloader_klass)), + KlassHandle(THREAD, WK_KLASS(ClassLoader_klass)), vmSymbolHandles::getSystemClassLoader_name(), vmSymbolHandles::void_classloader_signature(), CHECK); @@ -99,6 +99,15 @@ return java_lang_Class::parallelCapable(class_loader()); } // ---------------------------------------------------------------------------- +// ParallelDefineClass flag does not apply to bootclass loader +bool SystemDictionary::is_parallelDefine(Handle class_loader) { + if (class_loader.is_null()) return false; + if (AllowParallelDefineClass && java_lang_Class::parallelCapable(class_loader())) { + return true; + } + return false; +} +// ---------------------------------------------------------------------------- // Resolving of classes // Forwards to resolve_or_null @@ -119,7 +128,7 @@ // in which case we have to check whether the pending exception is a ClassNotFoundException, // and if so convert it to a NoClassDefFoundError // And chain the original ClassNotFoundException - if (throw_error && PENDING_EXCEPTION->is_a(SystemDictionary::classNotFoundException_klass())) { + if (throw_error && PENDING_EXCEPTION->is_a(SystemDictionary::ClassNotFoundException_klass())) { ResourceMark rm(THREAD); assert(klass_h() == NULL, "Should not have result with exception pending"); Handle e(THREAD, PENDING_EXCEPTION); @@ -350,7 +359,7 @@ assert(class_loader() != NULL, "should not have non-null protection domain for null classloader"); - KlassHandle system_loader(THREAD, SystemDictionary::classloader_klass()); + KlassHandle system_loader(THREAD, SystemDictionary::ClassLoader_klass()); JavaCalls::call_special(&result, class_loader, system_loader, @@ -724,17 +733,17 @@ // Do actual loading k = load_instance_class(name, class_loader, THREAD); - // For UnsyncloadClass and AllowParallelDefineClass only: + // For UnsyncloadClass only // If they got a linkageError, check if a parallel class load succeeded. // If it did, then for bytecode resolution the specification requires // that we return the same result we did for the other thread, i.e. the // successfully loaded instanceKlass // Should not get here for classloaders that support parallelism - // with the new cleaner mechanism + // with the new cleaner mechanism, even with AllowParallelDefineClass // Bootstrap goes through here to allow for an extra guarantee check if (UnsyncloadClass || (class_loader.is_null())) { if (k.is_null() && HAS_PENDING_EXCEPTION - && PENDING_EXCEPTION->is_a(SystemDictionary::linkageError_klass())) { + && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { MutexLocker mu(SystemDictionary_lock, THREAD); klassOop check = find_class(d_index, d_hash, name, class_loader); if (check != NULL) { @@ -970,6 +979,7 @@ host_klass, cp_patches, parsed_name, + true, THREAD); @@ -1025,6 +1035,7 @@ Handle class_loader, Handle protection_domain, ClassFileStream* st, + bool verify, TRAPS) { // Classloaders that support parallelism, e.g. bootstrap classloader, @@ -1055,6 +1066,7 @@ class_loader, protection_domain, parsed_name, + verify, THREAD); const char* pkg = "java/"; @@ -1355,7 +1367,7 @@ JavaValue result(T_OBJECT); - KlassHandle spec_klass (THREAD, SystemDictionary::classloader_klass()); + KlassHandle spec_klass (THREAD, SystemDictionary::ClassLoader_klass()); // Call public unsynchronized loadClass(String) directly for all class loaders // for parallelCapable class loaders. JDK >=7, loadClass(String, boolean) will @@ -1480,14 +1492,17 @@ } // Support parallel classloading -// Initial implementation for bootstrap classloader -// For custom class loaders that support parallel classloading, +// All parallel class loaders, including bootstrap classloader +// lock a placeholder entry for this class/class_loader pair +// to allow parallel defines of different classes for this class loader // With AllowParallelDefine flag==true, in case they do not synchronize around // FindLoadedClass/DefineClass, calls, we check for parallel // loading for them, wait if a defineClass is in progress // and return the initial requestor's results +// This flag does not apply to the bootstrap classloader. // With AllowParallelDefine flag==false, call through to define_instance_class // which will throw LinkageError: duplicate class definition. +// False is the requested default. // For better performance, the class loaders should synchronize // findClass(), i.e. FindLoadedClass/DefineClassIfAbsent or they // potentially waste time reading and parsing the bytestream. @@ -1508,9 +1523,11 @@ { MutexLocker mu(SystemDictionary_lock, THREAD); // First check if class already defined - klassOop check = find_class(d_index, d_hash, name_h, class_loader); - if (check != NULL) { - return(instanceKlassHandle(THREAD, check)); + if (UnsyncloadClass || (is_parallelDefine(class_loader))) { + klassOop check = find_class(d_index, d_hash, name_h, class_loader); + if (check != NULL) { + return(instanceKlassHandle(THREAD, check)); + } } // Acquire define token for this class/classloader @@ -1526,7 +1543,7 @@ // Only special cases allow parallel defines and can use other thread's results // Other cases fall through, and may run into duplicate defines // caught by finding an entry in the SystemDictionary - if ((UnsyncloadClass || AllowParallelDefineClass) && (probe->instanceKlass() != NULL)) { + if ((UnsyncloadClass || is_parallelDefine(class_loader)) && (probe->instanceKlass() != NULL)) { probe->remove_seen_thread(THREAD, PlaceholderTable::DEFINE_CLASS); placeholders()->find_and_remove(p_index, p_hash, name_h, class_loader, THREAD); SystemDictionary_lock->notify_all(); @@ -1927,13 +1944,13 @@ void SystemDictionary::initialize_preloaded_classes(TRAPS) { - assert(WK_KLASS(object_klass) == NULL, "preloaded classes should only be initialized once"); + assert(WK_KLASS(Object_klass) == NULL, "preloaded classes should only be initialized once"); // Preload commonly used klasses WKID scan = FIRST_WKID; // first do Object, String, Class - initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(class_klass), scan, CHECK); + initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(Class_klass), scan, CHECK); - debug_only(instanceKlass::verify_class_klass_nonstatic_oop_maps(WK_KLASS(class_klass))); + debug_only(instanceKlass::verify_class_klass_nonstatic_oop_maps(WK_KLASS(Class_klass))); // Fixup mirrors for classes loaded before java.lang.Class. // These calls iterate over the objects currently in the perm gen @@ -1944,33 +1961,33 @@ Universe::fixup_mirrors(CHECK); // do a bunch more: - initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(reference_klass), scan, CHECK); + initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(Reference_klass), scan, CHECK); // Preload ref klasses and set reference types - instanceKlass::cast(WK_KLASS(reference_klass))->set_reference_type(REF_OTHER); - instanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(reference_klass)); + instanceKlass::cast(WK_KLASS(Reference_klass))->set_reference_type(REF_OTHER); + instanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass)); - initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(phantom_reference_klass), scan, CHECK); - instanceKlass::cast(WK_KLASS(soft_reference_klass))->set_reference_type(REF_SOFT); - instanceKlass::cast(WK_KLASS(weak_reference_klass))->set_reference_type(REF_WEAK); - instanceKlass::cast(WK_KLASS(final_reference_klass))->set_reference_type(REF_FINAL); - instanceKlass::cast(WK_KLASS(phantom_reference_klass))->set_reference_type(REF_PHANTOM); + initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(PhantomReference_klass), scan, CHECK); + instanceKlass::cast(WK_KLASS(SoftReference_klass))->set_reference_type(REF_SOFT); + instanceKlass::cast(WK_KLASS(WeakReference_klass))->set_reference_type(REF_WEAK); + instanceKlass::cast(WK_KLASS(FinalReference_klass))->set_reference_type(REF_FINAL); + instanceKlass::cast(WK_KLASS(PhantomReference_klass))->set_reference_type(REF_PHANTOM); WKID meth_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass); WKID meth_group_end = WK_KLASS_ENUM_NAME(WrongMethodTypeException_klass); initialize_wk_klasses_until(meth_group_start, scan, CHECK); if (EnableMethodHandles) { - initialize_wk_klasses_through(meth_group_start, scan, CHECK); + initialize_wk_klasses_through(meth_group_end, scan, CHECK); } if (_well_known_klasses[meth_group_start] == NULL) { // Skip the rest of the method handle classes, if MethodHandle is not loaded. scan = WKID(meth_group_end+1); } WKID indy_group_start = WK_KLASS_ENUM_NAME(Linkage_klass); - WKID indy_group_end = WK_KLASS_ENUM_NAME(Dynamic_klass); + WKID indy_group_end = WK_KLASS_ENUM_NAME(InvokeDynamic_klass); initialize_wk_klasses_until(indy_group_start, scan, CHECK); if (EnableInvokeDynamic) { - initialize_wk_klasses_through(indy_group_start, scan, CHECK); + initialize_wk_klasses_through(indy_group_end, scan, CHECK); } if (_well_known_klasses[indy_group_start] == NULL) { // Skip the rest of the dynamic typing classes, if Linkage is not loaded. @@ -1979,14 +1996,14 @@ initialize_wk_klasses_until(WKID_LIMIT, scan, CHECK); - _box_klasses[T_BOOLEAN] = WK_KLASS(boolean_klass); - _box_klasses[T_CHAR] = WK_KLASS(char_klass); - _box_klasses[T_FLOAT] = WK_KLASS(float_klass); - _box_klasses[T_DOUBLE] = WK_KLASS(double_klass); - _box_klasses[T_BYTE] = WK_KLASS(byte_klass); - _box_klasses[T_SHORT] = WK_KLASS(short_klass); - _box_klasses[T_INT] = WK_KLASS(int_klass); - _box_klasses[T_LONG] = WK_KLASS(long_klass); + _box_klasses[T_BOOLEAN] = WK_KLASS(Boolean_klass); + _box_klasses[T_CHAR] = WK_KLASS(Character_klass); + _box_klasses[T_FLOAT] = WK_KLASS(Float_klass); + _box_klasses[T_DOUBLE] = WK_KLASS(Double_klass); + _box_klasses[T_BYTE] = WK_KLASS(Byte_klass); + _box_klasses[T_SHORT] = WK_KLASS(Short_klass); + _box_klasses[T_INT] = WK_KLASS(Integer_klass); + _box_klasses[T_LONG] = WK_KLASS(Long_klass); //_box_klasses[T_OBJECT] = WK_KLASS(object_klass); //_box_klasses[T_ARRAY] = WK_KLASS(object_klass); @@ -1997,11 +2014,11 @@ #endif // KERNEL { // Compute whether we should use loadClass or loadClassInternal when loading classes. - methodOop method = instanceKlass::cast(classloader_klass())->find_method(vmSymbols::loadClassInternal_name(), vmSymbols::string_class_signature()); + methodOop method = instanceKlass::cast(ClassLoader_klass())->find_method(vmSymbols::loadClassInternal_name(), vmSymbols::string_class_signature()); _has_loadClassInternal = (method != NULL); } { // Compute whether we should use checkPackageAccess or NOT - methodOop method = instanceKlass::cast(classloader_klass())->find_method(vmSymbols::checkPackageAccess_name(), vmSymbols::class_protectiondomain_signature()); + methodOop method = instanceKlass::cast(ClassLoader_klass())->find_method(vmSymbols::checkPackageAccess_name(), vmSymbols::class_protectiondomain_signature()); _has_checkPackageAccess = (method != NULL); } } @@ -2323,6 +2340,8 @@ SymbolPropertyEntry* spe = invoke_method_table()->find_entry(index, hash, signature); if (spe == NULL || spe->property_oop() == NULL) { // Must create lots of stuff here, but outside of the SystemDictionary lock. + if (THREAD->is_Compiler_thread()) + return NULL; // do not attempt from within compiler Handle mt = compute_method_handle_type(signature(), class_loader, protection_domain, CHECK_NULL); @@ -2355,7 +2374,7 @@ TRAPS) { Handle empty; int npts = ArgumentCount(signature()).size(); - objArrayHandle pts = oopFactory::new_objArray(SystemDictionary::class_klass(), npts, CHECK_(empty)); + objArrayHandle pts = oopFactory::new_objArray(SystemDictionary::Class_klass(), npts, CHECK_(empty)); int arg = 0; Handle rt; // the return type from the signature for (SignatureStream ss(signature()); !ss.is_done(); ss.next()) { @@ -2401,7 +2420,7 @@ methodHandle mh_invdyn, TRAPS) { Handle empty; - // call sun.dyn.CallSiteImpl::makeSite(caller, name, mtype, cmid, cbci) + // call java.dyn.CallSite::makeSite(caller, name, mtype, cmid, cbci) oop name_str_oop = StringTable::intern(name(), CHECK_(empty)); // not a handle! JavaCallArguments args(Handle(THREAD, caller->java_mirror())); args.push_oop(name_str_oop); @@ -2410,15 +2429,19 @@ args.push_int(caller_bci); JavaValue result(T_OBJECT); JavaCalls::call_static(&result, - SystemDictionary::CallSiteImpl_klass(), + SystemDictionary::CallSite_klass(), vmSymbols::makeSite_name(), vmSymbols::makeSite_signature(), &args, CHECK_(empty)); oop call_site_oop = (oop) result.get_jobject(); - sun_dyn_CallSiteImpl::set_vmmethod(call_site_oop, mh_invdyn()); + assert(call_site_oop->is_oop() + /*&& java_dyn_CallSite::is_instance(call_site_oop)*/, "must be sane"); + java_dyn_CallSite::set_vmmethod(call_site_oop, mh_invdyn()); if (TraceMethodHandles) { +#ifndef PRODUCT tty->print_cr("Linked invokedynamic bci=%d site="INTPTR_FORMAT":", caller_bci, call_site_oop); call_site_oop->print(); tty->cr(); +#endif //PRODUCT } return call_site_oop; } @@ -2431,9 +2454,17 @@ instanceKlassHandle ik(THREAD, caller()); - if (ik->bootstrap_method() != NULL) { - return Handle(THREAD, ik->bootstrap_method()); + oop boot_method_oop = ik->bootstrap_method(); + if (boot_method_oop != NULL) { + if (TraceMethodHandles) { + tty->print_cr("bootstrap method for "PTR_FORMAT" cached as "PTR_FORMAT":", ik(), boot_method_oop); + } + NOT_PRODUCT(if (!boot_method_oop->is_oop()) { tty->print_cr("*** boot MH of "PTR_FORMAT" = "PTR_FORMAT, ik(), boot_method_oop); ik()->print(); }); + assert(boot_method_oop->is_oop() + && java_dyn_MethodHandle::is_instance(boot_method_oop), "must be sane"); + return Handle(THREAD, boot_method_oop); } + boot_method_oop = NULL; // GC safety // call java.dyn.Linkage::findBootstrapMethod(caller, sbk) JavaCallArguments args(Handle(THREAD, ik->java_mirror())); @@ -2447,15 +2478,34 @@ vmSymbols::findBootstrapMethod_name(), vmSymbols::findBootstrapMethod_signature(), &args, CHECK_(empty)); - oop boot_method_oop = (oop) result.get_jobject(); + boot_method_oop = (oop) result.get_jobject(); if (boot_method_oop != NULL) { + if (TraceMethodHandles) { +#ifndef PRODUCT + tty->print_cr("--------"); + tty->print_cr("bootstrap method for "PTR_FORMAT" computed as "PTR_FORMAT":", ik(), boot_method_oop); + ik()->print(); + boot_method_oop->print(); + tty->print_cr("========"); +#endif //PRODUCT + } + assert(boot_method_oop->is_oop() + && java_dyn_MethodHandle::is_instance(boot_method_oop), "must be sane"); // probably no race conditions, but let's be careful: if (Atomic::cmpxchg_ptr(boot_method_oop, ik->adr_bootstrap_method(), NULL) == NULL) ik->set_bootstrap_method(boot_method_oop); else boot_method_oop = ik->bootstrap_method(); } else { + if (TraceMethodHandles) { +#ifndef PRODUCT + tty->print_cr("--------"); + tty->print_cr("bootstrap method for "PTR_FORMAT" computed as NULL:", ik()); + ik()->print(); + tty->print_cr("========"); +#endif //PRODUCT + } boot_method_oop = ik->bootstrap_method(); } @@ -2523,7 +2573,7 @@ // Verify constraint table guarantee(constraints() != NULL, "Verify of loader constraints failed"); - constraints()->verify(dictionary()); + constraints()->verify(dictionary(), placeholders()); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/systemDictionary.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -82,55 +82,55 @@ #define WK_KLASSES_DO(template) \ /* well-known classes */ \ - template(object_klass, java_lang_Object, Pre) \ - template(string_klass, java_lang_String, Pre) \ - template(class_klass, java_lang_Class, Pre) \ - template(cloneable_klass, java_lang_Cloneable, Pre) \ - template(classloader_klass, java_lang_ClassLoader, Pre) \ - template(serializable_klass, java_io_Serializable, Pre) \ - template(system_klass, java_lang_System, Pre) \ - template(throwable_klass, java_lang_Throwable, Pre) \ - template(error_klass, java_lang_Error, Pre) \ - template(threaddeath_klass, java_lang_ThreadDeath, Pre) \ - template(exception_klass, java_lang_Exception, Pre) \ - template(runtime_exception_klass, java_lang_RuntimeException, Pre) \ - template(protectionDomain_klass, java_security_ProtectionDomain, Pre) \ + template(Object_klass, java_lang_Object, Pre) \ + template(String_klass, java_lang_String, Pre) \ + template(Class_klass, java_lang_Class, Pre) \ + template(Cloneable_klass, java_lang_Cloneable, Pre) \ + template(ClassLoader_klass, java_lang_ClassLoader, Pre) \ + template(Serializable_klass, java_io_Serializable, Pre) \ + template(System_klass, java_lang_System, Pre) \ + template(Throwable_klass, java_lang_Throwable, Pre) \ + template(Error_klass, java_lang_Error, Pre) \ + template(ThreadDeath_klass, java_lang_ThreadDeath, Pre) \ + template(Exception_klass, java_lang_Exception, Pre) \ + template(RuntimeException_klass, java_lang_RuntimeException, Pre) \ + template(ProtectionDomain_klass, java_security_ProtectionDomain, Pre) \ template(AccessControlContext_klass, java_security_AccessControlContext, Pre) \ - template(classNotFoundException_klass, java_lang_ClassNotFoundException, Pre) \ - template(noClassDefFoundError_klass, java_lang_NoClassDefFoundError, Pre) \ - template(linkageError_klass, java_lang_LinkageError, Pre) \ + template(ClassNotFoundException_klass, java_lang_ClassNotFoundException, Pre) \ + template(NoClassDefFoundError_klass, java_lang_NoClassDefFoundError, Pre) \ + template(LinkageError_klass, java_lang_LinkageError, Pre) \ template(ClassCastException_klass, java_lang_ClassCastException, Pre) \ template(ArrayStoreException_klass, java_lang_ArrayStoreException, Pre) \ - template(virtualMachineError_klass, java_lang_VirtualMachineError, Pre) \ + template(VirtualMachineError_klass, java_lang_VirtualMachineError, Pre) \ template(OutOfMemoryError_klass, java_lang_OutOfMemoryError, Pre) \ template(StackOverflowError_klass, java_lang_StackOverflowError, Pre) \ template(IllegalMonitorStateException_klass, java_lang_IllegalMonitorStateException, Pre) \ - template(reference_klass, java_lang_ref_Reference, Pre) \ + template(Reference_klass, java_lang_ref_Reference, Pre) \ \ /* Preload ref klasses and set reference types */ \ - template(soft_reference_klass, java_lang_ref_SoftReference, Pre) \ - template(weak_reference_klass, java_lang_ref_WeakReference, Pre) \ - template(final_reference_klass, java_lang_ref_FinalReference, Pre) \ - template(phantom_reference_klass, java_lang_ref_PhantomReference, Pre) \ - template(finalizer_klass, java_lang_ref_Finalizer, Pre) \ + template(SoftReference_klass, java_lang_ref_SoftReference, Pre) \ + template(WeakReference_klass, java_lang_ref_WeakReference, Pre) \ + template(FinalReference_klass, java_lang_ref_FinalReference, Pre) \ + template(PhantomReference_klass, java_lang_ref_PhantomReference, Pre) \ + template(Finalizer_klass, java_lang_ref_Finalizer, Pre) \ \ - template(thread_klass, java_lang_Thread, Pre) \ - template(threadGroup_klass, java_lang_ThreadGroup, Pre) \ - template(properties_klass, java_util_Properties, Pre) \ - template(reflect_accessible_object_klass, java_lang_reflect_AccessibleObject, Pre) \ - template(reflect_field_klass, java_lang_reflect_Field, Pre) \ - template(reflect_method_klass, java_lang_reflect_Method, Pre) \ - template(reflect_constructor_klass, java_lang_reflect_Constructor, Pre) \ + template(Thread_klass, java_lang_Thread, Pre) \ + template(ThreadGroup_klass, java_lang_ThreadGroup, Pre) \ + template(Properties_klass, java_util_Properties, Pre) \ + template(reflect_AccessibleObject_klass, java_lang_reflect_AccessibleObject, Pre) \ + template(reflect_Field_klass, java_lang_reflect_Field, Pre) \ + template(reflect_Method_klass, java_lang_reflect_Method, Pre) \ + template(reflect_Constructor_klass, java_lang_reflect_Constructor, Pre) \ \ /* NOTE: needed too early in bootstrapping process to have checks based on JDK version */ \ /* Universe::is_gte_jdk14x_version() is not set up by this point. */ \ /* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \ - template(reflect_magic_klass, sun_reflect_MagicAccessorImpl, Opt) \ - template(reflect_method_accessor_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef) \ - template(reflect_constructor_accessor_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef) \ - template(reflect_delegating_classloader_klass, sun_reflect_DelegatingClassLoader, Opt) \ - template(reflect_constant_pool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \ - template(reflect_unsafe_static_field_accessor_impl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15) \ + template(reflect_MagicAccessorImpl_klass, sun_reflect_MagicAccessorImpl, Opt) \ + template(reflect_MethodAccessorImpl_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef) \ + template(reflect_ConstructorAccessorImpl_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef) \ + template(reflect_DelegatingClassLoader_klass, sun_reflect_DelegatingClassLoader, Opt) \ + template(reflect_ConstantPool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \ + template(reflect_UnsafeStaticFieldAccessorImpl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15) \ \ /* support for dynamic typing; it's OK if these are NULL in earlier JDKs */ \ template(MethodHandle_klass, java_dyn_MethodHandle, Opt) \ @@ -144,16 +144,14 @@ template(WrongMethodTypeException_klass, java_dyn_WrongMethodTypeException, Opt) \ template(Linkage_klass, java_dyn_Linkage, Opt) \ template(CallSite_klass, java_dyn_CallSite, Opt) \ - template(CallSiteImpl_klass, sun_dyn_CallSiteImpl, Opt) \ - template(Dynamic_klass, java_dyn_Dynamic, Opt) \ - /* Note: MethodHandle must be first, and Dynamic last in group */ \ + template(InvokeDynamic_klass, java_dyn_InvokeDynamic, Opt) \ + /* Note: MethodHandle must be first, and InvokeDynamic last in group */ \ \ - template(vector_klass, java_util_Vector, Pre) \ - template(hashtable_klass, java_util_Hashtable, Pre) \ - template(stringBuffer_klass, java_lang_StringBuffer, Pre) \ + template(StringBuffer_klass, java_lang_StringBuffer, Pre) \ + template(StringBuilder_klass, java_lang_StringBuilder, Pre) \ \ /* It's NULL in non-1.4 JDKs. */ \ - template(stackTraceElement_klass, java_lang_StackTraceElement, Opt) \ + template(StackTraceElement_klass, java_lang_StackTraceElement, Opt) \ /* Universe::is_gte_jdk14x_version() is not set up by this point. */ \ /* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \ template(java_nio_Buffer_klass, java_nio_Buffer, Opt) \ @@ -164,14 +162,14 @@ template(sun_jkernel_DownloadManager_klass, sun_jkernel_DownloadManager, Opt_Kernel) \ \ /* Preload boxing klasses */ \ - template(boolean_klass, java_lang_Boolean, Pre) \ - template(char_klass, java_lang_Character, Pre) \ - template(float_klass, java_lang_Float, Pre) \ - template(double_klass, java_lang_Double, Pre) \ - template(byte_klass, java_lang_Byte, Pre) \ - template(short_klass, java_lang_Short, Pre) \ - template(int_klass, java_lang_Integer, Pre) \ - template(long_klass, java_lang_Long, Pre) \ + template(Boolean_klass, java_lang_Boolean, Pre) \ + template(Character_klass, java_lang_Character, Pre) \ + template(Float_klass, java_lang_Float, Pre) \ + template(Double_klass, java_lang_Double, Pre) \ + template(Byte_klass, java_lang_Byte, Pre) \ + template(Short_klass, java_lang_Short, Pre) \ + template(Integer_klass, java_lang_Integer, Pre) \ + template(Long_klass, java_lang_Long, Pre) \ /*end*/ @@ -259,7 +257,9 @@ TRAPS); // Resolve from stream (called by jni_DefineClass and JVM_DefineClass) - static klassOop resolve_from_stream(symbolHandle class_name, Handle class_loader, Handle protection_domain, ClassFileStream* st, TRAPS); + static klassOop resolve_from_stream(symbolHandle class_name, Handle class_loader, + Handle protection_domain, + ClassFileStream* st, bool verify, TRAPS); // Lookup an already loaded class. If not found NULL is returned. static klassOop find(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); @@ -436,8 +436,8 @@ // Tells whether ClassLoader.checkPackageAccess is present static bool has_checkPackageAccess() { return _has_checkPackageAccess; } - static bool class_klass_loaded() { return WK_KLASS(class_klass) != NULL; } - static bool cloneable_klass_loaded() { return WK_KLASS(cloneable_klass) != NULL; } + static bool Class_klass_loaded() { return WK_KLASS(Class_klass) != NULL; } + static bool Cloneable_klass_loaded() { return WK_KLASS(Cloneable_klass) != NULL; } // Returns default system loader static oop java_system_loader(); @@ -576,6 +576,7 @@ static Handle compute_loader_lock_object(Handle class_loader, TRAPS); static void check_loader_lock_contention(Handle loader_lock, TRAPS); static bool is_parallelCapable(Handle class_loader); + static bool is_parallelDefine(Handle class_loader); static klassOop find_shared_class(symbolHandle class_name); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/verifier.cpp --- a/src/share/vm/classfile/verifier.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/verifier.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -53,8 +53,8 @@ // Methods in Verifier -bool Verifier::should_verify_for(oop class_loader) { - return class_loader == NULL ? +bool Verifier::should_verify_for(oop class_loader, bool should_verify_class) { + return (class_loader == NULL || !should_verify_class) ? BytecodeVerificationLocal : BytecodeVerificationRemote; } @@ -68,7 +68,7 @@ return !need_verify; } -bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, TRAPS) { +bool Verifier::verify(instanceKlassHandle klass, Verifier::Mode mode, bool should_verify_class, TRAPS) { ResourceMark rm(THREAD); HandleMark hm; @@ -81,7 +81,7 @@ // If the class should be verified, first see if we can use the split // verifier. If not, or if verification fails and FailOverToOldVerifier // is set, then call the inference verifier. - if (is_eligible_for_verification(klass)) { + if (is_eligible_for_verification(klass, should_verify_class)) { if (TraceClassInitialization) { tty->print_cr("Start class verification for: %s", klassName); } @@ -141,12 +141,13 @@ } } -bool Verifier::is_eligible_for_verification(instanceKlassHandle klass) { +bool Verifier::is_eligible_for_verification(instanceKlassHandle klass, bool should_verify_class) { symbolOop name = klass->name(); - klassOop refl_magic_klass = SystemDictionary::reflect_magic_klass(); + klassOop refl_magic_klass = SystemDictionary::reflect_MagicAccessorImpl_klass(); - return (should_verify_for(klass->class_loader()) && + return (should_verify_for(klass->class_loader(), should_verify_class) && // return if the class is a bootstrapping class + // or defineClass specified not to verify by default (flags override passed arg) // We need to skip the following four for bootstraping name != vmSymbols::java_lang_Object() && name != vmSymbols::java_lang_Class() && @@ -1902,17 +1903,8 @@ verify_cp_type(index, cp, types, CHECK_VERIFY(this)); // Get method name and signature - symbolHandle method_name; - symbolHandle method_sig; - if (opcode == Bytecodes::_invokedynamic) { - int name_index = cp->name_ref_index_at(index); - int sig_index = cp->signature_ref_index_at(index); - method_name = symbolHandle(THREAD, cp->symbol_at(name_index)); - method_sig = symbolHandle(THREAD, cp->symbol_at(sig_index)); - } else { - method_name = symbolHandle(THREAD, cp->name_ref_at(index)); - method_sig = symbolHandle(THREAD, cp->signature_ref_at(index)); - } + symbolHandle method_name(THREAD, cp->name_ref_at(index)); + symbolHandle method_sig(THREAD, cp->signature_ref_at(index)); if (!SignatureVerifier::is_valid_method_signature(method_sig)) { class_format_error( diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/verifier.hpp --- a/src/share/vm/classfile/verifier.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/verifier.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -34,16 +34,18 @@ * Otherwise, no exception is thrown and the return indicates the * error. */ - static bool verify(instanceKlassHandle klass, Mode mode, TRAPS); + static bool verify(instanceKlassHandle klass, Mode mode, bool should_verify_class, TRAPS); - // Return false if the class is loaded by the bootstrap loader. - static bool should_verify_for(oop class_loader); + // Return false if the class is loaded by the bootstrap loader, + // or if defineClass was called requesting skipping verification + // -Xverify:all/none override this value + static bool should_verify_for(oop class_loader, bool should_verify_class); // Relax certain verifier checks to enable some broken 1.1 apps to run on 1.2. static bool relax_verify_for(oop class_loader); private: - static bool is_eligible_for_verification(instanceKlassHandle klass); + static bool is_eligible_for_verification(instanceKlassHandle klass, bool should_verify_class); static symbolHandle inference_verify( instanceKlassHandle klass, char* msg, size_t msg_len, TRAPS); }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/vmSymbols.cpp --- a/src/share/vm/classfile/vmSymbols.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/vmSymbols.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -70,6 +70,7 @@ void vmSymbols::initialize(TRAPS) { assert((int)SID_LIMIT <= (1< (1<find_method(mname, msig); +} + #define VM_INTRINSIC_INITIALIZE(id, klass, name, sig, flags) #id "\0" static const char* vm_intrinsic_name_bodies = @@ -303,6 +356,11 @@ const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED; return (flags & (req | neg)) == req; } +inline bool match_F_Y(jshort flags) { + const int req = JVM_ACC_SYNCHRONIZED; + const int neg = JVM_ACC_STATIC; + return (flags & (req | neg)) == req; +} inline bool match_F_RN(jshort flags) { const int req = JVM_ACC_NATIVE; const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED; @@ -325,15 +383,15 @@ } // These are for forming case labels: -#define ID3(x, y, z) (( jint)(z) + \ - ((jint)(y) << vmSymbols::log2_SID_LIMIT) + \ - ((jint)(x) << (2*vmSymbols::log2_SID_LIMIT)) ) +#define ID3(x, y, z) (( jlong)(z) + \ + ((jlong)(y) << vmSymbols::log2_SID_LIMIT) + \ + ((jlong)(x) << (2*vmSymbols::log2_SID_LIMIT)) ) #define SID_ENUM(n) vmSymbols::VM_SYMBOL_ENUM_NAME(n) -vmIntrinsics::ID vmIntrinsics::find_id(vmSymbols::SID holder, - vmSymbols::SID name, - vmSymbols::SID sig, - jshort flags) { +vmIntrinsics::ID vmIntrinsics::find_id_impl(vmSymbols::SID holder, + vmSymbols::SID name, + vmSymbols::SID sig, + jshort flags) { assert((int)vmSymbols::SID_LIMIT <= (1<> shift) & mask) == 1021, ""); + return vmSymbols::SID( (info >> shift) & mask ); } vmSymbols::SID vmIntrinsics::name_for(vmIntrinsics::ID id) { -#ifndef PRODUCT -#define VM_INTRINSIC_CASE(id, klass, name, sig, fcode) \ - case id: return SID_ENUM(name); - - switch (id) { - VM_INTRINSICS_DO(VM_INTRINSIC_CASE, - VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE); - } -#undef VM_INTRINSIC_CASE -#endif //PRODUCT - return vmSymbols::NO_SID; + jlong info = intrinsic_info(id); + int shift = vmSymbols::log2_SID_LIMIT + log2_FLAG_LIMIT, mask = right_n_bits(vmSymbols::log2_SID_LIMIT); + assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 1022, ""); + return vmSymbols::SID( (info >> shift) & mask ); } vmSymbols::SID vmIntrinsics::signature_for(vmIntrinsics::ID id) { -#ifndef PRODUCT -#define VM_INTRINSIC_CASE(id, klass, name, sig, fcode) \ - case id: return SID_ENUM(sig); - - switch (id) { - VM_INTRINSICS_DO(VM_INTRINSIC_CASE, - VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE); - } -#undef VM_INTRINSIC_CASE -#endif //PRODUCT - return vmSymbols::NO_SID; + jlong info = intrinsic_info(id); + int shift = log2_FLAG_LIMIT, mask = right_n_bits(vmSymbols::log2_SID_LIMIT); + assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 1023, ""); + return vmSymbols::SID( (info >> shift) & mask ); } vmIntrinsics::Flags vmIntrinsics::flags_for(vmIntrinsics::ID id) { -#ifndef PRODUCT -#define VM_INTRINSIC_CASE(id, klass, name, sig, fcode) \ - case id: return fcode; - - switch (id) { - VM_INTRINSICS_DO(VM_INTRINSIC_CASE, - VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE); - } -#undef VM_INTRINSIC_CASE -#endif //PRODUCT - return F_none; + jlong info = intrinsic_info(id); + int shift = 0, mask = right_n_bits(log2_FLAG_LIMIT); + assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 15, ""); + return Flags( (info >> shift) & mask ); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/classfile/vmSymbols.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -84,6 +84,7 @@ template(java_lang_reflect_Field, "java/lang/reflect/Field") \ template(java_lang_reflect_Array, "java/lang/reflect/Array") \ template(java_lang_StringBuffer, "java/lang/StringBuffer") \ + template(java_lang_StringBuilder, "java/lang/StringBuilder") \ template(java_lang_CharSequence, "java/lang/CharSequence") \ template(java_security_AccessControlContext, "java/security/AccessControlContext") \ template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \ @@ -104,6 +105,7 @@ template(java_lang_AssertionStatusDirectives, "java/lang/AssertionStatusDirectives") \ template(sun_jkernel_DownloadManager, "sun/jkernel/DownloadManager") \ template(getBootClassPathEntryForClass_name, "getBootClassPathEntryForClass") \ + template(setBootClassLoaderHook_name, "setBootClassLoaderHook") \ \ /* class file format tags */ \ template(tag_source_file, "SourceFile") \ @@ -217,7 +219,7 @@ template(base_name, "base") \ \ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ - template(java_dyn_Dynamic, "java/dyn/Dynamic") \ + template(java_dyn_InvokeDynamic, "java/dyn/InvokeDynamic") \ template(java_dyn_Linkage, "java/dyn/Linkage") \ template(java_dyn_CallSite, "java/dyn/CallSite") \ template(java_dyn_MethodHandle, "java/dyn/MethodHandle") \ @@ -233,10 +235,9 @@ template(sun_dyn_AdapterMethodHandle, "sun/dyn/AdapterMethodHandle") \ template(sun_dyn_BoundMethodHandle, "sun/dyn/BoundMethodHandle") \ template(sun_dyn_DirectMethodHandle, "sun/dyn/DirectMethodHandle") \ - template(sun_dyn_CallSiteImpl, "sun/dyn/CallSiteImpl") \ template(makeImpl_name, "makeImpl") /*MethodType::makeImpl*/ \ template(makeImpl_signature, "(Ljava/lang/Class;[Ljava/lang/Class;ZZ)Ljava/dyn/MethodType;") \ - template(makeSite_name, "makeSite") /*CallSiteImpl::makeImpl*/ \ + template(makeSite_name, "makeSite") /*CallSite::makeSite*/ \ template(makeSite_signature, "(Ljava/lang/Class;Ljava/lang/String;Ljava/dyn/MethodType;II)Ljava/dyn/CallSite;") \ template(findBootstrapMethod_name, "findBootstrapMethod") \ template(findBootstrapMethod_signature, "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/dyn/MethodHandle;") \ @@ -335,6 +336,7 @@ template(ptypes_name, "ptypes") \ template(form_name, "form") \ template(erasedType_name, "erasedType") \ + template(append_name, "append") \ \ /* non-intrinsic name/signature pairs: */ \ template(register_method_name, "register") \ @@ -345,9 +347,14 @@ \ /* common signatures names */ \ template(void_method_signature, "()V") \ + template(void_boolean_signature, "()Z") \ + template(void_byte_signature, "()B") \ + template(void_char_signature, "()C") \ + template(void_short_signature, "()S") \ template(void_int_signature, "()I") \ template(void_long_signature, "()J") \ - template(void_boolean_signature, "()Z") \ + template(void_float_signature, "()F") \ + template(void_double_signature, "()D") \ template(int_void_signature, "(I)V") \ template(int_int_signature, "(I)I") \ template(int_bool_signature, "(I)Z") \ @@ -416,6 +423,13 @@ template(string_signature, "Ljava/lang/String;") \ template(reference_signature, "Ljava/lang/ref/Reference;") \ template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \ + template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \ + template(int_StringBuilder_signature, "(I)Ljava/lang/StringBuilder;") \ + template(char_StringBuilder_signature, "(C)Ljava/lang/StringBuilder;") \ + template(String_StringBuffer_signature, "(Ljava/lang/String;)Ljava/lang/StringBuffer;") \ + template(int_StringBuffer_signature, "(I)Ljava/lang/StringBuffer;") \ + template(char_StringBuffer_signature, "(C)Ljava/lang/StringBuffer;") \ + template(int_String_signature, "(I)Ljava/lang/String;") \ /* signature symbols needed by intrinsics */ \ VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \ \ @@ -815,12 +829,76 @@ /*the compiler does have special inlining code for these; bytecode inline is just fine */ \ \ do_intrinsic(_fillInStackTrace, java_lang_Throwable, fillInStackTrace_name, void_throwable_signature, F_RNY) \ - \ - do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \ - /* (symbol object_initializer_name defined above) */ \ - \ + \ + do_intrinsic(_StringBuilder_void, java_lang_StringBuilder, object_initializer_name, void_method_signature, F_R) \ + do_intrinsic(_StringBuilder_int, java_lang_StringBuilder, object_initializer_name, int_void_signature, F_R) \ + do_intrinsic(_StringBuilder_String, java_lang_StringBuilder, object_initializer_name, string_void_signature, F_R) \ + \ + do_intrinsic(_StringBuilder_append_char, java_lang_StringBuilder, append_name, char_StringBuilder_signature, F_R) \ + do_intrinsic(_StringBuilder_append_int, java_lang_StringBuilder, append_name, int_StringBuilder_signature, F_R) \ + do_intrinsic(_StringBuilder_append_String, java_lang_StringBuilder, append_name, String_StringBuilder_signature, F_R) \ + \ + do_intrinsic(_StringBuilder_toString, java_lang_StringBuilder, toString_name, void_string_signature, F_R) \ + \ + do_intrinsic(_StringBuffer_void, java_lang_StringBuffer, object_initializer_name, void_method_signature, F_R) \ + do_intrinsic(_StringBuffer_int, java_lang_StringBuffer, object_initializer_name, int_void_signature, F_R) \ + do_intrinsic(_StringBuffer_String, java_lang_StringBuffer, object_initializer_name, string_void_signature, F_R) \ + \ + do_intrinsic(_StringBuffer_append_char, java_lang_StringBuffer, append_name, char_StringBuffer_signature, F_Y) \ + do_intrinsic(_StringBuffer_append_int, java_lang_StringBuffer, append_name, int_StringBuffer_signature, F_Y) \ + do_intrinsic(_StringBuffer_append_String, java_lang_StringBuffer, append_name, String_StringBuffer_signature, F_Y) \ + \ + do_intrinsic(_StringBuffer_toString, java_lang_StringBuffer, toString_name, void_string_signature, F_Y) \ + \ + do_intrinsic(_Integer_toString, java_lang_Integer, toString_name, int_String_signature, F_S) \ + \ + do_intrinsic(_String_String, java_lang_String, object_initializer_name, string_void_signature, F_R) \ + \ + do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \ + /* (symbol object_initializer_name defined above) */ \ + \ do_intrinsic(_invoke, java_lang_reflect_Method, invoke_name, object_array_object_object_signature, F_R) \ /* (symbols invoke_name and invoke_signature defined above) */ \ + do_intrinsic(_checkSpreadArgument, sun_dyn_MethodHandleImpl, checkSpreadArgument_name, checkSpreadArgument_signature, F_S) \ + do_name( checkSpreadArgument_name, "checkSpreadArgument") \ + do_name( checkSpreadArgument_signature, "(Ljava/lang/Object;I)V") \ + \ + /* unboxing methods: */ \ + do_intrinsic(_booleanValue, java_lang_Boolean, booleanValue_name, void_boolean_signature, F_R) \ + do_name( booleanValue_name, "booleanValue") \ + do_intrinsic(_byteValue, java_lang_Byte, byteValue_name, void_byte_signature, F_R) \ + do_name( byteValue_name, "byteValue") \ + do_intrinsic(_charValue, java_lang_Character, charValue_name, void_char_signature, F_R) \ + do_name( charValue_name, "charValue") \ + do_intrinsic(_shortValue, java_lang_Short, shortValue_name, void_short_signature, F_R) \ + do_name( shortValue_name, "shortValue") \ + do_intrinsic(_intValue, java_lang_Integer, intValue_name, void_int_signature, F_R) \ + do_name( intValue_name, "intValue") \ + do_intrinsic(_longValue, java_lang_Long, longValue_name, void_long_signature, F_R) \ + do_name( longValue_name, "longValue") \ + do_intrinsic(_floatValue, java_lang_Float, floatValue_name, void_float_signature, F_R) \ + do_name( floatValue_name, "floatValue") \ + do_intrinsic(_doubleValue, java_lang_Double, doubleValue_name, void_double_signature, F_R) \ + do_name( doubleValue_name, "doubleValue") \ + \ + /* boxing methods: */ \ + do_name( valueOf_name, "valueOf") \ + do_intrinsic(_Boolean_valueOf, java_lang_Boolean, valueOf_name, Boolean_valueOf_signature, F_S) \ + do_name( Boolean_valueOf_signature, "(Z)Ljava/lang/Boolean;") \ + do_intrinsic(_Byte_valueOf, java_lang_Byte, valueOf_name, Byte_valueOf_signature, F_S) \ + do_name( Byte_valueOf_signature, "(B)Ljava/lang/Byte;") \ + do_intrinsic(_Character_valueOf, java_lang_Character, valueOf_name, Character_valueOf_signature, F_S) \ + do_name( Character_valueOf_signature, "(C)Ljava/lang/Character;") \ + do_intrinsic(_Short_valueOf, java_lang_Short, valueOf_name, Short_valueOf_signature, F_S) \ + do_name( Short_valueOf_signature, "(S)Ljava/lang/Short;") \ + do_intrinsic(_Integer_valueOf, java_lang_Integer, valueOf_name, Integer_valueOf_signature, F_S) \ + do_name( Integer_valueOf_signature, "(I)Ljava/lang/Integer;") \ + do_intrinsic(_Long_valueOf, java_lang_Long, valueOf_name, Long_valueOf_signature, F_S) \ + do_name( Long_valueOf_signature, "(J)Ljava/lang/Long;") \ + do_intrinsic(_Float_valueOf, java_lang_Float, valueOf_name, Float_valueOf_signature, F_S) \ + do_name( Float_valueOf_signature, "(F)Ljava/lang/Float;") \ + do_intrinsic(_Double_valueOf, java_lang_Double, valueOf_name, Double_valueOf_signature, F_S) \ + do_name( Double_valueOf_signature, "(D)Ljava/lang/Double;") \ \ /*end*/ @@ -946,11 +1024,17 @@ enum Flags { // AccessFlags syndromes relevant to intrinsics. F_none = 0, - F_R, // !static !synchronized (R="regular") - F_S, // static !synchronized - F_RN, // !static native !synchronized - F_SN, // static native !synchronized - F_RNY // !static native synchronized + F_R, // !static ?native !synchronized (R="regular") + F_S, // static ?native !synchronized + F_Y, // !static ?native synchronized + F_RN, // !static native !synchronized + F_SN, // static native !synchronized + F_RNY, // !static native synchronized + + FLAG_LIMIT + }; + enum { + log2_FLAG_LIMIT = 4 // checked by an assert at start-up }; public: @@ -962,15 +1046,32 @@ static const char* name_at(ID id); +private: + static ID find_id_impl(vmSymbols::SID holder, + vmSymbols::SID name, + vmSymbols::SID sig, + jshort flags); + +public: // Given a method's class, name, signature, and access flags, report its ID. static ID find_id(vmSymbols::SID holder, vmSymbols::SID name, vmSymbols::SID sig, - jshort flags); + jshort flags) { + ID id = find_id_impl(holder, name, sig, flags); +#ifdef ASSERT + // ID _none does not hold the following asserts. + if (id == _none) return id; +#endif + assert( class_for(id) == holder, "correct id"); + assert( name_for(id) == name, "correct id"); + assert(signature_for(id) == sig, "correct id"); + return id; + } static void verify_method(ID actual_id, methodOop m) PRODUCT_RETURN; - // No need for these in the product: + // Find out the symbols behind an intrinsic: static vmSymbols::SID class_for(ID id); static vmSymbols::SID name_for(ID id); static vmSymbols::SID signature_for(ID id); @@ -980,4 +1081,11 @@ // Access to intrinsic methods: static methodOop method_for(ID id); + + // Wrapper object methods: + static ID for_boxing(BasicType type); + static ID for_unboxing(BasicType type); + + // Raw conversion: + static ID for_raw_conversion(BasicType src, BasicType dest); }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/codeBlob.hpp --- a/src/share/vm/code/codeBlob.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/codeBlob.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 @@ -102,6 +102,9 @@ virtual bool is_compiled_by_c2() const { return false; } virtual bool is_compiled_by_c1() const { return false; } + // Casting + nmethod* as_nmethod_or_null() { return is_nmethod() ? (nmethod*) this : NULL; } + // Boundaries address header_begin() const { return (address) this; } address header_end() const { return ((address) this) + _header_size; }; @@ -175,6 +178,8 @@ OopClosure* keep_alive, bool unloading_occurred); virtual void oops_do(OopClosure* f) = 0; + // (All CodeBlob subtypes other than NMethod currently have + // an empty oops_do() method. // OopMap for frame OopMapSet* oop_maps() const { return _oop_maps; } @@ -199,7 +204,8 @@ virtual void print_value_on(outputStream* st) const PRODUCT_RETURN; // Print the comment associated with offset on stream, if there is one - void print_block_comment(outputStream* stream, intptr_t offset) { + virtual void print_block_comment(outputStream* stream, address block_begin) { + intptr_t offset = (intptr_t)(block_begin - instructions_begin()); _comments.print_block_comment(stream, offset); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/codeCache.cpp --- a/src/share/vm/code/codeCache.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/codeCache.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -95,6 +95,8 @@ int CodeCache::_number_of_blobs = 0; int CodeCache::_number_of_nmethods_with_dependencies = 0; bool CodeCache::_needs_cache_clean = false; +nmethod* CodeCache::_scavenge_root_nmethods = NULL; +nmethod* CodeCache::_saved_nmethods = NULL; CodeBlob* CodeCache::first() { @@ -148,10 +150,7 @@ } } verify_if_often(); - if (PrintCodeCache2) { // Need to add a new flag - ResourceMark rm; - tty->print_cr("CodeCache allocation: addr: " INTPTR_FORMAT ", size: 0x%x\n", cb, size); - } + print_trace("allocation", cb, size); return cb; } @@ -159,10 +158,7 @@ assert_locked_or_safepoint(CodeCache_lock); verify_if_often(); - if (PrintCodeCache2) { // Need to add a new flag - ResourceMark rm; - tty->print_cr("CodeCache free: addr: " INTPTR_FORMAT ", size: 0x%x\n", cb, cb->size()); - } + print_trace("free", cb); if (cb->is_nmethod() && ((nmethod *)cb)->has_dependencies()) { _number_of_nmethods_with_dependencies--; } @@ -260,14 +256,227 @@ } } -void CodeCache::oops_do(OopClosure* f) { +void CodeCache::blobs_do(CodeBlobClosure* f) { assert_locked_or_safepoint(CodeCache_lock); FOR_ALL_ALIVE_BLOBS(cb) { - cb->oops_do(f); + f->do_code_blob(cb); + +#ifdef ASSERT + if (cb->is_nmethod()) + ((nmethod*)cb)->verify_scavenge_root_oops(); +#endif //ASSERT } } +// Walk the list of methods which might contain non-perm oops. +void CodeCache::scavenge_root_nmethods_do(CodeBlobClosure* f) { + assert_locked_or_safepoint(CodeCache_lock); + debug_only(mark_scavenge_root_nmethods()); + + for (nmethod* cur = scavenge_root_nmethods(); cur != NULL; cur = cur->scavenge_root_link()) { + debug_only(cur->clear_scavenge_root_marked()); + assert(cur->scavenge_root_not_marked(), ""); + assert(cur->on_scavenge_root_list(), "else shouldn't be on this list"); + + bool is_live = (!cur->is_zombie() && !cur->is_unloaded()); +#ifndef PRODUCT + if (TraceScavenge) { + cur->print_on(tty, is_live ? "scavenge root" : "dead scavenge root"); tty->cr(); + } +#endif //PRODUCT + if (is_live) + // Perform cur->oops_do(f), maybe just once per nmethod. + f->do_code_blob(cur); + } + + // Check for stray marks. + debug_only(verify_perm_nmethods(NULL)); +} + +void CodeCache::add_scavenge_root_nmethod(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + nm->set_on_scavenge_root_list(); + nm->set_scavenge_root_link(_scavenge_root_nmethods); + set_scavenge_root_nmethods(nm); + print_trace("add_scavenge_root", nm); +} + +void CodeCache::drop_scavenge_root_nmethod(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + print_trace("drop_scavenge_root", nm); + nmethod* last = NULL; + nmethod* cur = scavenge_root_nmethods(); + while (cur != NULL) { + nmethod* next = cur->scavenge_root_link(); + if (cur == nm) { + if (last != NULL) + last->set_scavenge_root_link(next); + else set_scavenge_root_nmethods(next); + nm->set_scavenge_root_link(NULL); + nm->clear_on_scavenge_root_list(); + return; + } + last = cur; + cur = next; + } + assert(false, "should have been on list"); +} + +void CodeCache::prune_scavenge_root_nmethods() { + assert_locked_or_safepoint(CodeCache_lock); + debug_only(mark_scavenge_root_nmethods()); + + nmethod* last = NULL; + nmethod* cur = scavenge_root_nmethods(); + while (cur != NULL) { + nmethod* next = cur->scavenge_root_link(); + debug_only(cur->clear_scavenge_root_marked()); + assert(cur->scavenge_root_not_marked(), ""); + assert(cur->on_scavenge_root_list(), "else shouldn't be on this list"); + + if (!cur->is_zombie() && !cur->is_unloaded() + && cur->detect_scavenge_root_oops()) { + // Keep it. Advance 'last' to prevent deletion. + last = cur; + } else { + // Prune it from the list, so we don't have to look at it any more. + print_trace("prune_scavenge_root", cur); + cur->set_scavenge_root_link(NULL); + cur->clear_on_scavenge_root_list(); + if (last != NULL) + last->set_scavenge_root_link(next); + else set_scavenge_root_nmethods(next); + } + cur = next; + } + + // Check for stray marks. + debug_only(verify_perm_nmethods(NULL)); +} + +#ifndef PRODUCT +void CodeCache::asserted_non_scavengable_nmethods_do(CodeBlobClosure* f) { + // While we are here, verify the integrity of the list. + mark_scavenge_root_nmethods(); + for (nmethod* cur = scavenge_root_nmethods(); cur != NULL; cur = cur->scavenge_root_link()) { + assert(cur->on_scavenge_root_list(), "else shouldn't be on this list"); + cur->clear_scavenge_root_marked(); + } + verify_perm_nmethods(f); +} + +// Temporarily mark nmethods that are claimed to be on the non-perm list. +void CodeCache::mark_scavenge_root_nmethods() { + FOR_ALL_ALIVE_BLOBS(cb) { + if (cb->is_nmethod()) { + nmethod *nm = (nmethod*)cb; + assert(nm->scavenge_root_not_marked(), "clean state"); + if (nm->on_scavenge_root_list()) + nm->set_scavenge_root_marked(); + } + } +} + +// If the closure is given, run it on the unlisted nmethods. +// Also make sure that the effects of mark_scavenge_root_nmethods is gone. +void CodeCache::verify_perm_nmethods(CodeBlobClosure* f_or_null) { + FOR_ALL_ALIVE_BLOBS(cb) { + bool call_f = (f_or_null != NULL); + if (cb->is_nmethod()) { + nmethod *nm = (nmethod*)cb; + assert(nm->scavenge_root_not_marked(), "must be already processed"); + if (nm->on_scavenge_root_list()) + call_f = false; // don't show this one to the client + nm->verify_scavenge_root_oops(); + } else { + call_f = false; // not an nmethod + } + if (call_f) f_or_null->do_code_blob(cb); + } +} +#endif //PRODUCT + + +nmethod* CodeCache::find_and_remove_saved_code(methodOop m) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + nmethod* saved = _saved_nmethods; + nmethod* prev = NULL; + while (saved != NULL) { + if (saved->is_in_use() && saved->method() == m) { + if (prev != NULL) { + prev->set_saved_nmethod_link(saved->saved_nmethod_link()); + } else { + _saved_nmethods = saved->saved_nmethod_link(); + } + assert(saved->is_speculatively_disconnected(), "shouldn't call for other nmethods"); + saved->set_speculatively_disconnected(false); + saved->set_saved_nmethod_link(NULL); + if (PrintMethodFlushing) { + saved->print_on(tty, " ### nmethod is reconnected"); + } + if (LogCompilation && (xtty != NULL)) { + ttyLocker ttyl; + xtty->begin_elem("nmethod_reconnected compile_id='%3d'", saved->compile_id()); + xtty->method(methodOop(m)); + xtty->stamp(); + xtty->end_elem(); + } + return saved; + } + prev = saved; + saved = saved->saved_nmethod_link(); + } + return NULL; +} + +void CodeCache::remove_saved_code(nmethod* nm) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + assert(nm->is_speculatively_disconnected(), "shouldn't call for other nmethods"); + nmethod* saved = _saved_nmethods; + nmethod* prev = NULL; + while (saved != NULL) { + if (saved == nm) { + if (prev != NULL) { + prev->set_saved_nmethod_link(saved->saved_nmethod_link()); + } else { + _saved_nmethods = saved->saved_nmethod_link(); + } + if (LogCompilation && (xtty != NULL)) { + ttyLocker ttyl; + xtty->begin_elem("nmethod_removed compile_id='%3d'", nm->compile_id()); + xtty->stamp(); + xtty->end_elem(); + } + return; + } + prev = saved; + saved = saved->saved_nmethod_link(); + } + ShouldNotReachHere(); +} + +void CodeCache::speculatively_disconnect(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + assert(nm->is_in_use() && !nm->is_speculatively_disconnected(), "should only disconnect live nmethods"); + nm->set_saved_nmethod_link(_saved_nmethods); + _saved_nmethods = nm; + if (PrintMethodFlushing) { + nm->print_on(tty, " ### nmethod is speculatively disconnected"); + } + if (LogCompilation && (xtty != NULL)) { + ttyLocker ttyl; + xtty->begin_elem("nmethod_disconnected compile_id='%3d'", nm->compile_id()); + xtty->method(methodOop(nm->method())); + xtty->stamp(); + xtty->end_elem(); + } + nm->method()->clear_code(); + nm->set_speculatively_disconnected(true); +} + + void CodeCache::gc_prologue() { + assert(!nmethod::oops_do_marking_is_active(), "oops_do_marking_epilogue must be called"); } @@ -285,6 +494,8 @@ cb->fix_oop_relocations(); } set_needs_cache_clean(false); + prune_scavenge_root_nmethods(); + assert(!nmethod::oops_do_marking_is_active(), "oops_do_marking_prologue must be called"); } @@ -508,6 +719,14 @@ } } +void CodeCache::print_trace(const char* event, CodeBlob* cb, int size) { + 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); + } +} + void CodeCache::print_internals() { int nmethodCount = 0; int runtimeStubCount = 0; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/codeCache.hpp --- a/src/share/vm/code/codeCache.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/codeCache.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -45,8 +45,14 @@ static int _number_of_blobs; static int _number_of_nmethods_with_dependencies; static bool _needs_cache_clean; + static nmethod* _scavenge_root_nmethods; // linked via nm->scavenge_root_link() + static nmethod* _saved_nmethods; // linked via nm->saved_nmethod_look() static void verify_if_often() PRODUCT_RETURN; + + static void mark_scavenge_root_nmethods() PRODUCT_RETURN; + static void verify_perm_nmethods(CodeBlobClosure* f_or_null) PRODUCT_RETURN; + public: // Initialization @@ -61,6 +67,7 @@ static void flush(); // flushes all CodeBlobs static bool contains(void *p); // returns whether p is included static void blobs_do(void f(CodeBlob* cb)); // iterates over all CodeBlobs + static void blobs_do(CodeBlobClosure* f); // iterates over all CodeBlobs static void nmethods_do(void f(nmethod* nm)); // iterates over all nmethods // Lookup @@ -106,12 +113,24 @@ static void do_unloading(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool unloading_occurred); - static void oops_do(OopClosure* f); + static void oops_do(OopClosure* f) { + CodeBlobToOopClosure oopc(f, /*do_marking=*/ false); + blobs_do(&oopc); + } + static void asserted_non_scavengable_nmethods_do(CodeBlobClosure* f = NULL) PRODUCT_RETURN; + static void scavenge_root_nmethods_do(CodeBlobClosure* f); + + static nmethod* scavenge_root_nmethods() { return _scavenge_root_nmethods; } + static void set_scavenge_root_nmethods(nmethod* nm) { _scavenge_root_nmethods = nm; } + static void add_scavenge_root_nmethod(nmethod* nm); + static void drop_scavenge_root_nmethod(nmethod* nm); + static void prune_scavenge_root_nmethods(); // Printing/debugging static void print() PRODUCT_RETURN; // prints summary static void print_internals(); static void verify(); // verifies the code cache + static void print_trace(const char* event, CodeBlob* cb, int size = 0) PRODUCT_RETURN; // The full limits of the codeCache static address low_bound() { return (address) _heap->low_boundary(); } @@ -123,11 +142,16 @@ static size_t capacity() { return _heap->capacity(); } static size_t max_capacity() { return _heap->max_capacity(); } static size_t unallocated_capacity() { return _heap->unallocated_capacity(); } + static bool needs_flushing() { return unallocated_capacity() < CodeCacheFlushingMinimumFreeSpace; } static bool needs_cache_clean() { return _needs_cache_clean; } static void set_needs_cache_clean(bool v) { _needs_cache_clean = v; } static void clear_inline_caches(); // clear all inline caches + static nmethod* find_and_remove_saved_code(methodOop m); + static void remove_saved_code(nmethod* nm); + static void speculatively_disconnect(nmethod* nm); + // Deoptimization static int mark_for_deoptimization(DepChange& changes); #ifdef HOTSWAP diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/debugInfo.hpp --- a/src/share/vm/code/debugInfo.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/debugInfo.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -255,8 +255,7 @@ ScopeValue* read_object_value(); ScopeValue* get_cached_object(); // BCI encoding is mostly unsigned, but -1 is a distinguished value - // Decoding based on encoding: bci = InvocationEntryBci + read_int()/2; reexecute = read_int()%2 == 1 ? true : false; - int read_bci_and_reexecute(bool& reexecute) { int i = read_int(); reexecute = (i & 1) ? true : false; return (i >> 1) + InvocationEntryBci; } + int read_bci() { return read_int() + InvocationEntryBci; } }; // DebugInfoWriteStream specializes CompressedWriteStream for @@ -269,6 +268,5 @@ public: DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size); void write_handle(jobject h); - //Encoding bci and reexecute into one word as (bci - InvocationEntryBci)*2 + reexecute - void write_bci_and_reexecute(int bci, bool reexecute) { write_int(((bci - InvocationEntryBci) << 1) + (reexecute ? 1 : 0)); } + void write_bci(int bci) { write_int(bci - InvocationEntryBci); } }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/debugInfoRec.cpp --- a/src/share/vm/code/debugInfoRec.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/debugInfoRec.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 @@ -281,6 +281,8 @@ ciMethod* method, int bci, bool reexecute, + bool is_method_handle_invoke, + bool return_oop, DebugToken* locals, DebugToken* expressions, DebugToken* monitors) { @@ -292,13 +294,18 @@ int stream_offset = stream()->position(); last_pd->set_scope_decode_offset(stream_offset); + // Record flags into pcDesc. + last_pd->set_should_reexecute(reexecute); + last_pd->set_is_method_handle_invoke(is_method_handle_invoke); + last_pd->set_return_oop(return_oop); + // serialize sender stream offest stream()->write_int(sender_stream_offset); // serialize scope - jobject method_enc = (method == NULL)? NULL: method->encoding(); + jobject method_enc = (method == NULL)? NULL: method->constant_encoding(); stream()->write_int(oop_recorder()->find_index(method_enc)); - stream()->write_bci_and_reexecute(bci, reexecute); + stream()->write_bci(bci); assert(method == NULL || (method->is_native() && bci == 0) || (!method->is_native() && 0 <= bci && bci < method->code_size()) || @@ -353,8 +360,7 @@ // search forward until it finds last. // In addition, it does not matter if the last PcDesc // is for a safepoint or not. - if (_prev_safepoint_pc < prev->pc_offset() && - prev->scope_decode_offset() == last->scope_decode_offset()) { + if (_prev_safepoint_pc < prev->pc_offset() && prev->is_same_info(last)) { assert(prev == last-1, "sane"); prev->set_pc_offset(pc_offset); _pcs_length -= 1; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/debugInfoRec.hpp --- a/src/share/vm/code/debugInfoRec.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/debugInfoRec.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 @@ -88,6 +88,8 @@ ciMethod* method, int bci, bool reexecute, + bool is_method_handle_invoke = false, + bool return_oop = false, DebugToken* locals = NULL, DebugToken* expressions = NULL, DebugToken* monitors = NULL); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/dependencies.cpp --- a/src/share/vm/code/dependencies.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/dependencies.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -302,7 +302,7 @@ bytes.write_byte(code_byte); for (int j = 0; j < stride; j++) { if (j == skipj) continue; - bytes.write_int(_oop_recorder->find_index(deps->at(i+j)->encoding())); + bytes.write_int(_oop_recorder->find_index(deps->at(i+j)->constant_encoding())); } } } @@ -843,13 +843,15 @@ if (occasional_print || final_stats) { // Every now and then dump a little info about dependency searching. if (xtty != NULL) { - xtty->elem("deps_find_witness calls='%d' steps='%d' recursions='%d' singles='%d'", + ttyLocker ttyl; + xtty->elem("deps_find_witness calls='%d' steps='%d' recursions='%d' singles='%d'", deps_find_witness_calls, deps_find_witness_steps, deps_find_witness_recursions, deps_find_witness_singles); } if (final_stats || (TraceDependencies && WizardMode)) { + ttyLocker ttyl; tty->print_cr("Dependency check (find_witness) " "calls=%d, steps=%d (avg=%.1f), recursions=%d, singles=%d", deps_find_witness_calls, @@ -1528,19 +1530,23 @@ int nsup = 0, nint = 0; for (ContextStream str(*this); str.next(); ) { klassOop k = str.klass(); - switch (str._change_type) { + switch (str.change_type()) { case Change_new_type: tty->print_cr(" dependee = %s", instanceKlass::cast(k)->external_name()); break; case Change_new_sub: - if (!WizardMode) - ++nsup; - else tty->print_cr(" context super = %s", instanceKlass::cast(k)->external_name()); + if (!WizardMode) { + ++nsup; + } else { + tty->print_cr(" context super = %s", instanceKlass::cast(k)->external_name()); + } break; case Change_new_impl: - if (!WizardMode) - ++nint; - else tty->print_cr(" context interface = %s", instanceKlass::cast(k)->external_name()); + if (!WizardMode) { + ++nint; + } else { + tty->print_cr(" context interface = %s", instanceKlass::cast(k)->external_name()); + } break; } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/dependencies.hpp --- a/src/share/vm/code/dependencies.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/dependencies.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -470,7 +470,7 @@ // super types can be context types for a relevant dependency, which the // new type could invalidate. class DepChange : public StackObj { - private: + public: enum ChangeType { NO_CHANGE = 0, // an uninvolved klass Change_new_type, // a newly loaded type @@ -480,6 +480,7 @@ Start_Klass = CHANGE_LIMIT // internal indicator for ContextStream }; + private: // each change set is rooted in exactly one new type (at present): KlassHandle _new_type; @@ -510,15 +511,15 @@ // } class ContextStream : public StackObj { private: - DepChange& _changes; + DepChange& _changes; friend class DepChange; // iteration variables: - ChangeType _change_type; - klassOop _klass; - objArrayOop _ti_base; // i.e., transitive_interfaces - int _ti_index; - int _ti_limit; + ChangeType _change_type; + klassOop _klass; + objArrayOop _ti_base; // i.e., transitive_interfaces + int _ti_index; + int _ti_limit; // start at the beginning: void start() { @@ -530,11 +531,11 @@ _ti_limit = 0; } + public: ContextStream(DepChange& changes) : _changes(changes) { start(); } - public: ContextStream(DepChange& changes, No_Safepoint_Verifier& nsv) : _changes(changes) // the nsv argument makes it safe to hold oops like _klass @@ -542,6 +543,7 @@ bool next(); + ChangeType change_type() { return _change_type; } klassOop klass() { return _klass; } }; friend class DepChange::ContextStream; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/jvmticmlr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/code/jvmticmlr.h Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright 2010 Sun Microsystems, 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * This header file defines the data structures sent by the VM + * through the JVMTI CompiledMethodLoad callback function via the + * "void * compile_info" parameter. The memory pointed to by the + * compile_info parameter may not be referenced after returning from + * the CompiledMethodLoad callback. These are VM implementation + * specific data structures that may evolve in future releases. A + * JVMTI agent should interpret a non-NULL compile_info as a pointer + * to a region of memory containing a list of records. In a typical + * usage scenario, a JVMTI agent would cast each record to a + * jvmtiCompiledMethodLoadRecordHeader, a struct that represents + * arbitrary information. This struct contains a kind field to indicate + * the kind of information being passed, and a pointer to the next + * record. If the kind field indicates inlining information, then the + * agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord. + * This record contains an array of PCStackInfo structs, which indicate + * for every pc address what are the methods on the invocation stack. + * The "methods" and "bcis" fields in each PCStackInfo struct specify a + * 1-1 mapping between these inlined methods and their bytecode indices. + * This can be used to derive the proper source lines of the inlined + * methods. + */ + +#ifndef _JVMTI_CMLR_H_ +#define _JVMTI_CMLR_H_ + +enum { + JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001, + JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000, + + JVMTI_CMLR_MAJOR_VERSION = 0x00000001, + JVMTI_CMLR_MINOR_VERSION = 0x00000000 + + /* + * This comment is for the "JDK import from HotSpot" sanity check: + * version: 1.0.0 + */ +}; + +typedef enum { + JVMTI_CMLR_DUMMY = 1, + JVMTI_CMLR_INLINE_INFO = 2 +} jvmtiCMLRKind; + +/* + * Record that represents arbitrary information passed through JVMTI + * CompiledMethodLoadEvent void pointer. + */ +typedef struct _jvmtiCompiledMethodLoadRecordHeader { + jvmtiCMLRKind kind; /* id for the kind of info passed in the record */ + jint majorinfoversion; /* major and minor info version values. Init'ed */ + jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */ + + struct _jvmtiCompiledMethodLoadRecordHeader* next; +} jvmtiCompiledMethodLoadRecordHeader; + +/* + * Record that gives information about the methods on the compile-time + * stack at a specific pc address of a compiled method. Each element in + * the methods array maps to same element in the bcis array. + */ +typedef struct _PCStackInfo { + void* pc; /* the pc address for this compiled method */ + jint numstackframes; /* number of methods on the stack */ + jmethodID* methods; /* array of numstackframes method ids */ + jint* bcis; /* array of numstackframes bytecode indices */ +} PCStackInfo; + +/* + * Record that contains inlining information for each pc address of + * an nmethod. + */ +typedef struct _jvmtiCompiledMethodLoadInlineRecord { + jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ + jint numpcs; /* number of pc descriptors in this nmethod */ + PCStackInfo* pcinfo; /* array of numpcs pc descriptors */ +} jvmtiCompiledMethodLoadInlineRecord; + +/* + * Dummy record used to test that we can pass records with different + * information through the void pointer provided that they can be cast + * to a jvmtiCompiledMethodLoadRecordHeader. + */ + +typedef struct _jvmtiCompiledMethodLoadDummyRecord { + jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ + char message[50]; +} jvmtiCompiledMethodLoadDummyRecord; + +#endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/nmethod.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -56,13 +56,13 @@ #endif bool nmethod::is_compiled_by_c1() const { + if (compiler() == NULL || method() == NULL) return false; // can happen during debug printing if (is_native_method()) return false; - assert(compiler() != NULL, "must be"); return compiler()->is_c1(); } bool nmethod::is_compiled_by_c2() const { + if (compiler() == NULL || method() == NULL) return false; // can happen during debug printing if (is_native_method()) return false; - assert(compiler() != NULL, "must be"); return compiler()->is_c2(); } @@ -414,9 +414,8 @@ } const char* nmethod::compile_kind() const { - if (method() == NULL) return "unloaded"; - if (is_native_method()) return "c2n"; if (is_osr_method()) return "osr"; + if (method() != NULL && is_native_method()) return "c2n"; return NULL; } @@ -581,15 +580,20 @@ debug_only(No_Safepoint_Verifier nsv;) assert_locked_or_safepoint(CodeCache_lock); - NOT_PRODUCT(_has_debug_info = false; ) + NOT_PRODUCT(_has_debug_info = false); + _oops_do_mark_link = NULL; _method = method; _entry_bci = InvocationEntryBci; - _link = NULL; + _osr_link = NULL; + _scavenge_root_link = NULL; + _scavenge_root_state = 0; + _saved_nmethod_link = NULL; _compiler = NULL; // We have no exception handler or deopt handler make the // values something that will never match a pc like the nmethod vtable entry _exception_offset = 0; _deoptimize_offset = 0; + _deoptimize_mh_offset = 0; _orig_pc_offset = 0; #ifdef HAVE_DTRACE_H _trap_offset = 0; @@ -618,7 +622,7 @@ _stack_traversal_mark = 0; code_buffer->copy_oops_to(this); - debug_only(check_store();) + debug_only(verify_scavenge_root_oops()); CodeCache::commit(this); VTune::create_nmethod(this); } @@ -668,15 +672,19 @@ debug_only(No_Safepoint_Verifier nsv;) assert_locked_or_safepoint(CodeCache_lock); - NOT_PRODUCT(_has_debug_info = false; ) + NOT_PRODUCT(_has_debug_info = false); + _oops_do_mark_link = NULL; _method = method; _entry_bci = InvocationEntryBci; - _link = NULL; + _osr_link = NULL; + _scavenge_root_link = NULL; + _scavenge_root_state = 0; _compiler = NULL; // We have no exception handler or deopt handler make the // values something that will never match a pc like the nmethod vtable entry _exception_offset = 0; _deoptimize_offset = 0; + _deoptimize_mh_offset = 0; _trap_offset = offsets->value(CodeOffsets::Dtrace_trap); _orig_pc_offset = 0; _stub_offset = data_offset(); @@ -703,7 +711,7 @@ _stack_traversal_mark = 0; code_buffer->copy_oops_to(this); - debug_only(check_store();) + debug_only(verify_scavenge_root_oops()); CodeCache::commit(this); VTune::create_nmethod(this); } @@ -770,12 +778,15 @@ debug_only(No_Safepoint_Verifier nsv;) assert_locked_or_safepoint(CodeCache_lock); - NOT_PRODUCT(_has_debug_info = false; ) + NOT_PRODUCT(_has_debug_info = false); + _oops_do_mark_link = NULL; _method = method; _compile_id = compile_id; _comp_level = comp_level; _entry_bci = entry_bci; - _link = NULL; + _osr_link = NULL; + _scavenge_root_link = NULL; + _scavenge_root_state = 0; _compiler = compiler; _orig_pc_offset = orig_pc_offset; #ifdef HAVE_DTRACE_H @@ -786,6 +797,7 @@ // Exception handler and deopt handler are in the stub section _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); _deoptimize_offset = _stub_offset + offsets->value(CodeOffsets::Deopt); + _deoptimize_mh_offset = _stub_offset + offsets->value(CodeOffsets::DeoptMH); _consts_offset = instructions_offset() + code_buffer->total_offset_of(code_buffer->consts()->start()); _scopes_data_offset = data_offset(); _scopes_pcs_offset = _scopes_data_offset + round_to(debug_info->data_size (), oopSize); @@ -813,7 +825,10 @@ code_buffer->copy_oops_to(this); debug_info->copy_to(this); dependencies->copy_to(this); - debug_only(check_store();) + if (ScavengeRootsInCode && detect_scavenge_root_oops()) { + CodeCache::add_scavenge_root_nmethod(this); + } + debug_only(verify_scavenge_root_oops()); CodeCache::commit(this); @@ -902,23 +917,30 @@ if (st != NULL) { ttyLocker ttyl; // Print a little tag line that looks like +PrintCompilation output: - st->print("%3d%c %s", + int tlen = (int) strlen(title); + bool do_nl = false; + if (tlen > 0 && title[tlen-1] == '\n') { tlen--; do_nl = true; } + st->print("%3d%c %.*s", compile_id(), is_osr_method() ? '%' : method() != NULL && is_native_method() ? 'n' : ' ', - title); + tlen, title); #ifdef TIERED st->print(" (%d) ", comp_level()); #endif // TIERED if (WizardMode) st->print(" (" INTPTR_FORMAT ")", this); - if (method() != NULL) { - method()->print_short_name(st); + if (Universe::heap()->is_gc_active() && method() != NULL) { + st->print("(method)"); + } else if (method() != NULL) { + method()->print_short_name(st); if (is_osr_method()) st->print(" @ %d", osr_entry_bci()); if (method()->code_size() > 0) st->print(" (%d bytes)", method()->code_size()); } + + if (do_nl) st->cr(); } } @@ -966,7 +988,8 @@ PcDesc* pd = pc_desc_at(pc); guarantee(pd != NULL, "scope must be present"); return new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset()); + pd->obj_decode_offset(), pd->should_reexecute(), + pd->return_oop()); } @@ -1015,7 +1038,7 @@ if( cb != NULL && cb->is_nmethod() ) { nmethod* nm = (nmethod*)cb; // Clean inline caches pointing to both zombie and not_entrant methods - if (!nm->is_in_use()) ic->set_to_clean(); + if (!nm->is_in_use() || (nm->method()->code() != nm)) ic->set_to_clean(); } break; } @@ -1025,7 +1048,7 @@ if( cb != NULL && cb->is_nmethod() ) { nmethod* nm = (nmethod*)cb; // Clean inline caches pointing to both zombie and not_entrant methods - if (!nm->is_in_use()) csc->set_to_clean(); + if (!nm->is_in_use() || (nm->method()->code() != nm)) csc->set_to_clean(); } break; } @@ -1033,6 +1056,7 @@ } } +// This is a private interface with the sweeper. void nmethod::mark_as_seen_on_stack() { assert(is_not_entrant(), "must be a non-entrant method"); set_stack_traversal_mark(NMethodSweeper::traversal_count()); @@ -1077,7 +1101,12 @@ " unloadable], methodOop(" INTPTR_FORMAT "), cause(" INTPTR_FORMAT ")", this, (address)_method, (address)cause); - cause->klass()->print(); + if (!Universe::heap()->is_gc_active()) + cause->klass()->print(); + } + // Unlink the osr method, so we do not look this up again + if (is_osr_method()) { + invalidate_osr_method(); } // If _method is already NULL the methodOop is about to be unloaded, // so we don't have to break the cycle. Note that it is possible to @@ -1089,7 +1118,6 @@ if (_method->code() == this) { _method->clear_code(); // Break a cycle } - inc_decompile_count(); // Last chance to make a mark on the MDO _method = NULL; // Clear the method of this dead nmethod } // Make the class unloaded - i.e., change state and notify sweeper @@ -1102,17 +1130,19 @@ } flags.state = unloaded; + // Log the unloading. + log_state_change(); + // The methodOop is gone at this point assert(_method == NULL, "Tautology"); - set_link(NULL); + set_osr_link(NULL); + //set_scavenge_root_link(NULL); // done by prune_scavenge_root_nmethods NMethodSweeper::notify(this); } void nmethod::invalidate_osr_method() { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); - if (_entry_bci != InvalidOSREntryBci) - inc_decompile_count(); // Remove from list of active nmethods if (method() != NULL) instanceKlass::cast(method()->method_holder())->remove_osr_nmethod(this); @@ -1120,59 +1150,65 @@ _entry_bci = InvalidOSREntryBci; } -void nmethod::log_state_change(int state) const { +void nmethod::log_state_change() const { if (LogCompilation) { if (xtty != NULL) { ttyLocker ttyl; // keep the following output all in one block - xtty->begin_elem("make_not_entrant %sthread='" UINTX_FORMAT "'", - (state == zombie ? "zombie='1' " : ""), - os::current_thread_id()); + if (flags.state == unloaded) { + xtty->begin_elem("make_unloaded thread='" UINTX_FORMAT "'", + os::current_thread_id()); + } else { + xtty->begin_elem("make_not_entrant thread='" UINTX_FORMAT "'%s", + os::current_thread_id(), + (flags.state == zombie ? " zombie='1'" : "")); + } log_identity(xtty); xtty->stamp(); xtty->end_elem(); } } - if (PrintCompilation) { - print_on(tty, state == zombie ? "made zombie " : "made not entrant "); + if (PrintCompilation && flags.state != unloaded) { + print_on(tty, flags.state == zombie ? "made zombie " : "made not entrant "); tty->cr(); } } // Common functionality for both make_not_entrant and make_zombie -void nmethod::make_not_entrant_or_zombie(int state) { +bool nmethod::make_not_entrant_or_zombie(unsigned int state) { assert(state == zombie || state == not_entrant, "must be zombie or not_entrant"); - // Code for an on-stack-replacement nmethod is removed when a class gets unloaded. - // They never become zombie/non-entrant, so the nmethod sweeper will never remove - // them. Instead the entry_bci is set to InvalidOSREntryBci, so the osr nmethod - // will never be used anymore. That the nmethods only gets removed when class unloading - // happens, make life much simpler, since the nmethods are not just going to disappear - // out of the blue. - if (is_osr_only_method()) { - if (osr_entry_bci() != InvalidOSREntryBci) { - // only log this once - log_state_change(state); - } - invalidate_osr_method(); - return; - } - - // If the method is already zombie or set to the state we want, nothing to do - if (is_zombie() || (state == not_entrant && is_not_entrant())) { - return; - } - - log_state_change(state); + bool was_alive = false; // Make sure the nmethod is not flushed in case of a safepoint in code below. nmethodLocker nml(this); { + // If the method is already zombie there is nothing to do + if (is_zombie()) { + return false; + } + + // invalidate osr nmethod before acquiring the patching lock since + // they both acquire leaf locks and we don't want a deadlock. + // This logic is equivalent to the logic below for patching the + // verified entry point of regular methods. + if (is_osr_method()) { + // this effectively makes the osr nmethod not entrant + invalidate_osr_method(); + } + // Enter critical section. Does not block for safepoint. MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag); + + if (flags.state == state) { + // another thread already performed this transition so nothing + // to do, but return false to indicate this. + return false; + } + // The caller can be calling the method statically or through an inline // cache call. - if (!is_not_entrant()) { + if (!is_osr_method() && !is_not_entrant()) { NativeJump::patch_verified_entry(entry_point(), verified_entry_point(), SharedRuntime::get_handle_wrong_method_stub()); assert (NativeJump::instruction_size == nmethod::_zombie_instruction_size, ""); @@ -1189,8 +1225,14 @@ assert(state == not_entrant, "other cases may need to be handled differently"); } + was_alive = is_in_use(); // Read state under lock + // Change state flags.state = state; + + // Log the transition once + log_state_change(); + } // leave critical region under Patching_lock if (state == not_entrant) { @@ -1211,9 +1253,11 @@ mark_as_seen_on_stack(); } - // It's a true state change, so mark the method as decompiled. - inc_decompile_count(); - + if (was_alive) { + // It's a true state change, so mark the method as decompiled. + // Do it only for transition from alive. + inc_decompile_count(); + } // zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event // and it hasn't already been reported for this nmethod then report it now. @@ -1242,7 +1286,7 @@ // Check whether method got unloaded at a safepoint before this, // if so we can skip the flushing steps below - if (method() == NULL) return; + if (method() == NULL) return true; // Remove nmethod from method. // We need to check if both the _code and _from_compiled_code_entry_point @@ -1256,6 +1300,8 @@ HandleMark hm; method()->clear_code(); } + + return true; } @@ -1277,7 +1323,8 @@ // completely deallocate this method EventMark m("flushing nmethod " INTPTR_FORMAT " %s", this, ""); if (PrintMethodFlushing) { - tty->print_cr("*flushing nmethod " INTPTR_FORMAT ". Live blobs: %d", this, CodeCache::nof_blobs()); + tty->print_cr("*flushing nmethod %3d/" INTPTR_FORMAT ". Live blobs:" UINT32_FORMAT "/Free CodeCache:" SIZE_FORMAT "Kb", + _compile_id, this, CodeCache::nof_blobs(), CodeCache::unallocated_capacity()/1024); } // We need to deallocate any ExceptionCache data. @@ -1291,6 +1338,14 @@ ec = next; } + if (on_scavenge_root_list()) { + CodeCache::drop_scavenge_root_nmethod(this); + } + + if (is_speculatively_disconnected()) { + CodeCache::remove_saved_code(this); + } + ((CodeBlob*)(this))->flush(); CodeCache::free(this); @@ -1350,7 +1405,10 @@ return false; } } - assert(unloading_occurred, "Inconsistency in unloading"); + // If ScavengeRootsInCode is true, an nmethod might be unloaded + // simply because one of its constant oops has gone dead. + // No actual classes need to be unloaded in order for this to occur. + assert(unloading_occurred || ScavengeRootsInCode, "Inconsistency in unloading"); make_unloaded(is_alive, obj); return true; } @@ -1520,7 +1578,17 @@ #endif // !PRODUCT } -void nmethod::oops_do(OopClosure* f) { +// This method is called twice during GC -- once while +// tracing the "active" nmethods on thread stacks during +// the (strong) marking phase, and then again when walking +// the code cache contents during the weak roots processing +// phase. The two uses are distinguished by means of the +// 'do_strong_roots_only' flag, which is true in the first +// case. We want to walk the weak roots in the nmethod +// only in the second case. The weak roots in the nmethod +// are the oops in the ExceptionCache and the InlineCache +// oops. +void nmethod::oops_do(OopClosure* f, bool do_strong_roots_only) { // make sure the oops ready to receive visitors assert(!is_zombie() && !is_unloaded(), "should not call follow on zombie or unloaded nmethod"); @@ -1538,19 +1606,25 @@ // Compiled code f->do_oop((oop*) &_method); - ExceptionCache* ec = exception_cache(); - while(ec != NULL) { - f->do_oop((oop*)ec->exception_type_addr()); - ec = ec->next(); - } + if (!do_strong_roots_only) { + // weak roots processing phase -- update ExceptionCache oops + ExceptionCache* ec = exception_cache(); + while(ec != NULL) { + f->do_oop((oop*)ec->exception_type_addr()); + ec = ec->next(); + } + } // Else strong roots phase -- skip oops in ExceptionCache RelocIterator iter(this, low_boundary); + while (iter.next()) { if (iter.type() == relocInfo::oop_type ) { oop_Relocation* r = iter.oop_reloc(); // In this loop, we must only follow those oops directly embedded in // the code. Other oops (oop_index>0) are seen as part of scopes_oops. - assert(1 == (r->oop_is_immediate()) + (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()), "oop must be found in exactly one place"); + assert(1 == (r->oop_is_immediate()) + + (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()), + "oop must be found in exactly one place"); if (r->oop_is_immediate() && r->oop_value() != NULL) { f->do_oop(r->oop_addr()); } @@ -1558,21 +1632,117 @@ } // Scopes + // This includes oop constants not inlined in the code stream. for (oop* p = oops_begin(); p < oops_end(); p++) { if (*p == Universe::non_oop_word()) continue; // skip non-oops f->do_oop(p); } } +#define NMETHOD_SENTINEL ((nmethod*)badAddress) + +nmethod* volatile nmethod::_oops_do_mark_nmethods; + +// An nmethod is "marked" if its _mark_link is set non-null. +// Even if it is the end of the linked list, it will have a non-null link value, +// as long as it is on the list. +// This code must be MP safe, because it is used from parallel GC passes. +bool nmethod::test_set_oops_do_mark() { + assert(nmethod::oops_do_marking_is_active(), "oops_do_marking_prologue must be called"); + nmethod* observed_mark_link = _oops_do_mark_link; + if (observed_mark_link == NULL) { + // Claim this nmethod for this thread to mark. + observed_mark_link = (nmethod*) + Atomic::cmpxchg_ptr(NMETHOD_SENTINEL, &_oops_do_mark_link, NULL); + if (observed_mark_link == NULL) { + + // Atomically append this nmethod (now claimed) to the head of the list: + nmethod* observed_mark_nmethods = _oops_do_mark_nmethods; + for (;;) { + nmethod* required_mark_nmethods = observed_mark_nmethods; + _oops_do_mark_link = required_mark_nmethods; + observed_mark_nmethods = (nmethod*) + Atomic::cmpxchg_ptr(this, &_oops_do_mark_nmethods, required_mark_nmethods); + if (observed_mark_nmethods == required_mark_nmethods) + break; + } + // Mark was clear when we first saw this guy. + NOT_PRODUCT(if (TraceScavenge) print_on(tty, "oops_do, mark\n")); + return false; + } + } + // On fall through, another racing thread marked this nmethod before we did. + return true; +} + +void nmethod::oops_do_marking_prologue() { + NOT_PRODUCT(if (TraceScavenge) tty->print_cr("[oops_do_marking_prologue")); + assert(_oops_do_mark_nmethods == NULL, "must not call oops_do_marking_prologue twice in a row"); + // We use cmpxchg_ptr instead of regular assignment here because the user + // may fork a bunch of threads, and we need them all to see the same state. + void* observed = Atomic::cmpxchg_ptr(NMETHOD_SENTINEL, &_oops_do_mark_nmethods, NULL); + guarantee(observed == NULL, "no races in this sequential code"); +} + +void nmethod::oops_do_marking_epilogue() { + assert(_oops_do_mark_nmethods != NULL, "must not call oops_do_marking_epilogue twice in a row"); + nmethod* cur = _oops_do_mark_nmethods; + while (cur != NMETHOD_SENTINEL) { + assert(cur != NULL, "not NULL-terminated"); + nmethod* next = cur->_oops_do_mark_link; + cur->_oops_do_mark_link = NULL; + NOT_PRODUCT(if (TraceScavenge) cur->print_on(tty, "oops_do, unmark\n")); + cur = next; + } + void* required = _oops_do_mark_nmethods; + void* observed = Atomic::cmpxchg_ptr(NULL, &_oops_do_mark_nmethods, required); + guarantee(observed == required, "no races in this sequential code"); + NOT_PRODUCT(if (TraceScavenge) tty->print_cr("oops_do_marking_epilogue]")); +} + +class DetectScavengeRoot: public OopClosure { + bool _detected_scavenge_root; +public: + DetectScavengeRoot() : _detected_scavenge_root(false) + { NOT_PRODUCT(_print_nm = NULL); } + bool detected_scavenge_root() { return _detected_scavenge_root; } + virtual void do_oop(oop* p) { + if ((*p) != NULL && (*p)->is_scavengable()) { + NOT_PRODUCT(maybe_print(p)); + _detected_scavenge_root = true; + } + } + virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } + +#ifndef PRODUCT + nmethod* _print_nm; + void maybe_print(oop* p) { + if (_print_nm == NULL) return; + if (!_detected_scavenge_root) _print_nm->print_on(tty, "new scavenge root"); + tty->print_cr(""PTR_FORMAT"[offset=%d] detected non-perm oop "PTR_FORMAT" (found at "PTR_FORMAT")", + _print_nm, (int)((intptr_t)p - (intptr_t)_print_nm), + (intptr_t)(*p), (intptr_t)p); + (*p)->print(); + } +#endif //PRODUCT +}; + +bool nmethod::detect_scavenge_root_oops() { + DetectScavengeRoot detect_scavenge_root; + NOT_PRODUCT(if (TraceScavenge) detect_scavenge_root._print_nm = this); + oops_do(&detect_scavenge_root); + return detect_scavenge_root.detected_scavenge_root(); +} + // Method that knows how to preserve outgoing arguments at call. This method must be // called with a frame corresponding to a Java invoke void nmethod::preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { if (!method()->is_native()) { SimpleScopeDesc ssd(this, fr.pc()); Bytecode_invoke* call = Bytecode_invoke_at(ssd.method(), ssd.bci()); - bool is_static = call->is_invokestatic(); + bool has_receiver = call->has_receiver(); symbolOop signature = call->signature(); - fr.oops_compiled_arguments_do(signature, is_static, reg_map, f); + fr.oops_compiled_arguments_do(signature, has_receiver, reg_map, f); } } @@ -1609,6 +1779,14 @@ "must end with a sentinel"); #endif //ASSERT + // Search for MethodHandle invokes and tag the nmethod. + for (int i = 0; i < count; i++) { + if (pcs[i].is_method_handle_invoke()) { + set_has_method_handle_invokes(true); + break; + } + } + int size = count * sizeof(PcDesc); assert(scopes_pcs_size() >= size, "oob"); memcpy(scopes_pcs_begin(), pcs, size); @@ -1833,7 +2011,10 @@ print_pcs(); } #endif - guarantee(cont_offset != 0, "unhandled implicit exception in compiled code"); + if (cont_offset == 0) { + // Let the normal error handling report the exception + return NULL; + } return instructions_begin() + cont_offset; } @@ -1869,15 +2050,57 @@ guarantee(nm->_lock_count >= 0, "unmatched nmethod lock/unlock"); } -bool nmethod::is_deopt_pc(address pc) { - bool ret = pc == deopt_handler_begin(); - return ret; + +// ----------------------------------------------------------------------------- +// nmethod::get_deopt_original_pc +// +// Return the original PC for the given PC if: +// (a) the given PC belongs to a nmethod and +// (b) it is a deopt PC +address nmethod::get_deopt_original_pc(const frame* fr) { + if (fr->cb() == NULL) return NULL; + + nmethod* nm = fr->cb()->as_nmethod_or_null(); + if (nm != NULL && nm->is_deopt_pc(fr->pc())) + return nm->get_original_pc(fr); + + return NULL; +} + + +// ----------------------------------------------------------------------------- +// MethodHandle + +bool nmethod::is_method_handle_return(address return_pc) { + if (!has_method_handle_invokes()) return false; + PcDesc* pd = pc_desc_at(return_pc); + if (pd == NULL) + return false; + return pd->is_method_handle_invoke(); } // ----------------------------------------------------------------------------- // Verification +class VerifyOopsClosure: public OopClosure { + nmethod* _nm; + bool _ok; +public: + VerifyOopsClosure(nmethod* nm) : _nm(nm), _ok(true) { } + bool ok() { return _ok; } + virtual void do_oop(oop* p) { + if ((*p) == NULL || (*p)->is_oop()) return; + if (_ok) { + _nm->print_nmethod(true); + _ok = false; + } + tty->print_cr("*** non-oop "PTR_FORMAT" found at "PTR_FORMAT" (offset %d)", + (intptr_t)(*p), (intptr_t)p, (int)((intptr_t)p - (intptr_t)_nm)); + } + virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } +}; + void nmethod::verify() { // Hmm. OSR methods can be deopted but not marked as zombie or not_entrant @@ -1911,6 +2134,11 @@ } } + VerifyOopsClosure voc(this); + oops_do(&voc); + assert(voc.ok(), "embedded oops must be OK"); + verify_scavenge_root_oops(); + verify_scopes(); } @@ -1932,7 +2160,8 @@ PcDesc* pd = pc_desc_at(ic->end_of_call()); assert(pd != NULL, "PcDesc must exist"); for (ScopeDesc* sd = new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset()); + pd->obj_decode_offset(), pd->should_reexecute(), + pd->return_oop()); !sd->is_top(); sd = sd->sender()) { sd->verify(); } @@ -1974,19 +2203,34 @@ // Non-product code #ifndef PRODUCT -void nmethod::check_store() { - // Make sure all oops in the compiled code are tenured +class DebugScavengeRoot: public OopClosure { + nmethod* _nm; + bool _ok; +public: + DebugScavengeRoot(nmethod* nm) : _nm(nm), _ok(true) { } + bool ok() { return _ok; } + virtual void do_oop(oop* p) { + if ((*p) == NULL || !(*p)->is_scavengable()) return; + if (_ok) { + _nm->print_nmethod(true); + _ok = false; + } + tty->print_cr("*** non-perm oop "PTR_FORMAT" found at "PTR_FORMAT" (offset %d)", + (intptr_t)(*p), (intptr_t)p, (int)((intptr_t)p - (intptr_t)_nm)); + (*p)->print(); + } + virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } +}; - RelocIterator iter(this); - while (iter.next()) { - if (iter.type() == relocInfo::oop_type) { - oop_Relocation* reloc = iter.oop_reloc(); - oop obj = reloc->oop_value(); - if (obj != NULL && !obj->is_perm()) { - fatal("must be permanent oop in compiled code"); - } - } +void nmethod::verify_scavenge_root_oops() { + if (!on_scavenge_root_list()) { + // Actually look inside, to verify the claim that it's clean. + DebugScavengeRoot debug_scavenge_root(this); + oops_do(&debug_scavenge_root); + if (!debug_scavenge_root.ok()) + fatal("found an unadvertised bad non-perm oop in the code cache"); } + assert(scavenge_root_not_marked(), ""); } #endif // PRODUCT @@ -2019,6 +2263,7 @@ if (is_not_entrant()) tty->print("not_entrant "); if (is_zombie()) tty->print("zombie "); if (is_unloaded()) tty->print("unloaded "); + if (on_scavenge_root_list()) tty->print("scavenge_root "); tty->print_cr("}:"); } if (size () > 0) tty->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", @@ -2181,11 +2426,115 @@ PcDesc* p = pc_desc_near(begin+1); if (p != NULL && p->real_pc(this) <= end) { return new ScopeDesc(this, p->scope_decode_offset(), - p->obj_decode_offset()); + p->obj_decode_offset(), p->should_reexecute(), + p->return_oop()); } return NULL; } +void nmethod::print_nmethod_labels(outputStream* stream, address block_begin) { + if (block_begin == entry_point()) stream->print_cr("[Entry Point]"); + if (block_begin == verified_entry_point()) stream->print_cr("[Verified Entry Point]"); + if (block_begin == exception_begin()) stream->print_cr("[Exception Handler]"); + if (block_begin == stub_begin()) stream->print_cr("[Stub Code]"); + if (block_begin == deopt_handler_begin()) stream->print_cr("[Deopt Handler Code]"); + if (block_begin == deopt_mh_handler_begin()) stream->print_cr("[Deopt MH Handler Code]"); + if (block_begin == consts_begin()) stream->print_cr("[Constants]"); + if (block_begin == entry_point()) { + methodHandle m = method(); + if (m.not_null()) { + stream->print(" # "); + m->print_value_on(stream); + stream->cr(); + } + if (m.not_null() && !is_osr_method()) { + ResourceMark rm; + int sizeargs = m->size_of_parameters(); + BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sizeargs); + VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, sizeargs); + { + int sig_index = 0; + if (!m->is_static()) + sig_bt[sig_index++] = T_OBJECT; // 'this' + for (SignatureStream ss(m->signature()); !ss.at_return_type(); ss.next()) { + BasicType t = ss.type(); + sig_bt[sig_index++] = t; + if (type2size[t] == 2) { + sig_bt[sig_index++] = T_VOID; + } else { + assert(type2size[t] == 1, "size is 1 or 2"); + } + } + assert(sig_index == sizeargs, ""); + } + const char* spname = "sp"; // make arch-specific? + intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs, false); + int stack_slot_offset = this->frame_size() * wordSize; + int tab1 = 14, tab2 = 24; + int sig_index = 0; + int arg_index = (m->is_static() ? 0 : -1); + bool did_old_sp = false; + for (SignatureStream ss(m->signature()); !ss.at_return_type(); ) { + bool at_this = (arg_index == -1); + bool at_old_sp = false; + BasicType t = (at_this ? T_OBJECT : ss.type()); + assert(t == sig_bt[sig_index], "sigs in sync"); + if (at_this) + stream->print(" # this: "); + else + stream->print(" # parm%d: ", arg_index); + stream->move_to(tab1); + VMReg fst = regs[sig_index].first(); + VMReg snd = regs[sig_index].second(); + if (fst->is_reg()) { + stream->print("%s", fst->name()); + if (snd->is_valid()) { + stream->print(":%s", snd->name()); + } + } else if (fst->is_stack()) { + int offset = fst->reg2stack() * VMRegImpl::stack_slot_size + stack_slot_offset; + if (offset == stack_slot_offset) at_old_sp = true; + stream->print("[%s+0x%x]", spname, offset); + } else { + stream->print("reg%d:%d??", (int)(intptr_t)fst, (int)(intptr_t)snd); + } + stream->print(" "); + stream->move_to(tab2); + stream->print("= "); + if (at_this) { + m->method_holder()->print_value_on(stream); + } else { + bool did_name = false; + if (!at_this && ss.is_object()) { + symbolOop name = ss.as_symbol_or_null(); + if (name != NULL) { + name->print_value_on(stream); + did_name = true; + } + } + if (!did_name) + stream->print("%s", type2name(t)); + } + if (at_old_sp) { + stream->print(" (%s of caller)", spname); + did_old_sp = true; + } + stream->cr(); + sig_index += type2size[t]; + arg_index += 1; + if (!at_this) ss.next(); + } + if (!did_old_sp) { + stream->print(" # "); + stream->move_to(tab1); + stream->print("[%s+0x%x]", spname, stack_slot_offset); + stream->print(" (%s of caller)", spname); + stream->cr(); + } + } + } +} + void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin, u_char* end) { // First, find an oopmap in (begin, end]. // We use the odd half-closed interval so that oop maps and scope descs diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/nmethod.hpp --- a/src/share/vm/code/nmethod.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/nmethod.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -81,18 +81,21 @@ struct nmFlags { friend class VMStructs; - unsigned int version:8; // version number (0 = first version) - unsigned int level:4; // optimization level - unsigned int age:4; // age (in # of sweep steps) + unsigned int version:8; // version number (0 = first version) + unsigned int level:4; // optimization level + unsigned int age:4; // age (in # of sweep steps) - unsigned int state:2; // {alive, zombie, unloaded) + unsigned int state:2; // {alive, zombie, unloaded) - unsigned int isUncommonRecompiled:1; // recompiled because of uncommon trap? - unsigned int isToBeRecompiled:1; // to be recompiled as soon as it matures - unsigned int hasFlushedDependencies:1; // Used for maintenance of dependencies - unsigned int markedForReclamation:1; // Used by NMethodSweeper + unsigned int isUncommonRecompiled:1; // recompiled because of uncommon trap? + unsigned int isToBeRecompiled:1; // to be recompiled as soon as it matures + unsigned int hasFlushedDependencies:1; // Used for maintenance of dependencies + unsigned int markedForReclamation:1; // Used by NMethodSweeper - unsigned int has_unsafe_access:1; // May fault due to unsafe access. + unsigned int has_unsafe_access:1; // May fault due to unsafe access. + unsigned int has_method_handle_invokes:1; // Has this method MethodHandle invokes? + + unsigned int speculatively_disconnected:1; // Marked for potential unload void clear(); }; @@ -125,6 +128,7 @@ class nmethod : public CodeBlob { friend class VMStructs; friend class NMethodSweeper; + friend class CodeCache; // non-perm oops private: // Shared fields for all nmethod's static int _zombie_instruction_size; @@ -132,14 +136,24 @@ methodOop _method; int _entry_bci; // != InvocationEntryBci if this nmethod is an on-stack replacement method - nmethod* _link; // To support simple linked-list chaining of nmethods + // To support simple linked-list chaining of nmethods: + nmethod* _osr_link; // from instanceKlass::osr_nmethods_head + nmethod* _scavenge_root_link; // from CodeCache::scavenge_root_nmethods + nmethod* _saved_nmethod_link; // from CodeCache::speculatively_disconnect + + static nmethod* volatile _oops_do_mark_nmethods; + nmethod* volatile _oops_do_mark_link; AbstractCompiler* _compiler; // The compiler which compiled this nmethod // Offsets for different nmethod parts int _exception_offset; - // All deoptee's will resume execution at this location described by this offset + // All deoptee's will resume execution at this location described by + // this offset. int _deoptimize_offset; + // All deoptee's at a MethodHandle call site will resume execution + // at this location described by this offset. + int _deoptimize_mh_offset; #ifdef HAVE_DTRACE_H int _trap_offset; #endif // def HAVE_DTRACE_H @@ -174,6 +188,8 @@ // used by jvmti to track if an unload event has been posted for this nmethod. bool _unload_reported; + jbyte _scavenge_root_state; + NOT_PRODUCT(bool _has_debug_info; ) // Nmethod Flushing lock (if non-zero, then the nmethod is not removed) @@ -242,10 +258,11 @@ // helper methods void* operator new(size_t size, int nmethod_size); - void check_store(); const char* reloc_string_for(u_char* begin, u_char* end); - void make_not_entrant_or_zombie(int state); + // Returns true if this thread changed the state of the nmethod or + // false if another thread performed the transition. + bool make_not_entrant_or_zombie(unsigned int state); void inc_decompile_count(); // used to check that writes to nmFlags are done consistently. @@ -314,30 +331,30 @@ bool is_java_method() const { return !method()->is_native(); } bool is_native_method() const { return method()->is_native(); } bool is_osr_method() const { return _entry_bci != InvocationEntryBci; } - bool is_osr_only_method() const { return is_osr_method(); } bool is_compiled_by_c1() const; bool is_compiled_by_c2() const; // boundaries for different parts - address code_begin () const { return _entry_point; } - address code_end () const { return header_begin() + _stub_offset ; } - address exception_begin () const { return header_begin() + _exception_offset ; } - address deopt_handler_begin() const { return header_begin() + _deoptimize_offset ; } - address stub_begin () const { return header_begin() + _stub_offset ; } - address stub_end () const { return header_begin() + _consts_offset ; } - address consts_begin () const { return header_begin() + _consts_offset ; } - address consts_end () const { return header_begin() + _scopes_data_offset ; } - address scopes_data_begin () const { return header_begin() + _scopes_data_offset ; } - address scopes_data_end () const { return header_begin() + _scopes_pcs_offset ; } - PcDesc* scopes_pcs_begin () const { return (PcDesc*)(header_begin() + _scopes_pcs_offset ); } - PcDesc* scopes_pcs_end () const { return (PcDesc*)(header_begin() + _dependencies_offset); } - address dependencies_begin () const { return header_begin() + _dependencies_offset ; } - address dependencies_end () const { return header_begin() + _handler_table_offset ; } - address handler_table_begin() const { return header_begin() + _handler_table_offset ; } - address handler_table_end () const { return header_begin() + _nul_chk_table_offset ; } - address nul_chk_table_begin() const { return header_begin() + _nul_chk_table_offset ; } - address nul_chk_table_end () const { return header_begin() + _nmethod_end_offset ; } + address code_begin () const { return _entry_point; } + address code_end () const { return header_begin() + _stub_offset ; } + address exception_begin () const { return header_begin() + _exception_offset ; } + address deopt_handler_begin () const { return header_begin() + _deoptimize_offset ; } + address deopt_mh_handler_begin() const { return header_begin() + _deoptimize_mh_offset ; } + address stub_begin () const { return header_begin() + _stub_offset ; } + address stub_end () const { return header_begin() + _consts_offset ; } + address consts_begin () const { return header_begin() + _consts_offset ; } + address consts_end () const { return header_begin() + _scopes_data_offset ; } + address scopes_data_begin () const { return header_begin() + _scopes_data_offset ; } + address scopes_data_end () const { return header_begin() + _scopes_pcs_offset ; } + PcDesc* scopes_pcs_begin () const { return (PcDesc*)(header_begin() + _scopes_pcs_offset ); } + PcDesc* scopes_pcs_end () const { return (PcDesc*)(header_begin() + _dependencies_offset) ; } + address dependencies_begin () const { return header_begin() + _dependencies_offset ; } + address dependencies_end () const { return header_begin() + _handler_table_offset ; } + address handler_table_begin () const { return header_begin() + _handler_table_offset ; } + address handler_table_end () const { return header_begin() + _nul_chk_table_offset ; } + address nul_chk_table_begin () const { return header_begin() + _nul_chk_table_offset ; } + address nul_chk_table_end () const { return header_begin() + _nmethod_end_offset ; } int code_size () const { return code_end () - code_begin (); } int stub_size () const { return stub_end () - stub_begin (); } @@ -369,10 +386,12 @@ bool is_zombie() const { return flags.state == zombie; } bool is_unloaded() const { return flags.state == unloaded; } - // Make the nmethod non entrant. The nmethod will continue to be alive. - // It is used when an uncommon trap happens. - void make_not_entrant() { make_not_entrant_or_zombie(not_entrant); } - void make_zombie() { make_not_entrant_or_zombie(zombie); } + // 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 + // another thread performed the transition. + bool make_not_entrant() { return make_not_entrant_or_zombie(not_entrant); } + bool make_zombie() { return make_not_entrant_or_zombie(zombie); } // used by jvmti to track if the unload event has been reported bool unload_reported() { return _unload_reported; } @@ -399,6 +418,12 @@ bool has_unsafe_access() const { return flags.has_unsafe_access; } void set_has_unsafe_access(bool z) { flags.has_unsafe_access = z; } + bool has_method_handle_invokes() const { return flags.has_method_handle_invokes; } + void set_has_method_handle_invokes(bool z) { flags.has_method_handle_invokes = z; } + + bool is_speculatively_disconnected() const { return flags.speculatively_disconnected; } + void set_speculatively_disconnected(bool z) { flags.speculatively_disconnected = z; } + int level() const { return flags.level; } void set_level(int newLevel) { check_safepoint(); flags.level = newLevel; } @@ -407,6 +432,27 @@ int version() const { return flags.version; } void set_version(int v); + // Non-perm oop support + bool on_scavenge_root_list() const { return (_scavenge_root_state & 1) != 0; } + protected: + enum { npl_on_list = 0x01, npl_marked = 0x10 }; + void set_on_scavenge_root_list() { _scavenge_root_state = npl_on_list; } + void clear_on_scavenge_root_list() { _scavenge_root_state = 0; } + // assertion-checking and pruning logic uses the bits of _scavenge_root_state +#ifndef PRODUCT + void set_scavenge_root_marked() { _scavenge_root_state |= npl_marked; } + void clear_scavenge_root_marked() { _scavenge_root_state &= ~npl_marked; } + bool scavenge_root_not_marked() { return (_scavenge_root_state &~ npl_on_list) == 0; } + // N.B. there is no positive marked query, and we only use the not_marked query for asserts. +#endif //PRODUCT + nmethod* scavenge_root_link() const { return _scavenge_root_link; } + void set_scavenge_root_link(nmethod *n) { _scavenge_root_link = n; } + + nmethod* saved_nmethod_link() const { return _saved_nmethod_link; } + void set_saved_nmethod_link(nmethod *n) { _saved_nmethod_link = n; } + + public: + // Sweeper support long stack_traversal_mark() { return _stack_traversal_mark; } void set_stack_traversal_mark(long l) { _stack_traversal_mark = l; } @@ -425,8 +471,8 @@ int osr_entry_bci() const { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); return _entry_bci; } address osr_entry() const { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); return _osr_entry_point; } void invalidate_osr_method(); - nmethod* link() const { return _link; } - void set_link(nmethod *n) { _link = n; } + nmethod* osr_link() const { return _osr_link; } + void set_osr_link(nmethod *n) { _osr_link = n; } // tells whether frames described by this nmethod can be deoptimized // note: native wrappers cannot be deoptimized. @@ -466,7 +512,16 @@ void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f); - void oops_do(OopClosure* f); + virtual void oops_do(OopClosure* f) { oops_do(f, false); } + void oops_do(OopClosure* f, bool do_strong_roots_only); + bool detect_scavenge_root_oops(); + void verify_scavenge_root_oops() PRODUCT_RETURN; + + bool test_set_oops_do_mark(); + static void oops_do_marking_prologue(); + static void oops_do_marking_epilogue(); + static bool oops_do_marking_is_active() { return _oops_do_mark_nmethods != NULL; } + DEBUG_ONLY(bool test_oops_do_mark() { return _oops_do_mark_link != NULL; }) // ScopeDesc for an instruction ScopeDesc* scope_desc_at(address pc); @@ -474,7 +529,7 @@ private: ScopeDesc* scope_desc_in(address begin, address end); - address* orig_pc_addr(const frame* fr ) { return (address*) ((address)fr->unextended_sp() + _orig_pc_offset); } + address* orig_pc_addr(const frame* fr) { return (address*) ((address)fr->unextended_sp() + _orig_pc_offset); } PcDesc* find_pc_desc_internal(address pc, bool approximate); @@ -497,13 +552,20 @@ void copy_scopes_pcs(PcDesc* pcs, int count); void copy_scopes_data(address buffer, int size); - // deopt - // return true is the pc is one would expect if the frame is being deopted. - bool is_deopt_pc(address pc); + // Deopt + // Return true is the PC is one would expect if the frame is being deopted. + bool is_deopt_pc (address pc) { return is_deopt_entry(pc) || is_deopt_mh_entry(pc); } + bool is_deopt_entry (address pc) { return pc == deopt_handler_begin(); } + bool is_deopt_mh_entry(address pc) { return pc == deopt_mh_handler_begin(); } // Accessor/mutator for the original pc of a frame before a frame was deopted. address get_original_pc(const frame* fr) { return *orig_pc_addr(fr); } void set_original_pc(const frame* fr, address pc) { *orig_pc_addr(fr) = pc; } + static address get_deopt_original_pc(const frame* fr); + + // MethodHandle + bool is_method_handle_return(address return_pc); + // jvmti support: void post_compiled_method_load_event(); @@ -530,7 +592,14 @@ // Logging void log_identity(xmlStream* log) const; void log_new_nmethod() const; - void log_state_change(int state) const; + void log_state_change() const; + + // Prints block-level comments, including nmethod specific block labels: + virtual void print_block_comment(outputStream* stream, address block_begin) { + print_nmethod_labels(stream, block_begin); + CodeBlob::print_block_comment(stream, block_begin); + } + void print_nmethod_labels(outputStream* stream, address block_begin); // Prints a comment for one native instruction (reloc info, pc desc) void print_code_comment_on(outputStream* st, int column, address begin, address end); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/pcDesc.cpp --- a/src/share/vm/code/pcDesc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/pcDesc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -26,9 +26,11 @@ # include "incls/_pcDesc.cpp.incl" PcDesc::PcDesc(int pc_offset, int scope_decode_offset, int obj_decode_offset) { + assert(sizeof(PcDescFlags) <= 4, "occupies more than a word"); _pc_offset = pc_offset; _scope_decode_offset = scope_decode_offset; _obj_decode_offset = obj_decode_offset; + _flags.word = 0; } address PcDesc::real_pc(const nmethod* code) const { @@ -50,6 +52,8 @@ tty->print(" "); sd->method()->print_short_name(tty); tty->print(" @%d", sd->bci()); + if (sd->should_reexecute()) + tty->print(" reexecute=true"); tty->cr(); } #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/pcDesc.hpp --- a/src/share/vm/code/pcDesc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/pcDesc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -34,6 +34,16 @@ int _scope_decode_offset; // offset for scope in nmethod int _obj_decode_offset; + union PcDescFlags { + int word; + struct { + unsigned int reexecute: 1; + unsigned int is_method_handle_invoke: 1; + unsigned int return_oop: 1; + } bits; + bool operator ==(const PcDescFlags& other) { return word == other.word; } + } _flags; + public: int pc_offset() const { return _pc_offset; } int scope_decode_offset() const { return _scope_decode_offset; } @@ -53,6 +63,23 @@ upper_offset_limit = (unsigned int)-1 >> 1 }; + // Flags + bool should_reexecute() const { return _flags.bits.reexecute; } + void set_should_reexecute(bool z) { _flags.bits.reexecute = z; } + + // Does pd refer to the same information as pd? + bool is_same_info(const PcDesc* pd) { + return _scope_decode_offset == pd->_scope_decode_offset && + _obj_decode_offset == pd->_obj_decode_offset && + _flags == pd->_flags; + } + + bool is_method_handle_invoke() const { return _flags.bits.is_method_handle_invoke; } + void set_is_method_handle_invoke(bool z) { _flags.bits.is_method_handle_invoke = z; } + + bool return_oop() const { return _flags.bits.return_oop; } + void set_return_oop(bool z) { _flags.bits.return_oop = z; } + // Returns the real pc address real_pc(const nmethod* code) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/scopeDesc.cpp --- a/src/share/vm/code/scopeDesc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/scopeDesc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -26,17 +26,21 @@ # include "incls/_scopeDesc.cpp.incl" -ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset) { +ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool return_oop) { _code = code; _decode_offset = decode_offset; _objects = decode_object_values(obj_decode_offset); + _reexecute = reexecute; + _return_oop = return_oop; decode_body(); } -ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset) { +ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, bool reexecute, bool return_oop) { _code = code; _decode_offset = decode_offset; _objects = decode_object_values(DebugInformationRecorder::serialized_null); + _reexecute = reexecute; + _return_oop = return_oop; decode_body(); } @@ -45,8 +49,9 @@ _code = parent->_code; _decode_offset = parent->_sender_decode_offset; _objects = parent->_objects; + _reexecute = false; //reexecute only applies to the first scope + _return_oop = false; decode_body(); - assert(_reexecute == false, "reexecute not allowed"); } @@ -57,7 +62,6 @@ _sender_decode_offset = DebugInformationRecorder::serialized_null; _method = methodHandle(_code->method()); _bci = InvocationEntryBci; - _reexecute = false; _locals_decode_offset = DebugInformationRecorder::serialized_null; _expressions_decode_offset = DebugInformationRecorder::serialized_null; _monitors_decode_offset = DebugInformationRecorder::serialized_null; @@ -67,7 +71,7 @@ _sender_decode_offset = stream->read_int(); _method = methodHandle((methodOop) stream->read_oop()); - _bci = stream->read_bci_and_reexecute(_reexecute); + _bci = stream->read_bci(); // decode offsets for body and sender _locals_decode_offset = stream->read_int(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/code/scopeDesc.hpp --- a/src/share/vm/code/scopeDesc.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/code/scopeDesc.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -39,8 +39,7 @@ DebugInfoReadStream buffer(code, pc_desc->scope_decode_offset()); int ignore_sender = buffer.read_int(); _method = methodOop(buffer.read_oop()); - bool dummy_reexecute; //only methodOop and bci are needed! - _bci = buffer.read_bci_and_reexecute(dummy_reexecute); + _bci = buffer.read_bci(); } methodOop method() { return _method; } @@ -53,17 +52,18 @@ class ScopeDesc : public ResourceObj { public: // Constructor - ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset); + ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool return_oop); // Calls above, giving default value of "serialized_null" to the // "obj_decode_offset" argument. (We don't use a default argument to // avoid a .hpp-.hpp dependency.) - ScopeDesc(const nmethod* code, int decode_offset); + ScopeDesc(const nmethod* code, int decode_offset, bool reexecute, bool return_oop); // JVM state methodHandle method() const { return _method; } int bci() const { return _bci; } bool should_reexecute() const { return _reexecute; } + bool return_oop() const { return _return_oop; } GrowableArray* locals(); GrowableArray* expressions(); @@ -89,6 +89,7 @@ methodHandle _method; int _bci; bool _reexecute; + bool _return_oop; // Decoding offsets int _decode_offset; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/compileBroker.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -69,6 +69,7 @@ bool CompileBroker::_initialized = false; volatile bool CompileBroker::_should_block = false; +volatile jint CompileBroker::_should_compile_new_jobs = run_compilation; // The installed compiler(s) AbstractCompiler* CompileBroker::_compilers[2]; @@ -986,6 +987,13 @@ return method_code; } if (method->is_not_compilable(comp_level)) return NULL; + + nmethod* saved = CodeCache::find_and_remove_saved_code(method()); + if (saved != NULL) { + method->set_code(method, saved); + return saved; + } + } else { // osr compilation #ifndef TIERED @@ -1037,6 +1045,14 @@ method->jmethod_id(); } + // If the compiler is shut off due to code cache flushing or otherwise, + // fail out now so blocking compiles dont hang the java thread + if (!should_compile_new_jobs() || (UseCodeCacheFlushing && CodeCache::needs_flushing())) { + method->invocation_counter()->decay(); + method->backedge_counter()->decay(); + return NULL; + } + // do the compilation if (method->is_native()) { if (!PreferInterpreterNativeStubs) { @@ -1116,7 +1132,7 @@ // the specified level if (is_native && (!CICompileNatives || !compiler(comp_level)->supports_native())) { - method->set_not_compilable(); + method->set_not_compilable_quietly(); return true; } @@ -1140,7 +1156,7 @@ method->print_short_name(tty); tty->cr(); } - method->set_not_compilable(); + method->set_not_compilable_quietly(); } return false; @@ -1173,7 +1189,7 @@ } // Method was not in the appropriate compilation range. - method->set_not_compilable(); + method->set_not_compilable_quietly(); return 0; } @@ -1325,26 +1341,13 @@ { // We need this HandleMark to avoid leaking VM handles. HandleMark hm(thread); + if (CodeCache::unallocated_capacity() < CodeCacheMinimumFreeSpace) { - // The CodeCache is full. Print out warning and disable compilation. - UseInterpreter = true; - if (UseCompiler || AlwaysCompileLoopMethods ) { - if (log != NULL) { - log->begin_elem("code_cache_full"); - log->stamp(); - log->end_elem(); - } -#ifndef PRODUCT - warning("CodeCache is full. Compiler has been disabled"); - if (CompileTheWorld || ExitOnFullCodeCache) { - before_exit(thread); - exit_globals(); // will delete tty - vm_direct_exit(CompileTheWorld ? 0 : 1); - } -#endif - UseCompiler = false; - AlwaysCompileLoopMethods = false; - } + // the code cache is really full + handle_full_code_cache(); + } else if (UseCodeCacheFlushing && CodeCache::needs_flushing()) { + // Attempt to start cleaning the code cache while there is still a little headroom + NMethodSweeper::handle_full_code_cache(false); } CompileTask* task = queue->get(); @@ -1369,7 +1372,7 @@ // Never compile a method if breakpoints are present in it if (method()->number_of_breakpoints() == 0) { // Compile the method. - if (UseCompiler || AlwaysCompileLoopMethods) { + if ((UseCompiler || AlwaysCompileLoopMethods) && CompileBroker::should_compile_new_jobs()) { #ifdef COMPILER1 // Allow repeating compilations for the purpose of benchmarking // compile speed. This is not useful for customers. @@ -1587,10 +1590,10 @@ if (is_osr) { method->set_not_osr_compilable(); } else { - method->set_not_compilable(); + method->set_not_compilable_quietly(); } } else if (compilable == ciEnv::MethodCompilable_not_at_tier) { - method->set_not_compilable(task->comp_level()); + method->set_not_compilable_quietly(task->comp_level()); } // Note that the queued_for_compilation bits are cleared without @@ -1614,6 +1617,38 @@ // ------------------------------------------------------------------ +// CompileBroker::handle_full_code_cache +// +// The CodeCache is full. Print out warning and disable compilation or +// try code cache cleaning so compilation can continue later. +void CompileBroker::handle_full_code_cache() { + UseInterpreter = true; + if (UseCompiler || AlwaysCompileLoopMethods ) { + CompilerThread* thread = CompilerThread::current(); + CompileLog* log = thread->log(); + if (log != NULL) { + log->begin_elem("code_cache_full"); + log->stamp(); + log->end_elem(); + } + #ifndef PRODUCT + warning("CodeCache is full. Compiler has been disabled"); + if (CompileTheWorld || ExitOnFullCodeCache) { + before_exit(JavaThread::current()); + exit_globals(); // will delete tty + vm_direct_exit(CompileTheWorld ? 0 : 1); + } + #endif + if (UseCodeCacheFlushing) { + NMethodSweeper::handle_full_code_cache(true); + } else { + UseCompiler = false; + AlwaysCompileLoopMethods = false; + } + } +} + +// ------------------------------------------------------------------ // CompileBroker::set_last_compile // // Record this compilation for debugging purposes. @@ -1820,9 +1855,11 @@ CompileBroker::_t_standard_compilation.seconds(), CompileBroker::_t_standard_compilation.seconds() / CompileBroker::_total_standard_compile_count); tty->print_cr(" On stack replacement : %6.3f s, Average : %2.3f", CompileBroker::_t_osr_compilation.seconds(), CompileBroker::_t_osr_compilation.seconds() / CompileBroker::_total_osr_compile_count); - compiler(CompLevel_fast_compile)->print_timers(); - if (compiler(CompLevel_fast_compile) != compiler(CompLevel_highest_tier)) { - compiler(CompLevel_highest_tier)->print_timers(); + + if (compiler(CompLevel_fast_compile)) { + compiler(CompLevel_fast_compile)->print_timers(); + if (compiler(CompLevel_fast_compile) != compiler(CompLevel_highest_tier)) + compiler(CompLevel_highest_tier)->print_timers(); } tty->cr(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/compileBroker.hpp --- a/src/share/vm/compiler/compileBroker.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/compileBroker.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -193,6 +193,9 @@ static bool _initialized; static volatile bool _should_block; + // This flag can be used to stop compilation or turn it back on + static volatile jint _should_compile_new_jobs; + // The installed compiler(s) static AbstractCompiler* _compilers[2]; @@ -319,6 +322,7 @@ static void compiler_thread_loop(); + static uint get_compilation_id() { return _compilation_id; } static bool is_idle(); // Set _should_block. @@ -328,6 +332,20 @@ // Call this from the compiler at convenient points, to poll for _should_block. static void maybe_block(); + enum { + // Flags for toggling compiler activity + stop_compilation = 0, + run_compilation = 1 + }; + + static bool should_compile_new_jobs() { return UseCompiler && (_should_compile_new_jobs == run_compilation); } + static bool set_should_compile_new_jobs(jint new_state) { + // Return success if the current caller set it + jint old = Atomic::cmpxchg(new_state, &_should_compile_new_jobs, 1-new_state); + return (old == (1-new_state)); + } + static void handle_full_code_cache(); + // Return total compilation ticks static jlong total_compilation_ticks() { return _perf_total_compilation != NULL ? _perf_total_compilation->get_value() : 0; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/compilerOracle.cpp --- a/src/share/vm/compiler/compilerOracle.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/compilerOracle.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -392,18 +392,18 @@ }; static MethodMatcher::Mode check_mode(char name[], const char*& error_msg) { - if (strcmp(name, "*") == 0) return MethodMatcher::Any; - int match = MethodMatcher::Exact; - if (name[0] == '*') { + while (name[0] == '*') { match |= MethodMatcher::Suffix; strcpy(name, name + 1); } + if (strcmp(name, "*") == 0) return MethodMatcher::Any; + size_t len = strlen(name); - if (len > 0 && name[len - 1] == '*') { + while (len > 0 && name[len - 1] == '*') { match |= MethodMatcher::Prefix; - name[len - 1] = '\0'; + name[--len] = '\0'; } if (strstr(name, "*") != NULL) { @@ -610,6 +610,14 @@ CompilerOracle::parse_from_string(CompileCommand, CompilerOracle::parse_from_line); CompilerOracle::parse_from_string(CompileOnly, CompilerOracle::parse_compile_only); CompilerOracle::parse_from_file(); + if (lists[PrintCommand] != NULL) { + if (PrintAssembly) { + warning("CompileCommand and/or .hotspot_compiler file contains 'print' commands, but PrintAssembly is also enabled"); + } else if (FLAG_IS_DEFAULT(DebugNonSafepoints)) { + warning("printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output"); + DebugNonSafepoints = true; + } + } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/disassembler.cpp --- a/src/share/vm/compiler/disassembler.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/disassembler.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -151,8 +151,10 @@ outputStream* st = output(); if (_print_bytes && pc > pc0) print_insn_bytes(pc0, pc); - if (_nm != NULL) + if (_nm != NULL) { _nm->print_code_comment_on(st, COMMENT_COLUMN, pc0, pc); + // this calls reloc_string_for which calls oop::print_value_on + } // Output pc bucket ticks if we have any if (total_ticks() != 0) { @@ -273,8 +275,15 @@ oop obj; if (_nm != NULL && (obj = _nm->embeddedOop_at(cur_insn())) != NULL - && (address) obj == adr) { + && (address) obj == adr + && Universe::heap()->is_in(obj) + && Universe::heap()->is_in(obj->klass())) { + julong c = st->count(); obj->print_value_on(st); + if (st->count() == c) { + // No output. (Can happen in product builds.) + st->print("(a %s)", Klass::cast(obj->klass())->external_name()); + } return; } } @@ -286,17 +295,9 @@ void decode_env::print_insn_labels() { address p = cur_insn(); outputStream* st = output(); - nmethod* nm = _nm; - if (nm != NULL) { - if (p == nm->entry_point()) st->print_cr("[Entry Point]"); - if (p == nm->verified_entry_point()) st->print_cr("[Verified Entry Point]"); - if (p == nm->exception_begin()) st->print_cr("[Exception Handler]"); - if (p == nm->stub_begin()) st->print_cr("[Stub Code]"); - if (p == nm->consts_begin()) st->print_cr("[Constants]"); - } CodeBlob* cb = _code; if (cb != NULL) { - cb->print_block_comment(st, (intptr_t)(p - cb->instructions_begin())); + cb->print_block_comment(st, p); } if (_print_pc) { st->print(" " INTPTR_FORMAT ": ", (intptr_t) p); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/methodLiveness.cpp --- a/src/share/vm/compiler/methodLiveness.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/methodLiveness.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -782,6 +782,7 @@ case Bytecodes::_invokespecial: case Bytecodes::_invokestatic: case Bytecodes::_invokeinterface: + case Bytecodes::_invokedynamic: case Bytecodes::_newarray: case Bytecodes::_anewarray: case Bytecodes::_checkcast: diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/oopMap.cpp --- a/src/share/vm/compiler/oopMap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/oopMap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/compiler/oopMap.hpp --- a/src/share/vm/compiler/oopMap.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/compiler/oopMap.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -62,12 +62,13 @@ tl->link_head(tc); tl->link_tail(tc); tl->set_count(1); - tl->init_statistics(); + tl->init_statistics(true /* split_birth */); tl->setParent(NULL); tl->setLeft(NULL); tl->setRight(NULL); return tl; } + TreeList* TreeList::as_TreeList(HeapWord* addr, size_t size) { TreeChunk* tc = (TreeChunk*) addr; assert(size >= sizeof(TreeChunk), "Chunk is too small for a TreeChunk"); @@ -267,6 +268,31 @@ return retTC; } +// Returns the block with the largest heap address amongst +// those in the list for this size; potentially slow and expensive, +// use with caution! +TreeChunk* TreeList::largest_address() { + guarantee(head() != NULL, "The head of the list cannot be NULL"); + FreeChunk* fc = head()->next(); + TreeChunk* retTC; + if (fc == NULL) { + retTC = head_as_TreeChunk(); + } else { + // walk down the list and return the one with the highest + // heap address among chunks of this size. + FreeChunk* last = fc; + while (fc->next() != NULL) { + if ((HeapWord*)last < (HeapWord*)fc) { + last = fc; + } + fc = fc->next(); + } + retTC = TreeChunk::as_TreeChunk(last); + } + assert(retTC->list() == this, "Wrong type of chunk."); + return retTC; +} + BinaryTreeDictionary::BinaryTreeDictionary(MemRegion mr, bool splay): _splay(splay) { @@ -379,7 +405,7 @@ break; } // The evm code reset the hint of the candidate as - // at an interrim point. Why? Seems like this leaves + // at an interim point. Why? Seems like this leaves // the hint pointing to a list that didn't work. // curTL->set_hint(hintTL->size()); } @@ -436,7 +462,7 @@ TreeList *curTL = root(); if (curTL != NULL) { while(curTL->right() != NULL) curTL = curTL->right(); - return curTL->first_available(); + return curTL->largest_address(); } else { return NULL; } @@ -664,7 +690,7 @@ } } TreeChunk* tc = TreeChunk::as_TreeChunk(fc); - // This chunk is being returned to the binary try. It's embedded + // This chunk is being returned to the binary tree. Its embedded // TreeList should be unused at this point. tc->initialize(); if (curTL != NULL) { // exact match @@ -807,6 +833,8 @@ } bool BinaryTreeDictionary::coalDictOverPopulated(size_t size) { + if (FLSAlwaysCoalesceLarge) return true; + TreeList* list_of_size = findList(size); // None of requested size implies overpopulated. return list_of_size == NULL || list_of_size->coalDesired() <= 0 || @@ -854,17 +882,20 @@ double _percentage; float _inter_sweep_current; float _inter_sweep_estimate; + float _intra_sweep_estimate; public: BeginSweepClosure(double p, float inter_sweep_current, - float inter_sweep_estimate) : + float inter_sweep_estimate, + float intra_sweep_estimate) : _percentage(p), _inter_sweep_current(inter_sweep_current), - _inter_sweep_estimate(inter_sweep_estimate) { } + _inter_sweep_estimate(inter_sweep_estimate), + _intra_sweep_estimate(intra_sweep_estimate) { } void do_list(FreeList* fl) { double coalSurplusPercent = _percentage; - fl->compute_desired(_inter_sweep_current, _inter_sweep_estimate); + fl->compute_desired(_inter_sweep_current, _inter_sweep_estimate, _intra_sweep_estimate); fl->set_coalDesired((ssize_t)((double)fl->desired() * coalSurplusPercent)); fl->set_beforeSweep(fl->count()); fl->set_bfrSurp(fl->surplus()); @@ -939,9 +970,10 @@ } void BinaryTreeDictionary::beginSweepDictCensus(double coalSurplusPercent, - float inter_sweep_current, float inter_sweep_estimate) { + float inter_sweep_current, float inter_sweep_estimate, float intra_sweep_estimate) { BeginSweepClosure bsc(coalSurplusPercent, inter_sweep_current, - inter_sweep_estimate); + inter_sweep_estimate, + intra_sweep_estimate); bsc.do_tree(root()); } @@ -1077,13 +1109,13 @@ // Print census information - counts, births, deaths, etc. // for each list in the tree. Also print some summary // information. -class printTreeCensusClosure : public AscendTreeCensusClosure { +class PrintTreeCensusClosure : public AscendTreeCensusClosure { int _print_line; size_t _totalFree; FreeList _total; public: - printTreeCensusClosure() { + PrintTreeCensusClosure() { _print_line = 0; _totalFree = 0; } @@ -1113,7 +1145,7 @@ gclog_or_tty->print("\nBinaryTree\n"); FreeList::print_labels_on(gclog_or_tty, "size"); - printTreeCensusClosure ptc; + PrintTreeCensusClosure ptc; ptc.do_tree(root()); FreeList* total = ptc.total(); @@ -1130,6 +1162,38 @@ /(total->desired() != 0 ? (double)total->desired() : 1.0)); } +class PrintFreeListsClosure : public AscendTreeCensusClosure { + outputStream* _st; + int _print_line; + + public: + PrintFreeListsClosure(outputStream* st) { + _st = st; + _print_line = 0; + } + void do_list(FreeList* fl) { + if (++_print_line >= 40) { + FreeList::print_labels_on(_st, "size"); + _print_line = 0; + } + fl->print_on(gclog_or_tty); + size_t sz = fl->size(); + for (FreeChunk* fc = fl->head(); fc != NULL; + fc = fc->next()) { + _st->print_cr("\t[" PTR_FORMAT "," PTR_FORMAT ") %s", + fc, (HeapWord*)fc + sz, + fc->cantCoalesce() ? "\t CC" : ""); + } + } +}; + +void BinaryTreeDictionary::print_free_lists(outputStream* st) const { + + FreeList::print_labels_on(st, "size"); + PrintFreeListsClosure pflc(st); + pflc.do_tree(root()); +} + // Verify the following tree invariants: // . _root has no parent // . parent and child point to each other diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/binaryTreeDictionary.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -42,9 +42,6 @@ friend class AscendTreeCensusClosure; friend class DescendTreeCensusClosure; friend class DescendTreeSearchClosure; - TreeList* _parent; - TreeList* _left; - TreeList* _right; protected: TreeList* parent() const { return _parent; } @@ -82,6 +79,11 @@ // to a TreeChunk. TreeChunk* first_available(); + // Returns the block with the largest heap address amongst + // those in the list for this size; potentially slow and expensive, + // use with caution! + TreeChunk* largest_address(); + // removeChunkReplaceIfNeeded() removes the given "tc" from the TreeList. // If "tc" is the first chunk in the list, it is also the // TreeList that is the node in the tree. removeChunkReplaceIfNeeded() @@ -254,8 +256,9 @@ // Methods called at the beginning of a sweep to prepare the // statistics for the sweep. void beginSweepDictCensus(double coalSurplusPercent, - float sweep_current, - float sweep_estimate); + float inter_sweep_current, + float inter_sweep_estimate, + float intra_sweep_estimate); // Methods called after the end of a sweep to modify the // statistics for the sweep. void endSweepDictCensus(double splitSurplusPercent); @@ -269,6 +272,7 @@ // Print the statistcis for all the lists in the tree. Also may // print out summaries. void printDictCensus(void) const; + void print_free_lists(outputStream* st) const; // For debugging. Returns the sum of the _returnedBytes for // all lists in the tree. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsAdaptiveSizePolicy.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -46,9 +46,9 @@ _processor_count = os::active_processor_count(); - if (CMSConcurrentMTEnabled && (ParallelCMSThreads > 1)) { + if (CMSConcurrentMTEnabled && (ConcGCThreads > 1)) { assert(_processor_count > 0, "Processor count is suspect"); - _concurrent_processor_count = MIN2((uint) ParallelCMSThreads, + _concurrent_processor_count = MIN2((uint) ConcGCThreads, (uint) _processor_count); } else { _concurrent_processor_count = 1; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -32,7 +32,9 @@ // threads. The second argument is in support of an extra locking // check for CFL spaces' free list locks. #ifndef PRODUCT -void CMSLockVerifier::assert_locked(const Mutex* lock, const Mutex* p_lock) { +void CMSLockVerifier::assert_locked(const Mutex* lock, + const Mutex* p_lock1, + const Mutex* p_lock2) { if (!Universe::is_fully_initialized()) { return; } @@ -40,7 +42,7 @@ Thread* myThread = Thread::current(); if (lock == NULL) { // a "lock-free" structure, e.g. MUT, protected by CMS token - assert(p_lock == NULL, "Unexpected state"); + assert(p_lock1 == NULL && p_lock2 == NULL, "Unexpected caller error"); if (myThread->is_ConcurrentGC_thread()) { // This test might have to change in the future, if there can be // multiple peer CMS threads. But for now, if we're testing the CMS @@ -60,36 +62,39 @@ return; } - if (ParallelGCThreads == 0) { + if (myThread->is_VM_thread() + || myThread->is_ConcurrentGC_thread() + || myThread->is_Java_thread()) { + // Make sure that we are holding the associated lock. assert_lock_strong(lock); + // The checking of p_lock is a spl case for CFLS' free list + // locks: we make sure that none of the parallel GC work gang + // threads are holding "sub-locks" of freeListLock(). We check only + // the parDictionaryAllocLock because the others are too numerous. + // This spl case code is somewhat ugly and any improvements + // are welcome. + assert(p_lock1 == NULL || !p_lock1->is_locked() || p_lock1->owned_by_self(), + "Possible race between this and parallel GC threads"); + assert(p_lock2 == NULL || !p_lock2->is_locked() || p_lock2->owned_by_self(), + "Possible race between this and parallel GC threads"); + } else if (myThread->is_GC_task_thread()) { + // Make sure that the VM or CMS thread holds lock on our behalf + // XXX If there were a concept of a gang_master for a (set of) + // gang_workers, we could have used the identity of that thread + // for checking ownership here; for now we just disjunct. + assert(lock->owner() == VMThread::vm_thread() || + lock->owner() == ConcurrentMarkSweepThread::cmst(), + "Should be locked by VM thread or CMS thread on my behalf"); + if (p_lock1 != NULL) { + assert_lock_strong(p_lock1); + } + if (p_lock2 != NULL) { + assert_lock_strong(p_lock2); + } } else { - if (myThread->is_VM_thread() - || myThread->is_ConcurrentGC_thread() - || myThread->is_Java_thread()) { - // Make sure that we are holding the associated lock. - assert_lock_strong(lock); - // The checking of p_lock is a spl case for CFLS' free list - // locks: we make sure that none of the parallel GC work gang - // threads are holding "sub-locks" of freeListLock(). We check only - // the parDictionaryAllocLock because the others are too numerous. - // This spl case code is somewhat ugly and any improvements - // are welcome XXX FIX ME!! - if (p_lock != NULL) { - assert(!p_lock->is_locked() || p_lock->owned_by_self(), - "Possible race between this and parallel GC threads"); - } - } else if (myThread->is_GC_task_thread()) { - // Make sure that the VM or CMS thread holds lock on our behalf - // XXX If there were a concept of a gang_master for a (set of) - // gang_workers, we could have used the identity of that thread - // for checking ownership here; for now we just disjunct. - assert(lock->owner() == VMThread::vm_thread() || - lock->owner() == ConcurrentMarkSweepThread::cmst(), - "Should be locked by VM thread or CMS thread on my behalf"); - } else { - // Make sure we didn't miss some obscure corner case - ShouldNotReachHere(); - } + // Make sure we didn't miss some other thread type calling into here; + // perhaps as a result of future VM evolution. + ShouldNotReachHere(); } } #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsLockVerifier.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -29,8 +29,11 @@ // the parallel threads. class CMSLockVerifier: AllStatic { public: - static void assert_locked(const Mutex* lock, const Mutex* p_lock) + static void assert_locked(const Mutex* lock, const Mutex* p_lock1, const Mutex* p_lock2) PRODUCT_RETURN; + static void assert_locked(const Mutex* lock, const Mutex* p_lock) { + assert_locked(lock, p_lock, NULL); + } static void assert_locked(const Mutex* lock) { assert_locked(lock, NULL); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -47,20 +47,15 @@ private: const MemRegion _span; CMSBitMap* _bitMap; - const bool _should_do_nmethods; protected: DO_OOP_WORK_DEFN public: - MarkRefsIntoClosure(MemRegion span, CMSBitMap* bitMap, - bool should_do_nmethods); + MarkRefsIntoClosure(MemRegion span, CMSBitMap* bitMap); virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { MarkRefsIntoClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { MarkRefsIntoClosure::do_oop_work(p); } bool do_header() { return true; } - virtual const bool do_nmethods() const { - return _should_do_nmethods; - } Prefetch::style prefetch_style() { return Prefetch::do_read; } @@ -73,36 +68,65 @@ const MemRegion _span; CMSBitMap* _verification_bm; CMSBitMap* _cms_bm; - const bool _should_do_nmethods; protected: DO_OOP_WORK_DEFN public: MarkRefsIntoVerifyClosure(MemRegion span, CMSBitMap* verification_bm, - CMSBitMap* cms_bm, bool should_do_nmethods); + CMSBitMap* cms_bm); virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { MarkRefsIntoVerifyClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { MarkRefsIntoVerifyClosure::do_oop_work(p); } bool do_header() { return true; } - virtual const bool do_nmethods() const { - return _should_do_nmethods; - } Prefetch::style prefetch_style() { return Prefetch::do_read; } }; +// KlassRememberingOopClosure is used when marking of the permanent generation +// is being done. It adds fields to support revisiting of klasses +// for class unloading. _should_remember_klasses should be set to +// indicate if klasses should be remembered. Currently that is whenever +// CMS class unloading is turned on. The _revisit_stack is used +// to save the klasses for later processing. +class KlassRememberingOopClosure : public OopClosure { + protected: + CMSCollector* _collector; + CMSMarkStack* _revisit_stack; + bool const _should_remember_klasses; + public: + void check_remember_klasses() const PRODUCT_RETURN; + virtual const bool should_remember_klasses() const { + check_remember_klasses(); + return _should_remember_klasses; + } + virtual void remember_klass(Klass* k); + + KlassRememberingOopClosure(CMSCollector* collector, + ReferenceProcessor* rp, + CMSMarkStack* revisit_stack); +}; + +// Similar to KlassRememberingOopClosure for use when multiple +// GC threads will execute the closure. + +class Par_KlassRememberingOopClosure : public KlassRememberingOopClosure { + public: + Par_KlassRememberingOopClosure(CMSCollector* collector, + ReferenceProcessor* rp, + CMSMarkStack* revisit_stack): + KlassRememberingOopClosure(collector, rp, revisit_stack) {} + virtual void remember_klass(Klass* k); +}; + // The non-parallel version (the parallel version appears further below). -class PushAndMarkClosure: public OopClosure { +class PushAndMarkClosure: public KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _span; CMSBitMap* _bit_map; CMSBitMap* _mod_union_table; CMSMarkStack* _mark_stack; - CMSMarkStack* _revisit_stack; bool _concurrent_precleaning; - bool const _should_remember_klasses; protected: DO_OOP_WORK_DEFN public: @@ -122,10 +146,12 @@ Prefetch::style prefetch_style() { return Prefetch::do_read; } - virtual const bool should_remember_klasses() const { - return _should_remember_klasses; + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; } - virtual void remember_klass(Klass* k); + virtual void remember_mdo(DataLayout* v); }; // In the parallel case, the revisit stack, the bit map and the @@ -134,14 +160,11 @@ // synchronization (for instance, via CAS). The marking stack // used in the non-parallel case above is here replaced with // an OopTaskQueue structure to allow efficient work stealing. -class Par_PushAndMarkClosure: public OopClosure { +class Par_PushAndMarkClosure: public Par_KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _span; CMSBitMap* _bit_map; OopTaskQueue* _work_queue; - CMSMarkStack* _revisit_stack; - bool const _should_remember_klasses; protected: DO_OOP_WORK_DEFN public: @@ -159,10 +182,12 @@ Prefetch::style prefetch_style() { return Prefetch::do_read; } - virtual const bool should_remember_klasses() const { - return _should_remember_klasses; + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; } - virtual void remember_klass(Klass* k); + virtual void remember_mdo(DataLayout* v); }; // The non-parallel version (the parallel version appears further below). @@ -194,13 +219,18 @@ inline void do_oop_nv(oop* p) { MarkRefsIntoAndScanClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { MarkRefsIntoAndScanClosure::do_oop_work(p); } bool do_header() { return true; } - virtual const bool do_nmethods() const { return true; } Prefetch::style prefetch_style() { return Prefetch::do_read; } void set_freelistLock(Mutex* m) { _freelistLock = m; } + virtual const bool should_remember_klasses() const { + return _pushAndMarkClosure.should_remember_klasses(); + } + virtual void remember_klass(Klass* k) { + _pushAndMarkClosure.remember_klass(k); + } private: inline void do_yield_check(); @@ -233,7 +263,16 @@ inline void do_oop_nv(oop* p) { Par_MarkRefsIntoAndScanClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { Par_MarkRefsIntoAndScanClosure::do_oop_work(p); } bool do_header() { return true; } - virtual const bool do_nmethods() const { return true; } + // When ScanMarkedObjectsAgainClosure is used, + // it passes [Par_]MarkRefsIntoAndScanClosure to oop_oop_iterate(), + // and this delegation is used. + virtual const bool should_remember_klasses() const { + return _par_pushAndMarkClosure.should_remember_klasses(); + } + // See comment on should_remember_klasses() above. + virtual void remember_klass(Klass* k) { + _par_pushAndMarkClosure.remember_klass(k); + } Prefetch::style prefetch_style() { return Prefetch::do_read; } @@ -243,17 +282,14 @@ // This closure is used during the concurrent marking phase // following the first checkpoint. Its use is buried in // the closure MarkFromRootsClosure. -class PushOrMarkClosure: public OopClosure { +class PushOrMarkClosure: public KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _span; CMSBitMap* _bitMap; CMSMarkStack* _markStack; - CMSMarkStack* _revisitStack; HeapWord* const _finger; MarkFromRootsClosure* const _parent; - bool const _should_remember_klasses; protected: DO_OOP_WORK_DEFN public: @@ -268,10 +304,13 @@ virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { PushOrMarkClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { PushOrMarkClosure::do_oop_work(p); } - virtual const bool should_remember_klasses() const { - return _should_remember_klasses; + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; } - virtual void remember_klass(Klass* k); + virtual void remember_mdo(DataLayout* v); + // Deal with a stack overflow condition void handle_stack_overflow(HeapWord* lost); private: @@ -282,20 +321,17 @@ // This closure is used during the concurrent marking phase // following the first checkpoint. Its use is buried in // the closure Par_MarkFromRootsClosure. -class Par_PushOrMarkClosure: public OopClosure { +class Par_PushOrMarkClosure: public Par_KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _whole_span; MemRegion _span; // local chunk CMSBitMap* _bit_map; OopTaskQueue* _work_queue; CMSMarkStack* _overflow_stack; - CMSMarkStack* _revisit_stack; HeapWord* const _finger; HeapWord** const _global_finger_addr; Par_MarkFromRootsClosure* const _parent; - bool const _should_remember_klasses; protected: DO_OOP_WORK_DEFN public: @@ -312,10 +348,13 @@ virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { Par_PushOrMarkClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { Par_PushOrMarkClosure::do_oop_work(p); } - virtual const bool should_remember_klasses() const { - return _should_remember_klasses; + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; } - virtual void remember_klass(Klass* k); + virtual void remember_mdo(DataLayout* v); + // Deal with a stack overflow condition void handle_stack_overflow(HeapWord* lost); private: @@ -328,9 +367,8 @@ // processing phase of the CMS final checkpoint step, as // well as during the concurrent precleaning of the discovered // reference lists. -class CMSKeepAliveClosure: public OopClosure { +class CMSKeepAliveClosure: public KlassRememberingOopClosure { private: - CMSCollector* _collector; const MemRegion _span; CMSMarkStack* _mark_stack; CMSBitMap* _bit_map; @@ -340,14 +378,7 @@ public: CMSKeepAliveClosure(CMSCollector* collector, MemRegion span, CMSBitMap* bit_map, CMSMarkStack* mark_stack, - bool cpc): - _collector(collector), - _span(span), - _bit_map(bit_map), - _mark_stack(mark_stack), - _concurrent_precleaning(cpc) { - assert(!_span.is_empty(), "Empty span could spell trouble"); - } + CMSMarkStack* revisit_stack, bool cpc); bool concurrent_precleaning() const { return _concurrent_precleaning; } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); @@ -355,9 +386,8 @@ inline void do_oop_nv(narrowOop* p) { CMSKeepAliveClosure::do_oop_work(p); } }; -class CMSInnerParMarkAndPushClosure: public OopClosure { +class CMSInnerParMarkAndPushClosure: public Par_KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _span; OopTaskQueue* _work_queue; CMSBitMap* _bit_map; @@ -366,11 +396,8 @@ public: CMSInnerParMarkAndPushClosure(CMSCollector* collector, MemRegion span, CMSBitMap* bit_map, - OopTaskQueue* work_queue): - _collector(collector), - _span(span), - _bit_map(bit_map), - _work_queue(work_queue) { } + CMSMarkStack* revisit_stack, + OopTaskQueue* work_queue); virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { CMSInnerParMarkAndPushClosure::do_oop_work(p); } @@ -380,9 +407,8 @@ // A parallel (MT) version of the above, used when // reference processing is parallel; the only difference // is in the do_oop method. -class CMSParKeepAliveClosure: public OopClosure { +class CMSParKeepAliveClosure: public Par_KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _span; OopTaskQueue* _work_queue; CMSBitMap* _bit_map; @@ -394,7 +420,8 @@ DO_OOP_WORK_DEFN public: CMSParKeepAliveClosure(CMSCollector* collector, MemRegion span, - CMSBitMap* bit_map, OopTaskQueue* work_queue); + CMSBitMap* bit_map, CMSMarkStack* revisit_stack, + OopTaskQueue* work_queue); virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { CMSParKeepAliveClosure::do_oop_work(p); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -37,16 +37,34 @@ } } -inline void PushOrMarkClosure::remember_klass(Klass* k) { - if (!_revisitStack->push(oop(k))) { +#ifndef PRODUCT +void KlassRememberingOopClosure::check_remember_klasses() const { + assert(_should_remember_klasses == must_remember_klasses(), + "Should remember klasses in this context."); +} +#endif + +void KlassRememberingOopClosure::remember_klass(Klass* k) { + if (!_revisit_stack->push(oop(k))) { fatal("Revisit stack overflow in PushOrMarkClosure"); } + check_remember_klasses(); +} + +inline void PushOrMarkClosure::remember_mdo(DataLayout* v) { + // TBD } -inline void Par_PushOrMarkClosure::remember_klass(Klass* k) { + +void Par_KlassRememberingOopClosure::remember_klass(Klass* k) { if (!_revisit_stack->par_push(oop(k))) { - fatal("Revisit stack overflow in PushOrMarkClosure"); + fatal("Revisit stack overflow in Par_KlassRememberingOopClosure"); } + check_remember_klasses(); +} + +inline void Par_PushOrMarkClosure::remember_mdo(DataLayout* v) { + // TBD } inline void PushOrMarkClosure::do_yield_check() { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -62,18 +62,15 @@ // implementation, namely, the simple binary tree (splaying // temporarily disabled). switch (dictionaryChoice) { - case FreeBlockDictionary::dictionaryBinaryTree: - _dictionary = new BinaryTreeDictionary(mr); - break; case FreeBlockDictionary::dictionarySplayTree: case FreeBlockDictionary::dictionarySkipList: default: warning("dictionaryChoice: selected option not understood; using" " default BinaryTreeDictionary implementation instead."); + case FreeBlockDictionary::dictionaryBinaryTree: _dictionary = new BinaryTreeDictionary(mr); break; } - splitBirth(mr.word_size()); assert(_dictionary != NULL, "CMS dictionary initialization"); // The indexed free lists are initially all empty and are lazily // filled in on demand. Initialize the array elements to NULL. @@ -388,6 +385,105 @@ return res; } +void CompactibleFreeListSpace::print_indexed_free_lists(outputStream* st) +const { + reportIndexedFreeListStatistics(); + gclog_or_tty->print_cr("Layout of Indexed Freelists"); + gclog_or_tty->print_cr("---------------------------"); + FreeList::print_labels_on(st, "size"); + for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { + _indexedFreeList[i].print_on(gclog_or_tty); + 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, + fc->cantCoalesce() ? "\t CC" : ""); + } + } +} + +void CompactibleFreeListSpace::print_promo_info_blocks(outputStream* st) +const { + _promoInfo.print_on(st); +} + +void CompactibleFreeListSpace::print_dictionary_free_lists(outputStream* st) +const { + _dictionary->reportStatistics(); + st->print_cr("Layout of Freelists in Tree"); + st->print_cr("---------------------------"); + _dictionary->print_free_lists(st); +} + +class BlkPrintingClosure: public BlkClosure { + const CMSCollector* _collector; + const CompactibleFreeListSpace* _sp; + const CMSBitMap* _live_bit_map; + const bool _post_remark; + outputStream* _st; +public: + BlkPrintingClosure(const CMSCollector* collector, + const CompactibleFreeListSpace* sp, + const CMSBitMap* live_bit_map, + outputStream* st): + _collector(collector), + _sp(sp), + _live_bit_map(live_bit_map), + _post_remark(collector->abstract_state() > CMSCollector::FinalMarking), + _st(st) { } + size_t do_blk(HeapWord* addr); +}; + +size_t BlkPrintingClosure::do_blk(HeapWord* addr) { + size_t sz = _sp->block_size_no_stall(addr, _collector); + assert(sz != 0, "Should always be able to compute a size"); + 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, + dead ? "dead" : "live", + sz, + (!dead && CMSPrintObjectsInDump) ? ":" : "."); + if (CMSPrintObjectsInDump && !dead) { + oop(addr)->print_on(_st); + _st->print_cr("--------------------------------------"); + } + } else { // free block + _st->print_cr(PTR_FORMAT ": free block of size " SIZE_FORMAT "%s", + addr, sz, CMSPrintChunksInDump ? ":" : "."); + if (CMSPrintChunksInDump) { + ((FreeChunk*)addr)->print_on(_st); + _st->print_cr("--------------------------------------"); + } + } + return sz; +} + +void CompactibleFreeListSpace::dump_at_safepoint_with_locks(CMSCollector* c, + outputStream* st) { + st->print_cr("\n========================="); + st->print_cr("Block layout in CMS Heap:"); + st->print_cr("========================="); + BlkPrintingClosure bpcl(c, this, c->markBitMap(), st); + blk_iterate(&bpcl); + + st->print_cr("\n======================================="); + st->print_cr("Order & Layout of Promotion Info Blocks"); + st->print_cr("======================================="); + print_promo_info_blocks(st); + + st->print_cr("\n==========================="); + st->print_cr("Order of Indexed Free Lists"); + st->print_cr("========================="); + print_indexed_free_lists(st); + + st->print_cr("\n================================="); + st->print_cr("Order of Free Lists in Dictionary"); + st->print_cr("================================="); + print_dictionary_free_lists(st); +} + + void CompactibleFreeListSpace::reportFreeListStatistics() const { assert_lock_strong(&_freelistLock); assert(PrintFLSStatistics != 0, "Reporting error"); @@ -449,37 +545,37 @@ if (prevEnd != NULL) { // Resize the underlying block offset table. _bt.resize(pointer_delta(value, bottom())); - if (value <= prevEnd) { - assert(value >= unallocated_block(), "New end is below unallocated block"); - } else { - // Now, take this new chunk and add it to the free blocks. - // Note that the BOT has not yet been updated for this block. - size_t newFcSize = pointer_delta(value, prevEnd); - // XXX This is REALLY UGLY and should be fixed up. XXX - if (!_adaptive_freelists && _smallLinearAllocBlock._ptr == NULL) { - // Mark the boundary of the new block in BOT - _bt.mark_block(prevEnd, value); - // put it all in the linAB - if (ParallelGCThreads == 0) { - _smallLinearAllocBlock._ptr = prevEnd; - _smallLinearAllocBlock._word_size = newFcSize; - repairLinearAllocBlock(&_smallLinearAllocBlock); - } else { // ParallelGCThreads > 0 - MutexLockerEx x(parDictionaryAllocLock(), - Mutex::_no_safepoint_check_flag); - _smallLinearAllocBlock._ptr = prevEnd; - _smallLinearAllocBlock._word_size = newFcSize; - repairLinearAllocBlock(&_smallLinearAllocBlock); + if (value <= prevEnd) { + assert(value >= unallocated_block(), "New end is below unallocated block"); + } else { + // Now, take this new chunk and add it to the free blocks. + // Note that the BOT has not yet been updated for this block. + size_t newFcSize = pointer_delta(value, prevEnd); + // XXX This is REALLY UGLY and should be fixed up. XXX + if (!_adaptive_freelists && _smallLinearAllocBlock._ptr == NULL) { + // Mark the boundary of the new block in BOT + _bt.mark_block(prevEnd, value); + // put it all in the linAB + if (ParallelGCThreads == 0) { + _smallLinearAllocBlock._ptr = prevEnd; + _smallLinearAllocBlock._word_size = newFcSize; + repairLinearAllocBlock(&_smallLinearAllocBlock); + } else { // ParallelGCThreads > 0 + MutexLockerEx x(parDictionaryAllocLock(), + Mutex::_no_safepoint_check_flag); + _smallLinearAllocBlock._ptr = prevEnd; + _smallLinearAllocBlock._word_size = newFcSize; + repairLinearAllocBlock(&_smallLinearAllocBlock); + } + // Births of chunks put into a LinAB are not recorded. Births + // of chunks as they are allocated out of a LinAB are. + } else { + // Add the block to the free lists, if possible coalescing it + // with the last free block, and update the BOT and census data. + addChunkToFreeListsAtEndRecordingStats(prevEnd, newFcSize); } - // Births of chunks put into a LinAB are not recorded. Births - // of chunks as they are allocated out of a LinAB are. - } else { - // Add the block to the free lists, if possible coalescing it - // with the last free block, and update the BOT and census data. - addChunkToFreeListsAtEndRecordingStats(prevEnd, newFcSize); } } - } } class FreeListSpace_DCTOC : public Filtering_DCTOC { @@ -732,7 +828,7 @@ void CompactibleFreeListSpace::object_iterate_mem(MemRegion mr, UpwardsObjectClosure* cl) { - assert_locked(); + assert_locked(freelistLock()); NOT_PRODUCT(verify_objects_initialized()); Space::object_iterate_mem(mr, cl); } @@ -1212,12 +1308,15 @@ void CompactibleFreeListSpace::assert_locked() const { CMSLockVerifier::assert_locked(freelistLock(), parDictionaryAllocLock()); } + +void CompactibleFreeListSpace::assert_locked(const Mutex* lock) const { + CMSLockVerifier::assert_locked(lock); +} #endif FreeChunk* CompactibleFreeListSpace::allocateScratch(size_t size) { // In the parallel case, the main thread holds the free list lock // on behalf the parallel threads. - assert_locked(); FreeChunk* fc; { // If GC is parallel, this might be called by several threads. @@ -1298,17 +1397,18 @@ res = blk->_ptr; _bt.allocated(res, blk->_word_size); } else if (size + MinChunkSize <= blk->_refillSize) { + size_t sz = blk->_word_size; // Update _unallocated_block if the size is such that chunk would be // returned to the indexed free list. All other chunks in the indexed // free lists are allocated from the dictionary so that _unallocated_block // has already been adjusted for them. Do it here so that the cost // for all chunks added back to the indexed free lists. - if (blk->_word_size < SmallForDictionary) { - _bt.allocated(blk->_ptr, blk->_word_size); + if (sz < SmallForDictionary) { + _bt.allocated(blk->_ptr, sz); } // Return the chunk that isn't big enough, and then refill below. - addChunkToFreeLists(blk->_ptr, blk->_word_size); - _bt.verify_single_block(blk->_ptr, (blk->_ptr + blk->_word_size)); + addChunkToFreeLists(blk->_ptr, sz); + splitBirth(sz); // Don't keep statistics on adding back chunk from a LinAB. } else { // A refilled block would not satisfy the request. @@ -1376,11 +1476,13 @@ res = getChunkFromIndexedFreeListHelper(size); } _bt.verify_not_unallocated((HeapWord*) res, size); + assert(res == NULL || res->size() == size, "Incorrect block size"); return res; } FreeChunk* -CompactibleFreeListSpace::getChunkFromIndexedFreeListHelper(size_t size) { +CompactibleFreeListSpace::getChunkFromIndexedFreeListHelper(size_t size, + bool replenish) { assert_locked(); FreeChunk* fc = NULL; if (size < SmallForDictionary) { @@ -1398,54 +1500,66 @@ // and replenishing indexed lists from the small linAB. // FreeChunk* newFc = NULL; - size_t replenish_size = CMSIndexedFreeListReplenish * size; + const size_t replenish_size = CMSIndexedFreeListReplenish * size; if (replenish_size < SmallForDictionary) { // Do not replenish from an underpopulated size. if (_indexedFreeList[replenish_size].surplus() > 0 && _indexedFreeList[replenish_size].head() != NULL) { - newFc = - _indexedFreeList[replenish_size].getChunkAtHead(); - } else { + newFc = _indexedFreeList[replenish_size].getChunkAtHead(); + } else if (bestFitFirst()) { newFc = bestFitSmall(replenish_size); } } - if (newFc != NULL) { - splitDeath(replenish_size); - } else if (replenish_size > size) { + if (newFc == NULL && replenish_size > size) { assert(CMSIndexedFreeListReplenish > 1, "ctl pt invariant"); - newFc = - getChunkFromIndexedFreeListHelper(replenish_size); + newFc = getChunkFromIndexedFreeListHelper(replenish_size, false); } + // Note: The stats update re split-death of block obtained above + // will be recorded below precisely when we know we are going to + // be actually splitting it into more than one pieces below. if (newFc != NULL) { - assert(newFc->size() == replenish_size, "Got wrong size"); - size_t i; - FreeChunk *curFc, *nextFc; - // carve up and link blocks 0, ..., CMSIndexedFreeListReplenish - 2 - // The last chunk is not added to the lists but is returned as the - // free chunk. - for (curFc = newFc, nextFc = (FreeChunk*)((HeapWord*)curFc + size), - i = 0; - i < (CMSIndexedFreeListReplenish - 1); - curFc = nextFc, nextFc = (FreeChunk*)((HeapWord*)nextFc + size), - i++) { + if (replenish || CMSReplenishIntermediate) { + // Replenish this list and return one block to caller. + size_t i; + FreeChunk *curFc, *nextFc; + size_t num_blk = newFc->size() / size; + assert(num_blk >= 1, "Smaller than requested?"); + assert(newFc->size() % size == 0, "Should be integral multiple of request"); + if (num_blk > 1) { + // we are sure we will be splitting the block just obtained + // into multiple pieces; record the split-death of the original + splitDeath(replenish_size); + } + // carve up and link blocks 0, ..., num_blk - 2 + // The last chunk is not added to the lists but is returned as the + // free chunk. + for (curFc = newFc, nextFc = (FreeChunk*)((HeapWord*)curFc + size), + i = 0; + i < (num_blk - 1); + curFc = nextFc, nextFc = (FreeChunk*)((HeapWord*)nextFc + size), + i++) { + curFc->setSize(size); + // Don't record this as a return in order to try and + // determine the "returns" from a GC. + _bt.verify_not_unallocated((HeapWord*) fc, size); + _indexedFreeList[size].returnChunkAtTail(curFc, false); + _bt.mark_block((HeapWord*)curFc, size); + splitBirth(size); + // Don't record the initial population of the indexed list + // as a split birth. + } + + // check that the arithmetic was OK above + assert((HeapWord*)nextFc == (HeapWord*)newFc + num_blk*size, + "inconsistency in carving newFc"); curFc->setSize(size); - // Don't record this as a return in order to try and - // determine the "returns" from a GC. - _bt.verify_not_unallocated((HeapWord*) fc, size); - _indexedFreeList[size].returnChunkAtTail(curFc, false); _bt.mark_block((HeapWord*)curFc, size); splitBirth(size); - // Don't record the initial population of the indexed list - // as a split birth. + fc = curFc; + } else { + // Return entire block to caller + fc = newFc; } - - // check that the arithmetic was OK above - assert((HeapWord*)nextFc == (HeapWord*)newFc + replenish_size, - "inconsistency in carving newFc"); - curFc->setSize(size); - _bt.mark_block((HeapWord*)curFc, size); - splitBirth(size); - return curFc; } } } else { @@ -1453,7 +1567,7 @@ // replenish the indexed free list. fc = getChunkFromDictionaryExact(size); } - assert(fc == NULL || fc->isFree(), "Should be returning a free chunk"); + // assert(fc == NULL || fc->isFree(), "Should be returning a free chunk"); return fc; } @@ -1512,6 +1626,11 @@ // adjust _unallocated_block downward, as necessary _bt.freed((HeapWord*)chunk, size); _dictionary->returnChunk(chunk); +#ifndef PRODUCT + if (CMSCollector::abstract_state() != CMSCollector::Sweeping) { + TreeChunk::as_TreeChunk(chunk)->list()->verify_stats(); + } +#endif // PRODUCT } void @@ -1525,6 +1644,11 @@ } else { _indexedFreeList[size].returnChunkAtHead(fc); } +#ifndef PRODUCT + if (CMSCollector::abstract_state() != CMSCollector::Sweeping) { + _indexedFreeList[size].verify_stats(); + } +#endif // PRODUCT } // Add chunk to end of last block -- if it's the largest @@ -1537,7 +1661,6 @@ HeapWord* chunk, size_t size) { // check that the chunk does lie in this space! assert(chunk != NULL && is_in_reserved(chunk), "Not in this space!"); - assert_locked(); // One of the parallel gc task threads may be here // whilst others are allocating. Mutex* lock = NULL; @@ -1991,24 +2114,26 @@ return frag; } -#define CoalSurplusPercent 1.05 -#define SplitSurplusPercent 1.10 - void CompactibleFreeListSpace::beginSweepFLCensus( float inter_sweep_current, - float inter_sweep_estimate) { + float inter_sweep_estimate, + float intra_sweep_estimate) { assert_locked(); size_t i; for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { FreeList* fl = &_indexedFreeList[i]; - fl->compute_desired(inter_sweep_current, inter_sweep_estimate); - fl->set_coalDesired((ssize_t)((double)fl->desired() * CoalSurplusPercent)); + if (PrintFLSStatistics > 1) { + gclog_or_tty->print("size[%d] : ", i); + } + fl->compute_desired(inter_sweep_current, inter_sweep_estimate, intra_sweep_estimate); + fl->set_coalDesired((ssize_t)((double)fl->desired() * CMSSmallCoalSurplusPercent)); fl->set_beforeSweep(fl->count()); fl->set_bfrSurp(fl->surplus()); } - _dictionary->beginSweepDictCensus(CoalSurplusPercent, + _dictionary->beginSweepDictCensus(CMSLargeCoalSurplusPercent, inter_sweep_current, - inter_sweep_estimate); + inter_sweep_estimate, + intra_sweep_estimate); } void CompactibleFreeListSpace::setFLSurplus() { @@ -2017,7 +2142,7 @@ for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { FreeList *fl = &_indexedFreeList[i]; fl->set_surplus(fl->count() - - (ssize_t)((double)fl->desired() * SplitSurplusPercent)); + (ssize_t)((double)fl->desired() * CMSSmallSplitSurplusPercent)); } } @@ -2048,6 +2173,11 @@ } void CompactibleFreeListSpace::endSweepFLCensus(size_t sweep_count) { + if (PrintFLSStatistics > 0) { + HeapWord* largestAddr = (HeapWord*) dictionary()->findLargestDict(); + gclog_or_tty->print_cr("CMS: Large block " PTR_FORMAT, + largestAddr); + } setFLSurplus(); setFLHints(); if (PrintGC && PrintFLSCensus > 0) { @@ -2055,7 +2185,7 @@ } clearFLCensus(); assert_locked(); - _dictionary->endSweepDictCensus(SplitSurplusPercent); + _dictionary->endSweepDictCensus(CMSLargeSplitSurplusPercent); } bool CompactibleFreeListSpace::coalOverPopulated(size_t size) { @@ -2312,13 +2442,18 @@ } void CompactibleFreeListSpace::verifyIndexedFreeList(size_t size) const { - FreeChunk* fc = _indexedFreeList[size].head(); + FreeChunk* fc = _indexedFreeList[size].head(); + FreeChunk* tail = _indexedFreeList[size].tail(); + size_t num = _indexedFreeList[size].count(); + size_t n = 0; guarantee((size % 2 == 0) || fc == NULL, "Odd slots should be empty"); - for (; fc != NULL; fc = fc->next()) { + for (; fc != NULL; fc = fc->next(), n++) { guarantee(fc->size() == size, "Size inconsistency"); guarantee(fc->isFree(), "!free?"); guarantee(fc->next() == NULL || fc->next()->prev() == fc, "Broken list"); + guarantee((fc->next() == NULL) == (fc == tail), "Incorrect tail"); } + guarantee(n == num, "Incorrect count"); } #ifndef PRODUCT @@ -2516,11 +2651,41 @@ _tracking = true; } -void PromotionInfo::stopTrackingPromotions() { +#define CMSPrintPromoBlockInfo 1 + +void PromotionInfo::stopTrackingPromotions(uint worker_id) { assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, "spooling inconsistency?"); _firstIndex = _nextIndex = 1; _tracking = false; + if (CMSPrintPromoBlockInfo > 1) { + print_statistics(worker_id); + } +} + +void PromotionInfo::print_statistics(uint worker_id) const { + assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, + "Else will undercount"); + assert(CMSPrintPromoBlockInfo > 0, "Else unnecessary call"); + // Count the number of blocks and slots in the free pool + size_t slots = 0; + size_t blocks = 0; + for (SpoolBlock* cur_spool = _spareSpool; + cur_spool != NULL; + cur_spool = cur_spool->nextSpoolBlock) { + // the first entry is just a self-pointer; indices 1 through + // bufferSize - 1 are occupied (thus, bufferSize - 1 slots). + guarantee((void*)cur_spool->displacedHdr == (void*)&cur_spool->displacedHdr, + "first entry of displacedHdr should be self-referential"); + slots += cur_spool->bufferSize - 1; + blocks++; + } + if (_spoolHead != NULL) { + slots += _spoolHead->bufferSize - 1; + blocks++; + } + gclog_or_tty->print_cr(" [worker %d] promo_blocks = %d, promo_slots = %d ", + worker_id, blocks, slots); } // When _spoolTail is not NULL, then the slot <_spoolTail, _nextIndex> @@ -2584,15 +2749,84 @@ guarantee(numDisplacedHdrs == numObjsWithDisplacedHdrs, "Displaced hdr count"); } +void PromotionInfo::print_on(outputStream* st) const { + SpoolBlock* curSpool = NULL; + size_t i = 0; + st->print_cr("start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")", + _firstIndex, _nextIndex); + for (curSpool = _spoolHead; curSpool != _spoolTail && curSpool != NULL; + curSpool = curSpool->nextSpoolBlock) { + curSpool->print_on(st); + st->print_cr(" active "); + i++; + } + for (curSpool = _spoolTail; curSpool != NULL; + curSpool = curSpool->nextSpoolBlock) { + curSpool->print_on(st); + st->print_cr(" inactive "); + i++; + } + for (curSpool = _spareSpool; curSpool != NULL; + curSpool = curSpool->nextSpoolBlock) { + curSpool->print_on(st); + st->print_cr(" free "); + i++; + } + st->print_cr(SIZE_FORMAT " header spooling blocks", i); +} + +void SpoolBlock::print_on(outputStream* st) const { + st->print("[" PTR_FORMAT "," PTR_FORMAT "), " SIZE_FORMAT " HeapWords -> " PTR_FORMAT, + this, (HeapWord*)displacedHdr + bufferSize, + bufferSize, nextSpoolBlock); +} + +/////////////////////////////////////////////////////////////////////////// +// CFLS_LAB +/////////////////////////////////////////////////////////////////////////// + +#define VECTOR_257(x) \ + /* 1 2 3 4 5 6 7 8 9 1x 11 12 13 14 15 16 17 18 19 2x 21 22 23 24 25 26 27 28 29 3x 31 32 */ \ + { x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, \ + x } + +// Initialize with default setting of CMSParPromoteBlocksToClaim, _not_ +// OldPLABSize, whose static default is different; if overridden at the +// command-line, this will get reinitialized via a call to +// modify_initialization() below. +AdaptiveWeightedAverage CFLS_LAB::_blocks_to_claim[] = + VECTOR_257(AdaptiveWeightedAverage(OldPLABWeight, (float)CMSParPromoteBlocksToClaim)); +size_t CFLS_LAB::_global_num_blocks[] = VECTOR_257(0); +int CFLS_LAB::_global_num_workers[] = VECTOR_257(0); CFLS_LAB::CFLS_LAB(CompactibleFreeListSpace* cfls) : _cfls(cfls) { - _blocks_to_claim = CMSParPromoteBlocksToClaim; + assert(CompactibleFreeListSpace::IndexSetSize == 257, "Modify VECTOR_257() macro above"); for (size_t i = CompactibleFreeListSpace::IndexSetStart; i < CompactibleFreeListSpace::IndexSetSize; i += CompactibleFreeListSpace::IndexSetStride) { _indexedFreeList[i].set_size(i); + _num_blocks[i] = 0; + } +} + +static bool _CFLS_LAB_modified = false; + +void CFLS_LAB::modify_initialization(size_t n, unsigned wt) { + assert(!_CFLS_LAB_modified, "Call only once"); + _CFLS_LAB_modified = true; + for (size_t i = CompactibleFreeListSpace::IndexSetStart; + i < CompactibleFreeListSpace::IndexSetSize; + i += CompactibleFreeListSpace::IndexSetStride) { + _blocks_to_claim[i].modify(n, wt, true /* force */); } } @@ -2607,11 +2841,9 @@ if (res == NULL) return NULL; } else { FreeList* fl = &_indexedFreeList[word_sz]; - bool filled = false; //TRAP if (fl->count() == 0) { - bool filled = true; //TRAP // Attempt to refill this local free list. - _cfls->par_get_chunk_of_blocks(word_sz, _blocks_to_claim, fl); + get_from_global_pool(word_sz, fl); // If it didn't work, give up. if (fl->count() == 0) return NULL; } @@ -2626,80 +2858,190 @@ return (HeapWord*)res; } -void CFLS_LAB::retire() { - for (size_t i = CompactibleFreeListSpace::IndexSetStart; +// Get a chunk of blocks of the right size and update related +// book-keeping stats +void CFLS_LAB::get_from_global_pool(size_t word_sz, FreeList* fl) { + // Get the #blocks we want to claim + size_t n_blks = (size_t)_blocks_to_claim[word_sz].average(); + assert(n_blks > 0, "Error"); + assert(ResizePLAB || n_blks == OldPLABSize, "Error"); + // In some cases, when the application has a phase change, + // there may be a sudden and sharp shift in the object survival + // profile, and updating the counts at the end of a scavenge + // may not be quick enough, giving rise to large scavenge pauses + // during these phase changes. It is beneficial to detect such + // changes on-the-fly during a scavenge and avoid such a phase-change + // pothole. The following code is a heuristic attempt to do that. + // It is protected by a product flag until we have gained + // enough experience with this heuristic and fine-tuned its behaviour. + // WARNING: This might increase fragmentation if we overreact to + // small spikes, so some kind of historical smoothing based on + // previous experience with the greater reactivity might be useful. + // Lacking sufficient experience, CMSOldPLABResizeQuicker is disabled by + // default. + if (ResizeOldPLAB && CMSOldPLABResizeQuicker) { + size_t multiple = _num_blocks[word_sz]/(CMSOldPLABToleranceFactor*CMSOldPLABNumRefills*n_blks); + n_blks += CMSOldPLABReactivityFactor*multiple*n_blks; + n_blks = MIN2(n_blks, CMSOldPLABMax); + } + assert(n_blks > 0, "Error"); + _cfls->par_get_chunk_of_blocks(word_sz, n_blks, fl); + // Update stats table entry for this block size + _num_blocks[word_sz] += fl->count(); +} + +void CFLS_LAB::compute_desired_plab_size() { + for (size_t i = CompactibleFreeListSpace::IndexSetStart; i < CompactibleFreeListSpace::IndexSetSize; i += CompactibleFreeListSpace::IndexSetStride) { - if (_indexedFreeList[i].count() > 0) { - MutexLockerEx x(_cfls->_indexedFreeListParLocks[i], - Mutex::_no_safepoint_check_flag); - _cfls->_indexedFreeList[i].prepend(&_indexedFreeList[i]); - // Reset this list. - _indexedFreeList[i] = FreeList(); - _indexedFreeList[i].set_size(i); + assert((_global_num_workers[i] == 0) == (_global_num_blocks[i] == 0), + "Counter inconsistency"); + if (_global_num_workers[i] > 0) { + // Need to smooth wrt historical average + if (ResizeOldPLAB) { + _blocks_to_claim[i].sample( + MAX2((size_t)CMSOldPLABMin, + MIN2((size_t)CMSOldPLABMax, + _global_num_blocks[i]/(_global_num_workers[i]*CMSOldPLABNumRefills)))); + } + // Reset counters for next round + _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()); + } } } } -void -CompactibleFreeListSpace:: -par_get_chunk_of_blocks(size_t word_sz, size_t n, FreeList* fl) { +void CFLS_LAB::retire(int tid) { + // We run this single threaded with the world stopped; + // so no need for locks and such. +#define CFLS_LAB_PARALLEL_ACCESS 0 + NOT_PRODUCT(Thread* t = Thread::current();) + assert(Thread::current()->is_VM_thread(), "Error"); + assert(CompactibleFreeListSpace::IndexSetStart == CompactibleFreeListSpace::IndexSetStride, + "Will access to uninitialized slot below"); +#if CFLS_LAB_PARALLEL_ACCESS + for (size_t i = CompactibleFreeListSpace::IndexSetSize - 1; + i > 0; + i -= CompactibleFreeListSpace::IndexSetStride) { +#else // CFLS_LAB_PARALLEL_ACCESS + for (size_t i = CompactibleFreeListSpace::IndexSetStart; + i < CompactibleFreeListSpace::IndexSetSize; + i += CompactibleFreeListSpace::IndexSetStride) { +#endif // !CFLS_LAB_PARALLEL_ACCESS + assert(_num_blocks[i] >= (size_t)_indexedFreeList[i].count(), + "Can't retire more than what we obtained"); + if (_num_blocks[i] > 0) { + size_t num_retire = _indexedFreeList[i].count(); + assert(_num_blocks[i] > num_retire, "Should have used at least one"); + { +#if CFLS_LAB_PARALLEL_ACCESS + MutexLockerEx x(_cfls->_indexedFreeListParLocks[i], + Mutex::_no_safepoint_check_flag); +#endif // CFLS_LAB_PARALLEL_ACCESS + // Update globals stats for num_blocks used + _global_num_blocks[i] += (_num_blocks[i] - num_retire); + _global_num_workers[i]++; + assert(_global_num_workers[i] <= (ssize_t)ParallelGCThreads, "Too big"); + if (num_retire > 0) { + _cfls->_indexedFreeList[i].prepend(&_indexedFreeList[i]); + // Reset this list. + _indexedFreeList[i] = FreeList(); + _indexedFreeList[i].set_size(i); + } + } + if (PrintOldPLAB) { + gclog_or_tty->print_cr("%d[%d]: %d/%d/%d", + tid, i, num_retire, _num_blocks[i], (size_t)_blocks_to_claim[i].average()); + } + // Reset stats for next round + _num_blocks[i] = 0; + } + } +} + +void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, FreeList* fl) { assert(fl->count() == 0, "Precondition."); assert(word_sz < CompactibleFreeListSpace::IndexSetSize, "Precondition"); - // We'll try all multiples of word_sz in the indexed set (starting with - // word_sz itself), then try getting a big chunk and splitting it. - int k = 1; - size_t cur_sz = k * word_sz; - bool found = false; - while (cur_sz < CompactibleFreeListSpace::IndexSetSize && k == 1) { - FreeList* gfl = &_indexedFreeList[cur_sz]; - FreeList fl_for_cur_sz; // Empty. - fl_for_cur_sz.set_size(cur_sz); - { - MutexLockerEx x(_indexedFreeListParLocks[cur_sz], - Mutex::_no_safepoint_check_flag); - if (gfl->count() != 0) { - size_t nn = MAX2(n/k, (size_t)1); - gfl->getFirstNChunksFromList(nn, &fl_for_cur_sz); - found = true; + // We'll try all multiples of word_sz in the indexed set, starting with + // word_sz itself and, if CMSSplitIndexedFreeListBlocks, try larger multiples, + // then try getting a big chunk and splitting it. + { + bool found; + int k; + size_t cur_sz; + for (k = 1, cur_sz = k * word_sz, found = false; + (cur_sz < CompactibleFreeListSpace::IndexSetSize) && + (CMSSplitIndexedFreeListBlocks || k <= 1); + k++, cur_sz = k * word_sz) { + FreeList* gfl = &_indexedFreeList[cur_sz]; + FreeList fl_for_cur_sz; // Empty. + fl_for_cur_sz.set_size(cur_sz); + { + MutexLockerEx x(_indexedFreeListParLocks[cur_sz], + Mutex::_no_safepoint_check_flag); + if (gfl->count() != 0) { + // nn is the number of chunks of size cur_sz that + // we'd need to split k-ways each, in order to create + // "n" chunks of size word_sz each. + const size_t nn = MAX2(n/k, (size_t)1); + gfl->getFirstNChunksFromList(nn, &fl_for_cur_sz); + found = true; + if (k > 1) { + // Update split death stats for the cur_sz-size blocks list: + // we increment the split death count by the number of blocks + // we just took from the cur_sz-size blocks list and which + // we will be splitting below. + ssize_t deaths = _indexedFreeList[cur_sz].splitDeaths() + + fl_for_cur_sz.count(); + _indexedFreeList[cur_sz].set_splitDeaths(deaths); + } + } + } + // Now transfer fl_for_cur_sz to fl. Common case, we hope, is k = 1. + if (found) { + if (k == 1) { + fl->prepend(&fl_for_cur_sz); + } else { + // Divide each block on fl_for_cur_sz up k ways. + FreeChunk* fc; + while ((fc = fl_for_cur_sz.getChunkAtHead()) != NULL) { + // Must do this in reverse order, so that anybody attempting to + // access the main chunk sees it as a single free block until we + // change it. + size_t fc_size = fc->size(); + for (int i = k-1; i >= 0; i--) { + FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz); + ffc->setSize(word_sz); + ffc->linkNext(NULL); + ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + // Above must occur before BOT is updated below. + // splitting from the right, fc_size == (k - i + 1) * wordsize + _bt.mark_block((HeapWord*)ffc, word_sz); + fc_size -= word_sz; + _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size()); + _bt.verify_single_block((HeapWord*)fc, fc_size); + _bt.verify_single_block((HeapWord*)ffc, ffc->size()); + // Push this on "fl". + fl->returnChunkAtHead(ffc); + } + // TRAP + assert(fl->tail()->next() == NULL, "List invariant."); + } + } + // Update birth stats for this block size. + size_t num = fl->count(); + MutexLockerEx x(_indexedFreeListParLocks[word_sz], + Mutex::_no_safepoint_check_flag); + ssize_t births = _indexedFreeList[word_sz].splitBirths() + num; + _indexedFreeList[word_sz].set_splitBirths(births); + return; } } - // Now transfer fl_for_cur_sz to fl. Common case, we hope, is k = 1. - if (found) { - if (k == 1) { - fl->prepend(&fl_for_cur_sz); - } else { - // Divide each block on fl_for_cur_sz up k ways. - FreeChunk* fc; - while ((fc = fl_for_cur_sz.getChunkAtHead()) != NULL) { - // Must do this in reverse order, so that anybody attempting to - // access the main chunk sees it as a single free block until we - // change it. - size_t fc_size = fc->size(); - for (int i = k-1; i >= 0; i--) { - FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz); - ffc->setSize(word_sz); - ffc->linkNext(NULL); - ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. - // Above must occur before BOT is updated below. - // splitting from the right, fc_size == (k - i + 1) * wordsize - _bt.mark_block((HeapWord*)ffc, word_sz); - fc_size -= word_sz; - _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size()); - _bt.verify_single_block((HeapWord*)fc, fc_size); - _bt.verify_single_block((HeapWord*)ffc, ffc->size()); - // Push this on "fl". - fl->returnChunkAtHead(ffc); - } - // TRAP - assert(fl->tail()->next() == NULL, "List invariant."); - } - } - return; - } - k++; cur_sz = k * word_sz; } // Otherwise, we'll split a block from the dictionary. FreeChunk* fc = NULL; @@ -2723,17 +3065,31 @@ } } if (fc == NULL) return; + assert((ssize_t)n >= 1, "Control point invariant"); // Otherwise, split up that block. - size_t nn = fc->size() / word_sz; + const size_t nn = fc->size() / word_sz; n = MIN2(nn, n); + assert((ssize_t)n >= 1, "Control point invariant"); rem = fc->size() - n * word_sz; // If there is a remainder, and it's too small, allocate one fewer. if (rem > 0 && rem < MinChunkSize) { n--; rem += word_sz; } + // Note that at this point we may have n == 0. + assert((ssize_t)n >= 0, "Control point invariant"); + + // If n is 0, the chunk fc that was found is not large + // enough to leave a viable remainder. We are unable to + // allocate even one block. Return fc to the + // dictionary and return, leaving "fl" empty. + if (n == 0) { + returnChunkToDictionary(fc); + return; + } + // First return the remainder, if any. // Note that we hold the lock until we decide if we're going to give - // back the remainder to the dictionary, since a contending allocator + // back the remainder to the dictionary, since a concurrent allocation // may otherwise see the heap as empty. (We're willing to take that // hit if the block is a small block.) if (rem > 0) { @@ -2743,18 +3099,16 @@ rem_fc->linkNext(NULL); rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. // Above must occur before BOT is updated below. + assert((ssize_t)n > 0 && prefix_size > 0 && rem_fc > fc, "Error"); _bt.split_block((HeapWord*)fc, fc->size(), prefix_size); if (rem >= IndexSetSize) { returnChunkToDictionary(rem_fc); - dictionary()->dictCensusUpdate(fc->size(), - true /*split*/, - true /*birth*/); + dictionary()->dictCensusUpdate(rem, true /*split*/, true /*birth*/); rem_fc = NULL; } // Otherwise, return it to the small list below. } } - // if (rem_fc != NULL) { MutexLockerEx x(_indexedFreeListParLocks[rem], Mutex::_no_safepoint_check_flag); @@ -2762,7 +3116,7 @@ _indexedFreeList[rem].returnChunkAtHead(rem_fc); smallSplitBirth(rem); } - + assert((ssize_t)n > 0 && fc != NULL, "Consistency"); // Now do the splitting up. // Must do this in reverse order, so that anybody attempting to // access the main chunk sees it as a single free block until we @@ -2792,13 +3146,15 @@ _bt.verify_single_block((HeapWord*)fc, fc->size()); fl->returnChunkAtHead(fc); + assert((ssize_t)n > 0 && (ssize_t)n == fl->count(), "Incorrect number of blocks"); { + // Update the stats for this block size. MutexLockerEx x(_indexedFreeListParLocks[word_sz], Mutex::_no_safepoint_check_flag); - ssize_t new_births = _indexedFreeList[word_sz].splitBirths() + n; - _indexedFreeList[word_sz].set_splitBirths(new_births); - ssize_t new_surplus = _indexedFreeList[word_sz].surplus() + n; - _indexedFreeList[word_sz].set_surplus(new_surplus); + const ssize_t births = _indexedFreeList[word_sz].splitBirths() + n; + _indexedFreeList[word_sz].set_splitBirths(births); + // ssize_t new_surplus = _indexedFreeList[word_sz].surplus() + n; + // _indexedFreeList[word_sz].set_surplus(new_surplus); } // TRAP diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,8 +25,6 @@ // Classes in support of keeping track of promotions into a non-Contiguous // space, in this case a CompactibleFreeListSpace. -#define CFLS_LAB_REFILL_STATS 0 - // Forward declarations class CompactibleFreeListSpace; class BlkClosure; @@ -89,6 +87,9 @@ displacedHdr = (markOop*)&displacedHdr; nextSpoolBlock = NULL; } + + void print_on(outputStream* st) const; + void print() const { print_on(gclog_or_tty); } }; class PromotionInfo VALUE_OBJ_CLASS_SPEC { @@ -121,7 +122,7 @@ return _promoHead == NULL; } void startTrackingPromotions(); - void stopTrackingPromotions(); + void stopTrackingPromotions(uint worker_id = 0); bool tracking() const { return _tracking; } void track(PromotedObject* trackOop); // keep track of a promoted oop // The following variant must be used when trackOop is not fully @@ -161,6 +162,9 @@ _nextIndex = 0; } + + void print_on(outputStream* st) const; + void print_statistics(uint worker_id) const; }; class LinearAllocBlock VALUE_OBJ_CLASS_SPEC { @@ -243,6 +247,7 @@ mutable Mutex _freelistLock; // locking verifier convenience function void assert_locked() const PRODUCT_RETURN; + void assert_locked(const Mutex* lock) const PRODUCT_RETURN; // Linear allocation blocks LinearAllocBlock _smallLinearAllocBlock; @@ -281,13 +286,6 @@ // Locks protecting the exact lists during par promotion allocation. Mutex* _indexedFreeListParLocks[IndexSetSize]; -#if CFLS_LAB_REFILL_STATS - // Some statistics. - jint _par_get_chunk_from_small; - jint _par_get_chunk_from_large; -#endif - - // Attempt to obtain up to "n" blocks of the size "word_sz" (which is // required to be smaller than "IndexSetSize".) If successful, // adds them to "fl", which is required to be an empty free list. @@ -320,7 +318,7 @@ // Helper function for getChunkFromIndexedFreeList. // Replenish the indexed free list for this "size". Do not take from an // underpopulated size. - FreeChunk* getChunkFromIndexedFreeListHelper(size_t size); + FreeChunk* getChunkFromIndexedFreeListHelper(size_t size, bool replenish = true); // Get a chunk from the indexed free list. If the indexed free list // does not have a free chunk, try to replenish the indexed free list @@ -430,10 +428,6 @@ void initialize_sequential_subtasks_for_marking(int n_threads, HeapWord* low = NULL); -#if CFLS_LAB_REFILL_STATS - void print_par_alloc_stats(); -#endif - // Space enquiries size_t used() const; size_t free() const; @@ -617,6 +611,12 @@ // Do some basic checks on the the free lists. void checkFreeListConsistency() const PRODUCT_RETURN; + // Printing support + void dump_at_safepoint_with_locks(CMSCollector* c, outputStream* st); + void print_indexed_free_lists(outputStream* st) const; + void print_dictionary_free_lists(outputStream* st) const; + void print_promo_info_blocks(outputStream* st) const; + NOT_PRODUCT ( void initializeIndexedFreeListArrayReturnedBytes(); size_t sumIndexedFreeListArrayReturnedBytes(); @@ -638,8 +638,9 @@ // Statistics functions // Initialize census for lists before the sweep. - void beginSweepFLCensus(float sweep_current, - float sweep_estimate); + void beginSweepFLCensus(float inter_sweep_current, + float inter_sweep_estimate, + float intra_sweep_estimate); // Set the surplus for each of the free lists. void setFLSurplus(); // Set the hint for each of the free lists. @@ -730,16 +731,17 @@ FreeList _indexedFreeList[CompactibleFreeListSpace::IndexSetSize]; // Initialized from a command-line arg. - size_t _blocks_to_claim; -#if CFLS_LAB_REFILL_STATS - // Some statistics. - int _refills; - int _blocksTaken; - static int _tot_refills; - static int _tot_blocksTaken; - static int _next_threshold; -#endif + // Allocation statistics in support of dynamic adjustment of + // #blocks to claim per get_from_global_pool() call below. + static AdaptiveWeightedAverage + _blocks_to_claim [CompactibleFreeListSpace::IndexSetSize]; + static size_t _global_num_blocks [CompactibleFreeListSpace::IndexSetSize]; + static int _global_num_workers[CompactibleFreeListSpace::IndexSetSize]; + size_t _num_blocks [CompactibleFreeListSpace::IndexSetSize]; + + // Internal work method + void get_from_global_pool(size_t word_sz, FreeList* fl); public: CFLS_LAB(CompactibleFreeListSpace* cfls); @@ -748,7 +750,12 @@ HeapWord* alloc(size_t word_sz); // Return any unused portions of the buffer to the global pool. - void retire(); + void retire(int tid); + + // Dynamic OldPLABSize sizing + static void compute_desired_plab_size(); + // When the settings are modified from default static initialization + static void modify_initialization(size_t n, unsigned wt); }; size_t PromotionInfo::refillSize() const { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -253,7 +253,6 @@ } } - void ConcurrentMarkSweepGeneration::ref_processor_init() { assert(collector() != NULL, "no collector"); collector()->ref_processor_init(); @@ -341,6 +340,14 @@ _icms_duty_cycle = CMSIncrementalDutyCycle; } +double CMSStats::cms_free_adjustment_factor(size_t free) const { + // TBD: CR 6909490 + return 1.0; +} + +void CMSStats::adjust_cms_free_adjustment_factor(bool fail, size_t free) { +} + // If promotion failure handling is on use // the padded average size of the promotion for each // young generation collection. @@ -361,7 +368,11 @@ // Adjust by the safety factor. double cms_free_dbl = (double)cms_free; - cms_free_dbl = cms_free_dbl * (100.0 - CMSIncrementalSafetyFactor) / 100.0; + double cms_adjustment = (100.0 - CMSIncrementalSafetyFactor)/100.0; + // Apply a further correction factor which tries to adjust + // for recent occurance of concurrent mode failures. + cms_adjustment = cms_adjustment * cms_free_adjustment_factor(cms_free); + cms_free_dbl = cms_free_dbl * cms_adjustment; if (PrintGCDetails && Verbose) { gclog_or_tty->print_cr("CMSStats::time_until_cms_gen_full: cms_free " @@ -395,6 +406,8 @@ // late. double work = cms_duration() + gc0_period(); double deadline = time_until_cms_gen_full(); + // If a concurrent mode failure occurred recently, we want to be + // more conservative and halve our expected time_until_cms_gen_full() if (work > deadline) { if (Verbose && PrintGCDetails) { gclog_or_tty->print( @@ -556,7 +569,8 @@ _should_unload_classes(false), _concurrent_cycles_since_last_unload(0), _roots_scanning_options(0), - _sweep_estimate(CMS_SweepWeight, CMS_SweepPadding) + _inter_sweep_estimate(CMS_SweepWeight, CMS_SweepPadding), + _intra_sweep_estimate(CMS_SweepWeight, CMS_SweepPadding) { if (ExplicitGCInvokesConcurrentAndUnloadsClasses) { ExplicitGCInvokesConcurrent = true; @@ -592,7 +606,7 @@ assert(_modUnionTable.covers(_span), "_modUnionTable inconsistency?"); } - if (!_markStack.allocate(CMSMarkStackSize)) { + if (!_markStack.allocate(MarkStackSize)) { warning("Failed to allocate CMS Marking Stack"); return; } @@ -603,13 +617,13 @@ // Support for multi-threaded concurrent phases if (ParallelGCThreads > 0 && CMSConcurrentMTEnabled) { - if (FLAG_IS_DEFAULT(ParallelCMSThreads)) { + if (FLAG_IS_DEFAULT(ConcGCThreads)) { // just for now - FLAG_SET_DEFAULT(ParallelCMSThreads, (ParallelGCThreads + 3)/4); - } - if (ParallelCMSThreads > 1) { + FLAG_SET_DEFAULT(ConcGCThreads, (ParallelGCThreads + 3)/4); + } + if (ConcGCThreads > 1) { _conc_workers = new YieldingFlexibleWorkGang("Parallel CMS Threads", - ParallelCMSThreads, true); + ConcGCThreads, true); if (_conc_workers == NULL) { warning("GC/CMS: _conc_workers allocation failure: " "forcing -CMSConcurrentMTEnabled"); @@ -620,13 +634,13 @@ } } if (!CMSConcurrentMTEnabled) { - ParallelCMSThreads = 0; + ConcGCThreads = 0; } else { // Turn off CMSCleanOnEnter optimization temporarily for // the MT case where it's not fixed yet; see 6178663. CMSCleanOnEnter = false; } - assert((_conc_workers != NULL) == (ParallelCMSThreads > 1), + assert((_conc_workers != NULL) == (ConcGCThreads > 1), "Inconsistency"); // Parallel task queues; these are shared for the @@ -634,7 +648,7 @@ // are not shared with parallel scavenge (ParNew). { uint i; - uint num_queues = (uint) MAX2(ParallelGCThreads, ParallelCMSThreads); + uint num_queues = (uint) MAX2(ParallelGCThreads, ConcGCThreads); if ((CMSParallelRemarkEnabled || CMSConcurrentMTEnabled || ParallelRefProcEnabled) @@ -709,7 +723,9 @@ // Support for parallelizing survivor space rescan if (CMSParallelRemarkEnabled && CMSParallelSurvivorRemarkEnabled) { - size_t max_plab_samples = MaxNewSize/((SurvivorRatio+2)*MinTLABSize); + const size_t max_plab_samples = + ((DefNewGeneration*)_young_gen)->max_survivor_size()/MinTLABSize; + _survivor_plab_array = NEW_C_HEAP_ARRAY(ChunkArray, ParallelGCThreads); _survivor_chunk_array = NEW_C_HEAP_ARRAY(HeapWord*, 2*max_plab_samples); _cursor = NEW_C_HEAP_ARRAY(size_t, ParallelGCThreads); @@ -772,7 +788,7 @@ NOT_PRODUCT(_overflow_counter = CMSMarkStackOverflowInterval;) _gc_counters = new CollectorCounters("CMS", 1); _completed_initialization = true; - _sweep_timer.start(); // start of time + _inter_sweep_timer.start(); // start of time } const char* ConcurrentMarkSweepGeneration::name() const { @@ -899,6 +915,14 @@ return result; } +// At a promotion failure dump information on block layout in heap +// (cms old generation). +void ConcurrentMarkSweepGeneration::promotion_failure_occurred() { + if (CMSDumpAtPromotionFailure) { + cmsSpace()->dump_at_safepoint_with_locks(collector(), gclog_or_tty); + } +} + CompactibleSpace* ConcurrentMarkSweepGeneration::first_compaction_space() const { return _cmsSpace; @@ -1367,12 +1391,7 @@ ConcurrentMarkSweepGeneration:: par_promote_alloc_done(int thread_num) { CMSParGCThreadState* ps = _par_gc_thread_states[thread_num]; - ps->lab.retire(); -#if CFLS_LAB_REFILL_STATS - if (thread_num == 0) { - _cmsSpace->print_par_alloc_stats(); - } -#endif + ps->lab.retire(thread_num); } void @@ -1973,11 +1992,14 @@ // We must adjust the allocation statistics being maintained // in the free list space. We do so by reading and clearing // the sweep timer and updating the block flux rate estimates below. - assert(_sweep_timer.is_active(), "We should never see the timer inactive"); - _sweep_timer.stop(); - // Note that we do not use this sample to update the _sweep_estimate. - _cmsGen->cmsSpace()->beginSweepFLCensus((float)(_sweep_timer.seconds()), - _sweep_estimate.padded_average()); + assert(!_intra_sweep_timer.is_active(), "_intra_sweep_timer should be inactive"); + if (_inter_sweep_timer.is_active()) { + _inter_sweep_timer.stop(); + // Note that we do not use this sample to update the _inter_sweep_estimate. + _cmsGen->cmsSpace()->beginSweepFLCensus((float)(_inter_sweep_timer.seconds()), + _inter_sweep_estimate.padded_average(), + _intra_sweep_estimate.padded_average()); + } GenMarkSweep::invoke_at_safepoint(_cmsGen->level(), ref_processor(), clear_all_soft_refs); @@ -2014,10 +2036,10 @@ } // Adjust the per-size allocation stats for the next epoch. - _cmsGen->cmsSpace()->endSweepFLCensus(sweepCount() /* fake */); - // Restart the "sweep timer" for next epoch. - _sweep_timer.reset(); - _sweep_timer.start(); + _cmsGen->cmsSpace()->endSweepFLCensus(sweep_count() /* fake */); + // Restart the "inter sweep timer" for the next epoch. + _inter_sweep_timer.reset(); + _inter_sweep_timer.start(); // Sample collection pause time and reset for collection interval. if (UseAdaptiveSizePolicy) { @@ -2276,7 +2298,7 @@ VM_CMS_Final_Remark final_remark_op(this); VMThread::execute(&final_remark_op); - } + } assert(_foregroundGCShouldWait, "block post-condition"); break; case Sweeping: @@ -2675,7 +2697,7 @@ // Also reset promotion tracking in par gc thread states. if (ParallelGCThreads > 0) { for (uint i = 0; i < ParallelGCThreads; i++) { - _par_gc_thread_states[i]->promo.stopTrackingPromotions(); + _par_gc_thread_states[i]->promo.stopTrackingPromotions(i); } } } @@ -2770,7 +2792,7 @@ bool do_bit(size_t offset) { HeapWord* addr = _marks->offsetToHeapWord(offset); if (!_marks->isMarked(addr)) { - oop(addr)->print(); + oop(addr)->print_on(gclog_or_tty); gclog_or_tty->print_cr(" ("INTPTR_FORMAT" should have been marked)", addr); _failed = true; } @@ -2819,7 +2841,7 @@ // Clear any marks from a previous round verification_mark_bm()->clear_all(); assert(verification_mark_stack()->isEmpty(), "markStack should be empty"); - assert(overflow_list_is_empty(), "overflow list should be empty"); + verify_work_stacks_empty(); GenCollectedHeap* gch = GenCollectedHeap::heap(); gch->ensure_parsability(false); // fill TLABs, but no need to retire them @@ -2852,14 +2874,17 @@ GenCollectedHeap* gch = GenCollectedHeap::heap(); // Mark from roots one level into CMS - MarkRefsIntoClosure notOlder(_span, verification_mark_bm(), true /* nmethods */); + MarkRefsIntoClosure notOlder(_span, verification_mark_bm()); gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->gen_process_strong_roots(_cmsGen->level(), true, // younger gens are roots + true, // activate StrongRootsScope true, // collecting perm gen SharedHeap::ScanningOption(roots_scanning_options()), - NULL, ¬Older); + ¬Older, + true, // walk code active on stacks + NULL); // Now mark from the roots assert(_revisitStack.isEmpty(), "Should be empty"); @@ -2889,8 +2914,8 @@ verification_mark_bm()->iterate(&vcl); if (vcl.failed()) { gclog_or_tty->print("Verification failed"); - Universe::heap()->print(); - fatal(" ... aborting"); + Universe::heap()->print_on(gclog_or_tty); + fatal("CMS: failed marking verification after remark"); } } @@ -2901,13 +2926,16 @@ // Mark from roots one level into CMS MarkRefsIntoVerifyClosure notOlder(_span, verification_mark_bm(), - markBitMap(), true /* nmethods */); + markBitMap()); gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->gen_process_strong_roots(_cmsGen->level(), true, // younger gens are roots + true, // activate StrongRootsScope true, // collecting perm gen SharedHeap::ScanningOption(roots_scanning_options()), - NULL, ¬Older); + ¬Older, + true, // walk code active on stacks + NULL); // Now mark from the roots assert(_revisitStack.isEmpty(), "Should be empty"); @@ -3307,7 +3335,7 @@ Universe::heap()->barrier_set()->resize_covered_region(mr); // Hmmmm... why doesn't CFLS::set_end verify locking? // This is quite ugly; FIX ME XXX - _cmsSpace->assert_locked(); + _cmsSpace->assert_locked(freelistLock()); _cmsSpace->set_end((HeapWord*)_virtual_space.high()); // update the space and generation capacity counters @@ -3484,8 +3512,10 @@ FalseClosure falseClosure; // In the case of a synchronous collection, we will elide the // remark step, so it's important to catch all the nmethod oops - // in this step; hence the last argument to the constrcutor below. - MarkRefsIntoClosure notOlder(_span, &_markBitMap, !asynch /* nmethods */); + // in this step. + // The final 'true' flag to gen_process_strong_roots will ensure this. + // If 'async' is true, we can relax the nmethod tracing. + MarkRefsIntoClosure notOlder(_span, &_markBitMap); GenCollectedHeap* gch = GenCollectedHeap::heap(); verify_work_stacks_empty(); @@ -3499,13 +3529,17 @@ ref_processor()->set_enqueuing_is_done(false); { + // This is not needed. DEBUG_ONLY(RememberKlassesChecker imx(true);) COMPILER2_PRESENT(DerivedPointerTableDeactivate dpt_deact;) gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. gch->gen_process_strong_roots(_cmsGen->level(), true, // younger gens are roots + true, // activate StrongRootsScope true, // collecting perm gen SharedHeap::ScanningOption(roots_scanning_options()), - NULL, ¬Older); + ¬Older, + true, // walk all of code cache if (so & SO_CodeCache) + NULL); } // Clear mod-union table; it will be dirtied in the prologue of @@ -3622,9 +3656,9 @@ verify_work_stacks_empty(); verify_overflow_empty(); assert(_revisitStack.isEmpty(), "tabula rasa"); - + DEBUG_ONLY(RememberKlassesChecker cmx(should_unload_classes());) bool result = false; - if (CMSConcurrentMTEnabled && ParallelCMSThreads > 0) { + if (CMSConcurrentMTEnabled && ConcGCThreads > 0) { result = do_marking_mt(asynch); } else { result = do_marking_st(asynch); @@ -3958,24 +3992,24 @@ pst->all_tasks_completed(); } -class Par_ConcMarkingClosure: public OopClosure { +class Par_ConcMarkingClosure: public Par_KlassRememberingOopClosure { private: - CMSCollector* _collector; MemRegion _span; CMSBitMap* _bit_map; CMSMarkStack* _overflow_stack; - CMSMarkStack* _revisit_stack; // XXXXXX Check proper use OopTaskQueue* _work_queue; protected: DO_OOP_WORK_DEFN public: Par_ConcMarkingClosure(CMSCollector* collector, OopTaskQueue* work_queue, - CMSBitMap* bit_map, CMSMarkStack* overflow_stack): - _collector(collector), + CMSBitMap* bit_map, CMSMarkStack* overflow_stack, + CMSMarkStack* revisit_stack): + Par_KlassRememberingOopClosure(collector, NULL, revisit_stack), _span(_collector->_span), _work_queue(work_queue), _bit_map(bit_map), - _overflow_stack(overflow_stack) { } // need to initialize revisit stack etc. + _overflow_stack(overflow_stack) + { } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); void trim_queue(size_t max); @@ -4063,8 +4097,9 @@ oop obj_to_scan; CMSBitMap* bm = &(_collector->_markBitMap); CMSMarkStack* ovflw = &(_collector->_markStack); + CMSMarkStack* revisit = &(_collector->_revisitStack); int* seed = _collector->hash_seed(i); - Par_ConcMarkingClosure cl(_collector, work_q, bm, ovflw); + Par_ConcMarkingClosure cl(_collector, work_q, bm, ovflw, revisit); while (true) { cl.trim_queue(0); assert(work_q->size() == 0, "Should have been emptied above"); @@ -4088,7 +4123,7 @@ void CMSConcMarkingTask::coordinator_yield() { assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), "CMS thread should hold CMS token"); - + DEBUG_ONLY(RememberKlassesChecker mux(false);) // First give up the locks, then yield, then re-lock // We should probably use a constructor/destructor idiom to // do this unlock/lock or modify the MutexUnlocker class to @@ -4140,10 +4175,10 @@ } bool CMSCollector::do_marking_mt(bool asynch) { - assert(ParallelCMSThreads > 0 && conc_workers() != NULL, "precondition"); + assert(ConcGCThreads > 0 && conc_workers() != NULL, "precondition"); // In the future this would be determined ergonomically, based // on #cpu's, # active mutator threads (and load), and mutation rate. - int num_workers = ParallelCMSThreads; + int num_workers = ConcGCThreads; CompactibleFreeListSpace* cms_space = _cmsGen->cmsSpace(); CompactibleFreeListSpace* perm_space = _permGen->cmsSpace(); @@ -4164,7 +4199,7 @@ // Mutate the Refs discovery so it is MT during the // multi-threaded marking phase. ReferenceProcessorMTMutator mt(ref_processor(), num_workers > 1); - + DEBUG_ONLY(RememberKlassesChecker cmx(should_unload_classes());) conc_workers()->start_task(&tsk); while (tsk.yielded()) { tsk.coordinator_yield(); @@ -4404,7 +4439,8 @@ CMSPrecleanRefsYieldClosure yield_cl(this); assert(rp->span().equals(_span), "Spans should be equal"); CMSKeepAliveClosure keep_alive(this, _span, &_markBitMap, - &_markStack, true /* preclean */); + &_markStack, &_revisitStack, + true /* preclean */); CMSDrainMarkingStackClosure complete_trace(this, _span, &_markBitMap, &_markStack, &keep_alive, true /* preclean */); @@ -4424,6 +4460,7 @@ bitMapLock()); startTimer(); sample_eden(); + // The following will yield to allow foreground // collection to proceed promptly. XXX YSR: // The code in this method may need further @@ -4431,7 +4468,7 @@ // for cleaner interfaces. rp->preclean_discovered_references( rp->is_alive_non_header(), &keep_alive, &complete_trace, - &yield_cl); + &yield_cl, should_unload_classes()); } if (clean_survivor) { // preclean the active survivor space(s) @@ -4453,6 +4490,7 @@ SurvivorSpacePrecleanClosure sss_cl(this, _span, &_markBitMap, &_markStack, &pam_cl, before_count, CMSYield); + DEBUG_ONLY(RememberKlassesChecker mx(should_unload_classes());) dng->from()->object_iterate_careful(&sss_cl); dng->to()->object_iterate_careful(&sss_cl); } @@ -4554,6 +4592,13 @@ verify_work_stacks_empty(); verify_overflow_empty(); + // Turn off checking for this method but turn it back on + // selectively. There are yield points in this method + // but it is difficult to turn the checking off just around + // the yield points. It is simpler to selectively turn + // it on. + DEBUG_ONLY(RememberKlassesChecker mux(false);) + // strategy: starting with the first card, accumulate contiguous // ranges of dirty cards; clear these cards, then scan the region // covered by these cards. @@ -4582,6 +4627,7 @@ MemRegion dirtyRegion; { stopTimer(); + // Potential yield point CMSTokenSync ts(true); startTimer(); sample_eden(); @@ -4607,6 +4653,7 @@ assert(numDirtyCards > 0, "consistency check"); HeapWord* stop_point = NULL; stopTimer(); + // Potential yield point CMSTokenSyncWithLocks ts(true, gen->freelistLock(), bitMapLock()); startTimer(); @@ -4614,6 +4661,7 @@ verify_work_stacks_empty(); verify_overflow_empty(); sample_eden(); + DEBUG_ONLY(RememberKlassesChecker mx(should_unload_classes());) stop_point = gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl); } @@ -4701,6 +4749,7 @@ sample_eden(); verify_work_stacks_empty(); verify_overflow_empty(); + DEBUG_ONLY(RememberKlassesChecker mx(should_unload_classes());) HeapWord* stop_point = gen->cmsSpace()->object_iterate_careful_m(dirtyRegion, cl); if (stop_point != NULL) { @@ -4800,6 +4849,7 @@ assert(haveFreelistLocks(), "must have free list locks"); assert_lock_strong(bitMapLock()); + DEBUG_ONLY(RememberKlassesChecker fmx(should_unload_classes());) if (!init_mark_was_synchronous) { // We might assume that we need not fill TLAB's when // CMSScavengeBeforeRemark is set, because we may have just done @@ -4903,6 +4953,9 @@ _markStack._hit_limit = 0; _markStack._failed_double = 0; + // Check that all the klasses have been checked + assert(_revisitStack.isEmpty(), "Not all klasses revisited"); + if ((VerifyAfterGC || VerifyDuringGC) && GenCollectedHeap::heap()->total_collections() >= VerifyGCStartAt) { verify_after_remark(); @@ -5015,9 +5068,15 @@ _timer.start(); gch->gen_process_strong_roots(_collector->_cmsGen->level(), false, // yg was scanned above + false, // this is parallel code true, // collecting perm gen SharedHeap::ScanningOption(_collector->CMSCollector::roots_scanning_options()), - NULL, &par_mrias_cl); + &par_mrias_cl, + true, // walk all of code cache if (so & SO_CodeCache) + NULL); + assert(_collector->should_unload_classes() + || (_collector->CMSCollector::roots_scanning_options() & SharedHeap::SO_CodeCache), + "if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops"); _timer.stop(); if (PrintCMSStatistics != 0) { gclog_or_tty->print_cr( @@ -5398,7 +5457,6 @@ // Set up for parallel process_strong_roots work. gch->set_par_threads(n_workers); - gch->change_strong_roots_parity(); // We won't be iterating over the cards in the card table updating // the younger_gen cards, so we shouldn't call the following else // the verification code as well as subsequent younger_refs_iterate @@ -5429,8 +5487,10 @@ if (n_workers > 1) { // Make refs discovery MT-safe ReferenceProcessorMTMutator mt(ref_processor(), true); + GenCollectedHeap::StrongRootsScope srs(gch); workers->run_task(&tsk); } else { + GenCollectedHeap::StrongRootsScope srs(gch); tsk.work(0); } gch->set_par_threads(0); // 0 ==> non-parallel. @@ -5514,11 +5574,18 @@ verify_work_stacks_empty(); gch->rem_set()->prepare_for_younger_refs_iterate(false); // Not parallel. + GenCollectedHeap::StrongRootsScope srs(gch); gch->gen_process_strong_roots(_cmsGen->level(), true, // younger gens as roots + false, // use the local StrongRootsScope true, // collecting perm gen SharedHeap::ScanningOption(roots_scanning_options()), - NULL, &mrias_cl); + &mrias_cl, + true, // walk code active on stacks + NULL); + assert(should_unload_classes() + || (roots_scanning_options() & SharedHeap::SO_CodeCache), + "if we didn't scan the code cache, we have to be ready to drop nmethods with expired weak oops"); } verify_work_stacks_empty(); // Restore evacuated mark words, if any, used for overflow list links @@ -5574,9 +5641,13 @@ void CMSRefProcTaskProxy::work(int i) { assert(_collector->_span.equals(_span), "Inconsistency in _span"); CMSParKeepAliveClosure par_keep_alive(_collector, _span, - _mark_bit_map, work_queue(i)); + _mark_bit_map, + &_collector->_revisitStack, + work_queue(i)); CMSParDrainMarkingStackClosure par_drain_stack(_collector, _span, - _mark_bit_map, work_queue(i)); + _mark_bit_map, + &_collector->_revisitStack, + work_queue(i)); CMSIsAliveClosure is_alive_closure(_span, _mark_bit_map); _task.work(i, is_alive_closure, par_keep_alive, par_drain_stack); if (_task.marks_oops_alive()) { @@ -5604,12 +5675,13 @@ }; CMSParKeepAliveClosure::CMSParKeepAliveClosure(CMSCollector* collector, - MemRegion span, CMSBitMap* bit_map, OopTaskQueue* work_queue): - _collector(collector), + MemRegion span, CMSBitMap* bit_map, CMSMarkStack* revisit_stack, + OopTaskQueue* work_queue): + Par_KlassRememberingOopClosure(collector, NULL, revisit_stack), _span(span), _bit_map(bit_map), _work_queue(work_queue), - _mark_and_push(collector, span, bit_map, work_queue), + _mark_and_push(collector, span, bit_map, revisit_stack, work_queue), _low_water_mark(MIN2((uint)(work_queue->max_elems()/4), (uint)(CMSWorkQueueDrainThreshold * ParallelGCThreads))) { } @@ -5696,7 +5768,8 @@ verify_work_stacks_empty(); CMSKeepAliveClosure cmsKeepAliveClosure(this, _span, &_markBitMap, - &_markStack, false /* !preclean */); + &_markStack, &_revisitStack, + false /* !preclean */); CMSDrainMarkingStackClosure cmsDrainMarkingStackClosure(this, _span, &_markBitMap, &_markStack, &cmsKeepAliveClosure, false /* !preclean */); @@ -5811,9 +5884,9 @@ check_correct_thread_executing(); verify_work_stacks_empty(); verify_overflow_empty(); - incrementSweepCount(); - _sweep_timer.stop(); - _sweep_estimate.sample(_sweep_timer.seconds()); + increment_sweep_count(); + _inter_sweep_timer.stop(); + _inter_sweep_estimate.sample(_inter_sweep_timer.seconds()); size_policy()->avg_cms_free_at_sweep()->sample(_cmsGen->free()); // PermGen verification support: If perm gen sweeping is disabled in @@ -5836,6 +5909,9 @@ } } + assert(!_intra_sweep_timer.is_active(), "Should not be active"); + _intra_sweep_timer.reset(); + _intra_sweep_timer.start(); if (asynch) { TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); CMSPhaseAccounting pa(this, "sweep", !PrintGCDetails); @@ -5880,8 +5956,11 @@ verify_work_stacks_empty(); verify_overflow_empty(); - _sweep_timer.reset(); - _sweep_timer.start(); + _intra_sweep_timer.stop(); + _intra_sweep_estimate.sample(_intra_sweep_timer.seconds()); + + _inter_sweep_timer.reset(); + _inter_sweep_timer.start(); update_time_of_last_gc(os::javaTimeMillis()); @@ -5924,11 +6003,11 @@ // FIX ME!!! Looks like this belongs in CFLSpace, with // CMSGen merely delegating to it. void ConcurrentMarkSweepGeneration::setNearLargestChunk() { - double nearLargestPercent = 0.999; + double nearLargestPercent = FLSLargestBlockCoalesceProximity; HeapWord* minAddr = _cmsSpace->bottom(); HeapWord* largestAddr = (HeapWord*) _cmsSpace->dictionary()->findLargestDict(); - if (largestAddr == 0) { + if (largestAddr == NULL) { // The dictionary appears to be empty. In this case // try to coalesce at the end of the heap. largestAddr = _cmsSpace->end(); @@ -5936,6 +6015,13 @@ size_t largestOffset = pointer_delta(largestAddr, minAddr); size_t nearLargestOffset = (size_t)((double)largestOffset * nearLargestPercent) - MinChunkSize; + if (PrintFLSStatistics != 0) { + gclog_or_tty->print_cr( + "CMS: Large Block: " PTR_FORMAT ";" + " Proximity: " PTR_FORMAT " -> " PTR_FORMAT, + largestAddr, + _cmsSpace->nearLargestChunk(), minAddr + nearLargestOffset); + } _cmsSpace->set_nearLargestChunk(minAddr + nearLargestOffset); } @@ -6015,9 +6101,11 @@ assert_lock_strong(gen->freelistLock()); assert_lock_strong(bitMapLock()); - assert(!_sweep_timer.is_active(), "Was switched off in an outer context"); - gen->cmsSpace()->beginSweepFLCensus((float)(_sweep_timer.seconds()), - _sweep_estimate.padded_average()); + assert(!_inter_sweep_timer.is_active(), "Was switched off in an outer context"); + assert(_intra_sweep_timer.is_active(), "Was switched on in an outer context"); + gen->cmsSpace()->beginSweepFLCensus((float)(_inter_sweep_timer.seconds()), + _inter_sweep_estimate.padded_average(), + _intra_sweep_estimate.padded_average()); gen->setNearLargestChunk(); { @@ -6030,7 +6118,7 @@ // end-of-sweep-census below will be off by a little bit. } gen->cmsSpace()->sweep_completed(); - gen->cmsSpace()->endSweepFLCensus(sweepCount()); + gen->cmsSpace()->endSweepFLCensus(sweep_count()); if (should_unload_classes()) { // unloaded classes this cycle, _concurrent_cycles_since_last_unload = 0; // ... reset count } else { // did not unload classes, @@ -6342,8 +6430,8 @@ // For now we take the expedient path of just disabling the // messages for the problematic case.) void CMSMarkStack::expand() { - assert(_capacity <= CMSMarkStackSizeMax, "stack bigger than permitted"); - if (_capacity == CMSMarkStackSizeMax) { + assert(_capacity <= MarkStackSizeMax, "stack bigger than permitted"); + if (_capacity == MarkStackSizeMax) { if (_hit_limit++ == 0 && !CMSConcurrentMTEnabled && PrintGCDetails) { // We print a warning message only once per CMS cycle. gclog_or_tty->print_cr(" (benign) Hit CMSMarkStack max size limit"); @@ -6351,7 +6439,7 @@ return; } // Double capacity if possible - size_t new_capacity = MIN2(_capacity*2, CMSMarkStackSizeMax); + size_t new_capacity = MIN2(_capacity*2, MarkStackSizeMax); // Do not give up existing stack until we have managed to // get the double capacity that we desired. ReservedSpace rs(ReservedSpace::allocation_align_size_up( @@ -6387,10 +6475,9 @@ // generation then this will lose younger_gen cards! MarkRefsIntoClosure::MarkRefsIntoClosure( - MemRegion span, CMSBitMap* bitMap, bool should_do_nmethods): + MemRegion span, CMSBitMap* bitMap): _span(span), - _bitMap(bitMap), - _should_do_nmethods(should_do_nmethods) + _bitMap(bitMap) { assert(_ref_processor == NULL, "deliberately left NULL"); assert(_bitMap->covers(_span), "_bitMap/_span mismatch"); @@ -6411,12 +6498,11 @@ // A variant of the above, used for CMS marking verification. MarkRefsIntoVerifyClosure::MarkRefsIntoVerifyClosure( - MemRegion span, CMSBitMap* verification_bm, CMSBitMap* cms_bm, - bool should_do_nmethods): + MemRegion span, CMSBitMap* verification_bm, CMSBitMap* cms_bm): _span(span), _verification_bm(verification_bm), - _cms_bm(cms_bm), - _should_do_nmethods(should_do_nmethods) { + _cms_bm(cms_bm) +{ assert(_ref_processor == NULL, "deliberately left NULL"); assert(_verification_bm->covers(_span), "_verification_bm/_span mismatch"); } @@ -6531,6 +6617,7 @@ assert_lock_strong(_freelistLock); assert_lock_strong(_bit_map->lock()); // relinquish the free_list_lock and bitMaplock() + DEBUG_ONLY(RememberKlassesChecker mux(false);) _bit_map->lock()->unlock(); _freelistLock->unlock(); ConcurrentMarkSweepThread::desynchronize(true); @@ -6703,6 +6790,7 @@ "CMS thread should hold CMS token"); assert_lock_strong(_freelistLock); assert_lock_strong(_bitMap->lock()); + DEBUG_ONLY(RememberKlassesChecker mux(false);) // relinquish the free_list_lock and bitMaplock() _bitMap->lock()->unlock(); _freelistLock->unlock(); @@ -6779,6 +6867,7 @@ assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), "CMS thread should hold CMS token"); assert_lock_strong(_bit_map->lock()); + DEBUG_ONLY(RememberKlassesChecker smx(false);) // Relinquish the bit map lock _bit_map->lock()->unlock(); ConcurrentMarkSweepThread::desynchronize(true); @@ -6941,6 +7030,7 @@ assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), "CMS thread should hold CMS token"); assert_lock_strong(_bitMap->lock()); + DEBUG_ONLY(RememberKlassesChecker mux(false);) _bitMap->lock()->unlock(); ConcurrentMarkSweepThread::desynchronize(true); ConcurrentMarkSweepThread::acknowledge_yield_request(); @@ -7295,15 +7385,12 @@ CMSBitMap* bitMap, CMSMarkStack* markStack, CMSMarkStack* revisitStack, HeapWord* finger, MarkFromRootsClosure* parent) : - OopClosure(collector->ref_processor()), - _collector(collector), + KlassRememberingOopClosure(collector, collector->ref_processor(), revisitStack), _span(span), _bitMap(bitMap), _markStack(markStack), - _revisitStack(revisitStack), _finger(finger), - _parent(parent), - _should_remember_klasses(collector->should_unload_classes()) + _parent(parent) { } Par_PushOrMarkClosure::Par_PushOrMarkClosure(CMSCollector* collector, @@ -7315,18 +7402,17 @@ HeapWord* finger, HeapWord** global_finger_addr, Par_MarkFromRootsClosure* parent) : - OopClosure(collector->ref_processor()), - _collector(collector), + Par_KlassRememberingOopClosure(collector, + collector->ref_processor(), + revisit_stack), _whole_span(collector->_span), _span(span), _bit_map(bit_map), _work_queue(work_queue), _overflow_stack(overflow_stack), - _revisit_stack(revisit_stack), _finger(finger), _global_finger_addr(global_finger_addr), - _parent(parent), - _should_remember_klasses(collector->should_unload_classes()) + _parent(parent) { } // Assumes thread-safe access by callers, who are @@ -7456,6 +7542,14 @@ void Par_PushOrMarkClosure::do_oop(oop* p) { Par_PushOrMarkClosure::do_oop_work(p); } void Par_PushOrMarkClosure::do_oop(narrowOop* p) { Par_PushOrMarkClosure::do_oop_work(p); } +KlassRememberingOopClosure::KlassRememberingOopClosure(CMSCollector* collector, + ReferenceProcessor* rp, + CMSMarkStack* revisit_stack) : + OopClosure(rp), + _collector(collector), + _revisit_stack(revisit_stack), + _should_remember_klasses(collector->should_unload_classes()) {} + PushAndMarkClosure::PushAndMarkClosure(CMSCollector* collector, MemRegion span, ReferenceProcessor* rp, @@ -7464,15 +7558,12 @@ CMSMarkStack* mark_stack, CMSMarkStack* revisit_stack, bool concurrent_precleaning): - OopClosure(rp), - _collector(collector), + KlassRememberingOopClosure(collector, rp, revisit_stack), _span(span), _bit_map(bit_map), _mod_union_table(mod_union_table), _mark_stack(mark_stack), - _revisit_stack(revisit_stack), - _concurrent_precleaning(concurrent_precleaning), - _should_remember_klasses(collector->should_unload_classes()) + _concurrent_precleaning(concurrent_precleaning) { assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); } @@ -7540,13 +7631,10 @@ CMSBitMap* bit_map, OopTaskQueue* work_queue, CMSMarkStack* revisit_stack): - OopClosure(rp), - _collector(collector), + Par_KlassRememberingOopClosure(collector, rp, revisit_stack), _span(span), _bit_map(bit_map), - _work_queue(work_queue), - _revisit_stack(revisit_stack), - _should_remember_klasses(collector->should_unload_classes()) + _work_queue(work_queue) { assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); } @@ -7599,19 +7687,16 @@ void Par_PushAndMarkClosure::do_oop(oop* p) { Par_PushAndMarkClosure::do_oop_work(p); } void Par_PushAndMarkClosure::do_oop(narrowOop* p) { Par_PushAndMarkClosure::do_oop_work(p); } -void PushAndMarkClosure::remember_klass(Klass* k) { - if (!_revisit_stack->push(oop(k))) { - fatal("Revisit stack overflowed in PushAndMarkClosure"); - } -} - -void Par_PushAndMarkClosure::remember_klass(Klass* k) { - if (!_revisit_stack->par_push(oop(k))) { - fatal("Revist stack overflowed in Par_PushAndMarkClosure"); - } +void PushAndMarkClosure::remember_mdo(DataLayout* v) { + // TBD +} + +void Par_PushAndMarkClosure::remember_mdo(DataLayout* v) { + // TBD } void CMSPrecleanRefsYieldClosure::do_yield_work() { + DEBUG_ONLY(RememberKlassesChecker mux(false);) Mutex* bml = _collector->bitMapLock(); assert_lock_strong(bml); assert(ConcurrentMarkSweepThread::cms_thread_has_cms_token(), @@ -8302,6 +8387,19 @@ (!_span.contains(addr) || _bit_map->isMarked(addr)); } +CMSKeepAliveClosure::CMSKeepAliveClosure( CMSCollector* collector, + MemRegion span, + CMSBitMap* bit_map, CMSMarkStack* mark_stack, + CMSMarkStack* revisit_stack, bool cpc): + KlassRememberingOopClosure(collector, NULL, revisit_stack), + _span(span), + _bit_map(bit_map), + _mark_stack(mark_stack), + _concurrent_precleaning(cpc) { + assert(!_span.is_empty(), "Empty span could spell trouble"); +} + + // CMSKeepAliveClosure: the serial version void CMSKeepAliveClosure::do_oop(oop obj) { HeapWord* addr = (HeapWord*)obj; @@ -8385,6 +8483,16 @@ } } +CMSInnerParMarkAndPushClosure::CMSInnerParMarkAndPushClosure( + CMSCollector* collector, + MemRegion span, CMSBitMap* bit_map, + CMSMarkStack* revisit_stack, + OopTaskQueue* work_queue): + Par_KlassRememberingOopClosure(collector, NULL, revisit_stack), + _span(span), + _bit_map(bit_map), + _work_queue(work_queue) { } + void CMSInnerParMarkAndPushClosure::do_oop(oop obj) { HeapWord* addr = (HeapWord*)obj; if (_span.contains(addr) && diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -355,6 +355,11 @@ unsigned int new_duty_cycle); unsigned int icms_update_duty_cycle_impl(); + // In support of adjusting of cms trigger ratios based on history + // of concurrent mode failure. + double cms_free_adjustment_factor(size_t free) const; + void adjust_cms_free_adjustment_factor(bool fail, size_t free); + public: CMSStats(ConcurrentMarkSweepGeneration* cms_gen, unsigned int alpha = CMSExpAvgFactor); @@ -570,8 +575,11 @@ // appropriately. void check_gc_time_limit(); // XXX Move these to CMSStats ??? FIX ME !!! - elapsedTimer _sweep_timer; - AdaptivePaddedAverage _sweep_estimate; + elapsedTimer _inter_sweep_timer; // time between sweeps + elapsedTimer _intra_sweep_timer; // time _in_ sweeps + // padded decaying average estimates of the above + AdaptivePaddedAverage _inter_sweep_estimate; + AdaptivePaddedAverage _intra_sweep_estimate; protected: ConcurrentMarkSweepGeneration* _cmsGen; // old gen (CMS) @@ -625,6 +633,7 @@ // . _collectorState <= Idling == post-sweep && pre-mark // . _collectorState in (Idling, Sweeping) == {initial,final}marking || // precleaning || abortablePrecleanb + public: enum CollectorState { Resizing = 0, Resetting = 1, @@ -636,6 +645,7 @@ FinalMarking = 7, Sweeping = 8 }; + protected: static CollectorState _collectorState; // State related to prologue/epilogue invocation for my generations @@ -655,7 +665,7 @@ int _numYields; size_t _numDirtyCards; - uint _sweepCount; + size_t _sweep_count; // number of full gc's since the last concurrent gc. uint _full_gcs_since_conc_gc; @@ -905,7 +915,7 @@ // Check that the currently executing thread is the expected // one (foreground collector or background collector). - void check_correct_thread_executing() PRODUCT_RETURN; + static void check_correct_thread_executing() PRODUCT_RETURN; // XXXPERM void print_statistics() PRODUCT_RETURN; bool is_cms_reachable(HeapWord* addr); @@ -930,8 +940,8 @@ static void set_foregroundGCShouldWait(bool v) { _foregroundGCShouldWait = v; } static bool foregroundGCIsActive() { return _foregroundGCIsActive; } static void set_foregroundGCIsActive(bool v) { _foregroundGCIsActive = v; } - uint sweepCount() const { return _sweepCount; } - void incrementSweepCount() { _sweepCount++; } + size_t sweep_count() const { return _sweep_count; } + void increment_sweep_count() { _sweep_count++; } // Timers/stats for gc scheduling and incremental mode pacing. CMSStats& stats() { return _stats; } @@ -1165,6 +1175,11 @@ virtual bool promotion_attempt_is_safe(size_t promotion_in_bytes, bool younger_handles_promotion_failure) const; + // Inform this (non-young) generation that a promotion failure was + // encountered during a collection of a younger generation that + // promotes into this generation. + virtual void promotion_failure_occurred(); + bool should_collect(bool full, size_t size, bool tlab); virtual bool should_concurrent_collect() const; virtual bool is_too_full() const; @@ -1790,12 +1805,13 @@ public: CMSParDrainMarkingStackClosure(CMSCollector* collector, MemRegion span, CMSBitMap* bit_map, + CMSMarkStack* revisit_stack, OopTaskQueue* work_queue): _collector(collector), _span(span), _bit_map(bit_map), _work_queue(work_queue), - _mark_and_push(collector, span, bit_map, work_queue) { } + _mark_and_push(collector, span, bit_map, revisit_stack, work_queue) { } public: void trim_queue(uint max); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeBlockDictionary.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -55,7 +55,8 @@ virtual void dictCensusUpdate(size_t size, bool split, bool birth) = 0; virtual bool coalDictOverPopulated(size_t size) = 0; virtual void beginSweepDictCensus(double coalSurplusPercent, - float sweep_current, float sweep_ewstimate) = 0; + float inter_sweep_current, float inter_sweep_estimate, + float intra__sweep_current) = 0; virtual void endSweepDictCensus(double splitSurplusPercent) = 0; virtual FreeChunk* findLargestDict() const = 0; // verify that the given chunk is in the dictionary. @@ -79,6 +80,7 @@ } virtual void printDictCensus() const = 0; + virtual void print_free_lists(outputStream* st) const = 0; virtual void verify() const = 0; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -67,3 +67,8 @@ } } #endif + +void FreeChunk::print_on(outputStream* st) { + st->print_cr("Next: " PTR_FORMAT " Prev: " PTR_FORMAT " %s", + next(), prev(), cantCoalesce() ? "[can't coalesce]" : ""); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -129,6 +129,8 @@ void verifyList() const PRODUCT_RETURN; void mangleAllocated(size_t size) PRODUCT_RETURN; void mangleFreed(size_t size) PRODUCT_RETURN; + + void print_on(outputStream* st); }; // Alignment helpers etc. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/freeList.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -81,8 +81,8 @@ set_hint(hint); } -void FreeList::init_statistics() { - _allocation_stats.initialize(); +void FreeList::init_statistics(bool split_birth) { + _allocation_stats.initialize(split_birth); } FreeChunk* FreeList::getChunkAtHead() { @@ -292,14 +292,31 @@ } #ifndef PRODUCT +void FreeList::verify_stats() const { + // The +1 of the LH comparand is to allow some "looseness" in + // checking: we usually call this interface when adding a block + // and we'll subsequently update the stats; we cannot update the + // stats beforehand because in the case of the large-block BT + // dictionary for example, this might be the first block and + // in that case there would be no place that we could record + // the stats (which are kept in the block itself). + assert(_allocation_stats.prevSweep() + _allocation_stats.splitBirths() + 1 // Total Stock + 1 + >= _allocation_stats.splitDeaths() + (ssize_t)count(), "Conservation Principle"); +} + void FreeList::assert_proper_lock_protection_work() const { -#ifdef ASSERT - if (_protecting_lock != NULL && - SharedHeap::heap()->n_par_threads() > 0) { - // Should become an assert. - guarantee(_protecting_lock->owned_by_self(), "FreeList RACE DETECTED"); + assert(_protecting_lock != NULL, "Don't call this directly"); + assert(ParallelGCThreads > 0, "Don't call this directly"); + Thread* thr = Thread::current(); + if (thr->is_VM_thread() || thr->is_ConcurrentGC_thread()) { + // assert that we are holding the freelist lock + } else if (thr->is_GC_task_thread()) { + assert(_protecting_lock->owned_by_self(), "FreeList RACE DETECTED"); + } else if (thr->is_Java_thread()) { + assert(!SafepointSynchronize::is_at_safepoint(), "Should not be executing"); + } else { + ShouldNotReachHere(); // unaccounted thread type? } -#endif } #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/concurrentMarkSweep/freeList.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeList.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -35,18 +35,26 @@ // for that implementation. class Mutex; +class TreeList; class FreeList VALUE_OBJ_CLASS_SPEC { friend class CompactibleFreeListSpace; friend class VMStructs; - friend class printTreeCensusClosure; - FreeChunk* _head; // List of free chunks + friend class PrintTreeCensusClosure; + + protected: + TreeList* _parent; + TreeList* _left; + TreeList* _right; + + private: + FreeChunk* _head; // Head of list of free chunks FreeChunk* _tail; // Tail of list of free chunks - size_t _size; // Size in Heap words of each chunks + size_t _size; // Size in Heap words of each chunk ssize_t _count; // Number of entries in list size_t _hint; // next larger size list with a positive surplus - AllocationStats _allocation_stats; // statistics for smart allocation + AllocationStats _allocation_stats; // allocation-related statistics #ifdef ASSERT Mutex* _protecting_lock; @@ -63,9 +71,12 @@ // Initialize the allocation statistics. protected: - void init_statistics(); + void init_statistics(bool split_birth = false); void set_count(ssize_t v) { _count = v;} - void increment_count() { _count++; } + void increment_count() { + _count++; + } + void decrement_count() { _count--; assert(_count >= 0, "Count should not be negative"); @@ -167,11 +178,13 @@ _allocation_stats.set_desired(v); } void compute_desired(float inter_sweep_current, - float inter_sweep_estimate) { + float inter_sweep_estimate, + float intra_sweep_estimate) { assert_proper_lock_protection(); _allocation_stats.compute_desired(_count, inter_sweep_current, - inter_sweep_estimate); + inter_sweep_estimate, + intra_sweep_estimate); } ssize_t coalDesired() const { return _allocation_stats.coalDesired(); @@ -306,6 +319,9 @@ // found. Return NULL if "fc" is not found. bool verifyChunkInFreeLists(FreeChunk* fc) const; + // Stats verification + void verify_stats() const PRODUCT_RETURN; + // Printing support static void print_labels_on(outputStream* st, const char* c); void print_on(outputStream* st, const char* c = NULL) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/collectionSetChooser.cpp --- a/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -351,9 +351,16 @@ CollectionSetChooser::printSortedHeapRegions() { gclog_or_tty->print_cr("Printing %d Heap Regions sorted by amount of known garbage", _numMarkedRegions); + + DEBUG_ONLY(int marked_count = 0;) for (int i = 0; i < _markedRegions.length(); i++) { - printHeapRegion(_markedRegions.at(i)); + HeapRegion* r = _markedRegions.at(i); + if (r != NULL) { + printHeapRegion(r); + DEBUG_ONLY(marked_count++;) + } } + assert(marked_count == _numMarkedRegions, "must be"); gclog_or_tty->print_cr("Done sorted heap region print"); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp --- a/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -42,28 +42,49 @@ _n_periods(0), _threads(NULL), _n_threads(0) { - if (G1ConcRefine) { - _n_threads = (int)thread_num(); - if (_n_threads > 0) { - _threads = NEW_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _n_threads); - int worker_id_offset = (int)DirtyCardQueueSet::num_par_ids(); - ConcurrentG1RefineThread *next = NULL; - for (int i = _n_threads - 1; i >= 0; i--) { - ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(this, next, worker_id_offset, i); - assert(t != NULL, "Conc refine should have been created"); - assert(t->cg1r() == this, "Conc refine thread should refer to this"); - _threads[i] = t; - next = t; - } - } + + // Ergomonically select initial concurrent refinement parameters + if (FLAG_IS_DEFAULT(G1ConcRefinementGreenZone)) { + FLAG_SET_DEFAULT(G1ConcRefinementGreenZone, MAX2(ParallelGCThreads, 1)); + } + set_green_zone(G1ConcRefinementGreenZone); + + if (FLAG_IS_DEFAULT(G1ConcRefinementYellowZone)) { + FLAG_SET_DEFAULT(G1ConcRefinementYellowZone, green_zone() * 3); + } + set_yellow_zone(MAX2(G1ConcRefinementYellowZone, green_zone())); + + if (FLAG_IS_DEFAULT(G1ConcRefinementRedZone)) { + FLAG_SET_DEFAULT(G1ConcRefinementRedZone, yellow_zone() * 2); + } + set_red_zone(MAX2(G1ConcRefinementRedZone, yellow_zone())); + _n_worker_threads = thread_num(); + // We need one extra thread to do the young gen rset size sampling. + _n_threads = _n_worker_threads + 1; + reset_threshold_step(); + + _threads = NEW_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _n_threads); + int worker_id_offset = (int)DirtyCardQueueSet::num_par_ids(); + ConcurrentG1RefineThread *next = NULL; + for (int i = _n_threads - 1; i >= 0; i--) { + ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(this, next, worker_id_offset, i); + assert(t != NULL, "Conc refine should have been created"); + assert(t->cg1r() == this, "Conc refine thread should refer to this"); + _threads[i] = t; + next = t; } } -size_t ConcurrentG1Refine::thread_num() { - if (G1ConcRefine) { - return (G1ParallelRSetThreads > 0) ? G1ParallelRSetThreads : ParallelGCThreads; +void ConcurrentG1Refine::reset_threshold_step() { + if (FLAG_IS_DEFAULT(G1ConcRefinementThresholdStep)) { + _thread_threshold_step = (yellow_zone() - green_zone()) / (worker_thread_num() + 1); + } else { + _thread_threshold_step = G1ConcRefinementThresholdStep; } - return 0; +} + +int ConcurrentG1Refine::thread_num() { + return MAX2((G1ConcRefinementThreads > 0) ? G1ConcRefinementThreads : ParallelGCThreads, 1); } void ConcurrentG1Refine::init() { @@ -123,6 +144,15 @@ } } +void ConcurrentG1Refine::reinitialize_threads() { + reset_threshold_step(); + if (_threads != NULL) { + for (int i = 0; i < _n_threads; i++) { + _threads[i]->initialize(); + } + } +} + ConcurrentG1Refine::~ConcurrentG1Refine() { if (G1ConcRSLogCacheSize > 0) { assert(_card_counts != NULL, "Logic"); @@ -270,7 +300,23 @@ int count; jbyte* cached_ptr = add_card_count(card_ptr, &count, defer); assert(cached_ptr != NULL, "bad cached card ptr"); - assert(!is_young_card(cached_ptr), "shouldn't get a card in young region"); + + if (is_young_card(cached_ptr)) { + // The region containing cached_ptr has been freed during a clean up + // pause, reallocated, and tagged as young. + assert(cached_ptr != card_ptr, "shouldn't be"); + + // We've just inserted a new old-gen card pointer into the card count + // cache and evicted the previous contents of that count slot. + // The evicted card pointer has been determined to be in a young region + // and so cannot be the newly inserted card pointer (that will be + // in an old region). + // The count for newly inserted card will be set to zero during the + // insertion, so we don't want to defer the cleaning of the newly + // inserted card pointer. + assert(*defer == false, "deferring non-hot card"); + return NULL; + } // The card pointer we obtained from card count cache is not hot // so do not store it in the cache; return it for immediate @@ -377,3 +423,10 @@ _g1h->g1_policy()->record_cc_clear_time(elapsed * 1000.0); #endif } + +void ConcurrentG1Refine::print_worker_threads_on(outputStream* st) const { + for (int i = 0; i < _n_threads; ++i) { + _threads[i]->print_on(st); + st->cr(); + } +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp --- a/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -29,6 +29,32 @@ class ConcurrentG1Refine: public CHeapObj { ConcurrentG1RefineThread** _threads; int _n_threads; + int _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 + * done, the buffers are left unprocessed to enable the caching effect of the + * dirtied cards. In the yellow zone [green, yellow) the concurrent refinement + * threads are gradually activated. In [yellow, red) all threads are + * running. If the length becomes red (max queue length) the mutators start + * processing the buffers. + * + * There are some interesting cases (when G1UseAdaptiveConcRefinement + * is turned off): + * 1) green = yellow = red = 0. In this case the mutator will process all + * buffers. Except for those that are created by the deferred updates + * machinery during a collection. + * 2) green = 0. Means no caching. Can be a good way to minimize the + * amount of time spent updating rsets during a collection. + */ + int _green_zone; + int _yellow_zone; + int _red_zone; + + int _thread_threshold_step; + + // Reset the threshold step value based of the current zone boundaries. + void reset_threshold_step(); // The cache for card refinement. bool _use_cache; @@ -147,6 +173,8 @@ void init(); // Accomplish some initialization that has to wait. void stop(); + void reinitialize_threads(); + // Iterate over the conc refine threads void threads_do(ThreadClosure *tc); @@ -178,5 +206,20 @@ void clear_and_record_card_counts(); - static size_t thread_num(); + static int thread_num(); + + void print_worker_threads_on(outputStream* st) const; + + void set_green_zone(int x) { _green_zone = x; } + void set_yellow_zone(int x) { _yellow_zone = x; } + void set_red_zone(int x) { _red_zone = x; } + + int green_zone() const { return _green_zone; } + 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; } + + int thread_threshold_step() const { return _thread_threshold_step; } }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp --- a/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,10 +25,6 @@ #include "incls/_precompiled.incl" #include "incls/_concurrentG1RefineThread.cpp.incl" -// ======= Concurrent Mark Thread ======== - -// The CM thread is created when the G1 garbage collector is used - ConcurrentG1RefineThread:: ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread *next, int worker_id_offset, int worker_id) : @@ -37,20 +33,42 @@ _worker_id(worker_id), _active(false), _next(next), + _monitor(NULL), _cg1r(cg1r), - _vtime_accum(0.0), - _co_tracker(G1CRGroup), - _interval_ms(5.0) + _vtime_accum(0.0) { + + // Each thread has its own monitor. The i-th thread is responsible for signalling + // to thread i+1 if the number of buffers in the queue exceeds a threashold for this + // thread. Monitors are also used to wake up the threads during termination. + // The 0th worker in notified by mutator threads and has a special monitor. + // The last worker is used for young gen rset size sampling. + if (worker_id > 0) { + _monitor = new Monitor(Mutex::nonleaf, "Refinement monitor", true); + } else { + _monitor = DirtyCardQ_CBL_mon; + } + initialize(); create_and_start(); } +void ConcurrentG1RefineThread::initialize() { + if (_worker_id < cg1r()->worker_thread_num()) { + // Current thread activation threshold + _threshold = MIN2(cg1r()->thread_threshold_step() * (_worker_id + 1) + cg1r()->green_zone(), + cg1r()->yellow_zone()); + // A thread deactivates once the number of buffer reached a deactivation threshold + _deactivation_threshold = MAX2(_threshold - cg1r()->thread_threshold_step(), cg1r()->green_zone()); + } else { + set_active(true); + } +} + void ConcurrentG1RefineThread::sample_young_list_rs_lengths() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1CollectorPolicy* g1p = g1h->g1_policy(); if (g1p->adaptive_young_list_length()) { int regions_visited = 0; - g1h->young_list_rs_length_sampling_init(); while (g1h->young_list_rs_length_sampling_more()) { g1h->young_list_rs_length_sampling_next(); @@ -71,104 +89,121 @@ } } -void ConcurrentG1RefineThread::run() { - initialize_in_thread(); +void ConcurrentG1RefineThread::run_young_rs_sampling() { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); _vtime_start = os::elapsedVTime(); - wait_for_universe_init(); - - _co_tracker.enable(); - _co_tracker.start(); + while(!_should_terminate) { + _sts.join(); + sample_young_list_rs_lengths(); + _sts.leave(); - while (!_should_terminate) { - DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); - // Wait for completed log buffers to exist. - { - MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag); - while (((_worker_id == 0 && !dcqs.process_completed_buffers()) || - (_worker_id > 0 && !is_active())) && - !_should_terminate) { - DirtyCardQ_CBL_mon->wait(Mutex::_no_safepoint_check_flag); - } - } - - if (_should_terminate) { - return; + if (os::supports_vtime()) { + _vtime_accum = (os::elapsedVTime() - _vtime_start); + } else { + _vtime_accum = 0.0; } - // Now we take them off (this doesn't hold locks while it applies - // closures.) (If we did a full collection, then we'll do a full - // traversal. - _sts.join(); - int n_logs = 0; - int lower_limit = 0; - double start_vtime_sec; // only used when G1SmoothConcRefine is on - int prev_buffer_num; // only used when G1SmoothConcRefine is on - // This thread activation threshold - int threshold = G1UpdateBufferQueueProcessingThreshold * _worker_id; - // Next thread activation threshold - int next_threshold = threshold + G1UpdateBufferQueueProcessingThreshold; - int deactivation_threshold = MAX2(threshold - G1UpdateBufferQueueProcessingThreshold / 2, 0); + MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag); + if (_should_terminate) { + break; + } + _monitor->wait(Mutex::_no_safepoint_check_flag, G1ConcRefinementServiceIntervalMillis); + } +} - if (G1SmoothConcRefine) { - lower_limit = 0; - start_vtime_sec = os::elapsedVTime(); - prev_buffer_num = (int) dcqs.completed_buffers_num(); - } else { - lower_limit = G1UpdateBufferQueueProcessingThreshold / 4; // For now. +void ConcurrentG1RefineThread::wait_for_completed_buffers() { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag); + while (!_should_terminate && !is_active()) { + _monitor->wait(Mutex::_no_safepoint_check_flag); + } +} + +bool ConcurrentG1RefineThread::is_active() { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + return _worker_id > 0 ? _active : dcqs.process_completed_buffers(); +} + +void ConcurrentG1RefineThread::activate() { + MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag); + if (_worker_id > 0) { + if (G1TraceConcRefinement) { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + gclog_or_tty->print_cr("G1-Refine-activated worker %d, on threshold %d, current %d", + _worker_id, _threshold, (int)dcqs.completed_buffers_num()); } - while (dcqs.apply_closure_to_completed_buffer(_worker_id + _worker_id_offset, lower_limit)) { - double end_vtime_sec; - double elapsed_vtime_sec; - int elapsed_vtime_ms; - int curr_buffer_num = (int) dcqs.completed_buffers_num(); + set_active(true); + } else { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + dcqs.set_process_completed(true); + } + _monitor->notify(); +} - if (G1SmoothConcRefine) { - end_vtime_sec = os::elapsedVTime(); - elapsed_vtime_sec = end_vtime_sec - start_vtime_sec; - elapsed_vtime_ms = (int) (elapsed_vtime_sec * 1000.0); +void ConcurrentG1RefineThread::deactivate() { + MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag); + if (_worker_id > 0) { + if (G1TraceConcRefinement) { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + gclog_or_tty->print_cr("G1-Refine-deactivated worker %d, off threshold %d, current %d", + _worker_id, _deactivation_threshold, (int)dcqs.completed_buffers_num()); + } + set_active(false); + } else { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + dcqs.set_process_completed(false); + } +} + +void ConcurrentG1RefineThread::run() { + initialize_in_thread(); + wait_for_universe_init(); - if (curr_buffer_num > prev_buffer_num || - curr_buffer_num > next_threshold) { - decreaseInterval(elapsed_vtime_ms); - } else if (curr_buffer_num < prev_buffer_num) { - increaseInterval(elapsed_vtime_ms); - } + if (_worker_id >= cg1r()->worker_thread_num()) { + run_young_rs_sampling(); + terminate(); + } + + _vtime_start = os::elapsedVTime(); + while (!_should_terminate) { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + + // Wait for work + wait_for_completed_buffers(); + + if (_should_terminate) { + break; + } + + _sts.join(); + + do { + int curr_buffer_num = (int)dcqs.completed_buffers_num(); + // If the number of the buffers falls down into the yellow zone, + // that means that the transition period after the evacuation pause has ended. + if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= cg1r()->yellow_zone()) { + dcqs.set_completed_queue_padding(0); } - if (_worker_id == 0) { - sample_young_list_rs_lengths(); - } else if (curr_buffer_num < deactivation_threshold) { + + if (_worker_id > 0 && curr_buffer_num <= _deactivation_threshold) { // If the number of the buffer has fallen below our threshold // we should deactivate. The predecessor will reactivate this // thread should the number of the buffers cross the threshold again. - MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag); deactivate(); - if (G1TraceConcurrentRefinement) { - gclog_or_tty->print_cr("G1-Refine-deactivated worker %d", _worker_id); - } break; } - _co_tracker.update(false); // Check if we need to activate the next thread. - if (curr_buffer_num > next_threshold && _next != NULL && !_next->is_active()) { - MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag); + if (_next != NULL && !_next->is_active() && curr_buffer_num > _next->_threshold) { _next->activate(); - DirtyCardQ_CBL_mon->notify_all(); - if (G1TraceConcurrentRefinement) { - gclog_or_tty->print_cr("G1-Refine-activated worker %d", _next->_worker_id); - } } + } while (dcqs.apply_closure_to_completed_buffer(_worker_id + _worker_id_offset, cg1r()->green_zone())); - if (G1SmoothConcRefine) { - prev_buffer_num = curr_buffer_num; - _sts.leave(); - os::sleep(Thread::current(), (jlong) _interval_ms, false); - _sts.join(); - start_vtime_sec = os::elapsedVTime(); - } - n_logs++; + // We can exit the loop above while being active if there was a yield request. + if (is_active()) { + deactivate(); } - _co_tracker.update(false); + _sts.leave(); if (os::supports_vtime()) { @@ -177,19 +212,19 @@ _vtime_accum = 0.0; } } - _sts.join(); - _co_tracker.update(true); - _sts.leave(); assert(_should_terminate, "just checking"); - terminate(); } void ConcurrentG1RefineThread::yield() { - if (G1TraceConcurrentRefinement) gclog_or_tty->print_cr("G1-Refine-yield"); + if (G1TraceConcRefinement) { + gclog_or_tty->print_cr("G1-Refine-yield"); + } _sts.yield("G1 refine"); - if (G1TraceConcurrentRefinement) gclog_or_tty->print_cr("G1-Refine-yield-end"); + if (G1TraceConcRefinement) { + gclog_or_tty->print_cr("G1-Refine-yield-end"); + } } void ConcurrentG1RefineThread::stop() { @@ -200,8 +235,8 @@ } { - MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag); - DirtyCardQ_CBL_mon->notify_all(); + MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag); + _monitor->notify(); } { @@ -210,11 +245,17 @@ Terminator_lock->wait(); } } - if (G1TraceConcurrentRefinement) gclog_or_tty->print_cr("G1-Refine-stop"); + if (G1TraceConcRefinement) { + gclog_or_tty->print_cr("G1-Refine-stop"); + } } -void ConcurrentG1RefineThread::print() { - gclog_or_tty->print("\"Concurrent G1 Refinement Thread\" "); - Thread::print(); - gclog_or_tty->cr(); +void ConcurrentG1RefineThread::print() const { + print_on(tty); } + +void ConcurrentG1RefineThread::print_on(outputStream* st) const { + st->print("\"G1 Concurrent Refinement Thread#%d\" ", _worker_id); + Thread::print_on(st); + st->cr(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp --- a/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -40,56 +40,47 @@ // when the number of the rset update buffer crosses a certain threshold. A successor // would self-deactivate when the number of the buffers falls below the threshold. bool _active; - ConcurrentG1RefineThread * _next; - public: - virtual void run(); - - bool is_active() { return _active; } - void activate() { _active = true; } - void deactivate() { _active = false; } - - private: - ConcurrentG1Refine* _cg1r; - - COTracker _co_tracker; - double _interval_ms; + ConcurrentG1RefineThread* _next; + Monitor* _monitor; + ConcurrentG1Refine* _cg1r; - void decreaseInterval(int processing_time_ms) { - double min_interval_ms = (double) processing_time_ms; - _interval_ms = 0.8 * _interval_ms; - if (_interval_ms < min_interval_ms) - _interval_ms = min_interval_ms; - } - void increaseInterval(int processing_time_ms) { - double max_interval_ms = 9.0 * (double) processing_time_ms; - _interval_ms = 1.1 * _interval_ms; - if (max_interval_ms > 0 && _interval_ms > max_interval_ms) - _interval_ms = max_interval_ms; - } + int _thread_threshold_step; + // This thread activation threshold + int _threshold; + // This thread deactivation threshold + int _deactivation_threshold; - void sleepBeforeNextCycle(); + void sample_young_list_rs_lengths(); + void run_young_rs_sampling(); + void wait_for_completed_buffers(); + + void set_active(bool x) { _active = x; } + bool is_active(); + void activate(); + void deactivate(); // For use by G1CollectedHeap, which is a friend. static SuspendibleThreadSet* sts() { return &_sts; } - public: +public: + virtual void run(); // Constructor ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread* next, int worker_id_offset, int worker_id); + void initialize(); + // Printing - void print(); + void print() const; + void print_on(outputStream* st) const; // Total virtual time so far. double vtime_accum() { return _vtime_accum; } - ConcurrentG1Refine* cg1r() { return _cg1r; } - - void sample_young_list_rs_lengths(); + ConcurrentG1Refine* cg1r() { return _cg1r; } // Yield for GC - void yield(); - + void yield(); // shutdown void stop(); }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentMark.cpp --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -237,7 +237,7 @@ _index = next_index; for (int i = 0; i < n; i++) { int ind = start + i; - guarantee(ind < _capacity, "By overflow test above."); + assert(ind < _capacity, "By overflow test above."); _base[ind] = ptr_arr[i]; } } @@ -310,12 +310,12 @@ if (res == index) { MemRegion mr = _base[next_index]; if (mr.start() != NULL) { - tmp_guarantee_CM( mr.end() != NULL, "invariant" ); - tmp_guarantee_CM( mr.word_size() > 0, "invariant" ); + assert(mr.end() != NULL, "invariant"); + assert(mr.word_size() > 0, "invariant"); return mr; } else { // that entry was invalidated... let's skip it - tmp_guarantee_CM( mr.end() == NULL, "invariant" ); + assert(mr.end() == NULL, "invariant"); } } // Otherwise, we need to try again. @@ -328,10 +328,10 @@ for (int i = 0; i < _oops_do_bound; ++i) { MemRegion mr = _base[i]; if (mr.start() != NULL) { - tmp_guarantee_CM( mr.end() != NULL, "invariant"); - tmp_guarantee_CM( mr.word_size() > 0, "invariant" ); + assert(mr.end() != NULL, "invariant"); + assert(mr.word_size() > 0, "invariant"); HeapRegion* hr = g1h->heap_region_containing(mr.start()); - tmp_guarantee_CM( hr != NULL, "invariant" ); + assert(hr != NULL, "invariant"); if (hr->in_collection_set()) { // The region points into the collection set _base[i] = MemRegion(); @@ -339,7 +339,7 @@ } } else { // that entry was invalidated... let's skip it - tmp_guarantee_CM( mr.end() == NULL, "invariant" ); + assert(mr.end() == NULL, "invariant"); } } return result; @@ -433,8 +433,7 @@ _total_counting_time(0.0), _total_rs_scrub_time(0.0), - _parallel_workers(NULL), - _cleanup_co_tracker(G1CLGroup) + _parallel_workers(NULL) { CMVerboseLevel verbose_level = (CMVerboseLevel) G1MarkingVerboseLevel; @@ -448,7 +447,7 @@ gclog_or_tty->print_cr("[global] init, heap start = "PTR_FORMAT", " "heap end = "PTR_FORMAT, _heap_start, _heap_end); - _markStack.allocate(G1MarkStackSize); + _markStack.allocate(MarkStackSize); _regionStack.allocate(G1MarkRegionStackSize); // Create & start a ConcurrentMark thread. @@ -462,7 +461,7 @@ assert(_markBitMap2.covers(rs), "_markBitMap2 inconsistency"); SATBMarkQueueSet& satb_qs = JavaThread::satb_mark_queue_set(); - satb_qs.set_buffer_size(G1SATBLogBufferSize); + satb_qs.set_buffer_size(G1SATBBufferSize); int size = (int) MAX2(ParallelGCThreads, (size_t)1); _par_cleanup_thread_state = NEW_C_HEAP_ARRAY(ParCleanupThreadState*, size); @@ -484,8 +483,8 @@ _accum_task_vtime[i] = 0.0; } - if (ParallelMarkingThreads > ParallelGCThreads) { - vm_exit_during_initialization("Can't have more ParallelMarkingThreads " + if (ConcGCThreads > ParallelGCThreads) { + vm_exit_during_initialization("Can't have more ConcGCThreads " "than ParallelGCThreads."); } if (ParallelGCThreads == 0) { @@ -495,11 +494,11 @@ _sleep_factor = 0.0; _marking_task_overhead = 1.0; } else { - if (ParallelMarkingThreads > 0) { - // notice that ParallelMarkingThreads overwrites G1MarkingOverheadPercent + if (ConcGCThreads > 0) { + // notice that ConcGCThreads overwrites G1MarkingOverheadPercent // if both are set - _parallel_marking_threads = ParallelMarkingThreads; + _parallel_marking_threads = ConcGCThreads; _sleep_factor = 0.0; _marking_task_overhead = 1.0; } else if (G1MarkingOverheadPercent > 0) { @@ -543,8 +542,8 @@ gclog_or_tty->print_cr("CL Sleep Factor %1.4lf", cleanup_sleep_factor()); #endif - guarantee( parallel_marking_threads() > 0, "peace of mind" ); - _parallel_workers = new WorkGang("Parallel Marking Threads", + guarantee(parallel_marking_threads() > 0, "peace of mind"); + _parallel_workers = new WorkGang("G1 Parallel Marking Threads", (int) parallel_marking_threads(), false, true); if (_parallel_workers == NULL) vm_exit_during_initialization("Failed necessary allocation."); @@ -570,8 +569,7 @@ return; MemRegion committed = _g1h->g1_committed(); - tmp_guarantee_CM( committed.start() == _heap_start, - "start shouldn't change" ); + assert(committed.start() == _heap_start, "start shouldn't change"); HeapWord* new_end = committed.end(); if (new_end > _heap_end) { // The heap has been expanded. @@ -593,9 +591,10 @@ _heap_start = committed.start(); _heap_end = committed.end(); - guarantee( _heap_start != NULL && - _heap_end != NULL && - _heap_start < _heap_end, "heap bounds should look ok" ); + // Separated the asserts so that we know which one fires. + assert(_heap_start != NULL, "heap bounds should look ok"); + assert(_heap_end != NULL, "heap bounds should look ok"); + assert(_heap_start < _heap_end, "heap bounds should look ok"); // reset all the marking data structures and any necessary flags clear_marking_state(); @@ -615,7 +614,7 @@ } void ConcurrentMark::set_phase(size_t active_tasks, bool concurrent) { - guarantee( active_tasks <= _max_task_num, "we should not have more" ); + assert(active_tasks <= _max_task_num, "we should not have more"); _active_tasks = active_tasks; // Need to update the three data structures below according to the @@ -635,8 +634,8 @@ // We currently assume that the concurrent flag has been set to // false before we start remark. At this point we should also be // in a STW phase. - guarantee( !concurrent_marking_in_progress(), "invariant" ); - guarantee( _finger == _heap_end, "only way to get here" ); + assert(!concurrent_marking_in_progress(), "invariant"); + assert(_finger == _heap_end, "only way to get here"); update_g1_committed(true); } } @@ -668,39 +667,6 @@ // Called at the first checkpoint. // -#define PRINT_REACHABLE_AT_INITIAL_MARK 0 -#if PRINT_REACHABLE_AT_INITIAL_MARK -static FILE* reachable_file = NULL; - -class PrintReachableClosure: public OopsInGenClosure { - CMBitMap* _bm; - int _level; -public: - PrintReachableClosure(CMBitMap* bm) : - _bm(bm), _level(0) { - guarantee(reachable_file != NULL, "pre-condition"); - } - void do_oop(oop* p) { - oop obj = *p; - HeapWord* obj_addr = (HeapWord*)obj; - if (obj == NULL) return; - fprintf(reachable_file, "%d: "PTR_FORMAT" -> "PTR_FORMAT" (%d)\n", - _level, p, (void*) obj, _bm->isMarked(obj_addr)); - if (!_bm->isMarked(obj_addr)) { - _bm->mark(obj_addr); - _level++; - obj->oop_iterate(this); - _level--; - } - } -}; -#endif // PRINT_REACHABLE_AT_INITIAL_MARK - -#define SEND_HEAP_DUMP_TO_FILE 0 -#if SEND_HEAP_DUMP_TO_FILE -static FILE* heap_dump_file = NULL; -#endif // SEND_HEAP_DUMP_TO_FILE - void ConcurrentMark::clearNextBitmap() { guarantee(!G1CollectedHeap::heap()->mark_in_progress(), "Precondition."); @@ -738,31 +704,9 @@ _has_aborted = false; - // Find all the reachable objects... -#if PRINT_REACHABLE_AT_INITIAL_MARK - guarantee(reachable_file == NULL, "Protocol"); - char fn_buf[100]; - sprintf(fn_buf, "/tmp/reachable.txt.%d", os::current_process_id()); - reachable_file = fopen(fn_buf, "w"); - // clear the mark bitmap (no grey objects to start with) - _nextMarkBitMap->clearAll(); - PrintReachableClosure prcl(_nextMarkBitMap); - g1h->process_strong_roots( - false, // fake perm gen collection - SharedHeap::SO_AllClasses, - &prcl, // Regular roots - &prcl // Perm Gen Roots - ); - // The root iteration above "consumed" dirty cards in the perm gen. - // Therefore, as a shortcut, we dirty all such cards. - g1h->rem_set()->invalidate(g1h->perm_gen()->used_region(), false); - fclose(reachable_file); - reachable_file = NULL; - // clear the mark bitmap again. - _nextMarkBitMap->clearAll(); - COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); - COMPILER2_PRESENT(DerivedPointerTable::clear()); -#endif // PRINT_REACHABLE_AT_INITIAL_MARK + if (G1PrintReachableAtInitialMark) { + print_reachable(true, "before"); + } // Initialise marking structures. This has to be done in a STW phase. reset(); @@ -816,25 +760,12 @@ rp->setup_policy(false); // snapshot the soft ref policy to be used in this cycle SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); - satb_mq_set.set_process_completed_threshold(G1SATBProcessCompletedThreshold); satb_mq_set.set_active_all_threads(true); // update_g1_committed() will be called at the end of an evac pause // when marking is on. So, it's also called at the end of the // initial-mark pause to update the heap end, if the heap expands // during it. No need to call it here. - - guarantee( !_cleanup_co_tracker.enabled(), "invariant" ); - - size_t max_marking_threads = - MAX2((size_t) 1, parallel_marking_threads()); - for (int i = 0; i < (int)_max_task_num; ++i) { - _tasks[i]->enable_co_tracker(); - if (i < (int) max_marking_threads) - _tasks[i]->reset_co_tracker(marking_task_overhead()); - else - _tasks[i]->reset_co_tracker(0.0); - } } // Checkpoint the roots into this generation from outside @@ -845,7 +776,6 @@ G1CollectedHeap* g1h = G1CollectedHeap::heap(); double start = os::elapsedTime(); - GCOverheadReporter::recordSTWStart(start); G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy(); g1p->record_concurrent_mark_init_start(); @@ -866,9 +796,11 @@ g1h->set_marking_started(); g1h->rem_set()->prepare_for_younger_refs_iterate(false); - g1h->process_strong_roots(false, // fake perm gen collection + g1h->process_strong_roots(true, // activate StrongRootsScope + false, // fake perm gen collection SharedHeap::SO_AllClasses, ¬Older, // Regular roots + NULL, // do not visit active blobs &older // Perm Gen Roots ); checkpointRootsInitialPost(); @@ -876,7 +808,6 @@ // Statistics. double end = os::elapsedTime(); _init_times.add((end - start) * 1000.0); - GCOverheadReporter::recordSTWEnd(end); g1p->record_concurrent_mark_init_end(); } @@ -945,8 +876,8 @@ // initial-mark that the committed space is expanded during the // pause without CM observing this change. So the assertions below // is a bit conservative; but better than nothing. - tmp_guarantee_CM( _g1h->g1_committed().contains(addr), - "address should be within the heap bounds" ); + assert(_g1h->g1_committed().contains(addr), + "address should be within the heap bounds"); if (!_nextMarkBitMap->isMarked(addr)) _nextMarkBitMap->parMark(addr); @@ -972,12 +903,15 @@ if (mr.start() < finger) { // The finger is always heap region aligned and it is not possible // for mr to span heap regions. - tmp_guarantee_CM( mr.end() <= finger, "invariant" ); - - tmp_guarantee_CM( mr.start() <= mr.end() && - _heap_start <= mr.start() && - mr.end() <= _heap_end, - "region boundaries should fall within the committed space" ); + assert(mr.end() <= finger, "invariant"); + + // Separated the asserts so that we know which one fires. + assert(mr.start() <= mr.end(), + "region boundaries should fall within the committed space"); + assert(_heap_start <= mr.start(), + "region boundaries should fall within the committed space"); + assert(mr.end() <= _heap_end, + "region boundaries should fall within the committed space"); if (verbose_low()) gclog_or_tty->print_cr("[global] region ["PTR_FORMAT", "PTR_FORMAT") " "below the finger, pushing it", @@ -1026,16 +960,15 @@ public: void work(int worker_i) { - guarantee( Thread::current()->is_ConcurrentGC_thread(), - "this should only be done by a conc GC thread" ); + assert(Thread::current()->is_ConcurrentGC_thread(), + "this should only be done by a conc GC thread"); double start_vtime = os::elapsedVTime(); ConcurrentGCThread::stsJoin(); - guarantee( (size_t)worker_i < _cm->active_tasks(), "invariant" ); + assert((size_t) worker_i < _cm->active_tasks(), "invariant"); CMTask* the_task = _cm->task(worker_i); - the_task->start_co_tracker(); the_task->record_start_time(); if (!_cm->has_aborted()) { do { @@ -1061,8 +994,6 @@ double end_time2_sec = os::elapsedTime(); double elapsed_time2_sec = end_time2_sec - start_time_sec; - the_task->update_co_tracker(); - #if 0 gclog_or_tty->print_cr("CM: elapsed %1.4lf ms, sleep %1.4lf ms, " "overhead %1.4lf", @@ -1074,12 +1005,11 @@ } while (!_cm->has_aborted() && the_task->has_aborted()); } the_task->record_end_time(); - guarantee( !the_task->has_aborted() || _cm->has_aborted(), "invariant" ); + guarantee(!the_task->has_aborted() || _cm->has_aborted(), "invariant"); ConcurrentGCThread::stsLeave(); double end_vtime = os::elapsedVTime(); - the_task->update_co_tracker(true); _cm->update_accum_task_vtime(worker_i, end_vtime - start_vtime); } @@ -1133,7 +1063,6 @@ g1p->record_concurrent_mark_remark_start(); double start = os::elapsedTime(); - GCOverheadReporter::recordSTWStart(start); checkpointRootsFinalWork(); @@ -1173,11 +1102,6 @@ _remark_weak_ref_times.add((now - mark_work_end) * 1000.0); _remark_times.add((now - start) * 1000.0); - GCOverheadReporter::recordSTWEnd(now); - for (int i = 0; i < (int)_max_task_num; ++i) - _tasks[i]->disable_co_tracker(); - _cleanup_co_tracker.enable(); - _cleanup_co_tracker.reset(cleanup_task_overhead()); g1p->record_concurrent_mark_remark_end(); } @@ -1188,7 +1112,6 @@ CMBitMapRO* _bm; ConcurrentMark* _cm; - COTracker* _co_tracker; bool _changed; bool _yield; size_t _words_done; @@ -1205,8 +1128,7 @@ void mark_card_num_range(intptr_t start_card_num, intptr_t last_card_num) { for (intptr_t i = start_card_num; i <= last_card_num; i++) { #if CARD_BM_TEST_MODE - guarantee(_card_bm->at(i - _bottom_card_num), - "Should already be set."); + guarantee(_card_bm->at(i - _bottom_card_num), "Should already be set."); #else _card_bm->par_at_put(i - _bottom_card_num, 1); #endif @@ -1216,12 +1138,10 @@ public: CalcLiveObjectsClosure(bool final, CMBitMapRO *bm, ConcurrentMark *cm, - BitMap* region_bm, BitMap* card_bm, - COTracker* co_tracker) : + BitMap* region_bm, BitMap* card_bm) : _bm(bm), _cm(cm), _changed(false), _yield(true), _words_done(0), _tot_live(0), _tot_used(0), - _region_bm(region_bm), _card_bm(card_bm), - _final(final), _co_tracker(co_tracker), + _region_bm(region_bm), _card_bm(card_bm),_final(final), _regions_done(0), _start_vtime_sec(0.0) { _bottom_card_num = @@ -1265,9 +1185,6 @@ } bool doHeapRegion(HeapRegion* hr) { - if (_co_tracker != NULL) - _co_tracker->update(); - if (!_final && _regions_done == 0) _start_vtime_sec = os::elapsedVTime(); @@ -1356,7 +1273,7 @@ // In any case, we set the last card num. last_card_num = obj_last_card_num; - marked_bytes += obj_sz * HeapWordSize; + marked_bytes += (size_t)obj_sz * HeapWordSize; // Find the next marked object after this one. start = _bm->getNextMarkedWordAddress(start + 1, nextTop); _changed = true; @@ -1396,12 +1313,6 @@ if (elapsed_vtime_sec > (10.0 / 1000.0)) { jlong sleep_time_ms = (jlong) (elapsed_vtime_sec * _cm->cleanup_sleep_factor() * 1000.0); -#if 0 - gclog_or_tty->print_cr("CL: elapsed %1.4lf ms, sleep %1.4lf ms, " - "overhead %1.4lf", - elapsed_vtime_sec * 1000.0, (double) sleep_time_ms, - _co_tracker->concOverhead(os::elapsedTime())); -#endif os::sleep(Thread::current(), sleep_time_ms, false); _start_vtime_sec = end_vtime_sec; } @@ -1421,15 +1332,11 @@ void ConcurrentMark::calcDesiredRegions() { - guarantee( _cleanup_co_tracker.enabled(), "invariant" ); - _cleanup_co_tracker.start(); - _region_bm.clear(); _card_bm.clear(); CalcLiveObjectsClosure calccl(false /*final*/, nextMarkBitMap(), this, - &_region_bm, &_card_bm, - &_cleanup_co_tracker); + &_region_bm, &_card_bm); G1CollectedHeap *g1h = G1CollectedHeap::heap(); g1h->heap_region_iterate(&calccl); @@ -1437,8 +1344,6 @@ calccl.reset(); g1h->heap_region_iterate(&calccl); } while (calccl.changed()); - - _cleanup_co_tracker.update(true); } class G1ParFinalCountTask: public AbstractGangTask { @@ -1472,8 +1377,7 @@ void work(int i) { CalcLiveObjectsClosure calccl(true /*final*/, _bm, _g1h->concurrent_mark(), - _region_bm, _card_bm, - NULL /* CO tracker */); + _region_bm, _card_bm); calccl.no_yield(); if (ParallelGCThreads > 0) { _g1h->heap_region_par_iterate_chunked(&calccl, i, @@ -1483,7 +1387,7 @@ } assert(calccl.complete(), "Shouldn't have yielded!"); - guarantee( (size_t)i < _n_workers, "invariant" ); + assert((size_t) i < _n_workers, "invariant"); _live_bytes[i] = calccl.tot_live(); _used_bytes[i] = calccl.tot_used(); } @@ -1663,13 +1567,10 @@ /* prev marking */ true); } - _cleanup_co_tracker.disable(); - G1CollectorPolicy* g1p = G1CollectedHeap::heap()->g1_policy(); g1p->record_concurrent_mark_cleanup_start(); double start = os::elapsedTime(); - GCOverheadReporter::recordSTWStart(start); // Do counting once more with the world stopped for good measure. G1ParFinalCountTask g1_par_count_task(g1h, nextMarkBitMap(), @@ -1774,7 +1675,6 @@ // Statistics. double end = os::elapsedTime(); _cleanup_times.add((end - start) * 1000.0); - GCOverheadReporter::recordSTWEnd(end); // G1CollectedHeap::heap()->print(); // gclog_or_tty->print_cr("HEAP GC TIME STAMP : %d", @@ -1819,14 +1719,14 @@ hd->rem_set()->clear(); HeapRegion* next_hd = hd->next_from_unclean_list(); (void)list->pop(); - guarantee(list->hd() == next_hd, "how not?"); + assert(list->hd() == next_hd, "how not?"); _g1h->put_region_on_unclean_list(hd); if (!hd->isHumongous()) { // Add this to the _free_regions count by 1. _g1h->finish_free_region_work(0, 0, 1, NULL); } hd = list->hd(); - guarantee(hd == next_hd, "how not?"); + assert(hd == next_hd, "how not?"); } } } @@ -1963,7 +1863,7 @@ g1h->ensure_parsability(false); if (ParallelGCThreads > 0) { - g1h->change_strong_roots_parity(); + G1CollectedHeap::StrongRootsScope srs(g1h); // this is remark, so we'll use up all available threads int active_workers = ParallelGCThreads; set_phase(active_workers, false); @@ -1976,11 +1876,8 @@ g1h->set_par_threads(n_workers); g1h->workers()->run_task(&remarkTask); g1h->set_par_threads(0); - - SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); - guarantee( satb_mq_set.completed_buffers_num() == 0, "invariant" ); } else { - g1h->change_strong_roots_parity(); + G1CollectedHeap::StrongRootsScope srs(g1h); // this is remark, so we'll use up all available threads int active_workers = 1; set_phase(active_workers, false); @@ -1990,10 +1887,9 @@ // active_workers will be fewer. The extra ones will just bail out // immediately. remarkTask.work(0); - - SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); - guarantee( satb_mq_set.completed_buffers_num() == 0, "invariant" ); } + SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); + guarantee(satb_mq_set.completed_buffers_num() == 0, "invariant"); print_stats(); @@ -2012,15 +1908,21 @@ #endif } +#ifndef PRODUCT + class ReachablePrinterOopClosure: public OopClosure { private: G1CollectedHeap* _g1h; CMBitMapRO* _bitmap; outputStream* _out; + bool _use_prev_marking; public: - ReachablePrinterOopClosure(CMBitMapRO* bitmap, outputStream* out) : - _bitmap(bitmap), _g1h(G1CollectedHeap::heap()), _out(out) { } + ReachablePrinterOopClosure(CMBitMapRO* bitmap, + outputStream* out, + bool use_prev_marking) : + _g1h(G1CollectedHeap::heap()), + _bitmap(bitmap), _out(out), _use_prev_marking(use_prev_marking) { } void do_oop(narrowOop* p) { do_oop_work(p); } void do_oop( oop* p) { do_oop_work(p); } @@ -2034,15 +1936,24 @@ str = "outside G1 reserved"; else { HeapRegion* hr = _g1h->heap_region_containing(obj); - guarantee( hr != NULL, "invariant" ); - if (hr->obj_allocated_since_prev_marking(obj)) { + guarantee(hr != NULL, "invariant"); + bool over_tams = false; + if (_use_prev_marking) { + over_tams = hr->obj_allocated_since_prev_marking(obj); + } else { + over_tams = hr->obj_allocated_since_next_marking(obj); + } + + if (over_tams) { str = "over TAMS"; - if (_bitmap->isMarked((HeapWord*) obj)) + if (_bitmap->isMarked((HeapWord*) obj)) { str2 = " AND MARKED"; - } else if (_bitmap->isMarked((HeapWord*) obj)) + } + } else if (_bitmap->isMarked((HeapWord*) obj)) { str = "marked"; - else + } else { str = "#### NOT MARKED ####"; + } } _out->print_cr(" "PTR_FORMAT" contains "PTR_FORMAT" %s%s", @@ -2052,16 +1963,19 @@ class ReachablePrinterClosure: public BitMapClosure { private: - CMBitMapRO* _bitmap; + CMBitMapRO* _bitmap; outputStream* _out; + bool _use_prev_marking; public: - ReachablePrinterClosure(CMBitMapRO* bitmap, outputStream* out) : - _bitmap(bitmap), _out(out) { } + ReachablePrinterClosure(CMBitMapRO* bitmap, + outputStream* out, + bool use_prev_marking) : + _bitmap(bitmap), _out(out), _use_prev_marking(use_prev_marking) { } bool do_bit(size_t offset) { HeapWord* addr = _bitmap->offsetToHeapWord(offset); - ReachablePrinterOopClosure oopCl(_bitmap, _out); + ReachablePrinterOopClosure oopCl(_bitmap, _out, _use_prev_marking); _out->print_cr(" obj "PTR_FORMAT", offset %10d (marked)", addr, offset); oop(addr)->oop_iterate(&oopCl); @@ -2073,76 +1987,111 @@ class ObjInRegionReachablePrinterClosure : public ObjectClosure { private: - CMBitMapRO* _bitmap; + CMBitMapRO* _bitmap; outputStream* _out; + bool _use_prev_marking; public: + ObjInRegionReachablePrinterClosure(CMBitMapRO* bitmap, + outputStream* out, + bool use_prev_marking) : + _bitmap(bitmap), _out(out), _use_prev_marking(use_prev_marking) { } + void do_object(oop o) { - ReachablePrinterOopClosure oopCl(_bitmap, _out); + ReachablePrinterOopClosure oopCl(_bitmap, _out, _use_prev_marking); _out->print_cr(" obj "PTR_FORMAT" (over TAMS)", (void*) o); o->oop_iterate(&oopCl); _out->print_cr(""); } - - ObjInRegionReachablePrinterClosure(CMBitMapRO* bitmap, outputStream* out) : - _bitmap(bitmap), _out(out) { } }; class RegionReachablePrinterClosure : public HeapRegionClosure { private: - CMBitMapRO* _bitmap; + CMBitMapRO* _bitmap; outputStream* _out; + bool _use_prev_marking; public: bool doHeapRegion(HeapRegion* hr) { HeapWord* b = hr->bottom(); HeapWord* e = hr->end(); HeapWord* t = hr->top(); - HeapWord* p = hr->prev_top_at_mark_start(); + HeapWord* p = NULL; + if (_use_prev_marking) { + p = hr->prev_top_at_mark_start(); + } else { + p = hr->next_top_at_mark_start(); + } _out->print_cr("** ["PTR_FORMAT", "PTR_FORMAT"] top: "PTR_FORMAT" " - "PTAMS: "PTR_FORMAT, b, e, t, p); + "TAMS: "PTR_FORMAT, b, e, t, p); _out->print_cr(""); - ObjInRegionReachablePrinterClosure ocl(_bitmap, _out); + ObjInRegionReachablePrinterClosure ocl(_bitmap, _out, _use_prev_marking); hr->object_iterate_mem_careful(MemRegion(p, t), &ocl); return false; } - RegionReachablePrinterClosure(CMBitMapRO* bitmap, - outputStream* out) : - _bitmap(bitmap), _out(out) { } + RegionReachablePrinterClosure(CMBitMapRO* bitmap, + outputStream* out, + bool use_prev_marking) : + _bitmap(bitmap), _out(out), _use_prev_marking(use_prev_marking) { } }; -void ConcurrentMark::print_prev_bitmap_reachable() { - outputStream* out = gclog_or_tty; - -#if SEND_HEAP_DUMP_TO_FILE - guarantee(heap_dump_file == NULL, "Protocol"); - char fn_buf[100]; - sprintf(fn_buf, "/tmp/dump.txt.%d", os::current_process_id()); - heap_dump_file = fopen(fn_buf, "w"); - fileStream fstream(heap_dump_file); - out = &fstream; -#endif // SEND_HEAP_DUMP_TO_FILE - - RegionReachablePrinterClosure rcl(_prevMarkBitMap, out); - out->print_cr("--- ITERATING OVER REGIONS WITH PTAMS < TOP"); +void ConcurrentMark::print_reachable(bool use_prev_marking, const char* str) { + gclog_or_tty->print_cr("== Doing reachable object dump... "); + + if (G1PrintReachableBaseFile == NULL) { + gclog_or_tty->print_cr(" #### error: no base file defined"); + return; + } + + if (strlen(G1PrintReachableBaseFile) + 1 + strlen(str) > + (JVM_MAXPATHLEN - 1)) { + gclog_or_tty->print_cr(" #### error: file name too long"); + return; + } + + char file_name[JVM_MAXPATHLEN]; + sprintf(file_name, "%s.%s", G1PrintReachableBaseFile, str); + gclog_or_tty->print_cr(" dumping to file %s", file_name); + + fileStream fout(file_name); + if (!fout.is_open()) { + gclog_or_tty->print_cr(" #### error: could not open file"); + return; + } + + outputStream* out = &fout; + + CMBitMapRO* bitmap = NULL; + if (use_prev_marking) { + bitmap = _prevMarkBitMap; + } else { + bitmap = _nextMarkBitMap; + } + + out->print_cr("-- USING %s", (use_prev_marking) ? "PTAMS" : "NTAMS"); + out->cr(); + + RegionReachablePrinterClosure rcl(bitmap, out, use_prev_marking); + out->print_cr("--- ITERATING OVER REGIONS WITH TAMS < TOP"); + out->cr(); _g1h->heap_region_iterate(&rcl); - out->print_cr(""); - - ReachablePrinterClosure cl(_prevMarkBitMap, out); - out->print_cr("--- REACHABLE OBJECTS ON THE BITMAP"); - _prevMarkBitMap->iterate(&cl); - out->print_cr(""); - -#if SEND_HEAP_DUMP_TO_FILE - fclose(heap_dump_file); - heap_dump_file = NULL; -#endif // SEND_HEAP_DUMP_TO_FILE + out->cr(); + + ReachablePrinterClosure cl(bitmap, out, use_prev_marking); + out->print_cr("--- ITERATING OVER MARKED OBJECTS ON THE BITMAP"); + out->cr(); + bitmap->iterate(&cl); + out->cr(); + + gclog_or_tty->print_cr(" done"); } +#endif // PRODUCT + // This note is for drainAllSATBBuffers and the code in between. // In the future we could reuse a task to do this work during an // evacuation pause (since now tasks are not active and can be claimed @@ -2170,7 +2119,7 @@ HeapWord* objAddr = (HeapWord*) obj; assert(obj->is_oop_or_null(true /* ignore mark word */), "Error"); if (_g1h->is_in_g1_reserved(objAddr)) { - tmp_guarantee_CM( obj != NULL, "is_in_g1_reserved should ensure this" ); + assert(obj != NULL, "is_in_g1_reserved should ensure this"); HeapRegion* hr = _g1h->heap_region_containing(obj); if (_g1h->is_obj_ill(obj, hr)) { if (verbose_high()) @@ -2212,7 +2161,7 @@ satb_mq_set.iterate_closure_all_threads(); satb_mq_set.set_closure(NULL); - guarantee( satb_mq_set.completed_buffers_num() == 0, "invariant" ); + assert(satb_mq_set.completed_buffers_num() == 0, "invariant"); } void ConcurrentMark::markPrev(oop p) { @@ -2245,7 +2194,7 @@ // _heap_end will not change underneath our feet; it only changes at // yield points. while (finger < _heap_end) { - tmp_guarantee_CM( _g1h->is_in_g1_reserved(finger), "invariant" ); + assert(_g1h->is_in_g1_reserved(finger), "invariant"); // is the gap between reading the finger and doing the CAS too long? @@ -2267,7 +2216,7 @@ // notice that _finger == end cannot be guaranteed here since, // someone else might have moved the finger even further - guarantee( _finger >= end, "the finger should have moved forward" ); + assert(_finger >= end, "the finger should have moved forward"); if (verbose_low()) gclog_or_tty->print_cr("[%d] we were successful with region = " @@ -2279,8 +2228,8 @@ "returning it ", task_num, curr_region); return curr_region; } else { - tmp_guarantee_CM( limit == bottom, - "the region limit should be at bottom" ); + assert(limit == bottom, + "the region limit should be at bottom"); if (verbose_low()) gclog_or_tty->print_cr("[%d] region "PTR_FORMAT" is empty, " "returning NULL", task_num, curr_region); @@ -2289,7 +2238,7 @@ return NULL; } } else { - guarantee( _finger > finger, "the finger should have moved forward" ); + assert(_finger > finger, "the finger should have moved forward"); if (verbose_low()) gclog_or_tty->print_cr("[%d] somebody else moved the finger, " "global finger = "PTR_FORMAT", " @@ -2327,7 +2276,7 @@ if (_regionStack.invalidate_entries_into_cset()) { // otherwise, any gray objects copied during the evacuation pause // might not be visited. - guarantee( _should_gray_objects, "invariant" ); + assert(_should_gray_objects, "invariant"); } } @@ -2625,24 +2574,6 @@ _should_gray_objects = true; } -void ConcurrentMark::disable_co_trackers() { - if (has_aborted()) { - if (_cleanup_co_tracker.enabled()) - _cleanup_co_tracker.disable(); - for (int i = 0; i < (int)_max_task_num; ++i) { - CMTask* task = _tasks[i]; - if (task->co_tracker_enabled()) - task->disable_co_tracker(); - } - } else { - guarantee( !_cleanup_co_tracker.enabled(), "invariant" ); - for (int i = 0; i < (int)_max_task_num; ++i) { - CMTask* task = _tasks[i]; - guarantee( !task->co_tracker_enabled(), "invariant" ); - } - } -} - // abandon current marking iteration due to a Full GC void ConcurrentMark::abort() { // Clear all marks to force marking thread to do nothing @@ -2700,6 +2631,10 @@ cmThread()->vtime_count_accum()); } +void ConcurrentMark::print_worker_threads_on(outputStream* st) const { + _parallel_workers->print_worker_threads_on(st); +} + // Closures // XXX: there seems to be a lot of code duplication here; // should refactor and consolidate the shared code. @@ -2774,12 +2709,12 @@ bool do_bit(size_t offset) { HeapWord* addr = _nextMarkBitMap->offsetToHeapWord(offset); - tmp_guarantee_CM( _nextMarkBitMap->isMarked(addr), "invariant" ); - tmp_guarantee_CM( addr < _cm->finger(), "invariant" ); + assert(_nextMarkBitMap->isMarked(addr), "invariant"); + assert( addr < _cm->finger(), "invariant"); if (_scanning_heap_region) { statsOnly( _task->increase_objs_found_on_bitmap() ); - tmp_guarantee_CM( addr >= _task->finger(), "invariant" ); + assert(addr >= _task->finger(), "invariant"); // We move that task's local finger along. _task->move_finger_to(addr); } else { @@ -2824,8 +2759,9 @@ virtual void do_oop( oop* p) { do_oop_work(p); } template void do_oop_work(T* p) { - tmp_guarantee_CM( _g1h->is_in_g1_reserved((HeapWord*) p), "invariant" ); - tmp_guarantee_CM( !_g1h->heap_region_containing((HeapWord*) p)->is_on_free_list(), "invariant" ); + assert(_g1h->is_in_g1_reserved((HeapWord*) p), "invariant"); + assert(!_g1h->heap_region_containing((HeapWord*) p)->is_on_free_list(), + "invariant"); oop obj = oopDesc::load_decode_heap_oop(p); if (_cm->verbose_high()) @@ -2842,8 +2778,11 @@ }; void CMTask::setup_for_region(HeapRegion* hr) { - tmp_guarantee_CM( hr != NULL && !hr->continuesHumongous(), - "claim_region() should have filtered out continues humongous regions" ); + // Separated the asserts so that we know which one fires. + assert(hr != NULL, + "claim_region() should have filtered out continues humongous regions"); + assert(!hr->continuesHumongous(), + "claim_region() should have filtered out continues humongous regions"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] setting up for region "PTR_FORMAT, @@ -2871,9 +2810,9 @@ // as the region is not supposed to be empty in the first place) _finger = bottom; } else if (limit >= _region_limit) { - tmp_guarantee_CM( limit >= _finger, "peace of mind" ); + assert(limit >= _finger, "peace of mind"); } else { - tmp_guarantee_CM( limit < _region_limit, "only way to get here" ); + assert(limit < _region_limit, "only way to get here"); // This can happen under some pretty unusual circumstances. An // evacuation pause empties the region underneath our feet (NTAMS // at bottom). We then do some allocation in the region (NTAMS @@ -2891,7 +2830,7 @@ } void CMTask::giveup_current_region() { - tmp_guarantee_CM( _curr_region != NULL, "invariant" ); + assert(_curr_region != NULL, "invariant"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] giving up region "PTR_FORMAT, _task_id, _curr_region); @@ -2909,7 +2848,7 @@ } void CMTask::reset(CMBitMap* nextMarkBitMap) { - guarantee( nextMarkBitMap != NULL, "invariant" ); + guarantee(nextMarkBitMap != NULL, "invariant"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] resetting", _task_id); @@ -2975,7 +2914,7 @@ HeapWord* objAddr = (HeapWord*) obj; assert(obj->is_oop_or_null(true /* ignore mark word */), "Error"); if (_g1h->is_in_g1_reserved(objAddr)) { - tmp_guarantee_CM( obj != NULL, "is_in_g1_reserved should ensure this" ); + assert(obj != NULL, "is_in_g1_reserved should ensure this"); HeapRegion* hr = _g1h->heap_region_containing(obj); if (_g1h->is_obj_ill(obj, hr)) { if (_cm->verbose_high()) @@ -3036,10 +2975,11 @@ void CMTask::push(oop obj) { HeapWord* objAddr = (HeapWord*) obj; - tmp_guarantee_CM( _g1h->is_in_g1_reserved(objAddr), "invariant" ); - tmp_guarantee_CM( !_g1h->heap_region_containing(objAddr)->is_on_free_list(), "invariant" ); - tmp_guarantee_CM( !_g1h->is_obj_ill(obj), "invariant" ); - tmp_guarantee_CM( _nextMarkBitMap->isMarked(objAddr), "invariant" ); + assert(_g1h->is_in_g1_reserved(objAddr), "invariant"); + assert(!_g1h->heap_region_containing(objAddr)->is_on_free_list(), + "invariant"); + assert(!_g1h->is_obj_ill(obj), "invariant"); + assert(_nextMarkBitMap->isMarked(objAddr), "invariant"); if (_cm->verbose_high()) gclog_or_tty->print_cr("[%d] pushing "PTR_FORMAT, _task_id, (void*) obj); @@ -3058,7 +2998,7 @@ // stack, we should have definitely removed some entries from the // local queue. So, there must be space on it. bool success = _task_queue->push(obj); - tmp_guarantee_CM( success, "invariant" ); + assert(success, "invariant"); } statsOnly( int tmp_size = _task_queue->size(); @@ -3068,9 +3008,9 @@ } void CMTask::reached_limit() { - tmp_guarantee_CM( _words_scanned >= _words_scanned_limit || - _refs_reached >= _refs_reached_limit , - "shouldn't have been called otherwise" ); + assert(_words_scanned >= _words_scanned_limit || + _refs_reached >= _refs_reached_limit , + "shouldn't have been called otherwise"); regular_clock_call(); } @@ -3228,8 +3168,8 @@ oop buffer[global_stack_transfer_size]; int n; _cm->mark_stack_pop(buffer, global_stack_transfer_size, &n); - tmp_guarantee_CM( n <= global_stack_transfer_size, - "we should not pop more than the given limit" ); + assert(n <= global_stack_transfer_size, + "we should not pop more than the given limit"); if (n > 0) { // yes, we did actually pop at least one entry @@ -3241,7 +3181,7 @@ bool success = _task_queue->push(buffer[i]); // We only call this when the local queue is empty or under a // given target limit. So, we do not expect this push to fail. - tmp_guarantee_CM( success, "invariant" ); + assert(success, "invariant"); } statsOnly( int tmp_size = _task_queue->size(); @@ -3281,10 +3221,9 @@ gclog_or_tty->print_cr("[%d] popped "PTR_FORMAT, _task_id, (void*) obj); - tmp_guarantee_CM( _g1h->is_in_g1_reserved((HeapWord*) obj), - "invariant" ); - tmp_guarantee_CM( !_g1h->heap_region_containing(obj)->is_on_free_list(), - "invariant" ); + assert(_g1h->is_in_g1_reserved((HeapWord*) obj), "invariant" ); + assert(!_g1h->heap_region_containing(obj)->is_on_free_list(), + "invariant"); scan_object(obj); @@ -3306,7 +3245,7 @@ // We have a policy to drain the local queue before we attempt to // drain the global stack. - tmp_guarantee_CM( partially || _task_queue->size() == 0, "invariant" ); + assert(partially || _task_queue->size() == 0, "invariant"); // Decide what the target size is, depending whether we're going to // drain it partially (so that other tasks can steal if they run out @@ -3387,9 +3326,9 @@ _draining_satb_buffers = false; - tmp_guarantee_CM( has_aborted() || - concurrent() || - satb_mq_set.completed_buffers_num() == 0, "invariant" ); + assert(has_aborted() || + concurrent() || + satb_mq_set.completed_buffers_num() == 0, "invariant"); if (ParallelGCThreads > 0) satb_mq_set.set_par_closure(_task_id, NULL); @@ -3405,8 +3344,8 @@ if (has_aborted()) return; - tmp_guarantee_CM( _region_finger == NULL, - "it should be NULL when we're not scanning a region" ); + assert(_region_finger == NULL, + "it should be NULL when we're not scanning a region"); if (!_cm->region_stack_empty()) { if (_cm->verbose_low()) @@ -3422,12 +3361,12 @@ gclog_or_tty->print_cr("[%d] we are scanning region " "["PTR_FORMAT", "PTR_FORMAT")", _task_id, mr.start(), mr.end()); - tmp_guarantee_CM( mr.end() <= _cm->finger(), - "otherwise the region shouldn't be on the stack" ); + assert(mr.end() <= _cm->finger(), + "otherwise the region shouldn't be on the stack"); assert(!mr.is_empty(), "Only non-empty regions live on the region stack"); if (_nextMarkBitMap->iterate(bc, mr)) { - tmp_guarantee_CM( !has_aborted(), - "cannot abort the task without aborting the bitmap iteration" ); + assert(!has_aborted(), + "cannot abort the task without aborting the bitmap iteration"); // We finished iterating over the region without aborting. regular_clock_call(); @@ -3439,14 +3378,14 @@ statsOnly(if (mr.start() != NULL) ++_region_stack_pops ); } } else { - guarantee( has_aborted(), "currently the only way to do so" ); + assert(has_aborted(), "currently the only way to do so"); // The only way to abort the bitmap iteration is to return // false from the do_bit() method. However, inside the // do_bit() method we move the _region_finger to point to the // object currently being looked at. So, if we bail out, we // have definitely set _region_finger to something non-null. - guarantee( _region_finger != NULL, "invariant" ); + assert(_region_finger != NULL, "invariant"); // The iteration was actually aborted. So now _region_finger // points to the address of the object we last scanned. If we @@ -3475,13 +3414,6 @@ _region_finger = NULL; } - // We only push regions on the region stack during evacuation - // pauses. So if we come out the above iteration because we region - // stack is empty, it will remain empty until the next yield - // point. So, the guarantee below is safe. - guarantee( has_aborted() || _cm->region_stack_empty(), - "only way to exit the loop" ); - if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] drained region stack, size = %d", _task_id, _cm->region_stack_size()); @@ -3639,21 +3571,21 @@ *****************************************************************************/ void CMTask::do_marking_step(double time_target_ms) { - guarantee( time_target_ms >= 1.0, "minimum granularity is 1ms" ); - guarantee( concurrent() == _cm->concurrent(), "they should be the same" ); - - guarantee( concurrent() || _cm->region_stack_empty(), - "the region stack should have been cleared before remark" ); - guarantee( _region_finger == NULL, - "this should be non-null only when a region is being scanned" ); + assert(time_target_ms >= 1.0, "minimum granularity is 1ms"); + assert(concurrent() == _cm->concurrent(), "they should be the same"); + + assert(concurrent() || _cm->region_stack_empty(), + "the region stack should have been cleared before remark"); + assert(_region_finger == NULL, + "this should be non-null only when a region is being scanned"); G1CollectorPolicy* g1_policy = _g1h->g1_policy(); - guarantee( _task_queues != NULL, "invariant" ); - guarantee( _task_queue != NULL, "invariant" ); - guarantee( _task_queues->queue(_task_id) == _task_queue, "invariant" ); - - guarantee( !_claimed, - "only one thread should claim this task at any one time" ); + assert(_task_queues != NULL, "invariant"); + assert(_task_queue != NULL, "invariant"); + assert(_task_queues->queue(_task_id) == _task_queue, "invariant"); + + assert(!_claimed, + "only one thread should claim this task at any one time"); // OK, this doesn't safeguard again all possible scenarios, as it is // possible for two threads to set the _claimed flag at the same @@ -3724,9 +3656,8 @@ do { if (!has_aborted() && _curr_region != NULL) { // This means that we're already holding on to a region. - tmp_guarantee_CM( _finger != NULL, - "if region is not NULL, then the finger " - "should not be NULL either" ); + assert(_finger != NULL, "if region is not NULL, then the finger " + "should not be NULL either"); // We might have restarted this task after an evacuation pause // which might have evacuated the region we're holding on to @@ -3758,13 +3689,13 @@ giveup_current_region(); regular_clock_call(); } else { - guarantee( has_aborted(), "currently the only way to do so" ); + assert(has_aborted(), "currently the only way to do so"); // The only way to abort the bitmap iteration is to return // false from the do_bit() method. However, inside the // do_bit() method we move the _finger to point to the // object currently being looked at. So, if we bail out, we // have definitely set _finger to something non-null. - guarantee( _finger != NULL, "invariant" ); + assert(_finger != NULL, "invariant"); // Region iteration was actually aborted. So now _finger // points to the address of the object we last scanned. If we @@ -3791,9 +3722,10 @@ while (!has_aborted() && _curr_region == NULL && !_cm->out_of_regions()) { // We are going to try to claim a new region. We should have // given up on the previous one. - tmp_guarantee_CM( _curr_region == NULL && - _finger == NULL && - _region_limit == NULL, "invariant" ); + // Separated the asserts so that we know which one fires. + assert(_curr_region == NULL, "invariant"); + assert(_finger == NULL, "invariant"); + assert(_region_limit == NULL, "invariant"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] trying to claim a new region", _task_id); HeapRegion* claimed_region = _cm->claim_region(_task_id); @@ -3807,7 +3739,7 @@ _task_id, claimed_region); setup_for_region(claimed_region); - tmp_guarantee_CM( _curr_region == claimed_region, "invariant" ); + assert(_curr_region == claimed_region, "invariant"); } // It is important to call the regular clock here. It might take // a while to claim a region if, for example, we hit a large @@ -3818,8 +3750,8 @@ } if (!has_aborted() && _curr_region == NULL) { - tmp_guarantee_CM( _cm->out_of_regions(), - "at this point we should be out of regions" ); + assert(_cm->out_of_regions(), + "at this point we should be out of regions"); } } while ( _curr_region != NULL && !has_aborted()); @@ -3828,8 +3760,8 @@ // tasks might be pushing objects to it concurrently. We also cannot // check if the region stack is empty because if a thread is aborting // it can push a partially done region back. - tmp_guarantee_CM( _cm->out_of_regions(), - "at this point we should be out of regions" ); + assert(_cm->out_of_regions(), + "at this point we should be out of regions"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] all regions claimed", _task_id); @@ -3853,8 +3785,8 @@ // tasks might be pushing objects to it concurrently. We also cannot // check if the region stack is empty because if a thread is aborting // it can push a partially done region back. - guarantee( _cm->out_of_regions() && - _task_queue->size() == 0, "only way to reach here" ); + assert(_cm->out_of_regions() && _task_queue->size() == 0, + "only way to reach here"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] starting to steal", _task_id); @@ -3870,8 +3802,8 @@ statsOnly( ++_steals ); - tmp_guarantee_CM( _nextMarkBitMap->isMarked((HeapWord*) obj), - "any stolen object should be marked" ); + assert(_nextMarkBitMap->isMarked((HeapWord*) obj), + "any stolen object should be marked"); scan_object(obj); // And since we're towards the end, let's totally drain the @@ -3891,8 +3823,9 @@ // tasks might be concurrently pushing objects on it. We also cannot // check if the region stack is empty because if a thread is aborting // it can push a partially done region back. - guarantee( _cm->out_of_regions() && - _task_queue->size() == 0, "only way to reach here" ); + // Separated the asserts so that we know which one fires. + assert(_cm->out_of_regions(), "only way to reach here"); + assert(_task_queue->size() == 0, "only way to reach here"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] starting termination protocol", _task_id); @@ -3912,7 +3845,7 @@ if (_task_id == 0) { // let's allow task 0 to do this if (concurrent()) { - guarantee( _cm->concurrent_marking_in_progress(), "invariant" ); + assert(_cm->concurrent_marking_in_progress(), "invariant"); // we need to set this to false before the next // safepoint. This way we ensure that the marking phase // doesn't observe any more heap expansions. @@ -3921,15 +3854,16 @@ } // We can now guarantee that the global stack is empty, since - // all other tasks have finished. - guarantee( _cm->out_of_regions() && - _cm->region_stack_empty() && - _cm->mark_stack_empty() && - _task_queue->size() == 0 && - !_cm->has_overflown() && - !_cm->mark_stack_overflow() && - !_cm->region_stack_overflow(), - "only way to reach here" ); + // all other tasks have finished. We separated the guarantees so + // that, if a condition is false, we can immediately find out + // which one. + guarantee(_cm->out_of_regions(), "only way to reach here"); + guarantee(_cm->region_stack_empty(), "only way to reach here"); + guarantee(_cm->mark_stack_empty(), "only way to reach here"); + guarantee(_task_queue->size() == 0, "only way to reach here"); + guarantee(!_cm->has_overflown(), "only way to reach here"); + guarantee(!_cm->mark_stack_overflow(), "only way to reach here"); + guarantee(!_cm->region_stack_overflow(), "only way to reach here"); if (_cm->verbose_low()) gclog_or_tty->print_cr("[%d] all tasks terminated", _task_id); @@ -4018,15 +3952,14 @@ CMTaskQueue* task_queue, CMTaskQueueSet* task_queues) : _g1h(G1CollectedHeap::heap()), - _co_tracker(G1CMGroup), _task_id(task_id), _cm(cm), _claimed(false), _nextMarkBitMap(NULL), _hash_seed(17), _task_queue(task_queue), _task_queues(task_queues), _oop_closure(NULL) { - guarantee( task_queue != NULL, "invariant" ); - guarantee( task_queues != NULL, "invariant" ); + guarantee(task_queue != NULL, "invariant"); + guarantee(task_queues != NULL, "invariant"); statsOnly( _clock_due_to_scanning = 0; _clock_due_to_marking = 0 ); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentMark.hpp --- a/src/share/vm/gc_implementation/g1/concurrentMark.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -295,12 +295,6 @@ } while (0) #endif // _MARKING_STATS_ -// Some extra guarantees that I like to also enable in optimised mode -// when debugging. If you want to enable them, comment out the assert -// macro and uncomment out the guaratee macro -// #define tmp_guarantee_CM(expr, str) guarantee(expr, str) -#define tmp_guarantee_CM(expr, str) assert(expr, str) - typedef enum { no_verbose = 0, // verbose turned off stats_verbose, // only prints stats at the end of marking @@ -407,8 +401,6 @@ // verbose level CMVerboseLevel _verbose_level; - COTracker _cleanup_co_tracker; - // These two fields are used to implement the optimisation that // avoids pushing objects on the global/region stack if there are // no collection set regions above the lowest finger. @@ -487,15 +479,15 @@ // Returns the task with the given id CMTask* task(int id) { - guarantee( 0 <= id && id < (int) _active_tasks, "task id not within " - "active bounds" ); + assert(0 <= id && id < (int) _active_tasks, + "task id not within active bounds"); return _tasks[id]; } // Returns the task queue with the given id CMTaskQueue* task_queue(int id) { - guarantee( 0 <= id && id < (int) _active_tasks, "task queue id not within " - "active bounds" ); + assert(0 <= id && id < (int) _active_tasks, + "task queue id not within active bounds"); return (CMTaskQueue*) _task_queues->queue(id); } @@ -620,10 +612,11 @@ // we do nothing. void markAndGrayObjectIfNecessary(oop p); - // This iterates over the bitmap of the previous marking and prints - // out all objects that are marked on the bitmap and indicates - // whether what they point to is also marked or not. - void print_prev_bitmap_reachable(); + // This iterates over the marking bitmap (either prev or next) and + // prints out all objects that are marked on the bitmap and indicates + // whether what they point to is also marked or not. It also iterates + // the objects over TAMS (either prev or next). + void print_reachable(bool use_prev_marking, const char* str); // Clear the next marking bitmap (will be called concurrently). void clearNextBitmap(); @@ -720,13 +713,13 @@ // Called to abort the marking cycle after a Full GC takes palce. void abort(); - void disable_co_trackers(); - // This prints the global/local fingers. It is used for debugging. NOT_PRODUCT(void print_finger();) void print_summary_info(); + void print_worker_threads_on(outputStream* st) const; + // The following indicate whether a given verbose level has been // set. Notice that anything above stats is conditional to // _MARKING_VERBOSE_ having been set to 1 @@ -773,9 +766,6 @@ // number of calls to this task int _calls; - // concurrent overhead over a single CPU for this task - COTracker _co_tracker; - // when the virtual timer reaches this time, the marking step should // exit double _time_target_ms; @@ -928,27 +918,6 @@ void set_concurrent(bool concurrent) { _concurrent = concurrent; } - void enable_co_tracker() { - guarantee( !_co_tracker.enabled(), "invariant" ); - _co_tracker.enable(); - } - void disable_co_tracker() { - guarantee( _co_tracker.enabled(), "invariant" ); - _co_tracker.disable(); - } - bool co_tracker_enabled() { - return _co_tracker.enabled(); - } - void reset_co_tracker(double starting_conc_overhead = 0.0) { - _co_tracker.reset(starting_conc_overhead); - } - void start_co_tracker() { - _co_tracker.start(); - } - void update_co_tracker(bool force_end = false) { - _co_tracker.update(force_end); - } - // The main method of this class which performs a marking step // trying not to exceed the given duration. However, it might exit // prematurely, according to some conditions (i.e. SATB buffers are @@ -987,8 +956,7 @@ // It scans an object and visits its children. void scan_object(oop obj) { - tmp_guarantee_CM( _nextMarkBitMap->isMarked((HeapWord*) obj), - "invariant" ); + assert(_nextMarkBitMap->isMarked((HeapWord*) obj), "invariant"); if (_cm->verbose_high()) gclog_or_tty->print_cr("[%d] we're scanning object "PTR_FORMAT, @@ -1027,14 +995,13 @@ // moves the local finger to a new location inline void move_finger_to(HeapWord* new_finger) { - tmp_guarantee_CM( new_finger >= _finger && new_finger < _region_limit, - "invariant" ); + assert(new_finger >= _finger && new_finger < _region_limit, "invariant"); _finger = new_finger; } // moves the region finger to a new location inline void move_region_finger_to(HeapWord* new_finger) { - tmp_guarantee_CM( new_finger < _cm->finger(), "invariant" ); + assert(new_finger < _cm->finger(), "invariant"); _region_finger = new_finger; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp --- a/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -260,10 +260,6 @@ } } - _sts.join(); - _cm->disable_co_trackers(); - _sts.leave(); - // we now want to allow clearing of the marking bitmap to be // suspended by a collection pause. _sts.join(); @@ -290,10 +286,14 @@ } } -void ConcurrentMarkThread::print() { - gclog_or_tty->print("\"Concurrent Mark GC Thread\" "); - Thread::print(); - gclog_or_tty->cr(); +void ConcurrentMarkThread::print() const { + print_on(tty); +} + +void ConcurrentMarkThread::print_on(outputStream* st) const { + st->print("\"G1 Main Concurrent Mark GC Thread\" "); + Thread::print_on(st); + st->cr(); } void ConcurrentMarkThread::sleepBeforeNextCycle() { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp --- a/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -57,7 +57,8 @@ static SurrogateLockerThread* slt() { return _slt; } // Printing - void print(); + void print_on(outputStream* st) const; + void print() const; // Total virtual time so far. double vtime_accum(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentZFThread.cpp --- a/src/share/vm/gc_implementation/g1/concurrentZFThread.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentZFThread.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 Sun Microsystems, 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 @@ -35,8 +35,7 @@ int ConcurrentZFThread::_regions_filled = 0; ConcurrentZFThread::ConcurrentZFThread() : - ConcurrentGCThread(), - _co_tracker(G1ZFGroup) + ConcurrentGCThread() { create_and_start(); } @@ -71,8 +70,6 @@ Thread* thr_self = Thread::current(); _vtime_start = os::elapsedVTime(); wait_for_universe_init(); - _co_tracker.enable(); - _co_tracker.start(); G1CollectedHeap* g1 = G1CollectedHeap::heap(); _sts.join(); @@ -135,10 +132,7 @@ } _vtime_accum = (os::elapsedVTime() - _vtime_start); _sts.join(); - - _co_tracker.update(); } - _co_tracker.update(false); _sts.leave(); assert(_should_terminate, "just checking"); @@ -163,10 +157,14 @@ } } -void ConcurrentZFThread::print() { - gclog_or_tty->print("\"Concurrent ZF Thread\" "); - Thread::print(); - gclog_or_tty->cr(); +void ConcurrentZFThread::print() const { + print_on(tty); +} + +void ConcurrentZFThread::print_on(outputStream* st) const { + st->print("\"G1 Concurrent Zero-Fill Thread\" "); + Thread::print_on(st); + st->cr(); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/concurrentZFThread.hpp --- a/src/share/vm/gc_implementation/g1/concurrentZFThread.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/concurrentZFThread.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -42,8 +42,6 @@ // Number of regions CFZ thread fills. static int _regions_filled; - COTracker _co_tracker; - double _vtime_start; // Initial virtual time. // These are static because the "print_summary_info" method is, and @@ -63,7 +61,8 @@ virtual void run(); // Printing - void print(); + void print_on(outputStream* st) const; + void print() const; // Waits until "r" has been zero-filled. Requires caller to hold the // ZF_mon. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -61,8 +61,8 @@ #pragma warning( disable:4355 ) // 'this' : used in base member initializer list #endif // _MSC_VER -DirtyCardQueueSet::DirtyCardQueueSet() : - PtrQueueSet(true /*notify_when_complete*/), +DirtyCardQueueSet::DirtyCardQueueSet(bool notify_when_complete) : + PtrQueueSet(notify_when_complete), _closure(NULL), _shared_dirty_card_queue(this, true /*perm*/), _free_ids(NULL), @@ -77,12 +77,12 @@ } void DirtyCardQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock, + int process_completed_threshold, int max_completed_queue, Mutex* lock, PtrQueueSet* fl_owner) { - PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue, fl_owner); + PtrQueueSet::initialize(cbl_mon, fl_lock, process_completed_threshold, + max_completed_queue, fl_owner); set_buffer_size(G1UpdateBufferSize); - set_process_completed_threshold(G1UpdateBufferQueueProcessingThreshold); - _shared_dirty_card_queue.set_lock(lock); _free_ids = new FreeIdSet((int) num_par_ids(), _cbl_mon); } @@ -154,9 +154,10 @@ return b; } -DirtyCardQueueSet::CompletedBufferNode* -DirtyCardQueueSet::get_completed_buffer_lock(int stop_at) { - CompletedBufferNode* nd = NULL; + +BufferNode* +DirtyCardQueueSet::get_completed_buffer(int stop_at) { + BufferNode* nd = NULL; MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); if ((int)_n_completed_buffers <= stop_at) { @@ -166,53 +167,31 @@ if (_completed_buffers_head != NULL) { nd = _completed_buffers_head; - _completed_buffers_head = nd->next; + _completed_buffers_head = nd->next(); if (_completed_buffers_head == NULL) _completed_buffers_tail = NULL; _n_completed_buffers--; + assert(_n_completed_buffers >= 0, "Invariant"); } debug_only(assert_completed_buffer_list_len_correct_locked()); return nd; } -// We only do this in contexts where there is no concurrent enqueueing. -DirtyCardQueueSet::CompletedBufferNode* -DirtyCardQueueSet::get_completed_buffer_CAS() { - CompletedBufferNode* nd = _completed_buffers_head; - - while (nd != NULL) { - CompletedBufferNode* next = nd->next; - CompletedBufferNode* result = - (CompletedBufferNode*)Atomic::cmpxchg_ptr(next, - &_completed_buffers_head, - nd); - if (result == nd) { - return result; - } else { - nd = _completed_buffers_head; - } - } - assert(_completed_buffers_head == NULL, "Loop post"); - _completed_buffers_tail = NULL; - return NULL; -} - bool DirtyCardQueueSet:: apply_closure_to_completed_buffer_helper(int worker_i, - CompletedBufferNode* nd) { + BufferNode* nd) { if (nd != NULL) { + void **buf = BufferNode::make_buffer_from_node(nd); + size_t index = nd->index(); bool b = - DirtyCardQueue::apply_closure_to_buffer(_closure, nd->buf, - nd->index, _sz, + DirtyCardQueue::apply_closure_to_buffer(_closure, buf, + index, _sz, true, worker_i); - void** buf = nd->buf; - size_t index = nd->index; - delete nd; if (b) { deallocate_buffer(buf); return true; // In normal case, go on to next buffer. } else { - enqueue_complete_buffer(buf, index, true); + enqueue_complete_buffer(buf, index); return false; } } else { @@ -222,40 +201,36 @@ bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i, int stop_at, - bool with_CAS) + bool during_pause) { - CompletedBufferNode* nd = NULL; - if (with_CAS) { - guarantee(stop_at == 0, "Precondition"); - nd = get_completed_buffer_CAS(); - } else { - nd = get_completed_buffer_lock(stop_at); - } + assert(!during_pause || stop_at == 0, "Should not leave any completed buffers during a pause"); + BufferNode* nd = get_completed_buffer(stop_at); bool res = apply_closure_to_completed_buffer_helper(worker_i, nd); if (res) Atomic::inc(&_processed_buffers_rs_thread); return res; } void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() { - CompletedBufferNode* nd = _completed_buffers_head; + BufferNode* nd = _completed_buffers_head; while (nd != NULL) { bool b = - DirtyCardQueue::apply_closure_to_buffer(_closure, nd->buf, 0, _sz, - false); + DirtyCardQueue::apply_closure_to_buffer(_closure, + BufferNode::make_buffer_from_node(nd), + 0, _sz, false); guarantee(b, "Should not stop early."); - nd = nd->next; + nd = nd->next(); } } void DirtyCardQueueSet::abandon_logs() { assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); - CompletedBufferNode* buffers_to_delete = NULL; + BufferNode* buffers_to_delete = NULL; { MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); while (_completed_buffers_head != NULL) { - CompletedBufferNode* nd = _completed_buffers_head; - _completed_buffers_head = nd->next; - nd->next = buffers_to_delete; + BufferNode* nd = _completed_buffers_head; + _completed_buffers_head = nd->next(); + nd->set_next(buffers_to_delete); buffers_to_delete = nd; } _n_completed_buffers = 0; @@ -263,10 +238,9 @@ debug_only(assert_completed_buffer_list_len_correct_locked()); } while (buffers_to_delete != NULL) { - CompletedBufferNode* nd = buffers_to_delete; - buffers_to_delete = nd->next; - deallocate_buffer(nd->buf); - delete nd; + BufferNode* nd = buffers_to_delete; + buffers_to_delete = nd->next(); + deallocate_buffer(BufferNode::make_buffer_from_node(nd)); } // Since abandon is done only at safepoints, we can safely manipulate // these queues. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 Sun Microsystems, 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 @@ -84,11 +84,12 @@ jint _processed_buffers_rs_thread; public: - DirtyCardQueueSet(); + DirtyCardQueueSet(bool notify_when_complete = true); void initialize(Monitor* cbl_mon, Mutex* fl_lock, - int max_completed_queue = 0, - Mutex* lock = NULL, PtrQueueSet* fl_owner = NULL); + int process_completed_threshold, + int max_completed_queue, + Mutex* lock, PtrQueueSet* fl_owner = NULL); // The number of parallel ids that can be claimed to allow collector or // mutator threads to do card-processing work. @@ -120,12 +121,13 @@ // is returned to the completed buffer set, and this call returns false. bool apply_closure_to_completed_buffer(int worker_i = 0, int stop_at = 0, - bool with_CAS = false); + bool during_pause = false); + bool apply_closure_to_completed_buffer_helper(int worker_i, - CompletedBufferNode* nd); + BufferNode* nd); - CompletedBufferNode* get_completed_buffer_CAS(); - CompletedBufferNode* get_completed_buffer_lock(int stop_at); + BufferNode* get_completed_buffer(int stop_at); + // Applies the current closure to all completed buffers, // non-consumptively. void apply_closure_to_all_completed_buffers(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,6 +25,8 @@ #include "incls/_precompiled.incl" #include "incls/_g1CollectedHeap.cpp.incl" +size_t G1CollectedHeap::_humongous_object_threshold_in_words = 0; + // turn it on so that the contents of the young list (scan-only / // to-be-collected) are printed at "strategic" points before / during // / after the collection --- this is useful for debugging @@ -581,7 +583,7 @@ res->zero_fill_state() == HeapRegion::Allocated)), "Non-young alloc Regions must be zero filled (and non-H)"); - if (G1PrintRegions) { + if (G1PrintHeapRegions) { if (res != NULL) { gclog_or_tty->print_cr("new alloc region %d:["PTR_FORMAT", "PTR_FORMAT"], " "top "PTR_FORMAT, @@ -926,8 +928,9 @@ TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); TraceTime t(full ? "Full GC (System.gc())" : "Full GC", PrintGC, true, gclog_or_tty); + TraceMemoryManagerStats tms(true /* fullGC */); + double start = os::elapsedTime(); - GCOverheadReporter::recordSTWStart(start); g1_policy()->record_full_collection_start(); gc_prologue(true); @@ -1000,6 +1003,8 @@ COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); + MemoryService::track_memory_usage(); + if (VerifyAfterGC && total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification gclog_or_tty->print(" VerifyAfterGC:"); @@ -1049,7 +1054,6 @@ } double end = os::elapsedTime(); - GCOverheadReporter::recordSTWEnd(end); g1_policy()->record_full_collection_end(); #ifdef TRACESPINNING @@ -1371,6 +1375,7 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) : SharedHeap(policy_), _g1_policy(policy_), + _dirty_card_queue_set(false), _ref_processor(NULL), _process_strong_tasks(new SubTasksDone(G1H_PS_NumElements)), _bot_shared(NULL), @@ -1396,6 +1401,9 @@ if (_process_strong_tasks == NULL || !_process_strong_tasks->valid()) { vm_exit_during_initialization("Failed necessary allocation."); } + + _humongous_object_threshold_in_words = HeapRegion::GrainWords / 2; + int n_queues = MAX2((int)ParallelGCThreads, 1); _task_queues = new RefToScanQueueSet(n_queues); @@ -1433,6 +1441,7 @@ } jint G1CollectedHeap::initialize() { + CollectedHeap::pre_initialize(); os::enable_vtime(); // Necessary to satisfy locking discipline assertions. @@ -1453,8 +1462,6 @@ Universe::check_alignment(init_byte_size, HeapRegion::GrainBytes, "g1 heap"); Universe::check_alignment(max_byte_size, HeapRegion::GrainBytes, "g1 heap"); - // We allocate this in any case, but only do no work if the command line - // param is off. _cg1r = new ConcurrentG1Refine(); // Reserve the maximum. @@ -1548,9 +1555,10 @@ const size_t max_region_idx = ((size_t)1 << (sizeof(RegionIdx_t)*BitsPerByte-1)) - 1; guarantee((max_regions() - 1) <= max_region_idx, "too many regions"); - const size_t cards_per_region = HeapRegion::GrainBytes >> CardTableModRefBS::card_shift; size_t max_cards_per_region = ((size_t)1 << (sizeof(CardIdx_t)*BitsPerByte-1)) - 1; - guarantee(cards_per_region < max_cards_per_region, "too many cards per region"); + guarantee(HeapRegion::CardsPerRegion > 0, "make sure it's initialized"); + guarantee((size_t) HeapRegion::CardsPerRegion < max_cards_per_region, + "too many cards per region"); _bot_shared = new G1BlockOffsetSharedArray(_reserved, heap_word_size(init_byte_size)); @@ -1586,18 +1594,20 @@ JavaThread::satb_mark_queue_set().initialize(SATB_Q_CBL_mon, SATB_Q_FL_lock, - 0, + G1SATBProcessCompletedThreshold, Shared_SATB_Q_lock); JavaThread::dirty_card_queue_set().initialize(DirtyCardQ_CBL_mon, DirtyCardQ_FL_lock, - G1UpdateBufferQueueMaxLength, + concurrent_g1_refine()->yellow_zone(), + concurrent_g1_refine()->red_zone(), Shared_DirtyCardQ_lock); if (G1DeferredRSUpdate) { dirty_card_queue_set().initialize(DirtyCardQ_CBL_mon, DirtyCardQ_FL_lock, - 0, + -1, // never trigger processing + -1, // no limit on length Shared_DirtyCardQ_lock, &JavaThread::dirty_card_queue_set()); } @@ -1610,9 +1620,6 @@ // Do later initialization work for concurrent refinement. _cg1r->init(); - const char* group_names[] = { "CR", "ZF", "CM", "CL" }; - GCOverheadReporter::initGCOverheadReporter(4, group_names); - return JNI_OK; } @@ -1731,13 +1738,6 @@ return car->free(); } -void G1CollectedHeap::collect(GCCause::Cause cause) { - // The caller doesn't have the Heap_lock - assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); - MutexLocker ml(Heap_lock); - collect_locked(cause); -} - void G1CollectedHeap::collect_as_vm_thread(GCCause::Cause cause) { assert(Thread::current()->is_VM_thread(), "Precondition#1"); assert(Heap_lock->is_locked(), "Precondition#2"); @@ -1754,17 +1754,31 @@ } } - -void G1CollectedHeap::collect_locked(GCCause::Cause cause) { - // Don't want to do a GC until cleanup is completed. - wait_for_cleanup_complete(); - - // Read the GC count while holding the Heap_lock - int gc_count_before = SharedHeap::heap()->total_collections(); +void G1CollectedHeap::collect(GCCause::Cause cause) { + // The caller doesn't have the Heap_lock + assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); + + int gc_count_before; { - MutexUnlocker mu(Heap_lock); // give up heap lock, execute gets it back - VM_G1CollectFull op(gc_count_before, cause); - VMThread::execute(&op); + MutexLocker ml(Heap_lock); + // Read the GC count while holding the Heap_lock + gc_count_before = SharedHeap::heap()->total_collections(); + + // Don't want to do a GC until cleanup is completed. + wait_for_cleanup_complete(); + } // We give up heap lock; VMThread::execute gets it back below + switch (cause) { + case GCCause::_scavenge_alot: { + // Do an incremental pause, which might sometimes be abandoned. + VM_G1IncCollectionPause op(gc_count_before, cause); + VMThread::execute(&op); + break; + } + default: { + // In all other cases, we currently do a full gc. + VM_G1CollectFull op(gc_count_before, cause); + VMThread::execute(&op); + } } } @@ -2118,7 +2132,7 @@ } size_t G1CollectedHeap::max_capacity() const { - return _g1_committed.byte_size(); + return g1_reserved_obj_bytes(); } jlong G1CollectedHeap::millis_since_last_gc() { @@ -2209,40 +2223,58 @@ bool _allow_dirty; bool _par; bool _use_prev_marking; + bool _failures; public: // use_prev_marking == true -> use "prev" marking information, // use_prev_marking == false -> use "next" marking information VerifyRegionClosure(bool allow_dirty, bool par, bool use_prev_marking) : _allow_dirty(allow_dirty), _par(par), - _use_prev_marking(use_prev_marking) {} + _use_prev_marking(use_prev_marking), + _failures(false) {} + + bool failures() { + return _failures; + } bool doHeapRegion(HeapRegion* r) { guarantee(_par || r->claim_value() == HeapRegion::InitialClaimValue, "Should be unclaimed at verify points."); if (!r->continuesHumongous()) { - VerifyObjsInRegionClosure not_dead_yet_cl(r, _use_prev_marking); - r->verify(_allow_dirty, _use_prev_marking); - r->object_iterate(¬_dead_yet_cl); - guarantee(r->max_live_bytes() >= not_dead_yet_cl.live_bytes(), - "More live objects than counted in last complete marking."); + bool failures = false; + r->verify(_allow_dirty, _use_prev_marking, &failures); + if (failures) { + _failures = true; + } else { + VerifyObjsInRegionClosure not_dead_yet_cl(r, _use_prev_marking); + r->object_iterate(¬_dead_yet_cl); + if (r->max_live_bytes() < not_dead_yet_cl.live_bytes()) { + gclog_or_tty->print_cr("["PTR_FORMAT","PTR_FORMAT"] " + "max_live_bytes "SIZE_FORMAT" " + "< calculated "SIZE_FORMAT, + r->bottom(), r->end(), + r->max_live_bytes(), + not_dead_yet_cl.live_bytes()); + _failures = true; + } + } } - return false; + return false; // stop the region iteration if we hit a failure } }; class VerifyRootsClosure: public OopsInGenClosure { private: G1CollectedHeap* _g1h; + bool _use_prev_marking; bool _failures; - bool _use_prev_marking; public: // use_prev_marking == true -> use "prev" marking information, // use_prev_marking == false -> use "next" marking information VerifyRootsClosure(bool use_prev_marking) : _g1h(G1CollectedHeap::heap()), - _failures(false), - _use_prev_marking(use_prev_marking) { } + _use_prev_marking(use_prev_marking), + _failures(false) { } bool failures() { return _failures; } @@ -2252,7 +2284,7 @@ oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); if (_g1h->is_obj_dead_cond(obj, _use_prev_marking)) { gclog_or_tty->print_cr("Root location "PTR_FORMAT" " - "points to dead obj "PTR_FORMAT, p, (void*) obj); + "points to dead obj "PTR_FORMAT, p, (void*) obj); obj->print_on(gclog_or_tty); _failures = true; } @@ -2270,6 +2302,7 @@ G1CollectedHeap* _g1h; bool _allow_dirty; bool _use_prev_marking; + bool _failures; public: // use_prev_marking == true -> use "prev" marking information, @@ -2279,13 +2312,21 @@ AbstractGangTask("Parallel verify task"), _g1h(g1h), _allow_dirty(allow_dirty), - _use_prev_marking(use_prev_marking) { } + _use_prev_marking(use_prev_marking), + _failures(false) { } + + bool failures() { + return _failures; + } void work(int worker_i) { HandleMark hm; VerifyRegionClosure blk(_allow_dirty, true, _use_prev_marking); _g1h->heap_region_par_iterate_chunked(&blk, worker_i, HeapRegion::ParVerifyClaimValue); + if (blk.failures()) { + _failures = true; + } } }; @@ -2299,10 +2340,14 @@ if (SafepointSynchronize::is_at_safepoint() || ! UseTLAB) { if (!silent) { gclog_or_tty->print("roots "); } VerifyRootsClosure rootsCl(use_prev_marking); - process_strong_roots(false, + CodeBlobToOopClosure blobsCl(&rootsCl, /*do_marking=*/ false); + process_strong_roots(true, // activate StrongRootsScope + false, SharedHeap::SO_AllClasses, &rootsCl, + &blobsCl, &rootsCl); + bool failures = rootsCl.failures(); rem_set()->invalidate(perm_gen()->used_region(), false); if (!silent) { gclog_or_tty->print("heapRegions "); } if (GCParallelVerificationEnabled && ParallelGCThreads > 1) { @@ -2314,6 +2359,9 @@ set_par_threads(n_workers); workers()->run_task(&task); set_par_threads(0); + if (task.failures()) { + failures = true; + } assert(check_heap_region_claim_values(HeapRegion::ParVerifyClaimValue), "sanity check"); @@ -2325,10 +2373,24 @@ } else { VerifyRegionClosure blk(allow_dirty, false, use_prev_marking); _hrs->iterate(&blk); + if (blk.failures()) { + failures = true; + } } if (!silent) gclog_or_tty->print("remset "); rem_set()->verify(); - guarantee(!rootsCl.failures(), "should not have had failures"); + + if (failures) { + gclog_or_tty->print_cr("Heap:"); + print_on(gclog_or_tty, true /* extended */); + gclog_or_tty->print_cr(""); + if (VerifyDuringGC && G1VerifyDuringGCPrintReachable) { + concurrent_mark()->print_reachable(use_prev_marking, + "failed-verification"); + } + gclog_or_tty->flush(); + } + guarantee(!failures, "there should not have been any failures"); } else { if (!silent) gclog_or_tty->print("(SKIPPING roots, heapRegions, remset) "); } @@ -2370,6 +2432,7 @@ st->cr(); perm()->as_gen()->print_on(st); if (extended) { + st->cr(); print_on_extended(st); } } @@ -2379,27 +2442,18 @@ _hrs->iterate(&blk); } -class PrintOnThreadsClosure : public ThreadClosure { - outputStream* _st; -public: - PrintOnThreadsClosure(outputStream* st) : _st(st) { } - virtual void do_thread(Thread *t) { - t->print_on(_st); - } -}; - void G1CollectedHeap::print_gc_threads_on(outputStream* st) const { if (ParallelGCThreads > 0) { - workers()->print_worker_threads(); - } - st->print("\"G1 concurrent mark GC Thread\" "); - _cmThread->print(); + workers()->print_worker_threads_on(st); + } + + _cmThread->print_on(st); st->cr(); - st->print("\"G1 concurrent refinement GC Threads\" "); - PrintOnThreadsClosure p(st); - _cg1r->threads_do(&p); - st->cr(); - st->print("\"G1 zero-fill GC Thread\" "); + + _cm->print_worker_threads_on(st); + + _cg1r->print_worker_threads_on(st); + _czft->print_on(st); st->cr(); } @@ -2423,7 +2477,7 @@ if (G1SummarizeRSetStats) { g1_rem_set()->print_summary_info(); } - if (G1SummarizeConcurrentMark) { + if (G1SummarizeConcMark) { concurrent_mark()->print_summary_info(); } if (G1SummarizeZFStats) { @@ -2431,8 +2485,6 @@ } g1_policy()->print_yg_surv_rate_info(); - GCOverheadReporter::printGCOverhead(); - SpecializationStats::print(); } @@ -2453,6 +2505,7 @@ } void G1CollectedHeap::gc_prologue(bool full /* Ignored */) { + // always_do_update_barrier = false; assert(InlineCacheBuffer::is_empty(), "should have cleaned up ICBuffer"); // Call allocation profiler AllocationProfiler::iterate_since_last_gc(); @@ -2466,6 +2519,7 @@ // is set. COMPILER2_PRESENT(assert(DerivedPointerTable::is_empty(), "derived pointer present")); + // always_do_update_barrier = true; } void G1CollectedHeap::do_collection_pause() { @@ -2592,6 +2646,13 @@ // +struct PrepareForRSScanningClosure : public HeapRegionClosure { + bool doHeapRegion(HeapRegion *r) { + r->rem_set()->set_iter_claimed(0); + return false; + } +}; + void G1CollectedHeap::do_collection_pause_at_safepoint() { if (PrintHeapAtGC) { @@ -2599,6 +2660,8 @@ } { + ResourceMark rm; + char verbose_str[128]; sprintf(verbose_str, "GC pause "); if (g1_policy()->in_young_gc_mode()) { @@ -2610,8 +2673,6 @@ if (g1_policy()->should_initiate_conc_mark()) strcat(verbose_str, " (initial-mark)"); - GCCauseSetter x(this, GCCause::_g1_inc_collection_pause); - // if PrintGCDetails is on, we'll print long statistics information // in the collector policy code, so let's not print this as the output // is messy if we do. @@ -2619,7 +2680,8 @@ TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty); TraceTime t(verbose_str, PrintGC && !PrintGCDetails, true, gclog_or_tty); - ResourceMark rm; + TraceMemoryManagerStats tms(false /* fullGC */); + assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint"); assert(Thread::current() == VMThread::vm_thread(), "should be in vm thread"); guarantee(!is_gc_active(), "collection is not reentrant"); @@ -2669,7 +2731,6 @@ // The elapsed time induced by the start time below deliberately elides // the possible verification above. double start_time_sec = os::elapsedTime(); - GCOverheadReporter::recordSTWStart(start_time_sec); size_t start_used_bytes = used(); g1_policy()->record_collection_pause_start(start_time_sec, @@ -2730,6 +2791,8 @@ gclog_or_tty->print_cr("\nAfter pause, heap:"); print(); #endif + PrepareForRSScanningClosure prepare_for_rs_scan; + collection_set_iterate(&prepare_for_rs_scan); setup_surviving_young_words(); @@ -2747,8 +2810,6 @@ _in_cset_fast_test = NULL; _in_cset_fast_test_base = NULL; - release_gc_alloc_regions(false /* totally */); - cleanup_surviving_young_words(); if (g1_policy()->in_young_gc_mode()) { @@ -2766,6 +2827,22 @@ _young_list->reset_auxilary_lists(); } } else { + if (_in_cset_fast_test != NULL) { + assert(_in_cset_fast_test_base != NULL, "Since _in_cset_fast_test isn't"); + FREE_C_HEAP_ARRAY(bool, _in_cset_fast_test_base); + // this is more for peace of mind; we're nulling them here and + // we're expecting them to be null at the beginning of the next GC + _in_cset_fast_test = NULL; + _in_cset_fast_test_base = NULL; + } + // This looks confusing, because the DPT should really be empty + // at this point -- since we have not done any collection work, + // there should not be any derived pointers in the table to update; + // however, there is some additional state in the DPT which is + // reset at the end of the (null) "gc" here via the following call. + // A better approach might be to split off that state resetting work + // into a separate method that asserts that the DPT is empty and call + // that here. That is deferred for now. COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); } @@ -2798,11 +2875,12 @@ double end_time_sec = os::elapsedTime(); double pause_time_ms = (end_time_sec - start_time_sec) * MILLIUNITS; g1_policy()->record_pause_time_ms(pause_time_ms); - GCOverheadReporter::recordSTWEnd(end_time_sec); g1_policy()->record_collection_pause_end(abandoned); assert(regions_accounted_for(), "Region leakage."); + MemoryService::track_memory_usage(); + if (VerifyAfterGC && total_collections() >= VerifyGCStartAt) { HandleMark hm; // Discard invalid handles created during verification gclog_or_tty->print(" VerifyAfterGC:"); @@ -3101,7 +3179,7 @@ _evac_failure_scan_stack->length() == 0, "Postcondition"); assert(!_drain_in_progress, "Postcondition"); - // Don't have to delete, since the scan stack is a resource object. + delete _evac_failure_scan_stack; _evac_failure_scan_stack = NULL; } @@ -3402,7 +3480,7 @@ HeapRegion* r = heap_region_containing(old); if (!r->evacuation_failed()) { r->set_evacuation_failed(true); - if (G1PrintRegions) { + if (G1PrintHeapRegions) { gclog_or_tty->print("evacuation failed in heap region "PTR_FORMAT" " "["PTR_FORMAT","PTR_FORMAT")\n", r, r->bottom(), r->end()); @@ -3712,22 +3790,16 @@ return obj; } -template +template template -void G1ParCopyClosure +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 nonNull"); - // The only time we skip the cset test is when we're scanning - // references popped from the queue. And we only push on the queue - // references that we know point into the cset, so no point in - // checking again. But we'll leave an assert here for peace of mind. - assert(!skip_cset_test || _g1->obj_in_cs(obj), "invariant"); - // here the null check is implicit in the cset_fast_test() test - if (skip_cset_test || _g1->in_cset_fast_test(obj)) { + if (_g1->in_cset_fast_test(obj)) { #if G1_REM_SET_LOGGING gclog_or_tty->print_cr("Loc "PTR_FORMAT" contains pointer "PTR_FORMAT" " "into CS.", p, (void*) obj); @@ -3744,7 +3816,6 @@ } } - // When scanning moved objs, must look at all oops. if (barrier == G1BarrierEvac && obj != NULL) { _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); } @@ -3754,8 +3825,8 @@ } } -template void G1ParCopyClosure::do_oop_work(oop* p); -template void G1ParCopyClosure::do_oop_work(narrowOop* 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"); @@ -3827,11 +3898,11 @@ assert(UseCompressedOops, "Error"); narrowOop* p = (narrowOop*) stolen_task; assert(has_partial_array_mask(p) || - _g1h->obj_in_cs(oopDesc::load_decode_heap_oop(p)), "Error"); + _g1h->is_in_g1_reserved(oopDesc::load_decode_heap_oop(p)), "Error"); pss->push_on_queue(p); } else { oop* p = (oop*) stolen_task; - assert(has_partial_array_mask(p) || _g1h->obj_in_cs(*p), "Error"); + assert(has_partial_array_mask(p) || _g1h->is_in_g1_reserved(*p), "Error"); pss->push_on_queue(p); } continue; @@ -3893,6 +3964,7 @@ G1ParScanExtRootClosure only_scan_root_cl(_g1h, &pss); G1ParScanPermClosure only_scan_perm_cl(_g1h, &pss); G1ParScanHeapRSClosure only_scan_heap_rs_cl(_g1h, &pss); + G1ParPushHeapRSClosure push_heap_rs_cl(_g1h, &pss); G1ParScanAndMarkExtRootClosure scan_mark_root_cl(_g1h, &pss); G1ParScanAndMarkPermClosure scan_mark_perm_cl(_g1h, &pss); @@ -3916,7 +3988,7 @@ _g1h->g1_process_strong_roots(/* not collecting perm */ false, SharedHeap::SO_AllClasses, scan_root_cl, - &only_scan_heap_rs_cl, + &push_heap_rs_cl, scan_so_cl, scan_perm_cl, i); @@ -3930,9 +4002,7 @@ _g1h->g1_policy()->record_obj_copy_time(i, elapsed_ms-term_ms); _g1h->g1_policy()->record_termination_time(i, term_ms); } - if (G1UseSurvivorSpaces) { - _g1h->g1_policy()->record_thread_age_table(pss.age_table()); - } + _g1h->g1_policy()->record_thread_age_table(pss.age_table()); _g1h->update_surviving_young_words(pss.surviving_young_words()+1); // Clean up any par-expanded rem sets. @@ -3992,8 +4062,14 @@ BufferingOopsInGenClosure buf_scan_perm(scan_perm); buf_scan_perm.set_generation(perm_gen()); - process_strong_roots(collecting_perm_gen, so, + // Walk the code cache w/o buffering, because StarTask cannot handle + // unaligned oop locations. + CodeBlobToOopClosure eager_scan_code_roots(scan_non_heap_roots, /*do_marking=*/ true); + + process_strong_roots(false, // no scoping; this is parallel code + collecting_perm_gen, so, &buf_scan_non_heap_roots, + &eager_scan_code_roots, &buf_scan_perm); // Finish up any enqueued closure apps. buf_scan_non_heap_roots.done(); @@ -4083,7 +4159,8 @@ void G1CollectedHeap::g1_process_weak_roots(OopClosure* root_closure, OopClosure* non_root_closure) { - SharedHeap::process_weak_roots(root_closure, non_root_closure); + CodeBlobToOopClosure roots_in_blobs(root_closure, /*do_marking=*/ false); + SharedHeap::process_weak_roots(root_closure, &roots_in_blobs, non_root_closure); } @@ -4117,15 +4194,16 @@ init_for_evac_failure(NULL); - change_strong_roots_parity(); // In preparation for parallel strong roots. rem_set()->prepare_for_younger_refs_iterate(true); assert(dirty_card_queue_set().completed_buffers_num() == 0, "Should be empty"); double start_par = os::elapsedTime(); if (ParallelGCThreads > 0) { // The individual threads will set their evac-failure closures. + StrongRootsScope srs(this); workers()->run_task(&g1_par_task); } else { + StrongRootsScope srs(this); g1_par_task.work(0); } @@ -4141,6 +4219,7 @@ G1KeepAliveClosure keep_alive(this); JNIHandles::weak_oops_do(&is_alive, &keep_alive); } + release_gc_alloc_regions(false /* totally */); g1_rem_set()->cleanup_after_oops_into_collection_set_do(); concurrent_g1_refine()->clear_hot_cache(); @@ -4165,10 +4244,11 @@ RedirtyLoggedCardTableEntryFastClosure redirty; dirty_card_queue_set().set_closure(&redirty); dirty_card_queue_set().apply_closure_to_all_completed_buffers(); - JavaThread::dirty_card_queue_set().merge_bufferlists(&dirty_card_queue_set()); + + 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"); } - COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); } @@ -4274,12 +4354,18 @@ class G1ParCleanupCTTask : public AbstractGangTask { CardTableModRefBS* _ct_bs; G1CollectedHeap* _g1h; + HeapRegion* volatile _so_head; + HeapRegion* volatile _su_head; public: G1ParCleanupCTTask(CardTableModRefBS* ct_bs, - G1CollectedHeap* g1h) : + G1CollectedHeap* g1h, + HeapRegion* scan_only_list, + HeapRegion* survivor_list) : AbstractGangTask("G1 Par Cleanup CT Task"), _ct_bs(ct_bs), - _g1h(g1h) + _g1h(g1h), + _so_head(scan_only_list), + _su_head(survivor_list) { } void work(int i) { @@ -4287,22 +4373,64 @@ while (r = _g1h->pop_dirty_cards_region()) { clear_cards(r); } - } + // Redirty the cards of the scan-only and survivor regions. + dirty_list(&this->_so_head); + dirty_list(&this->_su_head); + } + void clear_cards(HeapRegion* r) { // Cards for Survivor and Scan-Only regions will be dirtied later. if (!r->is_scan_only() && !r->is_survivor()) { _ct_bs->clear(MemRegion(r->bottom(), r->end())); } } + + void dirty_list(HeapRegion* volatile * head_ptr) { + HeapRegion* head; + do { + // Pop region off the list. + head = *head_ptr; + if (head != NULL) { + HeapRegion* r = (HeapRegion*) + Atomic::cmpxchg_ptr(head->get_next_young_region(), head_ptr, head); + if (r == head) { + assert(!r->isHumongous(), "Humongous regions shouldn't be on survivor list"); + _ct_bs->dirty(MemRegion(r->bottom(), r->end())); + } + } + } while (*head_ptr != NULL); + } }; +#ifndef PRODUCT +class G1VerifyCardTableCleanup: public HeapRegionClosure { + CardTableModRefBS* _ct_bs; +public: + G1VerifyCardTableCleanup(CardTableModRefBS* ct_bs) + : _ct_bs(ct_bs) + { } + virtual bool doHeapRegion(HeapRegion* r) + { + MemRegion mr(r->bottom(), r->end()); + if (r->is_scan_only() || r->is_survivor()) { + _ct_bs->verify_dirty_region(mr); + } else { + _ct_bs->verify_clean_region(mr); + } + return false; + } +}; +#endif + void G1CollectedHeap::cleanUpCardTable() { CardTableModRefBS* ct_bs = (CardTableModRefBS*) (barrier_set()); double start = os::elapsedTime(); // Iterate over the dirty cards region list. - G1ParCleanupCTTask cleanup_task(ct_bs, this); + G1ParCleanupCTTask cleanup_task(ct_bs, this, + _young_list->first_scan_only_region(), + _young_list->first_survivor_region()); if (ParallelGCThreads > 0) { set_par_threads(workers()->total_workers()); workers()->run_task(&cleanup_task); @@ -4318,18 +4446,22 @@ } r->set_next_dirty_cards_region(NULL); } - } - // now, redirty the cards of the scan-only and survivor regions - // (it seemed faster to do it this way, instead of iterating over - // all regions and then clearing / dirtying as appropriate) - dirtyCardsForYoungRegions(ct_bs, _young_list->first_scan_only_region()); - dirtyCardsForYoungRegions(ct_bs, _young_list->first_survivor_region()); - + // now, redirty the cards of the scan-only and survivor regions + // (it seemed faster to do it this way, instead of iterating over + // all regions and then clearing / dirtying as appropriate) + dirtyCardsForYoungRegions(ct_bs, _young_list->first_scan_only_region()); + dirtyCardsForYoungRegions(ct_bs, _young_list->first_survivor_region()); + } double elapsed = os::elapsedTime() - start; g1_policy()->record_clear_ct_time( elapsed * 1000.0); +#ifndef PRODUCT + if (G1VerifyCTCleanup || VerifyAfterGC) { + G1VerifyCardTableCleanup cleanup_verifier(ct_bs); + heap_region_iterate(&cleanup_verifier); + } +#endif } - void G1CollectedHeap::do_collection_pause_if_appropriate(size_t word_size) { if (g1_policy()->should_do_collection_pause(word_size)) { do_collection_pause(); @@ -5022,7 +5154,7 @@ return hr->is_in(p); } } -#endif // PRODUCT +#endif // !PRODUCT void G1CollectedHeap::g1_unimplemented() { // Unimplemented(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -167,16 +167,11 @@ friend class G1MarkSweep; private: - enum SomePrivateConstants { - VeryLargeInBytes = HeapRegion::GrainBytes/2, - VeryLargeInWords = VeryLargeInBytes/HeapWordSize, - MinHeapDeltaBytes = 10 * HeapRegion::GrainBytes, // FIXME - NumAPIs = HeapRegion::MaxAge - }; - // The one and only G1CollectedHeap, so static functions can find it. static G1CollectedHeap* _g1h; + static size_t _humongous_object_threshold_in_words; + // Storage for the G1 heap (excludes the permanent generation). VirtualSpace _g1_storage; MemRegion _g1_reserved; @@ -697,7 +692,7 @@ // Reserved (g1 only; super method includes perm), capacity and the used // portion in bytes. - size_t g1_reserved_obj_bytes() { return _g1_reserved.byte_size(); } + size_t g1_reserved_obj_bytes() const { return _g1_reserved.byte_size(); } virtual size_t capacity() const; virtual size_t used() const; // This should be called when we're not holding the heap lock. The @@ -859,7 +854,7 @@ return _g1_committed; } - NOT_PRODUCT( bool is_in_closed_subset(const void* p) const; ) + NOT_PRODUCT(bool is_in_closed_subset(const void* p) const;) // Dirty card table entries covering a list of young regions. void dirtyCardsForYoungRegions(CardTableModRefBS* ct_bs, HeapRegion* list); @@ -997,11 +992,50 @@ // Can a compiler initialize a new object without store barriers? // This permission only extends from the creation of a new object - // via a TLAB up to the first subsequent safepoint. + // via a TLAB up to the first subsequent safepoint. If such permission + // is granted for this heap type, the compiler promises to call + // defer_store_barrier() below on any slow path allocation of + // a new object for which such initializing store barriers will + // have been elided. G1, like CMS, allows this, but should be + // ready to provide a compensating write barrier as necessary + // if that storage came out of a non-young region. The efficiency + // of this implementation depends crucially on being able to + // answer very efficiently in constant time whether a piece of + // storage in the heap comes from a young region or not. + // See ReduceInitialCardMarks. virtual bool can_elide_tlab_store_barriers() const { - // Since G1's TLAB's may, on occasion, come from non-young regions - // as well. (Is there a flag controlling that? XXX) - return false; + // 6920090: Temporarily disabled, because of lingering + // instabilities related to RICM with G1. In the + // interim, the option ReduceInitialCardMarksForG1 + // below is left solely as a debugging device at least + // until 6920109 fixes the instabilities. + return ReduceInitialCardMarksForG1; + } + + virtual bool card_mark_must_follow_store() const { + return true; + } + + bool is_in_young(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. Note that non-generational + // G1 does not have any "young" objects, should not elide + // the rs logging barrier and so should always answer false below. + // However, non-generational G1 (-XX:-G1Gen) appears to have + // bit-rotted so was not tested below. + virtual bool can_elide_initializing_store_barrier(oop new_obj) { + // Re 6920090, 6920109 above. + assert(ReduceInitialCardMarksForG1, "Else cannot be here"); + assert(G1Gen || !is_in_young(new_obj), + "Non-generational G1 should never return true below"); + return is_in_young(new_obj); } // Can a compiler elide a store barrier when it writes @@ -1021,7 +1055,7 @@ // Returns "true" iff the given word_size is "very large". static bool isHumongous(size_t word_size) { - return word_size >= VeryLargeInWords; + return word_size >= _humongous_object_threshold_in_words; } // Update mod union table with the set of dirty cards. @@ -1589,7 +1623,7 @@ template void push_on_queue(T* ref) { assert(ref != NULL, "invariant"); assert(has_partial_array_mask(ref) || - _g1h->obj_in_cs(oopDesc::load_decode_heap_oop(ref)), "invariant"); + _g1h->is_in_g1_reserved(oopDesc::load_decode_heap_oop(ref)), "invariant"); #ifdef ASSERT if (has_partial_array_mask(ref)) { oop p = clear_partial_array_mask(ref); @@ -1610,9 +1644,9 @@ assert((oop*)ref != NULL, "pop_local() returned true"); assert(UseCompressedOops || !ref.is_narrow(), "Error"); assert(has_partial_array_mask((oop*)ref) || - _g1h->obj_in_cs(ref.is_narrow() ? oopDesc::load_decode_heap_oop((narrowOop*)ref) - : oopDesc::load_decode_heap_oop((oop*)ref)), - "invariant"); + _g1h->is_in_g1_reserved(ref.is_narrow() ? oopDesc::load_decode_heap_oop((narrowOop*)ref) + : oopDesc::load_decode_heap_oop((oop*)ref)), + "invariant"); IF_G1_DETAILED_STATS(note_pop()); } else { StarTask null_task; @@ -1625,9 +1659,9 @@ assert((oop*)new_ref != NULL, "pop() from a local non-empty stack"); assert(UseCompressedOops || !new_ref.is_narrow(), "Error"); assert(has_partial_array_mask((oop*)new_ref) || - _g1h->obj_in_cs(new_ref.is_narrow() ? oopDesc::load_decode_heap_oop((narrowOop*)new_ref) - : oopDesc::load_decode_heap_oop((oop*)new_ref)), - "invariant"); + _g1h->is_in_g1_reserved(new_ref.is_narrow() ? oopDesc::load_decode_heap_oop((narrowOop*)new_ref) + : oopDesc::load_decode_heap_oop((oop*)new_ref)), + "invariant"); ref = new_ref; } @@ -1791,12 +1825,12 @@ assert(UseCompressedOops, "Error"); narrowOop* p = (narrowOop*)ref_to_scan; assert(!has_partial_array_mask(p) && - _g1h->obj_in_cs(oopDesc::load_decode_heap_oop(p)), "sanity"); + _g1h->is_in_g1_reserved(oopDesc::load_decode_heap_oop(p)), "sanity"); deal_with_reference(p); } else { oop* p = (oop*)ref_to_scan; - assert((has_partial_array_mask(p) && _g1h->obj_in_cs(clear_partial_array_mask(p))) || - _g1h->obj_in_cs(oopDesc::load_decode_heap_oop(p)), "sanity"); + assert((has_partial_array_mask(p) && _g1h->is_in_g1_reserved(clear_partial_array_mask(p))) || + _g1h->is_in_g1_reserved(oopDesc::load_decode_heap_oop(p)), "sanity"); deal_with_reference(p); } } @@ -1810,12 +1844,12 @@ assert(UseCompressedOops, "Error"); narrowOop* p = (narrowOop*)ref_to_scan; assert(!has_partial_array_mask(p) && - _g1h->obj_in_cs(oopDesc::load_decode_heap_oop(p)), "sanity"); + _g1h->is_in_g1_reserved(oopDesc::load_decode_heap_oop(p)), "sanity"); deal_with_reference(p); } else { oop* p = (oop*)ref_to_scan; assert((has_partial_array_mask(p) && _g1h->obj_in_cs(clear_partial_array_mask(p))) || - _g1h->obj_in_cs(oopDesc::load_decode_heap_oop(p)), "sanity"); + _g1h->is_in_g1_reserved(oopDesc::load_decode_heap_oop(p)), "sanity"); deal_with_reference(p); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -201,6 +201,12 @@ _survivors_age_table(true) { + // Set up the region size and associated fields. Given that the + // policy is created before the heap, we have to set this up here, + // so it's done as soon as possible. + HeapRegion::setup_heap_region_size(Arguments::min_heap_size()); + HeapRegionRemSet::setup_remset_size(); + _recent_prev_end_times_for_all_gcs_sec->add(os::elapsedTime()); _prev_collection_pause_end_ms = os::elapsedTime() * 1000.0; @@ -264,14 +270,10 @@ _concurrent_mark_cleanup_times_ms->add(0.20); _tenuring_threshold = MaxTenuringThreshold; - if (G1UseSurvivorSpaces) { - // if G1FixedSurvivorSpaceSize is 0 which means the size is not - // fixed, then _max_survivor_regions will be calculated at - // calculate_young_list_target_config during initialization - _max_survivor_regions = G1FixedSurvivorSpaceSize / HeapRegion::GrainBytes; - } else { - _max_survivor_regions = 0; - } + // if G1FixedSurvivorSpaceSize is 0 which means the size is not + // fixed, then _max_survivor_regions will be calculated at + // calculate_young_list_target_config during initialization + _max_survivor_regions = G1FixedSurvivorSpaceSize / HeapRegion::GrainBytes; initialize_all(); } @@ -290,28 +292,54 @@ CollectorPolicy::initialize_flags(); } +// The easiest way to deal with the parsing of the NewSize / +// MaxNewSize / etc. parameteres is to re-use the code in the +// TwoGenerationCollectorPolicy class. This is similar to what +// ParallelScavenge does with its GenerationSizer class (see +// ParallelScavengeHeap::initialize()). We might change this in the +// future, but it's a good start. +class G1YoungGenSizer : public TwoGenerationCollectorPolicy { + size_t size_to_region_num(size_t byte_size) { + return MAX2((size_t) 1, byte_size / HeapRegion::GrainBytes); + } + +public: + G1YoungGenSizer() { + initialize_flags(); + initialize_size_info(); + } + + size_t min_young_region_num() { + return size_to_region_num(_min_gen0_size); + } + size_t initial_young_region_num() { + return size_to_region_num(_initial_gen0_size); + } + size_t max_young_region_num() { + return size_to_region_num(_max_gen0_size); + } +}; + void G1CollectorPolicy::init() { // Set aside an initial future to_space. _g1 = G1CollectedHeap::heap(); - size_t regions = Universe::heap()->capacity() / HeapRegion::GrainBytes; assert(Heap_lock->owned_by_self(), "Locking discipline."); - if (G1SteadyStateUsed < 50) { - vm_exit_during_initialization("G1SteadyStateUsed must be at least 50%."); - } - initialize_gc_policy_counters(); if (G1Gen) { _in_young_gc_mode = true; - if (G1YoungGenSize == 0) { + G1YoungGenSizer sizer; + size_t initial_region_num = sizer.initial_young_region_num(); + + if (UseAdaptiveSizePolicy) { set_adaptive_young_list_length(true); _young_list_fixed_length = 0; } else { set_adaptive_young_list_length(false); - _young_list_fixed_length = (G1YoungGenSize / HeapRegion::GrainBytes); + _young_list_fixed_length = initial_region_num; } _free_regions_at_end_of_collection = _g1->free_regions(); _scan_only_regions_at_end_of_collection = 0; @@ -449,7 +477,7 @@ guarantee( adaptive_young_list_length(), "pre-condition" ); double start_time_sec = os::elapsedTime(); - size_t min_reserve_perc = MAX2((size_t)2, (size_t)G1MinReservePercent); + size_t min_reserve_perc = MAX2((size_t)2, (size_t)G1ReservePercent); min_reserve_perc = MIN2((size_t) 50, min_reserve_perc); size_t reserve_regions = (size_t) ((double) min_reserve_perc * (double) _g1->n_regions() / 100.0); @@ -993,8 +1021,6 @@ double full_gc_time_sec = end_sec - _cur_collection_start_sec; double full_gc_time_ms = full_gc_time_sec * 1000.0; - checkpoint_conc_overhead(); - _all_full_gc_times_ms->add(full_gc_time_ms); update_recent_gc_times(end_sec, full_gc_time_ms); @@ -1106,10 +1132,7 @@ size_t short_lived_so_length = _young_list_so_prefix_length; _short_lived_surv_rate_group->record_scan_only_prefix(short_lived_so_length); tag_scan_only(short_lived_so_length); - - if (G1UseSurvivorSpaces) { - _survivors_age_table.clear(); - } + _survivors_age_table.clear(); assert( verify_young_ages(), "region age verification" ); } @@ -1164,7 +1187,6 @@ double end_time_sec = os::elapsedTime(); double elapsed_time_ms = (end_time_sec - _mark_init_start_sec) * 1000.0; _concurrent_mark_init_times_ms->add(elapsed_time_ms); - checkpoint_conc_overhead(); record_concurrent_mark_init_end_pre(elapsed_time_ms); _mmu_tracker->add_pause(_mark_init_start_sec, end_time_sec, true); @@ -1178,7 +1200,6 @@ void G1CollectorPolicy::record_concurrent_mark_remark_end() { double end_time_sec = os::elapsedTime(); double elapsed_time_ms = (end_time_sec - _mark_remark_start_sec)*1000.0; - checkpoint_conc_overhead(); _concurrent_mark_remark_times_ms->add(elapsed_time_ms); _cur_mark_stop_world_time_ms += elapsed_time_ms; _prev_collection_pause_end_ms += elapsed_time_ms; @@ -1210,7 +1231,6 @@ // The important thing about this is that it includes "os::elapsedTime". void G1CollectorPolicy::record_concurrent_mark_cleanup_end_work2() { - checkpoint_conc_overhead(); double end_time_sec = os::elapsedTime(); double elapsed_time_ms = (end_time_sec - _mark_cleanup_start_sec)*1000.0; _concurrent_mark_cleanup_times_ms->add(elapsed_time_ms); @@ -1425,15 +1445,13 @@ } #endif // PRODUCT - checkpoint_conc_overhead(); - if (in_young_gc_mode()) { last_pause_included_initial_mark = _should_initiate_conc_mark; if (last_pause_included_initial_mark) record_concurrent_mark_init_end_pre(0.0); size_t min_used_targ = - (_g1->capacity() / 100) * (G1SteadyStateUsed - G1SteadyStateUsedDelta); + (_g1->capacity() / 100) * InitiatingHeapOccupancyPercent; if (cur_used_bytes > min_used_targ) { if (cur_used_bytes <= _prev_collection_pause_used_at_end_bytes) { @@ -1518,7 +1536,30 @@ (end_time_sec - _recent_prev_end_times_for_all_gcs_sec->oldest()) * 1000.0; update_recent_gc_times(end_time_sec, elapsed_ms); _recent_avg_pause_time_ratio = _recent_gc_times_ms->sum()/interval_ms; - assert(recent_avg_pause_time_ratio() < 1.00, "All GC?"); + if (recent_avg_pause_time_ratio() < 0.0 || + (recent_avg_pause_time_ratio() - 1.0 > 0.0)) { +#ifndef PRODUCT + // Dump info to allow post-facto debugging + gclog_or_tty->print_cr("recent_avg_pause_time_ratio() out of bounds"); + gclog_or_tty->print_cr("-------------------------------------------"); + gclog_or_tty->print_cr("Recent GC Times (ms):"); + _recent_gc_times_ms->dump(); + gclog_or_tty->print_cr("(End Time=%3.3f) Recent GC End Times (s):", end_time_sec); + _recent_prev_end_times_for_all_gcs_sec->dump(); + gclog_or_tty->print_cr("GC = %3.3f, Interval = %3.3f, Ratio = %3.3f", + _recent_gc_times_ms->sum(), interval_ms, recent_avg_pause_time_ratio()); + // In debug mode, terminate the JVM if the user wants to debug at this point. + assert(!G1FailOnFPError, "Debugging data for CR 6898948 has been dumped above"); +#endif // !PRODUCT + // Clip ratio between 0.0 and 1.0, and continue. This will be fixed in + // CR 6902692 by redoing the manner in which the ratio is incrementally computed. + if (_recent_avg_pause_time_ratio < 0.0) { + _recent_avg_pause_time_ratio = 0.0; + } else { + assert(_recent_avg_pause_time_ratio - 1.0 > 0.0, "Ctl-point invariant"); + _recent_avg_pause_time_ratio = 1.0; + } + } } if (G1PolicyVerbose > 1) { @@ -1893,6 +1934,10 @@ calculate_young_list_min_length(); calculate_young_list_target_config(); + // Note that _mmu_tracker->max_gc_time() returns the time in seconds. + double update_rs_time_goal_ms = _mmu_tracker->max_gc_time() * MILLIUNITS * G1RSetUpdatingPauseTimePercent / 100.0; + adjust_concurrent_refinement(update_rs_time, update_rs_processed_buffers, update_rs_time_goal_ms); + // _target_pause_time_ms = -1.0; @@ -1900,6 +1945,47 @@ // +void G1CollectorPolicy::adjust_concurrent_refinement(double update_rs_time, + double update_rs_processed_buffers, + double goal_ms) { + DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); + ConcurrentG1Refine *cg1r = G1CollectedHeap::heap()->concurrent_g1_refine(); + + if (G1UseAdaptiveConcRefinement) { + const int k_gy = 3, k_gr = 6; + const double inc_k = 1.1, dec_k = 0.9; + + int g = cg1r->green_zone(); + if (update_rs_time > goal_ms) { + g = (int)(g * dec_k); // Can become 0, that's OK. That would mean a mutator-only processing. + } else { + if (update_rs_time < goal_ms && update_rs_processed_buffers > g) { + g = (int)MAX2(g * inc_k, g + 1.0); + } + } + // Change the refinement threads params + cg1r->set_green_zone(g); + cg1r->set_yellow_zone(g * k_gy); + cg1r->set_red_zone(g * k_gr); + cg1r->reinitialize_threads(); + + int processing_threshold_delta = MAX2((int)(cg1r->green_zone() * sigma()), 1); + int processing_threshold = MIN2(cg1r->green_zone() + processing_threshold_delta, + cg1r->yellow_zone()); + // Change the barrier params + dcqs.set_process_completed_threshold(processing_threshold); + dcqs.set_max_completed_queue(cg1r->red_zone()); + } + + int curr_queue_size = dcqs.completed_buffers_num(); + if (curr_queue_size >= cg1r->yellow_zone()) { + dcqs.set_completed_queue_padding(curr_queue_size); + } else { + dcqs.set_completed_queue_padding(0); + } + dcqs.notify_if_necessary(); +} + double G1CollectorPolicy:: predict_young_collection_elapsed_time_ms(size_t adjustment) { @@ -2525,19 +2611,6 @@ } #endif // PRODUCT -void -G1CollectorPolicy::checkpoint_conc_overhead() { - double conc_overhead = 0.0; - if (G1AccountConcurrentOverhead) - conc_overhead = COTracker::totalPredConcOverhead(); - _mmu_tracker->update_conc_overhead(conc_overhead); -#if 0 - gclog_or_tty->print(" CO %1.4lf TARGET %1.4lf", - conc_overhead, _mmu_tracker->max_gc_time()); -#endif -} - - size_t G1CollectorPolicy::max_regions(int purpose) { switch (purpose) { case GCAllocForSurvived: @@ -2553,9 +2626,6 @@ // Calculates survivor space parameters. void G1CollectorPolicy::calculate_survivors_policy() { - if (!G1UseSurvivorSpaces) { - return; - } if (G1FixedSurvivorSpaceSize == 0) { _max_survivor_regions = _young_list_target_length / SurvivorRatio; } else { @@ -2574,13 +2644,6 @@ G1CollectorPolicy_BestRegionsFirst::should_do_collection_pause(size_t word_size) { assert(_g1->regions_accounted_for(), "Region leakage!"); - // Initiate a pause when we reach the steady-state "used" target. - size_t used_hard = (_g1->capacity() / 100) * G1SteadyStateUsed; - size_t used_soft = - MAX2((_g1->capacity() / 100) * (G1SteadyStateUsed - G1SteadyStateUsedDelta), - used_hard/2); - size_t used = _g1->used(); - double max_pause_time_ms = _mmu_tracker->max_gc_time() * 1000.0; size_t young_list_length = _g1->young_list_length(); @@ -2813,7 +2876,7 @@ // estimate of the number of live bytes. void G1CollectorPolicy:: add_to_collection_set(HeapRegion* hr) { - if (G1PrintRegions) { + if (G1PrintHeapRegions) { gclog_or_tty->print_cr("added region to cset %d:["PTR_FORMAT", "PTR_FORMAT"], " "top "PTR_FORMAT", young %s", hr->hrs_index(), hr->bottom(), hr->end(), @@ -2839,8 +2902,15 @@ double non_young_start_time_sec; start_recording_regions(); - guarantee(_target_pause_time_ms > -1.0, + guarantee(_target_pause_time_ms > -1.0 + NOT_PRODUCT(|| Universe::heap()->gc_cause() == GCCause::_scavenge_alot), "_target_pause_time_ms should have been set!"); +#ifndef PRODUCT + if (_target_pause_time_ms <= -1.0) { + assert(ScavengeALot && Universe::heap()->gc_cause() == GCCause::_scavenge_alot, "Error"); + _target_pause_time_ms = _mmu_tracker->max_gc_time() * 1000.0; + } +#endif assert(_collection_set == NULL, "Precondition"); double base_time_ms = predict_base_elapsed_time_ms(_pending_cards); @@ -2986,7 +3056,3 @@ G1CollectorPolicy::record_collection_pause_end(abandoned); assert(assertMarkedBytesDataOK(), "Marked regions not OK at pause end."); } - -// Local Variables: *** -// c-indentation-style: gnu *** -// End: *** diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -92,9 +92,7 @@ int _parallel_gc_threads; enum SomePrivateConstants { - NumPrevPausesForHeuristics = 10, - NumPrevGCsForHeuristics = 10, - NumAPIs = HeapRegion::MaxAge + NumPrevPausesForHeuristics = 10 }; G1MMUTracker* _mmu_tracker; @@ -318,6 +316,10 @@ bool verify_young_ages(HeapRegion* head, SurvRateGroup *surv_rate_group); #endif // PRODUCT + void adjust_concurrent_refinement(double update_rs_time, + double update_rs_processed_buffers, + double goal_ms); + protected: double _pause_time_target_ms; double _recorded_young_cset_choice_time_ms; @@ -981,8 +983,6 @@ void set_should_initiate_conc_mark() { _should_initiate_conc_mark = true; } void unset_should_initiate_conc_mark(){ _should_initiate_conc_mark = false; } - void checkpoint_conc_overhead(); - // If an expansion would be appropriate, because recent GC overhead had // exceeded the desired limit, return an amount to expand by. virtual size_t expansion_amount(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1MMUTracker.cpp --- a/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 Sun Microsystems, 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 @@ -37,21 +37,7 @@ G1MMUTracker::G1MMUTracker(double time_slice, double max_gc_time) : _time_slice(time_slice), - _max_gc_time(max_gc_time), - _conc_overhead_time_sec(0.0) { } - -void -G1MMUTracker::update_conc_overhead(double conc_overhead) { - double conc_overhead_time_sec = _time_slice * conc_overhead; - if (conc_overhead_time_sec > 0.9 * _max_gc_time) { - // We are screwed, as we only seem to have <10% of the soft - // real-time goal available for pauses. Let's admit defeat and - // allow something more generous as a pause target. - conc_overhead_time_sec = 0.75 * _max_gc_time; - } - - _conc_overhead_time_sec = conc_overhead_time_sec; -} + _max_gc_time(max_gc_time) { } G1MMUTrackerQueue::G1MMUTrackerQueue(double time_slice, double max_gc_time) : G1MMUTracker(time_slice, max_gc_time), @@ -100,12 +86,22 @@ // increase the array size (:-) // remove the oldest entry (this might allow more GC time for // the time slice than what's allowed) - // concolidate the two entries with the minimum gap between them - // (this mighte allow less GC time than what's allowed) - guarantee(0, "array full, currently we can't recover"); + // consolidate the two entries with the minimum gap between them + // (this might allow less GC time than what's allowed) + guarantee(NOT_PRODUCT(ScavengeALot ||) G1UseFixedWindowMMUTracker, + "array full, currently we can't recover unless +G1UseFixedWindowMMUTracker"); + // In the case where ScavengeALot is true, such overflow is not + // uncommon; in such cases, we can, without much loss of precision + // or performance (we are GC'ing most of the time anyway!), + // simply overwrite the oldest entry in the tracker: this + // is also the behaviour when G1UseFixedWindowMMUTracker is enabled. + _head_index = trim_index(_head_index + 1); + assert(_head_index == _tail_index, "Because we have a full circular buffer"); + _tail_index = trim_index(_tail_index + 1); + } else { + _head_index = trim_index(_head_index + 1); + ++_no_entries; } - _head_index = trim_index(_head_index + 1); - ++_no_entries; _array[_head_index] = G1MMUTrackerQueueElem(start, end); } @@ -128,7 +124,7 @@ while( 1 ) { double gc_time = - calculate_gc_time(current_time + target_time) + _conc_overhead_time_sec; + calculate_gc_time(current_time + target_time); double diff = target_time + gc_time - _max_gc_time; if (!is_double_leq_0(diff)) { target_time -= diff; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1MMUTracker.hpp --- a/src/share/vm/gc_implementation/g1/g1MMUTracker.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1MMUTracker.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -33,19 +33,15 @@ double _time_slice; double _max_gc_time; // this is per time slice - double _conc_overhead_time_sec; - public: G1MMUTracker(double time_slice, double max_gc_time); - void update_conc_overhead(double conc_overhead); - virtual void add_pause(double start, double end, bool gc_thread) = 0; virtual double longest_pause(double current_time) = 0; virtual double when_sec(double current_time, double pause_time) = 0; double max_gc_time() { - return _max_gc_time - _conc_overhead_time_sec; + return _max_gc_time; } inline bool now_max_gc(double current_time) { @@ -103,7 +99,10 @@ // The array is of fixed size and I don't think we'll need more than // two or three entries with the current behaviour of G1 pauses. // If the array is full, an easy fix is to look for the pauses with - // the shortest gap between them and concolidate them. + // the shortest gap between them and consolidate them. + // For now, we have taken the expedient alternative of forgetting + // the oldest entry in the event that +G1UseFixedWindowMMUTracker, thus + // potentially violating MMU specs for some time thereafter. G1MMUTrackerQueueElem _array[QueueLength]; int _head_index; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1MarkSweep.cpp --- a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -102,9 +102,14 @@ GenMarkSweep::_marking_stack = new (ResourceObj::C_HEAP) GrowableArray(4000, true); - size_t size = SystemDictionary::number_of_classes() * 2; + int size = SystemDictionary::number_of_classes() * 2; GenMarkSweep::_revisit_klass_stack = - new (ResourceObj::C_HEAP) GrowableArray((int)size, true); + new (ResourceObj::C_HEAP) GrowableArray(size, true); + // (#klass/k)^2 for k ~ 10 appears a better fit, but this will have to do + // for now until we have a chance to work out a more optimal setting. + GenMarkSweep::_revisit_mdo_stack = + new (ResourceObj::C_HEAP) GrowableArray(size*2, true); + } void G1MarkSweep::mark_sweep_phase1(bool& marked_for_unloading, @@ -116,9 +121,11 @@ SharedHeap* sh = SharedHeap::heap(); - sh->process_strong_roots(true, // Collecting permanent generation. + sh->process_strong_roots(true, // activeate StrongRootsScope + true, // Collecting permanent generation. SharedHeap::SO_SystemClasses, &GenMarkSweep::follow_root_closure, + &GenMarkSweep::follow_code_root_closure, &GenMarkSweep::follow_root_closure); // Process reference objects found during marking @@ -139,13 +146,18 @@ CodeCache::do_unloading(&GenMarkSweep::is_alive, &GenMarkSweep::keep_alive, purged_class); - GenMarkSweep::follow_stack(); + GenMarkSweep::follow_stack(); // Update subklass/sibling/implementor links of live klasses GenMarkSweep::follow_weak_klass_links(); assert(GenMarkSweep::_marking_stack->is_empty(), "stack should be empty by now"); + // Visit memoized MDO's and clear any unmarked weak refs + GenMarkSweep::follow_mdo_weak_refs(); + assert(GenMarkSweep::_marking_stack->is_empty(), "just drained"); + + // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(&GenMarkSweep::is_alive); StringTable::unlink(&GenMarkSweep::is_alive); @@ -276,9 +288,11 @@ SharedHeap* sh = SharedHeap::heap(); - sh->process_strong_roots(true, // Collecting permanent generation. + sh->process_strong_roots(true, // activate StrongRootsScope + true, // Collecting permanent generation. SharedHeap::SO_AllClasses, &GenMarkSweep::adjust_root_pointer_closure, + NULL, // do not touch code cache here &GenMarkSweep::adjust_pointer_closure); g1h->ref_processor()->weak_oops_do(&GenMarkSweep::adjust_root_pointer_closure); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1OopClosures.hpp --- a/src/share/vm/gc_implementation/g1/g1OopClosures.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -53,6 +53,15 @@ bool apply_to_weak_ref_discovered_field() { return true; } }; +class G1ParPushHeapRSClosure : public G1ParClosureSuper { +public: + G1ParPushHeapRSClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : + G1ParClosureSuper(g1, par_scan_state) { } + 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); } +}; + class G1ParScanClosure : public G1ParClosureSuper { public: G1ParScanClosure(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : @@ -100,7 +109,7 @@ }; template + bool do_mark_forwardee> class G1ParCopyClosure : public G1ParCopyHelper { G1ParScanClosure _scanner; template void do_oop_work(T* p); @@ -116,12 +125,13 @@ virtual void do_oop(narrowOop* p) { do_oop_nv(p); } }; -typedef G1ParCopyClosure G1ParScanExtRootClosure; -typedef G1ParCopyClosure G1ParScanPermClosure; -typedef G1ParCopyClosure G1ParScanHeapRSClosure; -typedef G1ParCopyClosure G1ParScanAndMarkExtRootClosure; -typedef G1ParCopyClosure G1ParScanAndMarkPermClosure; -typedef G1ParCopyClosure G1ParScanAndMarkHeapRSClosure; +typedef G1ParCopyClosure G1ParScanExtRootClosure; +typedef G1ParCopyClosure G1ParScanPermClosure; +typedef G1ParCopyClosure G1ParScanHeapRSClosure; +typedef G1ParCopyClosure G1ParScanAndMarkExtRootClosure; +typedef G1ParCopyClosure G1ParScanAndMarkPermClosure; +typedef G1ParCopyClosure G1ParScanAndMarkHeapRSClosure; + // This is the only case when we set skip_cset_test. Basically, this // closure is (should?) only be called directly while we're draining // the overflow and task queues. In that case we know that the @@ -132,7 +142,7 @@ // We need a separate closure to handle references during evacuation // failure processing, as we cannot asume that the reference already // points into the collection set (like G1ParScanHeapEvacClosure does). -typedef G1ParCopyClosure G1ParScanHeapEvacFailureClosure; +typedef G1ParCopyClosure G1ParScanHeapEvacFailureClosure; class FilterIntoCSClosure: public OopClosure { G1CollectedHeap* _g1; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp --- a/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -104,3 +104,16 @@ } } } + +template inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) { + T heap_oop = oopDesc::load_heap_oop(p); + + if (!oopDesc::is_null(heap_oop)) { + oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); + if (_g1->in_cset_fast_test(obj)) { + Prefetch::write(obj->mark_addr(), 0); + Prefetch::read(obj->mark_addr(), (HeapWordSize*2)); + _par_scan_state->push_on_queue(p); + } + } +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1RemSet.cpp --- a/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -155,8 +155,8 @@ G1BlockOffsetSharedArray* _bot_shared; CardTableModRefBS *_ct_bs; int _worker_i; + int _block_size; bool _try_claimed; - size_t _min_skip_distance, _max_skip_distance; public: ScanRSClosure(OopsInHeapRegionClosure* oc, int worker_i) : _oc(oc), @@ -168,8 +168,7 @@ _g1h = G1CollectedHeap::heap(); _bot_shared = _g1h->bot_shared(); _ct_bs = (CardTableModRefBS*) (_g1h->barrier_set()); - _min_skip_distance = 16; - _max_skip_distance = 2 * _g1h->n_par_threads() * _min_skip_distance; + _block_size = MAX2(G1RSetScanBlockSize, 1); } void set_try_claimed() { _try_claimed = true; } @@ -225,12 +224,15 @@ HeapRegionRemSetIterator* iter = _g1h->rem_set_iterator(_worker_i); hrrs->init_iterator(iter); size_t card_index; - size_t skip_distance = 0, current_card = 0, jump_to_card = 0; - while (iter->has_next(card_index)) { - if (current_card < jump_to_card) { - ++current_card; - continue; + + // We claim cards in block so as to recude the contention. The block size is determined by + // the G1RSetScanBlockSize parameter. + size_t jump_to_card = hrrs->iter_claimed_next(_block_size); + for (size_t current_card = 0; iter->has_next(card_index); current_card++) { + if (current_card >= jump_to_card + _block_size) { + jump_to_card = hrrs->iter_claimed_next(_block_size); } + if (current_card < jump_to_card) continue; HeapWord* card_start = _g1h->bot_shared()->address_for_index(card_index); #if 0 gclog_or_tty->print("Rem set iteration yielded card [" PTR_FORMAT ", " PTR_FORMAT ").\n", @@ -247,22 +249,14 @@ // If the card is dirty, then we will scan it during updateRS. if (!card_region->in_collection_set() && !_ct_bs->is_card_dirty(card_index)) { - if (!_ct_bs->is_card_claimed(card_index) && _ct_bs->claim_card(card_index)) { - scanCard(card_index, card_region); - } else if (_try_claimed) { - if (jump_to_card == 0 || jump_to_card != current_card) { - // We did some useful work in the previous iteration. - // Decrease the distance. - skip_distance = MAX2(skip_distance >> 1, _min_skip_distance); - } else { - // Previous iteration resulted in a claim failure. - // Increase the distance. - skip_distance = MIN2(skip_distance << 1, _max_skip_distance); - } - jump_to_card = current_card + skip_distance; - } + // We make the card as "claimed" lazily (so races are possible but they're benign), + // which reduces the number of duplicate scans (the rsets of the regions in the cset + // can intersect). + if (!_ct_bs->is_card_claimed(card_index)) { + _ct_bs->set_card_claimed(card_index); + scanCard(card_index, card_region); + } } - ++current_card; } if (!_try_claimed) { hrrs->set_iter_complete(); @@ -299,30 +293,18 @@ double rs_time_start = os::elapsedTime(); HeapRegion *startRegion = calculateStartRegion(worker_i); - BufferingOopsInHeapRegionClosure boc(oc); - ScanRSClosure scanRScl(&boc, worker_i); + ScanRSClosure scanRScl(oc, worker_i); _g1->collection_set_iterate_from(startRegion, &scanRScl); scanRScl.set_try_claimed(); _g1->collection_set_iterate_from(startRegion, &scanRScl); - boc.done(); - double closure_app_time_sec = boc.closure_app_seconds(); - double scan_rs_time_sec = (os::elapsedTime() - rs_time_start) - - closure_app_time_sec; - double closure_app_time_ms = closure_app_time_sec * 1000.0; + double scan_rs_time_sec = os::elapsedTime() - rs_time_start; assert( _cards_scanned != NULL, "invariant" ); _cards_scanned[worker_i] = scanRScl.cards_done(); _g1p->record_scan_rs_start_time(worker_i, rs_time_start * 1000.0); _g1p->record_scan_rs_time(worker_i, scan_rs_time_sec * 1000.0); - - double scan_new_refs_time_ms = _g1p->get_scan_new_refs_time(worker_i); - if (scan_new_refs_time_ms > 0.0) { - closure_app_time_ms += scan_new_refs_time_ms; - } - - _g1p->record_obj_copy_time(worker_i, closure_app_time_ms); } void HRInto_G1RemSet::updateRS(int worker_i) { @@ -449,9 +431,8 @@ oc->do_oop(p); } } - _g1p->record_scan_new_refs_time(worker_i, - (os::elapsedTime() - scan_new_refs_start_sec) - * 1000.0); + double scan_new_refs_time_ms = (os::elapsedTime() - scan_new_refs_start_sec) * 1000.0; + _g1p->record_scan_new_refs_time(worker_i, scan_new_refs_time_ms); } void HRInto_G1RemSet::cleanupHRRS() { @@ -486,7 +467,7 @@ // and they are causing failures. When we resolve said race // conditions, we'll revert back to parallel remembered set // updating and scanning. See CRs 6677707 and 6677708. - if (G1ParallelRSetUpdatingEnabled || (worker_i == 0)) { + if (G1UseParallelRSetUpdating || (worker_i == 0)) { updateRS(worker_i); scanNewRefsRS(oc, worker_i); } else { @@ -495,7 +476,7 @@ _g1p->record_update_rs_time(worker_i, 0.0); _g1p->record_scan_new_refs_time(worker_i, 0.0); } - if (G1ParallelRSetScanningEnabled || (worker_i == 0)) { + if (G1UseParallelRSetScanning || (worker_i == 0)) { scanRS(oc, worker_i); } else { _g1p->record_scan_rs_start_time(worker_i, os::elapsedTime() * 1000.0); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1_globals.hpp --- a/src/share/vm/gc_implementation/g1/g1_globals.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1_globals.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -37,13 +37,6 @@ develop(intx, G1MarkingOverheadPercent, 0, \ "Overhead of concurrent marking") \ \ - develop(bool, G1AccountConcurrentOverhead, false, \ - "Whether soft real-time compliance in G1 will take into account" \ - "concurrent overhead") \ - \ - product(intx, G1YoungGenSize, 0, \ - "Size of the G1 young generation, 0 is the adaptive policy") \ - \ develop(bool, G1Gen, true, \ "If true, it will enable the generational G1") \ \ @@ -59,8 +52,14 @@ develop(intx, G1MarkingVerboseLevel, 0, \ "Level (0-4) of verboseness of the marking code") \ \ - develop(bool, G1VerifyConcMarkPrintReachable, false, \ - "If conc mark verification fails, print reachable objects") \ + develop(bool, G1PrintReachableAtInitialMark, false, \ + "Reachable object dump at the initial mark pause") \ + \ + develop(bool, G1VerifyDuringGCPrintReachable, false, \ + "If conc mark verification fails, dump reachable objects") \ + \ + develop(ccstr, G1PrintReachableBaseFile, NULL, \ + "The base file name for the reachable object dumps") \ \ develop(bool, G1TraceMarkStackOverflow, false, \ "If true, extra debugging code for CM restart for ovflw.") \ @@ -68,7 +67,7 @@ develop(intx, G1PausesBtwnConcMark, -1, \ "If positive, fixed number of pauses between conc markings") \ \ - diagnostic(bool, G1SummarizeConcurrentMark, false, \ + diagnostic(bool, G1SummarizeConcMark, false, \ "Summarize concurrent mark info") \ \ diagnostic(bool, G1SummarizeRSetStats, false, \ @@ -83,48 +82,25 @@ diagnostic(bool, G1SummarizeZFStats, false, \ "Summarize zero-filling info") \ \ - develop(bool, G1TraceConcurrentRefinement, false, \ + diagnostic(bool, G1TraceConcRefinement, false, \ "Trace G1 concurrent refinement") \ \ - product(intx, G1MarkStackSize, 2 * 1024 * 1024, \ - "Size of the mark stack for concurrent marking.") \ - \ product(intx, G1MarkRegionStackSize, 1024 * 1024, \ "Size of the region stack for concurrent marking.") \ \ - develop(bool, G1ConcRefine, true, \ - "If true, run concurrent rem set refinement for G1") \ - \ - develop(intx, G1ConcRefineTargTraversals, 4, \ - "Number of concurrent refinement we try to achieve") \ - \ - develop(intx, G1ConcRefineInitialDelta, 4, \ - "Number of heap regions of alloc ahead of starting collection " \ - "pause to start concurrent refinement (initially)") \ - \ - develop(bool, G1SmoothConcRefine, true, \ - "Attempts to smooth out the overhead of concurrent refinement") \ - \ develop(bool, G1ConcZeroFill, true, \ "If true, run concurrent zero-filling thread") \ \ develop(intx, G1ConcZFMaxRegions, 1, \ "Stop zero-filling when # of zf'd regions reaches") \ \ - product(intx, G1SteadyStateUsed, 90, \ - "If non-0, try to maintain 'used' at this pct (of max)") \ - \ - product(intx, G1SteadyStateUsedDelta, 30, \ - "If G1SteadyStateUsed is non-0, then do pause this number of " \ - "of percentage points earlier if no marking is in progress.") \ - \ develop(bool, G1SATBBarrierPrintNullPreVals, false, \ "If true, count frac of ptr writes with null pre-vals.") \ \ - product(intx, G1SATBLogBufferSize, 1*K, \ + product(intx, G1SATBBufferSize, 1*K, \ "Number of entries in an SATB log buffer.") \ \ - product(intx, G1SATBProcessCompletedThreshold, 20, \ + develop(intx, G1SATBProcessCompletedThreshold, 20, \ "Number of completed buffers that triggers log processing.") \ \ develop(intx, G1ExtraRegionSurvRate, 33, \ @@ -138,7 +114,7 @@ develop(bool, G1SATBPrintStubs, false, \ "If true, print generated stubs for the SATB barrier") \ \ - product(intx, G1ExpandByPercentOfAvailable, 20, \ + experimental(intx, G1ExpandByPercentOfAvailable, 20, \ "When expanding, % of uncommitted space to claim.") \ \ develop(bool, G1RSBarrierRegionFilter, true, \ @@ -176,13 +152,38 @@ product(intx, G1UpdateBufferSize, 256, \ "Size of an update buffer") \ \ - product(intx, G1UpdateBufferQueueProcessingThreshold, 5, \ + product(intx, G1ConcRefinementYellowZone, 0, \ "Number of enqueued update buffers that will " \ - "trigger concurrent processing") \ + "trigger concurrent processing. Will be selected ergonomically " \ + "by default.") \ + \ + product(intx, G1ConcRefinementRedZone, 0, \ + "Maximum number of enqueued update buffers before mutator " \ + "threads start processing new ones instead of enqueueing them. " \ + "Will be selected ergonomically by default. Zero will disable " \ + "concurrent processing.") \ + \ + product(intx, G1ConcRefinementGreenZone, 0, \ + "The number of update buffers that are left in the queue by the " \ + "concurrent processing threads. Will be selected ergonomically " \ + "by default.") \ \ - product(intx, G1UpdateBufferQueueMaxLength, 30, \ - "Maximum number of enqueued update buffers before mutator " \ - "threads start processing new ones instead of enqueueing them") \ + product(intx, G1ConcRefinementServiceIntervalMillis, 300, \ + "The last concurrent refinement thread wakes up every " \ + "specified number of milliseconds to do miscellaneous work.") \ + \ + product(intx, G1ConcRefinementThresholdStep, 0, \ + "Each time the rset update queue increases by this amount " \ + "activate the next refinement thread if available. " \ + "Will be selected ergonomically by default.") \ + \ + product(intx, G1RSetUpdatingPauseTimePercent, 10, \ + "A target percentage of time that is allowed to be spend on " \ + "process RS update buffers during the collection pause.") \ + \ + product(bool, G1UseAdaptiveConcRefinement, true, \ + "Select green, yellow and red zones adaptively to meet the " \ + "the pause requirements.") \ \ develop(intx, G1ConcRSLogCacheSize, 10, \ "Log base 2 of the length of conc RS hot-card cache.") \ @@ -193,8 +194,20 @@ develop(bool, G1PrintOopAppls, false, \ "When true, print applications of closures to external locs.") \ \ - develop(intx, G1LogRSRegionEntries, 7, \ - "Log_2 of max number of regions for which we keep bitmaps.") \ + develop(intx, G1RSetRegionEntriesBase, 256, \ + "Max number of regions in a fine-grain table per MB.") \ + \ + product(intx, G1RSetRegionEntries, 0, \ + "Max number of regions for which we keep bitmaps." \ + "Will be set ergonomically by default") \ + \ + develop(intx, G1RSetSparseRegionEntriesBase, 4, \ + "Max number of entries per region in a sparse table " \ + "per MB.") \ + \ + product(intx, G1RSetSparseRegionEntries, 0, \ + "Max number of entries per region in a sparse table." \ + "Will be set ergonomically by default.") \ \ develop(bool, G1RecordHRRSOops, false, \ "When true, record recent calls to rem set operations.") \ @@ -219,15 +232,15 @@ "the number of regions for which we'll print a surv rate " \ "summary.") \ \ - product(bool, G1UseScanOnlyPrefix, false, \ + develop(bool, G1UseScanOnlyPrefix, false, \ "It determines whether the system will calculate an optimum " \ "scan-only set.") \ \ - product(intx, G1MinReservePercent, 10, \ + product(intx, G1ReservePercent, 10, \ "It determines the minimum reserve we should have in the heap " \ "to minimize the probability of promotion failure.") \ \ - diagnostic(bool, G1PrintRegions, false, \ + diagnostic(bool, G1PrintHeapRegions, false, \ "If set G1 will print information on which regions are being " \ "allocated and which are reclaimed.") \ \ @@ -237,8 +250,9 @@ develop(bool, G1HRRSFlushLogBuffersOnVerify, false, \ "Forces flushing of log buffers before verification.") \ \ - product(bool, G1UseSurvivorSpaces, true, \ - "When true, use survivor space.") \ + develop(bool, G1FailOnFPError, false, \ + "When set, G1 will fail when it encounters an FP 'error', " \ + "so as to allow debugging") \ \ develop(bool, G1FixedTenuringThreshold, false, \ "When set, G1 will not adjust the tenuring threshold") \ @@ -250,20 +264,37 @@ "If non-0 is the size of the G1 survivor space, " \ "otherwise SurvivorRatio is used to determine the size") \ \ - experimental(bool, G1ParallelRSetUpdatingEnabled, false, \ + product(bool, G1UseFixedWindowMMUTracker, false, \ + "If the MMU tracker's memory is full, forget the oldest entry") \ + \ + product(uintx, G1HeapRegionSize, 0, \ + "Size of the G1 regions.") \ + \ + experimental(bool, G1UseParallelRSetUpdating, false, \ "Enables the parallelization of remembered set updating " \ "during evacuation pauses") \ \ - experimental(bool, G1ParallelRSetScanningEnabled, false, \ + experimental(bool, G1UseParallelRSetScanning, false, \ "Enables the parallelization of remembered set scanning " \ "during evacuation pauses") \ \ - product(uintx, G1ParallelRSetThreads, 0, \ + product(uintx, G1ConcRefinementThreads, 0, \ "If non-0 is the number of parallel rem set update threads, " \ "otherwise the value is determined ergonomically.") \ \ develop(intx, G1CardCountCacheExpandThreshold, 16, \ "Expand the card count cache if the number of collisions for " \ - "a particular entry exceeds this value.") + "a particular entry exceeds this value.") \ + \ + develop(bool, G1VerifyCTCleanup, false, \ + "Verify card table cleanup.") \ + \ + product(uintx, G1RSetScanBlockSize, 64, \ + "Size of a work unit of cards claimed by a worker thread" \ + "during RSet scanning.") \ + \ + develop(bool, ReduceInitialCardMarksForG1, false, \ + "When ReduceInitialCardMarks is true, this flag setting " \ + " controls whether G1 allows the RICM optimization") G1_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG, DECLARE_MANAGEABLE_FLAG, DECLARE_PRODUCT_RW_FLAG) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp --- a/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -33,11 +33,12 @@ }; template + bool do_mark_forwardee> class G1ParCopyClosure; class G1ParScanClosure; +class G1ParPushHeapRSClosure; -typedef G1ParCopyClosure G1ParScanHeapEvacClosure; +typedef G1ParCopyClosure G1ParScanHeapEvacClosure; class FilterIntoCSClosure; class FilterOutOfRegionClosure; @@ -51,6 +52,7 @@ #define FURTHER_SPECIALIZED_OOP_OOP_ITERATE_CLOSURES(f) \ f(G1ParScanHeapEvacClosure,_nv) \ f(G1ParScanClosure,_nv) \ + f(G1ParPushHeapRSClosure,_nv) \ f(FilterIntoCSClosure,_nv) \ f(FilterOutOfRegionClosure,_nv) \ f(FilterInHeapRegionAndIntoCSClosure,_nv) \ diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/heapRegion.cpp --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,6 +25,12 @@ #include "incls/_precompiled.incl" #include "incls/_heapRegion.cpp.incl" +int HeapRegion::LogOfHRGrainBytes = 0; +int HeapRegion::LogOfHRGrainWords = 0; +int HeapRegion::GrainBytes = 0; +int HeapRegion::GrainWords = 0; +int HeapRegion::CardsPerRegion = 0; + HeapRegionDCTOC::HeapRegionDCTOC(G1CollectedHeap* g1, HeapRegion* hr, OopClosure* cl, CardTableModRefBS::PrecisionStyle precision, @@ -231,6 +237,73 @@ } } +// Minimum region size; we won't go lower than that. +// We might want to decrease this in the future, to deal with small +// heaps a bit more efficiently. +#define MIN_REGION_SIZE ( 1024 * 1024 ) + +// Maximum region size; we don't go higher than that. There's a good +// reason for having an upper bound. We don't want regions to get too +// large, otherwise cleanup's effectiveness would decrease as there +// will be fewer opportunities to find totally empty regions after +// marking. +#define MAX_REGION_SIZE ( 32 * 1024 * 1024 ) + +// The automatic region size calculation will try to have around this +// many regions in the heap (based on the min heap size). +#define TARGET_REGION_NUMBER 2048 + +void HeapRegion::setup_heap_region_size(uintx min_heap_size) { + // region_size in bytes + uintx region_size = G1HeapRegionSize; + if (FLAG_IS_DEFAULT(G1HeapRegionSize)) { + // We base the automatic calculation on the min heap size. This + // can be problematic if the spread between min and max is quite + // wide, imagine -Xms128m -Xmx32g. But, if we decided it based on + // the max size, the region size might be way too large for the + // min size. Either way, some users might have to set the region + // size manually for some -Xms / -Xmx combos. + + region_size = MAX2(min_heap_size / TARGET_REGION_NUMBER, + (uintx) MIN_REGION_SIZE); + } + + int region_size_log = log2_long((jlong) region_size); + // Recalculate the region size to make sure it's a power of + // 2. This means that region_size is the largest power of 2 that's + // <= what we've calculated so far. + region_size = 1 << region_size_log; + + // Now make sure that we don't go over or under our limits. + if (region_size < MIN_REGION_SIZE) { + region_size = MIN_REGION_SIZE; + } else if (region_size > MAX_REGION_SIZE) { + region_size = MAX_REGION_SIZE; + } + + // And recalculate the log. + region_size_log = log2_long((jlong) region_size); + + // Now, set up the globals. + guarantee(LogOfHRGrainBytes == 0, "we should only set it once"); + LogOfHRGrainBytes = region_size_log; + + guarantee(LogOfHRGrainWords == 0, "we should only set it once"); + LogOfHRGrainWords = LogOfHRGrainBytes - LogHeapWordSize; + + guarantee(GrainBytes == 0, "we should only set it once"); + // The cast to int is safe, given that we've bounded region_size by + // MIN_REGION_SIZE and MAX_REGION_SIZE. + GrainBytes = (int) region_size; + + guarantee(GrainWords == 0, "we should only set it once"); + GrainWords = GrainBytes >> LogHeapWordSize; + guarantee(1 << LogOfHRGrainWords == GrainWords, "sanity"); + + guarantee(CardsPerRegion == 0, "we should only set it once"); + CardsPerRegion = GrainBytes >> CardTableModRefBS::card_shift; +} + void HeapRegion::reset_after_compaction() { G1OffsetTableContigSpace::reset_after_compaction(); // After a compaction the mark bitmap is invalid, so we must @@ -649,12 +722,13 @@ st->print(" F"); else st->print(" "); - st->print(" %d", _gc_time_stamp); + st->print(" %5d", _gc_time_stamp); G1OffsetTableContigSpace::print_on(st); } void HeapRegion::verify(bool allow_dirty) const { - verify(allow_dirty, /* use_prev_marking */ true); + bool dummy = false; + verify(allow_dirty, /* use_prev_marking */ true, /* failures */ &dummy); } #define OBJ_SAMPLE_INTERVAL 0 @@ -663,8 +737,11 @@ // This really ought to be commoned up into OffsetTableContigSpace somehow. // We would need a mechanism to make that code skip dead objects. -void HeapRegion::verify(bool allow_dirty, bool use_prev_marking) const { +void HeapRegion::verify(bool allow_dirty, + bool use_prev_marking, + bool* failures) const { G1CollectedHeap* g1 = G1CollectedHeap::heap(); + *failures = false; HeapWord* p = bottom(); HeapWord* prev_p = NULL; int objs = 0; @@ -673,8 +750,14 @@ while (p < top()) { size_t size = oop(p)->size(); if (blocks == BLOCK_SAMPLE_INTERVAL) { - guarantee(p == block_start_const(p + (size/2)), - "check offset computation"); + HeapWord* res = block_start_const(p + (size/2)); + if (p != res) { + gclog_or_tty->print_cr("offset computation 1 for "PTR_FORMAT" and " + SIZE_FORMAT" returned "PTR_FORMAT, + p, size, res); + *failures = true; + return; + } blocks = 0; } else { blocks++; @@ -682,11 +765,34 @@ if (objs == OBJ_SAMPLE_INTERVAL) { oop obj = oop(p); if (!g1->is_obj_dead_cond(obj, this, use_prev_marking)) { - obj->verify(); - vl_cl.set_containing_obj(obj); - obj->oop_iterate(&vl_cl); - if (G1MaxVerifyFailures >= 0 - && vl_cl.n_failures() >= G1MaxVerifyFailures) break; + if (obj->is_oop()) { + klassOop klass = obj->klass(); + if (!klass->is_perm()) { + gclog_or_tty->print_cr("klass "PTR_FORMAT" of object "PTR_FORMAT" " + "not in perm", klass, obj); + *failures = true; + return; + } else if (!klass->is_klass()) { + gclog_or_tty->print_cr("klass "PTR_FORMAT" of object "PTR_FORMAT" " + "not a klass", klass, obj); + *failures = true; + return; + } else { + vl_cl.set_containing_obj(obj); + obj->oop_iterate(&vl_cl); + if (vl_cl.failures()) { + *failures = true; + } + if (G1MaxVerifyFailures >= 0 && + vl_cl.n_failures() >= G1MaxVerifyFailures) { + return; + } + } + } else { + gclog_or_tty->print_cr(PTR_FORMAT" no an oop", obj); + *failures = true; + return; + } } objs = 0; } else { @@ -698,21 +804,22 @@ HeapWord* rend = end(); HeapWord* rtop = top(); if (rtop < rend) { - guarantee(block_start_const(rtop + (rend - rtop) / 2) == rtop, - "check offset computation"); - } - if (vl_cl.failures()) { - gclog_or_tty->print_cr("Heap:"); - G1CollectedHeap::heap()->print_on(gclog_or_tty, true /* extended */); - gclog_or_tty->print_cr(""); + HeapWord* res = block_start_const(rtop + (rend - rtop) / 2); + if (res != rtop) { + gclog_or_tty->print_cr("offset computation 2 for "PTR_FORMAT" and " + PTR_FORMAT" returned "PTR_FORMAT, + rtop, rend, res); + *failures = true; + return; + } } - if (VerifyDuringGC && - G1VerifyConcMarkPrintReachable && - vl_cl.failures()) { - g1->concurrent_mark()->print_prev_bitmap_reachable(); + + if (p != top()) { + gclog_or_tty->print_cr("end of last object "PTR_FORMAT" " + "does not match top "PTR_FORMAT, p, top()); + *failures = true; + return; } - guarantee(!vl_cl.failures(), "region verification failed"); - guarantee(p == top(), "end of last object must match end of space"); } // G1OffsetTableContigSpace code; copied from space.cpp. Hope this can go diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/heapRegion.hpp --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -297,15 +297,24 @@ HeapRegion(G1BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr, bool is_zeroed); - enum SomePublicConstants { - // HeapRegions are GrainBytes-aligned - // and have sizes that are multiples of GrainBytes. - LogOfHRGrainBytes = 20, - LogOfHRGrainWords = LogOfHRGrainBytes - LogHeapWordSize, - GrainBytes = 1 << LogOfHRGrainBytes, - GrainWords = 1 <= _next_top_at_mark_start, - "Increase only"); - // Survivor regions will be scanned on the start of concurrent - // marking. - if (!is_survivor()) { - _next_top_at_mark_start = top(); - } + assert(top() >= _next_top_at_mark_start, "Increase only"); + _next_top_at_mark_start = top(); } // Returns "false" iff no object in the region was allocated when the @@ -789,7 +793,7 @@ // use_prev_marking == true. Currently, there is only one case where // this is called with use_prev_marking == false, which is to verify // the "next" marking information at the end of remark. - void verify(bool allow_dirty, bool use_prev_marking) const; + void verify(bool allow_dirty, bool use_prev_marking, bool *failures) const; // Override; it uses the "prev" marking information virtual void verify(bool allow_dirty) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -57,10 +57,6 @@ #endif // _MSC_VER - enum SomePrivateConstants { - CardsPerRegion = HeapRegion::GrainBytes >> CardTableModRefBS::card_shift - }; - protected: // We need access in order to union things into the base table. BitMap* bm() { return &_bm; } @@ -76,7 +72,7 @@ #if PRT_COUNT_OCCUPIED _occupied(0), #endif - _bm(CardsPerRegion, false /* in-resource-area */) + _bm(HeapRegion::CardsPerRegion, false /* in-resource-area */) {} static void free(PerRegionTable* prt) { @@ -144,7 +140,8 @@ CardIdx_t from_card = (CardIdx_t) hw_offset >> (CardTableModRefBS::card_shift - LogHeapWordSize); - assert(0 <= from_card && from_card < CardsPerRegion, "Must be in range."); + assert(0 <= from_card && from_card < HeapRegion::CardsPerRegion, + "Must be in range."); add_card_work(from_card, par); } } @@ -261,42 +258,6 @@ ReserveParTableExpansion = 1 }; - void par_expand() { - int n = HeapRegionRemSet::num_par_rem_sets()-1; - if (n <= 0) return; - if (_par_tables == NULL) { - PerRegionTable* res = - (PerRegionTable*) - Atomic::cmpxchg_ptr((PerRegionTable*)ReserveParTableExpansion, - &_par_tables, NULL); - if (res != NULL) return; - // Otherwise, we reserved the right to do the expansion. - - PerRegionTable** ptables = NEW_C_HEAP_ARRAY(PerRegionTable*, n); - for (int i = 0; i < n; i++) { - PerRegionTable* ptable = PerRegionTable::alloc(hr()); - ptables[i] = ptable; - } - // Here we do not need an atomic. - _par_tables = ptables; -#if COUNT_PAR_EXPANDS - print_par_expand(); -#endif - // We must put this table on the expanded list. - PosParPRT* exp_head = _par_expanded_list; - while (true) { - set_next_par_expanded(exp_head); - PosParPRT* res = - (PosParPRT*) - Atomic::cmpxchg_ptr(this, &_par_expanded_list, exp_head); - if (res == exp_head) return; - // Otherwise. - exp_head = res; - } - ShouldNotReachHere(); - } - } - void par_contract() { assert(_par_tables != NULL, "Precondition."); int n = HeapRegionRemSet::num_par_rem_sets()-1; @@ -394,13 +355,49 @@ void set_next(PosParPRT* nxt) { _next = nxt; } PosParPRT** next_addr() { return &_next; } + bool should_expand(int tid) { + return par_tables() == NULL && tid > 0 && hr()->is_gc_alloc_region(); + } + + void par_expand() { + int n = HeapRegionRemSet::num_par_rem_sets()-1; + if (n <= 0) return; + if (_par_tables == NULL) { + PerRegionTable* res = + (PerRegionTable*) + Atomic::cmpxchg_ptr((PerRegionTable*)ReserveParTableExpansion, + &_par_tables, NULL); + if (res != NULL) return; + // Otherwise, we reserved the right to do the expansion. + + PerRegionTable** ptables = NEW_C_HEAP_ARRAY(PerRegionTable*, n); + for (int i = 0; i < n; i++) { + PerRegionTable* ptable = PerRegionTable::alloc(hr()); + ptables[i] = ptable; + } + // Here we do not need an atomic. + _par_tables = ptables; +#if COUNT_PAR_EXPANDS + print_par_expand(); +#endif + // We must put this table on the expanded list. + PosParPRT* exp_head = _par_expanded_list; + while (true) { + set_next_par_expanded(exp_head); + PosParPRT* res = + (PosParPRT*) + Atomic::cmpxchg_ptr(this, &_par_expanded_list, exp_head); + if (res == exp_head) return; + // Otherwise. + exp_head = res; + } + ShouldNotReachHere(); + } + } + void add_reference(OopOrNarrowOopStar from, int tid) { // Expand if necessary. PerRegionTable** pt = par_tables(); - if (par_tables() == NULL && tid > 0 && hr()->is_gc_alloc_region()) { - par_expand(); - pt = par_tables(); - } if (pt != NULL) { // We always have to assume that mods to table 0 are in parallel, // because of the claiming scheme in parallel expansion. A thread @@ -508,12 +505,13 @@ typedef PosParPRT* PosParPRTPtr; if (_max_fine_entries == 0) { assert(_mod_max_fine_entries_mask == 0, "Both or none."); - _max_fine_entries = (size_t)(1 << G1LogRSRegionEntries); + size_t max_entries_log = (size_t)log2_long((jlong)G1RSetRegionEntries); + _max_fine_entries = (size_t)(1 << max_entries_log); _mod_max_fine_entries_mask = _max_fine_entries - 1; #if SAMPLE_FOR_EVICTION assert(_fine_eviction_sample_size == 0 && _fine_eviction_stride == 0, "All init at same time."); - _fine_eviction_sample_size = MAX2((size_t)4, (size_t)G1LogRSRegionEntries); + _fine_eviction_sample_size = MAX2((size_t)4, max_entries_log); _fine_eviction_stride = _max_fine_entries / _fine_eviction_sample_size; #endif } @@ -631,7 +629,7 @@ uintptr_t(from_hr->bottom()) >> CardTableModRefBS::card_shift; CardIdx_t card_index = from_card - from_hr_bot_card_index; - assert(0 <= card_index && card_index < PosParPRT::CardsPerRegion, + assert(0 <= card_index && card_index < HeapRegion::CardsPerRegion, "Must be in range."); if (G1HRRSUseSparseTable && _sparse_table.add_card(from_hrs_ind, card_index)) { @@ -658,13 +656,6 @@ #endif } - // Otherwise, transfer from sparse to fine-grain. - CardIdx_t cards[SparsePRTEntry::CardsPerEntry]; - if (G1HRRSUseSparseTable) { - bool res = _sparse_table.get_cards(from_hrs_ind, &cards[0]); - assert(res, "There should have been an entry"); - } - if (_n_fine_entries == _max_fine_entries) { prt = delete_region_table(); } else { @@ -679,10 +670,12 @@ _fine_grain_regions[ind] = prt; _n_fine_entries++; - // Add in the cards from the sparse table. if (G1HRRSUseSparseTable) { - for (int i = 0; i < SparsePRTEntry::CardsPerEntry; i++) { - CardIdx_t c = cards[i]; + // Transfer from sparse to fine-grain. + SparsePRTEntry *sprt_entry = _sparse_table.get_entry(from_hrs_ind); + assert(sprt_entry != NULL, "There should have been an entry"); + for (int i = 0; i < SparsePRTEntry::cards_num(); i++) { + CardIdx_t c = sprt_entry->card(i); if (c != SparsePRTEntry::NullEntry) { prt->add_card(c); } @@ -699,7 +692,21 @@ // OtherRegionsTable for why this is OK. assert(prt != NULL, "Inv"); - prt->add_reference(from, tid); + if (prt->should_expand(tid)) { + MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); + HeapRegion* prt_hr = prt->hr(); + if (prt_hr == from_hr) { + // Make sure the table still corresponds to the same region + prt->par_expand(); + prt->add_reference(from, tid); + } + // else: The table has been concurrently coarsened, evicted, and + // the table data structure re-used for another table. So, we + // don't need to add the reference any more given that the table + // has been coarsened and the whole region will be scanned anyway. + } else { + prt->add_reference(from, tid); + } if (G1RecordHRRSOops) { HeapRegionRemSet::record(hr(), from); #if HRRS_VERBOSE @@ -922,7 +929,7 @@ } size_t OtherRegionsTable::occ_coarse() const { - return (_n_coarse_entries * PosParPRT::CardsPerRegion); + return (_n_coarse_entries * HeapRegion::CardsPerRegion); } size_t OtherRegionsTable::occ_sparse() const { @@ -1049,7 +1056,8 @@ uintptr_t(hr->bottom()) >> CardTableModRefBS::card_shift; assert(from_card >= hr_bot_card_index, "Inv"); CardIdx_t card_index = from_card - hr_bot_card_index; - assert(0 <= card_index && card_index < PosParPRT::CardsPerRegion, "Must be in range."); + assert(0 <= card_index && card_index < HeapRegion::CardsPerRegion, + "Must be in range."); return _sparse_table.contains_card(hr_ind, card_index); } @@ -1072,6 +1080,19 @@ {} +void HeapRegionRemSet::setup_remset_size() { + // Setup sparse and fine-grain tables sizes. + // table_size = base * (log(region_size / 1M) + 1) + int region_size_log_mb = MAX2((int)HeapRegion::LogOfHRGrainBytes - (int)LOG_M, 0); + if (FLAG_IS_DEFAULT(G1RSetSparseRegionEntries)) { + G1RSetSparseRegionEntries = G1RSetSparseRegionEntriesBase * (region_size_log_mb + 1); + } + if (FLAG_IS_DEFAULT(G1RSetRegionEntries)) { + G1RSetRegionEntries = G1RSetRegionEntriesBase * (region_size_log_mb + 1); + } + guarantee(G1RSetSparseRegionEntries > 0 && G1RSetRegionEntries > 0 , "Sanity"); +} + void HeapRegionRemSet::init_for_par_iteration() { _iter_state = Unclaimed; } @@ -1176,7 +1197,7 @@ _is = Sparse; // Set these values so that we increment to the first region. _coarse_cur_region_index = -1; - _coarse_cur_region_cur_card = (PosParPRT::CardsPerRegion-1);; + _coarse_cur_region_cur_card = (HeapRegion::CardsPerRegion-1);; _cur_region_cur_card = 0; @@ -1195,7 +1216,7 @@ // Go to the next card. _coarse_cur_region_cur_card++; // Was the last the last card in the current region? - if (_coarse_cur_region_cur_card == PosParPRT::CardsPerRegion) { + if (_coarse_cur_region_cur_card == HeapRegion::CardsPerRegion) { // Yes: find the next region. This may leave _coarse_cur_region_index // Set to the last index, in which case there are no more coarse // regions. @@ -1232,7 +1253,7 @@ _fine_cur_prt->_bm.get_next_one_offset(_cur_region_cur_card + 1); } while (!fine_has_next()) { - if (_cur_region_cur_card == PosParPRT::CardsPerRegion) { + if (_cur_region_cur_card == (size_t) HeapRegion::CardsPerRegion) { _cur_region_cur_card = 0; _fine_cur_prt = _fine_cur_prt->next(); } @@ -1255,7 +1276,7 @@ bool HeapRegionRemSetIterator::fine_has_next() { return _fine_cur_prt != NULL && - _cur_region_cur_card < PosParPRT::CardsPerRegion; + _cur_region_cur_card < (size_t) HeapRegion::CardsPerRegion; } bool HeapRegionRemSetIterator::has_next(size_t& card_index) { @@ -1387,7 +1408,7 @@ os::sleep(Thread::current(), (jlong)5000, false); G1CollectedHeap* g1h = G1CollectedHeap::heap(); - // Run with "-XX:G1LogRSRegionEntries=2", so that 1 and 5 end up in same + // Run with "-XX:G1LogRSetRegionEntries=2", so that 1 and 5 end up in same // hash bucket. HeapRegion* hr0 = g1h->region_at(0); HeapRegion* hr1 = g1h->region_at(1); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp --- a/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -187,7 +187,8 @@ void clear_outgoing_entries(); enum ParIterState { Unclaimed, Claimed, Complete }; - ParIterState _iter_state; + volatile ParIterState _iter_state; + volatile jlong _iter_claimed; // Unused unless G1RecordHRRSOops is true. @@ -209,6 +210,7 @@ HeapRegion* hr); static int num_par_rem_sets(); + static void setup_remset_size(); HeapRegion* hr() const { return _other_regions.hr(); @@ -272,6 +274,19 @@ // Returns "true" iff the region's iteration is complete. bool iter_is_complete(); + // Support for claiming blocks of cards during iteration + void set_iter_claimed(size_t x) { _iter_claimed = (jlong)x; } + size_t iter_claimed() const { return (size_t)_iter_claimed; } + // Claim the next block of cards + size_t iter_claimed_next(size_t step) { + size_t current, next; + do { + current = iter_claimed(); + next = current + step; + } while (Atomic::cmpxchg((jlong)next, &_iter_claimed, (jlong)current) != (jlong)current); + return current; + } + // Initialize the given iterator to iterate over this rem set. void init_iterator(HeapRegionRemSetIterator* iter) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/heapRegionSeq.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -302,9 +302,9 @@ if (cur->isHumongous()) { return MemRegion(last_start, end); } - cur->reset_zero_fill(); assert(cur == _regions.top(), "Should be top"); if (!cur->is_empty()) break; + cur->reset_zero_fill(); shrink_bytes -= cur->capacity(); num_regions_deleted++; _regions.pop(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/ptrQueue.cpp --- a/src/share/vm/gc_implementation/g1/ptrQueue.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/ptrQueue.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -64,8 +64,8 @@ while (_index == 0) { handle_zero_index(); } + assert(_index > 0, "postcondition"); - _index -= oopSize; _buf[byte_index_to_index((int)_index)] = ptr; assert(0 <= _index && _index <= _sz, "Invariant."); @@ -73,7 +73,12 @@ void PtrQueue::locking_enqueue_completed_buffer(void** buf) { assert(_lock->owned_by_self(), "Required."); + + // We have to unlock _lock (which may be Shared_DirtyCardQ_lock) before + // we acquire DirtyCardQ_CBL_mon inside enqeue_complete_buffer as they + // have the same rank and we may get the "possible deadlock" message _lock->unlock(); + qset()->enqueue_complete_buffer(buf); // We must relock only because the caller will unlock, for the normal // case. @@ -99,94 +104,139 @@ assert(_sz > 0, "Didn't set a buffer size."); MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag); if (_fl_owner->_buf_free_list != NULL) { - void** res = _fl_owner->_buf_free_list; - _fl_owner->_buf_free_list = (void**)_fl_owner->_buf_free_list[0]; + void** res = BufferNode::make_buffer_from_node(_fl_owner->_buf_free_list); + _fl_owner->_buf_free_list = _fl_owner->_buf_free_list->next(); _fl_owner->_buf_free_list_sz--; - // Just override the next pointer with NULL, just in case we scan this part - // of the buffer. - res[0] = NULL; return res; } else { - return NEW_C_HEAP_ARRAY(void*, _sz); + // Allocate space for the BufferNode in front of the buffer. + char *b = NEW_C_HEAP_ARRAY(char, _sz + BufferNode::aligned_size()); + return BufferNode::make_buffer_from_block(b); } } void PtrQueueSet::deallocate_buffer(void** buf) { assert(_sz > 0, "Didn't set a buffer size."); MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag); - buf[0] = (void*)_fl_owner->_buf_free_list; - _fl_owner->_buf_free_list = buf; + BufferNode *node = BufferNode::make_node_from_buffer(buf); + node->set_next(_fl_owner->_buf_free_list); + _fl_owner->_buf_free_list = node; _fl_owner->_buf_free_list_sz++; } void PtrQueueSet::reduce_free_list() { + assert(_fl_owner == this, "Free list reduction is allowed only for the owner"); // For now we'll adopt the strategy of deleting half. MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag); size_t n = _buf_free_list_sz / 2; while (n > 0) { assert(_buf_free_list != NULL, "_buf_free_list_sz must be wrong."); - void** head = _buf_free_list; - _buf_free_list = (void**)_buf_free_list[0]; - FREE_C_HEAP_ARRAY(void*,head); + void* b = BufferNode::make_block_from_node(_buf_free_list); + _buf_free_list = _buf_free_list->next(); + FREE_C_HEAP_ARRAY(char, b); + _buf_free_list_sz --; n--; } } -void PtrQueueSet::enqueue_complete_buffer(void** buf, size_t index, bool ignore_max_completed) { - // I use explicit locking here because there's a bailout in the middle. - _cbl_mon->lock_without_safepoint_check(); +void PtrQueue::handle_zero_index() { + assert(0 == _index, "Precondition."); + // This thread records the full buffer and allocates a new one (while + // holding the lock if there is one). + if (_buf != NULL) { + if (_lock) { + assert(_lock->owned_by_self(), "Required."); + + // The current PtrQ may be the shared dirty card queue and + // may be being manipulated by more than one worker thread + // during a pause. Since the enqueuing of the completed + // buffer unlocks the Shared_DirtyCardQ_lock more than one + // worker thread can 'race' on reading the shared queue attributes + // (_buf and _index) and multiple threads can call into this + // routine for the same buffer. This will cause the completed + // buffer to be added to the CBL multiple times. - Thread* thread = Thread::current(); - assert( ignore_max_completed || - thread->is_Java_thread() || - SafepointSynchronize::is_at_safepoint(), - "invariant" ); - ignore_max_completed = ignore_max_completed || !thread->is_Java_thread(); + // We "claim" the current buffer by caching value of _buf in + // a local and clearing the field while holding _lock. When + // _lock is released (while enqueueing the completed buffer) + // the thread that acquires _lock will skip this code, + // preventing the subsequent the multiple enqueue, and + // install a newly allocated buffer below. + + void** buf = _buf; // local pointer to completed buffer + _buf = NULL; // clear shared _buf field + + locking_enqueue_completed_buffer(buf); // enqueue completed buffer + + // While the current thread was enqueuing the buffer another thread + // may have a allocated a new buffer and inserted it into this pointer + // queue. If that happens then we just return so that the current + // thread doesn't overwrite the buffer allocated by the other thread + // and potentially losing some dirtied cards. - if (!ignore_max_completed && _max_completed_queue > 0 && - _n_completed_buffers >= (size_t) _max_completed_queue) { - _cbl_mon->unlock(); - bool b = mut_process_buffer(buf); - if (b) { - deallocate_buffer(buf); - return; + if (_buf != NULL) return; + } else { + if (qset()->process_or_enqueue_complete_buffer(_buf)) { + // Recycle the buffer. No allocation. + _sz = qset()->buffer_size(); + _index = _sz; + return; + } } + } + // Reallocate the buffer + _buf = qset()->allocate_buffer(); + _sz = qset()->buffer_size(); + _index = _sz; + assert(0 <= _index && _index <= _sz, "Invariant."); +} - // Otherwise, go ahead and enqueue the buffer. Must reaquire the lock. - _cbl_mon->lock_without_safepoint_check(); +bool PtrQueueSet::process_or_enqueue_complete_buffer(void** buf) { + if (Thread::current()->is_Java_thread()) { + // We don't lock. It is fine to be epsilon-precise here. + if (_max_completed_queue == 0 || _max_completed_queue > 0 && + _n_completed_buffers >= _max_completed_queue + _completed_queue_padding) { + bool b = mut_process_buffer(buf); + if (b) { + // True here means that the buffer hasn't been deallocated and the caller may reuse it. + return true; + } + } } + // The buffer will be enqueued. The caller will have to get a new one. + enqueue_complete_buffer(buf); + return false; +} - // Here we still hold the _cbl_mon. - CompletedBufferNode* cbn = new CompletedBufferNode; - cbn->buf = buf; - cbn->next = NULL; - cbn->index = index; +void PtrQueueSet::enqueue_complete_buffer(void** buf, size_t index) { + MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); + BufferNode* cbn = BufferNode::new_from_buffer(buf); + cbn->set_index(index); if (_completed_buffers_tail == NULL) { assert(_completed_buffers_head == NULL, "Well-formedness"); _completed_buffers_head = cbn; _completed_buffers_tail = cbn; } else { - _completed_buffers_tail->next = cbn; + _completed_buffers_tail->set_next(cbn); _completed_buffers_tail = cbn; } _n_completed_buffers++; - if (!_process_completed && + if (!_process_completed && _process_completed_threshold >= 0 && _n_completed_buffers >= _process_completed_threshold) { _process_completed = true; if (_notify_when_complete) - _cbl_mon->notify_all(); + _cbl_mon->notify(); } debug_only(assert_completed_buffer_list_len_correct_locked()); - _cbl_mon->unlock(); } int PtrQueueSet::completed_buffers_list_length() { int n = 0; - CompletedBufferNode* cbn = _completed_buffers_head; + BufferNode* cbn = _completed_buffers_head; while (cbn != NULL) { n++; - cbn = cbn->next; + cbn = cbn->next(); } return n; } @@ -197,7 +247,7 @@ } void PtrQueueSet::assert_completed_buffer_list_len_correct_locked() { - guarantee((size_t)completed_buffers_list_length() == _n_completed_buffers, + guarantee(completed_buffers_list_length() == _n_completed_buffers, "Completed buffer length is wrong."); } @@ -206,12 +256,8 @@ _sz = sz * oopSize; } -void PtrQueueSet::set_process_completed_threshold(size_t sz) { - _process_completed_threshold = sz; -} - -// Merge lists of buffers. Notify waiting threads if the length of the list -// exceeds threshold. The source queue is emptied as a result. The queues +// Merge lists of buffers. Notify the processing threads. +// The source queue is emptied as a result. The queues // must share the monitor. void PtrQueueSet::merge_bufferlists(PtrQueueSet *src) { assert(_cbl_mon == src->_cbl_mon, "Should share the same lock"); @@ -223,7 +269,7 @@ } else { assert(_completed_buffers_head != NULL, "Well formedness"); if (src->_completed_buffers_head != NULL) { - _completed_buffers_tail->next = src->_completed_buffers_head; + _completed_buffers_tail->set_next(src->_completed_buffers_head); _completed_buffers_tail = src->_completed_buffers_tail; } } @@ -236,31 +282,13 @@ assert(_completed_buffers_head == NULL && _completed_buffers_tail == NULL || _completed_buffers_head != NULL && _completed_buffers_tail != NULL, "Sanity"); - - if (!_process_completed && - _n_completed_buffers >= _process_completed_threshold) { - _process_completed = true; - if (_notify_when_complete) - _cbl_mon->notify_all(); - } } -// Merge free lists of the two queues. The free list of the source -// queue is emptied as a result. The queues must share the same -// mutex that guards free lists. -void PtrQueueSet::merge_freelists(PtrQueueSet* src) { - assert(_fl_lock == src->_fl_lock, "Should share the same lock"); - MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag); - if (_buf_free_list != NULL) { - void **p = _buf_free_list; - while (*p != NULL) { - p = (void**)*p; - } - *p = src->_buf_free_list; - } else { - _buf_free_list = src->_buf_free_list; +void PtrQueueSet::notify_if_necessary() { + MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); + if (_n_completed_buffers >= _process_completed_threshold || _max_completed_queue == 0) { + _process_completed = true; + if (_notify_when_complete) + _cbl_mon->notify(); } - _buf_free_list_sz += src->_buf_free_list_sz; - src->_buf_free_list = NULL; - src->_buf_free_list_sz = 0; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/ptrQueue.hpp --- a/src/share/vm/gc_implementation/g1/ptrQueue.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/ptrQueue.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -27,8 +27,10 @@ // the addresses of modified old-generation objects. This type supports // this operation. +// The definition of placement operator new(size_t, void*) in the . +#include + class PtrQueueSet; - class PtrQueue VALUE_OBJ_CLASS_SPEC { protected: @@ -77,7 +79,7 @@ else enqueue_known_active(ptr); } - inline void handle_zero_index(); + void handle_zero_index(); void locking_enqueue_completed_buffer(void** buf); void enqueue_known_active(void* ptr); @@ -126,34 +128,65 @@ }; +class BufferNode { + size_t _index; + BufferNode* _next; +public: + BufferNode() : _index(0), _next(NULL) { } + BufferNode* next() const { return _next; } + void set_next(BufferNode* n) { _next = n; } + size_t index() const { return _index; } + void set_index(size_t i) { _index = i; } + + // Align the size of the structure to the size of the pointer + static size_t aligned_size() { + static const size_t alignment = round_to(sizeof(BufferNode), sizeof(void*)); + return alignment; + } + + // BufferNode is allocated before the buffer. + // The chunk of memory that holds both of them is a block. + + // Produce a new BufferNode given a buffer. + static BufferNode* new_from_buffer(void** buf) { + return new (make_block_from_buffer(buf)) BufferNode; + } + + // The following are the required conversion routines: + static BufferNode* make_node_from_buffer(void** buf) { + return (BufferNode*)make_block_from_buffer(buf); + } + static void** make_buffer_from_node(BufferNode *node) { + return make_buffer_from_block(node); + } + static void* make_block_from_node(BufferNode *node) { + return (void*)node; + } + static void** make_buffer_from_block(void* p) { + return (void**)((char*)p + aligned_size()); + } + static void* make_block_from_buffer(void** p) { + return (void*)((char*)p - aligned_size()); + } +}; + // A PtrQueueSet represents resources common to a set of pointer queues. // In particular, the individual queues allocate buffers from this shared // set, and return completed buffers to the set. // All these variables are are protected by the TLOQ_CBL_mon. XXX ??? class PtrQueueSet VALUE_OBJ_CLASS_SPEC { - protected: - - class CompletedBufferNode: public CHeapObj { - public: - void** buf; - size_t index; - CompletedBufferNode* next; - CompletedBufferNode() : buf(NULL), - index(0), next(NULL){ } - }; - Monitor* _cbl_mon; // Protects the fields below. - CompletedBufferNode* _completed_buffers_head; - CompletedBufferNode* _completed_buffers_tail; - size_t _n_completed_buffers; - size_t _process_completed_threshold; + BufferNode* _completed_buffers_head; + BufferNode* _completed_buffers_tail; + int _n_completed_buffers; + int _process_completed_threshold; volatile bool _process_completed; // This (and the interpretation of the first element as a "next" // pointer) are protected by the TLOQ_FL_lock. Mutex* _fl_lock; - void** _buf_free_list; + BufferNode* _buf_free_list; size_t _buf_free_list_sz; // Queue set can share a freelist. The _fl_owner variable // specifies the owner. It is set to "this" by default. @@ -170,6 +203,7 @@ // Maximum number of elements allowed on completed queue: after that, // enqueuer does the work itself. Zero indicates no maximum. int _max_completed_queue; + int _completed_queue_padding; int completed_buffers_list_length(); void assert_completed_buffer_list_len_correct_locked(); @@ -191,9 +225,12 @@ // Because of init-order concerns, we can't pass these as constructor // arguments. void initialize(Monitor* cbl_mon, Mutex* fl_lock, - int max_completed_queue = 0, + int process_completed_threshold, + int max_completed_queue, PtrQueueSet *fl_owner = NULL) { _max_completed_queue = max_completed_queue; + _process_completed_threshold = process_completed_threshold; + _completed_queue_padding = 0; assert(cbl_mon != NULL && fl_lock != NULL, "Init order issue?"); _cbl_mon = cbl_mon; _fl_lock = fl_lock; @@ -208,14 +245,17 @@ void deallocate_buffer(void** buf); // Declares that "buf" is a complete buffer. - void enqueue_complete_buffer(void** buf, size_t index = 0, - bool ignore_max_completed = false); + void enqueue_complete_buffer(void** buf, size_t index = 0); + + // To be invoked by the mutator. + bool process_or_enqueue_complete_buffer(void** buf); bool completed_buffers_exist_dirty() { return _n_completed_buffers > 0; } bool process_completed_buffers() { return _process_completed; } + void set_process_completed(bool x) { _process_completed = x; } bool active() { return _all_active; } @@ -226,15 +266,24 @@ // Get the buffer size. size_t buffer_size() { return _sz; } - // Set the number of completed buffers that triggers log processing. - void set_process_completed_threshold(size_t sz); + // Get/Set the number of completed buffers that triggers log processing. + void set_process_completed_threshold(int sz) { _process_completed_threshold = sz; } + int process_completed_threshold() const { return _process_completed_threshold; } // Must only be called at a safe point. Indicates that the buffer free // list size may be reduced, if that is deemed desirable. void reduce_free_list(); - size_t completed_buffers_num() { return _n_completed_buffers; } + int completed_buffers_num() { return _n_completed_buffers; } void merge_bufferlists(PtrQueueSet* src); - void merge_freelists(PtrQueueSet* src); + + void set_max_completed_queue(int m) { _max_completed_queue = m; } + int max_completed_queue() { return _max_completed_queue; } + + void set_completed_queue_padding(int padding) { _completed_queue_padding = padding; } + int completed_queue_padding() { return _completed_queue_padding; } + + // Notify the consumer if the number of buffers crossed the threshold + void notify_if_necessary(); }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/ptrQueue.inline.hpp --- a/src/share/vm/gc_implementation/g1/ptrQueue.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * Copyright 2001-2007 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -void PtrQueue::handle_zero_index() { - assert(0 == _index, "Precondition."); - // This thread records the full buffer and allocates a new one (while - // holding the lock if there is one). - void** buf = _buf; - _buf = qset()->allocate_buffer(); - _sz = qset()->buffer_size(); - _index = _sz; - assert(0 <= _index && _index <= _sz, "Invariant."); - if (buf != NULL) { - if (_lock) { - locking_enqueue_completed_buffer(buf); - } else { - qset()->enqueue_complete_buffer(buf); - } - } -} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/satbQueue.cpp --- a/src/share/vm/gc_implementation/g1/satbQueue.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/satbQueue.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -67,9 +67,9 @@ {} void SATBMarkQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock, - int max_completed_queue, + int process_completed_threshold, Mutex* lock) { - PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue); + PtrQueueSet::initialize(cbl_mon, fl_lock, process_completed_threshold, -1); _shared_satb_queue.set_lock(lock); if (ParallelGCThreads > 0) { _par_closures = NEW_C_HEAP_ARRAY(ObjectClosure*, ParallelGCThreads); @@ -122,12 +122,12 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer_work(bool par, int worker) { - CompletedBufferNode* nd = NULL; + BufferNode* nd = NULL; { MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); if (_completed_buffers_head != NULL) { nd = _completed_buffers_head; - _completed_buffers_head = nd->next; + _completed_buffers_head = nd->next(); if (_completed_buffers_head == NULL) _completed_buffers_tail = NULL; _n_completed_buffers--; if (_n_completed_buffers == 0) _process_completed = false; @@ -135,9 +135,9 @@ } ObjectClosure* cl = (par ? _par_closures[worker] : _closure); if (nd != NULL) { - ObjPtrQueue::apply_closure_to_buffer(cl, nd->buf, 0, _sz); - deallocate_buffer(nd->buf); - delete nd; + void **buf = BufferNode::make_buffer_from_node(nd); + ObjPtrQueue::apply_closure_to_buffer(cl, buf, 0, _sz); + deallocate_buffer(buf); return true; } else { return false; @@ -145,13 +145,13 @@ } void SATBMarkQueueSet::abandon_partial_marking() { - CompletedBufferNode* buffers_to_delete = NULL; + BufferNode* buffers_to_delete = NULL; { MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); while (_completed_buffers_head != NULL) { - CompletedBufferNode* nd = _completed_buffers_head; - _completed_buffers_head = nd->next; - nd->next = buffers_to_delete; + BufferNode* nd = _completed_buffers_head; + _completed_buffers_head = nd->next(); + nd->set_next(buffers_to_delete); buffers_to_delete = nd; } _completed_buffers_tail = NULL; @@ -159,10 +159,9 @@ DEBUG_ONLY(assert_completed_buffer_list_len_correct_locked()); } while (buffers_to_delete != NULL) { - CompletedBufferNode* nd = buffers_to_delete; - buffers_to_delete = nd->next; - deallocate_buffer(nd->buf); - delete nd; + BufferNode* nd = buffers_to_delete; + buffers_to_delete = nd->next(); + deallocate_buffer(BufferNode::make_buffer_from_node(nd)); } assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); // So we can safely manipulate these queues. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/satbQueue.hpp --- a/src/share/vm/gc_implementation/g1/satbQueue.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/satbQueue.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -60,8 +60,8 @@ SATBMarkQueueSet(); void initialize(Monitor* cbl_mon, Mutex* fl_lock, - int max_completed_queue = 0, - Mutex* lock = NULL); + int process_completed_threshold, + Mutex* lock); static void handle_zero_index_for_thread(JavaThread* t); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/sparsePRT.cpp --- a/src/share/vm/gc_implementation/g1/sparsePRT.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/sparsePRT.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -27,7 +27,7 @@ #define SPARSE_PRT_VERBOSE 0 -#define UNROLL_CARD_LOOPS 1 +#define UNROLL_CARD_LOOPS 1 void SparsePRT::init_iterator(SparsePRTIter* sprt_iter) { sprt_iter->init(this); @@ -36,27 +36,32 @@ void SparsePRTEntry::init(RegionIdx_t region_ind) { _region_ind = region_ind; _next_index = NullEntry; + #if UNROLL_CARD_LOOPS - assert(CardsPerEntry == 4, "Assumption. If changes, un-unroll."); - _cards[0] = NullEntry; - _cards[1] = NullEntry; - _cards[2] = NullEntry; - _cards[3] = NullEntry; + assert((cards_num() & (UnrollFactor - 1)) == 0, "Invalid number of cards in the entry"); + for (int i = 0; i < cards_num(); i += UnrollFactor) { + _cards[i] = NullEntry; + _cards[i + 1] = NullEntry; + _cards[i + 2] = NullEntry; + _cards[i + 3] = NullEntry; + } #else - for (int i = 0; i < CardsPerEntry; i++) + for (int i = 0; i < cards_num(); i++) _cards[i] = NullEntry; #endif } bool SparsePRTEntry::contains_card(CardIdx_t card_index) const { #if UNROLL_CARD_LOOPS - assert(CardsPerEntry == 4, "Assumption. If changes, un-unroll."); - if (_cards[0] == card_index) return true; - if (_cards[1] == card_index) return true; - if (_cards[2] == card_index) return true; - if (_cards[3] == card_index) return true; + assert((cards_num() & (UnrollFactor - 1)) == 0, "Invalid number of cards in the entry"); + for (int i = 0; i < cards_num(); i += UnrollFactor) { + if (_cards[i] == card_index || + _cards[i + 1] == card_index || + _cards[i + 2] == card_index || + _cards[i + 3] == card_index) return true; + } #else - for (int i = 0; i < CardsPerEntry; i++) { + for (int i = 0; i < cards_num(); i++) { if (_cards[i] == card_index) return true; } #endif @@ -67,14 +72,16 @@ int SparsePRTEntry::num_valid_cards() const { int sum = 0; #if UNROLL_CARD_LOOPS - assert(CardsPerEntry == 4, "Assumption. If changes, un-unroll."); - if (_cards[0] != NullEntry) sum++; - if (_cards[1] != NullEntry) sum++; - if (_cards[2] != NullEntry) sum++; - if (_cards[3] != NullEntry) sum++; + assert((cards_num() & (UnrollFactor - 1)) == 0, "Invalid number of cards in the entry"); + for (int i = 0; i < cards_num(); i += UnrollFactor) { + sum += (_cards[i] != NullEntry); + sum += (_cards[i + 1] != NullEntry); + sum += (_cards[i + 2] != NullEntry); + sum += (_cards[i + 3] != NullEntry); + } #else - for (int i = 0; i < CardsPerEntry; i++) { - if (_cards[i] != NulLEntry) sum++; + for (int i = 0; i < cards_num(); i++) { + sum += (_cards[i] != NullEntry); } #endif // Otherwise, we're full. @@ -83,27 +90,27 @@ SparsePRTEntry::AddCardResult SparsePRTEntry::add_card(CardIdx_t card_index) { #if UNROLL_CARD_LOOPS - assert(CardsPerEntry == 4, "Assumption. If changes, un-unroll."); - CardIdx_t c = _cards[0]; - if (c == card_index) return found; - if (c == NullEntry) { _cards[0] = card_index; return added; } - c = _cards[1]; - if (c == card_index) return found; - if (c == NullEntry) { _cards[1] = card_index; return added; } - c = _cards[2]; - if (c == card_index) return found; - if (c == NullEntry) { _cards[2] = card_index; return added; } - c = _cards[3]; - if (c == card_index) return found; - if (c == NullEntry) { _cards[3] = card_index; return added; } + assert((cards_num() & (UnrollFactor - 1)) == 0, "Invalid number of cards in the entry"); + CardIdx_t c; + for (int i = 0; i < cards_num(); i += UnrollFactor) { + c = _cards[i]; + if (c == card_index) return found; + if (c == NullEntry) { _cards[i] = card_index; return added; } + c = _cards[i + 1]; + if (c == card_index) return found; + if (c == NullEntry) { _cards[i + 1] = card_index; return added; } + c = _cards[i + 2]; + if (c == card_index) return found; + if (c == NullEntry) { _cards[i + 2] = card_index; return added; } + c = _cards[i + 3]; + if (c == card_index) return found; + if (c == NullEntry) { _cards[i + 3] = card_index; return added; } + } #else - for (int i = 0; i < CardsPerEntry; i++) { + for (int i = 0; i < cards_num(); i++) { CardIdx_t c = _cards[i]; if (c == card_index) return found; - if (c == NullEntry) { - _cards[i] = card_index; - return added; - } + if (c == NullEntry) { _cards[i] = card_index; return added; } } #endif // Otherwise, we're full. @@ -112,13 +119,15 @@ void SparsePRTEntry::copy_cards(CardIdx_t* cards) const { #if UNROLL_CARD_LOOPS - assert(CardsPerEntry == 4, "Assumption. If changes, un-unroll."); - cards[0] = _cards[0]; - cards[1] = _cards[1]; - cards[2] = _cards[2]; - cards[3] = _cards[3]; + assert((cards_num() & (UnrollFactor - 1)) == 0, "Invalid number of cards in the entry"); + for (int i = 0; i < cards_num(); i += UnrollFactor) { + cards[i] = _cards[i]; + cards[i + 1] = _cards[i + 1]; + cards[i + 2] = _cards[i + 2]; + cards[i + 3] = _cards[i + 3]; + } #else - for (int i = 0; i < CardsPerEntry; i++) { + for (int i = 0; i < cards_num(); i++) { cards[i] = _cards[i]; } #endif @@ -133,9 +142,8 @@ RSHashTable::RSHashTable(size_t capacity) : _capacity(capacity), _capacity_mask(capacity-1), _occupied_entries(0), _occupied_cards(0), - _entries(NEW_C_HEAP_ARRAY(SparsePRTEntry, capacity)), + _entries((SparsePRTEntry*)NEW_C_HEAP_ARRAY(char, SparsePRTEntry::size() * capacity)), _buckets(NEW_C_HEAP_ARRAY(int, capacity)), - _next_deleted(NULL), _deleted(false), _free_list(NullEntry), _free_region(0) { clear(); @@ -162,8 +170,8 @@ "_capacity too large"); // This will put -1 == NullEntry in the key field of all entries. - memset(_entries, -1, _capacity * sizeof(SparsePRTEntry)); - memset(_buckets, -1, _capacity * sizeof(int)); + memset(_entries, NullEntry, _capacity * SparsePRTEntry::size()); + memset(_buckets, NullEntry, _capacity * sizeof(int)); _free_list = NullEntry; _free_region = 0; } @@ -176,8 +184,8 @@ if (res == SparsePRTEntry::added) _occupied_cards++; #if SPARSE_PRT_VERBOSE gclog_or_tty->print_cr(" after add_card[%d]: valid-cards = %d.", - pointer_delta(e, _entries, sizeof(SparsePRTEntry)), - e->num_valid_cards()); + pointer_delta(e, _entries, SparsePRTEntry::size()), + e->num_valid_cards()); #endif assert(e->num_valid_cards() > 0, "Postcondition"); return res != SparsePRTEntry::overflow; @@ -200,6 +208,22 @@ return true; } +SparsePRTEntry* RSHashTable::get_entry(RegionIdx_t region_ind) { + int ind = (int) (region_ind & capacity_mask()); + int cur_ind = _buckets[ind]; + SparsePRTEntry* cur; + while (cur_ind != NullEntry && + (cur = entry(cur_ind))->r_ind() != region_ind) { + cur_ind = cur->next_index(); + } + + if (cur_ind == NullEntry) return NULL; + // Otherwise... + assert(cur->r_ind() == region_ind, "Postcondition of loop + test above."); + assert(cur->num_valid_cards() > 0, "Inv"); + return cur; +} + bool RSHashTable::delete_entry(RegionIdx_t region_ind) { int ind = (int) (region_ind & capacity_mask()); int* prev_loc = &_buckets[ind]; @@ -226,20 +250,8 @@ int ind = (int) (region_ind & capacity_mask()); int cur_ind = _buckets[ind]; SparsePRTEntry* cur; - // XXX - // int k = 0; while (cur_ind != NullEntry && (cur = entry(cur_ind))->r_ind() != region_ind) { - /* - k++; - if (k > 10) { - gclog_or_tty->print_cr("RSHashTable::entry_for_region_ind(%d): " - "k = %d, cur_ind = %d.", region_ind, k, cur_ind); - if (k >= 1000) { - while (1) ; - } - } - */ cur_ind = cur->next_index(); } @@ -296,40 +308,6 @@ assert(e2->num_valid_cards() > 0, "Postcondition."); } -RSHashTable* RSHashTable::_head_deleted_list = NULL; - -void RSHashTable::add_to_deleted_list(RSHashTable* rsht) { - assert(!rsht->deleted(), "Should delete only once."); - rsht->set_deleted(true); - RSHashTable* hd = _head_deleted_list; - while (true) { - rsht->_next_deleted = hd; - RSHashTable* res = - (RSHashTable*) - Atomic::cmpxchg_ptr(rsht, &_head_deleted_list, hd); - if (res == hd) return; - else hd = res; - } -} - -RSHashTable* RSHashTable::get_from_deleted_list() { - RSHashTable* hd = _head_deleted_list; - while (hd != NULL) { - RSHashTable* next = hd->next_deleted(); - RSHashTable* res = - (RSHashTable*) - Atomic::cmpxchg_ptr(next, &_head_deleted_list, hd); - if (res == hd) { - hd->set_next_deleted(NULL); - hd->set_deleted(false); - return hd; - } else { - hd = res; - } - } - return NULL; -} - CardIdx_t /* RSHashTable:: */ RSHashTableIter::find_first_card_in_list() { CardIdx_t res; while (_bl_ind != RSHashTable::NullEntry) { @@ -347,14 +325,14 @@ size_t /* RSHashTable:: */ RSHashTableIter::compute_card_ind(CardIdx_t ci) { return _heap_bot_card_ind - + (_rsht->entry(_bl_ind)->r_ind() * CardsPerRegion) + + (_rsht->entry(_bl_ind)->r_ind() * HeapRegion::CardsPerRegion) + ci; } bool /* RSHashTable:: */ RSHashTableIter::has_next(size_t& card_index) { _card_ind++; CardIdx_t ci; - if (_card_ind < SparsePRTEntry::CardsPerEntry && + if (_card_ind < SparsePRTEntry::cards_num() && ((ci = _rsht->entry(_bl_ind)->card(_card_ind)) != SparsePRTEntry::NullEntry)) { card_index = compute_card_ind(ci); @@ -394,7 +372,7 @@ size_t RSHashTable::mem_size() const { return sizeof(this) + - capacity() * (sizeof(SparsePRTEntry) + sizeof(int)); + capacity() * (SparsePRTEntry::size() + sizeof(int)); } // ---------------------------------------------------------------------- @@ -442,15 +420,6 @@ sprt->cleanup(); sprt = get_from_expanded_list(); } - // Now delete all deleted RSHashTables. - RSHashTable* rsht = RSHashTable::get_from_deleted_list(); - while (rsht != NULL) { -#if SPARSE_PRT_VERBOSE - gclog_or_tty->print_cr("About to delete RSHT " PTR_FORMAT ".", rsht); -#endif - delete rsht; - rsht = RSHashTable::get_from_deleted_list(); - } } @@ -490,6 +459,10 @@ return _next->get_cards(region_id, cards); } +SparsePRTEntry* SparsePRT::get_entry(RegionIdx_t region_id) { + return _next->get_entry(region_id); +} + bool SparsePRT::delete_entry(RegionIdx_t region_id) { return _next->delete_entry(region_id); } @@ -511,8 +484,10 @@ } void SparsePRT::cleanup() { - // Make sure that the current and next tables agree. (Another mechanism - // takes care of deleting now-unused tables.) + // Make sure that the current and next tables agree. + if (_cur != _next) { + delete _cur; + } _cur = _next; set_expanded(false); } @@ -535,7 +510,8 @@ _next->add_entry(e); } } - if (last != _cur) - RSHashTable::add_to_deleted_list(last); + if (last != _cur) { + delete last; + } add_to_expanded_list(this); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/sparsePRT.hpp --- a/src/share/vm/gc_implementation/g1/sparsePRT.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/sparsePRT.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -32,21 +32,28 @@ // insertions only enqueue old versions for deletions, but do not delete // old versions synchronously. - class SparsePRTEntry: public CHeapObj { public: - enum SomePublicConstants { - CardsPerEntry = 4, - NullEntry = -1 + NullEntry = -1, + UnrollFactor = 4 }; - private: RegionIdx_t _region_ind; int _next_index; - CardIdx_t _cards[CardsPerEntry]; - + CardIdx_t _cards[1]; + // WARNING: Don't put any data members beyond this line. Card array has, in fact, variable length. + // It should always be the last data member. public: + // Returns the size of the entry, used for entry allocation. + static size_t size() { return sizeof(SparsePRTEntry) + sizeof(CardIdx_t) * (cards_num() - 1); } + // Returns the size of the card array. + static int cards_num() { + // The number of cards should be a multiple of 4, because that's our current + // unrolling factor. + static const int s = MAX2(G1RSetSparseRegionEntries & ~(UnrollFactor - 1), UnrollFactor); + return s; + } // Set the region_ind to the given value, and delete all cards. inline void init(RegionIdx_t region_ind); @@ -102,13 +109,6 @@ int _free_region; int _free_list; - static RSHashTable* _head_deleted_list; - RSHashTable* _next_deleted; - RSHashTable* next_deleted() { return _next_deleted; } - void set_next_deleted(RSHashTable* rsht) { _next_deleted = rsht; } - bool _deleted; - void set_deleted(bool b) { _deleted = b; } - // Requires that the caller hold a lock preventing parallel modifying // operations, and that the the table be less than completely full. If // an entry for "region_ind" is already in the table, finds it and @@ -141,12 +141,15 @@ bool add_card(RegionIdx_t region_id, CardIdx_t card_index); bool get_cards(RegionIdx_t region_id, CardIdx_t* cards); + bool delete_entry(RegionIdx_t region_id); bool contains_card(RegionIdx_t region_id, CardIdx_t card_index) const; void add_entry(SparsePRTEntry* e); + SparsePRTEntry* get_entry(RegionIdx_t region_id); + void clear(); size_t capacity() const { return _capacity; } @@ -154,28 +157,20 @@ size_t occupied_entries() const { return _occupied_entries; } size_t occupied_cards() const { return _occupied_cards; } size_t mem_size() const; - bool deleted() { return _deleted; } - SparsePRTEntry* entry(int i) const { return &_entries[i]; } + SparsePRTEntry* entry(int i) const { return (SparsePRTEntry*)((char*)_entries + SparsePRTEntry::size() * i); } void print(); - - static void add_to_deleted_list(RSHashTable* rsht); - static RSHashTable* get_from_deleted_list(); }; // ValueObj because will be embedded in HRRS iterator. class RSHashTableIter VALUE_OBJ_CLASS_SPEC { int _tbl_ind; // [-1, 0.._rsht->_capacity) int _bl_ind; // [-1, 0.._rsht->_capacity) - short _card_ind; // [0..CardsPerEntry) + short _card_ind; // [0..SparsePRTEntry::cards_num()) RSHashTable* _rsht; size_t _heap_bot_card_ind; - enum SomePrivateConstants { - CardsPerRegion = HeapRegion::GrainBytes >> CardTableModRefBS::card_shift - }; - // If the bucket list pointed to by _bl_ind contains a card, sets // _bl_ind to the index of that entry, and returns the card. // Otherwise, returns SparseEntry::NullEntry. @@ -191,7 +186,7 @@ RSHashTableIter(size_t heap_bot_card_ind) : _tbl_ind(RSHashTable::NullEntry), _bl_ind(RSHashTable::NullEntry), - _card_ind((SparsePRTEntry::CardsPerEntry-1)), + _card_ind((SparsePRTEntry::cards_num() - 1)), _rsht(NULL), _heap_bot_card_ind(heap_bot_card_ind) {} @@ -200,7 +195,7 @@ _rsht = rsht; _tbl_ind = -1; // So that first increment gets to 0. _bl_ind = RSHashTable::NullEntry; - _card_ind = (SparsePRTEntry::CardsPerEntry-1); + _card_ind = (SparsePRTEntry::cards_num() - 1); } bool has_next(size_t& card_index); @@ -256,9 +251,13 @@ // If the table hold an entry for "region_ind", Copies its // cards into "cards", which must be an array of length at least - // "CardsPerEntry", and returns "true"; otherwise, returns "false". + // "SparePRTEntry::cards_num()", and returns "true"; otherwise, + // returns "false". bool get_cards(RegionIdx_t region_ind, CardIdx_t* cards); + // Return the pointer to the entry associated with the given region. + SparsePRTEntry* get_entry(RegionIdx_t region_ind); + // If there is an entry for "region_ind", removes it and return "true"; // otherwise returns "false." bool delete_entry(RegionIdx_t region_ind); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/vm_operations_g1.cpp --- a/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -42,7 +42,7 @@ void VM_G1IncCollectionPause::doit() { JvmtiGCForAllocationMarker jgcm; G1CollectedHeap* g1h = G1CollectedHeap::heap(); - GCCauseSetter x(g1h, GCCause::_g1_inc_collection_pause); + GCCauseSetter x(g1h, _gc_cause); g1h->do_collection_pause_at_safepoint(); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/g1/vm_operations_g1.hpp --- a/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -68,8 +68,9 @@ class VM_G1IncCollectionPause: public VM_GC_Operation { public: - VM_G1IncCollectionPause(int gc_count_before) : - VM_GC_Operation(gc_count_before) {} + VM_G1IncCollectionPause(int gc_count_before, + GCCause::Cause gc_cause = GCCause::_g1_inc_collection_pause) : + VM_GC_Operation(gc_count_before) { _gc_cause = gc_cause; } virtual VMOp_Type type() const { return VMOp_G1IncCollectionPause; } virtual void doit(); virtual const char* name() const { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep --- a/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep Thu Mar 25 16:54:59 2010 -0700 @@ -145,6 +145,7 @@ concurrentMarkSweepGeneration.cpp globals_extension.hpp concurrentMarkSweepGeneration.cpp handles.inline.hpp concurrentMarkSweepGeneration.cpp isGCActiveMark.hpp +concurrentMarkSweepGeneration.cpp iterator.hpp concurrentMarkSweepGeneration.cpp java.hpp concurrentMarkSweepGeneration.cpp jvmtiExport.hpp concurrentMarkSweepGeneration.cpp oop.inline.hpp @@ -220,6 +221,7 @@ freeList.cpp globals.hpp freeList.cpp mutex.hpp freeList.cpp sharedHeap.hpp +freeList.cpp vmThread.hpp freeList.hpp allocationStats.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/includeDB_gc_g1 --- a/src/share/vm/gc_implementation/includeDB_gc_g1 Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_g1 Thu Mar 25 16:54:59 2010 -0700 @@ -64,14 +64,12 @@ concurrentG1RefineThread.cpp resourceArea.hpp concurrentG1RefineThread.hpp concurrentGCThread.hpp -concurrentG1RefineThread.hpp coTracker.hpp concurrentMark.cpp concurrentMark.hpp concurrentMark.cpp concurrentMarkThread.inline.hpp concurrentMark.cpp g1CollectedHeap.inline.hpp concurrentMark.cpp g1CollectorPolicy.hpp concurrentMark.cpp g1RemSet.hpp -concurrentMark.cpp gcOverheadReporter.hpp concurrentMark.cpp genOopClosures.inline.hpp concurrentMark.cpp heapRegionRemSet.hpp concurrentMark.cpp heapRegionSeq.inline.hpp @@ -82,7 +80,6 @@ concurrentMark.cpp resourceArea.hpp concurrentMark.cpp symbolTable.hpp -concurrentMark.hpp coTracker.hpp concurrentMark.hpp heapRegion.hpp concurrentMark.hpp taskqueue.hpp @@ -107,13 +104,11 @@ concurrentZFThread.cpp space.inline.hpp concurrentZFThread.hpp concurrentGCThread.hpp -concurrentZFThread.hpp coTracker.hpp dirtyCardQueue.cpp atomic.hpp dirtyCardQueue.cpp dirtyCardQueue.hpp dirtyCardQueue.cpp heapRegionRemSet.hpp dirtyCardQueue.cpp mutexLocker.hpp -dirtyCardQueue.cpp ptrQueue.inline.hpp dirtyCardQueue.cpp safepoint.hpp dirtyCardQueue.cpp thread.hpp dirtyCardQueue.cpp thread_.inline.hpp @@ -147,7 +142,6 @@ g1CollectedHeap.cpp g1OopClosures.inline.hpp g1CollectedHeap.cpp genOopClosures.inline.hpp g1CollectedHeap.cpp gcLocker.inline.hpp -g1CollectedHeap.cpp gcOverheadReporter.hpp g1CollectedHeap.cpp generationSpec.hpp g1CollectedHeap.cpp heapRegionRemSet.hpp g1CollectedHeap.cpp heapRegionSeq.inline.hpp @@ -170,6 +164,7 @@ g1CollectedHeap.inline.hpp heapRegionSeq.hpp g1CollectedHeap.inline.hpp taskqueue.hpp +g1CollectorPolicy.cpp arguments.hpp g1CollectorPolicy.cpp concurrentG1Refine.hpp g1CollectorPolicy.cpp concurrentMark.hpp g1CollectorPolicy.cpp concurrentMarkThread.inline.hpp @@ -226,6 +221,15 @@ g1MarkSweep.hpp timer.hpp g1MarkSweep.hpp universe.hpp +g1MemoryPool.cpp heapRegion.hpp +g1MemoryPool.cpp g1CollectedHeap.inline.hpp +g1MemoryPool.cpp g1CollectedHeap.hpp +g1MemoryPool.cpp g1CollectorPolicy.hpp +g1MemoryPool.cpp g1MemoryPool.hpp + +g1MemoryPool.hpp memoryUsage.hpp +g1MemoryPool.hpp memoryPool.hpp + g1OopClosures.inline.hpp concurrentMark.hpp g1OopClosures.inline.hpp g1OopClosures.hpp g1OopClosures.inline.hpp g1CollectedHeap.hpp @@ -307,12 +311,13 @@ klass.hpp g1OopClosures.hpp +memoryService.cpp g1MemoryPool.hpp + ptrQueue.cpp allocation.hpp ptrQueue.cpp allocation.inline.hpp ptrQueue.cpp mutex.hpp ptrQueue.cpp mutexLocker.hpp ptrQueue.cpp ptrQueue.hpp -ptrQueue.cpp ptrQueue.inline.hpp ptrQueue.cpp thread_.inline.hpp ptrQueue.hpp allocation.hpp @@ -322,7 +327,6 @@ satbQueue.cpp allocation.inline.hpp satbQueue.cpp mutexLocker.hpp -satbQueue.cpp ptrQueue.inline.hpp satbQueue.cpp satbQueue.hpp satbQueue.cpp sharedHeap.hpp satbQueue.cpp thread.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/includeDB_gc_parallelScavenge --- a/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Thu Mar 25 16:54:59 2010 -0700 @@ -253,10 +253,11 @@ psParallelCompact.cpp gcLocker.inline.hpp psParallelCompact.cpp gcTaskManager.hpp psParallelCompact.cpp isGCActiveMark.hpp +psParallelCompact.cpp management.hpp +psParallelCompact.cpp memoryService.hpp +psParallelCompact.cpp methodDataOop.hpp psParallelCompact.cpp oop.inline.hpp psParallelCompact.cpp oop.pcgc.inline.hpp -psParallelCompact.cpp memoryService.hpp -psParallelCompact.cpp management.hpp psParallelCompact.cpp parallelScavengeHeap.inline.hpp psParallelCompact.cpp pcTasks.hpp psParallelCompact.cpp psMarkSweep.hpp @@ -372,6 +373,7 @@ psScavenge.inline.hpp psPromotionManager.hpp psScavenge.inline.hpp psScavenge.hpp +pcTasks.cpp codeCache.hpp pcTasks.cpp collectedHeap.hpp pcTasks.cpp fprofiler.hpp pcTasks.cpp jniHandles.hpp @@ -391,6 +393,7 @@ pcTasks.hpp psTasks.hpp psTasks.cpp cardTableExtension.hpp +psTasks.cpp codeCache.hpp psTasks.cpp fprofiler.hpp psTasks.cpp gcTaskManager.hpp psTasks.cpp iterator.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/includeDB_gc_serial --- a/src/share/vm/gc_implementation/includeDB_gc_serial Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_serial Thu Mar 25 16:54:59 2010 -0700 @@ -71,6 +71,7 @@ gcUtil.hpp allocation.hpp gcUtil.hpp debug.hpp gcUtil.hpp globalDefinitions.hpp +gcUtil.hpp ostream.hpp gcUtil.hpp timer.hpp generationCounters.cpp generationCounters.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/includeDB_gc_shared --- a/src/share/vm/gc_implementation/includeDB_gc_shared Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_shared Thu Mar 25 16:54:59 2010 -0700 @@ -35,12 +35,6 @@ concurrentGCThread.hpp thread.hpp -coTracker.hpp globalDefinitions.hpp -coTracker.hpp numberSeq.hpp - -coTracker.cpp coTracker.hpp -coTracker.cpp os.hpp - allocationStats.cpp allocationStats.hpp allocationStats.cpp ostream.hpp @@ -54,13 +48,6 @@ gcAdaptivePolicyCounters.cpp resourceArea.hpp gcAdaptivePolicyCounters.cpp gcAdaptivePolicyCounters.hpp -gcOverheadReporter.cpp allocation.inline.hpp -gcOverheadReporter.cpp concurrentGCThread.hpp -gcOverheadReporter.cpp coTracker.hpp -gcOverheadReporter.cpp gcOverheadReporter.hpp -gcOverheadReporter.cpp ostream.hpp -gcOverheadReporter.cpp thread_.inline.hpp - gSpaceCounters.cpp generation.hpp gSpaceCounters.cpp resourceArea.hpp gSpaceCounters.cpp gSpaceCounters.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parNew/parNewGeneration.cpp --- a/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -50,6 +50,7 @@ work_queue_set_, &term_), _is_alive_closure(gen_), _scan_weak_ref_closure(gen_, this), _keep_alive_closure(&_scan_weak_ref_closure), + _promotion_failure_size(0), _pushes(0), _pops(0), _steals(0), _steal_attempts(0), _term_attempts(0), _strong_roots_time(0.0), _term_time(0.0) { @@ -249,6 +250,16 @@ } } +void ParScanThreadState::print_and_clear_promotion_failure_size() { + if (_promotion_failure_size != 0) { + if (PrintPromotionFailure) { + gclog_or_tty->print(" (%d: promotion failure size = " SIZE_FORMAT ") ", + _thread_num, _promotion_failure_size); + } + _promotion_failure_size = 0; + } +} + class ParScanThreadStateSet: private ResourceArray { public: // Initializes states for the specified number of threads; @@ -260,11 +271,11 @@ GrowableArray** overflow_stacks_, size_t desired_plab_sz, ParallelTaskTerminator& term); - inline ParScanThreadState& thread_sate(int i); + inline ParScanThreadState& thread_state(int i); int pushes() { return _pushes; } int pops() { return _pops; } int steals() { return _steals; } - void reset(); + void reset(bool promotion_failed); void flush(); private: ParallelTaskTerminator& _term; @@ -295,22 +306,31 @@ } } -inline ParScanThreadState& ParScanThreadStateSet::thread_sate(int i) +inline ParScanThreadState& ParScanThreadStateSet::thread_state(int i) { assert(i >= 0 && i < length(), "sanity check!"); return ((ParScanThreadState*)_data)[i]; } -void ParScanThreadStateSet::reset() +void ParScanThreadStateSet::reset(bool promotion_failed) { _term.reset_for_reuse(); + if (promotion_failed) { + for (int i = 0; i < length(); ++i) { + thread_state(i).print_and_clear_promotion_failure_size(); + } + } } void ParScanThreadStateSet::flush() { + // Work in this loop should be kept as lightweight as + // possible since this might otherwise become a bottleneck + // to scaling. Should we add heavy-weight work into this + // loop, consider parallelizing the loop into the worker threads. for (int i = 0; i < length(); ++i) { - ParScanThreadState& par_scan_state = thread_sate(i); + ParScanThreadState& par_scan_state = thread_state(i); // Flush stats related to To-space PLAB activity and // retire the last buffer. @@ -362,6 +382,14 @@ } } } + if (UseConcMarkSweepGC && ParallelGCThreads > 0) { + // We need to call this even when ResizeOldPLAB is disabled + // so as to avoid breaking some asserts. While we may be able + // to avoid this by reorganizing the code a bit, I am loathe + // to do that unless we find cases where ergo leads to bad + // performance. + CFLS_LAB::compute_desired_plab_size(); + } } ParScanClosure::ParScanClosure(ParNewGeneration* g, @@ -475,17 +503,19 @@ Generation* old_gen = gch->next_gen(_gen); - ParScanThreadState& par_scan_state = _state_set->thread_sate(i); + ParScanThreadState& par_scan_state = _state_set->thread_state(i); par_scan_state.set_young_old_boundary(_young_old_boundary); par_scan_state.start_strong_roots(); gch->gen_process_strong_roots(_gen->level(), - true, // Process younger gens, if any, - // as strong roots. - false,// not collecting perm generation. + true, // Process younger gens, if any, + // as strong roots. + false, // no scope; this is parallel code + false, // not collecting perm generation. SharedHeap::SO_AllClasses, - &par_scan_state.older_gen_closure(), - &par_scan_state.to_space_root_closure()); + &par_scan_state.to_space_root_closure(), + true, // walk *all* scavengable nmethods + &par_scan_state.older_gen_closure()); par_scan_state.end_strong_roots(); // "evacuate followers". @@ -657,7 +687,7 @@ { ResourceMark rm; HandleMark hm; - ParScanThreadState& par_scan_state = _state_set.thread_sate(i); + ParScanThreadState& par_scan_state = _state_set.thread_state(i); par_scan_state.set_young_old_boundary(_young_old_boundary); _task.work(i, par_scan_state.is_alive_closure(), par_scan_state.keep_alive_closure(), @@ -691,7 +721,7 @@ ParNewRefProcTaskProxy rp_task(task, _generation, *_generation.next_gen(), _generation.reserved().end(), _state_set); workers->run_task(&rp_task); - _state_set.reset(); + _state_set.reset(_generation.promotion_failed()); } void ParNewRefProcTaskExecutor::execute(EnqueueTask& task) @@ -799,18 +829,19 @@ ParNewGenTask tsk(this, _next_gen, reserved().end(), &thread_state_set); int n_workers = workers->total_workers(); gch->set_par_threads(n_workers); - gch->change_strong_roots_parity(); gch->rem_set()->prepare_for_younger_refs_iterate(true); // It turns out that even when we're using 1 thread, doing the work in a // separate thread causes wide variance in run times. We can't help this // in the multi-threaded case, but we special-case n=1 here to get // repeatable measurements of the 1-thread overhead of the parallel code. if (n_workers > 1) { + GenCollectedHeap::StrongRootsScope srs(gch); workers->run_task(&tsk); } else { + GenCollectedHeap::StrongRootsScope srs(gch); tsk.work(0); } - thread_state_set.reset(); + thread_state_set.reset(promotion_failed()); if (PAR_STATS_ENABLED && ParallelGCVerbose) { gclog_or_tty->print("Thread totals:\n" @@ -879,6 +910,8 @@ swap_spaces(); // Make life simpler for CMS || rescan; see 6483690. from()->set_next_compaction_space(to()); gch->set_incremental_collection_will_fail(); + // Inform the next generation that a promotion failure occurred. + _next_gen->promotion_failure_occurred(); // Reset the PromotionFailureALot counters. NOT_PRODUCT(Universe::heap()->reset_promotion_should_fail();) @@ -1026,6 +1059,8 @@ new_obj = old; preserve_mark_if_necessary(old, m); + // Log the size of the maiden promotion failure + par_scan_state->log_promotion_failure(sz); } old->forward_to(new_obj); @@ -1147,6 +1182,8 @@ failed_to_promote = true; preserve_mark_if_necessary(old, m); + // Log the size of the maiden promotion failure + par_scan_state->log_promotion_failure(sz); } } else { // Is in to-space; do copying ourselves. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parNew/parNewGeneration.hpp --- a/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -97,6 +97,9 @@ int _pushes, _pops, _steals, _steal_attempts, _term_attempts; int _overflow_pushes, _overflow_refills, _overflow_refill_objs; + // Stats for promotion failure + size_t _promotion_failure_size; + // Timing numbers. double _start; double _start_strong_roots; @@ -169,6 +172,15 @@ // Undo the most recent allocation ("obj", of "word_sz"). void undo_alloc_in_to_space(HeapWord* obj, size_t word_sz); + // Promotion failure stats + size_t promotion_failure_size() { return promotion_failure_size(); } + void log_promotion_failure(size_t sz) { + if (_promotion_failure_size == 0) { + _promotion_failure_size = sz; + } + } + void print_and_clear_promotion_failure_size(); + int pushes() { return _pushes; } int pops() { return _pops; } int steals() { return _steals; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -51,6 +51,8 @@ } jint ParallelScavengeHeap::initialize() { + CollectedHeap::pre_initialize(); + // Cannot be initialized until after the flags are parsed GenerationSizer flag_parser; @@ -314,41 +316,6 @@ return false; } -// Static method -bool ParallelScavengeHeap::is_in_young(oop* p) { - ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); - assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, - "Must be ParallelScavengeHeap"); - - PSYoungGen* young_gen = heap->young_gen(); - - if (young_gen->is_in_reserved(p)) { - return true; - } - - return false; -} - -// Static method -bool ParallelScavengeHeap::is_in_old_or_perm(oop* p) { - ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); - assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, - "Must be ParallelScavengeHeap"); - - PSOldGen* old_gen = heap->old_gen(); - PSPermGen* perm_gen = heap->perm_gen(); - - if (old_gen->is_in_reserved(p)) { - return true; - } - - if (perm_gen->is_in_reserved(p)) { - return true; - } - - return false; -} - // There are two levels of allocation policy here. // // When an allocation request fails, the requesting thread must invoke a VM @@ -752,10 +719,6 @@ return young_gen()->allocate(size, true); } -void ParallelScavengeHeap::fill_all_tlabs(bool retire) { - CollectedHeap::fill_all_tlabs(retire); -} - void ParallelScavengeHeap::accumulate_statistics_all_tlabs() { CollectedHeap::accumulate_statistics_all_tlabs(); } @@ -764,6 +727,13 @@ CollectedHeap::resize_all_tlabs(); } +bool ParallelScavengeHeap::can_elide_initializing_store_barrier(oop new_obj) { + // We don't need barriers for stores to objects in the + // young gen and, a fortiori, for initializing stores to + // objects therein. + return is_in_young(new_obj); +} + // This method is used by System.gc() and JVMTI. void ParallelScavengeHeap::collect(GCCause::Cause cause) { assert(!Heap_lock->owned_by_self(), @@ -962,6 +932,14 @@ _old_gen->resize(desired_free_space); } +ParallelScavengeHeap::ParStrongRootsScope::ParStrongRootsScope() { + // nothing particular +} + +ParallelScavengeHeap::ParStrongRootsScope::~ParStrongRootsScope() { + // nothing particular +} + #ifndef PRODUCT void ParallelScavengeHeap::record_gen_tops_before_GC() { if (ZapUnusedHeapArea) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -54,7 +54,6 @@ protected: static inline size_t total_invocations(); HeapWord* allocate_new_tlab(size_t size); - void fill_all_tlabs(bool retire); public: ParallelScavengeHeap() : CollectedHeap() { @@ -129,8 +128,8 @@ return perm_gen()->is_in(p); } - static bool is_in_young(oop *p); // reserved part - static bool is_in_old_or_perm(oop *p); // reserved part + inline bool is_in_young(oop p); // reserved part + inline bool is_in_old_or_perm(oop p); // reserved part // Memory allocation. "gc_time_limit_was_exceeded" will // be set to true if the adaptive size policy determine that @@ -191,6 +190,14 @@ return true; } + virtual bool card_mark_must_follow_store() const { + return false; + } + + // Return true if we don't we need a store barrier for + // initializing stores to an object at this address. + virtual bool can_elide_initializing_store_barrier(oop new_obj); + // Can a compiler elide a store barrier when it writes // a permanent oop into the heap? Applies when the compiler // is storing x to the heap, where x->is_perm() is true. @@ -234,6 +241,13 @@ // Mangle the unused parts of all spaces in the heap void gen_mangle_unused_area() PRODUCT_RETURN; + + // Call these in sequential code around the processing of strong roots. + class ParStrongRootsScope : public MarkingCodeBlobClosure::MarkScope { + public: + ParStrongRootsScope(); + ~ParStrongRootsScope(); + }; }; inline size_t ParallelScavengeHeap::set_alignment(size_t& var, size_t val) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -41,3 +41,11 @@ PSMarkSweep::invoke(maximum_compaction); } } + +inline bool ParallelScavengeHeap::is_in_young(oop p) { + return young_gen()->is_in_reserved(p); +} + +inline bool ParallelScavengeHeap::is_in_old_or_perm(oop p) { + return old_gen()->is_in_reserved(p) || perm_gen()->is_in_reserved(p); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, 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 @@ -39,12 +39,13 @@ ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(which); PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); + CodeBlobToOopClosure mark_and_push_in_blobs(&mark_and_push_closure, /*do_marking=*/ true); if (_java_thread != NULL) - _java_thread->oops_do(&mark_and_push_closure); + _java_thread->oops_do(&mark_and_push_closure, &mark_and_push_in_blobs); if (_vm_thread != NULL) - _vm_thread->oops_do(&mark_and_push_closure); + _vm_thread->oops_do(&mark_and_push_closure, &mark_and_push_in_blobs); // Do the real work cm->drain_marking_stacks(&mark_and_push_closure); @@ -58,9 +59,8 @@ PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(which); - // cm->allocate_stacks(); assert(cm->stacks_have_been_allocated(), - "Stack space has not been allocated"); + "Stack space has not been allocated"); PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); switch (_root_type) { @@ -79,7 +79,8 @@ case threads: { ResourceMark rm; - Threads::oops_do(&mark_and_push_closure); + CodeBlobToOopClosure each_active_code_blob(&mark_and_push_closure, /*do_marking=*/ true); + Threads::oops_do(&mark_and_push_closure, &each_active_code_blob); } break; @@ -107,6 +108,11 @@ vmSymbols::oops_do(&mark_and_push_closure); break; + case code_cache: + // Do not treat nmethods as strong roots for mark/sweep, since we can unload them. + //CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(&mark_and_push_closure)); + break; + default: fatal("Unknown root type"); } @@ -129,9 +135,8 @@ PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(which); - // cm->allocate_stacks(); assert(cm->stacks_have_been_allocated(), - "Stack space has not been allocated"); + "Stack space has not been allocated"); PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); PSParallelCompact::FollowStackClosure follow_stack_closure(cm); _rp_task.work(_work_id, *PSParallelCompact::is_alive_closure(), diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -92,7 +92,8 @@ jvmti = 7, system_dictionary = 8, vm_symbols = 9, - reference_processing = 10 + reference_processing = 10, + code_cache = 11 }; private: RootType _root_type; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, 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 @@ -61,12 +61,16 @@ int size = (SystemDictionary::number_of_classes() * 2) * 2 / ParallelGCThreads; _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + // From some experiments (#klass/k)^2 for k = 10 seems a better fit, but this will + // have to do for now until we are able to investigate a more optimal setting. + _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(size*2, true); } ParCompactionManager::~ParCompactionManager() { delete _overflow_stack; delete _revisit_klass_stack; + delete _revisit_mdo_stack; // _manager_array and _stack_array are statics // shared with all instances of ParCompactionManager // should not be deallocated. @@ -195,6 +199,7 @@ void ParCompactionManager::reset() { for(uint i=0; irevisit_klass_stack()->clear(); + manager_array(i)->revisit_mdo_stack()->clear(); } } @@ -296,6 +301,7 @@ #ifdef ASSERT bool ParCompactionManager::stacks_have_been_allocated() { - return (revisit_klass_stack()->data_addr() != NULL); + return (revisit_klass_stack()->data_addr() != NULL && + revisit_mdo_stack()->data_addr() != NULL); } #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, 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 @@ -93,6 +93,7 @@ #if 1 // does this happen enough to need a per thread stack? GrowableArray* _revisit_klass_stack; + GrowableArray* _revisit_mdo_stack; #endif static ParMarkBitMap* _mark_bitmap; @@ -154,6 +155,7 @@ #if 1 // Probably stays as a growable array GrowableArray* revisit_klass_stack() { return _revisit_klass_stack; } + GrowableArray* revisit_mdo_stack() { return _revisit_mdo_stack; } #endif // Save oop for later processing. Must not fail. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -51,7 +51,7 @@ cname = PerfDataManager::counter_name(name_space(), "oldCapacity"); _old_capacity = PerfDataManager::create_variable(SUN_GC, cname, - PerfData::U_Bytes, (jlong) Arguments::initial_heap_size(), CHECK); + PerfData::U_Bytes, (jlong) InitialHeapSize, CHECK); cname = PerfDataManager::counter_name(name_space(), "boundaryMoved"); _boundary_moved = PerfDataManager::create_variable(SUN_GC, cname, diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -482,6 +482,9 @@ int size = SystemDictionary::number_of_classes() * 2; _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + // (#klass/k)^2, for k ~ 10 appears a better setting, but this will have to do for + // now until we investigate a more optimal setting. + _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(size*2, true); } @@ -495,6 +498,7 @@ delete _marking_stack; delete _revisit_klass_stack; + delete _revisit_mdo_stack; } void PSMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) { @@ -507,16 +511,22 @@ assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); // General strong roots. - Universe::oops_do(mark_and_push_closure()); - ReferenceProcessor::oops_do(mark_and_push_closure()); - JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles - Threads::oops_do(mark_and_push_closure()); - ObjectSynchronizer::oops_do(mark_and_push_closure()); - FlatProfiler::oops_do(mark_and_push_closure()); - Management::oops_do(mark_and_push_closure()); - JvmtiExport::oops_do(mark_and_push_closure()); - SystemDictionary::always_strong_oops_do(mark_and_push_closure()); - vmSymbols::oops_do(mark_and_push_closure()); + { + ParallelScavengeHeap::ParStrongRootsScope psrs; + Universe::oops_do(mark_and_push_closure()); + ReferenceProcessor::oops_do(mark_and_push_closure()); + JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles + CodeBlobToOopClosure each_active_code_blob(mark_and_push_closure(), /*do_marking=*/ true); + Threads::oops_do(mark_and_push_closure(), &each_active_code_blob); + ObjectSynchronizer::oops_do(mark_and_push_closure()); + FlatProfiler::oops_do(mark_and_push_closure()); + Management::oops_do(mark_and_push_closure()); + JvmtiExport::oops_do(mark_and_push_closure()); + SystemDictionary::always_strong_oops_do(mark_and_push_closure()); + vmSymbols::oops_do(mark_and_push_closure()); + // Do not treat nmethods as strong roots for mark/sweep, since we can unload them. + //CodeCache::scavenge_root_nmethods_do(CodeBlobToOopClosure(mark_and_push_closure())); + } // Flush marking stack. follow_stack(); @@ -540,6 +550,10 @@ follow_weak_klass_links(); assert(_marking_stack->is_empty(), "just drained"); + // Visit memoized mdo's and clear unmarked weak refs + follow_mdo_weak_refs(); + assert(_marking_stack->is_empty(), "just drained"); + // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(is_alive_closure()); StringTable::unlink(is_alive_closure()); @@ -609,7 +623,7 @@ Universe::oops_do(adjust_root_pointer_closure()); ReferenceProcessor::oops_do(adjust_root_pointer_closure()); JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles - Threads::oops_do(adjust_root_pointer_closure()); + Threads::oops_do(adjust_root_pointer_closure(), NULL); ObjectSynchronizer::oops_do(adjust_root_pointer_closure()); FlatProfiler::oops_do(adjust_root_pointer_closure()); Management::oops_do(adjust_root_pointer_closure()); @@ -617,6 +631,7 @@ // SO_AllClasses SystemDictionary::oops_do(adjust_root_pointer_closure()); vmSymbols::oops_do(adjust_root_pointer_closure()); + //CodeCache::scavenge_root_nmethods_oops_do(adjust_root_pointer_closure()); // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -2322,6 +2322,7 @@ { TraceTime tm_m("par mark", print_phases(), true, gclog_or_tty); + ParallelScavengeHeap::ParStrongRootsScope psrs; GCTaskQueue* q = GCTaskQueue::create(); @@ -2335,6 +2336,7 @@ q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::system_dictionary)); q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::jvmti)); q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::vm_symbols)); + q->enqueue(new MarkFromRootsTask(MarkFromRootsTask::code_cache)); if (parallel_gc_threads > 1) { for (uint j = 0; j < parallel_gc_threads; j++) { @@ -2378,7 +2380,10 @@ // Update subklass/sibling/implementor links of live klasses // revisit_klass_stack is used in follow_weak_klass_links(). - follow_weak_klass_links(cm); + follow_weak_klass_links(); + + // Revisit memoized MDO's and clear any unmarked weak refs + follow_mdo_weak_refs(); // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(is_alive_closure()); @@ -2405,7 +2410,7 @@ Universe::oops_do(adjust_root_pointer_closure()); ReferenceProcessor::oops_do(adjust_root_pointer_closure()); JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles - Threads::oops_do(adjust_root_pointer_closure()); + Threads::oops_do(adjust_root_pointer_closure(), NULL); ObjectSynchronizer::oops_do(adjust_root_pointer_closure()); FlatProfiler::oops_do(adjust_root_pointer_closure()); Management::oops_do(adjust_root_pointer_closure()); @@ -2721,17 +2726,25 @@ } void -PSParallelCompact::follow_weak_klass_links(ParCompactionManager* serial_cm) { +PSParallelCompact::follow_weak_klass_links() { // All klasses on the revisit stack are marked at this point. // Update and follow all subklass, sibling and implementor links. - for (uint i = 0; i < ParallelGCThreads+1; i++) { + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + } + for (uint i = 0; i < ParallelGCThreads + 1; i++) { ParCompactionManager* cm = ParCompactionManager::manager_array(i); KeepAliveClosure keep_alive_closure(cm); - for (int i = 0; i < cm->revisit_klass_stack()->length(); i++) { - cm->revisit_klass_stack()->at(i)->follow_weak_klass_links( + int length = cm->revisit_klass_stack()->length(); + if (PrintRevisitStats) { + gclog_or_tty->print_cr("Revisit klass stack[%d] length = %d", i, length); + } + for (int j = 0; j < length; j++) { + cm->revisit_klass_stack()->at(j)->follow_weak_klass_links( is_alive_closure(), &keep_alive_closure); } + // revisit_klass_stack is cleared in reset() follow_stack(cm); } } @@ -2741,6 +2754,33 @@ cm->revisit_klass_stack()->push(k); } +void PSParallelCompact::revisit_mdo(ParCompactionManager* cm, DataLayout* p) { + cm->revisit_mdo_stack()->push(p); +} + +void PSParallelCompact::follow_mdo_weak_refs() { + // All strongly reachable oops have been marked at this point; + // we can visit and clear any weak references from MDO's which + // we memoized during the strong marking phase. + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + } + for (uint i = 0; i < ParallelGCThreads + 1; i++) { + ParCompactionManager* cm = ParCompactionManager::manager_array(i); + GrowableArray* rms = cm->revisit_mdo_stack(); + int length = rms->length(); + if (PrintRevisitStats) { + gclog_or_tty->print_cr("Revisit MDO stack[%d] length = %d", i, length); + } + for (int j = 0; j < length; j++) { + rms->at(j)->follow_weak_refs(is_alive_closure()); + } + // revisit_mdo_stack is cleared in reset() + follow_stack(cm); + } +} + + #ifdef VALIDATE_MARK_SWEEP void PSParallelCompact::track_adjusted_pointer(void* p, bool isroot) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -799,8 +799,7 @@ FollowRootClosure(ParCompactionManager* cm) : _compaction_manager(cm) { } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); - virtual const bool do_nmethods() const { return true; } - }; + }; class FollowStackClosure: public VoidClosure { private: @@ -817,6 +816,8 @@ AdjustPointerClosure(bool is_root) : _is_root(is_root) { } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); + // do not walk from thread stacks to the code cache on this phase + virtual void do_code_blob(CodeBlob* cb) const { } }; // Closure for verifying update of pointers. Does not @@ -901,7 +902,8 @@ static void marking_phase(ParCompactionManager* cm, bool maximum_heap_compaction); static void follow_stack(ParCompactionManager* cm); - static void follow_weak_klass_links(ParCompactionManager* cm); + static void follow_weak_klass_links(); + static void follow_mdo_weak_refs(); template static inline void adjust_pointer(T* p, bool is_root); static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); } @@ -1062,7 +1064,6 @@ MarkAndPushClosure(ParCompactionManager* cm) : _compaction_manager(cm) { } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); - virtual const bool do_nmethods() const { return true; } }; PSParallelCompact(); @@ -1221,6 +1222,9 @@ // Update subklass/sibling/implementor links at end of marking. static void revisit_weak_klass_link(ParCompactionManager* cm, Klass* k); + // Clear unmarked oops in MDOs at the end of marking. + static void revisit_mdo(ParCompactionManager* cm, DataLayout* p); + #ifndef PRODUCT // Debugging support. static const char* space_names[last_space_id]; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -358,6 +358,7 @@ PSPromotionManager* promotion_manager = PSPromotionManager::vm_thread_promotion_manager(); { // TraceTime("Roots"); + ParallelScavengeHeap::ParStrongRootsScope psrs; GCTaskQueue* q = GCTaskQueue::create(); @@ -376,6 +377,7 @@ q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::management)); q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::system_dictionary)); q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::jvmti)); + q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache)); ParallelTaskTerminator terminator( gc_task_manager()->workers(), diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -66,7 +66,7 @@ case threads: { ResourceMark rm; - Threads::oops_do(&roots_closure); + Threads::oops_do(&roots_closure, NULL); } break; @@ -90,6 +90,14 @@ JvmtiExport::oops_do(&roots_closure); break; + + case code_cache: + { + CodeBlobToOopClosure each_scavengable_code_blob(&roots_closure, /*do_marking=*/ true); + CodeCache::scavenge_root_nmethods_do(&each_scavengable_code_blob); + } + break; + default: fatal("Unknown root type"); } @@ -107,12 +115,13 @@ PSPromotionManager* pm = PSPromotionManager::gc_thread_promotion_manager(which); PSScavengeRootsClosure roots_closure(pm); + CodeBlobToOopClosure roots_in_blobs(&roots_closure, /*do_marking=*/ true); if (_java_thread != NULL) - _java_thread->oops_do(&roots_closure); + _java_thread->oops_do(&roots_closure, &roots_in_blobs); if (_vm_thread != NULL) - _vm_thread->oops_do(&roots_closure); + _vm_thread->oops_do(&roots_closure, &roots_in_blobs); // Do the real work pm->drain_stacks(false); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/parallelScavenge/psTasks.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psTasks.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psTasks.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -54,7 +54,8 @@ flat_profiler = 5, system_dictionary = 6, management = 7, - jvmti = 8 + jvmti = 8, + code_cache = 9 }; private: RootType _root_type; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/allocationStats.hpp --- a/src/share/vm/gc_implementation/shared/allocationStats.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/shared/allocationStats.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -31,7 +31,7 @@ // beginning of this sweep: // Count(end_last_sweep) - Count(start_this_sweep) // + splitBirths(between) - splitDeaths(between) - // The above number divided by the time since the start [END???] of the + // The above number divided by the time since the end of the // previous sweep gives us a time rate of demand for blocks // of this size. We compute a padded average of this rate as // our current estimate for the time rate of demand for blocks @@ -41,7 +41,7 @@ // estimates. AdaptivePaddedAverage _demand_rate_estimate; - ssize_t _desired; // Estimate computed as described above + ssize_t _desired; // Demand stimate computed as described above ssize_t _coalDesired; // desired +/- small-percent for tuning coalescing ssize_t _surplus; // count - (desired +/- small-percent), @@ -53,9 +53,9 @@ ssize_t _coalDeaths; // loss from coalescing ssize_t _splitBirths; // additional chunks from splitting ssize_t _splitDeaths; // loss from splitting - size_t _returnedBytes; // number of bytes returned to list. + size_t _returnedBytes; // number of bytes returned to list. public: - void initialize() { + void initialize(bool split_birth = false) { AdaptivePaddedAverage* dummy = new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight, CMS_FLSPadding); @@ -67,7 +67,7 @@ _beforeSweep = 0; _coalBirths = 0; _coalDeaths = 0; - _splitBirths = 0; + _splitBirths = split_birth? 1 : 0; _splitDeaths = 0; _returnedBytes = 0; } @@ -75,10 +75,12 @@ AllocationStats() { initialize(); } + // The rate estimate is in blocks per second. void compute_desired(size_t count, float inter_sweep_current, - float inter_sweep_estimate) { + float inter_sweep_estimate, + float intra_sweep_estimate) { // If the latest inter-sweep time is below our granularity // of measurement, we may call in here with // inter_sweep_current == 0. However, even for suitably small @@ -88,12 +90,31 @@ // vulnerable to noisy glitches. In such cases, we // ignore the current sample and use currently available // historical estimates. + // XXX NEEDS TO BE FIXED + // assert(prevSweep() + splitBirths() >= splitDeaths() + (ssize_t)count, "Conservation Principle"); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // "Total Stock" "Not used at this block size" if (inter_sweep_current > _threshold) { - ssize_t demand = prevSweep() - count + splitBirths() - splitDeaths(); + ssize_t demand = prevSweep() - (ssize_t)count + splitBirths() - splitDeaths(); + // XXX NEEDS TO BE FIXED + // assert(demand >= 0, "Demand should be non-negative"); + // Defensive: adjust for imprecision in event counting + if (demand < 0) { + demand = 0; + } + float old_rate = _demand_rate_estimate.padded_average(); float rate = ((float)demand)/inter_sweep_current; _demand_rate_estimate.sample(rate); - _desired = (ssize_t)(_demand_rate_estimate.padded_average() - *inter_sweep_estimate); + float new_rate = _demand_rate_estimate.padded_average(); + ssize_t old_desired = _desired; + _desired = (ssize_t)(new_rate * (inter_sweep_estimate + + CMSExtrapolateSweep + ? intra_sweep_estimate + : 0.0)); + 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); + } } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/coTracker.cpp --- a/src/share/vm/gc_implementation/shared/coTracker.cpp Thu Mar 25 16:27:12 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/* - * Copyright 2001-2007 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -# include "incls/_precompiled.incl" -# include "incls/_coTracker.cpp.incl" - -COTracker* COTracker::_head = NULL; -double COTracker::_cpu_number = -1.0; - -void -COTracker::resetPeriod(double now_sec, double vnow_sec) { - guarantee( _enabled, "invariant" ); - _period_start_time_sec = now_sec; - _period_start_vtime_sec = vnow_sec; -} - -void -COTracker::setConcOverhead(double time_stamp_sec, - double conc_overhead) { - guarantee( _enabled, "invariant" ); - _conc_overhead = conc_overhead; - _time_stamp_sec = time_stamp_sec; - if (conc_overhead > 0.001) - _conc_overhead_seq.add(conc_overhead); -} - -void -COTracker::reset(double starting_conc_overhead) { - guarantee( _enabled, "invariant" ); - double now_sec = os::elapsedTime(); - setConcOverhead(now_sec, starting_conc_overhead); -} - -void -COTracker::start() { - guarantee( _enabled, "invariant" ); - resetPeriod(os::elapsedTime(), os::elapsedVTime()); -} - -void -COTracker::update(bool force_end) { - assert( _enabled, "invariant" ); - double end_time_sec = os::elapsedTime(); - double elapsed_time_sec = end_time_sec - _period_start_time_sec; - if (force_end || elapsed_time_sec > _update_period_sec) { - // reached the end of the period - double end_vtime_sec = os::elapsedVTime(); - double elapsed_vtime_sec = end_vtime_sec - _period_start_vtime_sec; - - double conc_overhead = elapsed_vtime_sec / elapsed_time_sec; - - setConcOverhead(end_time_sec, conc_overhead); - resetPeriod(end_time_sec, end_vtime_sec); - } -} - -void -COTracker::updateForSTW(double start_sec, double end_sec) { - if (!_enabled) - return; - - // During a STW pause, no concurrent GC thread has done any - // work. So, we can safely adjust the start of the current period by - // adding the duration of the STW pause to it, so that the STW pause - // doesn't affect the reading of the concurrent overhead (it's - // basically like excluding the time of the STW pause from the - // concurrent overhead calculation). - - double stw_duration_sec = end_sec - start_sec; - guarantee( stw_duration_sec > 0.0, "invariant" ); - - if (outOfDate(start_sec)) - _conc_overhead = 0.0; - else - _time_stamp_sec = end_sec; - _period_start_time_sec += stw_duration_sec; - _conc_overhead_seq = NumberSeq(); - - guarantee( os::elapsedTime() > _period_start_time_sec, "invariant" ); -} - -double -COTracker::predConcOverhead() { - if (_enabled) { - // tty->print(" %1.2lf", _conc_overhead_seq.maximum()); - return _conc_overhead_seq.maximum(); - } else { - // tty->print(" DD"); - return 0.0; - } -} - -void -COTracker::resetPred() { - _conc_overhead_seq = NumberSeq(); -} - -COTracker::COTracker(int group) - : _enabled(false), - _group(group), - _period_start_time_sec(-1.0), - _period_start_vtime_sec(-1.0), - _conc_overhead(-1.0), - _time_stamp_sec(-1.0), - _next(NULL) { - // GCOverheadReportingPeriodMS indicates how frequently the - // concurrent overhead will be recorded by the GC Overhead - // Reporter. We want to take readings less often than that. If we - // took readings more often than some of them might be lost. - _update_period_sec = ((double) GCOverheadReportingPeriodMS) / 1000.0 * 1.25; - _next = _head; - _head = this; - - if (_cpu_number < 0.0) - _cpu_number = (double) os::processor_count(); -} - -// statics - -void -COTracker::updateAllForSTW(double start_sec, double end_sec) { - for (COTracker* curr = _head; curr != NULL; curr = curr->_next) { - curr->updateForSTW(start_sec, end_sec); - } -} - -double -COTracker::totalConcOverhead(double now_sec) { - double total_conc_overhead = 0.0; - - for (COTracker* curr = _head; curr != NULL; curr = curr->_next) { - double conc_overhead = curr->concOverhead(now_sec); - total_conc_overhead += conc_overhead; - } - - return total_conc_overhead; -} - -double -COTracker::totalConcOverhead(double now_sec, - size_t group_num, - double* co_per_group) { - double total_conc_overhead = 0.0; - - for (size_t i = 0; i < group_num; ++i) - co_per_group[i] = 0.0; - - for (COTracker* curr = _head; curr != NULL; curr = curr->_next) { - size_t group = curr->_group; - assert( 0 <= group && group < group_num, "invariant" ); - double conc_overhead = curr->concOverhead(now_sec); - - co_per_group[group] += conc_overhead; - total_conc_overhead += conc_overhead; - } - - return total_conc_overhead; -} - -double -COTracker::totalPredConcOverhead() { - double total_pred_conc_overhead = 0.0; - for (COTracker* curr = _head; curr != NULL; curr = curr->_next) { - total_pred_conc_overhead += curr->predConcOverhead(); - curr->resetPred(); - } - return total_pred_conc_overhead / _cpu_number; -} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/coTracker.hpp --- a/src/share/vm/gc_implementation/shared/coTracker.hpp Thu Mar 25 16:27:12 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/* - * Copyright 2001-2007 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -// COTracker keeps track of the concurrent overhead of a GC thread. - -// A thread that needs to be tracked must, itself, start up its -// tracker with the start() method and then call the update() method -// at regular intervals. What the tracker does is to calculate the -// concurrent overhead of a process at a given update period. The -// tracker starts and when is detects that it has exceeded the given -// period, it calculates the duration of the period in wall-clock time -// and the duration of the period in vtime (i.e. how much time the -// concurrent processes really took up during this period). The ratio -// of the latter over the former is the concurrent overhead of that -// process for that period over a single CPU. This overhead is stored -// on the tracker, "timestamped" with the wall-clock time of the end -// of the period. When the concurrent overhead of this process needs -// to be queried, this last "reading" provides a good approximation -// (we assume that the concurrent overhead of a particular thread -// stays largely constant over time). The timestamp is necessary to -// detect when the process has stopped working and the recorded -// reading hasn't been updated for some time. - -// Each concurrent GC thread is considered to be part of a "group" -// (i.e. any available concurrent marking threads are part of the -// "concurrent marking thread group"). A COTracker is associated with -// a single group at construction-time. It's up to each collector to -// decide how groups will be mapped to such an id (ids should start -// from 0 and be consecutive; there's a hardcoded max group num -// defined on the GCOverheadTracker class). The notion of a group has -// been introduced to be able to identify how much overhead was -// imposed by each group, instead of getting a single value that -// covers all concurrent overhead. - -class COTracker { -private: - // It indicates whether this tracker is enabled or not. When the - // tracker is disabled, then it returns 0.0 as the latest concurrent - // overhead and several methods (reset, start, and update) are not - // supposed to be called on it. This enabling / disabling facility - // is really provided to make a bit more explicit in the code when a - // particulary tracker of a processes that doesn't run all the time - // (e.g. concurrent marking) is supposed to be used and not it's not. - bool _enabled; - - // The ID of the group associated with this tracker. - int _group; - - // The update period of the tracker. A new value for the concurrent - // overhead of the associated process will be made at intervals no - // smaller than this. - double _update_period_sec; - - // The start times (both wall-block time and vtime) of the current - // interval. - double _period_start_time_sec; - double _period_start_vtime_sec; - - // Number seq of the concurrent overhead readings within a period - NumberSeq _conc_overhead_seq; - - // The latest reading of the concurrent overhead (over a single CPU) - // imposed by the associated concurrent thread, made available at - // the indicated wall-clock time. - double _conc_overhead; - double _time_stamp_sec; - - // The number of CPUs that the host machine has (for convenience - // really, as we'd have to keep translating it into a double) - static double _cpu_number; - - // Fields that keep a list of all trackers created. This is useful, - // since it allows us to sum up the concurrent overhead without - // having to write code for a specific collector to broadcast a - // request to all its concurrent processes. - COTracker* _next; - static COTracker* _head; - - // It indicates that a new period is starting by updating the - // _period_start_time_sec and _period_start_vtime_sec fields. - void resetPeriod(double now_sec, double vnow_sec); - // It updates the latest concurrent overhead reading, taken at a - // given wall-clock time. - void setConcOverhead(double time_stamp_sec, double conc_overhead); - - // It determines whether the time stamp of the latest concurrent - // overhead reading is out of date or not. - bool outOfDate(double now_sec) { - // The latest reading is considered out of date, if it was taken - // 1.2x the update period. - return (now_sec - _time_stamp_sec) > 1.2 * _update_period_sec; - } - -public: - // The constructor which associates the tracker with a group ID. - COTracker(int group); - - // Methods to enable / disable the tracker and query whether it is enabled. - void enable() { _enabled = true; } - void disable() { _enabled = false; } - bool enabled() { return _enabled; } - - // It resets the tracker and sets concurrent overhead reading to be - // the given parameter and the associated time stamp to be now. - void reset(double starting_conc_overhead = 0.0); - // The tracker starts tracking. IT should only be called from the - // concurrent thread that is tracked by this tracker. - void start(); - // It updates the tracker and, if the current period is longer than - // the update period, the concurrent overhead reading will be - // updated. force_end being true indicates that it's the last call - // to update() by this process before the tracker is disabled (the - // tracker can be re-enabled later if necessary). It should only be - // called from the concurrent thread that is tracked by this tracker - // and while the thread has joined the STS. - void update(bool force_end = false); - // It adjusts the contents of the tracker to take into account a STW - // pause. - void updateForSTW(double start_sec, double end_sec); - - // It returns the last concurrent overhead reading over a single - // CPU. If the reading is out of date, or the tracker is disabled, - // it returns 0.0. - double concCPUOverhead(double now_sec) { - if (!_enabled || outOfDate(now_sec)) - return 0.0; - else - return _conc_overhead; - } - - // It returns the last concurrent overhead reading over all CPUs - // that the host machine has. If the reading is out of date, or the - // tracker is disabled, it returns 0.0. - double concOverhead(double now_sec) { - return concCPUOverhead(now_sec) / _cpu_number; - } - - double predConcOverhead(); - - void resetPred(); - - // statics - - // It notifies all trackers about a STW pause. - static void updateAllForSTW(double start_sec, double end_sec); - - // It returns the sum of the concurrent overhead readings of all - // available (and enabled) trackers for the given time stamp. The - // overhead is over all the CPUs of the host machine. - - static double totalConcOverhead(double now_sec); - // Like the previous method, but it also sums up the overheads per - // group number. The length of the co_per_group array must be at - // least as large group_num - static double totalConcOverhead(double now_sec, - size_t group_num, - double* co_per_group); - - static double totalPredConcOverhead(); -}; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/gcOverheadReporter.cpp --- a/src/share/vm/gc_implementation/shared/gcOverheadReporter.cpp Thu Mar 25 16:27:12 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ -/* - * Copyright 2001-2007 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -# include "incls/_precompiled.incl" -# include "incls/_gcOverheadReporter.cpp.incl" - -class COReportingThread : public ConcurrentGCThread { -private: - GCOverheadReporter* _reporter; - -public: - COReportingThread(GCOverheadReporter* reporter) : _reporter(reporter) { - guarantee( _reporter != NULL, "precondition" ); - create_and_start(); - } - - virtual void run() { - initialize_in_thread(); - wait_for_universe_init(); - - int period_ms = GCOverheadReportingPeriodMS; - - while ( true ) { - os::sleep(Thread::current(), period_ms, false); - - _sts.join(); - double now_sec = os::elapsedTime(); - _reporter->collect_and_record_conc_overhead(now_sec); - _sts.leave(); - } - - terminate(); - } -}; - -GCOverheadReporter* GCOverheadReporter::_reporter = NULL; - -GCOverheadReporter::GCOverheadReporter(size_t group_num, - const char* group_names[], - size_t length) - : _group_num(group_num), _prev_end_sec(0.0) { - guarantee( 0 <= group_num && group_num <= MaxGCOverheadGroupNum, - "precondition" ); - - _base = NEW_C_HEAP_ARRAY(GCOverheadReporterEntry, length); - _top = _base + length; - _curr = _base; - - for (size_t i = 0; i < group_num; ++i) { - guarantee( group_names[i] != NULL, "precondition" ); - _group_names[i] = group_names[i]; - } -} - -void -GCOverheadReporter::add(double start_sec, double end_sec, - double* conc_overhead, - double stw_overhead) { - assert( _curr <= _top, "invariant" ); - - if (_curr == _top) { - guarantee( false, "trace full" ); - return; - } - - _curr->_start_sec = start_sec; - _curr->_end_sec = end_sec; - for (size_t i = 0; i < _group_num; ++i) { - _curr->_conc_overhead[i] = - (conc_overhead != NULL) ? conc_overhead[i] : 0.0; - } - _curr->_stw_overhead = stw_overhead; - - ++_curr; -} - -void -GCOverheadReporter::collect_and_record_conc_overhead(double end_sec) { - double start_sec = _prev_end_sec; - guarantee( end_sec > start_sec, "invariant" ); - - double conc_overhead[MaxGCOverheadGroupNum]; - COTracker::totalConcOverhead(end_sec, _group_num, conc_overhead); - add_conc_overhead(start_sec, end_sec, conc_overhead); - _prev_end_sec = end_sec; -} - -void -GCOverheadReporter::record_stw_start(double start_sec) { - guarantee( start_sec > _prev_end_sec, "invariant" ); - collect_and_record_conc_overhead(start_sec); -} - -void -GCOverheadReporter::record_stw_end(double end_sec) { - double start_sec = _prev_end_sec; - COTracker::updateAllForSTW(start_sec, end_sec); - add_stw_overhead(start_sec, end_sec, 1.0); - - _prev_end_sec = end_sec; -} - -void -GCOverheadReporter::print() const { - tty->print_cr(""); - tty->print_cr("GC Overhead (%d entries)", _curr - _base); - tty->print_cr(""); - GCOverheadReporterEntry* curr = _base; - while (curr < _curr) { - double total = curr->_stw_overhead; - for (size_t i = 0; i < _group_num; ++i) - total += curr->_conc_overhead[i]; - - tty->print("OVERHEAD %12.8lf %12.8lf ", - curr->_start_sec, curr->_end_sec); - - for (size_t i = 0; i < _group_num; ++i) - tty->print("%s %12.8lf ", _group_names[i], curr->_conc_overhead[i]); - - tty->print_cr("STW %12.8lf TOT %12.8lf", curr->_stw_overhead, total); - ++curr; - } - tty->print_cr(""); -} - -// statics - -void -GCOverheadReporter::initGCOverheadReporter(size_t group_num, - const char* group_names[]) { - guarantee( _reporter == NULL, "should only be called once" ); - guarantee( 0 <= group_num && group_num <= MaxGCOverheadGroupNum, - "precondition" ); - guarantee( group_names != NULL, "pre-condition" ); - - if (GCOverheadReporting) { - _reporter = new GCOverheadReporter(group_num, group_names); - new COReportingThread(_reporter); - } -} - -void -GCOverheadReporter::recordSTWStart(double start_sec) { - if (_reporter != NULL) - _reporter->record_stw_start(start_sec); -} - -void -GCOverheadReporter::recordSTWEnd(double end_sec) { - if (_reporter != NULL) - _reporter->record_stw_end(end_sec); -} - -void -GCOverheadReporter::printGCOverhead() { - if (_reporter != NULL) - _reporter->print(); -} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/gcOverheadReporter.hpp --- a/src/share/vm/gc_implementation/shared/gcOverheadReporter.hpp Thu Mar 25 16:27:12 2010 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/* - * Copyright 2001-2007 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -// Keeps track of the GC overhead (both concurrent and STW). It stores -// it in a large array and then prints it to tty at the end of the -// execution. - -// See coTracker.hpp for the explanation on what groups are. - -// Let's set a maximum number of concurrent overhead groups, to -// statically allocate any arrays we need and not to have to -// malloc/free them. This is just a bit more convenient. -enum { - MaxGCOverheadGroupNum = 4 -}; - -typedef struct { - double _start_sec; - double _end_sec; - - double _conc_overhead[MaxGCOverheadGroupNum]; - double _stw_overhead; -} GCOverheadReporterEntry; - -class GCOverheadReporter { - friend class COReportingThread; - -private: - enum PrivateConstants { - DefaultReporterLength = 128 * 1024 - }; - - // Reference to the single instance of this class. - static GCOverheadReporter* _reporter; - - // These three references point to the array that contains the GC - // overhead entries (_base is the base of the array, _top is the - // address passed the last entry of the array, _curr is the next - // entry to be used). - GCOverheadReporterEntry* _base; - GCOverheadReporterEntry* _top; - GCOverheadReporterEntry* _curr; - - // The number of concurrent overhead groups. - size_t _group_num; - - // The wall-clock time of the end of the last recorded period of GC - // overhead. - double _prev_end_sec; - - // Names for the concurrent overhead groups. - const char* _group_names[MaxGCOverheadGroupNum]; - - // Add a new entry to the large array. conc_overhead being NULL is - // equivalent to an array full of 0.0s. conc_overhead should have a - // length of at least _group_num. - void add(double start_sec, double end_sec, - double* conc_overhead, - double stw_overhead); - - // Add an entry that represents concurrent GC overhead. - // conc_overhead must be at least of length _group_num. - // conc_overhead being NULL is equivalent to an array full of 0.0s. - void add_conc_overhead(double start_sec, double end_sec, - double* conc_overhead) { - add(start_sec, end_sec, conc_overhead, 0.0); - } - - // Add an entry that represents STW GC overhead. - void add_stw_overhead(double start_sec, double end_sec, - double stw_overhead) { - add(start_sec, end_sec, NULL, stw_overhead); - } - - // It records the start of a STW pause (i.e. it records the - // concurrent overhead up to that point) - void record_stw_start(double start_sec); - - // It records the end of a STW pause (i.e. it records the overhead - // associated with the pause and adjusts all the trackers to reflect - // the pause) - void record_stw_end(double end_sec); - - // It queries all the trackers of their concurrent overhead and - // records it. - void collect_and_record_conc_overhead(double end_sec); - - // It prints the contents of the GC overhead array - void print() const; - - - // Constructor. The same preconditions for group_num and group_names - // from initGCOverheadReporter apply here too. - GCOverheadReporter(size_t group_num, - const char* group_names[], - size_t length = DefaultReporterLength); - -public: - - // statics - - // It initialises the GCOverheadReporter and launches the concurrent - // overhead reporting thread. Both actions happen only if the - // GCOverheadReporting parameter is set. The length of the - // group_names array should be >= group_num and group_num should be - // <= MaxGCOverheadGroupNum. Entries group_namnes[0..group_num-1] - // should not be NULL. - static void initGCOverheadReporter(size_t group_num, - const char* group_names[]); - - // The following three are provided for convenience and they are - // wrappers around record_stw_start(start_sec), record_stw_end(end_sec), - // and print(). Each of these checks whether GC overhead reporting - // is on (i.e. _reporter != NULL) and, if it is, calls the - // corresponding method. Saves from repeating this pattern again and - // again from the places where they need to be called. - static void recordSTWStart(double start_sec); - static void recordSTWEnd(double end_sec); - static void printGCOverhead(); -}; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/gcUtil.cpp --- a/src/share/vm/gc_implementation/shared/gcUtil.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/shared/gcUtil.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -52,11 +52,35 @@ _last_sample = new_sample; } +void AdaptiveWeightedAverage::print() const { + print_on(tty); +} + +void AdaptiveWeightedAverage::print_on(outputStream* st) const { + guarantee(false, "NYI"); +} + +void AdaptivePaddedAverage::print() const { + print_on(tty); +} + +void AdaptivePaddedAverage::print_on(outputStream* st) const { + guarantee(false, "NYI"); +} + +void AdaptivePaddedNoZeroDevAverage::print() const { + print_on(tty); +} + +void AdaptivePaddedNoZeroDevAverage::print_on(outputStream* st) const { + guarantee(false, "NYI"); +} + void AdaptivePaddedAverage::sample(float new_sample) { - // Compute our parent classes sample information + // Compute new adaptive weighted average based on new sample. AdaptiveWeightedAverage::sample(new_sample); - // Now compute the deviation and the new padded sample + // Now update the deviation and the padded average. float new_avg = average(); float new_dev = compute_adaptive_average(fabsd(new_sample - new_avg), deviation()); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/gcUtil.hpp --- a/src/share/vm/gc_implementation/shared/gcUtil.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/shared/gcUtil.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -54,8 +54,8 @@ public: // Input weight must be between 0 and 100 - AdaptiveWeightedAverage(unsigned weight) : - _average(0.0), _sample_count(0), _weight(weight), _last_sample(0.0) { + AdaptiveWeightedAverage(unsigned weight, float avg = 0.0) : + _average(avg), _sample_count(0), _weight(weight), _last_sample(0.0) { } void clear() { @@ -64,6 +64,13 @@ _last_sample = 0; } + // Useful for modifying static structures after startup. + void modify(size_t avg, unsigned wt, bool force = false) { + assert(force, "Are you sure you want to call this?"); + _average = (float)avg; + _weight = wt; + } + // Accessors float average() const { return _average; } unsigned weight() const { return _weight; } @@ -83,6 +90,10 @@ // Convert to float and back to avoid integer overflow. return (size_t)exp_avg((float)avg, (float)sample, weight); } + + // Printing + void print_on(outputStream* st) const; + void print() const; }; @@ -129,6 +140,10 @@ // Override void sample(float new_sample); + + // Printing + void print_on(outputStream* st) const; + void print() const; }; // A weighted average that includes a deviation from the average, @@ -146,7 +161,12 @@ AdaptivePaddedAverage(weight, padding) {} // Override void sample(float new_sample); + + // Printing + void print_on(outputStream* st) const; + void print() const; }; + // Use a least squares fit to a set of data to generate a linear // equation. // y = intercept + slope * x diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/markSweep.cpp --- a/src/share/vm/gc_implementation/shared/markSweep.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/shared/markSweep.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -27,6 +27,7 @@ GrowableArray* MarkSweep::_marking_stack = NULL; GrowableArray* MarkSweep::_revisit_klass_stack = NULL; +GrowableArray* MarkSweep::_revisit_mdo_stack = NULL; GrowableArray* MarkSweep::_preserved_oop_stack = NULL; GrowableArray* MarkSweep::_preserved_mark_stack= NULL; @@ -62,13 +63,37 @@ void MarkSweep::follow_weak_klass_links() { // All klasses on the revisit stack are marked at this point. // Update and follow all subklass, sibling and implementor links. + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + gclog_or_tty->print_cr("Revisit klass stack length = %d", _revisit_klass_stack->length()); + } for (int i = 0; i < _revisit_klass_stack->length(); i++) { _revisit_klass_stack->at(i)->follow_weak_klass_links(&is_alive,&keep_alive); } follow_stack(); } +void MarkSweep::revisit_mdo(DataLayout* p) { + _revisit_mdo_stack->push(p); +} + +void MarkSweep::follow_mdo_weak_refs() { + // All strongly reachable oops have been marked at this point; + // we can visit and clear any weak references from MDO's which + // we memoized during the strong marking phase. + assert(_marking_stack->is_empty(), "Marking stack should be empty"); + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + gclog_or_tty->print_cr("Revisit MDO stack length = %d", _revisit_mdo_stack->length()); + } + for (int i = 0; i < _revisit_mdo_stack->length(); i++) { + _revisit_mdo_stack->at(i)->follow_weak_refs(&is_alive); + } + follow_stack(); +} + MarkSweep::FollowRootClosure MarkSweep::follow_root_closure; +CodeBlobToOopClosure MarkSweep::follow_code_root_closure(&MarkSweep::follow_root_closure, /*do_marking=*/ true); void MarkSweep::FollowRootClosure::do_oop(oop* p) { follow_root(p); } void MarkSweep::FollowRootClosure::do_oop(narrowOop* p) { follow_root(p); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_implementation/shared/markSweep.hpp --- a/src/share/vm/gc_implementation/shared/markSweep.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_implementation/shared/markSweep.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -23,6 +23,7 @@ */ class ReferenceProcessor; +class DataLayout; // MarkSweep takes care of global mark-compact garbage collection for a // GenCollectedHeap using a four-phase pointer forwarding algorithm. All @@ -57,14 +58,14 @@ public: virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); - virtual const bool do_nmethods() const { return true; } }; class MarkAndPushClosure: public OopClosure { public: virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); - virtual const bool do_nmethods() const { return true; } + virtual const bool should_remember_mdo() const { return true; } + virtual void remember_mdo(DataLayout* p) { MarkSweep::revisit_mdo(p); } }; class FollowStackClosure: public VoidClosure { @@ -103,6 +104,7 @@ friend class KeepAliveClosure; friend class VM_MarkSweep; friend void marksweep_init(); + friend class DataLayout; // // Vars @@ -112,6 +114,8 @@ static GrowableArray* _marking_stack; // Stack for live klasses to revisit at end of marking phase static GrowableArray* _revisit_klass_stack; + // Set (stack) of MDO's to revisit at end of marking phase + static GrowableArray* _revisit_mdo_stack; // Space for storing/restoring mark word static GrowableArray* _preserved_mark_stack; @@ -157,12 +161,17 @@ // Class unloading. Update subklass/sibling/implementor links at end of marking phase. static void follow_weak_klass_links(); + // Class unloading. Clear weak refs in MDO's (ProfileData) + // at the end of the marking phase. + static void follow_mdo_weak_refs(); + // Debugging static void trace(const char* msg) PRODUCT_RETURN; public: // Public closures static FollowRootClosure follow_root_closure; + static CodeBlobToOopClosure follow_code_root_closure; // => follow_root_closure static MarkAndPushClosure mark_and_push_closure; static FollowStackClosure follow_stack_closure; static AdjustPointerClosure adjust_root_pointer_closure; @@ -213,7 +222,10 @@ #endif // Call backs for class unloading - static void revisit_weak_klass_link(Klass* k); // Update subklass/sibling/implementor links at end of marking. + // Update subklass/sibling/implementor links at end of marking. + static void revisit_weak_klass_link(Klass* k); + // For weak refs clearing in MDO's + static void revisit_mdo(DataLayout* p); }; class PreservedMark VALUE_OBJ_CLASS_SPEC { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_interface/collectedHeap.cpp --- a/src/share/vm/gc_interface/collectedHeap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_interface/collectedHeap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -59,8 +59,19 @@ PerfDataManager::create_string_variable(SUN_GC, "lastCause", 80, GCCause::to_string(_gc_lastcause), CHECK); } + _defer_initial_card_mark = false; // strengthened by subclass in pre_initialize() below. } +void CollectedHeap::pre_initialize() { + // Used for ReduceInitialCardMarks (when COMPILER2 is used); + // otherwise remains unused. +#ifdef COMPLER2 + _defer_initial_card_mark = ReduceInitialCardMarks && can_elide_tlab_store_barriers() + && (DeferInitialCardMark || card_mark_must_follow_store()); +#else + assert(_defer_initial_card_mark == false, "Who would set it?"); +#endif +} #ifndef PRODUCT void CollectedHeap::check_for_bad_heap_word_value(HeapWord* addr, size_t size) { @@ -137,6 +148,96 @@ return obj; } +void CollectedHeap::flush_deferred_store_barrier(JavaThread* thread) { + MemRegion deferred = thread->deferred_card_mark(); + if (!deferred.is_empty()) { + assert(_defer_initial_card_mark, "Otherwise should be empty"); + { + // Verify that the storage points to a parsable object in heap + DEBUG_ONLY(oop old_obj = oop(deferred.start());) + assert(is_in(old_obj), "Not in allocated heap"); + assert(!can_elide_initializing_store_barrier(old_obj), + "Else should have been filtered in new_store_pre_barrier()"); + assert(!is_in_permanent(old_obj), "Sanity: not expected"); + assert(old_obj->is_oop(true), "Not an oop"); + assert(old_obj->is_parsable(), "Will not be concurrently parsable"); + assert(deferred.word_size() == (size_t)(old_obj->size()), + "Mismatch: multiple objects?"); + } + BarrierSet* bs = barrier_set(); + assert(bs->has_write_region_opt(), "No write_region() on BarrierSet"); + bs->write_region(deferred); + // "Clear" the deferred_card_mark field + thread->set_deferred_card_mark(MemRegion()); + } + assert(thread->deferred_card_mark().is_empty(), "invariant"); +} + +// Helper for ReduceInitialCardMarks. For performance, +// compiled code may elide card-marks for initializing stores +// to a newly allocated object along the fast-path. We +// compensate for such elided card-marks as follows: +// (a) Generational, non-concurrent collectors, such as +// GenCollectedHeap(ParNew,DefNew,Tenured) and +// ParallelScavengeHeap(ParallelGC, ParallelOldGC) +// need the card-mark if and only if the region is +// in the old gen, and do not care if the card-mark +// succeeds or precedes the initializing stores themselves, +// so long as the card-mark is completed before the next +// scavenge. For all these cases, we can do a card mark +// at the point at which we do a slow path allocation +// in the old gen, i.e. in this call. +// (b) GenCollectedHeap(ConcurrentMarkSweepGeneration) requires +// in addition that the card-mark for an old gen allocated +// object strictly follow any associated initializing stores. +// In these cases, the memRegion remembered below is +// used to card-mark the entire region either just before the next +// slow-path allocation by this thread or just before the next scavenge or +// CMS-associated safepoint, whichever of these events happens first. +// (The implicit assumption is that the object has been fully +// initialized by this point, a fact that we assert when doing the +// card-mark.) +// (c) G1CollectedHeap(G1) uses two kinds of write barriers. When a +// G1 concurrent marking is in progress an SATB (pre-write-)barrier is +// is used to remember the pre-value of any store. Initializing +// stores will not need this barrier, so we need not worry about +// compensating for the missing pre-barrier here. Turning now +// to the post-barrier, we note that G1 needs a RS update barrier +// which simply enqueues a (sequence of) dirty cards which may +// optionally be refined by the concurrent update threads. Note +// that this barrier need only be applied to a non-young write, +// but, like in CMS, because of the presence of concurrent refinement +// (much like CMS' precleaning), must strictly follow the oop-store. +// Thus, using the same protocol for maintaining the intended +// invariants turns out, serendepitously, to be the same for both +// G1 and CMS. +// +// For any future collector, this code should be reexamined with +// that specific collector in mind, and the documentation above suitably +// extended and updated. +oop CollectedHeap::new_store_pre_barrier(JavaThread* thread, oop new_obj) { + // If a previous card-mark was deferred, flush it now. + flush_deferred_store_barrier(thread); + if (can_elide_initializing_store_barrier(new_obj)) { + // The deferred_card_mark region should be empty + // following the flush above. + assert(thread->deferred_card_mark().is_empty(), "Error"); + } else { + MemRegion mr((HeapWord*)new_obj, new_obj->size()); + assert(!mr.is_empty(), "Error"); + if (_defer_initial_card_mark) { + // Defer the card mark + thread->set_deferred_card_mark(mr); + } else { + // Do the card mark + BarrierSet* bs = barrier_set(); + assert(bs->has_write_region_opt(), "No write_region() on BarrierSet"); + bs->write_region(mr); + } + } + return new_obj; +} + size_t CollectedHeap::filler_array_hdr_size() { return size_t(arrayOopDesc::header_size(T_INT)); } @@ -158,9 +259,9 @@ assert(Universe::heap()->is_in_reserved(start + words - 1), "not in heap"); } -void CollectedHeap::zap_filler_array(HeapWord* start, size_t words) +void CollectedHeap::zap_filler_array(HeapWord* start, size_t words, bool zap) { - if (ZapFillerObjects) { + if (ZapFillerObjects && zap) { Copy::fill_to_words(start + filler_array_hdr_size(), words - filler_array_hdr_size(), 0XDEAFBABE); } @@ -168,7 +269,7 @@ #endif // ASSERT void -CollectedHeap::fill_with_array(HeapWord* start, size_t words) +CollectedHeap::fill_with_array(HeapWord* start, size_t words, bool zap) { assert(words >= filler_array_min_size(), "too small for an array"); assert(words <= filler_array_max_size(), "too big for a single object"); @@ -179,31 +280,31 @@ // Set the length first for concurrent GC. ((arrayOop)start)->set_length((int)len); post_allocation_setup_common(Universe::intArrayKlassObj(), start, words); - DEBUG_ONLY(zap_filler_array(start, words);) + DEBUG_ONLY(zap_filler_array(start, words, zap);) } void -CollectedHeap::fill_with_object_impl(HeapWord* start, size_t words) +CollectedHeap::fill_with_object_impl(HeapWord* start, size_t words, bool zap) { assert(words <= filler_array_max_size(), "too big for a single object"); if (words >= filler_array_min_size()) { - fill_with_array(start, words); + fill_with_array(start, words, zap); } else if (words > 0) { assert(words == min_fill_size(), "unaligned size"); - post_allocation_setup_common(SystemDictionary::object_klass(), start, + post_allocation_setup_common(SystemDictionary::Object_klass(), start, words); } } -void CollectedHeap::fill_with_object(HeapWord* start, size_t words) +void CollectedHeap::fill_with_object(HeapWord* start, size_t words, bool zap) { DEBUG_ONLY(fill_args_check(start, words);) HandleMark hm; // Free handles before leaving. - fill_with_object_impl(start, words); + fill_with_object_impl(start, words, zap); } -void CollectedHeap::fill_with_objects(HeapWord* start, size_t words) +void CollectedHeap::fill_with_objects(HeapWord* start, size_t words, bool zap) { DEBUG_ONLY(fill_args_check(start, words);) HandleMark hm; // Free handles before leaving. @@ -216,23 +317,13 @@ const size_t max = filler_array_max_size(); while (words > max) { const size_t cur = words - max >= min ? max : max - min; - fill_with_array(start, cur); + fill_with_array(start, cur, zap); start += cur; words -= cur; } #endif - fill_with_object_impl(start, words); -} - -oop CollectedHeap::new_store_barrier(oop new_obj) { - // %%% This needs refactoring. (It was imported from the server compiler.) - guarantee(can_elide_tlab_store_barriers(), "store barrier elision not supported"); - BarrierSet* bs = this->barrier_set(); - assert(bs->has_write_region_opt(), "Barrier set does not have write_region"); - int new_size = new_obj->size(); - bs->write_region(MemRegion((HeapWord*)new_obj, new_size)); - return new_obj; + fill_with_object_impl(start, words, zap); } HeapWord* CollectedHeap::allocate_new_tlab(size_t size) { @@ -240,22 +331,6 @@ return NULL; } -void CollectedHeap::fill_all_tlabs(bool retire) { - assert(UseTLAB, "should not reach here"); - // See note in ensure_parsability() below. - assert(SafepointSynchronize::is_at_safepoint() || - !is_init_completed(), - "should only fill tlabs at safepoint"); - // The main thread starts allocating via a TLAB even before it - // has added itself to the threads list at vm boot-up. - assert(Threads::first() != NULL, - "Attempt to fill tlabs before main thread has been added" - " to threads list is doomed to failure!"); - for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { - thread->tlab().make_parsable(retire); - } -} - void CollectedHeap::ensure_parsability(bool retire_tlabs) { // The second disjunct in the assertion below makes a concession // for the start-up verification done while the VM is being @@ -270,8 +345,24 @@ "Should only be called at a safepoint or at start-up" " otherwise concurrent mutator activity may make heap " " unparsable again"); - if (UseTLAB) { - fill_all_tlabs(retire_tlabs); + const bool use_tlab = UseTLAB; + const bool deferred = _defer_initial_card_mark; + // The main thread starts allocating via a TLAB even before it + // has added itself to the threads list at vm boot-up. + assert(!use_tlab || Threads::first() != NULL, + "Attempt to fill tlabs before main thread has been added" + " to threads list is doomed to failure!"); + for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + if (use_tlab) thread->tlab().make_parsable(retire_tlabs); +#ifdef COMPILER2 + // The deferred store barriers must all have been flushed to the + // card-table (or other remembered set structure) before GC starts + // processing the card-table (or other remembered set). + if (deferred) flush_deferred_store_barrier(thread); +#else + assert(!deferred, "Should be false"); + assert(thread->deferred_card_mark().is_empty(), "Should be empty"); +#endif } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/gc_interface/collectedHeap.hpp --- a/src/share/vm/gc_interface/collectedHeap.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/gc_interface/collectedHeap.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -51,6 +51,9 @@ // Used for filler objects (static, but initialized in ctor). static size_t _filler_array_max_size; + // Used in support of ReduceInitialCardMarks; only consulted if COMPILER2 is being used + bool _defer_initial_card_mark; + protected: MemRegion _reserved; BarrierSet* _barrier_set; @@ -70,13 +73,16 @@ // Constructor CollectedHeap(); + // Do common initializations that must follow instance construction, + // for example, those needing virtual calls. + // This code could perhaps be moved into initialize() but would + // be slightly more awkward because we want the latter to be a + // pure virtual. + void pre_initialize(); + // Create a new tlab virtual HeapWord* allocate_new_tlab(size_t size); - // Fix up tlabs to make the heap well-formed again, - // optionally retiring the tlabs. - virtual void fill_all_tlabs(bool retire); - // Accumulate statistics on all tlabs. virtual void accumulate_statistics_all_tlabs(); @@ -127,14 +133,14 @@ static inline size_t filler_array_max_size(); DEBUG_ONLY(static void fill_args_check(HeapWord* start, size_t words);) - DEBUG_ONLY(static void zap_filler_array(HeapWord* start, size_t words);) + DEBUG_ONLY(static void zap_filler_array(HeapWord* start, size_t words, bool zap = true);) // Fill with a single array; caller must ensure filler_array_min_size() <= // words <= filler_array_max_size(). - static inline void fill_with_array(HeapWord* start, size_t words); + static inline void fill_with_array(HeapWord* start, size_t words, bool zap = true); // Fill with a single object (either an int array or a java.lang.Object). - static inline void fill_with_object_impl(HeapWord* start, size_t words); + static inline void fill_with_object_impl(HeapWord* start, size_t words, bool zap = true); // Verification functions virtual void check_for_bad_heap_word_value(HeapWord* addr, size_t size) @@ -239,6 +245,9 @@ return p == NULL || is_in_closed_subset(p); } + // XXX is_permanent() and is_in_permanent() should be better named + // to distinguish one from the other. + // Returns "TRUE" if "p" is allocated as "permanent" data. // If the heap does not use "permanent" data, returns the same // value is_in_reserved() would return. @@ -247,13 +256,25 @@ // space). If you need the more conservative answer use is_permanent(). virtual bool is_in_permanent(const void *p) const = 0; + bool is_in_permanent_or_null(const void *p) const { + return p == NULL || is_in_permanent(p); + } + // Returns "TRUE" if "p" is in the committed area of "permanent" data. // If the heap does not use "permanent" data, returns the same // value is_in() would return. virtual bool is_permanent(const void *p) const = 0; - bool is_in_permanent_or_null(const void *p) const { - return p == NULL || is_in_permanent(p); + bool is_permanent_or_null(const void *p) const { + return p == NULL || is_permanent(p); + } + + // An object is scavengable if its location may move during a scavenge. + // (A scavenge is a GC which is not a full GC.) + // Currently, this just means it is not perm (and not null). + // This could change if we rethink what's in perm-gen. + bool is_scavengable(const void *p) const { + return !is_in_permanent_or_null(p); } // Returns "TRUE" if "p" is a method oop in the @@ -323,14 +344,14 @@ return size_t(align_object_size(oopDesc::header_size())); } - static void fill_with_objects(HeapWord* start, size_t words); + static void fill_with_objects(HeapWord* start, size_t words, bool zap = true); - static void fill_with_object(HeapWord* start, size_t words); - static void fill_with_object(MemRegion region) { - fill_with_object(region.start(), region.word_size()); + static void fill_with_object(HeapWord* start, size_t words, bool zap = true); + static void fill_with_object(MemRegion region, bool zap = true) { + fill_with_object(region.start(), region.word_size(), zap); } - static void fill_with_object(HeapWord* start, HeapWord* end) { - fill_with_object(start, pointer_delta(end, start)); + static void fill_with_object(HeapWord* start, HeapWord* end, bool zap = true) { + fill_with_object(start, pointer_delta(end, start), zap); } // Some heaps may offer a contiguous region for shared non-blocking @@ -400,9 +421,14 @@ guarantee(false, "thread-local allocation buffers not supported"); return 0; } + // Can a compiler initialize a new object without store barriers? // This permission only extends from the creation of a new object - // via a TLAB up to the first subsequent safepoint. + // via a TLAB up to the first subsequent safepoint. If such permission + // is granted for this heap type, the compiler promises to call + // defer_store_barrier() below on any slow path allocation of + // a new object for which such initializing store barriers will + // have been elided. virtual bool can_elide_tlab_store_barriers() const = 0; // If a compiler is eliding store barriers for TLAB-allocated objects, @@ -410,8 +436,30 @@ // an object allocated anywhere. The compiler's runtime support // promises to call this function on such a slow-path-allocated // object before performing initializations that have elided - // store barriers. Returns new_obj, or maybe a safer copy thereof. - virtual oop new_store_barrier(oop new_obj); + // store barriers. Returns new_obj, or maybe a safer copy thereof. + virtual oop new_store_pre_barrier(JavaThread* thread, oop new_obj); + + // Answers whether an initializing store to a new object currently + // allocated at the given address doesn't need a store + // barrier. Returns "true" if it doesn't need an initializing + // store barrier; answers "false" if it does. + virtual bool can_elide_initializing_store_barrier(oop new_obj) = 0; + + // If a compiler is eliding store barriers for TLAB-allocated objects, + // we will be informed of a slow-path allocation by a call + // to new_store_pre_barrier() above. Such a call precedes the + // initialization of the object itself, and no post-store-barriers will + // be issued. Some heap types require that the barrier strictly follows + // the initializing stores. (This is currently implemented by deferring the + // barrier until the next slow-path allocation or gc-related safepoint.) + // This interface answers whether a particular heap type needs the card + // mark to be thus strictly sequenced after the stores. + virtual bool card_mark_must_follow_store() const = 0; + + // If the CollectedHeap was asked to defer a store barrier above, + // this informs it to flush such a deferred store barrier to the + // remembered set. + virtual void flush_deferred_store_barrier(JavaThread* thread); // Can a compiler elide a store barrier when it writes // a permanent oop into the heap? Applies when the compiler diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/includeDB_compiler1 --- a/src/share/vm/includeDB_compiler1 Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/includeDB_compiler1 Thu Mar 25 16:54:59 2010 -0700 @@ -409,8 +409,6 @@ compileBroker.cpp c1_Compiler.hpp -frame.hpp c1_Defs.hpp - frame_.cpp c1_Runtime1.hpp globals.cpp c1_globals.hpp @@ -433,8 +431,6 @@ os_.cpp c1_Runtime1.hpp -registerMap.hpp c1_Defs.hpp - safepoint.cpp c1_globals.hpp sharedRuntime.cpp c1_Runtime1.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/includeDB_compiler2 --- a/src/share/vm/includeDB_compiler2 Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/includeDB_compiler2 Thu Mar 25 16:54:59 2010 -0700 @@ -149,11 +149,15 @@ c2compiler.hpp abstractCompiler.hpp callGenerator.cpp addnode.hpp +callGenerator.cpp bcEscapeAnalyzer.hpp callGenerator.cpp callGenerator.hpp callGenerator.cpp callnode.hpp callGenerator.cpp cfgnode.hpp callGenerator.cpp compileLog.hpp callGenerator.cpp connode.hpp +callGenerator.cpp ciCPCache.hpp +callGenerator.cpp ciMethodHandle.hpp +callGenerator.cpp javaClasses.hpp callGenerator.cpp parse.hpp callGenerator.cpp rootnode.hpp callGenerator.cpp runtime.hpp @@ -321,6 +325,7 @@ compile.cpp rootnode.hpp compile.cpp runtime.hpp compile.cpp signature.hpp +compile.cpp stringopts.hpp compile.cpp stubRoutines.hpp compile.cpp systemDictionary.hpp compile.cpp timer.hpp @@ -389,6 +394,9 @@ doCall.cpp addnode.hpp doCall.cpp callGenerator.hpp +doCall.cpp ciCallSite.hpp +doCall.cpp ciCPCache.hpp +doCall.cpp ciMethodHandle.hpp doCall.cpp cfgnode.hpp doCall.cpp compileLog.hpp doCall.cpp linkResolver.hpp @@ -476,12 +484,16 @@ graphKit.cpp runtime.hpp graphKit.cpp sharedRuntime.hpp +graphKit.hpp addnode.hpp graphKit.hpp callnode.hpp graphKit.hpp cfgnode.hpp graphKit.hpp ciEnv.hpp +graphKit.hpp divnode.hpp graphKit.hpp compile.hpp graphKit.hpp deoptimization.hpp graphKit.hpp phaseX.hpp +graphKit.hpp mulnode.hpp +graphKit.hpp subnode.hpp graphKit.hpp type.hpp idealKit.cpp addnode.hpp @@ -490,7 +502,10 @@ idealKit.cpp idealKit.hpp idealKit.cpp runtime.hpp +idealKit.hpp addnode.hpp +idealKit.hpp cfgnode.hpp idealKit.hpp connode.hpp +idealKit.hpp divnode.hpp idealKit.hpp mulnode.hpp idealKit.hpp phaseX.hpp idealKit.hpp subnode.hpp @@ -586,6 +601,7 @@ loopTransform.cpp addnode.hpp loopTransform.cpp allocation.inline.hpp +loopTransform.cpp callnode.hpp loopTransform.cpp connode.hpp loopTransform.cpp compileLog.hpp loopTransform.cpp divnode.hpp @@ -641,6 +657,7 @@ macro.cpp callnode.hpp macro.cpp cfgnode.hpp macro.cpp compile.hpp +macro.cpp compileLog.hpp macro.cpp connode.hpp macro.cpp locknode.hpp macro.cpp loopnode.hpp @@ -758,6 +775,7 @@ output.cpp assembler.inline.hpp output.cpp callnode.hpp output.cpp cfgnode.hpp +output.cpp compileBroker.hpp output.cpp debugInfo.hpp output.cpp debugInfoRec.hpp output.cpp handles.inline.hpp @@ -993,6 +1011,21 @@ split_if.cpp connode.hpp split_if.cpp loopnode.hpp +stringopts.hpp phaseX.hpp +stringopts.hpp node.hpp + +stringopts.cpp addnode.hpp +stringopts.cpp callnode.hpp +stringopts.cpp callGenerator.hpp +stringopts.cpp compileLog.hpp +stringopts.cpp divnode.hpp +stringopts.cpp idealKit.hpp +stringopts.cpp graphKit.hpp +stringopts.cpp rootnode.hpp +stringopts.cpp runtime.hpp +stringopts.cpp subnode.hpp +stringopts.cpp stringopts.hpp + stubGenerator_.cpp runtime.hpp stubRoutines.cpp runtime.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/includeDB_core --- a/src/share/vm/includeDB_core Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/includeDB_core Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ // -// Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. +// Copyright 1997-2010 Sun Microsystems, 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 @@ -175,6 +175,8 @@ arguments.cpp management.hpp arguments.cpp oop.inline.hpp arguments.cpp os_.inline.hpp +arguments.cpp referenceProcessor.hpp +arguments.cpp taskqueue.hpp arguments.cpp universe.inline.hpp arguments.cpp vm_version_.hpp @@ -289,7 +291,7 @@ attachListener.hpp debug.hpp attachListener.hpp ostream.hpp -barrierSet.cpp barrierSet.hpp +barrierSet.cpp barrierSet.inline.hpp barrierSet.cpp collectedHeap.hpp barrierSet.cpp universe.hpp @@ -516,6 +518,11 @@ ciCallProfile.hpp ciClassList.hpp +ciCallSite.cpp ciCallSite.hpp +ciCallSite.cpp ciUtilities.hpp + +ciCallSite.hpp ciInstance.hpp + ciConstant.cpp allocation.hpp ciConstant.cpp allocation.inline.hpp ciConstant.cpp ciConstant.hpp @@ -532,6 +539,12 @@ ciConstantPoolCache.hpp growableArray.hpp ciConstantPoolCache.hpp resourceArea.hpp +ciCPCache.cpp cpCacheOop.hpp +ciCPCache.cpp ciCPCache.hpp + +ciCPCache.hpp ciClassList.hpp +ciCPCache.hpp ciObject.hpp + ciEnv.cpp allocation.inline.hpp ciEnv.cpp ciConstant.hpp ciEnv.cpp ciEnv.hpp @@ -570,6 +583,7 @@ ciEnv.hpp dependencies.hpp ciEnv.hpp exceptionHandlerTable.hpp ciEnv.hpp oopMap.hpp +ciEnv.hpp systemDictionary.hpp ciEnv.hpp thread.hpp ciExceptionHandler.cpp ciExceptionHandler.hpp @@ -592,6 +606,7 @@ ciField.hpp ciClassList.hpp ciField.hpp ciConstant.hpp ciField.hpp ciFlags.hpp +ciField.hpp ciInstance.hpp ciFlags.cpp ciFlags.hpp @@ -678,6 +693,7 @@ ciMethod.hpp ciInstanceKlass.hpp ciMethod.hpp ciObject.hpp ciMethod.hpp ciSignature.hpp +ciMethod.hpp methodHandles.hpp ciMethod.hpp methodLiveness.hpp ciMethodBlocks.cpp bytecode.hpp @@ -709,6 +725,15 @@ ciMethodKlass.hpp ciKlass.hpp ciMethodKlass.hpp ciSymbol.hpp +ciMethodHandle.cpp ciClassList.hpp +ciMethodHandle.cpp ciInstance.hpp +ciMethodHandle.cpp ciMethodHandle.hpp +ciMethodHandle.cpp ciUtilities.hpp +ciMethodHandle.cpp methodHandles.hpp +ciMethodHandle.cpp methodHandleWalk.hpp + +ciMethodHandle.hpp methodHandles.hpp + ciNullObject.cpp ciNullObject.hpp ciNullObject.hpp ciClassList.hpp @@ -754,11 +779,14 @@ ciObject.hpp jniHandles.hpp ciObjectFactory.cpp allocation.inline.hpp +ciObjectFactory.cpp ciCallSite.hpp +ciObjectFactory.cpp ciCPCache.hpp ciObjectFactory.cpp ciInstance.hpp ciObjectFactory.cpp ciInstanceKlass.hpp ciObjectFactory.cpp ciInstanceKlassKlass.hpp ciObjectFactory.cpp ciMethod.hpp ciObjectFactory.cpp ciMethodData.hpp +ciObjectFactory.cpp ciMethodHandle.hpp ciObjectFactory.cpp ciMethodKlass.hpp ciObjectFactory.cpp ciNullObject.hpp ciObjectFactory.cpp ciObjArray.hpp @@ -792,6 +820,7 @@ ciSignature.hpp globalDefinitions.hpp ciSignature.hpp growableArray.hpp +ciStreams.cpp ciCallSite.hpp ciStreams.cpp ciConstant.hpp ciStreams.cpp ciField.hpp ciStreams.cpp ciStreams.hpp @@ -894,6 +923,7 @@ classLoader.cpp allocation.inline.hpp classLoader.cpp arguments.hpp +classLoader.cpp bytecodeStream.hpp classLoader.cpp classFileParser.hpp classLoader.cpp classFileStream.hpp classLoader.cpp classLoader.hpp @@ -921,6 +951,7 @@ classLoader.cpp management.hpp classLoader.cpp oop.inline.hpp classLoader.cpp oopFactory.hpp +classLoader.cpp oopMapCache.hpp classLoader.cpp os_.inline.hpp classLoader.cpp symbolOop.hpp classLoader.cpp systemDictionary.hpp @@ -991,6 +1022,7 @@ codeCache.cpp codeCache.hpp codeCache.cpp dependencies.hpp codeCache.cpp gcLocker.hpp +codeCache.cpp handles.inline.hpp codeCache.cpp icache.hpp codeCache.cpp iterator.hpp codeCache.cpp java.hpp @@ -1003,6 +1035,7 @@ codeCache.cpp oop.inline.hpp codeCache.cpp pcDesc.hpp codeCache.cpp resourceArea.hpp +codeCache.cpp xmlstream.hpp codeCache.hpp allocation.hpp codeCache.hpp codeBlob.hpp @@ -1091,6 +1124,7 @@ compileBroker.cpp oop.inline.hpp compileBroker.cpp os.hpp compileBroker.cpp sharedRuntime.hpp +compileBroker.cpp sweeper.hpp compileBroker.cpp systemDictionary.hpp compileBroker.cpp vmSymbols.hpp @@ -1291,6 +1325,7 @@ cpCacheOop.cpp markSweep.inline.hpp cpCacheOop.cpp objArrayOop.hpp cpCacheOop.cpp oop.inline.hpp +cpCacheOop.cpp rewriter.hpp cpCacheOop.cpp universe.inline.hpp cpCacheOop.hpp allocation.hpp @@ -1451,6 +1486,7 @@ deoptimization.cpp vframe.hpp deoptimization.cpp vframeArray.hpp deoptimization.cpp vframe_hp.hpp +deoptimization.cpp vmreg_.inline.hpp deoptimization.cpp xmlstream.hpp deoptimization.hpp allocation.hpp @@ -1497,6 +1533,7 @@ disassembler.cpp fprofiler.hpp disassembler.cpp handles.inline.hpp disassembler.cpp hpi.hpp +disassembler.cpp javaClasses.hpp disassembler.cpp stubCodeGenerator.hpp disassembler.cpp stubRoutines.hpp @@ -1987,6 +2024,7 @@ instanceKlass.cpp collectedHeap.inline.hpp instanceKlass.cpp compileBroker.hpp +instanceKlass.cpp dtrace.hpp instanceKlass.cpp fieldDescriptor.hpp instanceKlass.cpp genOopClosures.inline.hpp instanceKlass.cpp handles.inline.hpp @@ -2446,6 +2484,7 @@ jvm.cpp collectedHeap.inline.hpp jvm.cpp copy.hpp jvm.cpp defaultStream.hpp +jvm.cpp dtrace.hpp jvm.cpp dtraceJSDT.hpp jvm.cpp events.hpp jvm.cpp handles.inline.hpp @@ -2501,6 +2540,7 @@ jvmtiExport.hpp handles.hpp jvmtiExport.hpp iterator.hpp jvmtiExport.hpp jvmti.h +jvmtiExport.hpp jvmticmlr.h jvmtiExport.hpp oop.hpp jvmtiExport.hpp oopsHierarchy.hpp @@ -2619,6 +2659,7 @@ loaderConstraints.cpp safepoint.hpp loaderConstraints.hpp dictionary.hpp +loaderConstraints.hpp placeholders.hpp loaderConstraints.hpp hashtable.hpp location.cpp debugInfo.hpp @@ -2684,6 +2725,7 @@ markOop.inline.hpp markOop.hpp markSweep.cpp compileBroker.hpp +markSweep.cpp methodDataOop.hpp markSweep.hpp collectedHeap.hpp @@ -2811,6 +2853,12 @@ methodDataOop.hpp orderAccess.hpp methodDataOop.hpp universe.hpp +methodHandleWalk.hpp methodHandles.hpp + +methodHandleWalk.cpp methodHandleWalk.hpp +methodHandleWalk.cpp oopFactory.hpp +methodHandleWalk.cpp rewriter.hpp + methodHandles.hpp frame.inline.hpp methodHandles.hpp globals.hpp methodHandles.hpp interfaceSupport.hpp @@ -3468,6 +3516,7 @@ reflection.cpp javaClasses.hpp reflection.cpp jvm.h reflection.cpp linkResolver.hpp +reflection.cpp methodHandleWalk.hpp reflection.cpp objArrayKlass.hpp reflection.cpp objArrayOop.hpp reflection.cpp oopFactory.hpp @@ -3679,6 +3728,7 @@ sharedRuntime.cpp abstractCompiler.hpp sharedRuntime.cpp arguments.hpp sharedRuntime.cpp biasedLocking.hpp +sharedRuntime.cpp compileBroker.hpp sharedRuntime.cpp compiledIC.hpp sharedRuntime.cpp compilerOracle.hpp sharedRuntime.cpp copy.hpp @@ -3687,6 +3737,7 @@ sharedRuntime.cpp forte.hpp sharedRuntime.cpp gcLocker.inline.hpp sharedRuntime.cpp handles.inline.hpp +sharedRuntime.cpp hashtable.inline.hpp sharedRuntime.cpp init.hpp sharedRuntime.cpp interfaceSupport.hpp sharedRuntime.cpp interpreterRuntime.hpp @@ -3714,6 +3765,7 @@ sharedRuntime.hpp allocation.hpp sharedRuntime.hpp bytecodeHistogram.hpp sharedRuntime.hpp bytecodeTracer.hpp +sharedRuntime.hpp hashtable.hpp sharedRuntime.hpp linkResolver.hpp sharedRuntime.hpp resourceArea.hpp sharedRuntime.hpp threadLocalStorage.hpp @@ -3931,6 +3983,7 @@ sweeper.cpp atomic.hpp sweeper.cpp codeCache.hpp +sweeper.cpp compileBroker.hpp sweeper.cpp events.hpp sweeper.cpp methodOop.hpp sweeper.cpp mutexLocker.hpp @@ -3938,6 +3991,8 @@ sweeper.cpp os.hpp sweeper.cpp resourceArea.hpp sweeper.cpp sweeper.hpp +sweeper.cpp vm_operations.hpp +sweeper.cpp xmlstream.hpp symbolKlass.cpp gcLocker.hpp symbolKlass.cpp handles.inline.hpp @@ -4396,6 +4451,7 @@ unsafe.cpp allocation.inline.hpp unsafe.cpp copy.hpp +unsafe.cpp dtrace.hpp unsafe.cpp globals.hpp unsafe.cpp interfaceSupport.hpp unsafe.cpp jni.h @@ -4567,6 +4623,7 @@ vmThread.cpp collectedHeap.hpp vmThread.cpp compileBroker.hpp +vmThread.cpp dtrace.hpp vmThread.cpp events.hpp vmThread.cpp interfaceSupport.hpp vmThread.cpp methodOop.hpp @@ -4591,6 +4648,7 @@ vm_operations.cpp interfaceSupport.hpp vm_operations.cpp isGCActiveMark.hpp vm_operations.cpp resourceArea.hpp +vm_operations.cpp sweeper.hpp vm_operations.cpp threadService.hpp vm_operations.cpp thread_.inline.hpp vm_operations.cpp vmSymbols.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/includeDB_gc_parallel --- a/src/share/vm/includeDB_gc_parallel Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/includeDB_gc_parallel Thu Mar 25 16:54:59 2010 -0700 @@ -21,6 +21,8 @@ // have any questions. // +arguments.cpp compactibleFreeListSpace.hpp + assembler_.cpp g1SATBCardTableModRefBS.hpp assembler_.cpp g1CollectedHeap.inline.hpp assembler_.cpp heapRegion.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/includeDB_zero --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/includeDB_zero Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,55 @@ +// +// Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved. +// Copyright 2009 Red Hat, Inc. +// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +// +// This code is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License version 2 only, as +// published by the Free Software Foundation. +// +// This code is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// version 2 for more details (a copy is included in the LICENSE file that +// accompanied this code). +// +// You should have received a copy of the GNU General Public License version +// 2 along with this work; if not, write to the Free Software Foundation, +// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +// CA 95054 USA or visit www.sun.com if you need additional information or +// have any questions. +// +// + +// NOTE: DO NOT CHANGE THIS COPYRIGHT TO NEW STYLE - IT WILL BREAK makeDeps! + +entryFrame_.hpp javaCalls.hpp +entryFrame_.hpp stack_.hpp + +fakeStubFrame_.hpp stack_.hpp + +frame.hpp stack_.hpp + +frame.inline.hpp fakeStubFrame_.hpp +frame.inline.hpp entryFrame_.hpp +frame.inline.hpp interpreterFrame_.hpp +frame.inline.hpp sharkFrame_.hpp + +frame_.cpp interpreterRuntime.hpp +frame_.cpp scopeDesc.hpp + +interpreter.hpp entry_.hpp + +interpreterFrame_.hpp bytecodeInterpreter.hpp +interpreterFrame_.hpp methodOop.hpp +interpreterFrame_.hpp stack_.hpp +interpreterFrame_.hpp thread.hpp + +sharkFrame_.hpp methodOop.hpp +sharkFrame_.hpp stack_.hpp + +stack_.hpp sizes.hpp + +thread.hpp stack_.hpp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/abstractInterpreter.hpp --- a/src/share/vm/interpreter/abstractInterpreter.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/abstractInterpreter.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -109,6 +109,8 @@ static void print_method_kind(MethodKind kind) PRODUCT_RETURN; + static bool can_be_compiled(methodHandle m); + // Runtime support // length = invoke bytecode length (to advance to next bytecode) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/bytecode.cpp --- a/src/share/vm/interpreter/bytecode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/bytecode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -102,7 +102,9 @@ KlassHandle resolved_klass; constantPoolHandle constants(THREAD, _method->constants()); - if (adjusted_invoke_code() != Bytecodes::_invokeinterface) { + if (adjusted_invoke_code() == Bytecodes::_invokedynamic) { + LinkResolver::resolve_dynamic_method(m, resolved_klass, constants, index(), CHECK_(methodHandle())); + } else if (adjusted_invoke_code() != Bytecodes::_invokeinterface) { LinkResolver::resolve_method(m, resolved_klass, constants, index(), CHECK_(methodHandle())); } else { LinkResolver::resolve_interface_method(m, resolved_klass, constants, index(), CHECK_(methodHandle())); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/bytecode.hpp --- a/src/share/vm/interpreter/bytecode.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/bytecode.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -205,12 +205,14 @@ bool is_invokespecial() const { return adjusted_invoke_code() == Bytecodes::_invokespecial; } bool is_invokedynamic() const { return adjusted_invoke_code() == Bytecodes::_invokedynamic; } + bool has_receiver() const { return !is_invokestatic() && !is_invokedynamic(); } bool has_giant_index() const { return is_invokedynamic(); } bool is_valid() const { return is_invokeinterface() || is_invokevirtual() || is_invokestatic() || - is_invokespecial(); } + is_invokespecial() || + is_invokedynamic(); } // Creation inline friend Bytecode_invoke* Bytecode_invoke_at(methodHandle method, int bci); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/bytecodeInterpreter.cpp --- a/src/share/vm/interpreter/bytecodeInterpreter.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/bytecodeInterpreter.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -281,7 +281,7 @@ #define DO_BACKEDGE_CHECKS(skip, branch_pc) \ if ((skip) <= 0) { \ - if (UseCompiler && UseLoopCounter) { \ + if (UseLoopCounter) { \ bool do_OSR = UseOnStackReplacement; \ BACKEDGE_COUNT->increment(); \ if (do_OSR) do_OSR = BACKEDGE_COUNT->reached_InvocationLimit(); \ @@ -289,16 +289,12 @@ nmethod* osr_nmethod; \ OSR_REQUEST(osr_nmethod, branch_pc); \ if (osr_nmethod != NULL && osr_nmethod->osr_entry_bci() != InvalidOSREntryBci) { \ - intptr_t* buf; \ - CALL_VM(buf=SharedRuntime::OSR_migration_begin(THREAD), handle_exception); \ + intptr_t* 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()); \ return; \ } \ - } else { \ - INCR_INVOCATION_COUNT; \ - SAFEPOINT; \ } \ } /* UseCompiler ... */ \ INCR_INVOCATION_COUNT; \ @@ -1281,12 +1277,7 @@ jfloat f; jdouble r; f = STACK_FLOAT(-1); -#ifdef IA64 - // IA64 gcc bug - r = ( f == 0.0f ) ? (jdouble) f : (jdouble) f + ia64_double_zero; -#else r = (jdouble) f; -#endif MORE_STACK(-1); // POP SET_STACK_DOUBLE(r, 1); UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); @@ -3031,9 +3022,9 @@ tty->print_cr("&native_fresult: " INTPTR_FORMAT, (uintptr_t) &this->_native_fresult); tty->print_cr("native_lresult: " INTPTR_FORMAT, (uintptr_t) this->_native_lresult); #endif -#ifdef IA64 +#if defined(IA64) && !defined(ZERO) tty->print_cr("last_Java_fp: " INTPTR_FORMAT, (uintptr_t) this->_last_Java_fp); -#endif // IA64 +#endif // IA64 && !ZERO tty->print_cr("self_link: " INTPTR_FORMAT, (uintptr_t) this->_self_link); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/bytecodeTracer.cpp --- a/src/share/vm/interpreter/bytecodeTracer.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/bytecodeTracer.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -270,6 +270,8 @@ st->print_cr(" %s", constants->resolved_klass_at(i)->klass_part()->external_name()); } else if (tag.is_unresolved_klass()) { st->print_cr(" ", i); + } else if (tag.is_object()) { + st->print_cr(" " PTR_FORMAT, constants->object_at(i)); } else { st->print_cr(" bad tag=%d at %d", tag.value(), i); } @@ -282,18 +284,21 @@ constantPoolOop constants = method()->constants(); constantTag tag = constants->tag_at(i); + int nt_index = -1; + switch (tag.value()) { case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_Methodref: case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_NameAndType: break; default: st->print_cr(" bad tag=%d at %d", tag.value(), i); return; } - symbolOop name = constants->name_ref_at(orig_i); - symbolOop signature = constants->signature_ref_at(orig_i); + symbolOop name = constants->uncached_name_ref_at(i); + symbolOop signature = constants->uncached_signature_ref_at(i); st->print_cr(" %d <%s> <%s> ", i, name->as_C_string(), signature->as_C_string()); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/bytecodes.cpp --- a/src/share/vm/interpreter/bytecodes.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/bytecodes.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -357,7 +357,7 @@ def(_invokespecial , "invokespecial" , "bjj" , NULL , T_ILLEGAL, -1, true); def(_invokestatic , "invokestatic" , "bjj" , NULL , T_ILLEGAL, 0, true); def(_invokeinterface , "invokeinterface" , "bjj__", NULL , T_ILLEGAL, -1, true); - def(_invokedynamic , "invokedynamic" , "bjjjj", NULL , T_ILLEGAL, -1, true ); + def(_invokedynamic , "invokedynamic" , "bjjjj", NULL , T_ILLEGAL, 0, true ); def(_new , "new" , "bii" , NULL , T_OBJECT , 1, true ); def(_newarray , "newarray" , "bc" , NULL , T_OBJECT , 0, true ); def(_anewarray , "anewarray" , "bii" , NULL , T_OBJECT , 0, true ); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/interpreter.cpp --- a/src/share/vm/interpreter/interpreter.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/interpreter.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -314,6 +314,20 @@ break; } + case Bytecodes::_invokedynamic: { + Thread *thread = Thread::current(); + ResourceMark rm(thread); + methodHandle mh(thread, method); + type = Bytecode_invoke_at(mh, bci)->result_type(thread); + // since the cache entry might not be initialized: + // (NOT needed for the old calling convension) + if (!is_top_frame) { + int index = Bytes::get_native_u4(bcp+1); + method->constants()->cache()->secondary_entry_at(index)->set_parameter_size(callee_parameters); + } + break; + } + case Bytecodes::_ldc : type = constant_pool_type( method, *(bcp+1) ); break; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/interpreterRuntime.cpp --- a/src/share/vm/interpreter/interpreterRuntime.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -353,7 +353,7 @@ assert(h_exception.not_null(), "NULL exceptions should be handled by athrow"); assert(h_exception->is_oop(), "just checking"); // Check that exception is a subclass of Throwable, otherwise we have a VerifyError - if (!(h_exception->is_a(SystemDictionary::throwable_klass()))) { + if (!(h_exception->is_a(SystemDictionary::Throwable_klass()))) { if (ExitVMOnVerifyError) vm_exit(-1); ShouldNotReachHere(); } @@ -397,7 +397,7 @@ // notify JVMTI of an exception throw; JVMTI will detect if this is a first // time throw or a stack unwinding throw and accordingly notify the debugger - if (JvmtiExport::can_post_exceptions()) { + if (JvmtiExport::can_post_on_exceptions()) { JvmtiExport::post_exception_throw(thread, h_method(), bcp(thread), h_exception()); } @@ -426,7 +426,7 @@ } // notify debugger of an exception catch // (this is good for exceptions caught in native methods as well) - if (JvmtiExport::can_post_exceptions()) { + if (JvmtiExport::can_post_on_exceptions()) { JvmtiExport::notice_unwind_due_to_exception(thread, h_method(), handler_pc, h_exception(), (handler_pc != NULL)); } @@ -585,7 +585,7 @@ Handle exception(thread, thread->vm_result()); assert(exception() != NULL, "vm result should be set"); thread->set_vm_result(NULL); // clear vm result before continuing (may cause memory leaks and assert failures) - if (!exception->is_a(SystemDictionary::threaddeath_klass())) { + if (!exception->is_a(SystemDictionary::ThreadDeath_klass())) { exception = get_preinitialized_exception( SystemDictionary::IllegalMonitorStateException_klass(), CATCH); @@ -660,7 +660,7 @@ tty->print_cr("Resolving: klass: %s to method: %s", info.resolved_klass()->name()->as_C_string(), info.resolved_method()->name()->as_C_string()); } if (info.resolved_method()->method_holder() == - SystemDictionary::object_klass()) { + SystemDictionary::Object_klass()) { // NOTE: THIS IS A FIX FOR A CORNER CASE in the JVM spec // (see also cpCacheOop.cpp for details) methodHandle rm = info.resolved_method(); @@ -681,7 +681,7 @@ IRT_END -// First time execution: Resolve symbols, create a permanent CallSiteImpl object. +// First time execution: Resolve symbols, create a permanent CallSite object. IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) { ResourceMark rm(thread); @@ -708,21 +708,16 @@ constantPoolHandle pool(thread, caller_method->constants()); pool->set_invokedynamic(); // mark header to flag active call sites - int raw_index = four_byte_index(thread); - assert(constantPoolCacheOopDesc::is_secondary_index(raw_index), "invokedynamic indexes marked specially"); - - // there are two CPC entries that are of interest: - int site_index = constantPoolCacheOopDesc::decode_secondary_index(raw_index); - int main_index = pool->cache()->entry_at(site_index)->main_entry_index(); - // and there is one CP entry, a NameAndType: - int nt_index = pool->map_instruction_operand_to_index(raw_index); + int site_index = four_byte_index(thread); + // there is a second CPC entries that is of interest; it caches signature info: + int main_index = pool->cache()->secondary_entry_at(site_index)->main_entry_index(); // first resolve the signature to a MH.invoke methodOop if (!pool->cache()->entry_at(main_index)->is_resolved(bytecode)) { JvmtiHideSingleStepping jhss(thread); CallInfo info; LinkResolver::resolve_invoke(info, Handle(), pool, - raw_index, bytecode, CHECK); + site_index, bytecode, CHECK); // The main entry corresponds to a JVM_CONSTANT_NameAndType, and serves // as a common reference point for all invokedynamic call sites with // that exact call descriptor. We will link it in the CP cache exactly @@ -741,7 +736,7 @@ assert(mh_invdyn.not_null() && mh_invdyn->is_method() && mh_invdyn->is_method_handle_invoke(), "correct result from LinkResolver::resolve_invokedynamic"); - symbolHandle call_site_name(THREAD, pool->nt_name_ref_at(nt_index)); + symbolHandle call_site_name(THREAD, pool->name_ref_at(site_index)); Handle call_site = SystemDictionary::make_dynamic_call_site(caller_method->method_holder(), caller_method->method_idnum(), @@ -753,61 +748,11 @@ // In the secondary entry, the f1 field is the call site, and the f2 (index) // field is some data about the invoke site. int extra_data = 0; - pool->cache()->entry_at(site_index)->set_dynamic_call(call_site(), extra_data); + pool->cache()->secondary_entry_at(site_index)->set_dynamic_call(call_site(), extra_data); } IRT_END -// Called on first time execution, and also whenever the CallSite.target is null. -// FIXME: Do more of this in Java code. -IRT_ENTRY(void, InterpreterRuntime::bootstrap_invokedynamic(JavaThread* thread, oopDesc* call_site)) { - methodHandle mh_invdyn(thread, (methodOop) sun_dyn_CallSiteImpl::vmmethod(call_site)); - Handle mh_type(thread, mh_invdyn->method_handle_type()); - objArrayHandle mh_ptypes(thread, java_dyn_MethodType::ptypes(mh_type())); - - // squish the arguments down to a single array - int nargs = mh_ptypes->length(); - objArrayHandle arg_array; - { - objArrayOop aaoop = oopFactory::new_objArray(SystemDictionary::object_klass(), nargs, CHECK); - arg_array = objArrayHandle(thread, aaoop); - } - frame fr = thread->last_frame(); - assert(fr.interpreter_frame_bcp() != NULL, "sanity"); - int tos_offset = 0; - for (int i = nargs; --i >= 0; ) { - intptr_t* slot_addr = fr.interpreter_frame_tos_at(tos_offset++); - oop ptype = mh_ptypes->obj_at(i); - oop arg = NULL; - if (!java_lang_Class::is_primitive(ptype)) { - arg = *(oop*) slot_addr; - } else { - BasicType bt = java_lang_Class::primitive_type(ptype); - assert(frame::interpreter_frame_expression_stack_direction() < 0, "else reconsider this code"); - jvalue value; - Interpreter::get_jvalue_in_slot(slot_addr, bt, &value); - tos_offset += type2size[bt]-1; - arg = java_lang_boxing_object::create(bt, &value, CHECK); - // FIXME: These boxing objects are not canonicalized under - // the Java autoboxing rules. They should be... - // The best approach would be to push the arglist creation into Java. - // The JVM should use a lower-level interface to communicate argument lists. - } - arg_array->obj_at_put(i, arg); - } - - // now find the bootstrap method - oop bootstrap_mh_oop = instanceKlass::cast(fr.interpreter_frame_method()->method_holder())->bootstrap_method(); - assert(bootstrap_mh_oop != NULL, "resolve_invokedynamic ensures a BSM"); - - // return the bootstrap method and argument array via vm_result/_2 - thread->set_vm_result(bootstrap_mh_oop); - thread->set_vm_result_2(arg_array()); -} -IRT_END - - - //------------------------------------------------------------------------------------------------------------------------ // Miscellaneous @@ -849,8 +794,25 @@ } #endif // !PRODUCT +nmethod* InterpreterRuntime::frequency_counter_overflow(JavaThread* thread, address branch_bcp) { + nmethod* nm = frequency_counter_overflow_inner(thread, branch_bcp); + assert(branch_bcp != NULL || nm == NULL, "always returns null for non OSR requests"); + if (branch_bcp != NULL && nm != NULL) { + // This was a successful request for an OSR nmethod. Because + // frequency_counter_overflow_inner ends with a safepoint check, + // nm could have been unloaded so look it up again. It's unsafe + // to examine nm directly since it might have been freed and used + // for something else. + frame fr = thread->last_frame(); + methodOop method = fr.interpreter_frame_method(); + int bci = method->bci_from(fr.interpreter_frame_bcp()); + nm = method->lookup_osr_nmethod_for(bci); + } + return nm; +} + IRT_ENTRY(nmethod*, - InterpreterRuntime::frequency_counter_overflow(JavaThread* thread, address branch_bcp)) + InterpreterRuntime::frequency_counter_overflow_inner(JavaThread* thread, address branch_bcp)) // use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized // flag, in case this method triggers classloading which will call into Java. UnlockFlagSaver fs(thread); @@ -923,7 +885,6 @@ } BiasedLocking::revoke(objects_to_revoke); } - return osr_nm; } } @@ -1289,7 +1250,7 @@ methodHandle mh(thread, fr.interpreter_frame_method()); Bytecode_invoke* invoke = Bytecode_invoke_at(mh, bci); ArgumentSizeComputer asc(invoke->signature()); - int size_of_arguments = (asc.size() + (invoke->is_invokestatic() ? 0 : 1)); // receiver + int size_of_arguments = (asc.size() + (invoke->has_receiver() ? 1 : 0)); // receiver Copy::conjoint_bytes(src_address, dest_address, size_of_arguments * Interpreter::stackElementSize()); IRT_END diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/interpreterRuntime.hpp --- a/src/share/vm/interpreter/interpreterRuntime.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/interpreterRuntime.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -49,6 +49,9 @@ static ConstantPoolCacheEntry* cache_entry(JavaThread *thread) { return cache_entry_at(thread, Bytes::get_native_u2(bcp(thread) + 1)); } static void note_trap(JavaThread *thread, int reason, TRAPS); + // Inner work method for Interpreter's frequency counter overflow + static nmethod* frequency_counter_overflow_inner(JavaThread* thread, address branch_bcp); + public: // Constants static void ldc (JavaThread* thread, bool wide); @@ -88,7 +91,6 @@ // Calls static void resolve_invoke (JavaThread* thread, Bytecodes::Code bytecode); static void resolve_invokedynamic(JavaThread* thread); - static void bootstrap_invokedynamic(JavaThread* thread, oopDesc* call_site); // Breakpoints static void _breakpoint(JavaThread* thread, methodOopDesc* method, address bcp); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/linkResolver.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -55,7 +55,7 @@ // we should pick the vtable index from the resolved method. // Other than that case, there is no valid vtable index to specify. int vtable_index = methodOopDesc::invalid_vtable_index; - if (resolved_method->method_holder() == SystemDictionary::object_klass()) { + if (resolved_method->method_holder() == SystemDictionary::Object_klass()) { assert(resolved_method->vtable_index() == selected_method->vtable_index(), "sanity check"); vtable_index = resolved_method->vtable_index(); } @@ -75,6 +75,8 @@ _selected_method = selected_method; _vtable_index = vtable_index; if (CompilationPolicy::mustBeCompiled(selected_method)) { + // This path is unusual, mostly used by the '-Xcomp' stress test mode. + // Note: with several active threads, the mustBeCompiled may be true // while canBeCompiled is false; remove assert // assert(CompilationPolicy::canBeCompiled(selected_method), "cannot compile"); @@ -82,6 +84,16 @@ // don't force compilation, resolve was on behalf of compiler return; } + if (instanceKlass::cast(selected_method->method_holder())->is_not_initialized()) { + // 'is_not_initialized' means not only '!is_initialized', but also that + // initialization has not been started yet ('!being_initialized') + // Do not force compilation of methods in uninitialized classes. + // Note that doing this would throw an assert later, + // in CompileBroker::compile_method. + // We sometimes use the link resolver to do reflective lookups + // even before classes are initialized. + return; + } CompileBroker::compile_method(selected_method, InvocationEntryBci, methodHandle(), 0, "mustBeCompiled", CHECK); } @@ -181,7 +193,7 @@ // We'll check for the method name first, as that's most likely // to be false (so we'll short-circuit out of these tests). if (sel_method->name() == vmSymbols::clone_name() && - sel_klass() == SystemDictionary::object_klass() && + sel_klass() == SystemDictionary::Object_klass() && resolved_klass->oop_is_array()) { // We need to change "protected" to "public". assert(flags.is_protected(), "clone not protected?"); @@ -223,6 +235,18 @@ resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); } +void LinkResolver::resolve_dynamic_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) { + // The class is java.dyn.MethodHandle + resolved_klass = SystemDictionaryHandles::MethodHandle_klass(); + + symbolHandle method_name = vmSymbolHandles::invoke_name(); + + symbolHandle method_signature(THREAD, pool->signature_ref_at(index)); + KlassHandle current_klass (THREAD, pool->pool_holder()); + + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); +} + void LinkResolver::resolve_interface_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) { // resolve klass @@ -1015,11 +1039,8 @@ // This guy is reached from InterpreterRuntime::resolve_invokedynamic. - assert(constantPoolCacheOopDesc::is_secondary_index(raw_index), "must be secondary index"); - int nt_index = pool->map_instruction_operand_to_index(raw_index); - // At this point, we only need the signature, and can ignore the name. - symbolHandle method_signature(THREAD, pool->nt_signature_ref_at(nt_index)); + symbolHandle method_signature(THREAD, pool->signature_ref_at(raw_index)); // raw_index works directly symbolHandle method_name = vmSymbolHandles::invoke_name(); KlassHandle resolved_klass = SystemDictionaryHandles::MethodHandle_klass(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/linkResolver.hpp --- a/src/share/vm/interpreter/linkResolver.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/linkResolver.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -133,6 +133,7 @@ // static resolving for all calls except interface calls static void resolve_method (methodHandle& method_result, KlassHandle& klass_result, constantPoolHandle pool, int index, TRAPS); + static void resolve_dynamic_method (methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS); static void resolve_interface_method(methodHandle& method_result, KlassHandle& klass_result, constantPoolHandle pool, int index, TRAPS); // runtime/static resolving for fields diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/oopMapCache.cpp --- a/src/share/vm/interpreter/oopMapCache.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/oopMapCache.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -281,7 +281,7 @@ public: void pass_int() { /* ignore */ } void pass_long() { /* ignore */ } -#ifdef _LP64 +#if defined(_LP64) || defined(ZERO) void pass_float() { /* ignore */ } #endif void pass_double() { /* ignore */ } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/rewriter.cpp --- a/src/share/vm/interpreter/rewriter.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/rewriter.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -48,16 +48,6 @@ } -int Rewriter::add_extra_cp_cache_entry(int main_entry) { - // Hack: We put it on the map as an encoded value. - // The only place that consumes this is ConstantPoolCacheEntry::set_initial_state - int encoded = constantPoolCacheOopDesc::encode_secondary_index(main_entry); - int plain_secondary_index = _cp_cache_map.append(encoded); - return constantPoolCacheOopDesc::encode_secondary_index(plain_secondary_index); -} - - - // Creates a constant pool cache given a CPC map // This creates the constant pool cache initially in a state // that is unsafe for concurrent GC processing but sets it to @@ -127,7 +117,7 @@ assert(p[-1] == Bytecodes::_invokedynamic, ""); int cp_index = Bytes::get_Java_u2(p); int cpc = maybe_add_cp_cache_entry(cp_index); // add lazily - int cpc2 = add_extra_cp_cache_entry(cpc); + int cpc2 = add_secondary_cp_cache_entry(cpc); // Replace the trailing four bytes with a CPC index for the dynamic // call site. Unlike other CPC entries, there is one per bytecode, @@ -137,7 +127,7 @@ // all these entries. That is the main reason invokedynamic // must have a five-byte instruction format. (Of course, other JVM // implementations can use the bytes for other purposes.) - Bytes::put_native_u4(p, cpc2); + Bytes::put_native_u4(p, constantPoolCacheOopDesc::encode_secondary_index(cpc2)); // Note: We use native_u4 format exclusively for 4-byte indexes. } @@ -257,15 +247,22 @@ void Rewriter::rewrite(instanceKlassHandle klass, TRAPS) { ResourceMark rm(THREAD); - Rewriter rw(klass, CHECK); + Rewriter rw(klass, klass->constants(), klass->methods(), CHECK); // (That's all, folks.) } -Rewriter::Rewriter(instanceKlassHandle klass, TRAPS) + +void Rewriter::rewrite(instanceKlassHandle klass, constantPoolHandle cpool, objArrayHandle methods, TRAPS) { + ResourceMark rm(THREAD); + Rewriter rw(klass, cpool, methods, CHECK); + // (That's all, folks.) +} + + +Rewriter::Rewriter(instanceKlassHandle klass, constantPoolHandle cpool, objArrayHandle methods, TRAPS) : _klass(klass), - // gather starting points - _pool( THREAD, klass->constants()), - _methods(THREAD, klass->methods()) + _pool(cpool), + _methods(methods) { assert(_pool->cache() == NULL, "constant pool cache must not be set yet"); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/rewriter.hpp --- a/src/share/vm/interpreter/rewriter.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/rewriter.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -43,16 +43,21 @@ bool has_cp_cache(int i) { return (uint)i < (uint)_cp_map.length() && _cp_map[i] >= 0; } int maybe_add_cp_cache_entry(int i) { return has_cp_cache(i) ? _cp_map[i] : add_cp_cache_entry(i); } int add_cp_cache_entry(int cp_index) { + assert((cp_index & _secondary_entry_tag) == 0, "bad tag"); assert(_cp_map[cp_index] == -1, "not twice on same cp_index"); int cache_index = _cp_cache_map.append(cp_index); _cp_map.at_put(cp_index, cache_index); assert(cp_entry_to_cp_cache(cp_index) == cache_index, ""); return cache_index; } - int add_extra_cp_cache_entry(int main_entry); + int add_secondary_cp_cache_entry(int main_cpc_entry) { + assert(main_cpc_entry < _cp_cache_map.length(), "must be earlier CP cache entry"); + int cache_index = _cp_cache_map.append(main_cpc_entry | _secondary_entry_tag); + return cache_index; + } // All the work goes in here: - Rewriter(instanceKlassHandle klass, TRAPS); + Rewriter(instanceKlassHandle klass, constantPoolHandle cpool, objArrayHandle methods, TRAPS); void compute_index_maps(); void make_constant_pool_cache(TRAPS); @@ -65,4 +70,9 @@ public: // Driver routine: static void rewrite(instanceKlassHandle klass, TRAPS); + static void rewrite(instanceKlassHandle klass, constantPoolHandle cpool, objArrayHandle methods, TRAPS); + + enum { + _secondary_entry_tag = nth_bit(30) + }; }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/templateInterpreter.cpp --- a/src/share/vm/interpreter/templateInterpreter.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/templateInterpreter.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -178,14 +178,12 @@ #endif // !PRODUCT EntryPoint TemplateInterpreter::_return_entry[TemplateInterpreter::number_of_return_entries]; EntryPoint TemplateInterpreter::_earlyret_entry; -EntryPoint TemplateInterpreter::_return_unbox_entry; EntryPoint TemplateInterpreter::_deopt_entry [TemplateInterpreter::number_of_deopt_entries ]; EntryPoint TemplateInterpreter::_continuation_entry; EntryPoint TemplateInterpreter::_safept_entry; address TemplateInterpreter::_return_3_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; address TemplateInterpreter::_return_5_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; -address TemplateInterpreter::_return_5_unbox_addrs_by_index[TemplateInterpreter::number_of_return_addrs]; DispatchTable TemplateInterpreter::_active_table; DispatchTable TemplateInterpreter::_normal_table; @@ -253,22 +251,6 @@ } } - if (EnableInvokeDynamic) { - CodeletMark cm(_masm, "unboxing return entry points"); - Interpreter::_return_unbox_entry = - EntryPoint( - generate_return_unbox_entry_for(btos, 5), - generate_return_unbox_entry_for(ctos, 5), - generate_return_unbox_entry_for(stos, 5), - generate_return_unbox_entry_for(atos, 5), // cast conversion - generate_return_unbox_entry_for(itos, 5), - generate_return_unbox_entry_for(ltos, 5), - generate_return_unbox_entry_for(ftos, 5), - generate_return_unbox_entry_for(dtos, 5), - Interpreter::_return_entry[5].entry(vtos) // no unboxing for void - ); - } - { CodeletMark cm(_masm, "earlyret entry points"); Interpreter::_earlyret_entry = EntryPoint( @@ -319,8 +301,6 @@ int index = Interpreter::TosState_as_index(states[j]); Interpreter::_return_3_addrs_by_index[index] = Interpreter::return_entry(states[j], 3); Interpreter::_return_5_addrs_by_index[index] = Interpreter::return_entry(states[j], 5); - if (EnableInvokeDynamic) - Interpreter::_return_5_unbox_addrs_by_index[index] = Interpreter::return_unbox_entry(states[j], 5); } { CodeletMark cm(_masm, "continuation entry points"); @@ -485,9 +465,11 @@ void TemplateInterpreterGenerator::set_short_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(), "template must exist"); switch (t->tos_in()) { - case btos: vep = __ pc(); __ pop(btos); bep = __ pc(); generate_and_dispatch(t); break; - case ctos: vep = __ pc(); __ pop(ctos); sep = __ pc(); generate_and_dispatch(t); break; - case stos: vep = __ pc(); __ pop(stos); sep = __ pc(); generate_and_dispatch(t); break; + case btos: + case ctos: + case stos: + ShouldNotReachHere(); // btos/ctos/stos should use itos. + break; case atos: vep = __ pc(); __ pop(atos); aep = __ pc(); generate_and_dispatch(t); break; case itos: vep = __ pc(); __ pop(itos); iep = __ pc(); generate_and_dispatch(t); break; case ltos: vep = __ pc(); __ pop(ltos); lep = __ pc(); generate_and_dispatch(t); break; @@ -547,18 +529,6 @@ } -address TemplateInterpreter::return_unbox_entry(TosState state, int length) { - assert(EnableInvokeDynamic, ""); - if (state == vtos) { - // no unboxing to do, actually - return return_entry(state, length); - } else { - assert(length == 5, "unboxing entries generated for invokedynamic only"); - return _return_unbox_entry.entry(state); - } -} - - address TemplateInterpreter::deopt_entry(TosState state, int length) { guarantee(0 <= length && length < Interpreter::number_of_deopt_entries, "illegal length"); return _deopt_entry[length].entry(state); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/templateInterpreter.hpp --- a/src/share/vm/interpreter/templateInterpreter.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/templateInterpreter.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -110,14 +110,12 @@ #endif // !PRODUCT static EntryPoint _return_entry[number_of_return_entries]; // entry points to return to from a call static EntryPoint _earlyret_entry; // entry point to return early from a call - static EntryPoint _return_unbox_entry; // entry point to unbox a return value from a call static EntryPoint _deopt_entry[number_of_deopt_entries]; // entry points to return to from a deoptimization static EntryPoint _continuation_entry; static EntryPoint _safept_entry; static address _return_3_addrs_by_index[number_of_return_addrs]; // for invokevirtual return entries static address _return_5_addrs_by_index[number_of_return_addrs]; // for invokeinterface return entries - static address _return_5_unbox_addrs_by_index[number_of_return_addrs]; // for invokedynamic bootstrap methods static DispatchTable _active_table; // the active dispatch table (used by the interpreter for dispatch) static DispatchTable _normal_table; // the normal dispatch table (used to set the active table in normal mode) @@ -159,12 +157,10 @@ // Support for invokes static address* return_3_addrs_by_index_table() { return _return_3_addrs_by_index; } static address* return_5_addrs_by_index_table() { return _return_5_addrs_by_index; } - static address* return_5_unbox_addrs_by_index_table() { return _return_5_unbox_addrs_by_index; } static int TosState_as_index(TosState state); // computes index into return_3_entry_by_index table static address return_entry (TosState state, int length); static address deopt_entry (TosState state, int length); - static address return_unbox_entry(TosState state, int length); // Safepoint support static void notice_safepoints(); // stops the thread when reaching a safepoint diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/interpreter/templateInterpreterGenerator.hpp --- a/src/share/vm/interpreter/templateInterpreterGenerator.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/interpreter/templateInterpreterGenerator.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -51,10 +51,7 @@ address generate_WrongMethodType_handler(); address generate_ArrayIndexOutOfBounds_handler(const char* name); address generate_continuation_for(TosState state); - address generate_return_entry_for(TosState state, int step, bool unbox = false); - address generate_return_unbox_entry_for(TosState state, int step) { - return generate_return_entry_for(state, step, true); - } + address generate_return_entry_for(TosState state, int step); address generate_earlyret_entry_for(TosState state); address generate_deopt_entry_for(TosState state, int step); address generate_safept_entry_for(TosState state, address runtime_entry); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/barrierSet.cpp --- a/src/share/vm/memory/barrierSet.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/barrierSet.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -41,11 +41,6 @@ // count is number of array elements being written void BarrierSet::static_write_ref_array_post(HeapWord* start, size_t count) { - assert(count <= (size_t)max_intx, "count too large"); - HeapWord* end = start + objArrayOopDesc::array_size((int)count); -#if 0 - warning("Post:\t" INTPTR_FORMAT "[" SIZE_FORMAT "] : [" INTPTR_FORMAT","INTPTR_FORMAT")\t", - start, count, start, end); -#endif - Universe::heap()->barrier_set()->write_ref_array_work(MemRegion(start, end)); + // simply delegate to instance method + Universe::heap()->barrier_set()->write_ref_array(start, count); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/barrierSet.hpp --- a/src/share/vm/memory/barrierSet.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/barrierSet.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -121,17 +121,18 @@ virtual void read_ref_array(MemRegion mr) = 0; virtual void read_prim_array(MemRegion mr) = 0; + // Below length is the # array elements being written virtual void write_ref_array_pre( oop* dst, int length) {} virtual void write_ref_array_pre(narrowOop* dst, int length) {} - inline void write_ref_array(MemRegion mr); + // Below count is the # array elements being written, starting + // at the address "start", which may not necessarily be HeapWord-aligned + inline void write_ref_array(HeapWord* start, size_t count); - // Static versions, suitable for calling from generated code. + // Static versions, suitable for calling from generated code; + // count is # array elements being written, starting with "start", + // which may not necessarily be HeapWord-aligned. static void static_write_ref_array_pre(HeapWord* start, size_t count); static void static_write_ref_array_post(HeapWord* start, size_t count); - // Narrow oop versions of the above; count is # of array elements being written, - // starting with "start", which is HeapWord-aligned. - static void static_write_ref_array_pre_narrow(HeapWord* start, size_t count); - static void static_write_ref_array_post_narrow(HeapWord* start, size_t count); protected: virtual void write_ref_array_work(MemRegion mr) = 0; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/barrierSet.inline.hpp --- a/src/share/vm/memory/barrierSet.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/barrierSet.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -42,14 +42,34 @@ } } -void BarrierSet::write_ref_array(MemRegion mr) { - if (kind() == CardTableModRef) { - ((CardTableModRefBS*)this)->inline_write_ref_array(mr); - } else { - write_ref_array_work(mr); - } +// count is number of array elements being written +void BarrierSet::write_ref_array(HeapWord* start, size_t count) { + assert(count <= (size_t)max_intx, "count too large"); + HeapWord* end = (HeapWord*)((char*)start + (count*heapOopSize)); + // In the case of compressed oops, start and end may potentially be misaligned; + // so we need to conservatively align the first downward (this is not + // strictly necessary for current uses, but a case of good hygiene and, + // if you will, aesthetics) and the second upward (this is essential for + // current uses) to a HeapWord boundary, so we mark all cards overlapping + // this write. If this evolves in the future to calling a + // logging barrier of narrow oop granularity, like the pre-barrier for G1 + // (mentioned here merely by way of example), we will need to change this + // interface, so it is "exactly precise" (if i may be allowed the adverbial + // redundancy for emphasis) and does not include narrow oop slots not + // included in the original write interval. + HeapWord* aligned_start = (HeapWord*)align_size_down((uintptr_t)start, HeapWordSize); + HeapWord* aligned_end = (HeapWord*)align_size_up ((uintptr_t)end, HeapWordSize); + // If compressed oops were not being used, these should already be aligned + assert(UseCompressedOops || (aligned_start == start && aligned_end == end), + "Expected heap word alignment of start and end"); +#if 0 + warning("Post:\t" INTPTR_FORMAT "[" SIZE_FORMAT "] : [" INTPTR_FORMAT","INTPTR_FORMAT")\t", + start, count, aligned_start, aligned_end); +#endif + write_ref_array_work(MemRegion(aligned_start, aligned_end)); } + void BarrierSet::write_region(MemRegion mr) { if (kind() == CardTableModRef) { ((CardTableModRefBS*)this)->inline_write_region(mr); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/cardTableModRefBS.cpp --- a/src/share/vm/memory/cardTableModRefBS.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/cardTableModRefBS.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -511,6 +511,8 @@ } void CardTableModRefBS::dirty_MemRegion(MemRegion mr) { + assert((HeapWord*)align_size_down((uintptr_t)mr.start(), HeapWordSize) == mr.start(), "Unaligned start"); + assert((HeapWord*)align_size_up ((uintptr_t)mr.end(), HeapWordSize) == mr.end(), "Unaligned end" ); jbyte* cur = byte_for(mr.start()); jbyte* last = byte_after(mr.last()); while (cur < last) { @@ -520,6 +522,8 @@ } void CardTableModRefBS::invalidate(MemRegion mr, bool whole_heap) { + assert((HeapWord*)align_size_down((uintptr_t)mr.start(), HeapWordSize) == mr.start(), "Unaligned start"); + assert((HeapWord*)align_size_up ((uintptr_t)mr.end(), HeapWordSize) == mr.end(), "Unaligned end" ); for (int i = 0; i < _cur_covered_regions; i++) { MemRegion mri = mr.intersection(_covered[i]); if (!mri.is_empty()) dirty_MemRegion(mri); @@ -660,6 +664,29 @@ GuaranteeNotModClosure blk(this); non_clean_card_iterate_work(mr, &blk, false); } + +// To verify a MemRegion is entirely dirty this closure is passed to +// dirty_card_iterate. If the region is dirty do_MemRegion will be +// invoked only once with a MemRegion equal to the one being +// verified. +class GuaranteeDirtyClosure: public MemRegionClosure { + CardTableModRefBS* _ct; + MemRegion _mr; + bool _result; +public: + GuaranteeDirtyClosure(CardTableModRefBS* ct, MemRegion mr) + : _ct(ct), _mr(mr), _result(false) {} + void do_MemRegion(MemRegion mr) { + _result = _mr.equals(mr); + } + bool result() const { return _result; } +}; + +void CardTableModRefBS::verify_dirty_region(MemRegion mr) { + GuaranteeDirtyClosure blk(this, mr); + dirty_card_iterate(mr, &blk); + guarantee(blk.result(), "Non-dirty cards in region that should be dirty"); +} #endif bool CardTableModRefBSForCTRS::card_will_be_scanned(jbyte cv) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/cardTableModRefBS.hpp --- a/src/share/vm/memory/cardTableModRefBS.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/cardTableModRefBS.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -339,6 +339,16 @@ return (val & (clean_card_mask_val() | claimed_card_val())) == claimed_card_val(); } + void set_card_claimed(size_t card_index) { + jbyte val = _byte_map[card_index]; + if (val == clean_card_val()) { + val = (jbyte)claimed_card_val(); + } else { + val |= (jbyte)claimed_card_val(); + } + _byte_map[card_index] = val; + } + bool claim_card(size_t card_index); bool is_card_clean(size_t card_index) { @@ -456,6 +466,7 @@ void verify_guard(); void verify_clean_region(MemRegion mr) PRODUCT_RETURN; + void verify_dirty_region(MemRegion mr) PRODUCT_RETURN; static size_t par_chunk_heapword_alignment() { return CardsPerStrideChunk * card_size_in_words; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/classify.cpp --- a/src/share/vm/memory/classify.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/classify.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -49,7 +49,7 @@ Klass* k = obj->blueprint(); - if (k->as_klassOop() == SystemDictionary::object_klass()) { + if (k->as_klassOop() == SystemDictionary::Object_klass()) { tty->print_cr("Found the class!"); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/collectorPolicy.cpp --- a/src/share/vm/memory/collectorPolicy.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/collectorPolicy.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -55,7 +55,7 @@ void CollectorPolicy::initialize_size_info() { // User inputs from -mx and ms are aligned - set_initial_heap_byte_size(Arguments::initial_heap_size()); + set_initial_heap_byte_size(InitialHeapSize); if (initial_heap_byte_size() == 0) { set_initial_heap_byte_size(NewSize + OldSize); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/compactingPermGenGen.cpp --- a/src/share/vm/memory/compactingPermGenGen.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/compactingPermGenGen.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -352,15 +352,19 @@ } +// Do not use in time-critical operations due to the possibility of paging +// in otherwise untouched or previously unread portions of the perm gen, +// for instance, the shared spaces. NOTE: Because CompactingPermGenGen +// derives from OneContigSpaceCardGeneration which is supposed to have a +// single space, and does not override its object_iterate() method, +// object iteration via that interface does not look at the objects in +// the shared spaces when using CDS. This should be fixed; see CR 6897798. void CompactingPermGenGen::space_iterate(SpaceClosure* blk, bool usedOnly) { OneContigSpaceCardGeneration::space_iterate(blk, usedOnly); if (spec()->enable_shared_spaces()) { -#ifdef PRODUCT // Making the rw_space walkable will page in the entire space, and - // is to be avoided. However, this is required for Verify options. - ShouldNotReachHere(); -#endif - + // is to be avoided in the case of time-critical operations. + // However, this is required for Verify and heap dump operations. blk->do_space(ro_space()); blk->do_space(rw_space()); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/compactingPermGenGen.hpp --- a/src/share/vm/memory/compactingPermGenGen.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/compactingPermGenGen.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -29,6 +29,9 @@ class PermanentGenerationSpec; // This is the "generation" view of a CompactingPermGen. +// NOTE: the shared spaces used for CDS are here handled in +// a somewhat awkward and potentially buggy fashion, see CR 6801625. +// This infelicity should be fixed, see CR 6897789. class CompactingPermGenGen: public OneContigSpaceCardGeneration { friend class VMStructs; // Abstractly, this is a subtype that gets access to protected fields. @@ -47,7 +50,7 @@ OffsetTableContigSpace* _ro_space; OffsetTableContigSpace* _rw_space; - // With shared spaces there is a dicotomy in the use of the + // With shared spaces there is a dichotomy in the use of the // _virtual_space of the generation. There is a portion of the // _virtual_space that is used for the unshared part of the // permanent generation and a portion that is reserved for the shared part. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/defNewGeneration.cpp --- a/src/share/vm/memory/defNewGeneration.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/defNewGeneration.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -555,12 +555,14 @@ "save marks have not been newly set."); gch->gen_process_strong_roots(_level, - true, // Process younger gens, if any, as - // strong roots. - false,// not collecting permanent generation. + true, // Process younger gens, if any, + // as strong roots. + true, // activate StrongRootsScope + false, // not collecting perm generation. SharedHeap::SO_AllClasses, - &fsc_with_gc_barrier, - &fsc_with_no_gc_barrier); + &fsc_with_no_gc_barrier, + true, // walk *all* scavengable nmethods + &fsc_with_gc_barrier); // "evacuate followers". evacuate_followers.do_void(); @@ -607,7 +609,7 @@ remove_forwarding_pointers(); if (PrintGCDetails) { - gclog_or_tty->print(" (promotion failed)"); + gclog_or_tty->print(" (promotion failed) "); } // Add to-space to the list of space to compact // when a promotion failure has occurred. In that @@ -618,6 +620,9 @@ from()->set_next_compaction_space(to()); gch->set_incremental_collection_will_fail(); + // Inform the next generation that a promotion failure occurred. + _next_gen->promotion_failure_occurred(); + // Reset the PromotionFailureALot counters. NOT_PRODUCT(Universe::heap()->reset_promotion_should_fail();) } @@ -677,6 +682,11 @@ void DefNewGeneration::handle_promotion_failure(oop old) { preserve_mark_if_necessary(old, old->mark()); + if (!_promotion_failed && PrintPromotionFailure) { + gclog_or_tty->print(" (promotion failure size = " SIZE_FORMAT ") ", + old->size()); + } + // forward to self old->forward_to(old); _promotion_failed = true; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/dump.cpp --- a/src/share/vm/memory/dump.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/dump.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -63,7 +63,7 @@ void do_oop(oop* p) { if (p != NULL) { oop obj = *p; - if (obj->klass() == SystemDictionary::string_klass()) { + if (obj->klass() == SystemDictionary::String_klass()) { int hash; typeArrayOop value = java_lang_String::value(obj); @@ -625,11 +625,11 @@ if (obj->is_klass() || obj->is_instance()) { if (obj->is_klass() || - obj->is_a(SystemDictionary::class_klass()) || - obj->is_a(SystemDictionary::throwable_klass())) { + obj->is_a(SystemDictionary::Class_klass()) || + obj->is_a(SystemDictionary::Throwable_klass())) { // Do nothing } - else if (obj->is_a(SystemDictionary::string_klass())) { + else if (obj->is_a(SystemDictionary::String_klass())) { // immutable objects. } else { // someone added an object we hadn't accounted for. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/genCollectedHeap.cpp --- a/src/share/vm/memory/genCollectedHeap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/genCollectedHeap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -51,6 +51,8 @@ } jint GenCollectedHeap::initialize() { + CollectedHeap::pre_initialize(); + int i; _n_gens = gen_policy()->number_of_generations(); @@ -129,6 +131,7 @@ _rem_set = collector_policy()->create_rem_set(_reserved, n_covered_regions); set_barrier_set(rem_set()->bs()); + _gch = this; for (i = 0; i < _n_gens; i++) { @@ -677,13 +680,23 @@ void GenCollectedHeap:: gen_process_strong_roots(int level, bool younger_gens_as_roots, + bool activate_scope, bool collecting_perm_gen, SharedHeap::ScanningOption so, - OopsInGenClosure* older_gens, - OopsInGenClosure* not_older_gens) { + OopsInGenClosure* not_older_gens, + bool do_code_roots, + OopsInGenClosure* older_gens) { // General strong roots. - SharedHeap::process_strong_roots(collecting_perm_gen, so, - not_older_gens, older_gens); + + if (!do_code_roots) { + SharedHeap::process_strong_roots(activate_scope, collecting_perm_gen, so, + not_older_gens, NULL, older_gens); + } else { + bool do_code_marking = (activate_scope || nmethod::oops_do_marking_is_active()); + CodeBlobToOopClosure code_roots(not_older_gens, /*do_marking=*/ do_code_marking); + SharedHeap::process_strong_roots(activate_scope, collecting_perm_gen, so, + not_older_gens, &code_roots, older_gens); + } if (younger_gens_as_roots) { if (!_gen_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) { @@ -706,8 +719,9 @@ } void GenCollectedHeap::gen_process_weak_roots(OopClosure* root_closure, + CodeBlobClosure* code_roots, OopClosure* non_root_closure) { - SharedHeap::process_weak_roots(root_closure, non_root_closure); + SharedHeap::process_weak_roots(root_closure, code_roots, non_root_closure); // "Local" "weak" refs for (int i = 0; i < _n_gens; i++) { _gens[i]->ref_processor()->weak_oops_do(root_closure); @@ -914,6 +928,8 @@ guarantee(VerifyBeforeGC || VerifyDuringGC || VerifyBeforeExit || + PrintAssembly || + tty->count() != 0 || // already printing VerifyAfterGC, "too expensive"); #endif // This might be sped up with a cache of the last generation that diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/genCollectedHeap.hpp --- a/src/share/vm/memory/genCollectedHeap.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/genCollectedHeap.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -260,6 +260,24 @@ return true; } + virtual bool card_mark_must_follow_store() const { + return UseConcMarkSweepGC; + } + + // We don't need barriers for stores to objects in the + // young gen and, a fortiori, for initializing stores to + // objects therein. This applies to {DefNew,ParNew}+{Tenured,CMS} + // only and may need to be re-examined in case other + // kinds of collectors are implemented in the future. + virtual bool can_elide_initializing_store_barrier(oop new_obj) { + // We wanted to assert that:- + // assert(UseParNewGC || UseSerialGC || UseConcMarkSweepGC, + // "Check can_elide_initializing_store_barrier() for this collector"); + // but unfortunately the flag UseSerialGC need not necessarily always + // be set when DefNew+Tenured are being used. + return is_in_youngest((void*)new_obj); + } + // Can a compiler elide a store barrier when it writes // a permanent oop into the heap? Applies when the compiler // is storing x to the heap, where x->is_perm() is true. @@ -408,16 +426,22 @@ // "SO_SystemClasses" to all the "system" classes and loaders; // "SO_Symbols_and_Strings" applies the closure to all entries in // SymbolsTable and StringTable. - void gen_process_strong_roots(int level, bool younger_gens_as_roots, + void gen_process_strong_roots(int level, + bool younger_gens_as_roots, + // The remaining arguments are in an order + // consistent with SharedHeap::process_strong_roots: + bool activate_scope, bool collecting_perm_gen, SharedHeap::ScanningOption so, - OopsInGenClosure* older_gens, - OopsInGenClosure* not_older_gens); + OopsInGenClosure* not_older_gens, + bool do_code_roots, + OopsInGenClosure* older_gens); // 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 gen_process_weak_roots(OopClosure* root_closure, + CodeBlobClosure* code_roots, OopClosure* non_root_closure); // Set the saved marks of generations, if that makes sense. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/genMarkSweep.cpp --- a/src/share/vm/memory/genMarkSweep.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/genMarkSweep.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 Sun Microsystems, 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 @@ -162,6 +162,9 @@ int size = SystemDictionary::number_of_classes() * 2; _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + // (#klass/k)^2 for k ~ 10 appears to be a better fit, but this will have to do for + // now until we have had a chance to investigate a more optimal setting. + _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(2*size, true); #ifdef VALIDATE_MARK_SWEEP if (ValidateMarkSweep) { @@ -206,6 +209,7 @@ delete _marking_stack; delete _revisit_klass_stack; + delete _revisit_mdo_stack; #ifdef VALIDATE_MARK_SWEEP if (ValidateMarkSweep) { @@ -240,9 +244,12 @@ gch->gen_process_strong_roots(level, false, // Younger gens are not roots. + true, // activate StrongRootsScope true, // Collecting permanent generation. SharedHeap::SO_SystemClasses, - &follow_root_closure, &follow_root_closure); + &follow_root_closure, + true, // walk code active on stacks + &follow_root_closure); // Process reference objects found during marking { @@ -262,6 +269,10 @@ follow_weak_klass_links(); assert(_marking_stack->is_empty(), "just drained"); + // Visit memoized MDO's and clear any unmarked weak refs + follow_mdo_weak_refs(); + assert(_marking_stack->is_empty(), "just drained"); + // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(&is_alive); StringTable::unlink(&is_alive); @@ -330,14 +341,19 @@ gch->gen_process_strong_roots(level, false, // Younger gens are not roots. + true, // activate StrongRootsScope true, // Collecting permanent generation. SharedHeap::SO_AllClasses, &adjust_root_pointer_closure, + false, // do not walk code &adjust_root_pointer_closure); // Now adjust pointers in remaining weak roots. (All of which should // have been cleared if they pointed to non-surviving objects.) + CodeBlobToOopClosure adjust_code_pointer_closure(&adjust_pointer_closure, + /*do_marking=*/ false); gch->gen_process_weak_roots(&adjust_root_pointer_closure, + &adjust_code_pointer_closure, &adjust_pointer_closure); adjust_marks(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/generation.cpp --- a/src/share/vm/memory/generation.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/generation.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -606,6 +606,13 @@ void OneContigSpaceCardGeneration::prepare_for_verify() {} +// Override for a card-table generation with one contiguous +// space. NOTE: For reasons that are lost in the fog of history, +// this code is used when you iterate over perm gen objects, +// even when one uses CDS, where the perm gen has a couple of +// other spaces; this is because CompactingPermGenGen derives +// from OneContigSpaceCardGeneration. This should be cleaned up, +// see CR 6897789.. void OneContigSpaceCardGeneration::object_iterate(ObjectClosure* blk) { _the_space->object_iterate(blk); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/generation.hpp --- a/src/share/vm/memory/generation.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/generation.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -181,6 +181,12 @@ virtual bool promotion_attempt_is_safe(size_t promotion_in_bytes, bool younger_handles_promotion_failure) const; + // For a non-young generation, this interface can be used to inform a + // generation that a promotion attempt into that generation failed. + // Typically used to enable diagnostic output for post-mortem analysis, + // but other uses of the interface are not ruled out. + virtual void promotion_failure_occurred() { /* does nothing */ } + // Return an estimate of the maximum allocation that could be performed // in the generation without triggering any collection or expansion // activity. It is "unsafe" because no locks are taken; the result diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/heap.cpp --- a/src/share/vm/memory/heap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/heap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -464,7 +464,7 @@ } // Verify that freelist contains the right amount of free space - guarantee(len == _free_segments, "wrong freelist"); + // guarantee(len == _free_segments, "wrong freelist"); // Verify that the number of free blocks is not out of hand. static int free_block_threshold = 10000; @@ -479,5 +479,5 @@ for(HeapBlock *h = first_block(); h != NULL; h = next_block(h)) { if (h->free()) count--; } - guarantee(count == 0, "missing free blocks"); + // guarantee(count == 0, "missing free blocks"); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/iterator.cpp --- a/src/share/vm/memory/iterator.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/iterator.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -25,6 +25,10 @@ # include "incls/_precompiled.incl" # include "incls/_iterator.cpp.incl" +#ifdef ASSERT +bool OopClosure::_must_remember_klasses = false; +#endif + void ObjectToOopClosure::do_object(oop obj) { obj->oop_iterate(_cl); } @@ -32,3 +36,52 @@ void VoidClosure::do_void() { ShouldNotCallThis(); } + +#ifdef ASSERT +bool OopClosure::must_remember_klasses() { + return _must_remember_klasses; +} +void OopClosure::set_must_remember_klasses(bool v) { + _must_remember_klasses = v; +} +#endif + + +MarkingCodeBlobClosure::MarkScope::MarkScope(bool activate) + : _active(activate) +{ + if (_active) nmethod::oops_do_marking_prologue(); +} + +MarkingCodeBlobClosure::MarkScope::~MarkScope() { + if (_active) nmethod::oops_do_marking_epilogue(); +} + +void MarkingCodeBlobClosure::do_code_blob(CodeBlob* cb) { + if (!cb->is_nmethod()) return; + nmethod* nm = (nmethod*) cb; + if (!nm->test_set_oops_do_mark()) { + NOT_PRODUCT(if (TraceScavenge) nm->print_on(tty, "oops_do, 1st visit\n")); + do_newly_marked_nmethod(nm); + } else { + NOT_PRODUCT(if (TraceScavenge) nm->print_on(tty, "oops_do, skipped on 2nd visit\n")); + } +} + +void CodeBlobToOopClosure::do_newly_marked_nmethod(nmethod* nm) { + nm->oops_do(_cl, /*do_strong_roots_only=*/ true); +} + +void CodeBlobToOopClosure::do_code_blob(CodeBlob* cb) { + if (!_do_marking) { + NOT_PRODUCT(if (TraceScavenge && Verbose && cb->is_nmethod()) ((nmethod*)cb)->print_on(tty, "oops_do, unmarked visit\n")); + // This assert won't work, since there are lots of mini-passes + // (mostly in debug mode) that co-exist with marking phases. + //assert(!(cb->is_nmethod() && ((nmethod*)cb)->test_oops_do_mark()), "found marked nmethod during mark-free phase"); + cb->oops_do(_cl); + } else { + MarkingCodeBlobClosure::do_code_blob(cb); + } +} + + diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/iterator.hpp --- a/src/share/vm/memory/iterator.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/iterator.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -24,7 +24,10 @@ // The following classes are C++ `closures` for iterating over objects, roots and spaces +class CodeBlob; +class nmethod; class ReferenceProcessor; +class DataLayout; // Closure provides abortability. @@ -54,11 +57,19 @@ // In support of post-processing of weak links of KlassKlass objects; // see KlassKlass::oop_oop_iterate(). - virtual const bool should_remember_klasses() const { return false; } + + virtual const bool should_remember_klasses() const { + assert(!must_remember_klasses(), "Should have overriden this method."); + return false; + } + virtual void remember_klass(Klass* k) { /* do nothing */ } - // If "true", invoke on nmethods (when scanning compiled frames). - virtual const bool do_nmethods() const { return false; } + // In support of post-processing of weak references in + // ProfileData (MethodDataOop) objects; see, for example, + // VirtualCallData::oop_iterate(). + virtual const bool should_remember_mdo() const { return false; } + virtual void remember_mdo(DataLayout* v) { /* do nothing */ } // The methods below control how object iterations invoking this closure // should be performed: @@ -74,6 +85,12 @@ // location without an intervening "major reset" (like the end of a GC). virtual bool idempotent() { return false; } virtual bool apply_to_weak_ref_discovered_field() { return false; } + +#ifdef ASSERT + static bool _must_remember_klasses; + static bool must_remember_klasses(); + static void set_must_remember_klasses(bool v); +#endif }; // ObjectClosure is used for iterating through an object space @@ -158,6 +175,51 @@ }; +// CodeBlobClosure is used for iterating through code blobs +// in the code cache or on thread stacks + +class CodeBlobClosure : public Closure { + public: + // Called for each code blob. + virtual void do_code_blob(CodeBlob* cb) = 0; +}; + + +class MarkingCodeBlobClosure : public CodeBlobClosure { + public: + // Called for each code blob, but at most once per unique blob. + virtual void do_newly_marked_nmethod(nmethod* nm) = 0; + + virtual void do_code_blob(CodeBlob* cb); + // = { if (!nmethod(cb)->test_set_oops_do_mark()) do_newly_marked_nmethod(cb); } + + class MarkScope : public StackObj { + protected: + bool _active; + public: + MarkScope(bool activate = true); + // = { if (active) nmethod::oops_do_marking_prologue(); } + ~MarkScope(); + // = { if (active) nmethod::oops_do_marking_epilogue(); } + }; +}; + + +// Applies an oop closure to all ref fields in code blobs +// iterated over in an object iteration. +class CodeBlobToOopClosure: public MarkingCodeBlobClosure { + OopClosure* _cl; + bool _do_marking; +public: + virtual void do_newly_marked_nmethod(nmethod* cb); + // = { cb->oops_do(_cl); } + virtual void do_code_blob(CodeBlob* cb); + // = { if (_do_marking) super::do_code_blob(cb); else cb->oops_do(_cl); } + CodeBlobToOopClosure(OopClosure* cl, bool do_marking) + : _cl(cl), _do_marking(do_marking) {} +}; + + // MonitorClosure is used for iterating over monitors in the monitors cache @@ -219,3 +281,47 @@ // correct length. virtual void do_tag(int tag) = 0; }; + +#ifdef ASSERT +// This class is used to flag phases of a collection that +// can unload classes and which should override the +// should_remember_klasses() and remember_klass() of OopClosure. +// The _must_remember_klasses is set in the contructor and restored +// in the destructor. _must_remember_klasses is checked in assertions +// in the OopClosure implementations of should_remember_klasses() and +// remember_klass() and the expectation is that the OopClosure +// implementation should not be in use if _must_remember_klasses is set. +// Instances of RememberKlassesChecker can be place in +// marking phases of collections which can do class unloading. +// RememberKlassesChecker can be passed "false" to turn off checking. +// It is used by CMS when CMS yields to a different collector. +class RememberKlassesChecker: StackObj { + bool _saved_state; + bool _do_check; + public: + RememberKlassesChecker(bool checking_on) : _saved_state(false), + _do_check(true) { + // The ClassUnloading unloading flag affects the collectors except + // for CMS. + // CMS unloads classes if CMSClassUnloadingEnabled is true or + // if ExplicitGCInvokesConcurrentAndUnloadsClasses is true and + // the current collection is an explicit collection. Turning + // on the checking in general for + // ExplicitGCInvokesConcurrentAndUnloadsClasses and + // UseConcMarkSweepGC should not lead to false positives. + _do_check = + ClassUnloading && !UseConcMarkSweepGC || + CMSClassUnloadingEnabled && UseConcMarkSweepGC || + ExplicitGCInvokesConcurrentAndUnloadsClasses && UseConcMarkSweepGC; + if (_do_check) { + _saved_state = OopClosure::must_remember_klasses(); + OopClosure::set_must_remember_klasses(checking_on); + } + } + ~RememberKlassesChecker() { + if (_do_check) { + OopClosure::set_must_remember_klasses(_saved_state); + } + } +}; +#endif // ASSERT diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/oopFactory.cpp --- a/src/share/vm/memory/oopFactory.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/oopFactory.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -98,10 +98,12 @@ } -klassOop oopFactory::new_instanceKlass(int vtable_len, int itable_len, int static_field_size, - int nonstatic_oop_map_size, ReferenceType rt, TRAPS) { +klassOop oopFactory::new_instanceKlass(int vtable_len, int itable_len, + int static_field_size, + unsigned int nonstatic_oop_map_count, + ReferenceType rt, TRAPS) { instanceKlassKlass* ikk = instanceKlassKlass::cast(Universe::instanceKlassKlassObj()); - return ikk->allocate_instance_klass(vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, CHECK_NULL); + return ikk->allocate_instance_klass(vtable_len, itable_len, static_field_size, nonstatic_oop_map_count, rt, CHECK_NULL); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/oopFactory.hpp --- a/src/share/vm/memory/oopFactory.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/oopFactory.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -89,8 +89,10 @@ TRAPS); // Instance classes - static klassOop new_instanceKlass(int vtable_len, int itable_len, int static_field_size, - int nonstatic_oop_map_size, ReferenceType rt, TRAPS); + static klassOop new_instanceKlass(int vtable_len, int itable_len, + int static_field_size, + unsigned int nonstatic_oop_map_count, + ReferenceType rt, TRAPS); // Methods private: diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/referenceProcessor.cpp --- a/src/share/vm/memory/referenceProcessor.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/referenceProcessor.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -71,7 +71,7 @@ assert(_sentinelRef == NULL, "should be initialized precisely once"); EXCEPTION_MARK; _sentinelRef = instanceKlass::cast( - SystemDictionary::reference_klass())-> + SystemDictionary::Reference_klass())-> allocate_permanent_instance(THREAD); // Initialize the master soft ref clock. @@ -299,8 +299,8 @@ template -static bool enqueue_discovered_ref_helper(ReferenceProcessor* ref, - AbstractRefProcTaskExecutor* task_executor) { +bool enqueue_discovered_ref_helper(ReferenceProcessor* ref, + AbstractRefProcTaskExecutor* task_executor) { // Remember old value of pending references list T* pending_list_addr = (T*)java_lang_ref_Reference::pending_list_addr(); @@ -1227,10 +1227,18 @@ BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc, - YieldClosure* yield) { + YieldClosure* yield, + bool should_unload_classes) { NOT_PRODUCT(verify_ok_to_handle_reflists()); +#ifdef ASSERT + bool must_remember_klasses = ClassUnloading && !UseConcMarkSweepGC || + CMSClassUnloadingEnabled && UseConcMarkSweepGC || + ExplicitGCInvokesConcurrentAndUnloadsClasses && + UseConcMarkSweepGC && should_unload_classes; + RememberKlassesChecker mx(must_remember_klasses); +#endif // Soft references { TraceTime tt("Preclean SoftReferences", PrintGCDetails && PrintReferenceGC, diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/referenceProcessor.hpp --- a/src/share/vm/memory/referenceProcessor.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/referenceProcessor.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -170,11 +170,13 @@ // The caller is responsible for taking care of potential // interference with concurrent operations on these lists // (or predicates involved) by other threads. Currently - // only used by the CMS collector. + // only used by the CMS collector. should_unload_classes is + // used to aid assertion checking when classes are collected. void preclean_discovered_references(BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc, - YieldClosure* yield); + YieldClosure* yield, + bool should_unload_classes); // Delete entries in the discovered lists that have // either a null referent or are not active. Such @@ -261,10 +263,13 @@ int parallel_gc_threads = 1, bool mt_processing = false, bool discovered_list_needs_barrier = false); + // RefDiscoveryPolicy values - enum { + enum DiscoveryPolicy { ReferenceBasedDiscovery = 0, - ReferentBasedDiscovery = 1 + ReferentBasedDiscovery = 1, + DiscoveryPolicyMin = ReferenceBasedDiscovery, + DiscoveryPolicyMax = ReferentBasedDiscovery }; static void init_statics(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/serialize.cpp --- a/src/share/vm/memory/serialize.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/serialize.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/sharedHeap.cpp --- a/src/share/vm/memory/sharedHeap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/sharedHeap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -100,12 +100,27 @@ "Not in range."); } -void SharedHeap::process_strong_roots(bool collecting_perm_gen, +SharedHeap::StrongRootsScope::StrongRootsScope(SharedHeap* outer, bool activate) + : MarkScope(activate) +{ + if (_active) { + outer->change_strong_roots_parity(); + } +} + +SharedHeap::StrongRootsScope::~StrongRootsScope() { + // nothing particular +} + +void SharedHeap::process_strong_roots(bool activate_scope, + bool collecting_perm_gen, ScanningOption so, OopClosure* roots, + CodeBlobClosure* code_roots, OopsInGenClosure* perm_blk) { + StrongRootsScope srs(this, activate_scope); // General strong roots. - if (n_par_threads() == 0) change_strong_roots_parity(); + assert(_strong_roots_parity != 0, "must have called prologue code"); if (!_process_strong_tasks->is_task_claimed(SH_PS_Universe_oops_do)) { Universe::oops_do(roots); ReferenceProcessor::oops_do(roots); @@ -117,9 +132,9 @@ JNIHandles::oops_do(roots); // All threads execute this; the individual threads are task groups. if (ParallelGCThreads > 0) { - Threads::possibly_parallel_oops_do(roots); + Threads::possibly_parallel_oops_do(roots, code_roots); } else { - Threads::oops_do(roots); + Threads::oops_do(roots, code_roots); } if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do)) ObjectSynchronizer::oops_do(roots); @@ -156,11 +171,29 @@ } if (!_process_strong_tasks->is_task_claimed(SH_PS_CodeCache_oops_do)) { - if (so & SO_CodeCache) { - CodeCache::oops_do(roots); - } + if (so & SO_CodeCache) { + // (Currently, CMSCollector uses this to do intermediate-strength collections.) + assert(collecting_perm_gen, "scanning all of code cache"); + assert(code_roots != NULL, "must supply closure for code cache"); + if (code_roots != NULL) { + CodeCache::blobs_do(code_roots); + } + } else if (so & (SO_SystemClasses|SO_AllClasses)) { + if (!collecting_perm_gen) { + // If we are collecting from class statics, but we are not going to + // visit all of the CodeCache, collect from the non-perm roots if any. + // This makes the code cache function temporarily as a source of strong + // roots for oops, until the next major collection. + // + // If collecting_perm_gen is true, we require that this phase will call + // CodeCache::do_unloading. This will kill off nmethods with expired + // weak references, such as stale invokedynamic targets. + CodeCache::scavenge_root_nmethods_do(code_roots); + } + } // Verify if the code cache contents are in the perm gen - NOT_PRODUCT(CodeCache::oops_do(&assert_is_perm_closure)); + NOT_PRODUCT(CodeBlobToOopClosure assert_code_is_perm(&assert_is_perm_closure, /*do_marking=*/ false)); + NOT_PRODUCT(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_perm)); } // Roots that should point only into permanent generation. @@ -220,11 +253,12 @@ // just skip adjusting any shared entries in the string table. void SharedHeap::process_weak_roots(OopClosure* root_closure, + CodeBlobClosure* code_roots, OopClosure* non_root_closure) { // Global (weak) JNI handles JNIHandles::weak_oops_do(&always_true, root_closure); - CodeCache::oops_do(non_root_closure); + CodeCache::blobs_do(code_roots); SymbolTable::oops_do(root_closure); if (UseSharedSpaces && !DumpSharedSpaces) { SkipAdjustingSharedStrings skip_closure(root_closure); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/sharedHeap.hpp --- a/src/share/vm/memory/sharedHeap.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/sharedHeap.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -165,9 +165,21 @@ // c) to never return a distinguished value (zero) with which such // task-claiming variables may be initialized, to indicate "never // claimed". + private: void change_strong_roots_parity(); + public: int strong_roots_parity() { return _strong_roots_parity; } + // Call these in sequential code around process_strong_roots. + // strong_roots_prologue calls change_strong_roots_parity, if + // parallel tasks are enabled. + class StrongRootsScope : public MarkingCodeBlobClosure::MarkScope { + public: + StrongRootsScope(SharedHeap* outer, bool activate = true); + ~StrongRootsScope(); + }; + friend class StrongRootsScope; + enum ScanningOption { SO_None = 0x0, SO_AllClasses = 0x1, @@ -198,21 +210,20 @@ // "SO_Symbols" applies the closure to all entries in SymbolsTable; // "SO_Strings" applies the closure to all entries in StringTable; // "SO_CodeCache" applies the closure to all elements of the CodeCache. - void process_strong_roots(bool collecting_perm_gen, + void process_strong_roots(bool activate_scope, + bool collecting_perm_gen, ScanningOption so, OopClosure* roots, + CodeBlobClosure* code_roots, OopsInGenClosure* perm_blk); // Apply "blk" to all the weak roots of the system. These include // JNI weak roots, the code cache, system dictionary, symbol table, // string table. void process_weak_roots(OopClosure* root_closure, + CodeBlobClosure* code_roots, OopClosure* non_root_closure); - - // Like CollectedHeap::collect, but assume that the caller holds the Heap_lock. - virtual void collect_locked(GCCause::Cause cause) = 0; - // The functions below are helper functions that a subclass of // "SharedHeap" can use in the implementation of its virtual // functions. diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/space.cpp --- a/src/share/vm/memory/space.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/space.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -876,7 +876,7 @@ instanceOop obj = (instanceOop) allocate(size); obj->set_mark(markOopDesc::prototype()); obj->set_klass_gap(0); - obj->set_klass(SystemDictionary::object_klass()); + obj->set_klass(SystemDictionary::Object_klass()); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/threadLocalAllocBuffer.cpp --- a/src/share/vm/memory/threadLocalAllocBuffer.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/threadLocalAllocBuffer.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -100,7 +100,7 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire) { if (end() != NULL) { invariants(); - CollectedHeap::fill_with_object(top(), hard_end()); + CollectedHeap::fill_with_object(top(), hard_end(), retire); if (retire || ZeroTLAB) { // "Reset" the TLAB set_start(NULL); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/threadLocalAllocBuffer.inline.hpp --- a/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/threadLocalAllocBuffer.inline.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, 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 @@ -27,8 +27,13 @@ HeapWord* obj = top(); if (pointer_delta(end(), obj) >= size) { // successful thread-local allocation - - DEBUG_ONLY(Copy::fill_to_words(obj, size, badHeapWordVal)); +#ifdef ASSERT + // Skip mangling the space corresponding to the object header to + // ensure that the returned space is not considered parsable by + // any concurrent GC thread. + size_t hdr_size = CollectedHeap::min_fill_size(); + Copy::fill_to_words(obj + hdr_size, size - hdr_size, badHeapWordVal); +#endif // ASSERT // This addition is safe because we know that top is // at least size below end, so the add can't wrap. set_top(obj + size); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/universe.cpp --- a/src/share/vm/memory/universe.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/universe.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -67,6 +67,8 @@ objArrayOop Universe::_the_empty_system_obj_array = NULL; objArrayOop Universe::_the_empty_class_klass_array = NULL; objArrayOop Universe::_the_array_interfaces_array = NULL; +oop Universe::_the_null_string = NULL; +oop Universe::_the_min_jint_string = NULL; LatestMethodOopCache* Universe::_finalizer_register_cache = NULL; LatestMethodOopCache* Universe::_loader_addClass_cache = NULL; ActiveMethodOopsCache* Universe::_reflect_invoke_cache = NULL; @@ -187,6 +189,8 @@ f->do_oop((oop*)&_the_empty_system_obj_array); f->do_oop((oop*)&_the_empty_class_klass_array); f->do_oop((oop*)&_the_array_interfaces_array); + f->do_oop((oop*)&_the_null_string); + f->do_oop((oop*)&_the_min_jint_string); _finalizer_register_cache->oops_do(f); _loader_addClass_cache->oops_do(f); _reflect_invoke_cache->oops_do(f); @@ -287,14 +291,17 @@ SystemDictionary::initialize(CHECK); - klassOop ok = SystemDictionary::object_klass(); + klassOop ok = SystemDictionary::Object_klass(); + + _the_null_string = StringTable::intern("null", CHECK); + _the_min_jint_string = StringTable::intern("-2147483648", CHECK); if (UseSharedSpaces) { // Verify shared interfaces array. assert(_the_array_interfaces_array->obj_at(0) == - SystemDictionary::cloneable_klass(), "u3"); + SystemDictionary::Cloneable_klass(), "u3"); assert(_the_array_interfaces_array->obj_at(1) == - SystemDictionary::serializable_klass(), "u3"); + SystemDictionary::Serializable_klass(), "u3"); // Verify element klass for system obj array klass assert(objArrayKlass::cast(_systemObjArrayKlassObj)->element_klass() == ok, "u1"); @@ -313,8 +320,8 @@ assert(Klass::cast(systemObjArrayKlassObj())->super() == ok, "u3"); } else { // Set up shared interfaces array. (Do this before supers are set up.) - _the_array_interfaces_array->obj_at_put(0, SystemDictionary::cloneable_klass()); - _the_array_interfaces_array->obj_at_put(1, SystemDictionary::serializable_klass()); + _the_array_interfaces_array->obj_at_put(0, SystemDictionary::Cloneable_klass()); + _the_array_interfaces_array->obj_at_put(1, SystemDictionary::Serializable_klass()); // Set element klass for system obj array klass objArrayKlass::cast(_systemObjArrayKlassObj)->set_element_klass(ok); @@ -358,7 +365,7 @@ // Initialize _objectArrayKlass after core bootstraping to make // sure the super class is set up properly for _objectArrayKlass. _objectArrayKlassObj = instanceKlass:: - cast(SystemDictionary::object_klass())->array_klass(1, CHECK); + cast(SystemDictionary::Object_klass())->array_klass(1, CHECK); // Add the class to the class hierarchy manually to make sure that // its vtable is initialized after core bootstrapping is completed. Klass::cast(_objectArrayKlassObj)->append_to_sibling_list(); @@ -419,11 +426,11 @@ while (i < size) { if (!UseConcMarkSweepGC) { // Allocate dummy in old generation - oop dummy = instanceKlass::cast(SystemDictionary::object_klass())->allocate_instance(CHECK); + oop dummy = instanceKlass::cast(SystemDictionary::Object_klass())->allocate_instance(CHECK); dummy_array->obj_at_put(i++, dummy); } // Allocate dummy in permanent generation - oop dummy = instanceKlass::cast(SystemDictionary::object_klass())->allocate_permanent_instance(CHECK); + oop dummy = instanceKlass::cast(SystemDictionary::Object_klass())->allocate_permanent_instance(CHECK); dummy_array->obj_at_put(i++, dummy); } { @@ -533,7 +540,7 @@ // but we cannot do that for classes created before java.lang.Class is loaded. Here we simply // walk over permanent objects created so far (mostly classes) and fixup their mirrors. Note // that the number of objects allocated at this point is very small. - assert(SystemDictionary::class_klass_loaded(), "java.lang.Class should be loaded"); + assert(SystemDictionary::Class_klass_loaded(), "java.lang.Class should be loaded"); FixupMirrorClosure blk; Universe::heap()->permanent_object_iterate(&blk); } @@ -549,7 +556,7 @@ if (TraceReferenceGC) tty->print_cr("Callback to run finalizers on exit"); { PRESERVE_EXCEPTION_MARK; - KlassHandle finalizer_klass(THREAD, SystemDictionary::finalizer_klass()); + KlassHandle finalizer_klass(THREAD, SystemDictionary::Finalizer_klass()); JavaValue result(T_VOID); JavaCalls::call_static( &result, @@ -744,19 +751,22 @@ static const uint64_t OopEncodingHeapMax = NarrowOopHeapMax << LogMinObjAlignmentInBytes; char* Universe::preferred_heap_base(size_t heap_size, NARROW_OOP_MODE mode) { + size_t base = 0; #ifdef _LP64 if (UseCompressedOops) { assert(mode == UnscaledNarrowOop || mode == ZeroBasedNarrowOop || mode == HeapBasedNarrowOop, "mode is invalid"); - const size_t total_size = heap_size + HeapBaseMinAddress; - if (total_size <= OopEncodingHeapMax && (mode != HeapBasedNarrowOop)) { + // Return specified base for the first request. + if (!FLAG_IS_DEFAULT(HeapBaseMinAddress) && (mode == UnscaledNarrowOop)) { + base = HeapBaseMinAddress; + } else if (total_size <= OopEncodingHeapMax && (mode != HeapBasedNarrowOop)) { if (total_size <= NarrowOopHeapMax && (mode == UnscaledNarrowOop) && (Universe::narrow_oop_shift() == 0)) { // Use 32-bits oops without encoding and // place heap's top on the 4Gb boundary - return (char*)(NarrowOopHeapMax - heap_size); + base = (NarrowOopHeapMax - heap_size); } else { // Can't reserve with NarrowOopShift == 0 Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes); @@ -765,16 +775,38 @@ // Use zero based compressed oops with encoding and // place heap's top on the 32Gb boundary in case // total_size > 4Gb or failed to reserve below 4Gb. - return (char*)(OopEncodingHeapMax - heap_size); + base = (OopEncodingHeapMax - heap_size); } } } else { // Can't reserve below 32Gb. Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes); } + // Set narrow_oop_base and narrow_oop_use_implicit_null_checks + // used in ReservedHeapSpace() constructors. + // The final values will be set in initialize_heap() below. + if (base != 0 && (base + heap_size) <= OopEncodingHeapMax) { + // Use zero based compressed oops + Universe::set_narrow_oop_base(NULL); + // Don't need guard page for implicit checks in indexed + // addressing mode with zero based Compressed Oops. + Universe::set_narrow_oop_use_implicit_null_checks(true); + } else { + // Set to a non-NULL value so the ReservedSpace ctor computes + // the correct no-access prefix. + // The final value will be set in initialize_heap() below. + Universe::set_narrow_oop_base((address)NarrowOopHeapMax); +#ifdef _WIN64 + if (UseLargePages) { + // Cannot allocate guard pages for implicit checks in indexed + // addressing mode when large pages are specified on windows. + Universe::set_narrow_oop_use_implicit_null_checks(false); + } +#endif // _WIN64 + } } #endif - return NULL; // also return NULL (don't care) for 32-bit VM + return (char*)base; // also return NULL (don't care) for 32-bit VM } jint Universe::initialize_heap() { @@ -857,7 +889,7 @@ // Can't reserve heap below 4Gb. Universe::set_narrow_oop_shift(LogMinObjAlignmentInBytes); } else { - assert(Universe::narrow_oop_shift() == 0, "use unscaled narrow oop"); + Universe::set_narrow_oop_shift(0); if (PrintCompressedOopsMode) { tty->print(", 32-bits Oops"); } @@ -918,7 +950,7 @@ { ResourceMark rm; Interpreter::initialize(); // needed for interpreter entry points if (!UseSharedSpaces) { - KlassHandle ok_h(THREAD, SystemDictionary::object_klass()); + KlassHandle ok_h(THREAD, SystemDictionary::Object_klass()); Universe::reinitialize_vtable_of(ok_h, CHECK_false); Universe::reinitialize_itables(CHECK_false); } @@ -928,7 +960,7 @@ instanceKlassHandle k_h; if (!UseSharedSpaces) { // Setup preallocated empty java.lang.Class array - Universe::_the_empty_class_klass_array = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_false); + Universe::_the_empty_class_klass_array = oopFactory::new_objArray(SystemDictionary::Class_klass(), 0, CHECK_false); // Setup preallocated OutOfMemoryError errors k = SystemDictionary::resolve_or_fail(vmSymbolHandles::java_lang_OutOfMemoryError(), true, CHECK_false); k_h = instanceKlassHandle(THREAD, k); @@ -995,8 +1027,8 @@ // Setup static method for registering finalizers // The finalizer klass must be linked before looking up the method, in // case it needs to get rewritten. - instanceKlass::cast(SystemDictionary::finalizer_klass())->link_class(CHECK_false); - methodOop m = instanceKlass::cast(SystemDictionary::finalizer_klass())->find_method( + instanceKlass::cast(SystemDictionary::Finalizer_klass())->link_class(CHECK_false); + methodOop m = instanceKlass::cast(SystemDictionary::Finalizer_klass())->find_method( vmSymbols::register_method_name(), vmSymbols::register_method_signature()); if (m == NULL || !m->is_static()) { @@ -1004,7 +1036,7 @@ "java.lang.ref.Finalizer.register", false); } Universe::_finalizer_register_cache->init( - SystemDictionary::finalizer_klass(), m, CHECK_false); + SystemDictionary::Finalizer_klass(), m, CHECK_false); // Resolve on first use and initialize class. // Note: No race-condition here, since a resolve will always return the same result @@ -1021,14 +1053,14 @@ Universe::_reflect_invoke_cache->init(k_h(), m, CHECK_false); // Setup method for registering loaded classes in class loader vector - instanceKlass::cast(SystemDictionary::classloader_klass())->link_class(CHECK_false); - m = instanceKlass::cast(SystemDictionary::classloader_klass())->find_method(vmSymbols::addClass_name(), vmSymbols::class_void_signature()); + instanceKlass::cast(SystemDictionary::ClassLoader_klass())->link_class(CHECK_false); + m = instanceKlass::cast(SystemDictionary::ClassLoader_klass())->find_method(vmSymbols::addClass_name(), vmSymbols::class_void_signature()); if (m == NULL || m->is_static()) { THROW_MSG_(vmSymbols::java_lang_NoSuchMethodException(), "java.lang.ClassLoader.addClass", false); } Universe::_loader_addClass_cache->init( - SystemDictionary::classloader_klass(), m, CHECK_false); + SystemDictionary::ClassLoader_klass(), m, CHECK_false); // The folowing is initializing converter functions for serialization in // JVM.cpp. If we clean up the StrictMath code above we may want to find diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/memory/universe.hpp --- a/src/share/vm/memory/universe.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/memory/universe.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -169,6 +169,8 @@ static objArrayOop _the_empty_system_obj_array; // Canonicalized system obj array static objArrayOop _the_empty_class_klass_array; // Canonicalized obj array of type java.lang.Class static objArrayOop _the_array_interfaces_array; // Canonicalized 2-array of cloneable & serializable klasses + static oop _the_null_string; // A cache of "null" as a Java string + static oop _the_min_jint_string; // A cache of "-2147483648" as a Java string static LatestMethodOopCache* _finalizer_register_cache; // static method for registering finalizable objects static LatestMethodOopCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector static ActiveMethodOopsCache* _reflect_invoke_cache; // method for security checks @@ -310,6 +312,8 @@ static objArrayOop the_empty_system_obj_array () { return _the_empty_system_obj_array; } static objArrayOop the_empty_class_klass_array () { return _the_empty_class_klass_array; } static objArrayOop the_array_interfaces_array() { return _the_array_interfaces_array; } + static oop the_null_string() { return _the_null_string; } + static oop the_min_jint_string() { return _the_min_jint_string; } static methodOop finalizer_register_method() { return _finalizer_register_cache->get_methodOop(); } static methodOop loader_addClass_method() { return _loader_addClass_cache->get_methodOop(); } static ActiveMethodOopsCache* reflect_invoke_cache() { return _reflect_invoke_cache; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/arrayKlass.cpp --- a/src/share/vm/oops/arrayKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/arrayKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -43,7 +43,7 @@ if (super() == NULL) return NULL; // bootstrap case // Array klasses have primary supertypes which are not reported to Java. // Example super chain: String[][] -> Object[][] -> Object[] -> Object - return SystemDictionary::object_klass(); + return SystemDictionary::Object_klass(); } @@ -82,7 +82,7 @@ k = arrayKlassHandle(THREAD, base_klass()); assert(!k()->is_parsable(), "not expecting parsability yet."); - k->set_super(Universe::is_bootstrapping() ? (klassOop)NULL : SystemDictionary::object_klass()); + k->set_super(Universe::is_bootstrapping() ? (klassOop)NULL : SystemDictionary::Object_klass()); k->set_layout_helper(Klass::_lh_neutral_value); k->set_dimension(1); k->set_higher_dimension(NULL); @@ -117,9 +117,9 @@ bool arrayKlass::compute_is_subtype_of(klassOop k) { // An array is a subtype of Serializable, Clonable, and Object - return k == SystemDictionary::object_klass() - || k == SystemDictionary::cloneable_klass() - || k == SystemDictionary::serializable_klass(); + return k == SystemDictionary::Object_klass() + || k == SystemDictionary::Cloneable_klass() + || k == SystemDictionary::Serializable_klass(); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/arrayKlass.hpp --- a/src/share/vm/oops/arrayKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/arrayKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -67,7 +67,7 @@ // Compiler/Interpreter offset static ByteSize component_mirror_offset() { return byte_offset_of(arrayKlass, _component_mirror); } - virtual klassOop java_super() const;//{ return SystemDictionary::object_klass(); } + virtual klassOop java_super() const;//{ return SystemDictionary::Object_klass(); } // Allocation // Sizes points to the first dimension of the array, subsequent dimensions diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/arrayKlassKlass.cpp --- a/src/share/vm/oops/arrayKlassKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/arrayKlassKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -159,7 +159,7 @@ assert(obj->is_klass(), "must be klass"); klassKlass::oop_print_on(obj, st); } - +#endif //PRODUCT void arrayKlassKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_klass(), "must be klass"); @@ -168,7 +168,6 @@ st->print("[]"); } } -#endif const char* arrayKlassKlass::internal_name() const { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/arrayKlassKlass.hpp --- a/src/share/vm/oops/arrayKlassKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/arrayKlassKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -55,14 +55,13 @@ int oop_oop_iterate(oop obj, OopClosure* blk); int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/compiledICHolderKlass.cpp --- a/src/share/vm/oops/compiledICHolderKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/compiledICHolderKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -166,12 +166,12 @@ st->print(" - klass: "); c->holder_klass()->print_value_on(st); st->cr(); } +#endif //PRODUCT void compiledICHolderKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_compiledICHolder(), "must be compiledICHolder"); Klass::oop_print_value_on(obj, st); } -#endif const char* compiledICHolderKlass::internal_name() const { return "{compiledICHolder}"; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/compiledICHolderKlass.hpp --- a/src/share/vm/oops/compiledICHolderKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/compiledICHolderKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -68,14 +68,13 @@ int oop_oop_iterate(oop obj, OopClosure* blk); int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constMethodKlass.cpp --- a/src/share/vm/oops/constMethodKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constMethodKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -216,6 +216,7 @@ } } +#endif //PRODUCT // Short version of printing constMethodOop - just print the name of the // method it belongs to. @@ -226,8 +227,6 @@ m->method()->print_value_on(st); } -#endif // PRODUCT - const char* constMethodKlass::internal_name() const { return "{constMethod}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constMethodKlass.hpp --- a/src/share/vm/oops/constMethodKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constMethodKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -77,15 +77,13 @@ int oop_oop_iterate(oop obj, OopClosure* blk); int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); +#endif //PRODUCT -#endif - - public: // Verify operations const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constMethodOop.hpp --- a/src/share/vm/oops/constMethodOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constMethodOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -258,6 +258,11 @@ LocalVariableTableElement* localvariable_table_start() const; // byte codes + void set_code(address code) { + if (code_size() > 0) { + memcpy(code_base(), code, code_size()); + } + } address code_base() const { return (address) (this+1); } address code_end() const { return code_base() + code_size(); } bool contains(address bcp) const { return code_base() <= bcp diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constantPoolKlass.cpp --- a/src/share/vm/oops/constantPoolKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constantPoolKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -387,8 +387,18 @@ cp->set_cache(cache()); } +#endif -#endif +void constantPoolKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_constantPool(), "must be constantPool"); + constantPoolOop cp = constantPoolOop(obj); + st->print("constant pool [%d]", cp->length()); + if (cp->has_pseudo_string()) st->print("/pseudo_string"); + if (cp->has_invokedynamic()) st->print("/invokedynamic"); + cp->print_address_on(st); + st->print(" for "); + cp->pool_holder()->print_value_on(st); +} const char* constantPoolKlass::internal_name() const { return "{constant pool}"; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constantPoolKlass.hpp --- a/src/share/vm/oops/constantPoolKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constantPoolKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -65,9 +65,10 @@ juint alloc_size() const { return _alloc_size; } void set_alloc_size(juint n) { _alloc_size = n; } -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constantPoolOop.cpp --- a/src/share/vm/oops/constantPoolOop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constantPoolOop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -110,7 +110,7 @@ } if (!PENDING_EXCEPTION-> - is_a(SystemDictionary::linkageError_klass())) { + is_a(SystemDictionary::LinkageError_klass())) { // Just throw the exception and don't prevent these classes from // being loaded due to virtual machine errors like StackOverflow // and OutOfMemoryError, etc, or if the thread was hit by stop() @@ -262,25 +262,48 @@ int constantPoolOopDesc::impl_name_and_type_ref_index_at(int which, bool uncached) { - jint ref_index = field_or_method_at(which, uncached); + int i = which; + if (!uncached && cache() != NULL) { + if (constantPoolCacheOopDesc::is_secondary_index(which)) + // Invokedynamic indexes are always processed in native order + // so there is no question of reading a native u2 in Java order here. + return cache()->main_entry_at(which)->constant_pool_index(); + // change byte-ordering and go via cache + i = remap_instruction_operand_from_cache(which); + } else { + if (tag_at(which).is_name_and_type()) + // invokedynamic index is a simple name-and-type + return which; + } + assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); + jint ref_index = *int_at_addr(i); return extract_high_short_from_int(ref_index); } int constantPoolOopDesc::impl_klass_ref_index_at(int which, bool uncached) { - jint ref_index = field_or_method_at(which, uncached); + guarantee(!constantPoolCacheOopDesc::is_secondary_index(which), + "an invokedynamic instruction does not have a klass"); + int i = which; + if (!uncached && cache() != NULL) { + // change byte-ordering and go via cache + i = remap_instruction_operand_from_cache(which); + } + assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); + jint ref_index = *int_at_addr(i); return extract_low_short_from_int(ref_index); } -int constantPoolOopDesc::map_instruction_operand_to_index(int operand) { - if (constantPoolCacheOopDesc::is_secondary_index(operand)) { - return cache()->main_entry_at(operand)->constant_pool_index(); - } +int constantPoolOopDesc::remap_instruction_operand_from_cache(int operand) { + // Operand was fetched by a stream using get_Java_u2, yet was stored + // by Rewriter::rewrite_member_reference in native order. + // So now we have to fix the damage by swapping back to native order. assert((int)(u2)operand == operand, "clean u2"); - int index = Bytes::swap_u2(operand); - return cache()->entry_at(index)->constant_pool_index(); + int cpc_index = Bytes::swap_u2(operand); + int member_index = cache()->entry_at(cpc_index)->constant_pool_index(); + return member_index; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/constantPoolOop.hpp --- a/src/share/vm/oops/constantPoolOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/constantPoolOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -191,6 +191,16 @@ } } + void object_at_put(int which, oop str) { + oop_store((volatile oop*) obj_at_addr(which), str); + release_tag_at_put(which, JVM_CONSTANT_Object); + if (UseConcMarkSweepGC) { + // In case the earlier card-mark was consumed by a concurrent + // marking thread before the tag was updated, redirty the card. + oop_store_without_check((volatile oop*) obj_at_addr(which), str); + } + } + // For temporary use while constructing constant pool void string_index_at_put(int which, int string_index) { tag_at_put(which, JVM_CONSTANT_StringIndex); @@ -228,7 +238,8 @@ tag.is_unresolved_klass() || tag.is_symbol() || tag.is_unresolved_string() || - tag.is_string(); + tag.is_string() || + tag.is_object(); } // Fetching constants @@ -291,6 +302,11 @@ return string_at_impl(h_this, which, CHECK_NULL); } + oop object_at(int which) { + assert(tag_at(which).is_object(), "Corrupted constant pool"); + return *obj_at_addr(which); + } + // A "pseudo-string" is an non-string oop that has found is way into // a String entry. // Under AnonymousClasses this can happen if the user patches a live @@ -342,12 +358,14 @@ } // The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve, - // name_and_type_ref_index_at) all expect constant pool indices - // from the bytecodes to be passed in, which are actually potentially byte-swapped - // or rewritten constant pool cache indices. They all call map_instruction_operand_to_index. - int map_instruction_operand_to_index(int operand); + // name_and_type_ref_index_at) all expect to be passed indices obtained + // directly from the bytecode, and extracted according to java byte order. + // If the indices are meant to refer to fields or methods, they are + // actually potentially byte-swapped, rewritten constant pool cache indices. + // The routine remap_instruction_operand_from_cache manages the adjustment + // of these values back to constant pool indices. - // There are also "uncached" versions which do not map the operand index; see below. + // There are also "uncached" versions which do not adjust the operand index; see below. // Lookup for entries consisting of (klass_index, name_and_type index) klassOop klass_ref_at(int which, TRAPS); @@ -361,8 +379,6 @@ // Lookup for entries consisting of (name_index, signature_index) int name_ref_index_at(int which_nt); // == low-order jshort of name_and_type_at(which_nt) int signature_ref_index_at(int which_nt); // == high-order jshort of name_and_type_at(which_nt) - symbolOop nt_name_ref_at(int which_nt) { return symbol_at(name_ref_index_at(which_nt)); } - symbolOop nt_signature_ref_at(int which_nt) { return symbol_at(signature_ref_index_at(which_nt)); } BasicType basic_type_for_signature_at(int which); @@ -425,18 +441,7 @@ int impl_klass_ref_index_at(int which, bool uncached); int impl_name_and_type_ref_index_at(int which, bool uncached); - // Takes either a constant pool cache index in possibly byte-swapped - // byte order (which comes from the bytecodes after rewriting) or, - // if "uncached" is true, a vanilla constant pool index - jint field_or_method_at(int which, bool uncached) { - int i = which; - if (!uncached && cache() != NULL) { - // change byte-ordering and go via cache - i = map_instruction_operand_to_index(which); - } - assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); - return *int_at_addr(i); - } + int remap_instruction_operand_from_cache(int operand); // Used while constructing constant pool (only by ClassFileParser) jint klass_index_at(int which) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/cpCacheKlass.cpp --- a/src/share/vm/oops/cpCacheKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/cpCacheKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -261,6 +261,15 @@ #endif +void constantPoolCacheKlass::oop_print_value_on(oop obj, outputStream* st) { + assert(obj->is_constantPoolCache(), "obj must be constant pool cache"); + constantPoolCacheOop cache = (constantPoolCacheOop)obj; + st->print("cache [%d]", cache->length()); + cache->print_address_on(st); + st->print(" for "); + cache->constant_pool()->print_value_on(st); +} + void constantPoolCacheKlass::oop_verify_on(oop obj, outputStream* st) { guarantee(obj->is_constantPoolCache(), "obj must be constant pool cache"); constantPoolCacheOop cache = (constantPoolCacheOop)obj; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/cpCacheKlass.hpp --- a/src/share/vm/oops/cpCacheKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/cpCacheKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -61,9 +61,10 @@ juint alloc_size() const { return _alloc_size; } void set_alloc_size(juint n) { _alloc_size = n; } -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/cpCacheOop.cpp --- a/src/share/vm/oops/cpCacheOop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/cpCacheOop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -28,21 +28,17 @@ // Implememtation of ConstantPoolCacheEntry -void ConstantPoolCacheEntry::set_initial_state(int index) { - if (constantPoolCacheOopDesc::is_secondary_index(index)) { - // Hack: The rewriter is trying to say that this entry itself - // will be a secondary entry. - int main_index = constantPoolCacheOopDesc::decode_secondary_index(index); - assert(0 <= main_index && main_index < 0x10000, "sanity check"); - _indices = (main_index << 16); - assert(main_entry_index() == main_index, ""); - return; - } +void ConstantPoolCacheEntry::initialize_entry(int index) { assert(0 < index && index < 0x10000, "sanity check"); _indices = index; assert(constant_pool_index() == index, ""); } +void ConstantPoolCacheEntry::initialize_secondary_entry(int main_index) { + assert(0 <= main_index && main_index < 0x10000, "sanity check"); + _indices = (main_index << 16); + assert(main_entry_index() == main_index, ""); +} int ConstantPoolCacheEntry::as_flags(TosState state, bool is_final, bool is_vfinal, bool is_volatile, @@ -223,10 +219,10 @@ void ConstantPoolCacheEntry::set_dynamic_call(Handle call_site, int extra_data) { - methodOop method = (methodOop) sun_dyn_CallSiteImpl::vmmethod(call_site()); + methodOop method = (methodOop) java_dyn_CallSite::vmmethod(call_site()); assert(method->is_method(), "must be initialized properly"); int param_size = method->size_of_parameters(); - assert(param_size > 1, "method argument size must include MH.this & initial dynamic receiver"); + assert(param_size >= 1, "method argument size must include MH.this"); param_size -= 1; // do not count MH.this; it is not stacked for invokedynamic if (Atomic::cmpxchg_ptr(call_site(), &_f1, NULL) == NULL) { // racing threads might be trying to install their own favorites @@ -439,7 +435,18 @@ void constantPoolCacheOopDesc::initialize(intArray& inverse_index_map) { assert(inverse_index_map.length() == length(), "inverse index map must have same length as cache"); - for (int i = 0; i < length(); i++) entry_at(i)->set_initial_state(inverse_index_map[i]); + for (int i = 0; i < length(); i++) { + ConstantPoolCacheEntry* e = entry_at(i); + int original_index = inverse_index_map[i]; + if ((original_index & Rewriter::_secondary_entry_tag) != 0) { + int main_index = (original_index - Rewriter::_secondary_entry_tag); + assert(!entry_at(main_index)->is_secondary_entry(), "valid main index"); + e->initialize_secondary_entry(main_index); + } else { + e->initialize_entry(original_index); + } + assert(entry_at(i) == e, "sanity"); + } } // RedefineClasses() API support: diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/cpCacheOop.hpp --- a/src/share/vm/oops/cpCacheOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/cpCacheOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -154,7 +154,8 @@ }; // Initialization - void set_initial_state(int index); // sets entry to initial state + void initialize_entry(int original_index); // initialize primary entry + void initialize_secondary_entry(int main_index); // initialize secondary entry void set_field( // sets entry to resolved field state Bytecodes::Code get_code, // the bytecode used for reading the field @@ -251,6 +252,7 @@ // Code generation support static WordSize size() { return in_WordSize(sizeof(ConstantPoolCacheEntry) / HeapWordSize); } + static ByteSize size_in_bytes() { return in_ByteSize(sizeof(ConstantPoolCacheEntry)); } static ByteSize indices_offset() { return byte_offset_of(ConstantPoolCacheEntry, _indices); } static ByteSize f1_offset() { return byte_offset_of(ConstantPoolCacheEntry, _f1); } static ByteSize f2_offset() { return byte_offset_of(ConstantPoolCacheEntry, _f2); } @@ -321,6 +323,7 @@ ConstantPoolCacheEntry* base() const { return (ConstantPoolCacheEntry*)((address)this + in_bytes(base_offset())); } friend class constantPoolCacheKlass; + friend class ConstantPoolCacheEntry; public: // Initialization @@ -329,7 +332,8 @@ // Secondary indexes. // They must look completely different from normal indexes. // The main reason is that byte swapping is sometimes done on normal indexes. - // Also, it is helpful for debugging to tell the two apart. + // Also, some of the CP accessors do different things for secondary indexes. + // Finally, it is helpful for debugging to tell the two apart. static bool is_secondary_index(int i) { return (i < 0); } static int decode_secondary_index(int i) { assert(is_secondary_index(i), ""); return ~i; } static int encode_secondary_index(int i) { assert(!is_secondary_index(i), ""); return ~i; } @@ -337,18 +341,35 @@ // Accessors void set_constant_pool(constantPoolOop pool) { oop_store_without_check((oop*)&_constant_pool, (oop)pool); } constantPoolOop constant_pool() const { return _constant_pool; } - ConstantPoolCacheEntry* entry_at(int i) const { assert(0 <= i && i < length(), "index out of bounds"); return base() + i; } + // Fetches the entry at the given index. + // The entry may be either primary or secondary. + // In either case the index must not be encoded or byte-swapped in any way. + ConstantPoolCacheEntry* entry_at(int i) const { + assert(0 <= i && i < length(), "index out of bounds"); + return base() + i; + } + // Fetches the secondary entry referred to by index. + // The index may be a secondary index, and must not be byte-swapped. + ConstantPoolCacheEntry* secondary_entry_at(int i) const { + int raw_index = i; + if (is_secondary_index(i)) { // correct these on the fly + raw_index = decode_secondary_index(i); + } + assert(entry_at(raw_index)->is_secondary_entry(), "not a secondary entry"); + return entry_at(raw_index); + } + // Given a primary or secondary index, fetch the corresponding primary entry. + // Indirect through the secondary entry, if the index is encoded as a secondary index. + // The index must not be byte-swapped. ConstantPoolCacheEntry* main_entry_at(int i) const { - ConstantPoolCacheEntry* e; + int primary_index = i; if (is_secondary_index(i)) { // run through an extra level of indirection: - i = decode_secondary_index(i); - e = entry_at(i); - i = e->main_entry_index(); + int raw_index = decode_secondary_index(i); + primary_index = entry_at(raw_index)->main_entry_index(); } - e = entry_at(i); - assert(!e->is_secondary_entry(), "only one level of indirection"); - return e; + assert(!entry_at(primary_index)->is_secondary_entry(), "only one level of indirection"); + return entry_at(primary_index); } // GC support @@ -359,6 +380,12 @@ // Code generation static ByteSize base_offset() { return in_ByteSize(sizeof(constantPoolCacheOopDesc)); } + static ByteSize entry_offset(int raw_index) { + int index = raw_index; + if (is_secondary_index(raw_index)) + index = decode_secondary_index(raw_index); + return (base_offset() + ConstantPoolCacheEntry::size_in_bytes() * index); + } // RedefineClasses() API support: // If any entry of this constantPoolCache points to any of diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/generateOopMap.cpp --- a/src/share/vm/oops/generateOopMap.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/generateOopMap.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1559,7 +1559,7 @@ case Bytecodes::_invokevirtual: case Bytecodes::_invokespecial: do_method(false, false, itr->get_index_big(), itr->bci()); break; case Bytecodes::_invokestatic: do_method(true, false, itr->get_index_big(), itr->bci()); break; - case Bytecodes::_invokedynamic: do_method(false, true, itr->get_index_int(), itr->bci()); break; + case Bytecodes::_invokedynamic: do_method(true, false, itr->get_index_int(), itr->bci()); break; case Bytecodes::_invokeinterface: do_method(false, true, itr->get_index_big(), itr->bci()); break; case Bytecodes::_newarray: case Bytecodes::_anewarray: pp_new_ref(vCTS, itr->bci()); break; @@ -1830,12 +1830,8 @@ void GenerateOopMap::do_ldc(int idx, int bci) { - constantPoolOop cp = method()->constants(); - constantTag tag = cp->tag_at(idx); - - CellTypeState cts = (tag.is_string() || tag.is_unresolved_string() || - tag.is_klass() || tag.is_unresolved_klass()) - ? CellTypeState::make_line_ref(bci) : valCTS; + constantPoolOop cp = method()->constants(); + CellTypeState cts = cp->is_pointer_entry(idx) ? CellTypeState::make_line_ref(bci) : valCTS; ppush1(cts); } @@ -1900,11 +1896,9 @@ } void GenerateOopMap::do_method(int is_static, int is_interface, int idx, int bci) { - // Dig up signature for field in constant pool - constantPoolOop cp = _method->constants(); - int nameAndTypeIdx = cp->name_and_type_ref_index_at(idx); - int signatureIdx = cp->signature_ref_index_at(nameAndTypeIdx); // @@@@@ - symbolOop signature = cp->symbol_at(signatureIdx); + // Dig up signature for field in constant pool + constantPoolOop cp = _method->constants(); + symbolOop signature = cp->signature_ref_at(idx); // Parse method signature CellTypeState out[4]; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/instanceKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,6 +25,58 @@ # include "incls/_precompiled.incl" # include "incls/_instanceKlass.cpp.incl" +#ifdef DTRACE_ENABLED + +HS_DTRACE_PROBE_DECL4(hotspot, class__initialization__required, + char*, intptr_t, oop, intptr_t); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__recursive, + char*, intptr_t, oop, intptr_t, int); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__concurrent, + char*, intptr_t, oop, intptr_t, int); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__erroneous, + char*, intptr_t, oop, intptr_t, int); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__super__failed, + char*, intptr_t, oop, intptr_t, int); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__clinit, + char*, intptr_t, oop, intptr_t, int); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__error, + char*, intptr_t, oop, intptr_t, int); +HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__end, + char*, intptr_t, oop, intptr_t, int); + +#define DTRACE_CLASSINIT_PROBE(type, clss, thread_type) \ + { \ + char* data = NULL; \ + int len = 0; \ + symbolOop name = (clss)->name(); \ + if (name != NULL) { \ + data = (char*)name->bytes(); \ + len = name->utf8_length(); \ + } \ + HS_DTRACE_PROBE4(hotspot, class__initialization__##type, \ + data, len, (clss)->class_loader(), thread_type); \ + } + +#define DTRACE_CLASSINIT_PROBE_WAIT(type, clss, thread_type, wait) \ + { \ + char* data = NULL; \ + int len = 0; \ + symbolOop name = (clss)->name(); \ + if (name != NULL) { \ + data = (char*)name->bytes(); \ + len = name->utf8_length(); \ + } \ + HS_DTRACE_PROBE5(hotspot, class__initialization__##type, \ + data, len, (clss)->class_loader(), thread_type, wait); \ + } + +#else // ndef DTRACE_ENABLED + +#define DTRACE_CLASSINIT_PROBE(type, clss, thread_type) +#define DTRACE_CLASSINIT_PROBE_WAIT(type, clss, thread_type, wait) + +#endif // ndef DTRACE_ENABLED + bool instanceKlass::should_be_initialized() const { return !is_initialized(); } @@ -110,7 +162,7 @@ // 1) Verify the bytecodes Verifier::Mode mode = throw_verifyerror ? Verifier::ThrowException : Verifier::NoException; - return Verifier::verify(this_oop, mode, CHECK_false); + return Verifier::verify(this_oop, mode, this_oop->should_verify_class(), CHECK_false); } @@ -292,6 +344,10 @@ // A class could already be verified, since it has been reflected upon. this_oop->link_class(CHECK); + DTRACE_CLASSINIT_PROBE(required, instanceKlass::cast(this_oop()), -1); + + bool wait = false; + // refer to the JVM book page 47 for description of steps // Step 1 { ObjectLocker ol(this_oop, THREAD); @@ -303,19 +359,25 @@ // we might end up throwing IE from link/symbol resolution sites // that aren't expected to throw. This would wreak havoc. See 6320309. while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) { + wait = true; ol.waitUninterruptibly(CHECK); } // Step 3 - if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) + if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) { + DTRACE_CLASSINIT_PROBE_WAIT(recursive, instanceKlass::cast(this_oop()), -1,wait); return; + } // Step 4 - if (this_oop->is_initialized()) + if (this_oop->is_initialized()) { + DTRACE_CLASSINIT_PROBE_WAIT(concurrent, instanceKlass::cast(this_oop()), -1,wait); return; + } // Step 5 if (this_oop->is_in_error_state()) { + DTRACE_CLASSINIT_PROBE_WAIT(erroneous, instanceKlass::cast(this_oop()), -1,wait); ResourceMark rm(THREAD); const char* desc = "Could not initialize class "; const char* className = this_oop->external_name(); @@ -348,6 +410,7 @@ this_oop->set_initialization_state_and_notify(initialization_error, THREAD); // Locks object, set state, and notify all waiting threads CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, superclass initialization error is thrown below } + DTRACE_CLASSINIT_PROBE_WAIT(super__failed, instanceKlass::cast(this_oop()), -1,wait); THROW_OOP(e()); } } @@ -356,6 +419,7 @@ { assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); JavaThread* jt = (JavaThread*)THREAD; + DTRACE_CLASSINIT_PROBE_WAIT(clinit, instanceKlass::cast(this_oop()), -1,wait); // Timer includes any side effects of class initialization (resolution, // etc), but not recursive entry into call_class_initializer(). PerfClassTraceTime timer(ClassLoader::perf_class_init_time(), @@ -383,7 +447,8 @@ this_oop->set_initialization_state_and_notify(initialization_error, THREAD); CLEAR_PENDING_EXCEPTION; // ignore any exception thrown, class initialization error is thrown below } - if (e->is_a(SystemDictionary::error_klass())) { + DTRACE_CLASSINIT_PROBE_WAIT(error, instanceKlass::cast(this_oop()), -1,wait); + if (e->is_a(SystemDictionary::Error_klass())) { THROW_OOP(e()); } else { JavaCallArguments args(e); @@ -392,6 +457,7 @@ &args); } } + DTRACE_CLASSINIT_PROBE_WAIT(end, instanceKlass::cast(this_oop()), -1,wait); } @@ -568,7 +634,7 @@ THROW_MSG(throwError ? vmSymbols::java_lang_InstantiationError() : vmSymbols::java_lang_InstantiationException(), external_name()); } - if (as_klassOop() == SystemDictionary::class_klass()) { + if (as_klassOop() == SystemDictionary::Class_klass()) { ResourceMark rm(THREAD); THROW_MSG(throwError ? vmSymbols::java_lang_IllegalAccessError() : vmSymbols::java_lang_IllegalAccessException(), external_name()); @@ -967,33 +1033,78 @@ // Lookup or create a jmethodID. -// This code can be called by the VM thread. For this reason it is critical that -// there are no blocking operations (safepoints) while the lock is held -- or a -// deadlock can occur. -jmethodID instanceKlass::jmethod_id_for_impl(instanceKlassHandle ik_h, methodHandle method_h) { +// This code is called by the VMThread and JavaThreads so the +// locking has to be done very carefully to avoid deadlocks +// and/or other cache consistency problems. +// +jmethodID instanceKlass::get_jmethod_id(instanceKlassHandle ik_h, methodHandle method_h) { size_t idnum = (size_t)method_h->method_idnum(); jmethodID* jmeths = ik_h->methods_jmethod_ids_acquire(); size_t length = 0; jmethodID id = NULL; - // array length stored in first element, other elements offset by one - if (jmeths == NULL || // If there is no jmethodID array, - (length = (size_t)jmeths[0]) <= idnum || // or if it is too short, - (id = jmeths[idnum+1]) == NULL) { // or if this jmethodID isn't allocated - - // Do all the safepointing things (allocations) before grabbing the lock. - // These allocations will have to be freed if they are unused. - - // Allocate a new array of methods. + + // We use a double-check locking idiom here because this cache is + // performance sensitive. In the normal system, this cache only + // transitions from NULL to non-NULL which is safe because we use + // release_set_methods_jmethod_ids() to advertise the new cache. + // A partially constructed cache should never be seen by a racing + // thread. We also use release_store_ptr() to save a new jmethodID + // in the cache so a partially constructed jmethodID should never be + // seen either. Cache reads of existing jmethodIDs proceed without a + // lock, but cache writes of a new jmethodID requires uniqueness and + // creation of the cache itself requires no leaks so a lock is + // generally acquired in those two cases. + // + // If the RedefineClasses() API has been used, then this cache can + // grow and we'll have transitions from non-NULL to bigger non-NULL. + // Cache creation requires no leaks and we require safety between all + // cache accesses and freeing of the old cache so a lock is generally + // acquired when the RedefineClasses() API has been used. + + if (jmeths != NULL) { + // the cache already exists + if (!ik_h->idnum_can_increment()) { + // the cache can't grow so we can just get the current values + get_jmethod_id_length_value(jmeths, idnum, &length, &id); + } else { + // cache can grow so we have to be more careful + if (Threads::number_of_threads() == 0 || + SafepointSynchronize::is_at_safepoint()) { + // we're single threaded or at a safepoint - no locking needed + get_jmethod_id_length_value(jmeths, idnum, &length, &id); + } else { + MutexLocker ml(JmethodIdCreation_lock); + get_jmethod_id_length_value(jmeths, idnum, &length, &id); + } + } + } + // implied else: + // we need to allocate a cache so default length and id values are good + + if (jmeths == NULL || // no cache yet + length <= idnum || // cache is too short + id == NULL) { // cache doesn't contain entry + + // This function can be called by the VMThread so we have to do all + // things that might block on a safepoint before grabbing the lock. + // Otherwise, we can deadlock with the VMThread or have a cache + // consistency issue. These vars keep track of what we might have + // to free after the lock is dropped. + jmethodID to_dealloc_id = NULL; + jmethodID* to_dealloc_jmeths = NULL; + + // may not allocate new_jmeths or use it if we allocate it jmethodID* new_jmeths = NULL; if (length <= idnum) { - // A new array will be needed (unless some other thread beats us to it) + // allocate a new cache that might be used size_t size = MAX2(idnum+1, (size_t)ik_h->idnum_allocated_count()); new_jmeths = NEW_C_HEAP_ARRAY(jmethodID, size+1); memset(new_jmeths, 0, (size+1)*sizeof(jmethodID)); - new_jmeths[0] =(jmethodID)size; // array size held in the first element + // cache size is stored in element[0], other elements offset by one + new_jmeths[0] = (jmethodID)size; } - // Allocate a new method ID. + // allocate a new jmethodID that might be used jmethodID new_id = NULL; if (method_h->is_old() && !method_h->is_obsolete()) { // The method passed in is old (but not obsolete), we need to use the current version @@ -1007,63 +1118,111 @@ new_id = JNIHandles::make_jmethod_id(method_h); } - if (Threads::number_of_threads() == 0 || SafepointSynchronize::is_at_safepoint()) { - // No need and unsafe to lock the JmethodIdCreation_lock at safepoint. - id = get_jmethod_id(ik_h, idnum, new_id, new_jmeths); + if (Threads::number_of_threads() == 0 || + SafepointSynchronize::is_at_safepoint()) { + // we're single threaded or at a safepoint - no locking needed + id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths, + &to_dealloc_id, &to_dealloc_jmeths); } else { MutexLocker ml(JmethodIdCreation_lock); - id = get_jmethod_id(ik_h, idnum, new_id, new_jmeths); + id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths, + &to_dealloc_id, &to_dealloc_jmeths); + } + + // The lock has been dropped so we can free resources. + // Free up either the old cache or the new cache if we allocated one. + if (to_dealloc_jmeths != NULL) { + FreeHeap(to_dealloc_jmeths); + } + // free up the new ID since it wasn't needed + if (to_dealloc_id != NULL) { + JNIHandles::destroy_jmethod_id(to_dealloc_id); } } return id; } -jmethodID instanceKlass::get_jmethod_id(instanceKlassHandle ik_h, size_t idnum, - jmethodID new_id, jmethodID* new_jmeths) { - // Retry lookup after we got the lock or ensured we are at safepoint +// Common code to fetch the jmethodID from the cache or update the +// cache with the new jmethodID. This function should never do anything +// that causes the caller to go to a safepoint or we can deadlock with +// the VMThread or have cache consistency issues. +// +jmethodID instanceKlass::get_jmethod_id_fetch_or_update( + instanceKlassHandle ik_h, size_t idnum, jmethodID new_id, + jmethodID* new_jmeths, jmethodID* to_dealloc_id_p, + jmethodID** to_dealloc_jmeths_p) { + assert(new_id != NULL, "sanity check"); + assert(to_dealloc_id_p != NULL, "sanity check"); + assert(to_dealloc_jmeths_p != NULL, "sanity check"); + assert(Threads::number_of_threads() == 0 || + SafepointSynchronize::is_at_safepoint() || + JmethodIdCreation_lock->owned_by_self(), "sanity check"); + + // reacquire the cache - we are locked, single threaded or at a safepoint jmethodID* jmeths = ik_h->methods_jmethod_ids_acquire(); - jmethodID id = NULL; - jmethodID to_dealloc_id = NULL; - jmethodID* to_dealloc_jmeths = NULL; - size_t length; - - if (jmeths == NULL || (length = (size_t)jmeths[0]) <= idnum) { + jmethodID id = NULL; + size_t length = 0; + + if (jmeths == NULL || // no cache yet + (length = (size_t)jmeths[0]) <= idnum) { // cache is too short if (jmeths != NULL) { - // We have grown the array: copy the existing entries, and delete the old array + // copy any existing entries from the old cache for (size_t index = 0; index < length; index++) { new_jmeths[index+1] = jmeths[index+1]; } - to_dealloc_jmeths = jmeths; // using the new jmeths, deallocate the old one + *to_dealloc_jmeths_p = jmeths; // save old cache for later delete } ik_h->release_set_methods_jmethod_ids(jmeths = new_jmeths); } else { + // fetch jmethodID (if any) from the existing cache id = jmeths[idnum+1]; - to_dealloc_jmeths = new_jmeths; // using the old jmeths, deallocate the new one + *to_dealloc_jmeths_p = new_jmeths; // save new cache for later delete } if (id == NULL) { + // No matching jmethodID in the existing cache or we have a new + // cache or we just grew the cache. This cache write is done here + // by the first thread to win the foot race because a jmethodID + // needs to be unique once it is generally available. id = new_id; - jmeths[idnum+1] = id; // install the new method ID + + // The jmethodID cache can be read while unlocked so we have to + // make sure the new jmethodID is complete before installing it + // in the cache. + OrderAccess::release_store_ptr(&jmeths[idnum+1], id); } else { - to_dealloc_id = new_id; // the new id wasn't used, mark it for deallocation - } - - // Free up unneeded or no longer needed resources - FreeHeap(to_dealloc_jmeths); - if (to_dealloc_id != NULL) { - JNIHandles::destroy_jmethod_id(to_dealloc_id); + *to_dealloc_id_p = new_id; // save new id for later delete } return id; } +// Common code to get the jmethodID cache length and the jmethodID +// value at index idnum if there is one. +// +void instanceKlass::get_jmethod_id_length_value(jmethodID* cache, + size_t idnum, size_t *length_p, jmethodID* id_p) { + assert(cache != NULL, "sanity check"); + assert(length_p != NULL, "sanity check"); + assert(id_p != NULL, "sanity check"); + + // cache size is stored in element[0], other elements offset by one + *length_p = (size_t)cache[0]; + if (*length_p <= idnum) { // cache is too short + *id_p = NULL; + } else { + *id_p = cache[idnum+1]; // fetch jmethodID (if any) + } +} + + // Lookup a jmethodID, NULL if not found. Do no blocking, no allocations, no handles jmethodID instanceKlass::jmethod_id_or_null(methodOop method) { size_t idnum = (size_t)method->method_idnum(); jmethodID* jmeths = methods_jmethod_ids_acquire(); size_t length; // length assigned as debugging crumb jmethodID id = NULL; - if (jmeths != NULL && // If there is a jmethodID array, + if (jmeths != NULL && // If there is a cache (length = (size_t)jmeths[0]) > idnum) { // and if it is long enough, id = jmeths[idnum+1]; // Look up the id (may be NULL) } @@ -1074,18 +1233,35 @@ // Cache an itable index void instanceKlass::set_cached_itable_index(size_t idnum, int index) { int* indices = methods_cached_itable_indices_acquire(); - if (indices == NULL || // If there is no index array, - ((size_t)indices[0]) <= idnum) { // or if it is too short - // Lock before we allocate the array so we don't leak + int* to_dealloc_indices = NULL; + + // We use a double-check locking idiom here because this cache is + // performance sensitive. In the normal system, this cache only + // transitions from NULL to non-NULL which is safe because we use + // release_set_methods_cached_itable_indices() to advertise the + // new cache. A partially constructed cache should never be seen + // by a racing thread. Cache reads and writes proceed without a + // lock, but creation of the cache itself requires no leaks so a + // lock is generally acquired in that case. + // + // If the RedefineClasses() API has been used, then this cache can + // grow and we'll have transitions from non-NULL to bigger non-NULL. + // Cache creation requires no leaks and we require safety between all + // cache accesses and freeing of the old cache so a lock is generally + // acquired when the RedefineClasses() API has been used. + + if (indices == NULL || idnum_can_increment()) { + // we need a cache or the cache can grow MutexLocker ml(JNICachedItableIndex_lock); - // Retry lookup after we got the lock + // reacquire the cache to see if another thread already did the work indices = methods_cached_itable_indices_acquire(); size_t length = 0; - // array length stored in first element, other elements offset by one + // cache size is stored in element[0], other elements offset by one if (indices == NULL || (length = (size_t)indices[0]) <= idnum) { size_t size = MAX2(idnum+1, (size_t)idnum_allocated_count()); int* new_indices = NEW_C_HEAP_ARRAY(int, size+1); - // Copy the existing entries, if any + new_indices[0] = (int)size; + // copy any existing entries size_t i; for (i = 0; i < length; i++) { new_indices[i+1] = indices[i+1]; @@ -1095,15 +1271,32 @@ new_indices[i+1] = -1; } if (indices != NULL) { - FreeHeap(indices); // delete any old indices + // We have an old cache to delete so save it for after we + // drop the lock. + to_dealloc_indices = indices; } release_set_methods_cached_itable_indices(indices = new_indices); } + + if (idnum_can_increment()) { + // this cache can grow so we have to write to it safely + indices[idnum+1] = index; + } } else { CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); } - // This is a cache, if there is a race to set it, it doesn't matter - indices[idnum+1] = index; + + if (!idnum_can_increment()) { + // The cache cannot grow and this JNI itable index value does not + // have to be unique like a jmethodID. If there is a race to set it, + // it doesn't matter. + indices[idnum+1] = index; + } + + if (to_dealloc_indices != NULL) { + // we allocated a new cache so free the old one + FreeHeap(to_dealloc_indices); + } } @@ -1396,18 +1589,18 @@ /* Compute oopmap block range. The common case \ is nonstatic_oop_map_size == 1. */ \ OopMapBlock* map = start_of_nonstatic_oop_maps(); \ - OopMapBlock* const end_map = map + nonstatic_oop_map_size(); \ + OopMapBlock* const end_map = map + nonstatic_oop_map_count(); \ if (UseCompressedOops) { \ while (map < end_map) { \ InstanceKlass_SPECIALIZED_OOP_ITERATE(narrowOop, \ - obj->obj_field_addr(map->offset()), map->length(), \ + obj->obj_field_addr(map->offset()), map->count(), \ do_oop, assert_fn) \ ++map; \ } \ } else { \ while (map < end_map) { \ InstanceKlass_SPECIALIZED_OOP_ITERATE(oop, \ - obj->obj_field_addr(map->offset()), map->length(), \ + obj->obj_field_addr(map->offset()), map->count(), \ do_oop, assert_fn) \ ++map; \ } \ @@ -1417,19 +1610,19 @@ #define InstanceKlass_OOP_MAP_REVERSE_ITERATE(obj, do_oop, assert_fn) \ { \ OopMapBlock* const start_map = start_of_nonstatic_oop_maps(); \ - OopMapBlock* map = start_map + nonstatic_oop_map_size(); \ + OopMapBlock* map = start_map + nonstatic_oop_map_count(); \ if (UseCompressedOops) { \ while (start_map < map) { \ --map; \ InstanceKlass_SPECIALIZED_OOP_REVERSE_ITERATE(narrowOop, \ - obj->obj_field_addr(map->offset()), map->length(), \ + obj->obj_field_addr(map->offset()), map->count(), \ do_oop, assert_fn) \ } \ } else { \ while (start_map < map) { \ --map; \ InstanceKlass_SPECIALIZED_OOP_REVERSE_ITERATE(oop, \ - obj->obj_field_addr(map->offset()), map->length(), \ + obj->obj_field_addr(map->offset()), map->count(), \ do_oop, assert_fn) \ } \ } \ @@ -1443,11 +1636,11 @@ usually non-existent extra overhead of examining \ all the maps. */ \ OopMapBlock* map = start_of_nonstatic_oop_maps(); \ - OopMapBlock* const end_map = map + nonstatic_oop_map_size(); \ + OopMapBlock* const end_map = map + nonstatic_oop_map_count(); \ if (UseCompressedOops) { \ while (map < end_map) { \ InstanceKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(narrowOop, \ - obj->obj_field_addr(map->offset()), map->length(), \ + obj->obj_field_addr(map->offset()), map->count(), \ low, high, \ do_oop, assert_fn) \ ++map; \ @@ -1455,7 +1648,7 @@ } else { \ while (map < end_map) { \ InstanceKlass_SPECIALIZED_BOUNDED_OOP_ITERATE(oop, \ - obj->obj_field_addr(map->offset()), map->length(), \ + obj->obj_field_addr(map->offset()), map->count(), \ low, high, \ do_oop, assert_fn) \ ++map; \ @@ -1773,7 +1966,7 @@ } } -char* instanceKlass::signature_name() const { +const char* instanceKlass::signature_name() const { const char* src = (const char*) (name()->as_C_string()); const int src_length = (int)strlen(src); char* dest = NEW_RESOURCE_ARRAY(char, src_length + 3); @@ -1918,8 +2111,9 @@ // As we walk along, look for equalities between outer1 and class2. // Eventually, the walks will terminate as outer1 stops // at the top-level class around the original class. - symbolOop ignore_name; - klassOop next = outer1->compute_enclosing_class(ignore_name, CHECK_false); + bool ignore_inner_is_member; + klassOop next = outer1->compute_enclosing_class(&ignore_inner_is_member, + CHECK_false); if (next == NULL) break; if (next == class2()) return true; outer1 = instanceKlassHandle(THREAD, next); @@ -1928,8 +2122,9 @@ // Now do the same for class2. instanceKlassHandle outer2 = class2; for (;;) { - symbolOop ignore_name; - klassOop next = outer2->compute_enclosing_class(ignore_name, CHECK_false); + bool ignore_inner_is_member; + klassOop next = outer2->compute_enclosing_class(&ignore_inner_is_member, + CHECK_false); if (next == NULL) break; // Might as well check the new outer against all available values. if (next == class1()) return true; @@ -2025,7 +2220,7 @@ // This is a short non-blocking critical region, so the no safepoint check is ok. OsrList_lock->lock_without_safepoint_check(); assert(n->is_osr_method(), "wrong kind of nmethod"); - n->set_link(osr_nmethods_head()); + n->set_osr_link(osr_nmethods_head()); set_osr_nmethods_head(n); // Remember to unlock again OsrList_lock->unlock(); @@ -2041,17 +2236,17 @@ // Search for match while(cur != NULL && cur != n) { last = cur; - cur = cur->link(); + cur = cur->osr_link(); } if (cur == n) { if (last == NULL) { // Remove first element - set_osr_nmethods_head(osr_nmethods_head()->link()); + set_osr_nmethods_head(osr_nmethods_head()->osr_link()); } else { - last->set_link(cur->link()); + last->set_osr_link(cur->osr_link()); } } - n->set_link(NULL); + n->set_osr_link(NULL); // Remember to unlock again OsrList_lock->unlock(); } @@ -2068,7 +2263,7 @@ OsrList_lock->unlock(); return osr; } - osr = osr->link(); + osr = osr->osr_link(); } OsrList_lock->unlock(); return NULL; @@ -2096,7 +2291,7 @@ void instanceKlass::oop_print_on(oop obj, outputStream* st) { Klass::oop_print_on(obj, st); - if (as_klassOop() == SystemDictionary::string_klass()) { + if (as_klassOop() == SystemDictionary::String_klass()) { typeArrayOop value = java_lang_String::value(obj); juint offset = java_lang_String::offset(obj); juint length = java_lang_String::length(obj); @@ -2116,7 +2311,7 @@ FieldPrinter print_nonstatic_field(st, obj); do_nonstatic_fields(&print_nonstatic_field); - if (as_klassOop() == SystemDictionary::class_klass()) { + if (as_klassOop() == SystemDictionary::Class_klass()) { st->print(BULLET"signature: "); java_lang_Class::print_signature(obj, st); st->cr(); @@ -2132,14 +2327,20 @@ st->print(BULLET"fake entry for array: "); array_klass->print_value_on(st); st->cr(); + } else if (as_klassOop() == SystemDictionary::MethodType_klass()) { + st->print(BULLET"signature: "); + java_dyn_MethodType::print_signature(obj, st); + st->cr(); } } +#endif //PRODUCT + void instanceKlass::oop_print_value_on(oop obj, outputStream* st) { st->print("a "); name()->print_value_on(st); obj->print_address_on(st); - if (as_klassOop() == SystemDictionary::string_klass() + if (as_klassOop() == SystemDictionary::String_klass() && java_lang_String::value(obj) != NULL) { ResourceMark rm; int len = java_lang_String::length(obj); @@ -2148,7 +2349,7 @@ st->print(" = \"%s\"", str); if (len > plen) st->print("...[%d]", len); - } else if (as_klassOop() == SystemDictionary::class_klass()) { + } else if (as_klassOop() == SystemDictionary::Class_klass()) { klassOop k = java_lang_Class::as_klassOop(obj); st->print(" = "); if (k != NULL) { @@ -2157,14 +2358,15 @@ const char* tname = type2name(java_lang_Class::primitive_type(obj)); st->print("%s", tname ? tname : "type?"); } + } else if (as_klassOop() == SystemDictionary::MethodType_klass()) { + st->print(" = "); + java_dyn_MethodType::print_signature(obj, st); } else if (java_lang_boxing_object::is_instance(obj)) { st->print(" = "); java_lang_boxing_object::print(obj, st); } } -#endif // ndef PRODUCT - const char* instanceKlass::internal_name() const { return external_name(); } @@ -2212,18 +2414,19 @@ // Check that we have the right class static bool first_time = true; - guarantee(k == SystemDictionary::class_klass() && first_time, "Invalid verify of maps"); + guarantee(k == SystemDictionary::Class_klass() && first_time, "Invalid verify of maps"); first_time = false; const int extra = java_lang_Class::number_of_fake_oop_fields; guarantee(ik->nonstatic_field_size() == extra, "just checking"); - guarantee(ik->nonstatic_oop_map_size() == 1, "just checking"); + guarantee(ik->nonstatic_oop_map_count() == 1, "just checking"); guarantee(ik->size_helper() == align_object_size(instanceOopDesc::header_size() + extra), "just checking"); // Check that the map is (2,extra) int offset = java_lang_Class::klass_offset; OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); - guarantee(map->offset() == offset && map->length() == extra, "just checking"); + guarantee(map->offset() == offset && map->count() == (unsigned int) extra, + "sanity"); } } @@ -2298,6 +2501,11 @@ // Add an information node that contains weak references to the // interesting parts of the previous version of the_class. +// This is also where we clean out any unused weak references. +// Note that while we delete nodes from the _previous_versions +// array, we never delete the array itself until the klass is +// unloaded. The has_been_redefined() query depends on that fact. +// void instanceKlass::add_previous_version(instanceKlassHandle ikh, BitMap* emcp_methods, int emcp_method_count) { assert(Thread::current()->is_VM_thread(), diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/instanceKlass.hpp --- a/src/share/vm/oops/instanceKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/instanceKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -71,7 +71,6 @@ // forward declaration for class -- see below for definition class SuperTypeClosure; -class OopMapBlock; class JNIid; class jniIdMapBase; class BreakpointInfo; @@ -99,6 +98,29 @@ }; #endif // !PRODUCT +// ValueObjs embedded in klass. Describes where oops are located in instances of +// this klass. +class OopMapBlock VALUE_OBJ_CLASS_SPEC { + public: + // Byte offset of the first oop mapped by this block. + int offset() const { return _offset; } + void set_offset(int offset) { _offset = offset; } + + // Number of oops in this block. + uint count() const { return _count; } + void set_count(uint count) { _count = count; } + + // sizeof(OopMapBlock) in HeapWords. + static const int size_in_words() { + return align_size_up(int(sizeof(OopMapBlock)), HeapWordSize) >> + LogHeapWordSize; + } + + private: + int _offset; + uint _count; +}; + class instanceKlass: public Klass { friend class VMStructs; public: @@ -191,10 +213,11 @@ int _nonstatic_field_size; int _static_field_size; // number words used by static fields (oop and non-oop) in this klass int _static_oop_field_size;// number of static oop fields in this klass - int _nonstatic_oop_map_size;// number of nonstatic oop-map blocks allocated at end of this klass + int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks bool _is_marked_dependent; // used for marking during flushing and deoptimization bool _rewritten; // methods rewritten. bool _has_nonstatic_fields; // for sizing with UseCompressedOops + bool _should_verify_class; // allow caching of preverification u2 _minor_version; // minor version number of class file u2 _major_version; // major version number of class file ClassState _init_state; // state of class @@ -314,12 +337,12 @@ static bool is_same_class_package(oop class_loader1, symbolOop class_name1, oop class_loader2, symbolOop class_name2); // find an enclosing class (defined where original code was, in jvm.cpp!) - klassOop compute_enclosing_class(symbolOop& simple_name_result, TRAPS) { + klassOop compute_enclosing_class(bool* inner_is_member, TRAPS) { instanceKlassHandle self(THREAD, this->as_klassOop()); - return compute_enclosing_class_impl(self, simple_name_result, THREAD); + return compute_enclosing_class_impl(self, inner_is_member, THREAD); } static klassOop compute_enclosing_class_impl(instanceKlassHandle self, - symbolOop& simple_name_result, TRAPS); + bool* inner_is_member, TRAPS); // tell if two classes have the same enclosing class (at package level) bool is_same_package_member(klassOop class2, TRAPS) { @@ -340,6 +363,10 @@ int get_init_state() { return _init_state; } // Useful for debugging bool is_rewritten() const { return _rewritten; } + // defineClass specified verification + bool should_verify_class() const { return _should_verify_class; } + void set_should_verify_class(bool value) { _should_verify_class = value; } + // marking bool is_marked_dependent() const { return _is_marked_dependent; } void set_is_marked_dependent(bool value) { _is_marked_dependent = value; } @@ -424,12 +451,24 @@ void set_source_debug_extension(symbolOop n){ oop_store_without_check((oop*) &_source_debug_extension, (oop) n); } // nonstatic oop-map blocks - int nonstatic_oop_map_size() const { return _nonstatic_oop_map_size; } - void set_nonstatic_oop_map_size(int size) { _nonstatic_oop_map_size = size; } + static int nonstatic_oop_map_size(unsigned int oop_map_count) { + return oop_map_count * OopMapBlock::size_in_words(); + } + unsigned int nonstatic_oop_map_count() const { + return _nonstatic_oop_map_size / OopMapBlock::size_in_words(); + } + int nonstatic_oop_map_size() const { return _nonstatic_oop_map_size; } + void set_nonstatic_oop_map_size(int words) { + _nonstatic_oop_map_size = words; + } // RedefineClasses() support for previous versions: void add_previous_version(instanceKlassHandle ikh, BitMap *emcp_methods, int emcp_method_count); + // If the _previous_versions array is non-NULL, then this klass + // has been redefined at least once even if we aren't currently + // tracking a previous version. + bool has_been_redefined() const { return _previous_versions != NULL; } bool has_previous_version() const; void init_previous_versions() { _previous_versions = NULL; @@ -471,9 +510,14 @@ void set_bootstrap_method(oop mh) { oop_store(&_bootstrap_method, mh); } // jmethodID support - static jmethodID get_jmethod_id(instanceKlassHandle ik_h, size_t idnum, - jmethodID new_id, jmethodID* new_jmeths); - static jmethodID jmethod_id_for_impl(instanceKlassHandle ik_h, methodHandle method_h); + static jmethodID get_jmethod_id(instanceKlassHandle ik_h, + methodHandle method_h); + static jmethodID get_jmethod_id_fetch_or_update(instanceKlassHandle ik_h, + size_t idnum, jmethodID new_id, jmethodID* new_jmeths, + jmethodID* to_dealloc_id_p, + jmethodID** to_dealloc_jmeths_p); + static void get_jmethod_id_length_value(jmethodID* cache, size_t idnum, + size_t *length_p, jmethodID* id_p); jmethodID jmethod_id_or_null(methodOop method); // cached itable index support @@ -678,7 +722,7 @@ #endif // SERIALGC // Naming - char* signature_name() const; + const char* signature_name() const; // Iterators int oop_oop_iterate(oop obj, OopClosure* blk) { @@ -719,6 +763,11 @@ void set_init_thread(Thread *thread) { _init_thread = thread; } u2 idnum_allocated_count() const { return _idnum_allocated_count; } + // The RedefineClasses() API can cause new method idnums to be needed + // which will cause the caches to grow. Safety requires different + // cache management logic if the caches can grow instead of just + // going from NULL to non-NULL. + bool idnum_can_increment() const { return has_been_redefined(); } jmethodID* methods_jmethod_ids_acquire() const { return (jmethodID*)OrderAccess::load_ptr_acquire(&_methods_jmethod_ids); } void release_set_methods_jmethod_ids(jmethodID* jmeths) @@ -790,17 +839,16 @@ // JVMTI support jint jvmti_class_status() const; -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); void print_dependent_nmethods(bool verbose = false); bool is_dependent_nmethod(nmethod* nm); #endif - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); @@ -839,21 +887,6 @@ } -// ValueObjs embedded in klass. Describes where oops are located in instances of this klass. - -class OopMapBlock VALUE_OBJ_CLASS_SPEC { - private: - jushort _offset; // Offset of first oop in oop-map block - jushort _length; // Length of oop-map block - public: - // Accessors - jushort offset() const { return _offset; } - void set_offset(jushort offset) { _offset = offset; } - - jushort length() const { return _length; } - void set_length(jushort length) { _length = length; } -}; - /* JNIid class for jfieldIDs only */ class JNIid: public CHeapObj { friend class VMStructs; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/instanceKlassKlass.cpp --- a/src/share/vm/oops/instanceKlassKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/instanceKlassKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -317,6 +317,11 @@ pm->claim_or_forward_breadth(sg_addr); } + oop* bsm_addr = ik->adr_bootstrap_method(); + if (PSScavenge::should_scavenge(bsm_addr)) { + pm->claim_or_forward_breadth(bsm_addr); + } + klassKlass::oop_copy_contents(pm, obj); } @@ -345,6 +350,11 @@ pm->claim_or_forward_depth(sg_addr); } + oop* bsm_addr = ik->adr_bootstrap_method(); + if (PSScavenge::should_scavenge(bsm_addr)) { + pm->claim_or_forward_depth(bsm_addr); + } + klassKlass::oop_copy_contents(pm, obj); } @@ -402,9 +412,14 @@ } #endif // SERIALGC -klassOop instanceKlassKlass::allocate_instance_klass(int vtable_len, int itable_len, int static_field_size, - int nonstatic_oop_map_size, ReferenceType rt, TRAPS) { +klassOop +instanceKlassKlass::allocate_instance_klass(int vtable_len, int itable_len, + int static_field_size, + unsigned nonstatic_oop_map_count, + ReferenceType rt, TRAPS) { + const int nonstatic_oop_map_size = + instanceKlass::nonstatic_oop_map_size(nonstatic_oop_map_count); int size = instanceKlass::object_size(align_object_offset(vtable_len) + align_object_offset(itable_len) + static_field_size + nonstatic_oop_map_size); // Allocation @@ -615,14 +630,15 @@ st->print(BULLET"non-static oop maps: "); OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); - OopMapBlock* end_map = map + ik->nonstatic_oop_map_size(); + OopMapBlock* end_map = map + ik->nonstatic_oop_map_count(); while (map < end_map) { - st->print("%d-%d ", map->offset(), map->offset() + heapOopSize*(map->length() - 1)); + st->print("%d-%d ", map->offset(), map->offset() + heapOopSize*(map->count() - 1)); map++; } st->cr(); } +#endif //PRODUCT void instanceKlassKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_klass(), "must be klass"); @@ -630,8 +646,6 @@ ik->name()->print_value_on(st); } -#endif // PRODUCT - const char* instanceKlassKlass::internal_name() const { return "{instance class}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/instanceKlassKlass.hpp --- a/src/share/vm/oops/instanceKlassKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/instanceKlassKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -39,7 +39,7 @@ klassOop allocate_instance_klass(int vtable_len, int itable_len, int static_field_size, - int nonstatic_oop_map_size, + unsigned int nonstatic_oop_map_count, ReferenceType rt, TRAPS); @@ -69,14 +69,13 @@ // Apply closure to the InstanceKlass oops that are outside the java heap. inline void iterate_c_heap_oops(instanceKlass* ik, OopClosure* closure); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); #endif - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/instanceRefKlass.cpp --- a/src/share/vm/oops/instanceRefKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/instanceRefKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -78,9 +78,9 @@ #ifndef SERIALGC template -static void specialized_oop_follow_contents(instanceRefKlass* ref, - ParCompactionManager* cm, - oop obj) { +void specialized_oop_follow_contents(instanceRefKlass* ref, + ParCompactionManager* cm, + oop obj) { T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); T heap_oop = oopDesc::load_heap_oop(referent_addr); debug_only( @@ -397,29 +397,29 @@ // Check that we have the right class debug_only(static bool first_time = true); - assert(k == SystemDictionary::reference_klass() && first_time, + assert(k == SystemDictionary::Reference_klass() && first_time, "Invalid update of maps"); debug_only(first_time = false); - assert(ik->nonstatic_oop_map_size() == 1, "just checking"); + assert(ik->nonstatic_oop_map_count() == 1, "just checking"); OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); // Check that the current map is (2,4) - currently points at field with // offset 2 (words) and has 4 map entries. debug_only(int offset = java_lang_ref_Reference::referent_offset); - debug_only(int length = ((java_lang_ref_Reference::discovered_offset - + debug_only(unsigned int count = ((java_lang_ref_Reference::discovered_offset - java_lang_ref_Reference::referent_offset)/heapOopSize) + 1); if (UseSharedSpaces) { assert(map->offset() == java_lang_ref_Reference::queue_offset && - map->length() == 1, "just checking"); + map->count() == 1, "just checking"); } else { - assert(map->offset() == offset && map->length() == length, + assert(map->offset() == offset && map->count() == count, "just checking"); // Update map to (3,1) - point to offset of 3 (words) with 1 map entry. map->set_offset(java_lang_ref_Reference::queue_offset); - map->set_length(1); + map->set_count(1); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/klass.cpp --- a/src/share/vm/oops/klass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/klass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -217,8 +217,8 @@ set_super(NULL); oop_store_without_check((oop*) &_primary_supers[0], (oop) this->as_klassOop()); assert(super_depth() == 0, "Object must already be initialized properly"); - } else if (k != super() || k == SystemDictionary::object_klass()) { - assert(super() == NULL || super() == SystemDictionary::object_klass(), + } else if (k != super() || k == SystemDictionary::Object_klass()) { + assert(super() == NULL || super() == SystemDictionary::Object_klass(), "initialize this only once to a non-trivial value"); set_super(k); Klass* sup = k->klass_part(); @@ -370,7 +370,7 @@ void Klass::remove_from_sibling_list() { // remove receiver from sibling list instanceKlass* super = superklass(); - assert(super != NULL || as_klassOop() == SystemDictionary::object_klass(), "should have super"); + assert(super != NULL || as_klassOop() == SystemDictionary::Object_klass(), "should have super"); if (super == NULL) return; // special case: class Object if (super->subklass() == this) { // first subklass @@ -496,11 +496,13 @@ return result; } } + if (name() == NULL) return ""; return name()->as_klass_external_name(); } -char* Klass::signature_name() const { +const char* Klass::signature_name() const { + if (name() == NULL) return ""; return name()->as_C_string(); } @@ -539,6 +541,7 @@ st->cr(); } +#endif //PRODUCT void Klass::oop_print_value_on(oop obj, outputStream* st) { // print title @@ -547,8 +550,6 @@ obj->print_address_on(st); } -#endif - // Verification void Klass::oop_verify_on(oop obj, outputStream* st) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/klass.hpp --- a/src/share/vm/oops/klass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/klass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -546,7 +546,7 @@ // For arrays, this returns the name of the element with a leading '['. // For classes, this returns the name with a leading 'L' and a trailing ';' // and the package separators as '/'. - virtual char* signature_name() const; + virtual const char* signature_name() const; // garbage collection support virtual void oop_follow_contents(oop obj) = 0; @@ -776,14 +776,13 @@ // JVMTI support virtual jint jvmti_class_status() const; -#ifndef PRODUCT public: // Printing + virtual void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT virtual void oop_print_on (oop obj, outputStream* st); - virtual void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verification virtual const char* internal_name() const = 0; virtual void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/klassKlass.cpp --- a/src/share/vm/oops/klassKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/klassKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -202,13 +202,12 @@ Klass::oop_print_on(obj, st); } +#endif //PRODUCT void klassKlass::oop_print_value_on(oop obj, outputStream* st) { Klass::oop_print_value_on(obj, st); } -#endif - const char* klassKlass::internal_name() const { return "{other class}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/klassKlass.hpp --- a/src/share/vm/oops/klassKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/klassKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -67,14 +67,13 @@ juint alloc_size() const { return _alloc_size; } void set_alloc_size(juint n) { _alloc_size = n; } -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/markOop.cpp --- a/src/share/vm/oops/markOop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/markOop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -31,8 +31,9 @@ st->print("locked(0x%lx)->", value()); markOop(*(markOop*)value())->print_on(st); } else { - assert(is_unlocked(), "just checking"); + assert(is_unlocked() || has_bias_pattern(), "just checking"); st->print("mark("); + if (has_bias_pattern()) st->print("biased,"); st->print("hash %#lx,", hash()); st->print("age %d)", age()); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodDataKlass.cpp --- a/src/share/vm/oops/methodDataKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodDataKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -214,6 +214,8 @@ m->print_data_on(st); } +#endif //PRODUCT + void methodDataKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_methodData(), "should be method data"); methodDataOop m = methodDataOop(obj); @@ -221,8 +223,6 @@ m->method()->print_value_on(st); } -#endif // !PRODUCT - const char* methodDataKlass::internal_name() const { return "{method data}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodDataKlass.hpp --- a/src/share/vm/oops/methodDataKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodDataKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -71,14 +71,13 @@ int oop_oop_iterate(oop obj, OopClosure* blk); int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif // !PRODUCT +#endif //PRODUCT - public: // Verify operations const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodDataOop.cpp --- a/src/share/vm/oops/methodDataOop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodDataOop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -49,6 +49,12 @@ } } +void DataLayout::follow_weak_refs(BoolObjectClosure* cl) { + ResourceMark m; + data_in()->follow_weak_refs(cl); +} + + // ================================================================== // ProfileData // @@ -145,42 +151,92 @@ // which are used to store a type profile for the receiver of the check. void ReceiverTypeData::follow_contents() { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - MarkSweep::mark_and_push(adr_receiver(row)); - } - } + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + MarkSweep::revisit_mdo(data()); } #ifndef SERIALGC void ReceiverTypeData::follow_contents(ParCompactionManager* cm) { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - PSParallelCompact::mark_and_push(cm, adr_receiver(row)); - } - } + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + PSParallelCompact::revisit_mdo(cm, data()); } #endif // SERIALGC void ReceiverTypeData::oop_iterate(OopClosure* blk) { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - blk->do_oop(adr_receiver(row)); - } - } -} - -void ReceiverTypeData::oop_iterate_m(OopClosure* blk, MemRegion mr) { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - oop* adr = adr_receiver(row); - if (mr.contains(adr)) { + if (blk->should_remember_mdo()) { + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + blk->remember_mdo(data()); + } else { // normal scan + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); blk->do_oop(adr); } } } } +void ReceiverTypeData::oop_iterate_m(OopClosure* blk, MemRegion mr) { + // Currently, this interface is called only during card-scanning for + // a young gen gc, in which case this object cannot contribute anything, + // since it does not contain any references that cross out of + // the perm gen. However, for future more general use we allow + // the possibility of calling for instance from more general + // iterators (for example, a future regionalized perm gen for G1, + // or the possibility of moving some references out of perm in + // the case of other collectors). In that case, you will need + // to relax or remove some of the assertions below. +#ifdef ASSERT + // Verify that none of the embedded oop references cross out of + // this generation. + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); + CollectedHeap* h = Universe::heap(); + assert(h->is_permanent(adr) && h->is_permanent_or_null(*adr), "Not intra-perm"); + } + } +#endif // ASSERT + assert(!blk->should_remember_mdo(), "Not expected to remember MDO"); + return; // Nothing to do, see comment above +#if 0 + if (blk->should_remember_mdo()) { + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + blk->remember_mdo(data()); + } else { // normal scan + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); + if (mr.contains(adr)) { + blk->do_oop(adr); + } else if ((HeapWord*)adr >= mr.end()) { + // Test that the current cursor and the two ends of the range + // that we may have skipped iterating over are monotonically ordered; + // this is just a paranoid assertion, just in case represetations + // should change in the future rendering the short-circuit return + // here invalid. + assert((row+1 >= row_limit() || adr_receiver(row+1) > adr) && + (row+2 >= row_limit() || adr_receiver(row_limit()-1) > adr_receiver(row+1)), "Reducing?"); + break; // remaining should be outside this mr too + } + } + } + } +#endif +} + void ReceiverTypeData::adjust_pointers() { for (uint row = 0; row < row_limit(); row++) { if (receiver(row) != NULL) { @@ -189,6 +245,15 @@ } } +void ReceiverTypeData::follow_weak_refs(BoolObjectClosure* is_alive_cl) { + for (uint row = 0; row < row_limit(); row++) { + klassOop p = receiver(row); + if (p != NULL && !is_alive_cl->do_object_b(p)) { + clear_row(row); + } + } +} + #ifndef SERIALGC void ReceiverTypeData::update_pointers() { for (uint row = 0; row < row_limit(); row++) { @@ -625,30 +690,33 @@ return NULL; } DataLayout* data_layout = data_layout_at(data_index); + return data_layout->data_in(); +} - switch (data_layout->tag()) { +ProfileData* DataLayout::data_in() { + switch (tag()) { case DataLayout::no_tag: default: ShouldNotReachHere(); return NULL; case DataLayout::bit_data_tag: - return new BitData(data_layout); + return new BitData(this); case DataLayout::counter_data_tag: - return new CounterData(data_layout); + return new CounterData(this); case DataLayout::jump_data_tag: - return new JumpData(data_layout); + return new JumpData(this); case DataLayout::receiver_type_data_tag: - return new ReceiverTypeData(data_layout); + return new ReceiverTypeData(this); case DataLayout::virtual_call_data_tag: - return new VirtualCallData(data_layout); + return new VirtualCallData(this); case DataLayout::ret_data_tag: - return new RetData(data_layout); + return new RetData(this); case DataLayout::branch_data_tag: - return new BranchData(data_layout); + return new BranchData(this); case DataLayout::multi_branch_data_tag: - return new MultiBranchData(data_layout); + return new MultiBranchData(this); case DataLayout::arg_info_data_tag: - return new ArgInfoData(data_layout); + return new ArgInfoData(this); }; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodDataOop.hpp --- a/src/share/vm/oops/methodDataOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodDataOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 Sun Microsystems, 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 @@ -55,6 +55,9 @@ // with invocation counter incrementation. None of these races harm correct // execution of the compiled code. +// forward decl +class ProfileData; + // DataLayout // // Overlay for generic profiling data. @@ -231,6 +234,10 @@ temp._header._struct._flags = byte_constant; return temp._header._bits; } + + // GC support + ProfileData* data_in(); + void follow_weak_refs(BoolObjectClosure* cl); }; @@ -430,6 +437,7 @@ virtual void oop_iterate(OopClosure* blk) {} virtual void oop_iterate_m(OopClosure* blk, MemRegion mr) {} virtual void adjust_pointers() {} + virtual void follow_weak_refs(BoolObjectClosure* is_alive_closure) {} #ifndef SERIALGC // Parallel old support @@ -537,6 +545,10 @@ return cell_offset(counter_cell_count); } + void set_count(uint count) { + set_uint_at(count_off, count); + } + #ifndef PRODUCT void print_data_on(outputStream* st); #endif @@ -667,11 +679,44 @@ return recv; } + void set_receiver(uint row, oop p) { + assert((uint)row < row_limit(), "oob"); + set_oop_at(receiver_cell_index(row), p); + } + uint receiver_count(uint row) { assert(row < row_limit(), "oob"); return uint_at(receiver_count_cell_index(row)); } + void set_receiver_count(uint row, uint count) { + assert(row < row_limit(), "oob"); + set_uint_at(receiver_count_cell_index(row), count); + } + + void clear_row(uint row) { + assert(row < row_limit(), "oob"); + // Clear total count - indicator of polymorphic call site. + // The site may look like as monomorphic after that but + // it allow to have more accurate profiling information because + // there was execution phase change since klasses were unloaded. + // If the site is still polymorphic then MDO will be updated + // to reflect it. But it could be the case that the site becomes + // only bimorphic. Then keeping total count not 0 will be wrong. + // Even if we use monomorphic (when it is not) for compilation + // we will only have trap, deoptimization and recompile again + // with updated MDO after executing method in Interpreter. + // An additional receiver will be recorded in the cleaned row + // during next call execution. + // + // Note: our profiling logic works with empty rows in any slot. + // We do sorting a profiling info (ciCallProfile) for compilation. + // + set_count(0); + set_receiver(row, NULL); + set_receiver_count(row, 0); + } + // Code generation support static ByteSize receiver_offset(uint row) { return cell_offset(receiver_cell_index(row)); @@ -688,6 +733,7 @@ virtual void oop_iterate(OopClosure* blk); virtual void oop_iterate_m(OopClosure* blk, MemRegion mr); virtual void adjust_pointers(); + virtual void follow_weak_refs(BoolObjectClosure* is_alive_closure); #ifndef SERIALGC // Parallel old support @@ -1366,6 +1412,9 @@ } void inc_decompile_count() { _nof_decompiles += 1; + if (decompile_count() > (uint)PerMethodRecompilationCutoff) { + method()->set_not_compilable(); + } } // Support for code generation diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodKlass.cpp --- a/src/share/vm/oops/methodKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -308,6 +308,7 @@ } } +#endif //PRODUCT void methodKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_method(), "must be method"); @@ -323,8 +324,6 @@ if (WizardMode && m->code() != NULL) st->print(" ((nmethod*)%p)", m->code()); } -#endif // PRODUCT - const char* methodKlass::internal_name() const { return "{method}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodKlass.hpp --- a/src/share/vm/oops/methodKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -68,14 +68,13 @@ int oop_oop_iterate(oop obj, OopClosure* blk); int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verify operations const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodOop.cpp --- a/src/share/vm/oops/methodOop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodOop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -456,12 +456,12 @@ return objArrayHandle(THREAD, Universe::the_empty_class_klass_array()); } else { methodHandle h_this(THREAD, this_oop); - objArrayOop m_oop = oopFactory::new_objArray(SystemDictionary::class_klass(), length, CHECK_(objArrayHandle())); + objArrayOop m_oop = oopFactory::new_objArray(SystemDictionary::Class_klass(), length, CHECK_(objArrayHandle())); objArrayHandle mirrors (THREAD, m_oop); for (int i = 0; i < length; i++) { CheckedExceptionElement* table = h_this->checked_exceptions_start(); // recompute on each iteration, not gc safe klassOop k = h_this->constants()->klass_at(table[i].class_cp_index, CHECK_(objArrayHandle())); - assert(Klass::cast(k)->is_subclass_of(SystemDictionary::throwable_klass()), "invalid exception class"); + assert(Klass::cast(k)->is_subclass_of(SystemDictionary::Throwable_klass()), "invalid exception class"); mirrors->obj_at_put(i, Klass::cast(k)->java_mirror()); } return mirrors; @@ -575,12 +575,6 @@ return true; } - methodDataOop mdo = method_data(); - if (mdo != NULL - && (uint)mdo->decompile_count() > (uint)PerMethodRecompilationCutoff) { - // Since (uint)-1 is large, -1 really means 'no cutoff'. - return true; - } #ifdef COMPILER2 if (is_tier1_compile(comp_level)) { if (is_not_tier1_compilable()) { @@ -593,7 +587,16 @@ } // call this when compiler finds that this method is not compilable -void methodOopDesc::set_not_compilable(int comp_level) { +void methodOopDesc::set_not_compilable(int comp_level, bool report) { + if (PrintCompilation && report) { + ttyLocker ttyl; + tty->print("made not compilable "); + this->print_short_name(tty); + int size = this->code_size(); + if (size > 0) + tty->print(" (%d bytes)", size); + tty->cr(); + } if ((TraceDeoptimization || LogCompilation) && (xtty != NULL)) { ttyLocker ttyl; xtty->begin_elem("make_not_compilable thread='%d'", (int) os::current_thread_id()); @@ -688,7 +691,7 @@ // so making them eagerly shouldn't be too expensive. AdapterHandlerEntry* adapter = AdapterHandlerLibrary::get_adapter(mh); if (adapter == NULL ) { - THROW_0(vmSymbols::java_lang_OutOfMemoryError()); + THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(), "out of space in CodeCache for adapters"); } mh->set_adapter_entry(adapter); @@ -705,6 +708,16 @@ // This function must not hit a safepoint! address methodOopDesc::verified_code_entry() { debug_only(No_Safepoint_Verifier nsv;) + nmethod *code = (nmethod *)OrderAccess::load_ptr_acquire(&_code); + if (code == NULL && UseCodeCacheFlushing) { + nmethod *saved_code = CodeCache::find_and_remove_saved_code(this); + if (saved_code != NULL) { + methodHandle method(this); + assert( ! saved_code->is_osr_method(), "should not get here for osr" ); + set_code( method, saved_code ); + } + } + assert(_from_compiled_entry != NULL, "must be set"); return _from_compiled_entry; } @@ -733,8 +746,8 @@ int comp_level = code->comp_level(); // In theory there could be a race here. In practice it is unlikely // and not worth worrying about. - if (comp_level > highest_tier_compile()) { - set_highest_tier_compile(comp_level); + if (comp_level > mh->highest_tier_compile()) { + mh->set_highest_tier_compile(comp_level); } OrderAccess::storestore(); @@ -821,6 +834,18 @@ return pchase; } +//------------------------------------------------------------------------------ +// methodOopDesc::is_method_handle_adapter +// +// Tests if this method is an internal adapter frame from the +// MethodHandleCompiler. +bool methodOopDesc::is_method_handle_adapter() const { + return ((name() == vmSymbols::invoke_name() && + method_holder() == SystemDictionary::MethodHandle_klass()) + || + method_holder() == SystemDictionary::InvokeDynamic_klass()); +} + methodHandle methodOopDesc::make_invoke_method(KlassHandle holder, symbolHandle signature, Handle method_type, TRAPS) { @@ -881,7 +906,7 @@ assert((oop)p == method_type(), "pointer chase is correct"); #endif - if (TraceMethodHandles) + if (TraceMethodHandles && (Verbose || WizardMode)) m->print_on(tty); return m; @@ -1032,8 +1057,8 @@ // We are loading classes eagerly. If a ClassNotFoundException or // a LinkageError was generated, be sure to ignore it. if (HAS_PENDING_EXCEPTION) { - if (PENDING_EXCEPTION->is_a(SystemDictionary::classNotFoundException_klass()) || - PENDING_EXCEPTION->is_a(SystemDictionary::linkageError_klass())) { + if (PENDING_EXCEPTION->is_a(SystemDictionary::ClassNotFoundException_klass()) || + PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { CLEAR_PENDING_EXCEPTION; } else { return false; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/methodOop.hpp --- a/src/share/vm/oops/methodOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/methodOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -303,7 +303,7 @@ bool check_code() const; // Not inline to avoid circular ref nmethod* volatile code() const { assert( check_code(), "" ); return (nmethod *)OrderAccess::load_ptr_acquire(&_code); } void clear_code(); // Clear out any compiled code - void set_code(methodHandle mh, nmethod* code); + static void set_code(methodHandle mh, nmethod* code); void set_adapter_entry(AdapterHandlerEntry* adapter) { _adapter = adapter; } address get_i2c_entry(); address get_c2i_entry(); @@ -365,6 +365,7 @@ #endif // byte codes + void set_code(address code) { return constMethod()->set_code(code); } address code_base() const { return constMethod()->code_base(); } bool contains(address bcp) const { return constMethod()->contains(bcp); } @@ -524,6 +525,9 @@ // JSR 292 support bool is_method_handle_invoke() const { return access_flags().is_method_handle_invoke(); } + // Tests if this method is an internal adapter frame from the + // MethodHandleCompiler. + bool is_method_handle_adapter() const; static methodHandle make_invoke_method(KlassHandle holder, symbolHandle signature, Handle method_type, @@ -537,6 +541,7 @@ // all without checking for a stack overflow static int extra_stack_entries() { return (EnableMethodHandles ? (int)MethodHandlePushLimit : 0) + (EnableInvokeDynamic ? 3 : 0); } static int extra_stack_words(); // = extra_stack_entries() * Interpreter::stackElementSize() + // RedefineClasses() support: bool is_old() const { return access_flags().is_old(); } void set_is_old() { _access_flags.set_is_old(); } @@ -555,7 +560,7 @@ // Get this method's jmethodID -- allocate if it doesn't exist jmethodID jmethod_id() { methodHandle this_h(this); - return instanceKlass::jmethod_id_for_impl(method_holder(), this_h); } + return instanceKlass::get_jmethod_id(method_holder(), this_h); } // Lookup the jmethodID for this method. Return NULL if not found. // NOTE that this function can be called from a signal handler @@ -591,7 +596,10 @@ // whether it is not compilable for another reason like having a // breakpoint set in it. bool is_not_compilable(int comp_level = CompLevel_highest_tier) const; - void set_not_compilable(int comp_level = CompLevel_highest_tier); + void set_not_compilable(int comp_level = CompLevel_highest_tier, bool report = true); + void set_not_compilable_quietly(int comp_level = CompLevel_highest_tier) { + set_not_compilable(comp_level, false); + } bool is_not_osr_compilable() const { return is_not_compilable() || access_flags().is_not_osr_compilable(); } void set_not_osr_compilable() { _access_flags.set_not_osr_compilable(); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/objArrayKlass.cpp --- a/src/share/vm/oops/objArrayKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/objArrayKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -127,16 +127,14 @@ // pointer delta is scaled to number of elements (length field in // objArrayOop) which we assume is 32 bit. assert(pd == (size_t)(int)pd, "length field overflow"); - const size_t done_word_len = objArrayOopDesc::array_size((int)pd); - bs->write_ref_array(MemRegion((HeapWord*)dst, done_word_len)); + bs->write_ref_array((HeapWord*)dst, pd); THROW(vmSymbols::java_lang_ArrayStoreException()); return; } } } } - const size_t word_len = objArrayOopDesc::array_size(length); - bs->write_ref_array(MemRegion((HeapWord*)dst, word_len)); + bs->write_ref_array((HeapWord*)dst, length); } void objArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, @@ -248,8 +246,8 @@ } else { objArrayOop sec_oop = oopFactory::new_system_objArray(num_secondaries, CHECK_NULL); objArrayHandle secondaries(THREAD, sec_oop); - secondaries->obj_at_put(num_extra_slots+0, SystemDictionary::cloneable_klass()); - secondaries->obj_at_put(num_extra_slots+1, SystemDictionary::serializable_klass()); + secondaries->obj_at_put(num_extra_slots+0, SystemDictionary::Cloneable_klass()); + secondaries->obj_at_put(num_extra_slots+1, SystemDictionary::Serializable_klass()); for (int i = 0; i < num_elem_supers; i++) { klassOop elem_super = (klassOop) elem_supers->obj_at(i); klassOop array_super = elem_super->klass_part()->array_klass_or_null(); @@ -501,6 +499,8 @@ } } +#endif //PRODUCT + static int max_objArray_print_length = 4; void objArrayKlass::oop_print_value_on(oop obj, outputStream* st) { @@ -510,7 +510,7 @@ int len = objArrayOop(obj)->length(); st->print("[%d] ", len); obj->print_address_on(st); - if (PrintOopAddress || PrintMiscellaneous && (WizardMode || Verbose)) { + if (NOT_PRODUCT(PrintOopAddress ||) PrintMiscellaneous && (WizardMode || Verbose)) { st->print("{"); for (int i = 0; i < len; i++) { if (i > max_objArray_print_length) { @@ -522,8 +522,6 @@ } } -#endif // PRODUCT - const char* objArrayKlass::internal_name() const { return external_name(); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/objArrayKlass.hpp --- a/src/share/vm/oops/objArrayKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/objArrayKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -119,14 +119,13 @@ private: static klassOop array_klass_impl (objArrayKlassHandle this_oop, bool or_null, int n, TRAPS); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on (oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/objArrayKlassKlass.cpp --- a/src/share/vm/oops/objArrayKlassKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/objArrayKlassKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -99,7 +99,7 @@ } } else { // The element type is already Object. Object[] has direct super of Object. - super_klass = KlassHandle(THREAD, SystemDictionary::object_klass()); + super_klass = KlassHandle(THREAD, SystemDictionary::Object_klass()); } } @@ -278,6 +278,7 @@ st->cr(); } +#endif //PRODUCT void objArrayKlassKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_klass(), "must be klass"); @@ -287,8 +288,6 @@ st->print("[]"); } -#endif - const char* objArrayKlassKlass::internal_name() const { return "{object array class}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/objArrayKlassKlass.hpp --- a/src/share/vm/oops/objArrayKlassKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/objArrayKlassKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -64,14 +64,13 @@ // helpers static klassOop allocate_objArray_klass_impl(objArrayKlassKlassHandle this_oop, int n, KlassHandle element_klass, TRAPS); -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT - public: // Verification const char* internal_name() const; void oop_verify_on(oop obj, outputStream* st); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/objArrayOop.hpp --- a/src/share/vm/oops/objArrayOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/objArrayOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -37,6 +37,32 @@ return &((T*)base())[index]; } +private: + // Give size of objArrayOop in HeapWords minus the header + static int array_size(int length) { + const int OopsPerHeapWord = HeapWordSize/heapOopSize; + assert(OopsPerHeapWord >= 1 && (HeapWordSize % heapOopSize == 0), + "Else the following (new) computation would be in error"); +#ifdef ASSERT + // The old code is left in for sanity-checking; it'll + // go away pretty soon. XXX + // Without UseCompressedOops, this is simply: + // oop->length() * HeapWordsPerOop; + // With narrowOops, HeapWordsPerOop is 1/2 or equal 0 as an integer. + // The oop elements are aligned up to wordSize + const int HeapWordsPerOop = heapOopSize/HeapWordSize; + int old_res; + if (HeapWordsPerOop > 0) { + old_res = length * HeapWordsPerOop; + } else { + old_res = align_size_up(length, OopsPerHeapWord)/OopsPerHeapWord; + } +#endif // ASSERT + int res = ((uint)length + OopsPerHeapWord - 1)/OopsPerHeapWord; + assert(res == old_res, "Inconsistency between old and new."); + return res; + } + public: // Returns the offset of the first element. static int base_offset_in_bytes() { @@ -67,27 +93,14 @@ // Sizing static int header_size() { return arrayOopDesc::header_size(T_OBJECT); } int object_size() { return object_size(length()); } - int array_size() { return array_size(length()); } static int object_size(int length) { // This returns the object size in HeapWords. - return align_object_size(header_size() + array_size(length)); - } - - // Give size of objArrayOop in HeapWords minus the header - static int array_size(int length) { - // Without UseCompressedOops, this is simply: - // oop->length() * HeapWordsPerOop; - // With narrowOops, HeapWordsPerOop is 1/2 or equal 0 as an integer. - // The oop elements are aligned up to wordSize - const int HeapWordsPerOop = heapOopSize/HeapWordSize; - if (HeapWordsPerOop > 0) { - return length * HeapWordsPerOop; - } else { - const int OopsPerHeapWord = HeapWordSize/heapOopSize; - int word_len = align_size_up(length, OopsPerHeapWord)/OopsPerHeapWord; - return word_len; - } + uint asz = array_size(length); + uint osz = align_object_size(header_size() + asz); + assert(osz >= asz, "no overflow"); + assert((int)osz > 0, "no overflow"); + return (int)osz; } // special iterators for index ranges, returns size of object diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/oop.cpp --- a/src/share/vm/oops/oop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/oop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -31,14 +31,13 @@ #ifdef PRODUCT void oopDesc::print_on(outputStream* st) const {} -void oopDesc::print_value_on(outputStream* st) const {} void oopDesc::print_address_on(outputStream* st) const {} -char* oopDesc::print_value_string() { return NULL; } char* oopDesc::print_string() { return NULL; } void oopDesc::print() {} -void oopDesc::print_value() {} void oopDesc::print_address() {} -#else + +#else //PRODUCT + void oopDesc::print_on(outputStream* st) const { if (this == NULL) { st->print_cr("NULL"); @@ -47,22 +46,6 @@ } } -void oopDesc::print_value_on(outputStream* st) const { - oop obj = oop(this); - if (this == NULL) { - st->print("NULL"); - } else if (java_lang_String::is_instance(obj)) { - java_lang_String::print(obj, st); - if (PrintOopAddress) print_address_on(st); -#ifdef ASSERT - } else if (!Universe::heap()->is_in(obj) || !Universe::heap()->is_in(klass())) { - st->print("### BAD OOP %p ###", (address)obj); -#endif - } else { - blueprint()->oop_print_value_on(obj, st); - } -} - void oopDesc::print_address_on(outputStream* st) const { if (PrintOopAddress) { st->print("{"INTPTR_FORMAT"}", this); @@ -71,23 +54,47 @@ void oopDesc::print() { print_on(tty); } -void oopDesc::print_value() { print_value_on(tty); } - void oopDesc::print_address() { print_address_on(tty); } char* oopDesc::print_string() { - stringStream* st = new stringStream(); - print_on(st); - return st->as_string(); + stringStream st; + print_on(&st); + return st.as_string(); +} + +#endif // PRODUCT + +// The print_value functions are present in all builds, to support the disassembler. + +void oopDesc::print_value() { + print_value_on(tty); } char* oopDesc::print_value_string() { - stringStream* st = new stringStream(); - print_value_on(st); - return st->as_string(); + char buf[100]; + stringStream st(buf, sizeof(buf)); + print_value_on(&st); + return st.as_string(); } -#endif // PRODUCT +void oopDesc::print_value_on(outputStream* st) const { + oop obj = oop(this); + if (this == NULL) { + st->print("NULL"); + } else if (java_lang_String::is_instance(obj)) { + java_lang_String::print(obj, st); +#ifndef PRODUCT + if (PrintOopAddress) print_address_on(st); +#endif //PRODUCT +#ifdef ASSERT + } else if (!Universe::heap()->is_in(obj) || !Universe::heap()->is_in(klass())) { + st->print("### BAD OOP %p ###", (address)obj); +#endif //ASSERT + } else { + blueprint()->oop_print_value_on(obj, st); + } +} + void oopDesc::verify_on(outputStream* st) { if (this != NULL) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/oop.hpp --- a/src/share/vm/oops/oop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/oop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -30,13 +30,12 @@ // no virtual functions allowed // store into oop with store check -template void oop_store(T* p, oop v); -template void oop_store(volatile T* p, oop v); +template inline void oop_store(T* p, oop v); +template inline void oop_store(volatile T* p, oop v); // store into oop without store check -template void oop_store_without_check(T* p, oop v); -template void oop_store_without_check(volatile T* p, oop v); - +template inline void oop_store_without_check(T* p, oop v); +template inline void oop_store_without_check(volatile T* p, oop v); extern bool always_do_update_barrier; @@ -330,6 +329,7 @@ bool is_perm() const; bool is_perm_or_null() const; + bool is_scavengable() const; bool is_shared() const; bool is_shared_readonly() const; bool is_shared_readwrite() const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/oop.inline2.hpp --- a/src/share/vm/oops/oop.inline2.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/oop.inline2.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -34,3 +34,7 @@ inline bool oopDesc::is_perm_or_null() const { return this == NULL || is_perm(); } + +inline bool oopDesc::is_scavengable() const { + return Universe::heap()->is_scavengable(this); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/symbolKlass.cpp --- a/src/share/vm/oops/symbolKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/symbolKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -213,6 +213,8 @@ st->print("'"); } +#endif //PRODUCT + void symbolKlass::oop_print_value_on(oop obj, outputStream* st) { symbolOop sym = symbolOop(obj); st->print("'"); @@ -222,8 +224,6 @@ st->print("'"); } -#endif //PRODUCT - const char* symbolKlass::internal_name() const { return "{symbol}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/symbolKlass.hpp --- a/src/share/vm/oops/symbolKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/symbolKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -65,10 +65,10 @@ int oop_oop_iterate(oop obj, OopClosure* blk); int oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr); -#ifndef PRODUCT // Printing void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); -#endif +#endif //PRODUCT const char* internal_name() const; }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/symbolOop.cpp --- a/src/share/vm/oops/symbolOop.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/symbolOop.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -25,6 +25,11 @@ # include "incls/_precompiled.incl" # include "incls/_symbolOop.cpp.incl" + +// ------------------------------------------------------------------ +// symbolOopDesc::equals +// +// Compares the symbol with a string of the given length. bool symbolOopDesc::equals(const char* str, int len) const { int l = utf8_length(); if (l != len) return false; @@ -36,6 +41,48 @@ return true; } + +// ------------------------------------------------------------------ +// symbolOopDesc::starts_with +// +// Tests if the symbol starts with the specified prefix of the given +// length. +bool symbolOopDesc::starts_with(const char* prefix, int len) const { + if (len > utf8_length()) return false; + while (len-- > 0) { + if (prefix[len] != (char) byte_at(len)) + return false; + } + assert(len == -1, "we should be at the beginning"); + return true; +} + + +// ------------------------------------------------------------------ +// symbolOopDesc::index_of +// +// Finds if the given string is a substring of this symbol's utf8 bytes. +// Return -1 on failure. Otherwise return the first index where str occurs. +int symbolOopDesc::index_of_at(int i, const char* str, int len) const { + assert(i >= 0 && i <= utf8_length(), "oob"); + if (len <= 0) return 0; + char first_char = str[0]; + address bytes = (address) ((symbolOopDesc*)this)->base(); + address limit = bytes + utf8_length() - len; // inclusive limit + address scan = bytes + i; + if (scan > limit) + return -1; + for (;;) { + scan = (address) memchr(scan, first_char, (limit + 1 - scan)); + if (scan == NULL) + return -1; // not found + assert(scan >= bytes+i && scan <= limit, "scan oob"); + if (memcmp(scan, str, len) == 0) + return (int)(scan - bytes); + } +} + + char* symbolOopDesc::as_C_string(char* buf, int size) const { if (size > 0) { int len = MIN2(size - 1, utf8_length()); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/symbolOop.hpp --- a/src/share/vm/oops/symbolOop.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/symbolOop.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -70,8 +70,21 @@ void set_utf8_length(int len) { _length = len; } - // Compares the symbol with a string + // Compares the symbol with a string. bool equals(const char* str, int len) const; + bool equals(const char* str) const { return equals(str, (int) strlen(str)); } + + // Tests if the symbol starts with the given prefix. + bool starts_with(const char* prefix, int len) const; + bool starts_with(const char* prefix) const { + return starts_with(prefix, (int) strlen(prefix)); + } + + // Tests if the symbol starts with the given prefix. + int index_of_at(int i, const char* str, int len) const; + int index_of_at(int i, const char* str) const { + return index_of_at(i, str, (int) strlen(str)); + } // Three-way compare for sorting; returns -1/0/1 if receiver is than arg // note that the ordering is not alfabetical diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/typeArrayKlassKlass.cpp --- a/src/share/vm/oops/typeArrayKlassKlass.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/typeArrayKlassKlass.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -45,6 +45,7 @@ Klass:: oop_print_on(obj, st); } +#endif //PRODUCT void typeArrayKlassKlass::oop_print_value_on(oop obj, outputStream* st) { assert(obj->is_klass(), "must be klass"); @@ -63,8 +64,6 @@ st->print("}"); } -#endif - const char* typeArrayKlassKlass::internal_name() const { return "{type array class}"; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/oops/typeArrayKlassKlass.hpp --- a/src/share/vm/oops/typeArrayKlassKlass.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/oops/typeArrayKlassKlass.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -47,12 +47,12 @@ static int header_size() { return oopDesc::header_size() + sizeof(typeArrayKlassKlass)/HeapWordSize; } int object_size() const { return align_object_size(header_size()); } -#ifndef PRODUCT public: // Printing + void oop_print_value_on(oop obj, outputStream* st); +#ifndef PRODUCT void oop_print_on(oop obj, outputStream* st); - void oop_print_value_on(oop obj, outputStream* st); -#endif - public: +#endif //PRODUCT + const char* internal_name() const; }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/bytecodeInfo.cpp --- a/src/share/vm/opto/bytecodeInfo.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/bytecodeInfo.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -27,11 +27,16 @@ //============================================================================= //------------------------------InlineTree------------------------------------- -InlineTree::InlineTree( Compile* c, const InlineTree *caller_tree, ciMethod* callee, JVMState* caller_jvms, int caller_bci, float site_invoke_ratio ) +InlineTree::InlineTree( Compile* c, + const InlineTree *caller_tree, ciMethod* callee, + JVMState* caller_jvms, int caller_bci, + float site_invoke_ratio, int site_depth_adjust) : C(c), _caller_jvms(caller_jvms), _caller_tree((InlineTree*)caller_tree), _method(callee), _site_invoke_ratio(site_invoke_ratio), - _count_inline_bcs(method()->code_size()) { + _site_depth_adjust(site_depth_adjust), + _count_inline_bcs(method()->code_size()) +{ NOT_PRODUCT(_count_inlines = 0;) if (_caller_jvms != NULL) { // Keep a private copy of the caller_jvms: @@ -40,7 +45,7 @@ assert(!caller_jvms->should_reexecute(), "there should be no reexecute bytecode with inlining"); } assert(_caller_jvms->same_calls_as(caller_jvms), "consistent JVMS"); - assert((caller_tree == NULL ? 0 : caller_tree->inline_depth() + 1) == inline_depth(), "correct (redundant) depth parameter"); + 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() @@ -52,10 +57,13 @@ } } -InlineTree::InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, float site_invoke_ratio) +InlineTree::InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, + float site_invoke_ratio, int site_depth_adjust) : C(c), _caller_jvms(caller_jvms), _caller_tree(NULL), _method(callee_method), _site_invoke_ratio(site_invoke_ratio), - _count_inline_bcs(method()->code_size()) { + _site_depth_adjust(site_depth_adjust), + _count_inline_bcs(method()->code_size()) +{ NOT_PRODUCT(_count_inlines = 0;) assert(!UseOldInlining, "do not use for old stuff"); } @@ -180,6 +188,10 @@ return NULL; } + // Always inline MethodHandle methods. + if (callee_method->is_method_handle_invoke()) + return NULL; + // First check all inlining restrictions which are required for correctness if (callee_method->is_abstract()) return "abstract method"; // note: we allow ik->is_abstract() @@ -265,10 +277,13 @@ return msg; } - bool is_accessor = InlineAccessors && callee_method->is_accessor(); + if (InlineAccessors && callee_method->is_accessor()) { + // accessor methods are not subject to any of the following limits. + return NULL; + } // suppress a few checks for accessors and trivial methods - if (!is_accessor && callee_method->code_size() > MaxTrivialSize) { + if (callee_method->code_size() > MaxTrivialSize) { // don't inline into giant methods if (C->unique() > (uint)NodeCountInliningCutoff) { @@ -287,7 +302,7 @@ } } - if (!C->do_inlining() && InlineAccessors && !is_accessor) { + if (!C->do_inlining() && InlineAccessors) { return "not an accessor"; } if( inline_depth() > MaxInlineLevel ) { @@ -322,14 +337,17 @@ // stricter than callee_holder->is_initialized() ciBytecodeStream iter(caller_method); iter.force_bci(caller_bci); - int index = iter.get_index_int(); - if( !caller_method->is_klass_loaded(index, true) ) { - return false; - } - // Try to do constant pool resolution if running Xcomp Bytecodes::Code call_bc = iter.cur_bc(); - if( !caller_method->check_call(index, call_bc == Bytecodes::_invokestatic) ) { - return false; + // An invokedynamic instruction does not have a klass. + if (call_bc != Bytecodes::_invokedynamic) { + int index = iter.get_index_int(); + if (!caller_method->is_klass_loaded(index, true)) { + return false; + } + // Try to do constant pool resolution if running Xcomp + if( !caller_method->check_call(index, call_bc == Bytecodes::_invokestatic) ) { + return false; + } } } // We will attempt to see if a class/field/etc got properly loaded. If it @@ -457,7 +475,30 @@ if (old_ilt != NULL) { return old_ilt; } - InlineTree *ilt = new InlineTree( C, this, callee_method, caller_jvms, caller_bci, recur_frequency ); + int new_depth_adjust = 0; + if (caller_jvms->method() != NULL) { + if ((caller_jvms->method()->name() == ciSymbol::invoke_name() && + caller_jvms->method()->holder()->name() == ciSymbol::java_dyn_MethodHandle()) + || caller_jvms->method()->holder()->name() == ciSymbol::java_dyn_InvokeDynamic()) + /* @@@ FIXME: + if (caller_jvms->method()->is_method_handle_adapter()) + */ + new_depth_adjust -= 1; // don't count actions in MH or indy adapter frames + else if (callee_method->is_method_handle_invoke()) { + new_depth_adjust -= 1; // don't count method handle calls from java.dyn implem + } + if (new_depth_adjust != 0 && PrintInlining) { + stringStream nm1; caller_jvms->method()->print_name(&nm1); + stringStream nm2; callee_method->print_name(&nm2); + tty->print_cr("discounting inlining depth from %s to %s", nm1.base(), nm2.base()); + } + if (new_depth_adjust != 0 && C->log()) { + int id1 = C->log()->identify(caller_jvms->method()); + int id2 = C->log()->identify(callee_method); + C->log()->elem("inline_depth_discount caller='%d' callee='%d'", id1, id2); + } + } + InlineTree *ilt = new InlineTree(C, this, callee_method, caller_jvms, caller_bci, recur_frequency, _site_depth_adjust + new_depth_adjust); _subtrees.append( ilt ); NOT_PRODUCT( _count_inlines += 1; ) @@ -483,7 +524,7 @@ Compile* C = Compile::current(); // Root of inline tree - InlineTree *ilt = new InlineTree(C, NULL, C->method(), NULL, -1, 1.0F); + InlineTree *ilt = new InlineTree(C, NULL, C->method(), NULL, -1, 1.0F, 0); return ilt; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/c2_globals.cpp --- a/src/share/vm/opto/c2_globals.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/c2_globals.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -25,4 +25,4 @@ # include "incls/_precompiled.incl" # include "incls/_c2_globals.cpp.incl" -C2_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_NOTPRODUCT_FLAG) +C2_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_EXPERIMENTAL_FLAG, MATERIALIZE_NOTPRODUCT_FLAG) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/c2_globals.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -26,7 +26,7 @@ // Defines all globals flags used by the server compiler. // -#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ +#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, experimental, notproduct) \ \ notproduct(intx, CompileZapFirst, 0, \ "If +ZapDeadCompiledLocals, " \ @@ -154,6 +154,12 @@ notproduct(bool, TraceProfileTripCount, false, \ "Trace profile loop trip count information") \ \ + product(bool, UseLoopPredicate, true, \ + "Generate a predicate to select fast/slow loop versions") \ + \ + develop(bool, TraceLoopPredicate, false, \ + "Trace generation of loop predicates") \ + \ develop(bool, OptoCoalesce, true, \ "Use Conservative Copy Coalescing in the Register Allocator") \ \ @@ -376,7 +382,7 @@ product(intx, AutoBoxCacheMax, 128, \ "Sets max value cached by the java.lang.Integer autobox cache") \ \ - product(bool, DoEscapeAnalysis, false, \ + product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ notproduct(bool, PrintEscapeAnalysis, false, \ @@ -394,6 +400,12 @@ product(bool, UseOptoBiasInlining, true, \ "Generate biased locking code in C2 ideal graph") \ \ + product(bool, OptimizeStringConcat, false, \ + "Optimize the construction of Strings by StringBuilder") \ + \ + notproduct(bool, PrintOptimizeStringConcat, false, \ + "Print information about transformations performed on Strings") \ + \ product(intx, ValueSearchLimit, 1000, \ "Recursion limit in PhaseMacroExpand::value_from_mem_phi") \ \ @@ -413,4 +425,4 @@ product(bool, BlockLayoutRotateLoops, true, \ "Allow back branches to be fall throughs in the block layour") \ -C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_NOTPRODUCT_FLAG) +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 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/callGenerator.cpp --- a/src/share/vm/opto/callGenerator.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/callGenerator.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 Sun Microsystems, 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 @@ -98,12 +98,21 @@ //---------------------------DirectCallGenerator------------------------------ // Internal class which handles all out-of-line calls w/o receiver type checks. class DirectCallGenerator : public CallGenerator { -public: - DirectCallGenerator(ciMethod* method) - : CallGenerator(method) + private: + CallStaticJavaNode* _call_node; + // Force separate memory and I/O projections for the exceptional + // paths to facilitate late inlinig. + bool _separate_io_proj; + + public: + DirectCallGenerator(ciMethod* method, bool separate_io_proj) + : CallGenerator(method), + _separate_io_proj(separate_io_proj) { } virtual JVMState* generate(JVMState* jvms); + + CallStaticJavaNode* call_node() const { return _call_node; } }; JVMState* DirectCallGenerator::generate(JVMState* jvms) { @@ -127,14 +136,85 @@ } // Mark the call node as virtual, sort of: call->set_optimized_virtual(true); + if (method()->is_method_handle_invoke()) { + call->set_method_handle_invoke(true); + kit.C->set_has_method_handle_invokes(true); + } } kit.set_arguments_for_java_call(call); + kit.set_edges_for_java_call(call, false, _separate_io_proj); + Node* ret = kit.set_results_for_java_call(call, _separate_io_proj); + kit.push_node(method()->return_type()->basic_type(), ret); + _call_node = call; // Save the call node in case we need it later + return kit.transfer_exceptions_into_jvms(); +} + +//---------------------------DynamicCallGenerator----------------------------- +// Internal class which handles all out-of-line invokedynamic calls. +class DynamicCallGenerator : public CallGenerator { +public: + DynamicCallGenerator(ciMethod* method) + : CallGenerator(method) + { + } + virtual JVMState* generate(JVMState* jvms); +}; + +JVMState* DynamicCallGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + + if (kit.C->log() != NULL) { + kit.C->log()->elem("dynamic_call bci='%d'", jvms->bci()); + } + + // Get the constant pool cache from the caller class. + ciMethod* caller_method = jvms->method(); + ciBytecodeStream str(caller_method); + str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci. + assert(str.cur_bc() == Bytecodes::_invokedynamic, "wrong place to issue a dynamic call!"); + ciCPCache* cpcache = str.get_cpcache(); + + // Get the offset of the CallSite from the constant pool cache + // pointer. + int index = str.get_method_index(); + size_t call_site_offset = cpcache->get_f1_offset(index); + + // Load the CallSite object from the constant pool cache. + const TypeOopPtr* cpcache_ptr = TypeOopPtr::make_from_constant(cpcache); + Node* cpcache_adr = kit.makecon(cpcache_ptr); + Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, cpcache_adr, call_site_offset); + Node* call_site = kit.make_load(kit.control(), call_site_adr, TypeInstPtr::BOTTOM, T_OBJECT, Compile::AliasIdxRaw); + + // Load the target MethodHandle from the CallSite object. + Node* target_mh_adr = kit.basic_plus_adr(call_site, call_site, java_dyn_CallSite::target_offset_in_bytes()); + Node* target_mh = kit.make_load(kit.control(), target_mh_adr, TypeInstPtr::BOTTOM, T_OBJECT); + + address resolve_stub = SharedRuntime::get_resolve_opt_virtual_call_stub(); + + CallStaticJavaNode *call = new (kit.C, tf()->domain()->cnt()) CallStaticJavaNode(tf(), resolve_stub, method(), kit.bci()); + // invokedynamic is treated as an optimized invokevirtual. + call->set_optimized_virtual(true); + // Take extra care (in the presence of argument motion) not to trash the SP: + call->set_method_handle_invoke(true); + kit.C->set_has_method_handle_invokes(true); + + // Pass the target MethodHandle as first argument and shift the + // other arguments. + call->init_req(0 + TypeFunc::Parms, target_mh); + uint nargs = call->method()->arg_size(); + for (uint i = 1; i < nargs; i++) { + Node* arg = kit.argument(i - 1); + call->init_req(i + TypeFunc::Parms, arg); + } + kit.set_edges_for_java_call(call); Node* ret = kit.set_results_for_java_call(call); kit.push_node(method()->return_type()->basic_type(), ret); return kit.transfer_exceptions_into_jvms(); } +//--------------------------VirtualCallGenerator------------------------------ +// Internal class which handles all out-of-line calls checking receiver type. class VirtualCallGenerator : public CallGenerator { private: int _vtable_index; @@ -149,8 +229,6 @@ virtual JVMState* generate(JVMState* jvms); }; -//--------------------------VirtualCallGenerator------------------------------ -// Internal class which handles all out-of-line calls checking receiver type. JVMState* VirtualCallGenerator::generate(JVMState* jvms) { GraphKit kit(jvms); Node* receiver = kit.argument(0); @@ -238,16 +316,124 @@ return new ParseGenerator(m, expected_uses, true); } -CallGenerator* CallGenerator::for_direct_call(ciMethod* m) { +CallGenerator* CallGenerator::for_direct_call(ciMethod* m, bool separate_io_proj) { assert(!m->is_abstract(), "for_direct_call mismatch"); - return new DirectCallGenerator(m); + return new DirectCallGenerator(m, separate_io_proj); +} + +CallGenerator* CallGenerator::for_dynamic_call(ciMethod* m) { + assert(m->is_method_handle_invoke(), "for_dynamic_call mismatch"); + return new DynamicCallGenerator(m); } CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) { assert(!m->is_static(), "for_virtual_call mismatch"); + assert(!m->is_method_handle_invoke(), "should be a direct call"); return new VirtualCallGenerator(m, vtable_index); } +// Allow inlining decisions to be delayed +class LateInlineCallGenerator : public DirectCallGenerator { + CallGenerator* _inline_cg; + + public: + LateInlineCallGenerator(ciMethod* method, CallGenerator* inline_cg) : + DirectCallGenerator(method, true), _inline_cg(inline_cg) {} + + virtual bool is_late_inline() const { return true; } + + // Convert the CallStaticJava into an inline + virtual void do_late_inline(); + + JVMState* generate(JVMState* jvms) { + // Record that this call site should be revisited once the main + // parse is finished. + Compile::current()->add_late_inline(this); + + // Emit the CallStaticJava and request separate projections so + // that the late inlining logic can distinguish between fall + // through and exceptional uses of the memory and io projections + // as is done for allocations and macro expansion. + return DirectCallGenerator::generate(jvms); + } + +}; + + +void LateInlineCallGenerator::do_late_inline() { + // Can't inline it + if (call_node() == NULL || call_node()->outcnt() == 0 || + call_node()->in(0) == NULL || call_node()->in(0)->is_top()) + return; + + CallStaticJavaNode* call = call_node(); + + // Make a clone of the JVMState that appropriate to use for driving a parse + Compile* C = Compile::current(); + JVMState* jvms = call->jvms()->clone_shallow(C); + uint size = call->req(); + SafePointNode* map = new (C, size) SafePointNode(size, jvms); + for (uint i1 = 0; i1 < size; i1++) { + map->init_req(i1, call->in(i1)); + } + + // Make sure the state is a MergeMem for parsing. + if (!map->in(TypeFunc::Memory)->is_MergeMem()) { + map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory))); + } + + // Make enough space for the expression stack and transfer the incoming arguments + int nargs = method()->arg_size(); + jvms->set_map(map); + map->ensure_stack(jvms, jvms->method()->max_stack()); + if (nargs > 0) { + for (int i1 = 0; i1 < nargs; i1++) { + map->set_req(i1 + jvms->argoff(), call->in(TypeFunc::Parms + i1)); + } + } + + CompileLog* log = C->log(); + if (log != NULL) { + log->head("late_inline method='%d'", log->identify(method())); + JVMState* p = jvms; + while (p != NULL) { + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); + p = p->caller(); + } + log->tail("late_inline"); + } + + // Setup default node notes to be picked up by the inlining + Node_Notes* old_nn = C->default_node_notes(); + if (old_nn != NULL) { + Node_Notes* entry_nn = old_nn->clone(C); + entry_nn->set_jvms(jvms); + C->set_default_node_notes(entry_nn); + } + + // Now perform the inling using the synthesized JVMState + JVMState* new_jvms = _inline_cg->generate(jvms); + if (new_jvms == NULL) return; // no change + if (C->failing()) return; + + // Capture any exceptional control flow + GraphKit kit(new_jvms); + + // Find the result object + Node* result = C->top(); + int result_size = method()->return_type()->size(); + if (result_size != 0 && !kit.stopped()) { + result = (result_size == 1) ? kit.pop() : kit.pop_pair(); + } + + kit.replace_call(call, result); +} + + +CallGenerator* CallGenerator::for_late_inline(ciMethod* method, CallGenerator* inline_cg) { + return new LateInlineCallGenerator(method, inline_cg); +} + //---------------------------WarmCallGenerator-------------------------------- // Internal class which handles initial deferral of inlining decisions. @@ -315,70 +501,7 @@ } void WarmCallInfo::make_hot() { - Compile* C = Compile::current(); - // Replace the callnode with something better. - CallJavaNode* call = this->call()->as_CallJava(); - ciMethod* method = call->method(); - int nargs = method->arg_size(); - JVMState* jvms = call->jvms()->clone_shallow(C); - uint size = TypeFunc::Parms + MAX2(2, nargs); - SafePointNode* map = new (C, size) SafePointNode(size, jvms); - for (uint i1 = 0; i1 < (uint)(TypeFunc::Parms + nargs); i1++) { - map->init_req(i1, call->in(i1)); - } - jvms->set_map(map); - jvms->set_offsets(map->req()); - jvms->set_locoff(TypeFunc::Parms); - jvms->set_stkoff(TypeFunc::Parms); - GraphKit kit(jvms); - - JVMState* new_jvms = _hot_cg->generate(kit.jvms()); - if (new_jvms == NULL) return; // no change - if (C->failing()) return; - - kit.set_jvms(new_jvms); - Node* res = C->top(); - int res_size = method->return_type()->size(); - if (res_size != 0) { - kit.inc_sp(-res_size); - res = kit.argument(0); - } - GraphKit ekit(kit.combine_and_pop_all_exception_states()->jvms()); - - // Replace the call: - for (DUIterator i = call->outs(); call->has_out(i); i++) { - Node* n = call->out(i); - Node* nn = NULL; // replacement - if (n->is_Proj()) { - ProjNode* nproj = n->as_Proj(); - assert(nproj->_con < (uint)(TypeFunc::Parms + (res_size ? 1 : 0)), "sane proj"); - if (nproj->_con == TypeFunc::Parms) { - nn = res; - } else { - nn = kit.map()->in(nproj->_con); - } - if (nproj->_con == TypeFunc::I_O) { - for (DUIterator j = nproj->outs(); nproj->has_out(j); j++) { - Node* e = nproj->out(j); - if (e->Opcode() == Op_CreateEx) { - e->replace_by(ekit.argument(0)); - } else if (e->Opcode() == Op_Catch) { - for (DUIterator k = e->outs(); e->has_out(k); k++) { - CatchProjNode* p = e->out(j)->as_CatchProj(); - if (p->is_handler_proj()) { - p->replace_by(ekit.control()); - } else { - p->replace_by(kit.control()); - } - } - } - } - } - } - NOT_PRODUCT(if (!nn) n->dump(2)); - assert(nn != NULL, "don't know what to do with this user"); - n->replace_by(nn); - } + Unimplemented(); } void WarmCallInfo::make_cold() { @@ -527,6 +650,155 @@ } +//------------------------PredictedDynamicCallGenerator----------------------- +// Internal class which handles all out-of-line calls checking receiver type. +class PredictedDynamicCallGenerator : public CallGenerator { + ciMethodHandle* _predicted_method_handle; + CallGenerator* _if_missed; + CallGenerator* _if_hit; + float _hit_prob; + +public: + PredictedDynamicCallGenerator(ciMethodHandle* predicted_method_handle, + CallGenerator* if_missed, + CallGenerator* if_hit, + float hit_prob) + : CallGenerator(if_missed->method()), + _predicted_method_handle(predicted_method_handle), + _if_missed(if_missed), + _if_hit(if_hit), + _hit_prob(hit_prob) + {} + + virtual bool is_inline() const { return _if_hit->is_inline(); } + virtual bool is_deferred() const { return _if_hit->is_deferred(); } + + virtual JVMState* generate(JVMState* jvms); +}; + + +CallGenerator* CallGenerator::for_predicted_dynamic_call(ciMethodHandle* predicted_method_handle, + CallGenerator* if_missed, + CallGenerator* if_hit, + float hit_prob) { + return new PredictedDynamicCallGenerator(predicted_method_handle, if_missed, if_hit, hit_prob); +} + + +JVMState* PredictedDynamicCallGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + PhaseGVN& gvn = kit.gvn(); + + CompileLog* log = kit.C->log(); + if (log != NULL) { + log->elem("predicted_dynamic_call bci='%d'", jvms->bci()); + } + + // Get the constant pool cache from the caller class. + ciMethod* caller_method = jvms->method(); + ciBytecodeStream str(caller_method); + str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci. + ciCPCache* cpcache = str.get_cpcache(); + + // Get the offset of the CallSite from the constant pool cache + // pointer. + int index = str.get_method_index(); + size_t call_site_offset = cpcache->get_f1_offset(index); + + // Load the CallSite object from the constant pool cache. + const TypeOopPtr* cpcache_ptr = TypeOopPtr::make_from_constant(cpcache); + Node* cpcache_adr = kit.makecon(cpcache_ptr); + Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, cpcache_adr, call_site_offset); + Node* call_site = kit.make_load(kit.control(), call_site_adr, TypeInstPtr::BOTTOM, T_OBJECT, Compile::AliasIdxRaw); + + // Load the target MethodHandle from the CallSite object. + Node* target_adr = kit.basic_plus_adr(call_site, call_site, java_dyn_CallSite::target_offset_in_bytes()); + Node* target_mh = kit.make_load(kit.control(), target_adr, TypeInstPtr::BOTTOM, T_OBJECT); + + // Check if the MethodHandle is still the same. + const TypeOopPtr* predicted_mh_ptr = TypeOopPtr::make_from_constant(_predicted_method_handle, true); + Node* predicted_mh = kit.makecon(predicted_mh_ptr); + + Node* cmp = gvn.transform(new(kit.C, 3) CmpPNode(target_mh, predicted_mh)); + Node* bol = gvn.transform(new(kit.C, 2) BoolNode(cmp, BoolTest::eq) ); + IfNode* iff = kit.create_and_xform_if(kit.control(), bol, _hit_prob, COUNT_UNKNOWN); + kit.set_control( gvn.transform(new(kit.C, 1) IfTrueNode (iff))); + Node* slow_ctl = gvn.transform(new(kit.C, 1) IfFalseNode(iff)); + + SafePointNode* slow_map = NULL; + JVMState* slow_jvms; + { PreserveJVMState pjvms(&kit); + kit.set_control(slow_ctl); + if (!kit.stopped()) { + slow_jvms = _if_missed->generate(kit.sync_jvms()); + assert(slow_jvms != NULL, "miss path must not fail to generate"); + kit.add_exception_states_from(slow_jvms); + kit.set_map(slow_jvms->map()); + if (!kit.stopped()) + slow_map = kit.stop(); + } + } + + if (kit.stopped()) { + // Instance exactly does not matches the desired type. + kit.set_jvms(slow_jvms); + return kit.transfer_exceptions_into_jvms(); + } + + // Make the hot call: + JVMState* new_jvms = _if_hit->generate(kit.sync_jvms()); + if (new_jvms == NULL) { + // Inline failed, so make a direct call. + assert(_if_hit->is_inline(), "must have been a failed inline"); + CallGenerator* cg = CallGenerator::for_direct_call(_if_hit->method()); + new_jvms = cg->generate(kit.sync_jvms()); + } + kit.add_exception_states_from(new_jvms); + kit.set_jvms(new_jvms); + + // Need to merge slow and fast? + if (slow_map == NULL) { + // The fast path is the only path remaining. + return kit.transfer_exceptions_into_jvms(); + } + + if (kit.stopped()) { + // Inlined method threw an exception, so it's just the slow path after all. + kit.set_jvms(slow_jvms); + return kit.transfer_exceptions_into_jvms(); + } + + // Finish the diamond. + kit.C->set_has_split_ifs(true); // Has chance for split-if optimization + RegionNode* region = new (kit.C, 3) RegionNode(3); + region->init_req(1, kit.control()); + region->init_req(2, slow_map->control()); + kit.set_control(gvn.transform(region)); + Node* iophi = PhiNode::make(region, kit.i_o(), Type::ABIO); + iophi->set_req(2, slow_map->i_o()); + kit.set_i_o(gvn.transform(iophi)); + kit.merge_memory(slow_map->merged_memory(), region, 2); + uint tos = kit.jvms()->stkoff() + kit.sp(); + uint limit = slow_map->req(); + for (uint i = TypeFunc::Parms; i < limit; i++) { + // Skip unused stack slots; fast forward to monoff(); + if (i == tos) { + i = kit.jvms()->monoff(); + if( i >= limit ) break; + } + 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)); + Node* phi = PhiNode::make(region, m, t); + phi->set_req(2, n); + kit.map()->set_req(i, gvn.transform(phi)); + } + } + return kit.transfer_exceptions_into_jvms(); +} + + //-------------------------UncommonTrapCallGenerator----------------------------- // Internal class which handles all out-of-line calls checking receiver type. class UncommonTrapCallGenerator : public CallGenerator { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/callGenerator.hpp --- a/src/share/vm/opto/callGenerator.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/callGenerator.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -57,6 +57,13 @@ // is_trap: Does not return to the caller. (E.g., uncommon trap.) virtual bool is_trap() const { return false; } + // is_late_inline: supports conversion of call into an inline + virtual bool is_late_inline() const { return false; } + // Replace the call with an inline version of the code + virtual void do_late_inline() { ShouldNotReachHere(); } + + virtual CallStaticJavaNode* call_node() const { ShouldNotReachHere(); return NULL; } + // Note: It is possible for a CG to be both inline and virtual. // (The hashCode intrinsic does a vtable check and an inlined fast path.) @@ -92,9 +99,13 @@ static CallGenerator* for_osr(ciMethod* m, int osr_bci); // How to generate vanilla out-of-line call sites: - static CallGenerator* for_direct_call(ciMethod* m); // static, special + static CallGenerator* for_direct_call(ciMethod* m, bool separate_io_projs = false); // static, special + static CallGenerator* for_dynamic_call(ciMethod* m); // invokedynamic static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface + // How to generate a replace a direct call with an inline version + static CallGenerator* for_late_inline(ciMethod* m, CallGenerator* inline_cg); + // How to make a call but defer the decision whether to inline or not. static CallGenerator* for_warm_call(WarmCallInfo* ci, CallGenerator* if_cold, @@ -106,6 +117,12 @@ CallGenerator* if_hit, float hit_prob); + // How to make a call that optimistically assumes a MethodHandle target: + static CallGenerator* for_predicted_dynamic_call(ciMethodHandle* predicted_method_handle, + CallGenerator* if_missed, + CallGenerator* if_hit, + float hit_prob); + // How to make a call that gives up and goes back to the interpreter: static CallGenerator* for_uncommon_trap(ciMethod* m, Deoptimization::DeoptReason reason, diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/callnode.cpp --- a/src/share/vm/opto/callnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/callnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -421,21 +421,23 @@ iklass = cik->as_instance_klass(); } else if (cik->is_type_array_klass()) { cik->as_array_klass()->base_element_type()->print_name_on(st); - st->print("[%d]=", spobj->n_fields()); + st->print("[%d]", spobj->n_fields()); } else if (cik->is_obj_array_klass()) { - ciType* cie = cik->as_array_klass()->base_element_type(); - int ndim = 1; - while (cie->is_obj_array_klass()) { - ndim += 1; - cie = cie->as_array_klass()->base_element_type(); + ciKlass* cie = cik->as_obj_array_klass()->base_element_klass(); + if (cie->is_instance_klass()) { + cie->print_name_on(st); + } else if (cie->is_type_array_klass()) { + cie->as_array_klass()->base_element_type()->print_name_on(st); + } else { + ShouldNotReachHere(); } - cie->print_name_on(st); + st->print("[%d]", spobj->n_fields()); + int ndim = cik->as_array_klass()->dimension() - 1; while (ndim-- > 0) { st->print("[]"); } - st->print("[%d]=", spobj->n_fields()); } - st->print("{"); + st->print("={"); uint nf = spobj->n_fields(); if (nf > 0) { uint first_ind = spobj->first_index(); @@ -493,7 +495,8 @@ if (!printed) _method->print_short_name(st); st->print(" @ bci:%d",_bci); - st->print(" reexecute:%s", _reexecute==Reexecute_True?"true":"false"); + if(_reexecute == Reexecute_True) + st->print(" reexecute"); } else { st->print(" runtime stub"); } @@ -690,6 +693,84 @@ } +void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj) { + projs->fallthrough_proj = NULL; + projs->fallthrough_catchproj = NULL; + projs->fallthrough_ioproj = NULL; + projs->catchall_ioproj = NULL; + projs->catchall_catchproj = NULL; + projs->fallthrough_memproj = NULL; + projs->catchall_memproj = NULL; + projs->resproj = NULL; + projs->exobj = NULL; + + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + ProjNode *pn = fast_out(i)->as_Proj(); + if (pn->outcnt() == 0) continue; + switch (pn->_con) { + case TypeFunc::Control: + { + // For Control (fallthrough) and I_O (catch_all_index) we have CatchProj -> Catch -> Proj + projs->fallthrough_proj = pn; + DUIterator_Fast jmax, j = pn->fast_outs(jmax); + const Node *cn = pn->fast_out(j); + if (cn->is_Catch()) { + ProjNode *cpn = NULL; + for (DUIterator_Fast kmax, k = cn->fast_outs(kmax); k < kmax; k++) { + cpn = cn->fast_out(k)->as_Proj(); + assert(cpn->is_CatchProj(), "must be a CatchProjNode"); + if (cpn->_con == CatchProjNode::fall_through_index) + projs->fallthrough_catchproj = cpn; + else { + assert(cpn->_con == CatchProjNode::catch_all_index, "must be correct index."); + projs->catchall_catchproj = cpn; + } + } + } + break; + } + case TypeFunc::I_O: + if (pn->_is_io_use) + projs->catchall_ioproj = pn; + else + projs->fallthrough_ioproj = pn; + for (DUIterator j = pn->outs(); pn->has_out(j); j++) { + Node* e = pn->out(j); + if (e->Opcode() == Op_CreateEx && e->in(0)->is_CatchProj()) { + assert(projs->exobj == NULL, "only one"); + projs->exobj = e; + } + } + break; + case TypeFunc::Memory: + if (pn->_is_io_use) + projs->catchall_memproj = pn; + else + projs->fallthrough_memproj = pn; + break; + case TypeFunc::Parms: + projs->resproj = pn; + break; + default: + assert(false, "unexpected projection from allocation node."); + } + } + + // The resproj may not exist because the result couuld be ignored + // and the exception object may not exist if an exception handler + // swallows the exception but all the other must exist and be found. + assert(projs->fallthrough_proj != NULL, "must be found"); + assert(projs->fallthrough_catchproj != NULL, "must be found"); + assert(projs->fallthrough_memproj != NULL, "must be found"); + assert(projs->fallthrough_ioproj != NULL, "must be found"); + assert(projs->catchall_catchproj != NULL, "must be found"); + if (separate_io_proj) { + assert(projs->catchall_memproj != NULL, "must be found"); + assert(projs->catchall_ioproj != NULL, "must be found"); + } +} + + //============================================================================= uint CallJavaNode::size_of() const { return sizeof(*this); } uint CallJavaNode::cmp( const Node &n ) const { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/callnode.hpp --- a/src/share/vm/opto/callnode.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/callnode.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -470,6 +470,23 @@ #endif }; + +// Simple container for the outgoing projections of a call. Useful +// for serious surgery on calls. +class CallProjections : public StackObj { +public: + Node* fallthrough_proj; + Node* fallthrough_catchproj; + Node* fallthrough_memproj; + Node* fallthrough_ioproj; + Node* catchall_catchproj; + Node* catchall_memproj; + Node* catchall_ioproj; + Node* resproj; + Node* exobj; +}; + + //------------------------------CallNode--------------------------------------- // Call nodes now subsume the function of debug nodes at callsites, so they // contain the functionality of a full scope chain of debug nodes. @@ -521,6 +538,11 @@ // or returns NULL if there is no one. Node *result_cast(); + // Collect all the interesting edges from a call for use in + // replacing the call by something else. Used by macro expansion + // and the late inlining support. + void extract_projections(CallProjections* projs, bool separate_io_proj); + virtual uint match_edge(uint idx) const; #ifndef PRODUCT @@ -529,6 +551,7 @@ #endif }; + //------------------------------CallJavaNode----------------------------------- // Make a static or dynamic subroutine call node using Java calling // convention. (The "Java" calling convention is the compiler's calling @@ -539,12 +562,15 @@ virtual uint size_of() const; // Size is bigger bool _optimized_virtual; + bool _method_handle_invoke; ciMethod* _method; // Method being direct called public: const int _bci; // Byte Code Index of call byte code CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method, int bci) : CallNode(tf, addr, TypePtr::BOTTOM), - _method(method), _bci(bci), _optimized_virtual(false) + _method(method), _bci(bci), + _optimized_virtual(false), + _method_handle_invoke(false) { init_class_id(Class_CallJava); } @@ -554,6 +580,8 @@ void set_method(ciMethod *m) { _method = m; } void set_optimized_virtual(bool f) { _optimized_virtual = f; } bool is_optimized_virtual() const { return _optimized_virtual; } + void set_method_handle_invoke(bool f) { _method_handle_invoke = f; } + bool is_method_handle_invoke() const { return _method_handle_invoke; } #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/cfgnode.cpp --- a/src/share/vm/opto/cfgnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/cfgnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1531,6 +1531,8 @@ return NULL; // No change Node *top = phase->C->top(); + bool new_phi = (outcnt() == 0); // transforming new Phi + assert(!can_reshape || !new_phi, "for igvn new phi should be hooked"); // The are 2 situations when only one valid phi's input is left // (in addition to Region input). @@ -1550,6 +1552,12 @@ } } + if (can_reshape && outcnt() == 0) { + // set_req() above may kill outputs if Phi is referenced + // only by itself on the dead (top) control path. + return top; + } + Node* uin = unique_input(phase); if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) // IGVN transformation @@ -1684,8 +1692,7 @@ // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! - progress = phase->C->top(); - break; + return top; } // If transformed to a MergeMem, get the desired slice // Otherwise the returned node represents memory for every slice diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/chaitin.cpp --- a/src/share/vm/opto/chaitin.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/chaitin.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -985,6 +985,8 @@ uint lo_score = _hi_degree; double score = lrgs(lo_score).score(); double area = lrgs(lo_score)._area; + double cost = lrgs(lo_score)._cost; + bool bound = lrgs(lo_score)._is_bound; // Find cheapest guy debug_only( int lo_no_simplify=0; ); @@ -1002,17 +1004,27 @@ debug_only( if( lrgs(i)._was_lo ) lo_no_simplify=i; ); double iscore = lrgs(i).score(); double iarea = lrgs(i)._area; + double icost = lrgs(i)._cost; + bool ibound = lrgs(i)._is_bound; // Compare cost/area of i vs cost/area of lo_score. Smaller cost/area // wins. Ties happen because all live ranges in question have spilled // a few times before and the spill-score adds a huge number which // washes out the low order bits. We are choosing the lesser of 2 // evils; in this case pick largest area to spill. + // Ties also happen when live ranges are defined and used only inside + // one block. In which case their area is 0 and score set to max. + // In such case choose bound live range over unbound to free registers + // or with smaller cost to spill. if( iscore < score || - (iscore == score && iarea > area && lrgs(lo_score)._was_spilled2) ) { + (iscore == score && iarea > area && lrgs(lo_score)._was_spilled2) || + (iscore == score && iarea == area && + ( (ibound && !bound) || ibound == bound && (icost < cost) )) ) { lo_score = i; score = iscore; area = iarea; + cost = icost; + bound = ibound; } } LRG *lo_lrg = &lrgs(lo_score); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/chaitin.hpp --- a/src/share/vm/opto/chaitin.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/chaitin.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -458,6 +458,16 @@ // Post-Allocation peephole copy removal void post_allocate_copy_removal(); Node *skip_copies( Node *c ); + // Replace the old node with the current live version of that value + // and yank the old value if it's dead. + int replace_and_yank_if_dead( Node *old, OptoReg::Name nreg, + Block *current_block, Node_List& value, Node_List& regnd ) { + Node* v = regnd[nreg]; + assert(v->outcnt() != 0, "no dead values"); + old->replace_by(v); + return yank_if_dead(old, current_block, &value, ®nd); + } + int yank_if_dead( Node *old, Block *current_block, Node_List *value, Node_List *regnd ); int elide_copy( Node *n, int k, Block *current_block, Node_List &value, Node_List ®nd, bool can_change_regs ); int use_prior_register( Node *copy, uint idx, Node *def, Block *current_block, Node_List &value, Node_List ®nd ); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/compile.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -224,6 +224,32 @@ } +void Compile::gvn_replace_by(Node* n, Node* nn) { + for (DUIterator_Last imin, i = n->last_outs(imin); i >= imin; ) { + Node* use = n->last_out(i); + bool is_in_table = initial_gvn()->hash_delete(use); + uint uses_found = 0; + for (uint j = 0; j < use->len(); j++) { + if (use->in(j) == n) { + if (j < use->req()) + use->set_req(j, nn); + else + use->set_prec(j, nn); + uses_found++; + } + } + if (is_in_table) { + // reinsert into table + initial_gvn()->hash_find_insert(use); + } + record_for_igvn(use); + i -= uses_found; // we deleted 1 or more copies of this edge + } +} + + + + // Identify all nodes that are reachable from below, useful. // Use breadth-first pass that records state in a Unique_Node_List, // recursive traversal is slower. @@ -439,6 +465,7 @@ _code_buffer("Compile::Fill_buffer"), _orig_pc_slot(0), _orig_pc_slot_offset_in_bytes(0), + _has_method_handle_invokes(false), _node_bundling_limit(0), _node_bundling_base(NULL), _java_calls(0), @@ -554,6 +581,28 @@ rethrow_exceptions(kit.transfer_exceptions_into_jvms()); } + if (!failing() && has_stringbuilder()) { + { + // remove useless nodes to make the usage analysis simpler + ResourceMark rm; + PhaseRemoveUseless pru(initial_gvn(), &for_igvn); + } + + { + ResourceMark rm; + print_method("Before StringOpts", 3); + PhaseStringOpts pso(initial_gvn(), &for_igvn); + print_method("After StringOpts", 3); + } + + // now inline anything that we skipped the first time around + while (_late_inlines.length() > 0) { + CallGenerator* cg = _late_inlines.pop(); + cg->do_late_inline(); + } + } + assert(_late_inlines.length() == 0, "should have been processed"); + print_method("Before RemoveUseless", 3); // Remove clutter produced by parsing. @@ -711,6 +760,7 @@ _do_escape_analysis(false), _failure_reason(NULL), _code_buffer("Compile::Fill_buffer"), + _has_method_handle_invokes(false), _node_bundling_limit(0), _node_bundling_base(NULL), _java_calls(0), @@ -820,6 +870,7 @@ _fixed_slots = 0; set_has_split_ifs(false); set_has_loops(has_method() && method()->has_loops()); // first approximation + set_has_stringbuilder(false); _deopt_happens = true; // start out assuming the worst _trap_can_recompile = false; // no traps emitted yet _major_progress = true; // start out assuming good things will happen @@ -883,6 +934,7 @@ _intrinsics = NULL; _macro_nodes = new GrowableArray(comp_arena(), 8, 0, NULL); + _predicate_opaqs = new GrowableArray(comp_arena(), 8, 0, NULL); register_library_intrinsics(); } @@ -1504,6 +1556,19 @@ } } +//---------------------cleanup_loop_predicates----------------------- +// Remove the opaque nodes that protect the predicates so that all unused +// checks and uncommon_traps will be eliminated from the ideal graph +void Compile::cleanup_loop_predicates(PhaseIterGVN &igvn) { + if (predicate_count()==0) return; + for (int i = predicate_count(); i > 0; i--) { + Node * n = predicate_opaque1_node(i-1); + assert(n->Opcode() == Op_Opaque1, "must be"); + igvn.replace_node(n, n->in(1)); + } + assert(predicate_count()==0, "should be clean!"); + igvn.optimize(); +} //------------------------------Optimize--------------------------------------- // Given a graph, optimize it. @@ -1545,7 +1610,7 @@ if((loop_opts_cnt > 0) && (has_loops() || has_split_ifs())) { { TracePhase t2("idealLoop", &_t_idealLoop, true); - PhaseIdealLoop ideal_loop( igvn, NULL, true ); + PhaseIdealLoop ideal_loop( igvn, true, UseLoopPredicate); loop_opts_cnt--; if (major_progress()) print_method("PhaseIdealLoop 1", 2); if (failing()) return; @@ -1553,7 +1618,7 @@ // Loop opts pass if partial peeling occurred in previous pass if(PartialPeelLoop && major_progress() && (loop_opts_cnt > 0)) { TracePhase t3("idealLoop", &_t_idealLoop, true); - PhaseIdealLoop ideal_loop( igvn, NULL, false ); + PhaseIdealLoop ideal_loop( igvn, false, UseLoopPredicate); loop_opts_cnt--; if (major_progress()) print_method("PhaseIdealLoop 2", 2); if (failing()) return; @@ -1561,10 +1626,15 @@ // Loop opts pass for loop-unrolling before CCP if(major_progress() && (loop_opts_cnt > 0)) { TracePhase t4("idealLoop", &_t_idealLoop, true); - PhaseIdealLoop ideal_loop( igvn, NULL, false ); + PhaseIdealLoop ideal_loop( igvn, false, UseLoopPredicate); loop_opts_cnt--; if (major_progress()) print_method("PhaseIdealLoop 3", 2); } + if (!failing()) { + // Verify that last round of loop opts produced a valid graph + NOT_PRODUCT( TracePhase t2("idealLoopVerify", &_t_idealLoopVerify, TimeCompiler); ) + PhaseIdealLoop::verify(igvn); + } } if (failing()) return; @@ -1594,15 +1664,31 @@ // peeling, unrolling, etc. if(loop_opts_cnt > 0) { debug_only( int cnt = 0; ); + bool loop_predication = UseLoopPredicate; while(major_progress() && (loop_opts_cnt > 0)) { TracePhase t2("idealLoop", &_t_idealLoop, true); assert( cnt++ < 40, "infinite cycle in loop optimization" ); - PhaseIdealLoop ideal_loop( igvn, NULL, true ); + PhaseIdealLoop ideal_loop( igvn, true, loop_predication); loop_opts_cnt--; if (major_progress()) print_method("PhaseIdealLoop iterations", 2); if (failing()) return; + // Perform loop predication optimization during first iteration after CCP. + // After that switch it off and cleanup unused loop predicates. + if (loop_predication) { + loop_predication = false; + cleanup_loop_predicates(igvn); + if (failing()) return; + } } } + + { + // Verify that all previous optimizations produced a valid graph + // at least to this point, even if no loop optimizations were done. + NOT_PRODUCT( TracePhase t2("idealLoopVerify", &_t_idealLoopVerify, TimeCompiler); ) + PhaseIdealLoop::verify(igvn); + } + { NOT_PRODUCT( TracePhase t2("macroExpand", &_t_macroExpand, TimeCompiler); ) PhaseMacroExpand mex(igvn); @@ -1790,6 +1876,7 @@ !n->is_Phi() && // a few noisely useless nodes !n->is_Proj() && !n->is_MachTemp() && + !n->is_SafePointScalarObject() && !n->is_Catch() && // Would be nice to print exception table targets !n->is_MergeMem() && // Not very interesting !n->is_top() && // Debug info table constants @@ -2227,6 +2314,30 @@ break; } + case Op_Proj: { + if (OptimizeStringConcat) { + ProjNode* p = n->as_Proj(); + if (p->_is_io_use) { + // Separate projections were used for the exception path which + // are normally removed by a late inline. If it wasn't inlined + // then they will hang around and should just be replaced with + // the original one. + Node* proj = NULL; + // Replace with just one + for (SimpleDUIterator i(p->in(0)); i.has_next(); i.next()) { + Node *use = i.get(); + if (use->is_Proj() && p != use && use->as_Proj()->_con == p->_con) { + proj = use; + break; + } + } + assert(p != NULL, "must be found"); + p->subsume_by(proj); + } + } + break; + } + case Op_Phi: if (n->as_Phi()->bottom_type()->isa_narrowoop()) { // The EncodeP optimization may create Phi with the same edges @@ -2520,7 +2631,7 @@ // If original bytecodes contained a mixture of floats and doubles // check if the optimizer has made it homogenous, item (3). - if( Use24BitFPMode && Use24BitFP && + if( Use24BitFPMode && Use24BitFP && UseSSE == 0 && frc.get_float_count() > 32 && frc.get_double_count() == 0 && (10 * frc.get_call_count() < frc.get_float_count()) ) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/compile.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -38,6 +38,7 @@ class OptoReg; class PhaseCFG; class PhaseGVN; +class PhaseIterGVN; class PhaseRegAlloc; class PhaseCCP; class PhaseCCP_DCE; @@ -149,6 +150,7 @@ bool _has_loops; // True if the method _may_ have some loops bool _has_split_ifs; // True if the method _may_ have some split-if bool _has_unsafe_access; // True if the method _may_ produce faults in unsafe loads or stores. + bool _has_stringbuilder; // True StringBuffers or StringBuilders are allocated uint _trap_hist[trapHistLength]; // Cumulative traps bool _trap_can_recompile; // Have we emitted a recompiling trap? uint _decompile_count; // Cumulative decompilation counts. @@ -164,6 +166,9 @@ bool _parsed_irreducible_loop; // True if ciTypeFlow detected irreducible loops during parsing #endif + // JSR 292 + bool _has_method_handle_invokes; // True if this method has MethodHandle invokes. + // Compilation environment. Arena _comp_arena; // Arena with lifetime equivalent to Compile ciEnv* _env; // CI interface @@ -171,6 +176,7 @@ const char* _failure_reason; // for record_failure/failing pattern GrowableArray* _intrinsics; // List of intrinsics. GrowableArray* _macro_nodes; // List of nodes which need to be expanded before matching. + GrowableArray* _predicate_opaqs; // List of Opaque1 nodes for the loop predicates. ConnectionGraph* _congraph; #ifndef PRODUCT IdealGraphPrinter* _printer; @@ -219,6 +225,9 @@ Unique_Node_List* _for_igvn; // Initial work-list for next round of Iterative GVN WarmCallInfo* _warm_calls; // Sorted work-list for heat-based inlining. + GrowableArray _late_inlines; // List of CallGenerators to be revisited after + // main parsing has finished. + // Matching, CFG layout, allocation, code generation PhaseCFG* _cfg; // Results of CFG finding bool _select_24_bit_instr; // We selected an instruction with a 24-bit result @@ -298,6 +307,8 @@ void set_has_split_ifs(bool z) { _has_split_ifs = z; } bool has_unsafe_access() const { return _has_unsafe_access; } void set_has_unsafe_access(bool z) { _has_unsafe_access = z; } + bool has_stringbuilder() const { return _has_stringbuilder; } + void set_has_stringbuilder(bool z) { _has_stringbuilder = z; } void set_trap_count(uint r, uint c) { assert(r < trapHistLength, "oob"); _trap_hist[r] = c; } uint trap_count(uint r) const { assert(r < trapHistLength, "oob"); return _trap_hist[r]; } bool trap_can_recompile() const { return _trap_can_recompile; } @@ -328,6 +339,10 @@ void set_parsed_irreducible_loop(bool z) { _parsed_irreducible_loop = z; } #endif + // JSR 292 + bool has_method_handle_invokes() const { return _has_method_handle_invokes; } + void set_has_method_handle_invokes(bool z) { _has_method_handle_invokes = z; } + void begin_method() { #ifndef PRODUCT if (_printer) _printer->begin_method(this); @@ -345,7 +360,9 @@ } int macro_count() { return _macro_nodes->length(); } + int predicate_count() { return _predicate_opaqs->length();} Node* macro_node(int idx) { return _macro_nodes->at(idx); } + Node* predicate_opaque1_node(int idx) { return _predicate_opaqs->at(idx);} ConnectionGraph* congraph() { return _congraph;} void add_macro_node(Node * n) { //assert(n->is_macro(), "must be a macro node"); @@ -357,7 +374,19 @@ // that the node is in the array before attempting to remove it if (_macro_nodes->contains(n)) _macro_nodes->remove(n); + // remove from _predicate_opaqs list also if it is there + if (predicate_count() > 0 && _predicate_opaqs->contains(n)){ + _predicate_opaqs->remove(n); + } } + void add_predicate_opaq(Node * n) { + assert(!_predicate_opaqs->contains(n), " duplicate entry in predicate opaque1"); + assert(_macro_nodes->contains(n), "should have already been in macro list"); + _predicate_opaqs->append(n); + } + // remove the opaque nodes that protect the predicates so that the unused checks and + // uncommon traps will be eliminated from the graph. + void cleanup_loop_predicates(PhaseIterGVN &igvn); // Compilation environment. Arena* comp_arena() { return &_comp_arena; } @@ -475,6 +504,7 @@ // Decide how to build a call. // The profile factor is a discount to apply to this site's interp. profile. CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float profile_factor); + bool should_delay_inlining(ciMethod* call_method, JVMState* jvms); // Report if there were too many traps at a current method and bci. // Report if a trap was recorded, and/or PerMethodTrapLimit was exceeded. @@ -495,6 +525,11 @@ void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; } void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; } + // Replace n by nn using initial_gvn, calling hash_delete and + // record_for_igvn as needed. + void gvn_replace_by(Node* n, Node* nn); + + void identify_useful_nodes(Unique_Node_List &useful); void remove_useless_nodes (Unique_Node_List &useful); @@ -502,6 +537,9 @@ void set_warm_calls(WarmCallInfo* l) { _warm_calls = l; } WarmCallInfo* pop_warm_call(); + // Record this CallGenerator for inlining at the end of parsing. + void add_late_inline(CallGenerator* cg) { _late_inlines.push(cg); } + // Matching, CFG layout, allocation, code generation PhaseCFG* cfg() { return _cfg; } bool select_24_bit_instr() const { return _select_24_bit_instr; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/connode.cpp --- a/src/share/vm/opto/connode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/connode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1085,6 +1085,9 @@ switch (op) { case Op_SubX: x = in(1)->in(1); + // Avoid ideal transformations ping-pong between this and AddP for raw pointers. + if (phase->find_intptr_t_con(x, -1) == 0) + break; y = in(1)->in(2); if (fits_in_int(phase->type(y), true)) { return addP_of_X2P(phase, x, y, true); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/divnode.cpp --- a/src/share/vm/opto/divnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/divnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -114,7 +114,8 @@ if( andconi_t && andconi_t->is_con() ) { jint andconi = andconi_t->get_con(); if( andconi < 0 && is_power_of_2(-andconi) && (-andconi) >= d ) { - dividend = dividend->in(1); + if( (-andconi) == d ) // Remove AND if it clears bits which will be shifted + dividend = dividend->in(1); needs_rounding = false; } } @@ -356,7 +357,8 @@ if( andconl_t && andconl_t->is_con() ) { jlong andconl = andconl_t->get_con(); if( andconl < 0 && is_power_of_2_long(-andconl) && (-andconl) >= d ) { - dividend = dividend->in(1); + if( (-andconl) == d ) // Remove AND if it clears bits which will be shifted + dividend = dividend->in(1); needs_rounding = false; } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/doCall.cpp --- a/src/share/vm/opto/doCall.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/doCall.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -43,7 +43,9 @@ } #endif -CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float prof_factor) { +CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, + JVMState* jvms, bool allow_inline, + float prof_factor) { CallGenerator* cg; // Dtrace currently doesn't work unless all calls are vanilla @@ -68,7 +70,7 @@ CompileLog* log = this->log(); if (log != NULL) { int rid = (receiver_count >= 0)? log->identify(profile.receiver(0)): -1; - int r2id = (profile.morphism() == 2)? log->identify(profile.receiver(1)):-1; + int r2id = (rid != -1 && profile.has_receiver(1))? log->identify(profile.receiver(1)):-1; log->begin_elem("call method='%d' count='%d' prof_factor='%g'", log->identify(call_method), site_count, prof_factor); if (call_is_virtual) log->print(" virtual='1'"); @@ -116,7 +118,7 @@ // 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); + ilt = new InlineTree(this, jvms->method(), jvms->caller(), site_invoke_ratio, 0); } WarmCallInfo scratch_ci; if (!UseOldInlining) @@ -128,6 +130,12 @@ if (allow_inline) { CallGenerator* cg = CallGenerator::for_inline(call_method, expected_uses); + if (require_inline && cg != NULL && should_delay_inlining(call_method, jvms)) { + // Delay the inlining of this method to give us the + // opportunity to perform some high level optimizations + // first. + return CallGenerator::for_late_inline(call_method, cg); + } if (cg == NULL) { // Fall through. } else if (require_inline || !InlineWarmCalls) { @@ -174,26 +182,16 @@ } } CallGenerator* miss_cg; + Deoptimization::DeoptReason reason = (profile.morphism() == 2) ? + Deoptimization::Reason_bimorphic : + Deoptimization::Reason_class_check; if (( profile.morphism() == 1 || (profile.morphism() == 2 && next_hit_cg != NULL) ) && - - !too_many_traps(Deoptimization::Reason_class_check) - - // Check only total number of traps per method to allow - // the transition from monomorphic to bimorphic case between - // compilations without falling into virtual call. - // A monomorphic case may have the class_check trap flag is set - // due to the time gap between the uncommon trap processing - // when flags are set in MDO and the call site bytecode execution - // in Interpreter when MDO counters are updated. - // There was also class_check trap in monomorphic case due to - // the bug 6225440. - + !too_many_traps(jvms->method(), jvms->bci(), reason) ) { // Generate uncommon trap for class check failure path // in case of monomorphic or bimorphic virtual call site. - miss_cg = CallGenerator::for_uncommon_trap(call_method, - Deoptimization::Reason_class_check, + miss_cg = CallGenerator::for_uncommon_trap(call_method, reason, Deoptimization::Action_maybe_recompile); } else { // Generate virtual call for class check failure path @@ -218,6 +216,57 @@ } } + // Do MethodHandle calls. + if (call_method->is_method_handle_invoke()) { + if (jvms->method()->java_code_at_bci(jvms->bci()) != Bytecodes::_invokedynamic) { + GraphKit kit(jvms); + Node* n = kit.argument(0); + + if (n->Opcode() == Op_ConP) { + const TypeOopPtr* oop_ptr = n->bottom_type()->is_oopptr(); + ciObject* const_oop = oop_ptr->const_oop(); + ciMethodHandle* method_handle = const_oop->as_method_handle(); + + // Set the actually called method to have access to the class + // and signature in the MethodHandleCompiler. + method_handle->set_callee(call_method); + + // Get an adapter for the MethodHandle. + ciMethod* target_method = method_handle->get_method_handle_adapter(); + + CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor); + if (hit_cg != NULL && hit_cg->is_inline()) + return hit_cg; + } + + return CallGenerator::for_direct_call(call_method); + } + else { + // Get the MethodHandle from the CallSite. + ciMethod* caller_method = jvms->method(); + ciBytecodeStream str(caller_method); + str.force_bci(jvms->bci()); // Set the stream to the invokedynamic bci. + ciCallSite* call_site = str.get_call_site(); + ciMethodHandle* method_handle = call_site->get_target(); + + // Set the actually called method to have access to the class + // and signature in the MethodHandleCompiler. + method_handle->set_callee(call_method); + + // Get an adapter for the MethodHandle. + ciMethod* target_method = method_handle->get_invokedynamic_adapter(); + + CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor); + if (hit_cg != NULL && hit_cg->is_inline()) { + CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method); + return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor); + } + + // If something failed, generate a normal dynamic call. + return CallGenerator::for_dynamic_call(call_method); + } + } + // There was no special inlining tactic, or it bailed out. // Use a more generic tactic, like a simple call. if (call_is_virtual) { @@ -225,10 +274,63 @@ } else { // Class Hierarchy Analysis or Type Profile reveals a unique target, // or it is a static or special call. - return CallGenerator::for_direct_call(call_method); + return CallGenerator::for_direct_call(call_method, should_delay_inlining(call_method, jvms)); } } +// Return true for methods that shouldn't be inlined early so that +// they are easier to analyze and optimize as intrinsics. +bool Compile::should_delay_inlining(ciMethod* call_method, JVMState* jvms) { + if (has_stringbuilder()) { + + if ((call_method->holder() == C->env()->StringBuilder_klass() || + call_method->holder() == C->env()->StringBuffer_klass()) && + (jvms->method()->holder() == C->env()->StringBuilder_klass() || + jvms->method()->holder() == C->env()->StringBuffer_klass())) { + // Delay SB calls only when called from non-SB code + return false; + } + + switch (call_method->intrinsic_id()) { + case vmIntrinsics::_StringBuilder_void: + case vmIntrinsics::_StringBuilder_int: + case vmIntrinsics::_StringBuilder_String: + case vmIntrinsics::_StringBuilder_append_char: + case vmIntrinsics::_StringBuilder_append_int: + case vmIntrinsics::_StringBuilder_append_String: + case vmIntrinsics::_StringBuilder_toString: + case vmIntrinsics::_StringBuffer_void: + case vmIntrinsics::_StringBuffer_int: + case vmIntrinsics::_StringBuffer_String: + case vmIntrinsics::_StringBuffer_append_char: + case vmIntrinsics::_StringBuffer_append_int: + case vmIntrinsics::_StringBuffer_append_String: + case vmIntrinsics::_StringBuffer_toString: + case vmIntrinsics::_Integer_toString: + return true; + + case vmIntrinsics::_String_String: + { + Node* receiver = jvms->map()->in(jvms->argoff() + 1); + if (receiver->is_Proj() && receiver->in(0)->is_CallStaticJava()) { + CallStaticJavaNode* csj = receiver->in(0)->as_CallStaticJava(); + ciMethod* m = csj->method(); + if (m != NULL && + (m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString || + m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString)) + // Delay String.(new SB()) + return true; + } + return false; + } + + default: + return false; + } + } + return false; +} + // uncommon-trap call-sites where callee is unloaded, uninitialized or will not link bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* klass) { @@ -240,7 +342,7 @@ // Interface classes can be loaded & linked and never get around to // being initialized. Uncommon-trap for not-initialized static or // v-calls. Let interface calls happen. - ciInstanceKlass* holder_klass = dest_method->holder(); + ciInstanceKlass* holder_klass = dest_method->holder(); if (!holder_klass->is_initialized() && !holder_klass->is_interface()) { uncommon_trap(Deoptimization::Reason_uninitialized, @@ -248,14 +350,6 @@ holder_klass); return true; } - if (dest_method->is_method_handle_invoke() - && holder_klass->name() == ciSymbol::java_dyn_Dynamic()) { - // FIXME: NYI - uncommon_trap(Deoptimization::Reason_unhandled, - Deoptimization::Action_none, - holder_klass); - return true; - } assert(dest_method->will_link(method()->holder(), klass, bc()), "dest_method: typeflow responsibility"); return false; @@ -274,6 +368,7 @@ bool is_virtual = bc() == Bytecodes::_invokevirtual; bool is_virtual_or_interface = is_virtual || bc() == Bytecodes::_invokeinterface; bool has_receiver = is_virtual_or_interface || bc() == Bytecodes::_invokespecial; + bool is_invokedynamic = bc() == Bytecodes::_invokedynamic; // Find target being called bool will_link; @@ -282,7 +377,8 @@ ciKlass* holder = iter().get_declared_method_holder(); ciInstanceKlass* klass = ciEnv::get_instance_klass_for_declared_method_holder(holder); - int nargs = dest_method->arg_size(); + int nargs = dest_method->arg_size(); + if (is_invokedynamic) nargs -= 1; // uncommon-trap when callee is unloaded, uninitialized or will not link // bailout when too many arguments for register representation @@ -296,7 +392,7 @@ return; } assert(holder_klass->is_loaded(), ""); - assert(dest_method->is_static() == !has_receiver, "must match bc"); + assert((dest_method->is_static() || is_invokedynamic) == !has_receiver , "must match bc"); // Note: this takes into account invokeinterface of methods declared in java/lang/Object, // which should be invokevirtuals but according to the VM spec may be invokeinterfaces assert(holder_klass->is_interface() || holder_klass->super() == NULL || (bc() != Bytecodes::_invokeinterface), "must match bc"); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/domgraph.cpp --- a/src/share/vm/opto/domgraph.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/domgraph.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -396,7 +396,7 @@ // nodes (using the is_CFG() call) and places them in a dominator tree. Thus, // it needs a count of the CFG nodes for the mapping table. This is the // Lengauer & Tarjan O(E-alpha(E,V)) algorithm. -void PhaseIdealLoop::Dominators( ) { +void PhaseIdealLoop::Dominators() { ResourceMark rm; // Setup mappings from my Graph to Tarjan's stuff and back // Note: Tarjan uses 1-based arrays @@ -454,7 +454,7 @@ // flow into the main graph (and hence into ROOT) but are not reachable // from above. Such code is dead, but requires a global pass to detect // it; this global pass was the 'build_loop_tree' pass run just prior. - if( whead->is_Region() ) { + if( !_verify_only && whead->is_Region() ) { for( uint i = 1; i < whead->req(); i++ ) { if (!has_node(whead->in(i))) { // Kill dead input path diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/escape.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -439,6 +439,11 @@ Node *base = addp->in(AddPNode::Base)->uncast(); if (base->is_top()) { // The AddP case #3 and #6. base = addp->in(AddPNode::Address)->uncast(); + while (base->is_AddP()) { + // Case #6 (unsafe access) may have several chained AddP nodes. + assert(base->in(AddPNode::Base)->is_top(), "expected unsafe access address only"); + base = base->in(AddPNode::Address)->uncast(); + } assert(base->Opcode() == Op_ConP || base->Opcode() == Op_ThreadLocal || base->Opcode() == Op_CastX2P || base->is_DecodeN() || (base->is_Mem() && base->bottom_type() == TypeRawPtr::NOTNULL) || @@ -519,21 +524,26 @@ // inlining) which was not eliminated during parsing since the exactness // of the allocation type was not propagated to the subclass type check. // + // Or the type 't' could be not related to 'base_t' at all. + // It could happened when CHA type is different from MDO type on a dead path + // (for example, from instanceof check) which is not collapsed during parsing. + // // Do nothing for such AddP node and don't process its users since // this code branch will go away. // if (!t->is_known_instance() && - !t->klass()->equals(base_t->klass()) && - t->klass()->is_subtype_of(base_t->klass())) { + !base_t->klass()->is_subtype_of(t->klass())) { return false; // bail out } const TypeOopPtr *tinst = base_t->add_offset(t->offset())->is_oopptr(); - // Do NOT remove the next call: ensure an new alias index is allocated - // for the instance type + // Do NOT remove the next line: ensure a new alias index is allocated + // for the instance type. Note: C++ will not remove it since the call + // has side effect. int alias_idx = _compile->get_alias_index(tinst); igvn->set_type(addp, tinst); // record the allocation in the node map + assert(ptnode_adr(addp->_idx)->_node != NULL, "should be registered"); set_map(addp->_idx, get_map(base->_idx)); // Set addp's Base and Address to 'base'. @@ -609,9 +619,14 @@ const TypePtr *atype = C->get_adr_type(alias_idx); result = PhiNode::make(orig_phi->in(0), NULL, Type::MEMORY, atype); C->copy_node_notes_to(result, orig_phi); - set_map_phi(orig_phi->_idx, result); igvn->set_type(result, result->bottom_type()); record_for_optimizer(result); + + debug_only(Node* pn = ptnode_adr(orig_phi->_idx)->_node;) + assert(pn == NULL || pn == orig_phi, "wrong node"); + set_map(orig_phi->_idx, result); + ptnode_adr(orig_phi->_idx)->_node = orig_phi; + new_created = true; return result; } @@ -702,6 +717,81 @@ } // +// Move memory users to their memory slices. +// +void ConnectionGraph::move_inst_mem(Node* n, GrowableArray &orig_phis, PhaseGVN *igvn) { + Compile* C = _compile; + + const TypePtr* tp = igvn->type(n->in(MemNode::Address))->isa_ptr(); + assert(tp != NULL, "ptr type"); + int alias_idx = C->get_alias_index(tp); + int general_idx = C->get_general_index(alias_idx); + + // Move users first + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); + if (use->is_MergeMem()) { + MergeMemNode* mmem = use->as_MergeMem(); + assert(n == mmem->memory_at(alias_idx), "should be on instance memory slice"); + if (n != mmem->memory_at(general_idx) || alias_idx == general_idx) { + continue; // Nothing to do + } + // Replace previous general reference to mem node. + uint orig_uniq = C->unique(); + Node* m = find_inst_mem(n, general_idx, orig_phis, igvn); + assert(orig_uniq == C->unique(), "no new nodes"); + mmem->set_memory_at(general_idx, m); + --imax; + --i; + } else if (use->is_MemBar()) { + assert(!use->is_Initialize(), "initializing stores should not be moved"); + if (use->req() > MemBarNode::Precedent && + use->in(MemBarNode::Precedent) == n) { + // Don't move related membars. + record_for_optimizer(use); + continue; + } + tp = use->as_MemBar()->adr_type()->isa_ptr(); + if (tp != NULL && C->get_alias_index(tp) == alias_idx || + alias_idx == general_idx) { + continue; // Nothing to do + } + // Move to general memory slice. + uint orig_uniq = C->unique(); + Node* m = find_inst_mem(n, general_idx, orig_phis, igvn); + assert(orig_uniq == C->unique(), "no new nodes"); + igvn->hash_delete(use); + imax -= use->replace_edge(n, m); + igvn->hash_insert(use); + record_for_optimizer(use); + --i; +#ifdef ASSERT + } else if (use->is_Mem()) { + if (use->Opcode() == Op_StoreCM && use->in(MemNode::OopStore) == n) { + // Don't move related cardmark. + continue; + } + // Memory nodes should have new memory input. + tp = igvn->type(use->in(MemNode::Address))->isa_ptr(); + assert(tp != NULL, "ptr type"); + int idx = C->get_alias_index(tp); + assert(get_map(use->_idx) != NULL || idx == alias_idx, + "Following memory nodes should have new memory input or be on the same memory slice"); + } else if (use->is_Phi()) { + // Phi nodes should be split and moved already. + tp = use->as_Phi()->adr_type()->isa_ptr(); + assert(tp != NULL, "ptr type"); + int idx = C->get_alias_index(tp); + assert(idx == alias_idx, "Following Phi nodes should be on the same memory slice"); + } else { + use->dump(); + assert(false, "should not be here"); +#endif + } + } +} + +// // Search memory chain of "mem" to find a MemNode whose address // is the specified alias index. // @@ -766,10 +856,18 @@ C->get_alias_index(result->as_Phi()->adr_type()) != alias_idx) { Node *un = result->as_Phi()->unique_input(phase); if (un != NULL) { + orig_phis.append_if_missing(result->as_Phi()); result = un; } else { break; } + } else if (result->is_ClearArray()) { + if (!ClearArrayNode::step_through(&result, (uint)tinst->instance_id(), phase)) { + // Can not bypass initialization of the instance + // we are looking for. + break; + } + // Otherwise skip it (the call updated 'result' value). } else if (result->Opcode() == Op_SCMemProj) { assert(result->in(0)->is_LoadStore(), "sanity"); const Type *at = phase->type(result->in(0)->in(MemNode::Address)); @@ -799,7 +897,6 @@ return result; } - // // Convert the types of unescaped object to instance types where possible, // propagate the new type information through the graph, and update memory @@ -891,12 +988,13 @@ // void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) { GrowableArray memnode_worklist; - GrowableArray mergemem_worklist; GrowableArray orig_phis; + PhaseGVN *igvn = _compile->initial_gvn(); uint new_index_start = (uint) _compile->num_alias_types(); - VectorSet visited(Thread::current()->resource_area()); - VectorSet ptset(Thread::current()->resource_area()); + Arena* arena = Thread::current()->resource_area(); + VectorSet visited(arena); + VectorSet ptset(arena); // Phase 1: Process possible allocations from alloc_worklist. @@ -972,6 +1070,8 @@ // - non-escaping // - eligible to be a unique type // - not determined to be ineligible by escape analysis + assert(ptnode_adr(alloc->_idx)->_node != NULL && + ptnode_adr(n->_idx)->_node != NULL, "should be registered"); set_map(alloc->_idx, n); set_map(n->_idx, alloc); const TypeOopPtr *t = igvn->type(n)->isa_oopptr(); @@ -1016,7 +1116,7 @@ alloc_worklist.append_if_missing(addp2); } alloc_worklist.append_if_missing(use); - } else if (use->is_Initialize()) { + } else if (use->is_MemBar()) { memnode_worklist.append_if_missing(use); } } @@ -1026,10 +1126,12 @@ PointsTo(ptset, get_addp_base(n), igvn); assert(ptset.Size() == 1, "AddP address is unique"); uint elem = ptset.getelem(); // Allocation node's index - if (elem == _phantom_object) + if (elem == _phantom_object) { + assert(false, "escaped allocation"); continue; // Assume the value was set outside this method. + } Node *base = get_map(elem); // CheckCastPP node - if (!split_AddP(n, base, igvn)) continue; // wrong type + if (!split_AddP(n, base, igvn)) continue; // wrong type from dead path tinst = igvn->type(base)->isa_oopptr(); } else if (n->is_Phi() || n->is_CheckCastPP() || @@ -1044,8 +1146,10 @@ PointsTo(ptset, n, igvn); if (ptset.Size() == 1) { uint elem = ptset.getelem(); // Allocation node's index - if (elem == _phantom_object) + if (elem == _phantom_object) { + assert(false, "escaped allocation"); continue; // Assume the value was set outside this method. + } Node *val = get_map(elem); // CheckCastPP node TypeNode *tn = n->as_Type(); tinst = igvn->type(val)->isa_oopptr(); @@ -1060,8 +1164,7 @@ tn_t = tn_type->isa_oopptr(); } - if (tn_t != NULL && - tinst->cast_to_instance_id(TypeOopPtr::InstanceBot)->higher_equal(tn_t)) { + if (tn_t != NULL && tinst->klass()->is_subtype_of(tn_t->klass())) { if (tn_type->isa_narrowoop()) { tn_type = tinst->make_narrowoop(); } else { @@ -1073,33 +1176,25 @@ igvn->hash_insert(tn); record_for_optimizer(n); } else { - continue; // wrong type + assert(tn_type == TypePtr::NULL_PTR || + tn_t != NULL && !tinst->klass()->is_subtype_of(tn_t->klass()), + "unexpected type"); + continue; // Skip dead path with different type } } } else { + debug_only(n->dump();) + assert(false, "EA: unexpected node"); continue; } - // push users on appropriate worklist + // push allocation's users on appropriate worklist for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node *use = n->fast_out(i); if(use->is_Mem() && use->in(MemNode::Address) == n) { - memnode_worklist.append_if_missing(use); - } else if (use->is_Initialize()) { + // Load/store to instance's field memnode_worklist.append_if_missing(use); - } else if (use->is_MergeMem()) { - mergemem_worklist.append_if_missing(use); - } else if (use->is_SafePoint() && tinst != NULL) { - // Look for MergeMem nodes for calls which reference unique allocation - // (through CheckCastPP nodes) even for debug info. - Node* m = use->in(TypeFunc::Memory); - uint iid = tinst->instance_id(); - while (m->is_Proj() && m->in(0)->is_SafePoint() && - m->in(0) != use && !m->in(0)->_idx != iid) { - m = m->in(0)->in(TypeFunc::Memory); - } - if (m->is_MergeMem()) { - mergemem_worklist.append_if_missing(m); - } + } else if (use->is_MemBar()) { + memnode_worklist.append_if_missing(use); } else if (use->is_AddP() && use->outcnt() > 0) { // No dead nodes Node* addp2 = find_second_addp(use, n); if (addp2 != NULL) { @@ -1112,6 +1207,29 @@ use->is_DecodeN() || (use->is_ConstraintCast() && use->Opcode() == Op_CastPP)) { alloc_worklist.append_if_missing(use); +#ifdef ASSERT + } else if (use->is_Mem()) { + assert(use->in(MemNode::Address) != n, "EA: missing allocation reference path"); + } else if (use->is_MergeMem()) { + assert(_mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist"); + } else if (use->is_SafePoint()) { + // Look for MergeMem nodes for calls which reference unique allocation + // (through CheckCastPP nodes) even for debug info. + Node* m = use->in(TypeFunc::Memory); + if (m->is_MergeMem()) { + assert(_mergemem_worklist.contains(m->as_MergeMem()), "EA: missing MergeMem node in the worklist"); + } + } else { + uint op = use->Opcode(); + if (!(op == Op_CmpP || op == Op_Conv2B || + op == Op_CastP2X || op == Op_StoreCM || + op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || + op == Op_StrEquals || op == Op_StrIndexOf)) { + n->dump(); + use->dump(); + assert(false, "EA: missing allocation reference path"); + } +#endif } } @@ -1129,19 +1247,16 @@ Node *n = memnode_worklist.pop(); if (visited.test_set(n->_idx)) continue; - if (n->is_Phi()) { - assert(n->as_Phi()->adr_type() != TypePtr::BOTTOM, "narrow memory slice required"); - // we don't need to do anything, but the users must be pushed if we haven't processed - // this Phi before - } else if (n->is_Initialize()) { - // we don't need to do anything, but the users of the memory projection must be pushed - n = n->as_Initialize()->proj_out(TypeFunc::Memory); + if (n->is_Phi() || n->is_ClearArray()) { + // we don't need to do anything, but the users must be pushed + } else if (n->is_MemBar()) { // Initialize, MemBar nodes + // we don't need to do anything, but the users must be pushed + n = n->as_MemBar()->proj_out(TypeFunc::Memory); if (n == NULL) continue; } else { assert(n->is_Mem(), "memory node required."); Node *addr = n->in(MemNode::Address); - assert(addr->is_AddP(), "AddP required"); const Type *addr_t = igvn->type(addr); if (addr_t == Type::TOP) continue; @@ -1153,6 +1268,10 @@ return; } if (mem != n->in(MemNode::Memory)) { + // We delay the memory edge update since we need old one in + // MergeMem code below when instances memory slices are separated. + debug_only(Node* pn = ptnode_adr(n->_idx)->_node;) + assert(pn == NULL || pn == n, "wrong node"); set_map(n->_idx, mem); ptnode_adr(n->_idx)->_node = n; } @@ -1173,36 +1292,55 @@ // push user on appropriate worklist for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node *use = n->fast_out(i); - if (use->is_Phi()) { + if (use->is_Phi() || use->is_ClearArray()) { memnode_worklist.append_if_missing(use); } else if(use->is_Mem() && use->in(MemNode::Memory) == n) { + if (use->Opcode() == Op_StoreCM) // Ignore cardmark stores + continue; memnode_worklist.append_if_missing(use); - } else if (use->is_Initialize()) { + } else if (use->is_MemBar()) { memnode_worklist.append_if_missing(use); +#ifdef ASSERT + } else if(use->is_Mem()) { + assert(use->in(MemNode::Memory) != n, "EA: missing memory path"); } else if (use->is_MergeMem()) { - mergemem_worklist.append_if_missing(use); + assert(_mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist"); + } else { + uint op = use->Opcode(); + if (!(op == Op_StoreCM || + (op == Op_CallLeaf && use->as_CallLeaf()->_name != NULL && + strcmp(use->as_CallLeaf()->_name, "g1_wb_pre") == 0) || + op == Op_AryEq || op == Op_StrComp || + op == Op_StrEquals || op == Op_StrIndexOf)) { + n->dump(); + use->dump(); + assert(false, "EA: missing memory path"); + } +#endif } } } // Phase 3: Process MergeMem nodes from mergemem_worklist. - // Walk each memory moving the first node encountered of each + // Walk each memory slice moving the first node encountered of each // instance type to the the input corresponding to its alias index. - while (mergemem_worklist.length() != 0) { - Node *n = mergemem_worklist.pop(); - assert(n->is_MergeMem(), "MergeMem node required."); - if (visited.test_set(n->_idx)) - continue; - MergeMemNode *nmm = n->as_MergeMem(); + uint length = _mergemem_worklist.length(); + for( uint next = 0; next < length; ++next ) { + MergeMemNode* nmm = _mergemem_worklist.at(next); + assert(!visited.test_set(nmm->_idx), "should not be visited before"); // Note: we don't want to use MergeMemStream here because we only want to - // scan inputs which exist at the start, not ones we add during processing. + // scan inputs which exist at the start, not ones we add during processing. + // Note 2: MergeMem may already contains instance memory slices added + // during find_inst_mem() call when memory nodes were processed above. + igvn->hash_delete(nmm); uint nslices = nmm->req(); - igvn->hash_delete(nmm); for (uint i = Compile::AliasIdxRaw+1; i < nslices; i++) { Node* mem = nmm->in(i); Node* cur = NULL; if (mem == NULL || mem->is_top()) continue; + // First, update mergemem by moving memory nodes to corresponding slices + // if their type became more precise since this mergemem was created. while (mem->is_Mem()) { const Type *at = igvn->type(mem->in(MemNode::Address)); if (at != Type::TOP) { @@ -1221,7 +1359,7 @@ } nmm->set_memory_at(i, (cur != NULL) ? cur : mem); // Find any instance of the current type if we haven't encountered - // a value of the instance along the chain. + // already a memory slice of the instance along the memory chain. for (uint ni = new_index_start; ni < new_index_end; ni++) { if((uint)_compile->get_general_index(ni) == i) { Node *m = (ni >= nmm->req()) ? nmm->empty_memory() : nmm->in(ni); @@ -1237,11 +1375,11 @@ } // Find the rest of instances values for (uint ni = new_index_start; ni < new_index_end; ni++) { - const TypeOopPtr *tinst = igvn->C->get_adr_type(ni)->isa_oopptr(); + const TypeOopPtr *tinst = _compile->get_adr_type(ni)->isa_oopptr(); Node* result = step_through_mergemem(nmm, ni, tinst); if (result == nmm->base_memory()) { // Didn't find instance memory, search through general slice recursively. - result = nmm->memory_at(igvn->C->get_general_index(ni)); + result = nmm->memory_at(_compile->get_general_index(ni)); result = find_inst_mem(result, ni, orig_phis, igvn); if (_compile->failing()) { return; @@ -1251,41 +1389,6 @@ } igvn->hash_insert(nmm); record_for_optimizer(nmm); - - // Propagate new memory slices to following MergeMem nodes. - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node *use = n->fast_out(i); - if (use->is_Call()) { - CallNode* in = use->as_Call(); - if (in->proj_out(TypeFunc::Memory) != NULL) { - Node* m = in->proj_out(TypeFunc::Memory); - for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { - Node* mm = m->fast_out(j); - if (mm->is_MergeMem()) { - mergemem_worklist.append_if_missing(mm); - } - } - } - if (use->is_Allocate()) { - use = use->as_Allocate()->initialization(); - if (use == NULL) { - continue; - } - } - } - if (use->is_Initialize()) { - InitializeNode* in = use->as_Initialize(); - if (in->proj_out(TypeFunc::Memory) != NULL) { - Node* m = in->proj_out(TypeFunc::Memory); - for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { - Node* mm = m->fast_out(j); - if (mm->is_MergeMem()) { - mergemem_worklist.append_if_missing(mm); - } - } - } - } - } } // Phase 4: Update the inputs of non-instance memory Phis and @@ -1314,19 +1417,48 @@ } // Update the memory inputs of MemNodes with the value we computed - // in Phase 2. + // in Phase 2 and move stores memory users to corresponding memory slices. +#ifdef ASSERT + visited.Clear(); + Node_Stack old_mems(arena, _compile->unique() >> 2); +#endif for (uint i = 0; i < nodes_size(); i++) { Node *nmem = get_map(i); if (nmem != NULL) { Node *n = ptnode_adr(i)->_node; - if (n != NULL && n->is_Mem()) { + assert(n != NULL, "sanity"); + if (n->is_Mem()) { +#ifdef ASSERT + Node* old_mem = n->in(MemNode::Memory); + if (!visited.test_set(old_mem->_idx)) { + old_mems.push(old_mem, old_mem->outcnt()); + } +#endif + assert(n->in(MemNode::Memory) != nmem, "sanity"); + if (!n->is_Load()) { + // Move memory users of a store first. + move_inst_mem(n, orig_phis, igvn); + } + // Now update memory input igvn->hash_delete(n); n->set_req(MemNode::Memory, nmem); igvn->hash_insert(n); record_for_optimizer(n); + } else { + assert(n->is_Allocate() || n->is_CheckCastPP() || + n->is_AddP() || n->is_Phi(), "unknown node used for set_map()"); } } } +#ifdef ASSERT + // Verify that memory was split correctly + while (old_mems.is_nonempty()) { + Node* old_mem = old_mems.node(); + uint old_cnt = old_mems.index(); + old_mems.pop(); + assert(old_cnt = old_mem->outcnt(), "old mem could be lost"); + } +#endif } bool ConnectionGraph::has_candidates(Compile *C) { @@ -1373,8 +1505,20 @@ ptnode_adr(n->_idx)->node_type() == PointsToNode::JavaObject) { has_allocations = true; } - if(n->is_AddP()) - cg_worklist.append(n->_idx); + if(n->is_AddP()) { + // Collect address nodes which directly reference an allocation. + // Use them during stage 3 below to build initial connection graph + // field edges. Other field edges could be added after StoreP/LoadP + // nodes are processed during stage 4 below. + Node* base = get_addp_base(n); + if(base->is_Proj() && base->in(0)->is_Allocate()) { + cg_worklist.append(n->_idx); + } + } else if (n->is_MergeMem()) { + // Collect all MergeMem nodes to add memory slices for + // scalar replaceable objects in split_unique_types(). + _mergemem_worklist.append(n->as_MergeMem()); + } for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { Node* m = n->fast_out(i); // Get user worklist_init.push(m); @@ -1415,12 +1559,13 @@ } } - VectorSet ptset(Thread::current()->resource_area()); + Arena* arena = Thread::current()->resource_area(); + VectorSet ptset(arena); GrowableArray deferred_edges; - VectorSet visited(Thread::current()->resource_area()); + VectorSet visited(arena); - // 5. Remove deferred edges from the graph and collect - // information needed for type splitting. + // 5. Remove deferred edges from the graph and adjust + // escape state of nonescaping objects. cg_length = cg_worklist.length(); for( uint next = 0; next < cg_length; ++next ) { int ni = cg_worklist.at(next); @@ -1430,98 +1575,9 @@ remove_deferred(ni, &deferred_edges, &visited); Node *n = ptn->_node; if (n->is_AddP()) { - // Search for objects which are not scalar replaceable. - // Mark their escape state as ArgEscape to propagate the state - // to referenced objects. - // Note: currently there are no difference in compiler optimizations - // for ArgEscape objects and NoEscape objects which are not - // scalar replaceable. - - int offset = ptn->offset(); - Node *base = get_addp_base(n); - ptset.Clear(); - PointsTo(ptset, base, igvn); - int ptset_size = ptset.Size(); - - // Check if a field's initializing value is recorded and add - // a corresponding NULL field's value if it is not recorded. - // Connection Graph does not record a default initialization by NULL - // captured by Initialize node. - // - // Note: it will disable scalar replacement in some cases: - // - // Point p[] = new Point[1]; - // p[0] = new Point(); // Will be not scalar replaced - // - // but it will save us from incorrect optimizations in next cases: - // - // Point p[] = new Point[1]; - // if ( x ) p[0] = new Point(); // Will be not scalar replaced - // - // Without a control flow analysis we can't distinguish above cases. - // - if (offset != Type::OffsetBot && ptset_size == 1) { - uint elem = ptset.getelem(); // Allocation node's index - // It does not matter if it is not Allocation node since - // only non-escaping allocations are scalar replaced. - if (ptnode_adr(elem)->_node->is_Allocate() && - ptnode_adr(elem)->escape_state() == PointsToNode::NoEscape) { - AllocateNode* alloc = ptnode_adr(elem)->_node->as_Allocate(); - InitializeNode* ini = alloc->initialization(); - Node* value = NULL; - if (ini != NULL) { - BasicType ft = UseCompressedOops ? T_NARROWOOP : T_OBJECT; - Node* store = ini->find_captured_store(offset, type2aelembytes(ft), igvn); - if (store != NULL && store->is_Store()) - value = store->in(MemNode::ValueIn); - } - if (value == NULL || value != ptnode_adr(value->_idx)->_node) { - // A field's initializing value was not recorded. Add NULL. - uint null_idx = UseCompressedOops ? _noop_null : _oop_null; - add_pointsto_edge(ni, null_idx); - } - } - } - - // An object is not scalar replaceable if the field which may point - // to it has unknown offset (unknown element of an array of objects). - // - if (offset == Type::OffsetBot) { - uint e_cnt = ptn->edge_count(); - for (uint ei = 0; ei < e_cnt; ei++) { - uint npi = ptn->edge_target(ei); - set_escape_state(npi, PointsToNode::ArgEscape); - ptnode_adr(npi)->_scalar_replaceable = false; - } - } - - // Currently an object is not scalar replaceable if a LoadStore node - // access its field since the field value is unknown after it. - // - bool has_LoadStore = false; - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node *use = n->fast_out(i); - if (use->is_LoadStore()) { - has_LoadStore = true; - break; - } - } - // An object is not scalar replaceable if the address points - // to unknown field (unknown element for arrays, offset is OffsetBot). - // - // Or the address may point to more then one object. This may produce - // the false positive result (set scalar_replaceable to false) - // since the flow-insensitive escape analysis can't separate - // the case when stores overwrite the field's value from the case - // when stores happened on different control branches. - // - if (ptset_size > 1 || ptset_size != 0 && - (has_LoadStore || offset == Type::OffsetBot)) { - for( VectorSetI j(&ptset); j.test(); ++j ) { - set_escape_state(j.elem, PointsToNode::ArgEscape); - ptnode_adr(j.elem)->_scalar_replaceable = false; - } - } + // Search for objects which are not scalar replaceable + // and adjust their escape state. + verify_escape_state(ni, ptset, igvn); } } } @@ -1638,6 +1694,150 @@ return has_non_escaping_obj; } +// Search for objects which are not scalar replaceable. +void ConnectionGraph::verify_escape_state(int nidx, VectorSet& ptset, PhaseTransform* phase) { + PointsToNode* ptn = ptnode_adr(nidx); + Node* n = ptn->_node; + assert(n->is_AddP(), "Should be called for AddP nodes only"); + // Search for objects which are not scalar replaceable. + // Mark their escape state as ArgEscape to propagate the state + // to referenced objects. + // Note: currently there are no difference in compiler optimizations + // for ArgEscape objects and NoEscape objects which are not + // scalar replaceable. + + Compile* C = _compile; + + int offset = ptn->offset(); + Node* base = get_addp_base(n); + ptset.Clear(); + PointsTo(ptset, base, phase); + int ptset_size = ptset.Size(); + + // Check if a oop field's initializing value is recorded and add + // a corresponding NULL field's value if it is not recorded. + // Connection Graph does not record a default initialization by NULL + // captured by Initialize node. + // + // Note: it will disable scalar replacement in some cases: + // + // Point p[] = new Point[1]; + // p[0] = new Point(); // Will be not scalar replaced + // + // but it will save us from incorrect optimizations in next cases: + // + // Point p[] = new Point[1]; + // if ( x ) p[0] = new Point(); // Will be not scalar replaced + // + // Do a simple control flow analysis to distinguish above cases. + // + if (offset != Type::OffsetBot && ptset_size == 1) { + uint elem = ptset.getelem(); // Allocation node's index + // It does not matter if it is not Allocation node since + // only non-escaping allocations are scalar replaced. + if (ptnode_adr(elem)->_node->is_Allocate() && + ptnode_adr(elem)->escape_state() == PointsToNode::NoEscape) { + AllocateNode* alloc = ptnode_adr(elem)->_node->as_Allocate(); + InitializeNode* ini = alloc->initialization(); + + // Check only oop fields. + const Type* adr_type = n->as_AddP()->bottom_type(); + BasicType basic_field_type = T_INT; + if (adr_type->isa_instptr()) { + ciField* field = C->alias_type(adr_type->isa_instptr())->field(); + if (field != NULL) { + basic_field_type = field->layout_type(); + } else { + // Ignore non field load (for example, klass load) + } + } else if (adr_type->isa_aryptr()) { + const Type* elemtype = adr_type->isa_aryptr()->elem(); + basic_field_type = elemtype->array_element_basic_type(); + } else { + // Raw pointers are used for initializing stores so skip it. + assert(adr_type->isa_rawptr() && base->is_Proj() && + (base->in(0) == alloc),"unexpected pointer type"); + } + if (basic_field_type == T_OBJECT || + basic_field_type == T_NARROWOOP || + basic_field_type == T_ARRAY) { + Node* value = NULL; + if (ini != NULL) { + BasicType ft = UseCompressedOops ? T_NARROWOOP : T_OBJECT; + Node* store = ini->find_captured_store(offset, type2aelembytes(ft), phase); + if (store != NULL && store->is_Store()) { + value = store->in(MemNode::ValueIn); + } else if (ptn->edge_count() > 0) { // Are there oop stores? + // Check for a store which follows allocation without branches. + // For example, a volatile field store is not collected + // by Initialize node. TODO: it would be nice to use idom() here. + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + store = n->fast_out(i); + if (store->is_Store() && store->in(0) != NULL) { + Node* ctrl = store->in(0); + while(!(ctrl == ini || ctrl == alloc || ctrl == NULL || + ctrl == C->root() || ctrl == C->top() || ctrl->is_Region() || + ctrl->is_IfTrue() || ctrl->is_IfFalse())) { + ctrl = ctrl->in(0); + } + if (ctrl == ini || ctrl == alloc) { + value = store->in(MemNode::ValueIn); + break; + } + } + } + } + } + if (value == NULL || value != ptnode_adr(value->_idx)->_node) { + // A field's initializing value was not recorded. Add NULL. + uint null_idx = UseCompressedOops ? _noop_null : _oop_null; + add_pointsto_edge(nidx, null_idx); + } + } + } + } + + // An object is not scalar replaceable if the field which may point + // to it has unknown offset (unknown element of an array of objects). + // + if (offset == Type::OffsetBot) { + uint e_cnt = ptn->edge_count(); + for (uint ei = 0; ei < e_cnt; ei++) { + uint npi = ptn->edge_target(ei); + set_escape_state(npi, PointsToNode::ArgEscape); + ptnode_adr(npi)->_scalar_replaceable = false; + } + } + + // Currently an object is not scalar replaceable if a LoadStore node + // access its field since the field value is unknown after it. + // + bool has_LoadStore = false; + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *use = n->fast_out(i); + if (use->is_LoadStore()) { + has_LoadStore = true; + break; + } + } + // An object is not scalar replaceable if the address points + // to unknown field (unknown element for arrays, offset is OffsetBot). + // + // Or the address may point to more then one object. This may produce + // the false positive result (set scalar_replaceable to false) + // since the flow-insensitive escape analysis can't separate + // the case when stores overwrite the field's value from the case + // when stores happened on different control branches. + // + if (ptset_size > 1 || ptset_size != 0 && + (has_LoadStore || offset == Type::OffsetBot)) { + for( VectorSetI j(&ptset); j.test(); ++j ) { + set_escape_state(j.elem, PointsToNode::ArgEscape); + ptnode_adr(j.elem)->_scalar_replaceable = false; + } + } +} + void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) { switch (call->Opcode()) { @@ -1649,6 +1849,7 @@ assert(false, "should be done already"); break; #endif + case Op_CallLeaf: case Op_CallLeafNoFP: { // Stub calls, objects do not escape but they are not scale replaceable. @@ -1659,9 +1860,23 @@ const Type* at = d->field_at(i); Node *arg = call->in(i)->uncast(); const Type *aat = phase->type(arg); - if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr()) { + if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() && + ptnode_adr(arg->_idx)->escape_state() < PointsToNode::ArgEscape) { + assert(aat == Type::TOP || aat == TypePtr::NULL_PTR || aat->isa_ptr() != NULL, "expecting an Ptr"); +#ifdef ASSERT + if (!(call->Opcode() == Op_CallLeafNoFP && + call->as_CallLeaf()->_name != NULL && + (strstr(call->as_CallLeaf()->_name, "arraycopy") != 0) || + call->as_CallLeaf()->_name != NULL && + (strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 || + strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 )) + ) { + call->dump(); + assert(false, "EA: unexpected CallLeaf"); + } +#endif set_escape_state(arg->_idx, PointsToNode::ArgEscape); if (arg->is_AddP()) { // @@ -1698,9 +1913,10 @@ for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { const Type* at = d->field_at(i); int k = i - TypeFunc::Parms; + Node *arg = call->in(i)->uncast(); - if (at->isa_oopptr() != NULL) { - Node *arg = call->in(i)->uncast(); + if (at->isa_oopptr() != NULL && + ptnode_adr(arg->_idx)->escape_state() < PointsToNode::GlobalEscape) { bool global_escapes = false; bool fields_escapes = false; @@ -1934,20 +2150,23 @@ record_for_optimizer(n); _processed.set(n->_idx); } else { - // Have to process call's arguments first. + // Don't mark as processed since call's arguments have to be processed. PointsToNode::NodeType nt = PointsToNode::UnknownType; + PointsToNode::EscapeState es = PointsToNode::UnknownEscape; // Check if a call returns an object. const TypeTuple *r = n->as_Call()->tf()->range(); - if (n->is_CallStaticJava() && r->cnt() > TypeFunc::Parms && + if (r->cnt() > TypeFunc::Parms && + r->field_at(TypeFunc::Parms)->isa_ptr() && n->as_Call()->proj_out(TypeFunc::Parms) != NULL) { - // Note: use isa_ptr() instead of isa_oopptr() here because - // the _multianewarray functions return a TypeRawPtr. - if (r->field_at(TypeFunc::Parms)->isa_ptr() != NULL) { - nt = PointsToNode::JavaObject; + nt = PointsToNode::JavaObject; + if (!n->is_CallStaticJava()) { + // Since the called mathod is statically unknown assume + // the worst case that the returned value globally escapes. + es = PointsToNode::GlobalEscape; } } - add_node(n, nt, PointsToNode::UnknownEscape, false); + add_node(n, nt, es, false); } return; } @@ -2080,18 +2299,27 @@ } case Op_Proj: { - // we are only interested in the result projection from a call + // we are only interested in the oop result projection from a call if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() ) { - add_node(n, PointsToNode::LocalVar, PointsToNode::UnknownEscape, false); - process_call_result(n->as_Proj(), phase); - if (!_processed.test(n->_idx)) { - // The call's result may need to be processed later if the call - // returns it's argument and the argument is not processed yet. - _delayed_worklist.push(n); + const TypeTuple *r = n->in(0)->as_Call()->tf()->range(); + assert(r->cnt() > TypeFunc::Parms, "sanity"); + if (r->field_at(TypeFunc::Parms)->isa_ptr() != NULL) { + add_node(n, PointsToNode::LocalVar, PointsToNode::UnknownEscape, false); + int ti = n->in(0)->_idx; + // The call may not be registered yet (since not all its inputs are registered) + // if this is the projection from backbranch edge of Phi. + if (ptnode_adr(ti)->node_type() != PointsToNode::UnknownType) { + process_call_result(n->as_Proj(), phase); + } + if (!_processed.test(n->_idx)) { + // The call's result may need to be processed later if the call + // returns it's argument and the argument is not processed yet. + _delayed_worklist.push(n); + } + break; } - } else { - _processed.set(n->_idx); } + _processed.set(n->_idx); break; } case Op_Return: @@ -2152,6 +2380,15 @@ } break; } + case Op_AryEq: + case Op_StrComp: + case Op_StrEquals: + case Op_StrIndexOf: + { + // char[] arrays passed to string intrinsics are not scalar replaceable. + add_node(n, PointsToNode::UnknownType, PointsToNode::UnknownEscape, false); + break; + } case Op_ThreadLocal: { add_node(n, PointsToNode::JavaObject, PointsToNode::ArgEscape, true); @@ -2166,6 +2403,7 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { uint n_idx = n->_idx; + assert(ptnode_adr(n_idx)->_node != NULL, "node should be registered"); // Don't set processed bit for AddP, LoadP, StoreP since // they may need more then one pass to process. @@ -2203,6 +2441,7 @@ case Op_DecodeN: { int ti = n->in(1)->_idx; + assert(ptnode_adr(ti)->node_type() != PointsToNode::UnknownType, "all nodes should be registered"); if (ptnode_adr(ti)->node_type() == PointsToNode::JavaObject) { add_pointsto_edge(n_idx, ti); } else { @@ -2242,7 +2481,6 @@ #endif Node* adr = n->in(MemNode::Address)->uncast(); - const Type *adr_type = phase->type(adr); Node* adr_base; if (adr->is_AddP()) { adr_base = get_addp_base(adr); @@ -2294,13 +2532,19 @@ } case Op_Proj: { - // we are only interested in the result projection from a call + // we are only interested in the oop result projection from a call if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() ) { - process_call_result(n->as_Proj(), phase); - assert(_processed.test(n_idx), "all call results should be processed"); - } else { - assert(false, "Op_Proj"); + assert(ptnode_adr(n->in(0)->_idx)->node_type() != PointsToNode::UnknownType, + "all nodes should be registered"); + const TypeTuple *r = n->in(0)->as_Call()->tf()->range(); + assert(r->cnt() > TypeFunc::Parms, "sanity"); + if (r->field_at(TypeFunc::Parms)->isa_ptr() != NULL) { + process_call_result(n->as_Proj(), phase); + assert(_processed.test(n_idx), "all call results should be processed"); + break; + } } + assert(false, "Op_Proj"); break; } case Op_Return: @@ -2312,6 +2556,7 @@ } #endif int ti = n->in(TypeFunc::Parms)->_idx; + assert(ptnode_adr(ti)->node_type() != PointsToNode::UnknownType, "node should be registered"); if (ptnode_adr(ti)->node_type() == PointsToNode::JavaObject) { add_pointsto_edge(n_idx, ti); } else { @@ -2346,14 +2591,38 @@ } break; } + case Op_AryEq: + case Op_StrComp: + case Op_StrEquals: + case Op_StrIndexOf: + { + // char[] arrays passed to string intrinsic do not escape but + // they are not scalar replaceable. Adjust escape state for them. + // Start from in(2) edge since in(1) is memory edge. + for (uint i = 2; i < n->req(); i++) { + Node* adr = n->in(i)->uncast(); + const Type *at = phase->type(adr); + if (!adr->is_top() && at->isa_ptr()) { + assert(at == Type::TOP || at == TypePtr::NULL_PTR || + at->isa_ptr() != NULL, "expecting an Ptr"); + if (adr->is_AddP()) { + adr = get_addp_base(adr); + } + // Mark as ArgEscape everything "adr" could point to. + set_escape_state(adr->_idx, PointsToNode::ArgEscape); + } + } + _processed.set(n_idx); + break; + } case Op_ThreadLocal: { assert(false, "Op_ThreadLocal"); break; } default: - ; - // nothing to do + // This method should be called only for EA specific nodes. + ShouldNotReachHere(); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/escape.hpp --- a/src/share/vm/opto/escape.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/escape.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -210,6 +210,8 @@ Unique_Node_List _delayed_worklist; // Nodes to be processed before // the call build_connection_graph(). + GrowableArray _mergemem_worklist; // List of all MergeMem nodes + VectorSet _processed; // Records which nodes have been // processed. @@ -289,7 +291,7 @@ bool split_AddP(Node *addp, Node *base, PhaseGVN *igvn); PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn, bool &new_created); PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn); - Node *find_mem(Node *mem, int alias_idx, PhaseGVN *igvn); + void move_inst_mem(Node* n, GrowableArray &orig_phis, PhaseGVN *igvn); Node *find_inst_mem(Node *mem, int alias_idx,GrowableArray &orig_phi_worklist, PhaseGVN *igvn); // Propagate unique types created for unescaped allocated objects @@ -298,7 +300,6 @@ // manage entries in _node_map void set_map(int idx, Node *n) { _node_map.map(idx, n); } - void set_map_phi(int idx, PhiNode *p) { _node_map.map(idx, (Node *) p); } Node *get_map(int idx) { return _node_map[idx]; } PhiNode *get_map_phi(int idx) { Node *phi = _node_map[idx]; @@ -315,6 +316,9 @@ // Set the escape state of a node void set_escape_state(uint ni, PointsToNode::EscapeState es); + // Search for objects which are not scalar replaceable. + void verify_escape_state(int nidx, VectorSet& ptset, PhaseTransform* phase); + public: ConnectionGraph(Compile *C); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/graphKit.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2010 Sun Microsystems, 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 @@ -455,16 +455,44 @@ return Bytecodes::_illegal; } +void GraphKit::uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptReason reason, + bool must_throw) { + // if the exception capability is set, then we will generate code + // to check the JavaThread.should_post_on_exceptions flag to see + // if we actually need to report exception events (for this + // thread). If we don't need to report exception events, we will + // take the normal fast path provided by add_exception_events. If + // exception event reporting is enabled for this thread, we will + // take the uncommon_trap in the BuildCutout below. + + // first must access the should_post_on_exceptions_flag in this thread's JavaThread + Node* jthread = _gvn.transform(new (C, 1) 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); + + // Test the should_post_on_exceptions_flag vs. 0 + Node* chk = _gvn.transform( new (C, 3) CmpINode(should_post_flag, intcon(0)) ); + Node* tst = _gvn.transform( new (C, 2) BoolNode(chk, BoolTest::eq) ); + + // Branch to slow_path if should_post_on_exceptions_flag was true + { BuildCutout unless(this, tst, PROB_MAX); + // Do not try anything fancy if we're notifying the VM on every throw. + // Cf. case Bytecodes::_athrow in parse2.cpp. + uncommon_trap(reason, Deoptimization::Action_none, + (ciKlass*)NULL, (char*)NULL, must_throw); + } + +} + //------------------------------builtin_throw---------------------------------- void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) { bool must_throw = true; - if (env()->jvmti_can_post_exceptions()) { - // Do not try anything fancy if we're notifying the VM on every throw. - // Cf. case Bytecodes::_athrow in parse2.cpp. - uncommon_trap(reason, Deoptimization::Action_none, - (ciKlass*)NULL, (char*)NULL, must_throw); - return; + if (env()->jvmti_can_post_on_exceptions()) { + // check if we must post exception events, take uncommon trap if so + uncommon_trap_if_should_post_on_exceptions(reason, must_throw); + // here if should_post_on_exceptions is false + // continue on with the normal codegen } // If this particular condition has not yet happened at this @@ -622,11 +650,13 @@ //---------------------------PreserveReexecuteState---------------------------- PreserveReexecuteState::PreserveReexecuteState(GraphKit* kit) { + assert(!kit->stopped(), "must call stopped() before"); _kit = kit; _sp = kit->sp(); _reexecute = kit->jvms()->_reexecute; } PreserveReexecuteState::~PreserveReexecuteState() { + if (_kit->stopped()) return; _kit->jvms()->_reexecute = _reexecute; _kit->set_sp(_sp); } @@ -750,12 +780,20 @@ // Helper function for enforcing certain bytecodes to reexecute if // deoptimization happens -static bool should_reexecute_implied_by_bytecode(JVMState *jvms) { +static bool should_reexecute_implied_by_bytecode(JVMState *jvms, bool is_anewarray) { ciMethod* cur_method = jvms->method(); int cur_bci = jvms->bci(); if (cur_method != NULL && cur_bci != InvocationEntryBci) { Bytecodes::Code code = cur_method->java_code_at_bci(cur_bci); - return Interpreter::bytecode_should_reexecute(code); + return Interpreter::bytecode_should_reexecute(code) || + is_anewarray && code == Bytecodes::_multianewarray; + // Reexecute _multianewarray bytecode which was replaced with + // sequence of [a]newarray. See Parse::do_multianewarray(). + // + // Note: interpreter should not have it set since this optimization + // is limited by dimensions and guarded by flag so in some cases + // multianewarray() runtime calls will be generated and + // the bytecode should not be reexecutes (stack will not be reset). } else return false; } @@ -806,7 +844,7 @@ // For a known set of bytecodes, the interpreter should reexecute them if // deoptimization happens. We set the reexecute state for them here if (out_jvms->is_reexecute_undefined() && //don't change if already specified - should_reexecute_implied_by_bytecode(out_jvms)) { + should_reexecute_implied_by_bytecode(out_jvms, call->is_AllocateArray())) { out_jvms->set_should_reexecute(true); //NOTE: youngest_jvms not changed } @@ -979,14 +1017,19 @@ case Bytecodes::_invokedynamic: case Bytecodes::_invokeinterface: { - bool is_static = (depth == 0); bool ignore; ciBytecodeStream iter(method()); iter.reset_to_bci(bci()); iter.next(); ciMethod* method = iter.get_method(ignore); inputs = method->arg_size_no_receiver(); - if (!is_static) inputs += 1; + // Add a receiver argument, maybe: + if (code != Bytecodes::_invokestatic && + code != Bytecodes::_invokedynamic) + inputs += 1; + // (Do not use ciMethod::arg_size(), because + // it might be an unloaded method, which doesn't + // know whether it is static or not.) int size = method->return_type()->size(); depth = size - inputs; } @@ -1086,7 +1129,7 @@ alen = _gvn.transform( new (C, 3) LoadRangeNode(0, immutable_memory(), r_adr, TypeInt::POS)); } else { alen = alloc->Ideal_length(); - Node* ccast = alloc->make_ideal_length(_gvn.type(array)->is_aryptr(), &_gvn); + Node* ccast = alloc->make_ideal_length(_gvn.type(array)->is_oopptr(), &_gvn); if (ccast != alen) { alen = _gvn.transform(ccast); } @@ -1123,8 +1166,8 @@ case T_OBJECT : { const Type *t = _gvn.type( value ); - const TypeInstPtr* tp = t->isa_instptr(); - if (tp != NULL && !tp->klass()->is_loaded() + const TypeOopPtr* tp = t->isa_oopptr(); + if (tp != NULL && tp->klass() != NULL && !tp->klass()->is_loaded() // Only for do_null_check, not any of its siblings: && !assert_null && null_control == NULL) { // Usually, any field access or invocation on an unloaded oop type @@ -1349,8 +1392,8 @@ } //------------------------------set_all_memory_call---------------------------- -void GraphKit::set_all_memory_call(Node* call) { - Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) ); +void GraphKit::set_all_memory_call(Node* call, bool separate_io_proj) { + Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory, separate_io_proj) ); set_all_memory(newmem); } @@ -1448,7 +1491,7 @@ case BarrierSet::CardTableModRef: case BarrierSet::CardTableExtension: - write_barrier_post(store, obj, adr, val, use_precise); + write_barrier_post(store, obj, adr, adr_idx, val, use_precise); break; case BarrierSet::ModRef: @@ -1571,7 +1614,7 @@ //---------------------------set_edges_for_java_call--------------------------- // Connect a newly created call into the current JVMS. // A return value node (if any) is returned from set_edges_for_java_call. -void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw) { +void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw, bool separate_io_proj) { // Add the predefined inputs: call->init_req( TypeFunc::Control, control() ); @@ -1593,13 +1636,13 @@ // Re-use the current map to produce the result. set_control(_gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Control))); - set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O ))); - set_all_memory_call(xcall); + set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O , separate_io_proj))); + set_all_memory_call(xcall, separate_io_proj); //return xcall; // no need, caller already has it } -Node* GraphKit::set_results_for_java_call(CallJavaNode* call) { +Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_proj) { if (stopped()) return top(); // maybe the call folded up? // Capture the return value, if any. @@ -1612,8 +1655,15 @@ // Note: Since any out-of-line call can produce an exception, // we always insert an I_O projection from the call into the result. - make_slow_call_ex(call, env()->Throwable_klass(), false); - + make_slow_call_ex(call, env()->Throwable_klass(), separate_io_proj); + + if (separate_io_proj) { + // The caller requested separate projections be used by the fall + // through and exceptional paths, so replace the projections for + // the fall through path. + set_i_o(_gvn.transform( new (C, 1) ProjNode(call, TypeFunc::I_O) )); + set_all_memory(_gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) )); + } return ret; } @@ -1676,6 +1726,64 @@ } } + +// Replace the call with the current state of the kit. +void GraphKit::replace_call(CallNode* call, Node* result) { + JVMState* ejvms = NULL; + if (has_exceptions()) { + ejvms = transfer_exceptions_into_jvms(); + } + + SafePointNode* final_state = stop(); + + // Find all the needed outputs of this call + CallProjections callprojs; + call->extract_projections(&callprojs, true); + + // Replace all the old call edges with the edges from the inlining result + C->gvn_replace_by(callprojs.fallthrough_catchproj, final_state->in(TypeFunc::Control)); + C->gvn_replace_by(callprojs.fallthrough_memproj, final_state->in(TypeFunc::Memory)); + C->gvn_replace_by(callprojs.fallthrough_ioproj, final_state->in(TypeFunc::I_O)); + + // Replace the result with the new result if it exists and is used + if (callprojs.resproj != NULL && result != NULL) { + C->gvn_replace_by(callprojs.resproj, result); + } + + if (ejvms == NULL) { + // No exception edges to simply kill off those paths + C->gvn_replace_by(callprojs.catchall_catchproj, C->top()); + C->gvn_replace_by(callprojs.catchall_memproj, C->top()); + C->gvn_replace_by(callprojs.catchall_ioproj, C->top()); + + // Replace the old exception object with top + if (callprojs.exobj != NULL) { + C->gvn_replace_by(callprojs.exobj, C->top()); + } + } else { + GraphKit ekit(ejvms); + + // Load my combined exception state into the kit, with all phis transformed: + SafePointNode* ex_map = ekit.combine_and_pop_all_exception_states(); + + Node* ex_oop = ekit.use_exception_state(ex_map); + + C->gvn_replace_by(callprojs.catchall_catchproj, ekit.control()); + C->gvn_replace_by(callprojs.catchall_memproj, ekit.reset_memory()); + C->gvn_replace_by(callprojs.catchall_ioproj, ekit.i_o()); + + // Replace the old exception object with the newly created one + if (callprojs.exobj != NULL) { + C->gvn_replace_by(callprojs.exobj, ex_oop); + } + } + + // Disconnect the call from the graph + call->disconnect_inputs(NULL); + C->gvn_replace_by(call, C->top()); +} + + //------------------------------increment_counter------------------------------ // for statistics: increment a VM counter by 1 @@ -3163,6 +3271,7 @@ void GraphKit::write_barrier_post(Node* oop_store, Node* obj, Node* adr, + uint adr_idx, Node* val, bool use_precise) { // No store check needed if we're storing a NULL or an old object @@ -3183,6 +3292,16 @@ return; } + if (use_ReduceInitialCardMarks() + && obj == just_allocated_object(control())) { + // We can skip marks on a freshly-allocated object in Eden. + // Keep this code in sync with new_store_pre_barrier() in runtime.cpp. + // That routine informs GC to take appropriate compensating steps, + // upon a slow-path allocation, so as to make this card-mark + // elision safe. + return; + } + if (!use_precise) { // All card marks for a (non-array) instance are in one place: adr = obj; @@ -3212,7 +3331,7 @@ __ store(__ ctrl(), card_adr, zero, bt, adr_type); } else { // Specialized path for CM store barrier - __ storeCM(__ ctrl(), card_adr, zero, oop_store, bt, adr_type); + __ storeCM(__ ctrl(), card_adr, zero, oop_store, adr_idx, bt, adr_type); } // Final sync IdealKit and GraphKit. @@ -3312,6 +3431,7 @@ void GraphKit::g1_mark_card(IdealKit& ideal, Node* card_adr, Node* oop_store, + uint oop_alias_idx, Node* index, Node* index_adr, Node* buffer, @@ -3321,7 +3441,7 @@ Node* no_base = __ top(); BasicType card_bt = T_BYTE; // Smash zero into card. MUST BE ORDERED WRT TO STORE - __ storeCM(__ ctrl(), card_adr, zero, oop_store, card_bt, Compile::AliasIdxRaw); + __ storeCM(__ ctrl(), card_adr, zero, oop_store, oop_alias_idx, card_bt, Compile::AliasIdxRaw); // Now do the queue work __ if_then(index, BoolTest::ne, zero); { @@ -3433,17 +3553,16 @@ Node* card_val = __ load(__ ctrl(), card_adr, TypeInt::INT, T_BYTE, Compile::AliasIdxRaw); __ if_then(card_val, BoolTest::ne, zero); { - g1_mark_card(ideal, card_adr, oop_store, index, index_adr, buffer, tf); + g1_mark_card(ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf); } __ end_if(); } __ end_if(); } __ end_if(); } else { // Object.clone() instrinsic uses this path. - g1_mark_card(ideal, card_adr, oop_store, index, index_adr, buffer, tf); + g1_mark_card(ideal, card_adr, oop_store, alias_idx, index, index_adr, buffer, tf); } // Final sync IdealKit and GraphKit. sync_kit(ideal); } #undef __ - diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/graphKit.hpp --- a/src/share/vm/opto/graphKit.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/graphKit.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2010 Sun Microsystems, 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 @@ -251,6 +251,11 @@ // via an uncommon trap. void builtin_throw(Deoptimization::DeoptReason reason, Node* arg = NULL); + // Helper to check the JavaThread::_should_post_on_exceptions flag + // and branch to an uncommon_trap if it is true (with the specified reason and must_throw) + void uncommon_trap_if_should_post_on_exceptions(Deoptimization::DeoptReason reason, + bool must_throw) ; + // Helper Functions for adding debug information void kill_dead_locals(); #ifdef ASSERT @@ -279,6 +284,34 @@ } Node* basic_plus_adr(Node* base, Node* ptr, Node* offset); + + // Some convenient shortcuts for common nodes + Node* IfTrue(IfNode* iff) { return _gvn.transform(new (C,1) IfTrueNode(iff)); } + Node* IfFalse(IfNode* iff) { return _gvn.transform(new (C,1) IfFalseNode(iff)); } + + Node* AddI(Node* l, Node* r) { return _gvn.transform(new (C,3) AddINode(l, r)); } + Node* SubI(Node* l, Node* r) { return _gvn.transform(new (C,3) SubINode(l, r)); } + Node* MulI(Node* l, Node* r) { return _gvn.transform(new (C,3) MulINode(l, r)); } + Node* DivI(Node* ctl, Node* l, Node* r) { return _gvn.transform(new (C,3) DivINode(ctl, l, r)); } + + Node* AndI(Node* l, Node* r) { return _gvn.transform(new (C,3) AndINode(l, r)); } + Node* OrI(Node* l, Node* r) { return _gvn.transform(new (C,3) OrINode(l, r)); } + Node* XorI(Node* l, Node* r) { return _gvn.transform(new (C,3) XorINode(l, r)); } + + Node* MaxI(Node* l, Node* r) { return _gvn.transform(new (C,3) MaxINode(l, r)); } + Node* MinI(Node* l, Node* r) { return _gvn.transform(new (C,3) MinINode(l, r)); } + + Node* LShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) LShiftINode(l, r)); } + Node* RShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) RShiftINode(l, r)); } + Node* URShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) URShiftINode(l, r)); } + + Node* CmpI(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpINode(l, r)); } + Node* CmpL(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpLNode(l, r)); } + Node* CmpP(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpPNode(l, r)); } + Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new (C,2) BoolNode(cmp, relop)); } + + Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(new (C,4) AddPNode(b, a, o)); } + // Convert between int and long, and size_t. // (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.) Node* ConvI2L(Node* offset); @@ -400,7 +433,7 @@ void set_all_memory(Node* newmem); // Create a memory projection from the call, then set_all_memory. - void set_all_memory_call(Node* call); + void set_all_memory_call(Node* call, bool separate_io_proj = false); // Create a LoadNode, reading from the parser's memory state. // (Note: require_atomic_access is useful only with T_LONG.) @@ -543,12 +576,12 @@ // Transform the call, and update the basics: control, i_o, memory. // (The next step is usually to call set_results_for_java_call.) void set_edges_for_java_call(CallJavaNode* call, - bool must_throw = false); + bool must_throw = false, bool separate_io_proj = false); // Finish up a java call that was started by set_edges_for_java_call. // Call add_exception on any throw arising from the call. // Return the call result (transformed). - Node* set_results_for_java_call(CallJavaNode* call); + Node* set_results_for_java_call(CallJavaNode* call, bool separate_io_proj = false); // Similar to set_edges_for_java_call, but simplified for runtime calls. void set_predefined_output_for_runtime_call(Node* call) { @@ -559,6 +592,11 @@ const TypePtr* hook_mem); Node* set_predefined_input_for_runtime_call(SafePointNode* call); + // Replace the call with the current state of the kit. Requires + // that the call was generated with separate io_projs so that + // exceptional control flow can be handled properly. + void replace_call(CallNode* call, Node* result); + // helper functions for statistics void increment_counter(address counter_addr); // increment a debug counter void increment_counter(Node* counter_addr); // increment a debug counter @@ -603,7 +641,8 @@ void sync_kit(IdealKit& ideal); // vanilla/CMS post barrier - void write_barrier_post(Node *store, Node* obj, Node* adr, Node* val, bool use_precise); + void write_barrier_post(Node *store, Node* obj, + Node* adr, uint adr_idx, Node* val, bool use_precise); // G1 pre/post barriers void g1_write_barrier_pre(Node* obj, @@ -622,7 +661,8 @@ bool use_precise); // Helper function for g1 private: - void g1_mark_card(IdealKit& ideal, Node* card_adr, Node* store, Node* index, Node* index_adr, + void g1_mark_card(IdealKit& ideal, Node* card_adr, Node* store, uint oop_alias_idx, + Node* index, Node* index_adr, Node* buffer, const TypeFunc* tf); public: diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/idealKit.cpp --- a/src/share/vm/opto/idealKit.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/idealKit.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, 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 @@ -378,7 +378,7 @@ // Card mark store. Must be ordered so that it will come after the store of // the oop. -Node* IdealKit::storeCM(Node* ctl, Node* adr, Node *val, Node* oop_store, +Node* IdealKit::storeCM(Node* ctl, Node* adr, Node *val, Node* oop_store, int oop_adr_idx, BasicType bt, int adr_idx) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); @@ -388,7 +388,7 @@ // Add required edge to oop_store, optimizer does not support precedence edges. // Convert required edge to precedence edge before allocation. - Node* st = new (C, 5) StoreCMNode(ctl, mem, adr, adr_type, val, oop_store); + Node* st = new (C, 5) StoreCMNode(ctl, mem, adr, adr_type, val, oop_store, oop_adr_idx); st = transform(st); set_memory(st, adr_idx); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/idealKit.hpp --- a/src/share/vm/opto/idealKit.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/idealKit.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, 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 @@ -216,6 +216,7 @@ Node* adr, Node* val, Node* oop_store, + int oop_adr_idx, BasicType bt, int adr_idx); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/ifg.cpp --- a/src/share/vm/opto/ifg.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/ifg.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/ifnode.cpp --- a/src/share/vm/opto/ifnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/ifnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2009 Sun Microsystems, 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 @@ -240,13 +240,13 @@ // as a single huge transform. igvn->register_new_node_with_optimizer( region_c ); igvn->register_new_node_with_optimizer( region_x ); - phi_x = phase->transform( phi_x ); // Prevent the untimely death of phi_x. Currently he has no uses. He is // about to get one. If this only use goes away, then phi_x will look dead. // However, he will be picking up some more uses down below. Node *hook = new (igvn->C, 4) Node(4); hook->init_req(0, phi_x); hook->init_req(1, phi_c); + phi_x = phase->transform( phi_x ); // Make the compare Node *cmp_c = phase->makecon(t); @@ -322,6 +322,7 @@ phi_s = PhiNode::make_blank(region_s,phi); phi_s->init_req( 1, phi_c ); phi_s->init_req( 2, phi_x ); + hook->add_req(phi_s); phi_s = phase->transform(phi_s); } proj_path_data = phi_s; @@ -333,6 +334,7 @@ phi_f = PhiNode::make_blank(region_f,phi); phi_f->init_req( 1, phi_c ); phi_f->init_req( 2, phi_x ); + hook->add_req(phi_f); phi_f = phase->transform(phi_f); } proj_path_data = phi_f; @@ -529,6 +531,9 @@ if (linear_only) return NULL; + if( dom->is_Root() ) + return NULL; + // Else hit a Region. Check for a loop header if( dom->is_Loop() ) return dom->in(1); // Skip up thru loops diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/lcm.cpp --- a/src/share/vm/opto/lcm.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/lcm.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -120,6 +120,7 @@ case Op_LoadRange: case Op_LoadD_unaligned: case Op_LoadL_unaligned: + assert(mach->in(2) == val, "should be address"); break; case Op_StoreB: case Op_StoreC: @@ -146,6 +147,21 @@ default: // Also check for embedded loads if( !mach->needs_anti_dependence_check() ) continue; // Not an memory op; skip it + { + // Check that value is used in memory address. + Node* base; + Node* index; + const MachOper* oper = mach->memory_inputs(base, index); + if (oper == NULL || oper == (MachOper*)-1) { + continue; // Not an memory op; skip it + } + if (val == base || + val == index && val->bottom_type()->isa_narrowoop()) { + break; // Found it + } else { + continue; // Skip it + } + } break; } // check if the offset is not too high for implicit exception @@ -542,6 +558,16 @@ // pointers as far as the kill mask goes. bool exclude_soe = op == Op_CallRuntime; + // If the call is a MethodHandle invoke, we need to exclude the + // register which is used to save the SP value over MH invokes from + // the mask. Otherwise this register could be used for + // deoptimization information. + if (op == Op_CallStaticJava) { + MachCallStaticJavaNode* mcallstaticjava = (MachCallStaticJavaNode*) mcall; + if (mcallstaticjava->_method_handle_invoke) + proj->_rout.OR(Matcher::method_handle_invoke_SP_save_mask()); + } + // Fill in the kill mask for the call for( OptoReg::Name r = OptoReg::Name(0); r < _last_Mach_Reg; r=OptoReg::add(r,1) ) { if( !regs.Member(r) ) { // Not already defined by the call @@ -616,8 +642,9 @@ assert(cfg->_bbs[oop_store->_idx]->_dom_depth <= this->_dom_depth, "oop_store must dominate card-mark"); } } - if( n->is_Mach() && n->as_Mach()->ideal_Opcode() == Op_MemBarAcquire && - n->req() > TypeFunc::Parms ) { + if( n->is_Mach() && n->req() > TypeFunc::Parms && + (n->as_Mach()->ideal_Opcode() == Op_MemBarAcquire || + n->as_Mach()->ideal_Opcode() == Op_MemBarVolatile) ) { // MemBarAcquire could be created without Precedent edge. // del_req() replaces the specified edge with the last input edge // and then removes the last edge. If the specified edge > number of diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/library_call.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -133,6 +133,7 @@ return generate_method_call(method_id, true, false); } + Node* make_string_method_node(int opcode, Node* str1, Node* cnt1, Node* str2, Node* cnt2); bool inline_string_compareTo(); bool inline_string_indexOf(); Node* string_indexOf(Node* string_object, ciTypeArray* target_array, jint offset, jint cache_i, jint md2_i); @@ -796,6 +797,64 @@ } +//------------------------------make_string_method_node------------------------ +// Helper method for String intrinsic finctions. +Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1, Node* cnt1, Node* str2, Node* cnt2) { + const int value_offset = java_lang_String::value_offset_in_bytes(); + const int count_offset = java_lang_String::count_offset_in_bytes(); + const int offset_offset = java_lang_String::offset_offset_in_bytes(); + + Node* no_ctrl = NULL; + + ciInstanceKlass* klass = env()->String_klass(); + const TypeInstPtr* string_type = + TypeInstPtr::make(TypePtr::BotPTR, klass, false, NULL, 0); + + const TypeAryPtr* value_type = + TypeAryPtr::make(TypePtr::NotNull, + TypeAry::make(TypeInt::CHAR,TypeInt::POS), + ciTypeArrayKlass::make(T_CHAR), true, 0); + + // Get start addr of string and substring + Node* str1_valuea = basic_plus_adr(str1, str1, value_offset); + Node* str1_value = make_load(no_ctrl, str1_valuea, value_type, T_OBJECT, string_type->add_offset(value_offset)); + Node* str1_offseta = basic_plus_adr(str1, str1, offset_offset); + Node* str1_offset = make_load(no_ctrl, str1_offseta, TypeInt::INT, T_INT, string_type->add_offset(offset_offset)); + Node* str1_start = array_element_address(str1_value, str1_offset, T_CHAR); + + // Pin loads from String::equals() argument since it could be NULL. + Node* str2_ctrl = (opcode == Op_StrEquals) ? control() : no_ctrl; + Node* str2_valuea = basic_plus_adr(str2, str2, value_offset); + Node* str2_value = make_load(str2_ctrl, str2_valuea, value_type, T_OBJECT, string_type->add_offset(value_offset)); + Node* str2_offseta = basic_plus_adr(str2, str2, offset_offset); + Node* str2_offset = make_load(str2_ctrl, str2_offseta, TypeInt::INT, T_INT, string_type->add_offset(offset_offset)); + Node* str2_start = array_element_address(str2_value, str2_offset, T_CHAR); + + Node* result = NULL; + switch (opcode) { + case Op_StrIndexOf: + result = new (C, 6) StrIndexOfNode(control(), memory(TypeAryPtr::CHARS), + str1_start, cnt1, str2_start, cnt2); + break; + case Op_StrComp: + result = new (C, 6) StrCompNode(control(), memory(TypeAryPtr::CHARS), + str1_start, cnt1, str2_start, cnt2); + break; + case Op_StrEquals: + result = new (C, 5) StrEqualsNode(control(), memory(TypeAryPtr::CHARS), + str1_start, str2_start, cnt1); + break; + default: + ShouldNotReachHere(); + return NULL; + } + + // All these intrinsics have checks. + C->set_has_split_ifs(true); // Has chance for split-if optimization + + return _gvn.transform(result); +} + //------------------------------inline_string_compareTo------------------------ bool LibraryCallKit::inline_string_compareTo() { @@ -824,16 +883,16 @@ ciInstanceKlass* klass = env()->String_klass(); const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::BotPTR, klass, false, NULL, 0); - - Node* compare = - _gvn.transform(new (C, 7) StrCompNode( - control(), - memory(TypeAryPtr::CHARS), - memory(string_type->add_offset(value_offset)), - memory(string_type->add_offset(count_offset)), - memory(string_type->add_offset(offset_offset)), - receiver, - argument)); + Node* no_ctrl = NULL; + + // Get counts for string and argument + Node* receiver_cnta = basic_plus_adr(receiver, receiver, count_offset); + Node* receiver_cnt = make_load(no_ctrl, receiver_cnta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + + Node* argument_cnta = basic_plus_adr(argument, argument, count_offset); + Node* argument_cnt = make_load(no_ctrl, argument_cnta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + + Node* compare = make_string_method_node(Op_StrComp, receiver, receiver_cnt, argument, argument_cnt); push(compare); return true; } @@ -865,45 +924,71 @@ return true; } + // paths (plus control) merge + RegionNode* region = new (C, 5) RegionNode(5); + Node* phi = new (C, 5) PhiNode(region, TypeInt::BOOL); + + // does source == target string? + Node* cmp = _gvn.transform(new (C, 3) CmpPNode(receiver, argument)); + Node* bol = _gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::eq)); + + Node* if_eq = generate_slow_guard(bol, NULL); + if (if_eq != NULL) { + // receiver == argument + phi->init_req(2, intcon(1)); + region->init_req(2, if_eq); + } + // get String klass for instanceOf ciInstanceKlass* klass = env()->String_klass(); - // two paths (plus control) merge - RegionNode* region = new (C, 3) RegionNode(3); - Node* phi = new (C, 3) PhiNode(region, TypeInt::BOOL); - - Node* inst = gen_instanceof(argument, makecon(TypeKlassPtr::make(klass))); - Node* cmp = _gvn.transform(new (C, 3) CmpINode(inst, intcon(1))); - Node* bol = _gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::eq)); - - IfNode* iff = create_and_map_if(control(), bol, PROB_MAX, COUNT_UNKNOWN); - - Node* if_true = _gvn.transform(new (C, 1) IfTrueNode(iff)); - set_control(if_true); + if (!stopped()) { + Node* inst = gen_instanceof(argument, makecon(TypeKlassPtr::make(klass))); + Node* cmp = _gvn.transform(new (C, 3) CmpINode(inst, intcon(1))); + Node* bol = _gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::ne)); + + Node* inst_false = generate_guard(bol, NULL, PROB_MIN); + //instanceOf == true, fallthrough + + if (inst_false != NULL) { + phi->init_req(3, intcon(0)); + region->init_req(3, inst_false); + } + } const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::BotPTR, klass, false, NULL, 0); - // instanceOf == true - Node* equals = - _gvn.transform(new (C, 7) StrEqualsNode( - control(), - memory(TypeAryPtr::CHARS), - memory(string_type->add_offset(value_offset)), - memory(string_type->add_offset(count_offset)), - memory(string_type->add_offset(offset_offset)), - receiver, - argument)); - - phi->init_req(1, _gvn.transform(equals)); - region->init_req(1, if_true); - - //instanceOf == false, fallthrough - Node* if_false = _gvn.transform(new (C, 1) IfFalseNode(iff)); - set_control(if_false); - - phi->init_req(2, _gvn.transform(intcon(0))); - region->init_req(2, if_false); + Node* no_ctrl = NULL; + Node* receiver_cnt; + Node* argument_cnt; + + if (!stopped()) { + // Get counts for string and argument + Node* receiver_cnta = basic_plus_adr(receiver, receiver, count_offset); + receiver_cnt = make_load(no_ctrl, receiver_cnta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + + // Pin load from argument string since it could be NULL. + Node* argument_cnta = basic_plus_adr(argument, argument, count_offset); + argument_cnt = make_load(control(), argument_cnta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + + // Check for receiver count != argument count + Node* cmp = _gvn.transform( new(C, 3) CmpINode(receiver_cnt, argument_cnt) ); + Node* bol = _gvn.transform( new(C, 2) BoolNode(cmp, BoolTest::ne) ); + Node* if_ne = generate_slow_guard(bol, NULL); + if (if_ne != NULL) { + phi->init_req(4, intcon(0)); + region->init_req(4, if_ne); + } + } + + // Check for count == 0 is done by mach node StrEquals. + + if (!stopped()) { + Node* equals = make_string_method_node(Op_StrEquals, receiver, receiver_cnt, argument, argument_cnt); + phi->init_req(1, equals); + region->init_req(1, control()); + } // post merge set_control(_gvn.transform(region)); @@ -924,10 +1009,8 @@ Node *argument1 = pop(); Node* equals = - _gvn.transform(new (C, 3) AryEqNode(control(), - argument1, - argument2) - ); + _gvn.transform(new (C, 4) AryEqNode(control(), memory(TypeAryPtr::CHARS), + argument1, argument2) ); push(equals); return true; } @@ -1108,19 +1191,40 @@ return true; } + // Make the merge point + RegionNode* result_rgn = new (C, 3) RegionNode(3); + Node* result_phi = new (C, 3) PhiNode(result_rgn, TypeInt::INT); + Node* no_ctrl = NULL; + ciInstanceKlass* klass = env()->String_klass(); const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::BotPTR, klass, false, NULL, 0); - result = - _gvn.transform(new (C, 7) - StrIndexOfNode(control(), - memory(TypeAryPtr::CHARS), - memory(string_type->add_offset(value_offset)), - memory(string_type->add_offset(count_offset)), - memory(string_type->add_offset(offset_offset)), - receiver, - argument)); + // Get counts for string and substr + Node* source_cnta = basic_plus_adr(receiver, receiver, count_offset); + Node* source_cnt = make_load(no_ctrl, source_cnta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + + Node* substr_cnta = basic_plus_adr(argument, argument, count_offset); + Node* substr_cnt = make_load(no_ctrl, substr_cnta, TypeInt::INT, T_INT, string_type->add_offset(count_offset)); + + // Check for substr count > string count + Node* cmp = _gvn.transform( new(C, 3) CmpINode(substr_cnt, source_cnt) ); + Node* bol = _gvn.transform( new(C, 2) BoolNode(cmp, BoolTest::gt) ); + Node* if_gt = generate_slow_guard(bol, NULL); + if (if_gt != NULL) { + result_phi->init_req(2, intcon(-1)); + result_rgn->init_req(2, if_gt); + } + + if (!stopped()) { + result = make_string_method_node(Op_StrIndexOf, receiver, source_cnt, argument, substr_cnt); + result_phi->init_req(1, result); + result_rgn->init_req(1, control()); + } + set_control(_gvn.transform(result_rgn)); + record_for_igvn(result_rgn); + result = _gvn.transform(result_phi); + } else { //Use LibraryCallKit::string_indexOf // don't intrinsify is argument isn't a constant string. if (!argument->is_Con()) { @@ -3593,12 +3697,14 @@ // Helper routine for above bool LibraryCallKit::is_method_invoke_or_aux_frame(JVMState* jvms) { + ciMethod* method = jvms->method(); + // Is this the Method.invoke method itself? - if (jvms->method()->intrinsic_id() == vmIntrinsics::_invoke) + if (method->intrinsic_id() == vmIntrinsics::_invoke) return true; // Is this a helper, defined somewhere underneath MethodAccessorImpl. - ciKlass* k = jvms->method()->holder(); + ciKlass* k = method->holder(); if (k->is_instance_klass()) { ciInstanceKlass* ik = k->as_instance_klass(); for (; ik != NULL; ik = ik->super()) { @@ -3608,6 +3714,10 @@ } } } + else if (method->is_method_handle_adapter()) { + // This is an internal adapter frame from the MethodHandleCompiler -- skip it + return true; + } return false; } @@ -3903,19 +4013,10 @@ guarantee(alloc != NULL && alloc->maybe_set_complete(&_gvn), ""); } - // Cast to Object for arraycopy. - // We can't use the original CheckCastPP since it should be moved - // after the arraycopy to prevent stores flowing above it. - Node* new_obj = new(C, 2) CheckCastPPNode(alloc_obj->in(0), raw_obj, - TypeInstPtr::NOTNULL); - new_obj = _gvn.transform(new_obj); - // Substitute in the locally valid dest_oop. - replace_in_map(alloc_obj, new_obj); - // Copy the fastest available way. // TODO: generate fields copies for small objects instead. Node* src = obj; - Node* dest = new_obj; + Node* dest = alloc_obj; Node* size = _gvn.transform(obj_size); // Exclude the header but include array length to copy by 8 bytes words. @@ -3961,7 +4062,7 @@ int raw_adr_idx = Compile::AliasIdxRaw; post_barrier(control(), memory(raw_adr_type), - new_obj, + alloc_obj, no_particular_field, raw_adr_idx, no_particular_value, @@ -3969,16 +4070,8 @@ false); } - // Move the original CheckCastPP after arraycopy. - _gvn.hash_delete(alloc_obj); - alloc_obj->set_req(0, control()); - // Replace raw memory edge with new CheckCastPP to have a live oop - // at safepoints instead of raw value. - assert(new_obj->is_CheckCastPP() && new_obj->in(1) == alloc_obj->in(1), "sanity"); - alloc_obj->set_req(1, new_obj); // cast to the original type - _gvn.hash_find_insert(alloc_obj); // put back into GVN table - // Restore in the locally valid dest_oop. - replace_in_map(new_obj, alloc_obj); + // Do not let reads from the cloned object float above the arraycopy. + insert_mem_bar(Op_MemBarCPUOrder); } //------------------------inline_native_clone---------------------------- @@ -4073,13 +4166,13 @@ result_mem ->set_req(_objArray_path, reset_memory()); } } - // We can dispense with card marks if we know the allocation - // comes out of eden (TLAB)... In fact, ReduceInitialCardMarks - // causes the non-eden paths to simulate a fresh allocation, - // insofar that no further card marks are required to initialize - // the object. - // Otherwise, there are no card marks to worry about. + // (We can dispense with card marks if we know the allocation + // comes out of eden (TLAB)... In fact, ReduceInitialCardMarks + // causes the non-eden paths to take compensating steps to + // simulate a fresh allocation, so that no further + // card marks are required in compiled code to initialize + // the object.) if (!stopped()) { copy_to_clone(obj, alloc_obj, obj_size, true, false); @@ -4448,15 +4541,6 @@ assert(init->is_complete(), "we just did this"); assert(dest->is_CheckCastPP(), "sanity"); assert(dest->in(0)->in(0) == init, "dest pinned"); - - // Cast to Object for arraycopy. - // We can't use the original CheckCastPP since it should be moved - // after the arraycopy to prevent stores flowing above it. - Node* new_obj = new(C, 2) CheckCastPPNode(dest->in(0), dest->in(1), - TypeInstPtr::NOTNULL); - dest = _gvn.transform(new_obj); - // Substitute in the locally valid dest_oop. - replace_in_map(original_dest, dest); adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory // From this point on, every exit path is responsible for // initializing any non-copied parts of the object to zero. @@ -4786,18 +4870,6 @@ set_i_o( _gvn.transform(result_i_o) ); set_memory( _gvn.transform(result_memory), adr_type ); - if (dest != original_dest) { - // Pin the "finished" array node after the arraycopy/zeroing operations. - _gvn.hash_delete(original_dest); - original_dest->set_req(0, control()); - // Replace raw memory edge with new CheckCastPP to have a live oop - // at safepoints instead of raw value. - assert(dest->is_CheckCastPP() && dest->in(1) == original_dest->in(1), "sanity"); - original_dest->set_req(1, dest); // cast to the original type - _gvn.hash_find_insert(original_dest); // put back into GVN table - // Restore in the locally valid dest_oop. - replace_in_map(dest, original_dest); - } // The memory edges above are precise in order to model effects around // array copies accurately to allow value numbering of field loads around // arraycopy. Such field loads, both before and after, are common in Java @@ -4808,7 +4880,9 @@ // The next memory barrier is added to avoid it. If the arraycopy can be // optimized away (which it can, sometimes) then we can manually remove // the membar also. - if (InsertMemBarAfterArraycopy) + // + // Do not let reads from the cloned object float above the arraycopy. + if (InsertMemBarAfterArraycopy || alloc != NULL) insert_mem_bar(Op_MemBarCPUOrder); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/loopTransform.cpp --- a/src/share/vm/opto/loopTransform.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/loopTransform.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -549,6 +549,10 @@ // Comparing trip+off vs limit Node *bol = iff->in(1); if( bol->req() != 2 ) continue; // dead constant test + if (!bol->is_Bool()) { + assert(UseLoopPredicate && bol->Opcode() == Op_Conv2B, "predicate check only"); + continue; + } Node *cmp = bol->in(1); Node *rc_exp = cmp->in(1); @@ -875,7 +879,7 @@ //------------------------------is_invariant----------------------------- // Return true if n is invariant bool IdealLoopTree::is_invariant(Node* n) const { - Node *n_c = _phase->get_ctrl(n); + Node *n_c = _phase->has_ctrl(n) ? _phase->get_ctrl(n) : n; if (n_c->is_top()) return false; return !is_member(_phase->get_loop(n_c)); } @@ -1594,7 +1598,7 @@ bool IdealLoopTree::iteration_split_impl( PhaseIdealLoop *phase, Node_List &old_new ) { // Check and remove empty loops (spam micro-benchmarks) if( policy_do_remove_empty_loop(phase) ) - return true; // Here we removed an empty loop + return true; // Here we removed an empty loop bool should_peel = policy_peeling(phase); // Should we peel? @@ -1688,8 +1692,8 @@ // an even number of trips). If we are peeling, we might enable some RCE // and we'd rather unroll the post-RCE'd loop SO... do not unroll if // peeling. - if( should_unroll && !should_peel ) - phase->do_unroll(this,old_new, true); + if( should_unroll && !should_peel ) + phase->do_unroll(this,old_new, true); // Adjust the pre-loop limits to align the main body // iterations. @@ -1731,9 +1735,9 @@ _allow_optimizations && !tail()->is_top() ) { // Also ignore the occasional dead backedge if (!_has_call) { - if (!iteration_split_impl( phase, old_new )) { - return false; - } + if (!iteration_split_impl( phase, old_new )) { + return false; + } } else if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); } @@ -1746,3 +1750,583 @@ return false; return true; } + +//-------------------------------is_uncommon_trap_proj---------------------------- +// Return true if proj is the form of "proj->[region->..]call_uct" +bool PhaseIdealLoop::is_uncommon_trap_proj(ProjNode* proj, bool must_reason_predicate) { + int path_limit = 10; + assert(proj, "invalid argument"); + Node* out = proj; + for (int ct = 0; ct < path_limit; ct++) { + out = out->unique_ctrl_out(); + if (out == NULL || out->is_Root() || out->is_Start()) + return false; + if (out->is_CallStaticJava()) { + int req = out->as_CallStaticJava()->uncommon_trap_request(); + if (req != 0) { + Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(req); + if (!must_reason_predicate || reason == Deoptimization::Reason_predicate){ + return true; + } + } + return false; // don't do further after call + } + } + return false; +} + +//-------------------------------is_uncommon_trap_if_pattern------------------------- +// Return true for "if(test)-> proj -> ... +// | +// V +// other_proj->[region->..]call_uct" +// +// "must_reason_predicate" means the uct reason must be Reason_predicate +bool PhaseIdealLoop::is_uncommon_trap_if_pattern(ProjNode *proj, bool must_reason_predicate) { + Node *in0 = proj->in(0); + if (!in0->is_If()) return false; + // Variation of a dead If node. + if (in0->outcnt() < 2) return false; + IfNode* iff = in0->as_If(); + + // we need "If(Conv2B(Opaque1(...)))" pattern for must_reason_predicate + if (must_reason_predicate) { + if (iff->in(1)->Opcode() != Op_Conv2B || + iff->in(1)->in(1)->Opcode() != Op_Opaque1) { + return false; + } + } + + ProjNode* other_proj = iff->proj_out(1-proj->_con)->as_Proj(); + return is_uncommon_trap_proj(other_proj, must_reason_predicate); +} + +//------------------------------create_new_if_for_predicate------------------------ +// create a new if above the uct_if_pattern for the predicate to be promoted. +// +// before after +// ---------- ---------- +// ctrl ctrl +// | | +// | | +// v v +// iff new_iff +// / \ / \ +// / \ / \ +// v v v v +// uncommon_proj cont_proj if_uct if_cont +// \ | | | | +// \ | | | | +// v v v | v +// rgn loop | iff +// | | / \ +// | | / \ +// v | v v +// uncommon_trap | uncommon_proj cont_proj +// \ \ | | +// \ \ | | +// v v v v +// rgn loop +// | +// | +// v +// uncommon_trap +// +// +// We will create a region to guard the uct call if there is no one there. +// The true projecttion (if_cont) of the new_iff is returned. +ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj) { + assert(is_uncommon_trap_if_pattern(cont_proj, true), "must be a uct if pattern!"); + IfNode* iff = cont_proj->in(0)->as_If(); + + ProjNode *uncommon_proj = iff->proj_out(1 - cont_proj->_con); + Node *rgn = uncommon_proj->unique_ctrl_out(); + assert(rgn->is_Region() || rgn->is_Call(), "must be a region or call uct"); + + if (!rgn->is_Region()) { // create a region to guard the call + assert(rgn->is_Call(), "must be call uct"); + CallNode* call = rgn->as_Call(); + rgn = new (C, 1) RegionNode(1); + _igvn.set_type(rgn, rgn->bottom_type()); + rgn->add_req(uncommon_proj); + set_idom(rgn, idom(uncommon_proj), dom_depth(uncommon_proj)+1); + _igvn.hash_delete(call); + call->set_req(0, rgn); + } + + // Create new_iff + uint iffdd = dom_depth(iff); + IdealLoopTree* lp = get_loop(iff); + IfNode *new_iff = new (C, 2) IfNode(iff->in(0), NULL, iff->_prob, iff->_fcnt); + register_node(new_iff, lp, idom(iff), iffdd); + Node *if_cont = new (C, 1) IfTrueNode(new_iff); + Node *if_uct = new (C, 1) IfFalseNode(new_iff); + if (cont_proj->is_IfFalse()) { + // Swap + Node* tmp = if_uct; if_uct = if_cont; if_cont = tmp; + } + register_node(if_cont, lp, new_iff, iffdd); + register_node(if_uct, get_loop(rgn), new_iff, iffdd); + + // if_cont to iff + _igvn.hash_delete(iff); + iff->set_req(0, if_cont); + set_idom(iff, if_cont, dom_depth(iff)); + + // if_uct to rgn + _igvn.hash_delete(rgn); + rgn->add_req(if_uct); + Node* ridom = idom(rgn); + Node* nrdom = dom_lca(ridom, new_iff); + set_idom(rgn, nrdom, dom_depth(rgn)); + + // rgn must have no phis + assert(!rgn->as_Region()->has_phi(), "region must have no phis"); + + return if_cont->as_Proj(); +} + +//------------------------------find_predicate_insertion_point-------------------------- +// Find a good location to insert a predicate +ProjNode* PhaseIdealLoop::find_predicate_insertion_point(Node* start_c) { + if (start_c == C->root() || !start_c->is_Proj()) + return NULL; + if (is_uncommon_trap_if_pattern(start_c->as_Proj(), true/*Reason_Predicate*/)) { + return start_c->as_Proj(); + } + return NULL; +} + +//------------------------------Invariance----------------------------------- +// Helper class for loop_predication_impl to compute invariance on the fly and +// clone invariants. +class Invariance : public StackObj { + VectorSet _visited, _invariant; + Node_Stack _stack; + VectorSet _clone_visited; + Node_List _old_new; // map of old to new (clone) + IdealLoopTree* _lpt; + PhaseIdealLoop* _phase; + + // Helper function to set up the invariance for invariance computation + // If n is a known invariant, set up directly. Otherwise, look up the + // the possibility to push n onto the stack for further processing. + void visit(Node* use, Node* n) { + if (_lpt->is_invariant(n)) { // known invariant + _invariant.set(n->_idx); + } else if (!n->is_CFG()) { + Node *n_ctrl = _phase->ctrl_or_self(n); + Node *u_ctrl = _phase->ctrl_or_self(use); // self if use is a CFG + if (_phase->is_dominator(n_ctrl, u_ctrl)) { + _stack.push(n, n->in(0) == NULL ? 1 : 0); + } + } + } + + // Compute invariance for "the_node" and (possibly) all its inputs recursively + // on the fly + void compute_invariance(Node* n) { + assert(_visited.test(n->_idx), "must be"); + visit(n, n); + while (_stack.is_nonempty()) { + Node* n = _stack.node(); + uint idx = _stack.index(); + if (idx == n->req()) { // all inputs are processed + _stack.pop(); + // n is invariant if it's inputs are all invariant + bool all_inputs_invariant = true; + for (uint i = 0; i < n->req(); i++) { + Node* in = n->in(i); + if (in == NULL) continue; + assert(_visited.test(in->_idx), "must have visited input"); + if (!_invariant.test(in->_idx)) { // bad guy + all_inputs_invariant = false; + break; + } + } + if (all_inputs_invariant) { + _invariant.set(n->_idx); // I am a invariant too + } + } else { // process next input + _stack.set_index(idx + 1); + Node* m = n->in(idx); + if (m != NULL && !_visited.test_set(m->_idx)) { + visit(n, m); + } + } + } + } + + // Helper function to set up _old_new map for clone_nodes. + // If n is a known invariant, set up directly ("clone" of n == n). + // Otherwise, push n onto the stack for real cloning. + void clone_visit(Node* n) { + assert(_invariant.test(n->_idx), "must be invariant"); + if (_lpt->is_invariant(n)) { // known invariant + _old_new.map(n->_idx, n); + } else{ // to be cloned + assert (!n->is_CFG(), "should not see CFG here"); + _stack.push(n, n->in(0) == NULL ? 1 : 0); + } + } + + // Clone "n" and (possibly) all its inputs recursively + void clone_nodes(Node* n, Node* ctrl) { + clone_visit(n); + while (_stack.is_nonempty()) { + Node* n = _stack.node(); + uint idx = _stack.index(); + if (idx == n->req()) { // all inputs processed, clone n! + _stack.pop(); + // clone invariant node + Node* n_cl = n->clone(); + _old_new.map(n->_idx, n_cl); + _phase->register_new_node(n_cl, ctrl); + for (uint i = 0; i < n->req(); i++) { + Node* in = n_cl->in(i); + if (in == NULL) continue; + n_cl->set_req(i, _old_new[in->_idx]); + } + } else { // process next input + _stack.set_index(idx + 1); + Node* m = n->in(idx); + if (m != NULL && !_clone_visited.test_set(m->_idx)) { + clone_visit(m); // visit the input + } + } + } + } + + public: + Invariance(Arena* area, IdealLoopTree* lpt) : + _lpt(lpt), _phase(lpt->_phase), + _visited(area), _invariant(area), _stack(area, 10 /* guess */), + _clone_visited(area), _old_new(area) + {} + + // Map old to n for invariance computation and clone + void map_ctrl(Node* old, Node* n) { + assert(old->is_CFG() && n->is_CFG(), "must be"); + _old_new.map(old->_idx, n); // "clone" of old is n + _invariant.set(old->_idx); // old is invariant + _clone_visited.set(old->_idx); + } + + // Driver function to compute invariance + bool is_invariant(Node* n) { + if (!_visited.test_set(n->_idx)) + compute_invariance(n); + return (_invariant.test(n->_idx) != 0); + } + + // Driver function to clone invariant + Node* clone(Node* n, Node* ctrl) { + assert(ctrl->is_CFG(), "must be"); + assert(_invariant.test(n->_idx), "must be an invariant"); + if (!_clone_visited.test(n->_idx)) + clone_nodes(n, ctrl); + return _old_new[n->_idx]; + } +}; + +//------------------------------is_range_check_if ----------------------------------- +// Returns true if the predicate of iff is in "scale*iv + offset u< load_range(ptr)" format +// Note: this function is particularly designed for loop predication. We require load_range +// and offset to be loop invariant computed on the fly by "invar" +bool IdealLoopTree::is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar) const { + if (!is_loop_exit(iff)) { + return false; + } + if (!iff->in(1)->is_Bool()) { + return false; + } + const BoolNode *bol = iff->in(1)->as_Bool(); + if (bol->_test._test != BoolTest::lt) { + return false; + } + if (!bol->in(1)->is_Cmp()) { + return false; + } + const CmpNode *cmp = bol->in(1)->as_Cmp(); + if (cmp->Opcode() != Op_CmpU ) { + return false; + } + if (cmp->in(2)->Opcode() != Op_LoadRange) { + return false; + } + LoadRangeNode* lr = (LoadRangeNode*)cmp->in(2); + if (!invar.is_invariant(lr)) { // loadRange must be invariant + return false; + } + Node *iv = _head->as_CountedLoop()->phi(); + int scale = 0; + Node *offset = NULL; + if (!phase->is_scaled_iv_plus_offset(cmp->in(1), iv, &scale, &offset)) { + return false; + } + if(offset && !invar.is_invariant(offset)) { // offset must be invariant + return false; + } + return true; +} + +//------------------------------rc_predicate----------------------------------- +// Create a range check predicate +// +// for (i = init; i < limit; i += stride) { +// a[scale*i+offset] +// } +// +// Compute max(scale*i + offset) for init <= i < limit and build the predicate +// as "max(scale*i + offset) u< a.length". +// +// There are two cases for max(scale*i + offset): +// (1) stride*scale > 0 +// max(scale*i + offset) = scale*(limit-stride) + offset +// (2) stride*scale < 0 +// max(scale*i + offset) = scale*init + offset +BoolNode* PhaseIdealLoop::rc_predicate(Node* ctrl, + int scale, Node* offset, + Node* init, Node* limit, Node* stride, + Node* range) { + Node* max_idx_expr = init; + int stride_con = stride->get_int(); + if ((stride_con > 0) == (scale > 0)) { + max_idx_expr = new (C, 3) SubINode(limit, stride); + register_new_node(max_idx_expr, ctrl); + } + + if (scale != 1) { + ConNode* con_scale = _igvn.intcon(scale); + max_idx_expr = new (C, 3) MulINode(max_idx_expr, con_scale); + register_new_node(max_idx_expr, ctrl); + } + + if (offset && (!offset->is_Con() || offset->get_int() != 0)){ + max_idx_expr = new (C, 3) AddINode(max_idx_expr, offset); + register_new_node(max_idx_expr, ctrl); + } + + CmpUNode* cmp = new (C, 3) CmpUNode(max_idx_expr, range); + register_new_node(cmp, ctrl); + BoolNode* bol = new (C, 2) BoolNode(cmp, BoolTest::lt); + register_new_node(bol, ctrl); + return bol; +} + +//------------------------------ loop_predication_impl-------------------------- +// Insert loop predicates for null checks and range checks +bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { + if (!UseLoopPredicate) return false; + + if (!loop->_head->is_Loop()) { + // Could be a simple region when irreducible loops are present. + return false; + } + + CountedLoopNode *cl = NULL; + if (loop->_head->is_CountedLoop()) { + cl = loop->_head->as_CountedLoop(); + // do nothing for iteration-splitted loops + if (!cl->is_normal_loop()) return false; + } + + // Too many traps seen? + bool tmt = C->too_many_traps(C->method(), 0, Deoptimization::Reason_predicate); + int tc = C->trap_count(Deoptimization::Reason_predicate); + if (tmt || tc > 0) { + if (TraceLoopPredicate) { + tty->print_cr("too many predicate traps: %d", tc); + C->method()->print(); // which method has too many predicate traps + tty->print_cr(""); + } + return false; + } + + LoopNode *lpn = loop->_head->as_Loop(); + Node* entry = lpn->in(LoopNode::EntryControl); + + ProjNode *predicate_proj = find_predicate_insertion_point(entry); + if (!predicate_proj){ +#ifndef PRODUCT + if (TraceLoopPredicate) { + tty->print("missing predicate:"); + loop->dump_head(); + } +#endif + return false; + } + + ConNode* zero = _igvn.intcon(0); + set_ctrl(zero, C->root()); + Node *cond_false = new (C, 2) Conv2BNode(zero); + register_new_node(cond_false, C->root()); + ConNode* one = _igvn.intcon(1); + set_ctrl(one, C->root()); + Node *cond_true = new (C, 2) Conv2BNode(one); + register_new_node(cond_true, C->root()); + + ResourceArea *area = Thread::current()->resource_area(); + Invariance invar(area, loop); + + // Create list of if-projs such that a newer proj dominates all older + // projs in the list, and they all dominate loop->tail() + Node_List if_proj_list(area); + LoopNode *head = loop->_head->as_Loop(); + Node *current_proj = loop->tail(); //start from tail + while ( current_proj != head ) { + if (loop == get_loop(current_proj) && // still in the loop ? + current_proj->is_Proj() && // is a projection ? + current_proj->in(0)->Opcode() == Op_If) { // is a if projection ? + if_proj_list.push(current_proj); + } + current_proj = idom(current_proj); + } + + bool hoisted = false; // true if at least one proj is promoted + while (if_proj_list.size() > 0) { + // Following are changed to nonnull when a predicate can be hoisted + ProjNode* new_predicate_proj = NULL; + BoolNode* new_predicate_bol = NULL; + + ProjNode* proj = if_proj_list.pop()->as_Proj(); + IfNode* iff = proj->in(0)->as_If(); + + if (!is_uncommon_trap_if_pattern(proj)) { + if (loop->is_loop_exit(iff)) { + // stop processing the remaining projs in the list because the execution of them + // depends on the condition of "iff" (iff->in(1)). + break; + } else { + // Both arms are inside the loop. There are two cases: + // (1) there is one backward branch. In this case, any remaining proj + // in the if_proj list post-dominates "iff". So, the condition of "iff" + // does not determine the execution the remining projs directly, and we + // can safely continue. + // (2) both arms are forwarded, i.e. a diamond shape. In this case, "proj" + // does not dominate loop->tail(), so it can not be in the if_proj list. + continue; + } + } + + Node* test = iff->in(1); + if (!test->is_Bool()){ //Conv2B, ... + continue; + } + BoolNode* bol = test->as_Bool(); + if (invar.is_invariant(bol)) { + // Invariant test + new_predicate_proj = create_new_if_for_predicate(predicate_proj); + Node* ctrl = new_predicate_proj->in(0)->as_If()->in(0); + new_predicate_bol = invar.clone(bol, ctrl)->as_Bool(); + if (TraceLoopPredicate) tty->print("invariant"); + } else if (cl != NULL && loop->is_range_check_if(iff, this, invar)) { + // Range check (only for counted loops) + new_predicate_proj = create_new_if_for_predicate(predicate_proj); + Node *ctrl = new_predicate_proj->in(0)->as_If()->in(0); + const Node* cmp = bol->in(1)->as_Cmp(); + Node* idx = cmp->in(1); + assert(!invar.is_invariant(idx), "index is variant"); + assert(cmp->in(2)->Opcode() == Op_LoadRange, "must be"); + LoadRangeNode* ld_rng = (LoadRangeNode*)cmp->in(2); // LoadRangeNode + assert(invar.is_invariant(ld_rng), "load range must be invariant"); + ld_rng = (LoadRangeNode*)invar.clone(ld_rng, ctrl); + int scale = 1; + Node* offset = zero; + bool ok = is_scaled_iv_plus_offset(idx, cl->phi(), &scale, &offset); + assert(ok, "must be index expression"); + if (offset && offset != zero) { + assert(invar.is_invariant(offset), "offset must be loop invariant"); + offset = invar.clone(offset, ctrl); + } + Node* init = cl->init_trip(); + Node* limit = cl->limit(); + Node* stride = cl->stride(); + new_predicate_bol = rc_predicate(ctrl, scale, offset, init, limit, stride, ld_rng); + if (TraceLoopPredicate) tty->print("range check"); + } + + if (new_predicate_proj == NULL) { + // The other proj of the "iff" is a uncommon trap projection, and we can assume + // the other proj will not be executed ("executed" means uct raised). + continue; + } else { + // Success - attach condition (new_predicate_bol) to predicate if + invar.map_ctrl(proj, new_predicate_proj); // so that invariance test can be appropriate + IfNode* new_iff = new_predicate_proj->in(0)->as_If(); + + // Negate test if necessary + if (proj->_con != predicate_proj->_con) { + new_predicate_bol = new (C, 2) BoolNode(new_predicate_bol->in(1), new_predicate_bol->_test.negate()); + register_new_node(new_predicate_bol, new_iff->in(0)); + if (TraceLoopPredicate) tty->print_cr(" if negated: %d", iff->_idx); + } else { + if (TraceLoopPredicate) tty->print_cr(" if: %d", iff->_idx); + } + + _igvn.hash_delete(new_iff); + new_iff->set_req(1, new_predicate_bol); + + _igvn.hash_delete(iff); + iff->set_req(1, proj->is_IfFalse() ? cond_false : cond_true); + + Node* ctrl = new_predicate_proj; // new control + ProjNode* dp = proj; // old control + assert(get_loop(dp) == loop, "guarenteed at the time of collecting proj"); + // Find nodes (depends only on the test) off the surviving projection; + // move them outside the loop with the control of proj_clone + for (DUIterator_Fast imax, i = dp->fast_outs(imax); i < imax; i++) { + Node* cd = dp->fast_out(i); // Control-dependent node + if (cd->depends_only_on_test()) { + assert(cd->in(0) == dp, ""); + _igvn.hash_delete(cd); + cd->set_req(0, ctrl); // ctrl, not NULL + set_early_ctrl(cd); + _igvn._worklist.push(cd); + IdealLoopTree *new_loop = get_loop(get_ctrl(cd)); + if (new_loop != loop) { + if (!loop->_child) loop->_body.yank(cd); + if (!new_loop->_child ) new_loop->_body.push(cd); + } + --i; + --imax; + } + } + + hoisted = true; + C->set_major_progress(); + } + } // end while + +#ifndef PRODUCT + // report that the loop predication has been actually performed + // for this loop + if (TraceLoopPredicate && hoisted) { + tty->print("Loop Predication Performed:"); + loop->dump_head(); + } +#endif + + return hoisted; +} + +//------------------------------loop_predication-------------------------------- +// driver routine for loop predication optimization +bool IdealLoopTree::loop_predication( PhaseIdealLoop *phase) { + bool hoisted = false; + // Recursively promote predicates + if ( _child ) { + hoisted = _child->loop_predication( phase); + } + + // self + if (!_irreducible && !tail()->is_top()) { + hoisted |= phase->loop_predication_impl(this); + } + + if ( _next ) { //sibling + hoisted |= _next->loop_predication( phase); + } + + return hoisted; +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/loopnode.cpp --- a/src/share/vm/opto/loopnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/loopnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1279,7 +1279,8 @@ // Visit all children, looking for Phis for (DUIterator i = cl->outs(); cl->has_out(i); i++) { Node *out = cl->out(i); - if (!out->is_Phi() || out == phi) continue; // Looking for other phis + // Look for other phis (secondary IVs). Skip dead ones + if (!out->is_Phi() || out == phi || !phase->has_node(out)) continue; PhiNode* phi2 = out->as_Phi(); Node *incr2 = phi2->in( LoopNode::LoopBackControl ); // Look for induction variables of the form: X += constant @@ -1419,14 +1420,59 @@ } } +//---------------------collect_potentially_useful_predicates----------------------- +// Helper function to collect potentially useful predicates to prevent them from +// being eliminated by PhaseIdealLoop::eliminate_useless_predicates +void PhaseIdealLoop::collect_potentially_useful_predicates( + IdealLoopTree * loop, Unique_Node_List &useful_predicates) { + if (loop->_child) { // child + collect_potentially_useful_predicates(loop->_child, useful_predicates); + } + + // self (only loops that we can apply loop predication may use their predicates) + if (loop->_head->is_Loop() && + !loop->_irreducible && + !loop->tail()->is_top()) { + LoopNode *lpn = loop->_head->as_Loop(); + Node* entry = lpn->in(LoopNode::EntryControl); + ProjNode *predicate_proj = find_predicate_insertion_point(entry); + if (predicate_proj != NULL ) { // right pattern that can be used by loop predication + assert(entry->in(0)->in(1)->in(1)->Opcode()==Op_Opaque1, "must be"); + useful_predicates.push(entry->in(0)->in(1)->in(1)); // good one + } + } + + if ( loop->_next ) { // sibling + collect_potentially_useful_predicates(loop->_next, useful_predicates); + } +} + +//------------------------eliminate_useless_predicates----------------------------- +// Eliminate all inserted predicates if they could not be used by loop predication. +void PhaseIdealLoop::eliminate_useless_predicates() { + if (C->predicate_count() == 0) return; // no predicate left + + Unique_Node_List useful_predicates; // to store useful predicates + if (C->has_loops()) { + collect_potentially_useful_predicates(_ltree_root->_child, useful_predicates); + } + + for (int i = C->predicate_count(); i > 0; i--) { + Node * n = C->predicate_opaque1_node(i-1); + assert(n->Opcode() == Op_Opaque1, "must be"); + if (!useful_predicates.member(n)) { // not in the useful list + _igvn.replace_node(n, n->in(1)); + } + } +} + //============================================================================= -//------------------------------PhaseIdealLoop--------------------------------- +//----------------------------build_and_optimize------------------------------- // Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to // its corresponding LoopNode. If 'optimize' is true, do some loop cleanups. -PhaseIdealLoop::PhaseIdealLoop( PhaseIterGVN &igvn, const PhaseIdealLoop *verify_me, bool do_split_ifs ) - : PhaseTransform(Ideal_Loop), - _igvn(igvn), - _dom_lca_tags(C->comp_arena()) { +void PhaseIdealLoop::build_and_optimize(bool do_split_ifs, bool do_loop_pred) { + int old_progress = C->major_progress(); + // Reset major-progress flag for the driver's heuristics C->clear_major_progress(); @@ -1465,18 +1511,20 @@ } // No loops after all - if( !_ltree_root->_child ) C->set_has_loops(false); + if( !_ltree_root->_child && !_verify_only ) C->set_has_loops(false); // There should always be an outer loop containing the Root and Return nodes. // If not, we have a degenerate empty program. Bail out in this case. if (!has_node(C->root())) { - C->clear_major_progress(); - C->record_method_not_compilable("empty program detected during loop optimization"); + if (!_verify_only) { + C->clear_major_progress(); + C->record_method_not_compilable("empty program detected during loop optimization"); + } return; } // Nothing to do, so get out - if( !C->has_loops() && !do_split_ifs && !verify_me) { + if( !C->has_loops() && !do_split_ifs && !_verify_me && !_verify_only ) { _igvn.optimize(); // Cleanup NeverBranches return; } @@ -1486,7 +1534,7 @@ // Split shared headers and insert loop landing pads. // Do not bother doing this on the Root loop of course. - if( !verify_me && _ltree_root->_child ) { + if( !_verify_me && !_verify_only && _ltree_root->_child ) { if( _ltree_root->_child->beautify_loops( this ) ) { // Re-build loop tree! _ltree_root->_child = NULL; @@ -1515,24 +1563,26 @@ Dominators(); - // As a side effect, Dominators removed any unreachable CFG paths - // into RegionNodes. It doesn't do this test against Root, so - // we do it here. - for( uint i = 1; i < C->root()->req(); i++ ) { - if( !_nodes[C->root()->in(i)->_idx] ) { // Dead path into Root? - _igvn.hash_delete(C->root()); - C->root()->del_req(i); - _igvn._worklist.push(C->root()); - i--; // Rerun same iteration on compressed edges + if (!_verify_only) { + // As a side effect, Dominators removed any unreachable CFG paths + // into RegionNodes. It doesn't do this test against Root, so + // we do it here. + for( uint i = 1; i < C->root()->req(); i++ ) { + if( !_nodes[C->root()->in(i)->_idx] ) { // Dead path into Root? + _igvn.hash_delete(C->root()); + C->root()->del_req(i); + _igvn._worklist.push(C->root()); + i--; // Rerun same iteration on compressed edges + } } + + // Given dominators, try to find inner loops with calls that must + // always be executed (call dominates loop tail). These loops do + // not need a separate safepoint. + Node_List cisstack(a); + _ltree_root->check_safepts(visited, cisstack); } - // Given dominators, try to find inner loops with calls that must - // always be executed (call dominates loop tail). These loops do - // not need a separate safepoint. - Node_List cisstack(a); - _ltree_root->check_safepts(visited, cisstack); - // Walk the DATA nodes and place into loops. Find earliest control // node. For CFG nodes, the _nodes array starts out and remains // holding the associated IdealLoopTree pointer. For DATA nodes, the @@ -1548,11 +1598,11 @@ // it will be processed among C->top() inputs worklist.push( C->top() ); visited.set( C->top()->_idx ); // Set C->top() as visited now - build_loop_early( visited, worklist, nstack, verify_me ); + build_loop_early( visited, worklist, nstack ); // Given early legal placement, try finding counted loops. This placement // is good enough to discover most loop invariants. - if( !verify_me ) + if( !_verify_me && !_verify_only ) _ltree_root->counted_loop( this ); // Find latest loop placement. Find ideal loop placement. @@ -1562,16 +1612,31 @@ worklist.push( C->root() ); NOT_PRODUCT( C->verify_graph_edges(); ) worklist.push( C->top() ); - build_loop_late( visited, worklist, nstack, verify_me ); + build_loop_late( visited, worklist, nstack ); + + if (_verify_only) { + // restore major progress flag + for (int i = 0; i < old_progress; i++) + C->set_major_progress(); + assert(C->unique() == unique, "verification mode made Nodes? ? ?"); + assert(_igvn._worklist.size() == 0, "shouldn't push anything"); + return; + } + + // some parser-inserted loop predicates could never be used by loop + // predication. Eliminate them before loop optimization + if (UseLoopPredicate) { + eliminate_useless_predicates(); + } // clear out the dead code while(_deadlist.size()) { - igvn.remove_globally_dead_node(_deadlist.pop()); + _igvn.remove_globally_dead_node(_deadlist.pop()); } #ifndef PRODUCT C->verify_graph_edges(); - if( verify_me ) { // Nested verify pass? + if( _verify_me ) { // Nested verify pass? // Check to see if the verify mode is broken assert(C->unique() == unique, "non-optimize mode made Nodes? ? ?"); return; @@ -1590,7 +1655,7 @@ // Because RCE opportunities can be masked by split_thru_phi, // look for RCE candidates and inhibit split_thru_phi // on just their loop-phi's for this pass of loop opts - if( SplitIfBlocks && do_split_ifs ) { + if (SplitIfBlocks && do_split_ifs) { if (lpt->policy_range_check(this)) { lpt->_rce_candidate = 1; // = true } @@ -1606,12 +1671,17 @@ NOT_PRODUCT( if( VerifyLoopOptimizations ) verify(); ); } + // Perform loop predication before iteration splitting + if (do_loop_pred && C->has_loops() && !C->major_progress()) { + _ltree_root->_child->loop_predication(this); + } + // Perform iteration-splitting on inner loops. Split iterations to avoid // range checks or one-shot null checks. // If split-if's didn't hack the graph too bad (no CFG changes) // then do loop opts. - if( C->has_loops() && !C->major_progress() ) { + if (C->has_loops() && !C->major_progress()) { memset( worklist.adr(), 0, worklist.Size()*sizeof(Node*) ); _ltree_root->_child->iteration_split( this, worklist ); // No verify after peeling! GCM has hoisted code out of the loop. @@ -1623,7 +1693,7 @@ // Do verify graph edges in any case NOT_PRODUCT( C->verify_graph_edges(); ); - if( !do_split_ifs ) { + if (!do_split_ifs) { // We saw major progress in Split-If to get here. We forced a // pass with unrolling and not split-if, however more split-if's // might make progress. If the unrolling didn't make progress @@ -1678,7 +1748,7 @@ void PhaseIdealLoop::verify() const { int old_progress = C->major_progress(); ResourceMark rm; - PhaseIdealLoop loop_verify( _igvn, this, false ); + PhaseIdealLoop loop_verify( _igvn, this ); VectorSet visited(Thread::current()->resource_area()); fail = 0; @@ -2138,54 +2208,58 @@ // optimizing an infinite loop? l = _ltree_root; // Oops, found infinite loop - // Insert the NeverBranch between 'm' and it's control user. - NeverBranchNode *iff = new (C, 1) NeverBranchNode( m ); - _igvn.register_new_node_with_optimizer(iff); - set_loop(iff, l); - Node *if_t = new (C, 1) CProjNode( iff, 0 ); - _igvn.register_new_node_with_optimizer(if_t); - set_loop(if_t, l); + if (!_verify_only) { + // Insert the NeverBranch between 'm' and it's control user. + NeverBranchNode *iff = new (C, 1) NeverBranchNode( m ); + _igvn.register_new_node_with_optimizer(iff); + set_loop(iff, l); + Node *if_t = new (C, 1) CProjNode( iff, 0 ); + _igvn.register_new_node_with_optimizer(if_t); + set_loop(if_t, l); - Node* cfg = NULL; // Find the One True Control User of m - for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { - Node* x = m->fast_out(j); - if (x->is_CFG() && x != m && x != iff) - { cfg = x; break; } + Node* cfg = NULL; // Find the One True Control User of m + for (DUIterator_Fast jmax, j = m->fast_outs(jmax); j < jmax; j++) { + Node* x = m->fast_out(j); + if (x->is_CFG() && x != m && x != iff) + { cfg = x; break; } + } + assert(cfg != NULL, "must find the control user of m"); + uint k = 0; // Probably cfg->in(0) + while( cfg->in(k) != m ) k++; // But check incase cfg is a Region + cfg->set_req( k, if_t ); // Now point to NeverBranch + + // Now create the never-taken loop exit + Node *if_f = new (C, 1) CProjNode( iff, 1 ); + _igvn.register_new_node_with_optimizer(if_f); + set_loop(if_f, l); + // Find frame ptr for Halt. Relies on the optimizer + // V-N'ing. Easier and quicker than searching through + // the program structure. + Node *frame = new (C, 1) ParmNode( C->start(), TypeFunc::FramePtr ); + _igvn.register_new_node_with_optimizer(frame); + // Halt & Catch Fire + Node *halt = new (C, TypeFunc::Parms) HaltNode( if_f, frame ); + _igvn.register_new_node_with_optimizer(halt); + set_loop(halt, l); + C->root()->add_req(halt); } - assert(cfg != NULL, "must find the control user of m"); - uint k = 0; // Probably cfg->in(0) - while( cfg->in(k) != m ) k++; // But check incase cfg is a Region - cfg->set_req( k, if_t ); // Now point to NeverBranch - - // Now create the never-taken loop exit - Node *if_f = new (C, 1) CProjNode( iff, 1 ); - _igvn.register_new_node_with_optimizer(if_f); - set_loop(if_f, l); - // Find frame ptr for Halt. Relies on the optimizer - // V-N'ing. Easier and quicker than searching through - // the program structure. - Node *frame = new (C, 1) ParmNode( C->start(), TypeFunc::FramePtr ); - _igvn.register_new_node_with_optimizer(frame); - // Halt & Catch Fire - Node *halt = new (C, TypeFunc::Parms) HaltNode( if_f, frame ); - _igvn.register_new_node_with_optimizer(halt); - set_loop(halt, l); - C->root()->add_req(halt); set_loop(C->root(), _ltree_root); } } // Weeny check for irreducible. This child was already visited (this // IS the post-work phase). Is this child's loop header post-visited // as well? If so, then I found another entry into the loop. - while( is_postvisited(l->_head) ) { - // found irreducible - l->_irreducible = 1; // = true - l = l->_parent; - _has_irreducible_loops = true; - // Check for bad CFG here to prevent crash, and bailout of compile - if (l == NULL) { - C->record_method_not_compilable("unhandled CFG detected during loop optimization"); - return pre_order; + if (!_verify_only) { + while( is_postvisited(l->_head) ) { + // found irreducible + l->_irreducible = 1; // = true + l = l->_parent; + _has_irreducible_loops = true; + // Check for bad CFG here to prevent crash, and bailout of compile + if (l == NULL) { + C->record_method_not_compilable("unhandled CFG detected during loop optimization"); + return pre_order; + } } } @@ -2253,7 +2327,7 @@ // Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. // First pass computes the earliest controlling node possible. This is the // controlling input with the deepest dominating depth. -void PhaseIdealLoop::build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ) { +void PhaseIdealLoop::build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ) { while (worklist.size() != 0) { // Use local variables nstack_top_n & nstack_top_i to cache values // on nstack's top. @@ -2285,7 +2359,7 @@ // (the old code here would yank a 2nd safepoint after seeing a // first one, even though the 1st did not dominate in the loop body // and thus could be avoided indefinitely) - if( !verify_me && ilt->_has_sfpt && n->Opcode() == Op_SafePoint && + if( !_verify_only && !_verify_me && ilt->_has_sfpt && n->Opcode() == Op_SafePoint && is_deleteable_safept(n)) { Node *in = n->in(TypeFunc::Control); lazy_replace(n,in); // Pull safepoint now @@ -2408,12 +2482,31 @@ return LCA; } -//------------------------------get_late_ctrl---------------------------------- -// Compute latest legal control. -Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) { - assert(early != NULL, "early control should not be NULL"); +bool PhaseIdealLoop::verify_dominance(Node* n, Node* use, Node* LCA, Node* early) { + 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); + if (d == C->root()) { + tty->print_cr("*** Use %d isn't dominated by def %s", use->_idx, n->_idx); + n->dump(); + use->dump(); + had_error = true; + break; + } + } + } +#endif + return had_error; +} + +Node* PhaseIdealLoop::compute_lca_of_uses(Node* n, Node* early, bool verify) { // Compute LCA over list of uses + bool had_error = false; Node *LCA = NULL; for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax && LCA != early; i++) { Node* c = n->fast_out(i); @@ -2423,15 +2516,34 @@ for( uint j=1; jreq(); j++ ) {// For all inputs if( c->in(j) == n ) { // Found matching input? Node *use = c->in(0)->in(j); + if (_verify_only && use->is_top()) continue; LCA = dom_lca_for_get_late_ctrl( LCA, use, n ); + if (verify) had_error = verify_dominance(n, use, LCA, early) || had_error; } } } else { // For CFG data-users, use is in the block just prior Node *use = has_ctrl(c) ? get_ctrl(c) : c->in(0); LCA = dom_lca_for_get_late_ctrl( LCA, use, n ); + if (verify) had_error = verify_dominance(n, use, LCA, early) || had_error; } } + assert(!had_error, "bad dominance"); + return LCA; +} + +//------------------------------get_late_ctrl---------------------------------- +// Compute latest legal control. +Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) { + assert(early != NULL, "early control should not be NULL"); + + Node* LCA = compute_lca_of_uses(n, early); +#ifdef ASSERT + if (LCA == C->root() && LCA != early) { + // def doesn't dominate uses so print some useful debugging output + compute_lca_of_uses(n, early, true); + } +#endif // if this is a load, check for anti-dependent stores // We use a conservative algorithm to identify potential interfering @@ -2576,7 +2688,7 @@ //------------------------------build_loop_late-------------------------------- // Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. // Second pass finds latest legal placement, and ideal loop placement. -void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ) { +void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ) { while (worklist.size() != 0) { Node *n = worklist.pop(); // Only visit once @@ -2612,7 +2724,7 @@ } } else { // All of n's children have been processed, complete post-processing. - build_loop_late_post(n, verify_me); + build_loop_late_post(n); if (nstack.is_empty()) { // Finished all nodes on stack. // Process next node on the worklist. @@ -2631,9 +2743,9 @@ //------------------------------build_loop_late_post--------------------------- // Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping. // Second pass finds latest legal placement, and ideal loop placement. -void PhaseIdealLoop::build_loop_late_post( Node *n, const PhaseIdealLoop *verify_me ) { +void PhaseIdealLoop::build_loop_late_post( Node *n ) { - if (n->req() == 2 && n->Opcode() == Op_ConvI2L && !C->major_progress()) { + if (n->req() == 2 && n->Opcode() == Op_ConvI2L && !C->major_progress() && !_verify_only) { _igvn._worklist.push(n); // Maybe we'll normalize it, if no more loops. } @@ -2708,12 +2820,29 @@ Node *legal = LCA; // Walk 'legal' up the IDOM chain Node *least = legal; // Best legal position so far while( early != legal ) { // While not at earliest legal +#ifdef ASSERT + if (legal->is_Start() && !early->is_Root()) { + // Bad graph. Print idom path and fail. + tty->print_cr( "Bad graph detected in build_loop_late"); + tty->print("n: ");n->dump(); tty->cr(); + tty->print("early: ");early->dump(); tty->cr(); + int ct = 0; + Node *dbg_legal = LCA; + while(!dbg_legal->is_Start() && ct < 100) { + tty->print("idom[%d] ",ct); dbg_legal->dump(); tty->cr(); + ct++; + dbg_legal = idom(dbg_legal); + } + assert(false, "Bad graph detected in build_loop_late"); + } +#endif // Find least loop nesting depth legal = idom(legal); // Bump up the IDOM tree // Check for lower nesting depth if( get_loop(legal)->_nest < get_loop(least)->_nest ) least = legal; } + assert(early == legal || legal != C->root(), "bad dominance of inputs"); // Try not to place code on a loop entry projection // which can inhibit range check elimination. @@ -2731,8 +2860,8 @@ #ifdef ASSERT // If verifying, verify that 'verify_me' has a legal location // and choose it as our location. - if( verify_me ) { - Node *v_ctrl = verify_me->get_ctrl_no_update(n); + if( _verify_me ) { + Node *v_ctrl = _verify_me->get_ctrl_no_update(n); Node *legal = LCA; while( early != legal ) { // While not at earliest legal if( legal == v_ctrl ) break; // Check for prior good location diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/loopnode.hpp --- a/src/share/vm/opto/loopnode.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/loopnode.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 @@ -30,6 +30,7 @@ class Node; class PhaseIdealLoop; class VectorSet; +class Invariance; struct small_cache; // @@ -325,6 +326,10 @@ // Returns TRUE if loop tree is structurally changed. bool beautify_loops( PhaseIdealLoop *phase ); + // Perform optimization to use the loop predicates for null checks and range checks. + // Applies to any loop level (not just the innermost one) + bool loop_predication( PhaseIdealLoop *phase); + // Perform iteration-splitting on inner loops. Split iterations to // avoid range checks or one-shot null checks. Returns false if the // current round of loop opts should stop. @@ -395,6 +400,9 @@ // into longer memory ops, we may want to increase alignment. bool policy_align( PhaseIdealLoop *phase ) const; + // Return TRUE if "iff" is a range check. + bool is_range_check_if(IfNode *iff, PhaseIdealLoop *phase, Invariance& invar) const; + // Compute loop trip count from profile data void compute_profile_trip_cnt( PhaseIdealLoop *phase ); @@ -442,6 +450,9 @@ uint *_preorders; uint _max_preorder; + const PhaseIdealLoop* _verify_me; + bool _verify_only; + // Allocate _preorders[] array void allocate_preorders() { _max_preorder = C->unique()+8; @@ -497,6 +508,12 @@ Node_Array _dom_lca_tags; void init_dom_lca_tags(); void clear_dom_lca_tags(); + + // Helper for debugging bad dominance relationships + bool verify_dominance(Node* n, Node* use, Node* LCA, Node* early); + + Node* compute_lca_of_uses(Node* n, Node* early, bool verify = false); + // Inline wrapper for frequent cases: // 1) only one use // 2) a use is the same as the current LCA passed as 'n1' @@ -511,8 +528,6 @@ return find_non_split_ctrl(n); } Node *dom_lca_for_get_late_ctrl_internal( Node *lca, Node *n, Node *tag ); - // true if CFG node d dominates CFG node n - bool is_dominator(Node *d, Node *n); // Helper function for directing control inputs away from CFG split // points. @@ -562,6 +577,17 @@ assert(n == find_non_split_ctrl(n), "must return legal ctrl" ); return n; } + // true if CFG node d dominates CFG node n + bool is_dominator(Node *d, Node *n); + // return get_ctrl for a data node and self(n) for a CFG node + Node* ctrl_or_self(Node* n) { + if (has_ctrl(n)) + return get_ctrl(n); + else { + assert (n->is_CFG(), "must be a CFG node"); + return n; + } + } private: Node *get_ctrl_no_update( Node *i ) const { @@ -590,7 +616,7 @@ // Lazy-dazy update of 'get_ctrl' and 'idom_at' mechanisms. Replace // the 'old_node' with 'new_node'. Kill old-node. Add a reference // from old_node to new_node to support the lazy update. Reference - // replaces loop reference, since that is not neede for dead node. + // replaces loop reference, since that is not needed for dead node. public: void lazy_update( Node *old_node, Node *new_node ) { assert( old_node != new_node, "no cycles please" ); @@ -621,9 +647,9 @@ IdealLoopTree *sort( IdealLoopTree *loop, IdealLoopTree *innermost ); // Place Data nodes in some loop nest - void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ); - void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack, const PhaseIdealLoop *verify_me ); - void build_loop_late_post ( Node* n, const PhaseIdealLoop *verify_me ); + void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ); + void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack ); + void build_loop_late_post ( Node* n ); // Array of immediate dominance info for each CFG node indexed by node idx private: @@ -662,6 +688,19 @@ // Is safept not required by an outer loop? bool is_deleteable_safept(Node* sfpt); + // Perform verification that the graph is valid. + PhaseIdealLoop( PhaseIterGVN &igvn) : + PhaseTransform(Ideal_Loop), + _igvn(igvn), + _dom_lca_tags(C->comp_arena()), + _verify_me(NULL), + _verify_only(true) { + build_and_optimize(false, false); + } + + // build the loop tree and perform any requested optimizations + void build_and_optimize(bool do_split_if, bool do_loop_pred); + public: // Dominators for the sea of nodes void Dominators(); @@ -671,7 +710,32 @@ Node *dom_lca_internal( Node *n1, Node *n2 ) const; // Compute the Ideal Node to Loop mapping - PhaseIdealLoop( PhaseIterGVN &igvn, const PhaseIdealLoop *verify_me, bool do_split_ifs ); + PhaseIdealLoop( PhaseIterGVN &igvn, bool do_split_ifs, bool do_loop_pred) : + PhaseTransform(Ideal_Loop), + _igvn(igvn), + _dom_lca_tags(C->comp_arena()), + _verify_me(NULL), + _verify_only(false) { + build_and_optimize(do_split_ifs, do_loop_pred); + } + + // Verify that verify_me made the same decisions as a fresh run. + PhaseIdealLoop( PhaseIterGVN &igvn, const PhaseIdealLoop *verify_me) : + PhaseTransform(Ideal_Loop), + _igvn(igvn), + _dom_lca_tags(C->comp_arena()), + _verify_me(verify_me), + _verify_only(false) { + build_and_optimize(false, false); + } + + // Build and verify the loop tree without modifying the graph. This + // is useful to verify that all inputs properly dominate their uses. + static void verify(PhaseIterGVN& igvn) { +#ifdef ASSERT + PhaseIdealLoop v(igvn); +#endif + } // True if the method has at least 1 irreducible loop bool _has_irreducible_loops; @@ -742,6 +806,30 @@ // Return true if exp is a scaled induction var plus (or minus) constant bool is_scaled_iv_plus_offset(Node* exp, Node* iv, int* p_scale, Node** p_offset, int depth = 0); + // Return true if proj is for "proj->[region->..]call_uct" + bool is_uncommon_trap_proj(ProjNode* proj, bool must_reason_predicate = false); + // Return true for "if(test)-> proj -> ... + // | + // V + // other_proj->[region->..]call_uct" + bool is_uncommon_trap_if_pattern(ProjNode* proj, bool must_reason_predicate = false); + // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted + ProjNode* create_new_if_for_predicate(ProjNode* cont_proj); + // Find a good location to insert a predicate + ProjNode* find_predicate_insertion_point(Node* start_c); + // Construct a range check for a predicate if + BoolNode* rc_predicate(Node* ctrl, + int scale, Node* offset, + Node* init, Node* limit, Node* stride, + Node* range); + + // Implementation of the loop predication to promote checks outside the loop + bool loop_predication_impl(IdealLoopTree *loop); + + // Helper function to collect predicate for eliminating the useless ones + void collect_potentially_useful_predicates(IdealLoopTree *loop, Unique_Node_List &predicate_opaque1); + void eliminate_useless_predicates(); + // Eliminate range-checks and other trip-counter vs loop-invariant tests. void do_range_check( IdealLoopTree *loop, Node_List &old_new ); @@ -858,7 +946,6 @@ const TypeInt* filtered_type_from_dominators( Node* val, Node *val_ctrl); // Helper functions - void register_new_node( Node *n, Node *blk ); Node *spinup( Node *iff, Node *new_false, Node *new_true, Node *region, Node *phi, small_cache *cache ); Node *find_use_block( Node *use, Node *def, Node *old_false, Node *new_false, Node *old_true, Node *new_true ); void handle_use( Node *use, Node *def, small_cache *cache, Node *region_dom, Node *new_false, Node *new_true, Node *old_false, Node *old_true ); @@ -870,6 +957,7 @@ public: void set_created_loop_node() { _created_loop_node = true; } bool created_loop_node() { return _created_loop_node; } + void register_new_node( Node *n, Node *blk ); #ifndef PRODUCT void dump( ) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/loopopts.cpp --- a/src/share/vm/opto/loopopts.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/loopopts.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -47,7 +47,7 @@ int offset = t_oop->offset(); phi = new (C,region->req()) PhiNode(region, type, NULL, iid, index, offset); } else { - phi = new (C,region->req()) PhiNode(region, type); + phi = PhiNode::make_blank(region, n); } uint old_unique = C->unique(); for( uint i = 1; i < region->req(); i++ ) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/machnode.cpp --- a/src/share/vm/opto/machnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/machnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -636,7 +636,9 @@ } #ifndef PRODUCT void MachCallJavaNode::dump_spec(outputStream *st) const { - if( _method ) { + if (_method_handle_invoke) + st->print("MethodHandle "); + if (_method) { _method->print_short_name(st); st->print(" "); } @@ -644,6 +646,20 @@ } #endif +//------------------------------Registers-------------------------------------- +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]; + // 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 + // debugmask which does not include the register we use to save the + // SP over MH invokes. + RegMask** debugmask = _method_handle_invoke ? m->idealreg2mhdebugmask : m->idealreg2debugmask; + return *debugmask[in(idx)->ideal_reg()]; +} + //============================================================================= uint MachCallStaticJavaNode::size_of() const { return sizeof(*this); } uint MachCallStaticJavaNode::cmp( const Node &n ) const { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/machnode.hpp --- a/src/share/vm/opto/machnode.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/machnode.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -232,7 +232,7 @@ // Expand method for MachNode, replaces nodes representing pseudo // instructions with a set of nodes which represent real machine // instructions and compute the same value. - virtual MachNode *Expand( State *, Node_List &proj_list ) { return this; } + virtual MachNode *Expand( State *, Node_List &proj_list, Node* mem ) { return this; } // Bottom_type call; value comes from operand0 virtual const class Type *bottom_type() const { return _opnds[0]->type(); } @@ -662,9 +662,13 @@ ciMethod* _method; // Method being direct called int _bci; // Byte Code index of call byte code bool _optimized_virtual; // Tells if node is a static call or an optimized virtual + bool _method_handle_invoke; // Tells if the call has to preserve SP MachCallJavaNode() : MachCallNode() { init_class_id(Class_MachCallJava); } + + virtual const RegMask &in_RegMask(uint) const; + #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/macro.cpp --- a/src/share/vm/opto/macro.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/macro.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -316,6 +316,21 @@ assert(adr_idx == Compile::AliasIdxRaw, "address must match or be raw"); } mem = mem->in(MemNode::Memory); + } else if (mem->is_ClearArray()) { + if (!ClearArrayNode::step_through(&mem, alloc->_idx, phase)) { + // Can not bypass initialization of the instance + // we are looking. + debug_only(intptr_t offset;) + assert(alloc == AllocateNode::Ideal_allocation(mem->in(3), phase, offset), "sanity"); + InitializeNode* init = alloc->as_Allocate()->initialization(); + // We are looking for stored value, return Initialize node + // or memory edge from Allocate node. + if (init != NULL) + return init; + else + return alloc->in(TypeFunc::Memory); // It will produce zero value (see callers). + } + // Otherwise skip it (the call updated 'mem' value). } else if (mem->Opcode() == Op_SCMemProj) { assert(mem->in(0)->is_LoadStore(), "sanity"); const TypePtr* atype = mem->in(0)->in(MemNode::Address)->bottom_type()->is_ptr(); @@ -823,6 +838,18 @@ Node *n = use->last_out(k); uint oc2 = use->outcnt(); if (n->is_Store()) { +#ifdef ASSERT + // Verify that there is no dependent MemBarVolatile nodes, + // they should be removed during IGVN, see MemBarNode::Ideal(). + for (DUIterator_Fast pmax, p = n->fast_outs(pmax); + p < pmax; p++) { + Node* mb = n->fast_out(p); + assert(mb->is_Initialize() || !mb->is_MemBar() || + mb->req() <= MemBarNode::Precedent || + mb->in(MemBarNode::Precedent) != n, + "MemBarVolatile should be eliminated for non-escaping object"); + } +#endif _igvn.replace_node(n, n->in(MemNode::Memory)); } else { eliminate_card_mark(n); @@ -912,15 +939,29 @@ return false; } + CompileLog* log = C->log(); + if (log != NULL) { + Node* klass = alloc->in(AllocateNode::KlassNode); + const TypeKlassPtr* tklass = _igvn.type(klass)->is_klassptr(); + log->head("eliminate_allocation type='%d'", + log->identify(tklass->klass())); + JVMState* p = alloc->jvms(); + while (p != NULL) { + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); + p = p->caller(); + } + log->tail("eliminate_allocation"); + } + process_users_of_allocation(alloc); #ifndef PRODUCT -if (PrintEliminateAllocations) { - if (alloc->is_AllocateArray()) - tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx); - else - tty->print_cr("++++ Eliminated: %d Allocate", alloc->_idx); -} + if (PrintEliminateAllocations) { + if (alloc->is_AllocateArray()) + tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx); + else + tty->print_cr("++++ Eliminated: %d Allocate", alloc->_idx); + } #endif return true; @@ -1639,6 +1680,18 @@ } // if (!oldbox->is_eliminated()) } // if (alock->is_Lock() && !lock->is_coarsened()) + CompileLog* log = C->log(); + if (log != NULL) { + log->head("eliminate_lock lock='%d'", + alock->is_Lock()); + JVMState* p = alock->jvms(); + while (p != NULL) { + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); + p = p->caller(); + } + log->tail("eliminate_lock"); + } + #ifndef PRODUCT if (PrintEliminateLocks) { if (alock->is_Lock()) { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/matcher.cpp --- a/src/share/vm/opto/matcher.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/matcher.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -70,19 +70,27 @@ _dontcare(&_states_arena) { C->set_matcher(this); - idealreg2spillmask[Op_RegI] = NULL; - idealreg2spillmask[Op_RegN] = NULL; - idealreg2spillmask[Op_RegL] = NULL; - idealreg2spillmask[Op_RegF] = NULL; - idealreg2spillmask[Op_RegD] = NULL; - idealreg2spillmask[Op_RegP] = NULL; + idealreg2spillmask [Op_RegI] = NULL; + idealreg2spillmask [Op_RegN] = NULL; + idealreg2spillmask [Op_RegL] = NULL; + idealreg2spillmask [Op_RegF] = NULL; + idealreg2spillmask [Op_RegD] = NULL; + idealreg2spillmask [Op_RegP] = NULL; - idealreg2debugmask[Op_RegI] = NULL; - idealreg2debugmask[Op_RegN] = NULL; - idealreg2debugmask[Op_RegL] = NULL; - idealreg2debugmask[Op_RegF] = NULL; - idealreg2debugmask[Op_RegD] = NULL; - idealreg2debugmask[Op_RegP] = NULL; + idealreg2debugmask [Op_RegI] = NULL; + idealreg2debugmask [Op_RegN] = NULL; + idealreg2debugmask [Op_RegL] = NULL; + idealreg2debugmask [Op_RegF] = NULL; + idealreg2debugmask [Op_RegD] = NULL; + idealreg2debugmask [Op_RegP] = NULL; + + idealreg2mhdebugmask[Op_RegI] = NULL; + idealreg2mhdebugmask[Op_RegN] = NULL; + idealreg2mhdebugmask[Op_RegL] = NULL; + idealreg2mhdebugmask[Op_RegF] = NULL; + idealreg2mhdebugmask[Op_RegD] = NULL; + idealreg2mhdebugmask[Op_RegP] = NULL; + debug_only(_mem_node = NULL;) // Ideal memory node consumed by mach node } @@ -389,19 +397,28 @@ void Matcher::init_first_stack_mask() { // Allocate storage for spill masks as masks for the appropriate load type. - RegMask *rms = (RegMask*)C->comp_arena()->Amalloc_D(sizeof(RegMask)*12); - idealreg2spillmask[Op_RegN] = &rms[0]; - idealreg2spillmask[Op_RegI] = &rms[1]; - idealreg2spillmask[Op_RegL] = &rms[2]; - idealreg2spillmask[Op_RegF] = &rms[3]; - idealreg2spillmask[Op_RegD] = &rms[4]; - idealreg2spillmask[Op_RegP] = &rms[5]; - idealreg2debugmask[Op_RegN] = &rms[6]; - idealreg2debugmask[Op_RegI] = &rms[7]; - idealreg2debugmask[Op_RegL] = &rms[8]; - idealreg2debugmask[Op_RegF] = &rms[9]; - idealreg2debugmask[Op_RegD] = &rms[10]; - idealreg2debugmask[Op_RegP] = &rms[11]; + RegMask *rms = (RegMask*)C->comp_arena()->Amalloc_D(sizeof(RegMask) * 3*6); + + idealreg2spillmask [Op_RegN] = &rms[0]; + idealreg2spillmask [Op_RegI] = &rms[1]; + idealreg2spillmask [Op_RegL] = &rms[2]; + idealreg2spillmask [Op_RegF] = &rms[3]; + idealreg2spillmask [Op_RegD] = &rms[4]; + idealreg2spillmask [Op_RegP] = &rms[5]; + + idealreg2debugmask [Op_RegN] = &rms[6]; + idealreg2debugmask [Op_RegI] = &rms[7]; + idealreg2debugmask [Op_RegL] = &rms[8]; + idealreg2debugmask [Op_RegF] = &rms[9]; + idealreg2debugmask [Op_RegD] = &rms[10]; + idealreg2debugmask [Op_RegP] = &rms[11]; + + idealreg2mhdebugmask[Op_RegN] = &rms[12]; + idealreg2mhdebugmask[Op_RegI] = &rms[13]; + idealreg2mhdebugmask[Op_RegL] = &rms[14]; + idealreg2mhdebugmask[Op_RegF] = &rms[15]; + idealreg2mhdebugmask[Op_RegD] = &rms[16]; + idealreg2mhdebugmask[Op_RegP] = &rms[17]; OptoReg::Name i; @@ -442,12 +459,19 @@ // Make up debug masks. Any spill slot plus callee-save registers. // Caller-save registers are assumed to be trashable by the various // inline-cache fixup routines. - *idealreg2debugmask[Op_RegN]= *idealreg2spillmask[Op_RegN]; - *idealreg2debugmask[Op_RegI]= *idealreg2spillmask[Op_RegI]; - *idealreg2debugmask[Op_RegL]= *idealreg2spillmask[Op_RegL]; - *idealreg2debugmask[Op_RegF]= *idealreg2spillmask[Op_RegF]; - *idealreg2debugmask[Op_RegD]= *idealreg2spillmask[Op_RegD]; - *idealreg2debugmask[Op_RegP]= *idealreg2spillmask[Op_RegP]; + *idealreg2debugmask [Op_RegN]= *idealreg2spillmask[Op_RegN]; + *idealreg2debugmask [Op_RegI]= *idealreg2spillmask[Op_RegI]; + *idealreg2debugmask [Op_RegL]= *idealreg2spillmask[Op_RegL]; + *idealreg2debugmask [Op_RegF]= *idealreg2spillmask[Op_RegF]; + *idealreg2debugmask [Op_RegD]= *idealreg2spillmask[Op_RegD]; + *idealreg2debugmask [Op_RegP]= *idealreg2spillmask[Op_RegP]; + + *idealreg2mhdebugmask[Op_RegN]= *idealreg2spillmask[Op_RegN]; + *idealreg2mhdebugmask[Op_RegI]= *idealreg2spillmask[Op_RegI]; + *idealreg2mhdebugmask[Op_RegL]= *idealreg2spillmask[Op_RegL]; + *idealreg2mhdebugmask[Op_RegF]= *idealreg2spillmask[Op_RegF]; + *idealreg2mhdebugmask[Op_RegD]= *idealreg2spillmask[Op_RegD]; + *idealreg2mhdebugmask[Op_RegP]= *idealreg2spillmask[Op_RegP]; // Prevent stub compilations from attempting to reference // callee-saved registers from debug info @@ -458,14 +482,31 @@ if( _register_save_policy[i] == 'C' || _register_save_policy[i] == 'A' || (_register_save_policy[i] == 'E' && exclude_soe) ) { - idealreg2debugmask[Op_RegN]->Remove(i); - idealreg2debugmask[Op_RegI]->Remove(i); // Exclude save-on-call - idealreg2debugmask[Op_RegL]->Remove(i); // registers from debug - idealreg2debugmask[Op_RegF]->Remove(i); // masks - idealreg2debugmask[Op_RegD]->Remove(i); - idealreg2debugmask[Op_RegP]->Remove(i); + idealreg2debugmask [Op_RegN]->Remove(i); + idealreg2debugmask [Op_RegI]->Remove(i); // Exclude save-on-call + idealreg2debugmask [Op_RegL]->Remove(i); // registers from debug + idealreg2debugmask [Op_RegF]->Remove(i); // masks + idealreg2debugmask [Op_RegD]->Remove(i); + idealreg2debugmask [Op_RegP]->Remove(i); + + idealreg2mhdebugmask[Op_RegN]->Remove(i); + idealreg2mhdebugmask[Op_RegI]->Remove(i); + idealreg2mhdebugmask[Op_RegL]->Remove(i); + idealreg2mhdebugmask[Op_RegF]->Remove(i); + idealreg2mhdebugmask[Op_RegD]->Remove(i); + idealreg2mhdebugmask[Op_RegP]->Remove(i); } } + + // Subtract the register we use to save the SP for MethodHandle + // invokes to from the debug mask. + const RegMask save_mask = method_handle_invoke_SP_save_mask(); + idealreg2mhdebugmask[Op_RegN]->SUBTRACT(save_mask); + idealreg2mhdebugmask[Op_RegI]->SUBTRACT(save_mask); + idealreg2mhdebugmask[Op_RegL]->SUBTRACT(save_mask); + idealreg2mhdebugmask[Op_RegF]->SUBTRACT(save_mask); + idealreg2mhdebugmask[Op_RegD]->SUBTRACT(save_mask); + idealreg2mhdebugmask[Op_RegP]->SUBTRACT(save_mask); } //---------------------------is_save_on_entry---------------------------------- @@ -989,6 +1030,7 @@ CallNode *call; const TypeTuple *domain; ciMethod* method = NULL; + bool is_method_handle_invoke = false; // for special kill effects if( sfpt->is_Call() ) { call = sfpt->as_Call(); domain = call->tf()->domain(); @@ -1013,6 +1055,8 @@ mcall_java->_method = method; mcall_java->_bci = call_java->_bci; mcall_java->_optimized_virtual = call_java->is_optimized_virtual(); + is_method_handle_invoke = call_java->is_method_handle_invoke(); + mcall_java->_method_handle_invoke = is_method_handle_invoke; if( mcall_java->is_MachCallStaticJava() ) mcall_java->as_MachCallStaticJava()->_name = call_java->as_CallStaticJava()->_name; @@ -1126,6 +1170,15 @@ mcall->_argsize = out_arg_limit_per_call - begin_out_arg_area; } + if (is_method_handle_invoke) { + // Kill some extra stack space in case method handles want to do + // a little in-place argument insertion. + int regs_per_word = NOT_LP64(1) LP64_ONLY(2); // %%% make a global const! + out_arg_limit_per_call += MethodHandlePushLimit * regs_per_word; + // Do not update mcall->_argsize because (a) the extra space is not + // pushed as arguments and (b) _argsize is dead (not used anywhere). + } + // Compute the max stack slot killed by any call. These will not be // available for debug info, and will be used to adjust FIRST_STACK_mask // after all call sites have been visited. @@ -1527,7 +1580,7 @@ uint num_proj = _proj_list.size(); // Perform any 1-to-many expansions required - MachNode *ex = mach->Expand(s,_proj_list); + MachNode *ex = mach->Expand(s,_proj_list, mem); if( ex != mach ) { assert(ex->ideal_reg() == mach->ideal_reg(), "ideal types should match"); if( ex->in(1)->is_Con() ) @@ -1832,67 +1885,23 @@ case Op_Binary: // These are introduced in the Post_Visit state. ShouldNotReachHere(); break; - case Op_StoreB: // Do match these, despite no ideal reg - case Op_StoreC: - case Op_StoreCM: - case Op_StoreD: - case Op_StoreF: - case Op_StoreI: - case Op_StoreL: - case Op_StoreP: - case Op_StoreN: - case Op_Store16B: - case Op_Store8B: - case Op_Store4B: - case Op_Store8C: - case Op_Store4C: - case Op_Store2C: - case Op_Store4I: - case Op_Store2I: - case Op_Store2L: - case Op_Store4F: - case Op_Store2F: - case Op_Store2D: case Op_ClearArray: case Op_SafePoint: mem_op = true; break; - case Op_LoadB: - case Op_LoadUS: - case Op_LoadD: - case Op_LoadF: - case Op_LoadI: - case Op_LoadKlass: - case Op_LoadNKlass: - case Op_LoadL: - case Op_LoadS: - case Op_LoadP: - case Op_LoadN: - case Op_LoadRange: - case Op_LoadD_unaligned: - case Op_LoadL_unaligned: - case Op_Load16B: - case Op_Load8B: - case Op_Load4B: - case Op_Load4C: - case Op_Load2C: - case Op_Load8C: - case Op_Load8S: - case Op_Load4S: - case Op_Load2S: - case Op_Load4I: - case Op_Load2I: - case Op_Load2L: - case Op_Load4F: - case Op_Load2F: - case Op_Load2D: - mem_op = true; - // Must be root of match tree due to prior load conflict - if( C->subsume_loads() == false ) { - set_shared(n); + default: + if( n->is_Store() ) { + // Do match stores, despite no ideal reg + mem_op = true; + break; + } + if( n->is_Mem() ) { // Loads and LoadStores + mem_op = true; + // Loads must be root of match tree due to prior load conflict + if( C->subsume_loads() == false ) + set_shared(n); } // Fall into default case - default: if( !n->ideal_reg() ) set_dontcare(n); // Unmatchable Nodes } // end_switch @@ -1913,15 +1922,15 @@ continue; // for(int i = ...) } - // Clone addressing expressions as they are "free" in most instructions + if( mop == Op_AddP && m->in(AddPNode::Base)->Opcode() == Op_DecodeN ) { + // Bases used in addresses must be shared but since + // they are shared through a DecodeN they may appear + // to have a single use so force sharing here. + set_shared(m->in(AddPNode::Base)->in(1)); + } + + // Clone addressing expressions as they are "free" in memory access instructions if( mem_op && i == MemNode::Address && mop == Op_AddP ) { - if (m->in(AddPNode::Base)->Opcode() == Op_DecodeN) { - // Bases used in addresses must be shared but since - // they are shared through a DecodeN they may appear - // to have a single use so force sharing here. - set_shared(m->in(AddPNode::Base)->in(1)); - } - // Some inputs for address expression are not put on stack // to avoid marking them as shared and forcing them into register // if they are used only in address expressions. @@ -2032,6 +2041,23 @@ n->del_req(3); break; } + case Op_StrEquals: { + Node *pair1 = new (C, 3) BinaryNode(n->in(2),n->in(3)); + n->set_req(2,pair1); + n->set_req(3,n->in(4)); + n->del_req(4); + break; + } + case Op_StrComp: + case Op_StrIndexOf: { + Node *pair1 = new (C, 3) BinaryNode(n->in(2),n->in(3)); + n->set_req(2,pair1); + Node *pair2 = new (C, 3) BinaryNode(n->in(4),n->in(5)); + n->set_req(3,pair2); + n->del_req(5); + n->del_req(4); + break; + } default: break; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/matcher.hpp --- a/src/share/vm/opto/matcher.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/matcher.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -117,8 +117,9 @@ static const int base2reg[]; // Map Types to machine register types // Convert ideal machine register to a register mask for spill-loads static const RegMask *idealreg2regmask[]; - RegMask *idealreg2spillmask[_last_machine_leaf]; - RegMask *idealreg2debugmask[_last_machine_leaf]; + RegMask *idealreg2spillmask [_last_machine_leaf]; + RegMask *idealreg2debugmask [_last_machine_leaf]; + RegMask *idealreg2mhdebugmask[_last_machine_leaf]; void init_spill_mask( Node *ret ); // Convert machine register number to register mask static uint mreg2regmask_max; @@ -297,6 +298,8 @@ // Register for MODL projection of divmodL static RegMask modL_proj_mask(); + static const RegMask method_handle_invoke_SP_save_mask(); + // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java @@ -370,8 +373,8 @@ // to implement the UseStrictFP mode. static const bool strict_fp_requires_explicit_rounding; - // Do floats take an entire double register or just half? - static const bool float_in_double; + // Are floats conerted to double when stored to stack during deoptimization? + static bool float_in_double(); // Do ints take an entire long register or just half? static const bool int_in_long; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/memnode.cpp --- a/src/share/vm/opto/memnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/memnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -123,6 +123,13 @@ } else { assert(false, "unexpected projection"); } + } else if (result->is_ClearArray()) { + if (!ClearArrayNode::step_through(&result, instance_id, phase)) { + // Can not bypass initialization of the instance + // we are looking for. + break; + } + // Otherwise skip it (the call updated 'result' value). } else if (result->is_MergeMem()) { result = step_through_mergemem(phase, result->as_MergeMem(), t_adr, NULL, tty); } @@ -255,6 +262,13 @@ return NodeSentinel; // caller will return NULL } + // Do NOT remove or optimize the next lines: ensure a new alias index + // is allocated for an oop pointer type before Escape Analysis. + // Note: C++ will not remove it since the call has side effect. + if ( t_adr->isa_oopptr() ) { + int alias_idx = phase->C->get_alias_index(t_adr->is_ptr()); + } + #ifdef ASSERT Node* base = NULL; if (address->is_AddP()) @@ -530,6 +544,15 @@ } else if (mem->is_Proj() && mem->in(0)->is_MemBar()) { mem = mem->in(0)->in(TypeFunc::Memory); continue; // (a) advance through independent MemBar memory + } else if (mem->is_ClearArray()) { + if (ClearArrayNode::step_through(&mem, (uint)addr_t->instance_id(), phase)) { + // (the call updated 'mem' value) + continue; // (a) advance through independent allocation memory + } else { + // Can not bypass initialization of the instance + // we are looking for. + return mem; + } } else if (mem->is_MergeMem()) { int alias_idx = phase->C->get_alias_index(adr_type()); mem = mem->as_MergeMem()->memory_at(alias_idx); @@ -1496,6 +1519,8 @@ } } } else if (tp->base() == Type::InstPtr) { + const TypeInstPtr* tinst = tp->is_instptr(); + ciKlass* klass = tinst->klass(); assert( off != Type::OffsetBot || // arrays can be cast to Objects tp->is_oopptr()->klass()->is_java_lang_Object() || @@ -1503,6 +1528,25 @@ phase->C->has_unsafe_access(), "Field accesses must be precise" ); // For oop loads, we expect the _type to be precise + if (OptimizeStringConcat && klass == phase->C->env()->String_klass() && + adr->is_AddP() && off != Type::OffsetBot) { + // For constant Strings treat the fields as compile time constants. + Node* base = adr->in(AddPNode::Base); + if (base->Opcode() == Op_ConP) { + const TypeOopPtr* t = phase->type(base)->isa_oopptr(); + ciObject* string = t->const_oop(); + ciConstant constant = string->as_instance()->field_value_by_offset(off); + if (constant.basic_type() == T_INT) { + return TypeInt::make(constant.as_int()); + } else if (constant.basic_type() == T_ARRAY) { + if (adr->bottom_type()->is_ptr_to_narrowoop()) { + return TypeNarrowOop::make_from_constant(constant.as_object()); + } else { + return TypeOopPtr::make_from_constant(constant.as_object()); + } + } + } + } } else if (tp->base() == Type::KlassPtr) { assert( off != Type::OffsetBot || // arrays can be cast to Objects @@ -2313,6 +2357,22 @@ return this; } +//============================================================================= +//------------------------------Ideal--------------------------------------- +Node *StoreCMNode::Ideal(PhaseGVN *phase, bool can_reshape){ + Node* progress = StoreNode::Ideal(phase, can_reshape); + if (progress != NULL) return progress; + + Node* my_store = in(MemNode::OopStore); + if (my_store->is_MergeMem()) { + Node* mem = my_store->as_MergeMem()->memory_at(oop_alias_idx()); + set_req(MemNode::OopStore, mem); + return this; + } + + return NULL; +} + //------------------------------Value----------------------------------------- const Type *StoreCMNode::Value( PhaseTransform *phase ) const { // Either input is TOP ==> the result is TOP @@ -2410,6 +2470,31 @@ return mem; } +//----------------------------step_through---------------------------------- +// Return allocation input memory edge if it is different instance +// or itself if it is the one we are looking for. +bool ClearArrayNode::step_through(Node** np, uint instance_id, PhaseTransform* phase) { + Node* n = *np; + assert(n->is_ClearArray(), "sanity"); + intptr_t offset; + AllocateNode* alloc = AllocateNode::Ideal_allocation(n->in(3), phase, offset); + // This method is called only before Allocate nodes are expanded during + // macro nodes expansion. Before that ClearArray nodes are only generated + // in LibraryCallKit::generate_arraycopy() which follows allocations. + assert(alloc != NULL, "should have allocation"); + if (alloc->_idx == instance_id) { + // Can not bypass initialization of the instance we are looking for. + return false; + } + // Otherwise skip it. + InitializeNode* init = alloc->initialization(); + if (init != NULL) + *np = init->in(TypeFunc::Memory); + else + *np = alloc->in(TypeFunc::Memory); + return true; +} + //----------------------------clear_memory------------------------------------- // Generate code to initialize object storage to zero. Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, @@ -2498,7 +2583,7 @@ //============================================================================= // Do we match on this edge? No memory edges uint StrCompNode::match_edge(uint idx) const { - return idx == 5 || idx == 6; + return idx == 2 || idx == 3; // StrComp (Binary str1 cnt1) (Binary str2 cnt2) } //------------------------------Ideal------------------------------------------ @@ -2508,9 +2593,10 @@ return remove_dead_region(phase, can_reshape) ? this : NULL; } +//============================================================================= // Do we match on this edge? No memory edges uint StrEqualsNode::match_edge(uint idx) const { - return idx == 5 || idx == 6; + return idx == 2 || idx == 3; // StrEquals (Binary str1 str2) cnt } //------------------------------Ideal------------------------------------------ @@ -2523,7 +2609,7 @@ //============================================================================= // Do we match on this edge? No memory edges uint StrIndexOfNode::match_edge(uint idx) const { - return idx == 5 || idx == 6; + return idx == 2 || idx == 3; // StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2) } //------------------------------Ideal------------------------------------------ @@ -2533,6 +2619,11 @@ return remove_dead_region(phase, can_reshape) ? this : NULL; } +//============================================================================= +// Do we match on this edge? No memory edges +uint AryEqNode::match_edge(uint idx) const { + return idx == 2 || idx == 3; // StrEquals ary1 ary2 +} //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies @@ -2577,7 +2668,30 @@ // Return a node which is more "ideal" than the current node. Strip out // control copies Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { - return remove_dead_region(phase, can_reshape) ? this : NULL; + if (remove_dead_region(phase, can_reshape)) return this; + + // Eliminate volatile MemBars for scalar replaced objects. + if (can_reshape && req() == (Precedent+1) && + (Opcode() == Op_MemBarAcquire || Opcode() == Op_MemBarVolatile)) { + // Volatile field loads and stores. + Node* my_mem = in(MemBarNode::Precedent); + if (my_mem != NULL && my_mem->is_Mem()) { + const TypeOopPtr* t_oop = my_mem->in(MemNode::Address)->bottom_type()->isa_oopptr(); + // Check for scalar replaced object reference. + if( t_oop != NULL && t_oop->is_known_instance_field() && + t_oop->offset() != Type::OffsetBot && + t_oop->offset() != Type::OffsetTop) { + // Replace MemBar projections by its inputs. + PhaseIterGVN* igvn = phase->is_IterGVN(); + igvn->replace_node(proj_out(TypeFunc::Memory), in(TypeFunc::Memory)); + igvn->replace_node(proj_out(TypeFunc::Control), in(TypeFunc::Control)); + // Must return either the original node (now dead) or a new node + // (Do not return a top here, since that would break the uniqueness of top.) + return new (phase->C, 1) ConINode(TypeInt::ZERO); + } + } + } + return NULL; } //------------------------------Value------------------------------------------ diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/memnode.hpp --- a/src/share/vm/opto/memnode.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/memnode.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -582,12 +582,29 @@ // The last StoreCM before a SafePoint must be preserved and occur after its "oop" store // Preceeding equivalent StoreCMs may be eliminated. class StoreCMNode : public StoreNode { + private: + virtual uint hash() const { return StoreNode::hash() + _oop_alias_idx; } + virtual uint cmp( const Node &n ) const { + return _oop_alias_idx == ((StoreCMNode&)n)._oop_alias_idx + && StoreNode::cmp(n); + } + virtual uint size_of() const { return sizeof(*this); } + int _oop_alias_idx; // The alias_idx of OopStore + public: - StoreCMNode( Node *c, Node *mem, Node *adr, const TypePtr* at, Node *val, Node *oop_store ) : StoreNode(c,mem,adr,at,val,oop_store) {} + 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), + _oop_alias_idx(oop_alias_idx) { + assert(_oop_alias_idx >= Compile::AliasIdxRaw || + _oop_alias_idx == Compile::AliasIdxBot && Compile::current()->AliasLevel() == 0, + "bad oop alias idx"); + } virtual int Opcode() const; virtual Node *Identity( PhaseTransform *phase ); + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *Value( PhaseTransform *phase ) const; virtual BasicType memory_type() const { return T_VOID; } // unspecific + int oop_alias_idx() const { return _oop_alias_idx; } }; //------------------------------LoadPLockedNode--------------------------------- @@ -713,7 +730,10 @@ //------------------------------ClearArray------------------------------------- class ClearArrayNode: public Node { public: - ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base ) : Node(ctrl,arymem,word_cnt,base) {} + ClearArrayNode( Node *ctrl, Node *arymem, Node *word_cnt, Node *base ) + : Node(ctrl,arymem,word_cnt,base) { + init_class_id(Class_ClearArray); + } virtual int Opcode() const; virtual const Type *bottom_type() const { return Type::MEMORY; } // ClearArray modifies array elements, and so affects only the @@ -739,27 +759,23 @@ Node* start_offset, Node* end_offset, PhaseGVN* phase); + // Return allocation input memory edge if it is different instance + // or itself if it is the one we are looking for. + static bool step_through(Node** np, uint instance_id, PhaseTransform* phase); }; //------------------------------StrComp------------------------------------- class StrCompNode: public Node { public: - StrCompNode(Node *control, - Node* char_array_mem, - Node* value_mem, - Node* count_mem, - Node* offset_mem, - Node* s1, Node* s2): Node(control, - char_array_mem, - value_mem, - count_mem, - offset_mem, - s1, s2) {}; + StrCompNode(Node* control, Node* char_array_mem, + Node* s1, Node* c1, + Node* s2, Node* c2): Node(control, char_array_mem, + s1, c1, + s2, c2) {}; virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::INT; } - // a StrCompNode (conservatively) aliases with everything: - virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual const TypePtr* adr_type() const { return TypeAryPtr::CHARS; } virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -768,22 +784,13 @@ //------------------------------StrEquals------------------------------------- class StrEqualsNode: public Node { public: - StrEqualsNode(Node *control, - Node* char_array_mem, - Node* value_mem, - Node* count_mem, - Node* offset_mem, - Node* s1, Node* s2): Node(control, - char_array_mem, - value_mem, - count_mem, - offset_mem, - s1, s2) {}; + StrEqualsNode(Node* control, Node* char_array_mem, + Node* s1, Node* s2, Node* c): Node(control, char_array_mem, + s1, s2, c) {}; virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::BOOL; } - // a StrEqualsNode (conservatively) aliases with everything: - virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual const TypePtr* adr_type() const { return TypeAryPtr::CHARS; } virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -792,22 +799,15 @@ //------------------------------StrIndexOf------------------------------------- class StrIndexOfNode: public Node { public: - StrIndexOfNode(Node *control, - Node* char_array_mem, - Node* value_mem, - Node* count_mem, - Node* offset_mem, - Node* s1, Node* s2): Node(control, - char_array_mem, - value_mem, - count_mem, - offset_mem, - s1, s2) {}; + StrIndexOfNode(Node* control, Node* char_array_mem, + Node* s1, Node* c1, + Node* s2, Node* c2): Node(control, char_array_mem, + s1, c1, + s2, c2) {}; virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::INT; } - // a StrIndexOfNode (conservatively) aliases with everything: - virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual const TypePtr* adr_type() const { return TypeAryPtr::CHARS; } virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); @@ -816,11 +816,13 @@ //------------------------------AryEq--------------------------------------- class AryEqNode: public Node { public: - AryEqNode(Node *control, Node* s1, Node* s2): Node(control, s1, s2) {}; + AryEqNode(Node* control, Node* char_array_mem, + Node* s1, Node* s2): Node(control, char_array_mem, s1, s2) {}; virtual int Opcode() const; virtual bool depends_only_on_test() const { return false; } virtual const Type* bottom_type() const { return TypeInt::BOOL; } virtual const TypePtr* adr_type() const { return TypeAryPtr::CHARS; } + virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); }; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/node.hpp --- a/src/share/vm/opto/node.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/node.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -47,6 +47,7 @@ class CatchNode; class CatchProjNode; class CheckCastPPNode; +class ClearArrayNode; class CmpNode; class CodeBuffer; class ConstraintCastNode; @@ -599,8 +600,9 @@ DEFINE_CLASS_ID(BoxLock, Node, 10) DEFINE_CLASS_ID(Add, Node, 11) DEFINE_CLASS_ID(Mul, Node, 12) + DEFINE_CLASS_ID(ClearArray, Node, 13) - _max_classes = ClassMask_Mul + _max_classes = ClassMask_ClearArray }; #undef DEFINE_CLASS_ID @@ -661,18 +663,25 @@ return (_flags & Flag_is_Call) != 0; } + CallNode* isa_Call() const { + return is_Call() ? as_Call() : NULL; + } + CallNode *as_Call() const { // Only for CallNode (not for MachCallNode) assert((_class_id & ClassMask_Call) == Class_Call, "invalid node class"); return (CallNode*)this; } - #define DEFINE_CLASS_QUERY(type) \ - bool is_##type() const { \ + #define DEFINE_CLASS_QUERY(type) \ + bool is_##type() const { \ return ((_class_id & ClassMask_##type) == Class_##type); \ - } \ - type##Node *as_##type() const { \ - assert(is_##type(), "invalid node class"); \ - return (type##Node*)this; \ + } \ + type##Node *as_##type() const { \ + assert(is_##type(), "invalid node class"); \ + return (type##Node*)this; \ + } \ + type##Node* isa_##type() const { \ + return (is_##type()) ? as_##type() : NULL; \ } DEFINE_CLASS_QUERY(AbstractLock) @@ -691,6 +700,7 @@ DEFINE_CLASS_QUERY(CatchProj) DEFINE_CLASS_QUERY(CheckCastPP) DEFINE_CLASS_QUERY(ConstraintCast) + DEFINE_CLASS_QUERY(ClearArray) DEFINE_CLASS_QUERY(CMove) DEFINE_CLASS_QUERY(Cmp) DEFINE_CLASS_QUERY(CountedLoop) @@ -1249,6 +1259,24 @@ #undef I_VDUI_ONLY #undef VDUI_ONLY +// An Iterator that truly follows the iterator pattern. Doesn't +// support deletion but could be made to. +// +// for (SimpleDUIterator i(n); i.has_next(); i.next()) { +// Node* m = i.get(); +// +class SimpleDUIterator : public StackObj { + private: + Node* node; + DUIterator_Fast i; + DUIterator_Fast imax; + public: + SimpleDUIterator(Node* n): node(n), i(n->fast_outs(imax)) {} + bool has_next() { return i < imax; } + void next() { i++; } + Node* get() { return node->fast_out(i); } +}; + //----------------------------------------------------------------------------- // Map dense integer indices to Nodes. Uses classic doubling-array trick. @@ -1290,6 +1318,12 @@ public: Node_List() : Node_Array(Thread::current()->resource_area()), _cnt(0) {} Node_List(Arena *a) : Node_Array(a), _cnt(0) {} + bool contains(Node* n) { + for (uint e = 0; e < size(); e++) { + if (at(e) == n) return true; + } + return false; + } void insert( uint i, Node *n ) { Node_Array::insert(i,n); _cnt++; } void remove( uint i ) { Node_Array::remove(i); _cnt--; } void push( Node *b ) { map(_cnt++,b); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/output.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -611,7 +611,7 @@ assert(cik->is_instance_klass() || cik->is_array_klass(), "Not supported allocation."); sv = new ObjectValue(spobj->_idx, - new ConstantOopWriteValue(cik->encoding())); + new ConstantOopWriteValue(cik->constant_encoding())); Compile::set_sv_for_object_node(objs, sv); uint first_ind = spobj->first_index(); @@ -678,7 +678,7 @@ #endif //_LP64 else if( (t->base() == Type::FloatBot || t->base() == Type::FloatCon) && OptoReg::is_reg(regnum) ) { - array->append(new_loc_value( _regalloc, regnum, Matcher::float_in_double + array->append(new_loc_value( _regalloc, regnum, Matcher::float_in_double() ? Location::float_in_dbl : Location::normal )); } else if( t->base() == Type::Int && OptoReg::is_reg(regnum) ) { array->append(new_loc_value( _regalloc, regnum, Matcher::int_in_long @@ -702,13 +702,13 @@ case Type::AryPtr: case Type::InstPtr: case Type::KlassPtr: // fall through - array->append(new ConstantOopWriteValue(t->isa_oopptr()->const_oop()->encoding())); + array->append(new ConstantOopWriteValue(t->isa_oopptr()->const_oop()->constant_encoding())); break; case Type::NarrowOop: if (t == TypeNarrowOop::NULL_PTR) { array->append(new ConstantOopWriteValue(NULL)); } else { - array->append(new ConstantOopWriteValue(t->make_ptr()->isa_oopptr()->const_oop()->encoding())); + array->append(new ConstantOopWriteValue(t->make_ptr()->isa_oopptr()->const_oop()->constant_encoding())); } break; case Type::Int: @@ -794,6 +794,8 @@ #endif int safepoint_pc_offset = current_offset; + bool is_method_handle_invoke = false; + bool return_oop = false; // Add the safepoint in the DebugInfoRecorder if( !mach->is_MachCall() ) { @@ -801,6 +803,20 @@ debug_info()->add_safepoint(safepoint_pc_offset, sfn->_oop_map); } else { mcall = mach->as_MachCall(); + + // Is the call a MethodHandle call? + if (mcall->is_MachCallJava()) { + if (mcall->as_MachCallJava()->_method_handle_invoke) { + assert(has_method_handle_invokes(), "must have been set during call generation"); + is_method_handle_invoke = true; + } + } + + // Check if a call returns an object. + if (mcall->return_value_is_used() && + mcall->tf()->range()->field_at(TypeFunc::Parms)->isa_ptr()) { + return_oop = true; + } safepoint_pc_offset += mcall->ret_addr_offset(); debug_info()->add_safepoint(safepoint_pc_offset, mcall->_oop_map); } @@ -871,7 +887,7 @@ assert(cik->is_instance_klass() || cik->is_array_klass(), "Not supported allocation."); ObjectValue* sv = new ObjectValue(spobj->_idx, - new ConstantOopWriteValue(cik->encoding())); + new ConstantOopWriteValue(cik->constant_encoding())); Compile::set_sv_for_object_node(objs, sv); uint first_ind = spobj->first_index(); @@ -890,7 +906,7 @@ } } else { const TypePtr *tp = obj_node->bottom_type()->make_ptr(); - scval = new ConstantOopWriteValue(tp->is_instptr()->const_oop()->encoding()); + scval = new ConstantOopWriteValue(tp->is_instptr()->const_oop()->constant_encoding()); } OptoReg::Name box_reg = BoxLockNode::stack_slot(box_node); @@ -911,9 +927,9 @@ ciMethod* scope_method = method ? method : _method; // Describe the scope here assert(jvms->bci() >= InvocationEntryBci && jvms->bci() <= 0x10000, "must be a valid or entry BCI"); - assert(!jvms->should_reexecute() || depth==max_depth, "reexecute allowed only for the youngest"); + assert(!jvms->should_reexecute() || depth == max_depth, "reexecute allowed only for the youngest"); // Now we can describe the scope. - debug_info()->describe_scope(safepoint_pc_offset,scope_method,jvms->bci(),jvms->should_reexecute(),locvals,expvals,monvals); + debug_info()->describe_scope(safepoint_pc_offset, scope_method, jvms->bci(), jvms->should_reexecute(), is_method_handle_invoke, return_oop, locvals, expvals, monvals); } // End jvms loop // Mark the end of the scope set. @@ -1080,14 +1096,26 @@ deopt_handler_req += 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 + if (StressCodeBuffers) code_req = const_req = stub_req = exception_handler_req = deopt_handler_req = 0x10; // force expansion - int total_req = code_req + pad_req + stub_req + exception_handler_req + deopt_handler_req + const_req; + + int total_req = + code_req + + pad_req + + stub_req + + exception_handler_req + + deopt_handler_req + // deopt handler + const_req; + + if (has_method_handle_invokes()) + total_req += deopt_handler_req; // deopt MH handler + CodeBuffer* cb = code_buffer(); cb->initialize(total_req, locs_req); // Have we run out of code space? - if (cb->blob() == NULL) { + if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { turn_off_compiler(this); return; } @@ -1308,7 +1336,7 @@ // Verify that there is sufficient space remaining cb->insts()->maybe_expand_to_ensure_remaining(MAX_inst_size); - if (cb->blob() == NULL) { + if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { turn_off_compiler(this); return; } @@ -1424,10 +1452,17 @@ _code_offsets.set_value(CodeOffsets::Exceptions, emit_exception_handler(*cb)); // Emit the deopt handler code. _code_offsets.set_value(CodeOffsets::Deopt, 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)); + } } // One last check for failed CodeBuffer::expand: - if (cb->blob() == NULL) { + if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { turn_off_compiler(this); return; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/parse.hpp --- a/src/share/vm/opto/parse.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/parse.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -39,6 +39,7 @@ // Always between 0.0 and 1.0. Represents the percentage of the method's // total execution time used at this call site. const float _site_invoke_ratio; + const int _site_depth_adjust; float compute_callee_frequency( int caller_bci ) const; GrowableArray _subtrees; @@ -50,7 +51,8 @@ ciMethod* callee_method, JVMState* caller_jvms, int caller_bci, - float site_invoke_ratio); + float site_invoke_ratio, + int site_depth_adjust); InlineTree *build_inline_tree_for_callee(ciMethod* callee_method, JVMState* caller_jvms, int caller_bci); @@ -61,14 +63,15 @@ InlineTree *caller_tree() const { return _caller_tree; } InlineTree* callee_at(int bci, ciMethod* m) const; - int inline_depth() const { return _caller_jvms ? _caller_jvms->depth() : 0; } + int inline_depth() const { return stack_depth() + _site_depth_adjust; } + int stack_depth() const { return _caller_jvms ? _caller_jvms->depth() : 0; } public: static InlineTree* build_inline_tree_root(); static InlineTree* find_subtree_from_root(InlineTree* root, JVMState* jvms, ciMethod* callee, bool create_if_not_found = false); // For temporary (stack-allocated, stateless) ilts: - InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, float site_invoke_ratio); + InlineTree(Compile* c, ciMethod* callee_method, JVMState* caller_jvms, float site_invoke_ratio, int site_depth_adjust); // InlineTree enum enum InlineStyle { @@ -427,6 +430,11 @@ } } + // Return true if the parser should add a loop predicate + bool should_add_predicate(int target_bci); + // Insert a loop predicate into the graph + void add_predicate(); + // Note: Intrinsic generation routines may be found in library_call.cpp. // Helper function to setup Ideal Call nodes @@ -469,7 +477,7 @@ // loading from a constant field or the constant pool // returns false if push failed (non-perm field constants only, not ldcs) - bool push_constant(ciConstant con); + bool push_constant(ciConstant con, bool require_constant = false); // implementation of object creation bytecodes void do_new(); @@ -488,7 +496,7 @@ void do_ifnull(BoolTest::mask btest, Node* c); void do_if(BoolTest::mask btest, Node* c); - void repush_if_args(); + int repush_if_args(); void adjust_map_after_if(BoolTest::mask btest, Node* c, float prob, Block* path, Block* other_path); IfNode* jump_if_fork_int(Node* a, Node* b, BoolTest::mask mask); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/parse1.cpp --- a/src/share/vm/opto/parse1.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/parse1.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -229,6 +229,8 @@ } } + // Use the raw liveness computation to make sure that unexpected + // values don't propagate into the OSR frame. MethodLivenessResult live_locals = method()->liveness_at_bci(osr_bci()); if (!live_locals.is_valid()) { // Degenerate or breakpointed method. @@ -303,6 +305,7 @@ SafePointNode* bad_type_exit = clone_map(); bad_type_exit->set_control(new (C, 1) RegionNode(1)); + assert(osr_block->flow()->jsrs()->size() == 0, "should be no jsrs live at osr point"); for (index = 0; index < max_locals; index++) { if (stopped()) break; Node* l = local(index); @@ -314,6 +317,20 @@ continue; } } + if (osr_block->flow()->local_type_at(index)->is_return_address()) { + // In our current system it's illegal for jsr addresses to be + // live into an OSR entry point because the compiler performs + // inlining of jsrs. ciTypeFlow has a bailout that detect this + // case and aborts the compile if addresses are live into an OSR + // entry point. Because of that we can assume that any address + // locals at the OSR entry point are dead. Method liveness + // isn't precise enought to figure out that they are dead in all + // cases so simply skip checking address locals all + // together. Any type check is guaranteed to fail since the + // interpreter type is the result of a load which might have any + // value and the expected type is a constant. + continue; + } set_local(index, check_interpreter_type(l, type, bad_type_exit)); } @@ -817,7 +834,6 @@ case Bytecodes::_ddiv: case Bytecodes::_checkcast: case Bytecodes::_instanceof: - case Bytecodes::_athrow: case Bytecodes::_anewarray: case Bytecodes::_newarray: case Bytecodes::_multianewarray: @@ -827,6 +843,8 @@ return true; break; + // Don't rerun athrow since it's part of the exception path. + case Bytecodes::_athrow: case Bytecodes::_invokestatic: case Bytecodes::_invokedynamic: case Bytecodes::_invokespecial: @@ -1376,6 +1394,10 @@ set_parse_bci(iter().cur_bci()); if (bci() == block()->limit()) { + // insert a predicate if it falls through to a loop head block + if (should_add_predicate(bci())){ + add_predicate(); + } // Do not walk into the next block until directed by do_all_blocks. merge(bci()); break; @@ -2076,6 +2098,37 @@ } } +//------------------------------should_add_predicate-------------------------- +bool Parse::should_add_predicate(int target_bci) { + if (!UseLoopPredicate) return false; + Block* target = successor_for_bci(target_bci); + if (target != NULL && + target->is_loop_head() && + block()->rpo() < target->rpo()) { + return true; + } + return false; +} + +//------------------------------add_predicate--------------------------------- +void Parse::add_predicate() { + assert(UseLoopPredicate,"use only for loop predicate"); + Node *cont = _gvn.intcon(1); + Node* opq = _gvn.transform(new (C, 2) Opaque1Node(C, cont)); + Node *bol = _gvn.transform(new (C, 2) Conv2BNode(opq)); + IfNode* iff = create_and_map_if(control(), bol, PROB_MAX, COUNT_UNKNOWN); + Node* iffalse = _gvn.transform(new (C, 1) IfFalseNode(iff)); + C->add_predicate_opaq(opq); + { + PreserveJVMState pjvms(this); + set_control(iffalse); + uncommon_trap(Deoptimization::Reason_predicate, + Deoptimization::Action_maybe_recompile); + } + Node* iftrue = _gvn.transform(new (C, 1) IfTrueNode(iff)); + set_control(iftrue); +} + #ifndef PRODUCT //------------------------show_parse_info-------------------------------------- void Parse::show_parse_info() { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/parse2.cpp --- a/src/share/vm/opto/parse2.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/parse2.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -278,6 +278,11 @@ if (len < 1) { // If this is a backward branch, add safepoint maybe_add_safepoint(default_dest); + if (should_add_predicate(default_dest)){ + _sp += 1; // set original stack for use by uncommon_trap + add_predicate(); + _sp -= 1; + } merge(default_dest); return; } @@ -324,6 +329,11 @@ if (len < 1) { // If this is a backward branch, add safepoint maybe_add_safepoint(default_dest); + if (should_add_predicate(default_dest)){ + _sp += 1; // set original stack for use by uncommon_trap + add_predicate(); + _sp -= 1; + } merge(default_dest); return; } @@ -731,6 +741,9 @@ push(_gvn.makecon(ret_addr)); // Flow to the jsr. + if (should_add_predicate(jsr_bci)){ + add_predicate(); + } merge(jsr_bci); } @@ -881,7 +894,7 @@ //-------------------------------repush_if_args-------------------------------- // Push arguments of an "if" bytecode back onto the stack by adjusting _sp. -inline void Parse::repush_if_args() { +inline int Parse::repush_if_args() { #ifndef PRODUCT if (PrintOpto && WizardMode) { tty->print("defending against excessive implicit null exceptions on %s @%d in ", @@ -895,6 +908,7 @@ assert(argument(0) != NULL, "must exist"); assert(bc_depth == 1 || argument(1) != NULL, "two must exist"); _sp += bc_depth; + return bc_depth; } //----------------------------------do_ifnull---------------------------------- @@ -954,8 +968,14 @@ // Update method data profile_taken_branch(target_bci); adjust_map_after_if(btest, c, prob, branch_block, next_block); - if (!stopped()) + if (!stopped()) { + if (should_add_predicate(target_bci)){ // add a predicate if it branches to a loop + int nargs = repush_if_args(); // set original stack for uncommon_trap + add_predicate(); + _sp -= nargs; + } merge(target_bci); + } } } @@ -1076,8 +1096,14 @@ // Update method data profile_taken_branch(target_bci); adjust_map_after_if(taken_btest, c, prob, branch_block, next_block); - if (!stopped()) + if (!stopped()) { + if (should_add_predicate(target_bci)){ // add a predicate if it branches to a loop + int nargs = repush_if_args(); // set original stack for the uncommon_trap + add_predicate(); + _sp -= nargs; + } merge(target_bci); + } } } @@ -1325,7 +1351,8 @@ } } } - push_constant(constant); + bool pushed = push_constant(constant, true); + guarantee(pushed, "must be possible to push this constant"); } break; @@ -2052,13 +2079,6 @@ // null exception oop throws NULL pointer exception do_null_check(peek(), T_OBJECT); if (stopped()) return; - if (env()->jvmti_can_post_exceptions()) { - // "Full-speed throwing" is not necessary here, - // since we're notifying the VM on every throw. - uncommon_trap(Deoptimization::Reason_unhandled, - Deoptimization::Action_none); - return; - } // Hook the thrown exception directly to subsequent handlers. if (BailoutToInterpreterForThrows) { // Keep method interpreted from now on. @@ -2066,6 +2086,11 @@ Deoptimization::Action_make_not_compilable); return; } + if (env()->jvmti_can_post_on_exceptions()) { + // check if we must post exception events, take uncommon trap if so (with must_throw = false) + uncommon_trap_if_should_post_on_exceptions(Deoptimization::Reason_unhandled, false); + } + // Here if either can_post_on_exceptions or should_post_on_exceptions is false add_exception_state(make_exception_state(peek())); break; @@ -2079,6 +2104,10 @@ // Update method data profile_taken_branch(target_bci); + // Add loop predicate if it goes to a loop + if (should_add_predicate(target_bci)){ + add_predicate(); + } // Merge the current control into the target basic block merge(target_bci); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/parse3.cpp --- a/src/share/vm/opto/parse3.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/parse3.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -125,7 +125,25 @@ void Parse::do_get_xxx(const TypePtr* obj_type, Node* obj, ciField* field, bool is_field) { // Does this field have a constant value? If so, just push the value. - if (field->is_constant() && push_constant(field->constant_value())) return; + if (field->is_constant()) { + if (field->is_static()) { + // final static field + if (push_constant(field->constant_value())) + return; + } + else { + // final non-static field of a trusted class ({java,sun}.dyn + // classes). + if (obj->is_Con()) { + const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr(); + ciObject* constant_oop = oop_ptr->const_oop(); + ciConstant constant = field->constant_value_of(constant_oop); + + if (push_constant(constant, true)) + return; + } + } + } ciType* field_klass = field->type(); bool is_vol = field->is_volatile(); @@ -145,7 +163,7 @@ if (!field->type()->is_loaded()) { type = TypeInstPtr::BOTTOM; must_assert_null = true; - } else if (field->is_constant()) { + } else if (field->is_constant() && field->is_static()) { // This can happen if the constant oop is non-perm. ciObject* con = field->constant_value().as_object(); // Do not "join" in the previous type; it doesn't add value, @@ -240,19 +258,19 @@ // membar is dependent on the store, keeping any other membars generated // below from floating up past the store. int adr_idx = C->get_alias_index(adr_type); - insert_mem_bar_volatile(Op_MemBarVolatile, adr_idx); + insert_mem_bar_volatile(Op_MemBarVolatile, adr_idx, store); // Now place a membar for AliasIdxBot for the unknown yet-to-be-parsed // volatile alias indices. Skip this if the membar is redundant. if (adr_idx != Compile::AliasIdxBot) { - insert_mem_bar_volatile(Op_MemBarVolatile, Compile::AliasIdxBot); + insert_mem_bar_volatile(Op_MemBarVolatile, Compile::AliasIdxBot, store); } // Finally, place alias-index-specific membars for each volatile index // that isn't the adr_idx membar. Typically there's only 1 or 2. for( int i = Compile::AliasIdxRaw; i < C->num_alias_types(); i++ ) { if (i != adr_idx && C->alias_type(i)->is_volatile()) { - insert_mem_bar_volatile(Op_MemBarVolatile, i); + insert_mem_bar_volatile(Op_MemBarVolatile, i, store); } } } @@ -267,7 +285,7 @@ } -bool Parse::push_constant(ciConstant constant) { +bool Parse::push_constant(ciConstant constant, bool require_constant) { switch (constant.basic_type()) { case T_BOOLEAN: push( intcon(constant.as_boolean()) ); break; case T_INT: push( intcon(constant.as_int()) ); break; @@ -279,13 +297,16 @@ case T_LONG: push_pair( longcon(constant.as_long()) ); break; case T_ARRAY: case T_OBJECT: { - // the oop is in perm space if the ciObject "has_encoding" + // cases: + // can_be_constant = (oop not scavengable || ScavengeRootsInCode != 0) + // should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2) + // An oop is not scavengable if it is in the perm gen. ciObject* oop_constant = constant.as_object(); if (oop_constant->is_null_object()) { push( zerocon(T_OBJECT) ); break; - } else if (oop_constant->has_encoding()) { - push( makecon(TypeOopPtr::make_from_constant(oop_constant)) ); + } else if (require_constant || oop_constant->should_be_constant()) { + push( makecon(TypeOopPtr::make_from_constant(oop_constant, require_constant)) ); break; } else { // we cannot inline the oop, but we can use it later to narrow a type @@ -418,8 +439,18 @@ // Can use multianewarray instead of [a]newarray if only one dimension, // or if all non-final dimensions are small constants. - if (expand_count == 1 || (1 <= expand_count && expand_count <= expand_limit)) { - Node* obj = expand_multianewarray(array_klass, &length[0], ndimensions, ndimensions); + if (ndimensions == 1 || (1 <= expand_count && expand_count <= expand_limit)) { + Node* obj = NULL; + // Set the original stack and the reexecute bit for the interpreter + // to reexecute the multianewarray bytecode if deoptimization happens. + // Do it unconditionally even for one dimension multianewarray. + // Note: the reexecute bit will be set in GraphKit::add_safepoint_edges() + // when AllocateArray node for newarray is created. + { PreserveReexecuteState preexecs(this); + _sp += ndimensions; + // Pass 0 as nargs since uncommon trap code does not need to restore stack. + obj = expand_multianewarray(array_klass, &length[0], ndimensions, 0); + } //original reexecute and sp are set back here push(obj); return; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/parseHelper.cpp --- a/src/share/vm/opto/parseHelper.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/parseHelper.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -221,6 +221,14 @@ // Push resultant oop onto stack push(obj); + + // Keep track of whether opportunities exist for StringBuilder + // optimizations. + if (OptimizeStringConcat && + (klass == C->env()->StringBuilder_klass() || + klass == C->env()->StringBuffer_klass())) { + C->set_has_stringbuilder(true); + } } #ifndef PRODUCT @@ -406,8 +414,6 @@ void Parse::profile_call(Node* receiver) { if (!method_data_update()) return; - profile_generic_call(); - switch (bc()) { case Bytecodes::_invokevirtual: case Bytecodes::_invokeinterface: @@ -416,6 +422,7 @@ case Bytecodes::_invokestatic: case Bytecodes::_invokedynamic: case Bytecodes::_invokespecial: + profile_generic_call(); break; default: fatal("unexpected call bytecode"); } @@ -436,13 +443,16 @@ void Parse::profile_receiver_type(Node* receiver) { assert(method_data_update(), "must be generating profile code"); - // Skip if we aren't tracking receivers - if (TypeProfileWidth < 1) return; - ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); assert(data->is_ReceiverTypeData(), "need ReceiverTypeData here"); + + // Skip if we aren't tracking receivers + if (TypeProfileWidth < 1) { + increment_md_counter_at(md, data, CounterData::count_offset()); + return; + } ciReceiverTypeData* rdata = (ciReceiverTypeData*)data->as_ReceiverTypeData(); Node* method_data = method_data_addressing(md, rdata, in_ByteSize(0)); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/phase.cpp --- a/src/share/vm/opto/phase.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/phase.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -53,6 +53,7 @@ elapsedTimer Phase::_t_registerMethod; elapsedTimer Phase::_t_temporaryTimer1; elapsedTimer Phase::_t_temporaryTimer2; +elapsedTimer Phase::_t_idealLoopVerify; // Subtimers for _t_optimizer elapsedTimer Phase::_t_iterGVN; @@ -88,51 +89,52 @@ tty->print_cr ("Accumulated compiler times:"); tty->print_cr ("---------------------------"); tty->print_cr (" Total compilation: %3.3f sec.", Phase::_t_totalCompilation.seconds()); - tty->print (" method compilation : %3.3f sec", Phase::_t_methodCompilation.seconds()); + tty->print (" method compilation : %3.3f sec", Phase::_t_methodCompilation.seconds()); tty->print ("/%d bytes",_total_bytes_compiled); tty->print_cr (" (%3.0f bytes per sec) ", Phase::_total_bytes_compiled / Phase::_t_methodCompilation.seconds()); - tty->print_cr (" stub compilation : %3.3f sec.", Phase::_t_stubCompilation.seconds()); + tty->print_cr (" stub compilation : %3.3f sec.", Phase::_t_stubCompilation.seconds()); tty->print_cr (" Phases:"); - tty->print_cr (" parse : %3.3f sec", Phase::_t_parser.seconds()); + tty->print_cr (" parse : %3.3f sec", Phase::_t_parser.seconds()); if (DoEscapeAnalysis) { - tty->print_cr (" escape analysis : %3.3f sec", Phase::_t_escapeAnalysis.seconds()); + tty->print_cr (" escape analysis : %3.3f sec", Phase::_t_escapeAnalysis.seconds()); } - tty->print_cr (" optimizer : %3.3f sec", Phase::_t_optimizer.seconds()); + tty->print_cr (" optimizer : %3.3f sec", Phase::_t_optimizer.seconds()); if( Verbose || WizardMode ) { - tty->print_cr (" iterGVN : %3.3f sec", Phase::_t_iterGVN.seconds()); - tty->print_cr (" idealLoop : %3.3f sec", Phase::_t_idealLoop.seconds()); - tty->print_cr (" ccp : %3.3f sec", Phase::_t_ccp.seconds()); - tty->print_cr (" iterGVN2 : %3.3f sec", Phase::_t_iterGVN2.seconds()); - tty->print_cr (" graphReshape : %3.3f sec", Phase::_t_graphReshaping.seconds()); + tty->print_cr (" iterGVN : %3.3f sec", Phase::_t_iterGVN.seconds()); + tty->print_cr (" idealLoop : %3.3f sec", Phase::_t_idealLoop.seconds()); + tty->print_cr (" idealLoopVerify: %3.3f sec", Phase::_t_idealLoopVerify.seconds()); + tty->print_cr (" ccp : %3.3f sec", Phase::_t_ccp.seconds()); + tty->print_cr (" iterGVN2 : %3.3f sec", Phase::_t_iterGVN2.seconds()); + tty->print_cr (" graphReshape : %3.3f sec", Phase::_t_graphReshaping.seconds()); double optimizer_subtotal = Phase::_t_iterGVN.seconds() + Phase::_t_idealLoop.seconds() + Phase::_t_ccp.seconds() + Phase::_t_graphReshaping.seconds(); double percent_of_optimizer = ((optimizer_subtotal == 0.0) ? 0.0 : (optimizer_subtotal / Phase::_t_optimizer.seconds() * 100.0)); - tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", optimizer_subtotal, percent_of_optimizer); + tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", optimizer_subtotal, percent_of_optimizer); } - tty->print_cr (" matcher : %3.3f sec", Phase::_t_matcher.seconds()); - tty->print_cr (" scheduler : %3.3f sec", Phase::_t_scheduler.seconds()); - tty->print_cr (" regalloc : %3.3f sec", Phase::_t_registerAllocation.seconds()); + tty->print_cr (" matcher : %3.3f sec", Phase::_t_matcher.seconds()); + tty->print_cr (" scheduler : %3.3f sec", Phase::_t_scheduler.seconds()); + tty->print_cr (" regalloc : %3.3f sec", Phase::_t_registerAllocation.seconds()); if( Verbose || WizardMode ) { - tty->print_cr (" ctorChaitin : %3.3f sec", Phase::_t_ctorChaitin.seconds()); - tty->print_cr (" buildIFG : %3.3f sec", Phase::_t_buildIFGphysical.seconds()); - tty->print_cr (" computeLive : %3.3f sec", Phase::_t_computeLive.seconds()); - tty->print_cr (" regAllocSplit: %3.3f sec", Phase::_t_regAllocSplit.seconds()); + tty->print_cr (" ctorChaitin : %3.3f sec", Phase::_t_ctorChaitin.seconds()); + tty->print_cr (" buildIFG : %3.3f sec", Phase::_t_buildIFGphysical.seconds()); + tty->print_cr (" computeLive : %3.3f sec", Phase::_t_computeLive.seconds()); + tty->print_cr (" regAllocSplit : %3.3f sec", Phase::_t_regAllocSplit.seconds()); tty->print_cr (" postAllocCopyRemoval: %3.3f sec", Phase::_t_postAllocCopyRemoval.seconds()); - tty->print_cr (" fixupSpills : %3.3f sec", Phase::_t_fixupSpills.seconds()); + tty->print_cr (" fixupSpills : %3.3f sec", Phase::_t_fixupSpills.seconds()); double regalloc_subtotal = Phase::_t_ctorChaitin.seconds() + Phase::_t_buildIFGphysical.seconds() + Phase::_t_computeLive.seconds() + Phase::_t_regAllocSplit.seconds() + Phase::_t_fixupSpills.seconds() + Phase::_t_postAllocCopyRemoval.seconds(); double percent_of_regalloc = ((regalloc_subtotal == 0.0) ? 0.0 : (regalloc_subtotal / Phase::_t_registerAllocation.seconds() * 100.0)); - tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", regalloc_subtotal, percent_of_regalloc); + tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", regalloc_subtotal, percent_of_regalloc); } - tty->print_cr (" macroExpand : %3.3f sec", Phase::_t_macroExpand.seconds()); - tty->print_cr (" blockOrdering: %3.3f sec", Phase::_t_blockOrdering.seconds()); - tty->print_cr (" peephole : %3.3f sec", Phase::_t_peephole.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 (" ------------ : ----------"); + tty->print_cr (" macroExpand : %3.3f sec", Phase::_t_macroExpand.seconds()); + tty->print_cr (" blockOrdering : %3.3f sec", Phase::_t_blockOrdering.seconds()); + tty->print_cr (" peephole : %3.3f sec", Phase::_t_peephole.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 (" -------------- : ----------"); double phase_subtotal = Phase::_t_parser.seconds() + (DoEscapeAnalysis ? Phase::_t_escapeAnalysis.seconds() : 0.0) + Phase::_t_optimizer.seconds() + Phase::_t_graphReshaping.seconds() + @@ -143,7 +145,7 @@ double percent_of_method_compile = ((phase_subtotal == 0.0) ? 0.0 : phase_subtotal / Phase::_t_methodCompilation.seconds()) * 100.0; // counters inside Compile::CodeGen include time for adapters and stubs // so phase-total can be greater than 100% - tty->print_cr (" total : %3.3f sec, %3.2f %%", phase_subtotal, percent_of_method_compile); + tty->print_cr (" total : %3.3f sec, %3.2f %%", phase_subtotal, percent_of_method_compile); assert( percent_of_method_compile > expected_method_compile_coverage || phase_subtotal < minimum_meaningful_method_compile, @@ -157,8 +159,8 @@ tty->cr(); tty->print_cr (" temporaryTimer2: %3.3f sec", Phase::_t_temporaryTimer2.seconds()); } - tty->print_cr (" output : %3.3f sec", Phase::_t_output.seconds()); - tty->print_cr (" isched : %3.3f sec", Phase::_t_instrSched.seconds()); - tty->print_cr (" bldOopMaps: %3.3f sec", Phase::_t_buildOopMaps.seconds()); + tty->print_cr (" output : %3.3f sec", Phase::_t_output.seconds()); + tty->print_cr (" isched : %3.3f sec", Phase::_t_instrSched.seconds()); + tty->print_cr (" bldOopMaps : %3.3f sec", Phase::_t_buildOopMaps.seconds()); } #endif diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/phase.hpp --- a/src/share/vm/opto/phase.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/phase.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -44,6 +44,7 @@ BlockLayout, // Linear ordering of blocks Register_Allocation, // Register allocation, duh LIVE, // Dragon-book LIVE range problem + StringOpts, // StringBuilder related optimizations Interference_Graph, // Building the IFG Coalesce, // Coalescing copies Ideal_Loop, // Find idealized trip-counted loops @@ -83,6 +84,7 @@ static elapsedTimer _t_registerMethod; static elapsedTimer _t_temporaryTimer1; static elapsedTimer _t_temporaryTimer2; + static elapsedTimer _t_idealLoopVerify; // Subtimers for _t_optimizer static elapsedTimer _t_iterGVN; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/phaseX.cpp --- a/src/share/vm/opto/phaseX.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/phaseX.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -1502,7 +1502,7 @@ //---------------------------------saturate------------------------------------ const Type* PhaseCCP::saturate(const Type* new_type, const Type* old_type, const Type* limit_type) const { - const Type* wide_type = new_type->widen(old_type); + const Type* wide_type = new_type->widen(old_type, limit_type); if (wide_type != new_type) { // did we widen? // If so, we may have widened beyond the limit type. Clip it back down. new_type = wide_type->filter(limit_type); @@ -1622,9 +1622,11 @@ // old goes dead? if( old ) { switch (old->outcnt()) { - case 0: // Kill all his inputs, and recursively kill other dead nodes. + case 0: + // Put into the worklist to kill later. We do not kill it now because the + // recursive kill will delete the current node (this) if dead-loop exists if (!old->is_top()) - igvn->remove_dead_node( old ); + igvn->_worklist.push( old ); break; case 1: if( old->is_Store() || old->has_special_unique_user() ) diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/phaseX.hpp --- a/src/share/vm/opto/phaseX.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/phaseX.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -345,7 +345,11 @@ Node *hash_find(const Node *n) { return _table.hash_find(n); } // Used after parsing to eliminate values that are no longer in program - void remove_useless_nodes(VectorSet &useful) { _table.remove_useless_nodes(useful); } + void remove_useless_nodes(VectorSet &useful) { + _table.remove_useless_nodes(useful); + // this may invalidate cached cons so reset the cache + init_con_caches(); + } virtual ConNode* uncached_makecon(const Type* t); // override from PhaseTransform diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/postaloc.cpp --- a/src/share/vm/opto/postaloc.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/postaloc.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2009 Sun Microsystems, 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 @@ -88,6 +88,7 @@ value->map(old_reg,NULL); // Yank from value/regnd maps regnd->map(old_reg,NULL); // This register's value is now unknown } + assert(old->req() <= 2, "can't handle more inputs"); Node *tmp = old->req() > 1 ? old->in(1) : NULL; old->disconnect_inputs(NULL); if( !tmp ) break; @@ -530,6 +531,16 @@ // Do not change from int to pointer Node *val = skip_copies(n); + // Clear out a dead definition before starting so that the + // elimination code doesn't have to guard against it. The + // definition could in fact be a kill projection with a count of + // 0 which is safe but since those are uninteresting for copy + // elimination just delete them as well. + if (regnd[nreg] != NULL && regnd[nreg]->outcnt() == 0) { + regnd.map(nreg, NULL); + value.map(nreg, NULL); + } + uint n_ideal_reg = n->ideal_reg(); if( is_single_register(n_ideal_reg) ) { // If Node 'n' does not change the value mapped by the register, @@ -537,8 +548,7 @@ // mapping so 'n' will go dead. if( value[nreg] != val ) { if (eliminate_copy_of_constant(val, n, b, value, regnd, nreg, OptoReg::Bad)) { - n->replace_by(regnd[nreg]); - j -= yank_if_dead(n,b,&value,®nd); + j -= replace_and_yank_if_dead(n, nreg, b, value, regnd); } else { // Update the mapping: record new Node defined by the register regnd.map(nreg,n); @@ -546,10 +556,9 @@ // Node after skipping all copies. value.map(nreg,val); } - } else if( !may_be_copy_of_callee(n) && regnd[nreg]->outcnt() != 0 ) { + } else if( !may_be_copy_of_callee(n) ) { assert( n->is_Copy(), "" ); - n->replace_by(regnd[nreg]); - j -= yank_if_dead(n,b,&value,®nd); + j -= replace_and_yank_if_dead(n, nreg, b, value, regnd); } } else { // If the value occupies a register pair, record same info @@ -565,18 +574,16 @@ } if( value[nreg] != val || value[nreg_lo] != val ) { if (eliminate_copy_of_constant(val, n, b, value, regnd, nreg, nreg_lo)) { - n->replace_by(regnd[nreg]); - j -= yank_if_dead(n,b,&value,®nd); + j -= replace_and_yank_if_dead(n, nreg, b, value, regnd); } else { regnd.map(nreg , n ); regnd.map(nreg_lo, n ); value.map(nreg ,val); value.map(nreg_lo,val); } - } else if( !may_be_copy_of_callee(n) && regnd[nreg]->outcnt() != 0 ) { + } else if( !may_be_copy_of_callee(n) ) { assert( n->is_Copy(), "" ); - n->replace_by(regnd[nreg]); - j -= yank_if_dead(n,b,&value,®nd); + j -= replace_and_yank_if_dead(n, nreg, b, value, regnd); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/runtime.cpp --- a/src/share/vm/opto/runtime.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/runtime.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -143,18 +143,20 @@ // We failed the fast-path allocation. Now we need to do a scavenge or GC // and try allocation again. -void OptoRuntime::do_eager_card_mark(JavaThread* thread) { +void OptoRuntime::new_store_pre_barrier(JavaThread* thread) { // After any safepoint, just before going back to compiled code, - // we perform a card mark. This lets the compiled code omit - // card marks for initialization of new objects. - // Keep this code consistent with GraphKit::store_barrier. + // we inform the GC that we will be doing initializing writes to + // this object in the future without emitting card-marks, so + // GC may take any compensating steps. + // NOTE: Keep this code consistent with GraphKit::store_barrier. oop new_obj = thread->vm_result(); if (new_obj == NULL) return; assert(Universe::heap()->can_elide_tlab_store_barriers(), "compiler must check this first"); - new_obj = Universe::heap()->new_store_barrier(new_obj); + // GC may decide to give back a safer copy of new_obj. + new_obj = Universe::heap()->new_store_pre_barrier(thread, new_obj); thread->set_vm_result(new_obj); } @@ -197,8 +199,8 @@ JRT_BLOCK_END; if (GraphKit::use_ReduceInitialCardMarks()) { - // do them now so we don't have to do them on the fast path - do_eager_card_mark(thread); + // inform GC that we won't do card marks for initializing writes. + new_store_pre_barrier(thread); } JRT_END @@ -236,8 +238,8 @@ JRT_BLOCK_END; if (GraphKit::use_ReduceInitialCardMarks()) { - // do them now so we don't have to do them on the fast path - do_eager_card_mark(thread); + // inform GC that we won't do card marks for initializing writes. + new_store_pre_barrier(thread); } JRT_END @@ -704,6 +706,11 @@ // vc->set_receiver_count(empty_row, DataLayout::counter_increment); int count_off = ReceiverTypeData::receiver_count_cell_index(empty_row); *(mdp + count_off) = DataLayout::counter_increment; + } else { + // Receiver did not match any saved receiver and there is no empty row for it. + // Increment total counter to indicate polymorphic case. + intptr_t* count_p = (intptr_t*)(((byte*)(data)) + in_bytes(CounterData::count_offset())); + *count_p += DataLayout::counter_increment; } JRT_END @@ -788,7 +795,7 @@ NOT_PRODUCT(Exceptions::debug_check_abort(exception)); #ifdef ASSERT - if (!(exception->is_a(SystemDictionary::throwable_klass()))) { + if (!(exception->is_a(SystemDictionary::Throwable_klass()))) { // should throw an exception here ShouldNotReachHere(); } @@ -808,7 +815,7 @@ // we are switching to old paradigm: search for exception handler in caller_frame // instead in exception handler of caller_frame.sender() - if (JvmtiExport::can_post_exceptions()) { + if (JvmtiExport::can_post_on_exceptions()) { // "Full-speed catching" is not necessary here, // since we're notifying the VM on every catch. // Force deoptimization and the rest of the lookup @@ -856,6 +863,9 @@ thread->set_exception_pc(pc); thread->set_exception_handler_pc(handler_address); thread->set_exception_stack_size(0); + + // Check if the exception PC is a MethodHandle call. + thread->set_is_method_handle_exception(nm->is_method_handle_return(pc)); } // Restore correct return pc. Was saved above. @@ -934,7 +944,7 @@ #endif assert (exception != NULL, "should have thrown a NULLPointerException"); #ifdef ASSERT - if (!(exception->is_a(SystemDictionary::throwable_klass()))) { + if (!(exception->is_a(SystemDictionary::Throwable_klass()))) { // should throw an exception here ShouldNotReachHere(); } @@ -970,8 +980,8 @@ assert(stub_frame.is_runtime_frame() || exception_blob()->contains(stub_frame.pc()), "sanity check"); frame caller_frame = stub_frame.sender(®_map); - VM_DeoptimizeFrame deopt(thread, caller_frame.id()); - VMThread::execute(&deopt); + // bypass VM_DeoptimizeFrame and deoptimize the frame directly + Deoptimization::deoptimize_frame(thread, caller_frame.id()); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/runtime.hpp --- a/src/share/vm/opto/runtime.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/runtime.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -133,8 +133,9 @@ // Allocate storage for a objArray or typeArray static void new_array_C(klassOopDesc* array_klass, int len, JavaThread *thread); - // Post-allocation step for implementing ReduceInitialCardMarks: - static void do_eager_card_mark(JavaThread* thread); + // Post-slow-path-allocation, pre-initializing-stores step for + // implementing ReduceInitialCardMarks + static void new_store_pre_barrier(JavaThread* thread); // Allocate storage for a multi-dimensional arrays // Note: needs to be fixed for arbitrary number of dimensions diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/split_if.cpp --- a/src/share/vm/opto/split_if.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/split_if.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2010 Sun Microsystems, 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 @@ -187,10 +187,20 @@ } #endif + // ConvI2L may have type information on it which becomes invalid if + // it moves up in the graph so change any clones so widen the type + // to TypeLong::INT when pushing it up. + const Type* rtype = NULL; + if (n->Opcode() == Op_ConvI2L && n->bottom_type() != TypeLong::INT) { + rtype = TypeLong::INT; + } + // Now actually split-up this guy. One copy per control path merging. Node *phi = PhiNode::make_blank(blk1, n); for( uint j = 1; j < blk1->req(); j++ ) { Node *x = n->clone(); + // Widen the type of the ConvI2L when pushing up. + if (rtype != NULL) x->as_Type()->set_type(rtype); if( n->in(0) && n->in(0) == blk1 ) x->set_req( 0, blk1->in(j) ); for( uint i = 1; i < n->req(); i++ ) { @@ -219,6 +229,7 @@ //------------------------------register_new_node------------------------------ void PhaseIdealLoop::register_new_node( Node *n, Node *blk ) { + assert(!n->is_CFG(), "must be data node"); _igvn.register_new_node_with_optimizer(n); set_ctrl(n, blk); IdealLoopTree *loop = get_loop(blk); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/stringopts.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/opto/stringopts.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,1396 @@ +/* + * Copyright 2009-2010 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_stringopts.cpp.incl" + +#define __ kit. + +class StringConcat : public ResourceObj { + private: + PhaseStringOpts* _stringopts; + Node* _string_alloc; + AllocateNode* _begin; // The allocation the begins the pattern + CallStaticJavaNode* _end; // The final call of the pattern. Will either be + // SB.toString or or String.(SB.toString) + bool _multiple; // indicates this is a fusion of two or more + // separate StringBuilders + + Node* _arguments; // The list of arguments to be concatenated + GrowableArray _mode; // into a String along with a mode flag + // indicating how to treat the value. + + Node_List _control; // List of control nodes that will be deleted + Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten + // to restart at the initial JVMState. + public: + // Mode for converting arguments to Strings + enum { + StringMode, + IntMode, + CharMode + }; + + StringConcat(PhaseStringOpts* stringopts, CallStaticJavaNode* end): + _end(end), + _begin(NULL), + _multiple(false), + _string_alloc(NULL), + _stringopts(stringopts) { + _arguments = new (_stringopts->C, 1) Node(1); + _arguments->del_req(0); + } + + bool validate_control_flow(); + + void merge_add() { +#if 0 + // XXX This is place holder code for reusing an existing String + // allocation but the logic for checking the state safety is + // probably inadequate at the moment. + CallProjections endprojs; + sc->end()->extract_projections(&endprojs, false); + if (endprojs.resproj != NULL) { + for (SimpleDUIterator i(endprojs.resproj); i.has_next(); i.next()) { + CallStaticJavaNode *use = i.get()->isa_CallStaticJava(); + if (use != NULL && use->method() != NULL && + use->method()->holder() == C->env()->String_klass() && + use->method()->name() == ciSymbol::object_initializer_name() && + use->in(TypeFunc::Parms + 1) == endprojs.resproj) { + // Found useless new String(sb.toString()) so reuse the newly allocated String + // when creating the result instead of allocating a new one. + sc->set_string_alloc(use->in(TypeFunc::Parms)); + sc->set_end(use); + } + } + } +#endif + } + + StringConcat* merge(StringConcat* other, Node* arg); + + void set_allocation(AllocateNode* alloc) { + _begin = alloc; + } + + void append(Node* value, int mode) { + _arguments->add_req(value); + _mode.append(mode); + } + void push(Node* value, int mode) { + _arguments->ins_req(0, value); + _mode.insert_before(0, mode); + } + void push_string(Node* value) { + push(value, StringMode); + } + void push_int(Node* value) { + push(value, IntMode); + } + void push_char(Node* value) { + push(value, CharMode); + } + + Node* argument(int i) { + return _arguments->in(i); + } + void set_argument(int i, Node* value) { + _arguments->set_req(i, value); + } + int num_arguments() { + return _mode.length(); + } + int mode(int i) { + return _mode.at(i); + } + void add_control(Node* ctrl) { + assert(!_control.contains(ctrl), "only push once"); + _control.push(ctrl); + } + CallStaticJavaNode* end() { return _end; } + AllocateNode* begin() { return _begin; } + Node* string_alloc() { return _string_alloc; } + + void eliminate_unneeded_control(); + void eliminate_initialize(InitializeNode* init); + void eliminate_call(CallNode* call); + + void maybe_log_transform() { + CompileLog* log = _stringopts->C->log(); + if (log != NULL) { + log->head("replace_string_concat arguments='%d' string_alloc='%d' multiple='%d'", + num_arguments(), + _string_alloc != NULL, + _multiple); + JVMState* p = _begin->jvms(); + while (p != NULL) { + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); + p = p->caller(); + } + log->tail("replace_string_concat"); + } + } + + void convert_uncommon_traps(GraphKit& kit, const JVMState* jvms) { + for (uint u = 0; u < _uncommon_traps.size(); u++) { + Node* uct = _uncommon_traps.at(u); + + // Build a new call using the jvms state of the allocate + address call_addr = SharedRuntime::uncommon_trap_blob()->instructions_begin(); + const TypeFunc* call_type = OptoRuntime::uncommon_trap_Type(); + int size = call_type->domain()->cnt(); + const TypePtr* no_memory_effects = NULL; + Compile* C = _stringopts->C; + CallStaticJavaNode* call = new (C, size) CallStaticJavaNode(call_type, call_addr, "uncommon_trap", + jvms->bci(), no_memory_effects); + for (int e = 0; e < TypeFunc::Parms; e++) { + call->init_req(e, uct->in(e)); + } + // Set the trap request to record intrinsic failure if this trap + // is taken too many times. Ideally we would handle then traps by + // doing the original bookkeeping in the MDO so that if it caused + // the code to be thrown out we could still recompile and use the + // optimization. Failing the uncommon traps doesn't really mean + // that the optimization is a bad idea but there's no other way to + // do the MDO updates currently. + int trap_request = Deoptimization::make_trap_request(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + call->init_req(TypeFunc::Parms, __ intcon(trap_request)); + kit.add_safepoint_edges(call); + + _stringopts->gvn()->transform(call); + C->gvn_replace_by(uct, call); + uct->disconnect_inputs(NULL); + } + } + + void cleanup() { + // disconnect the hook node + _arguments->disconnect_inputs(NULL); + } +}; + + +void StringConcat::eliminate_unneeded_control() { + eliminate_initialize(begin()->initialization()); + for (uint i = 0; i < _control.size(); i++) { + Node* n = _control.at(i); + if (n->is_Call()) { + if (n != _end) { + eliminate_call(n->as_Call()); + } + } else if (n->is_IfTrue()) { + Compile* C = _stringopts->C; + C->gvn_replace_by(n, n->in(0)->in(0)); + C->gvn_replace_by(n->in(0), C->top()); + } + } +} + + +StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { + StringConcat* result = new StringConcat(_stringopts, _end); + for (uint x = 0; x < _control.size(); x++) { + Node* n = _control.at(x); + if (n->is_Call()) { + result->_control.push(n); + } + } + for (uint x = 0; x < other->_control.size(); x++) { + Node* n = other->_control.at(x); + if (n->is_Call()) { + result->_control.push(n); + } + } + assert(result->_control.contains(other->_end), "what?"); + assert(result->_control.contains(_begin), "what?"); + for (int x = 0; x < num_arguments(); x++) { + if (argument(x) == arg) { + // replace the toString result with the all the arguments that + // made up the other StringConcat + for (int y = 0; y < other->num_arguments(); y++) { + result->append(other->argument(y), other->mode(y)); + } + } else { + result->append(argument(x), mode(x)); + } + } + result->set_allocation(other->_begin); + result->_multiple = true; + return result; +} + + +void StringConcat::eliminate_call(CallNode* call) { + Compile* C = _stringopts->C; + CallProjections projs; + call->extract_projections(&projs, false); + if (projs.fallthrough_catchproj != NULL) { + C->gvn_replace_by(projs.fallthrough_catchproj, call->in(TypeFunc::Control)); + } + if (projs.fallthrough_memproj != NULL) { + C->gvn_replace_by(projs.fallthrough_memproj, call->in(TypeFunc::Memory)); + } + if (projs.catchall_memproj != NULL) { + C->gvn_replace_by(projs.catchall_memproj, C->top()); + } + if (projs.fallthrough_ioproj != NULL) { + C->gvn_replace_by(projs.fallthrough_ioproj, call->in(TypeFunc::I_O)); + } + if (projs.catchall_ioproj != NULL) { + C->gvn_replace_by(projs.catchall_ioproj, C->top()); + } + if (projs.catchall_catchproj != NULL) { + // EA can't cope with the partially collapsed graph this + // creates so put it on the worklist to be collapsed later. + for (SimpleDUIterator i(projs.catchall_catchproj); i.has_next(); i.next()) { + Node *use = i.get(); + int opc = use->Opcode(); + if (opc == Op_CreateEx || opc == Op_Region) { + _stringopts->record_dead_node(use); + } + } + C->gvn_replace_by(projs.catchall_catchproj, C->top()); + } + if (projs.resproj != NULL) { + C->gvn_replace_by(projs.resproj, C->top()); + } + C->gvn_replace_by(call, C->top()); +} + +void StringConcat::eliminate_initialize(InitializeNode* init) { + Compile* C = _stringopts->C; + + // Eliminate Initialize node. + assert(init->outcnt() <= 2, "only a control and memory projection expected"); + assert(init->req() <= InitializeNode::RawStores, "no pending inits"); + Node *ctrl_proj = init->proj_out(TypeFunc::Control); + if (ctrl_proj != NULL) { + C->gvn_replace_by(ctrl_proj, init->in(TypeFunc::Control)); + } + Node *mem_proj = init->proj_out(TypeFunc::Memory); + if (mem_proj != NULL) { + Node *mem = init->in(TypeFunc::Memory); + C->gvn_replace_by(mem_proj, mem); + } + C->gvn_replace_by(init, C->top()); + init->disconnect_inputs(NULL); +} + +Node_List PhaseStringOpts::collect_toString_calls() { + Node_List string_calls; + Node_List worklist; + + _visited.Clear(); + + // Prime the worklist + for (uint i = 1; i < C->root()->len(); i++) { + Node* n = C->root()->in(i); + if (n != NULL && !_visited.test_set(n->_idx)) { + worklist.push(n); + } + } + + while (worklist.size() > 0) { + Node* ctrl = worklist.pop(); + if (ctrl->is_CallStaticJava()) { + CallStaticJavaNode* csj = ctrl->as_CallStaticJava(); + ciMethod* m = csj->method(); + if (m != NULL && + (m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString || + m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString)) { + string_calls.push(csj); + } + } + if (ctrl->in(0) != NULL && !_visited.test_set(ctrl->in(0)->_idx)) { + worklist.push(ctrl->in(0)); + } + if (ctrl->is_Region()) { + for (uint i = 1; i < ctrl->len(); i++) { + if (ctrl->in(i) != NULL && !_visited.test_set(ctrl->in(i)->_idx)) { + worklist.push(ctrl->in(i)); + } + } + } + } + return string_calls; +} + + +StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) { + ciMethod* m = call->method(); + ciSymbol* string_sig; + ciSymbol* int_sig; + ciSymbol* char_sig; + if (m->holder() == C->env()->StringBuilder_klass()) { + string_sig = ciSymbol::String_StringBuilder_signature(); + int_sig = ciSymbol::int_StringBuilder_signature(); + char_sig = ciSymbol::char_StringBuilder_signature(); + } else if (m->holder() == C->env()->StringBuffer_klass()) { + string_sig = ciSymbol::String_StringBuffer_signature(); + int_sig = ciSymbol::int_StringBuffer_signature(); + char_sig = ciSymbol::char_StringBuffer_signature(); + } else { + return NULL; + } +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("considering toString call in "); + call->jvms()->dump_spec(tty); tty->cr(); + } +#endif + + StringConcat* sc = new StringConcat(this, call); + + AllocateNode* alloc = NULL; + InitializeNode* init = NULL; + + // possible opportunity for StringBuilder fusion + CallStaticJavaNode* cnode = call; + while (cnode) { + Node* recv = cnode->in(TypeFunc::Parms)->uncast(); + if (recv->is_Proj()) { + recv = recv->in(0); + } + cnode = recv->isa_CallStaticJava(); + if (cnode == NULL) { + alloc = recv->isa_Allocate(); + if (alloc == NULL) { + break; + } + // Find the constructor call + Node* result = alloc->result_cast(); + if (result == NULL || !result->is_CheckCastPP()) { + // strange looking allocation +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("giving up because allocation looks strange "); + alloc->jvms()->dump_spec(tty); tty->cr(); + } +#endif + break; + } + Node* constructor = NULL; + for (SimpleDUIterator i(result); i.has_next(); i.next()) { + CallStaticJavaNode *use = i.get()->isa_CallStaticJava(); + if (use != NULL && use->method() != NULL && + use->method()->name() == ciSymbol::object_initializer_name() && + use->method()->holder() == m->holder()) { + // Matched the constructor. + ciSymbol* sig = use->method()->signature()->as_symbol(); + if (sig == ciSymbol::void_method_signature() || + sig == ciSymbol::int_void_signature() || + sig == ciSymbol::string_void_signature()) { + if (sig == ciSymbol::string_void_signature()) { + // StringBuilder(String) so pick this up as the first argument + assert(use->in(TypeFunc::Parms + 1) != NULL, "what?"); + sc->push_string(use->in(TypeFunc::Parms + 1)); + } + // The int variant takes an initial size for the backing + // array so just treat it like the void version. + constructor = use; + } else { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("unexpected constructor signature: %s", sig->as_utf8()); + } +#endif + } + break; + } + } + if (constructor == NULL) { + // couldn't find constructor +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("giving up because couldn't find constructor "); + alloc->jvms()->dump_spec(tty); + } +#endif + break; + } + + // Walked all the way back and found the constructor call so see + // if this call converted into a direct string concatenation. + sc->add_control(call); + sc->add_control(constructor); + sc->add_control(alloc); + sc->set_allocation(alloc); + if (sc->validate_control_flow()) { + return sc; + } else { + return NULL; + } + } else if (cnode->method() == NULL) { + break; + } else if (cnode->method()->holder() == m->holder() && + cnode->method()->name() == ciSymbol::append_name() && + (cnode->method()->signature()->as_symbol() == string_sig || + cnode->method()->signature()->as_symbol() == char_sig || + cnode->method()->signature()->as_symbol() == int_sig)) { + sc->add_control(cnode); + Node* arg = cnode->in(TypeFunc::Parms + 1); + if (cnode->method()->signature()->as_symbol() == int_sig) { + sc->push_int(arg); + } else if (cnode->method()->signature()->as_symbol() == char_sig) { + sc->push_char(arg); + } else { + if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) { + CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava(); + if (csj->method() != NULL && + csj->method()->holder() == C->env()->Integer_klass() && + csj->method()->name() == ciSymbol::toString_name()) { + sc->add_control(csj); + sc->push_int(csj->in(TypeFunc::Parms)); + continue; + } + } + sc->push_string(arg); + } + continue; + } else { + // some unhandled signature +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print("giving up because encountered unexpected signature "); + cnode->tf()->dump(); tty->cr(); + cnode->in(TypeFunc::Parms + 1)->dump(); + } +#endif + break; + } + } + return NULL; +} + + +PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List*): + Phase(StringOpts), + _gvn(gvn), + _visited(Thread::current()->resource_area()) { + + assert(OptimizeStringConcat, "shouldn't be here"); + + size_table_field = C->env()->Integer_klass()->get_field_by_name(ciSymbol::make("sizeTable"), + ciSymbol::make("[I"), true); + if (size_table_field == NULL) { + // Something wrong so give up. + assert(false, "why can't we find Integer.sizeTable?"); + return; + } + + // Collect the types needed to talk about the various slices of memory + const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), + false, NULL, 0); + + const TypePtr* value_field_type = string_type->add_offset(java_lang_String::value_offset_in_bytes()); + const TypePtr* offset_field_type = string_type->add_offset(java_lang_String::offset_offset_in_bytes()); + const TypePtr* count_field_type = string_type->add_offset(java_lang_String::count_offset_in_bytes()); + + value_field_idx = C->get_alias_index(value_field_type); + count_field_idx = C->get_alias_index(count_field_type); + offset_field_idx = C->get_alias_index(offset_field_type); + char_adr_idx = C->get_alias_index(TypeAryPtr::CHARS); + + // For each locally allocated StringBuffer see if the usages can be + // collapsed into a single String construction. + + // Run through the list of allocation looking for SB.toString to see + // if it's possible to fuse the usage of the SB into a single String + // construction. + GrowableArray concats; + Node_List toStrings = collect_toString_calls(); + while (toStrings.size() > 0) { + StringConcat* sc = build_candidate(toStrings.pop()->as_CallStaticJava()); + if (sc != NULL) { + concats.push(sc); + } + } + + // try to coalesce separate concats + restart: + for (int c = 0; c < concats.length(); c++) { + StringConcat* sc = concats.at(c); + for (int i = 0; i < sc->num_arguments(); i++) { + Node* arg = sc->argument(i); + if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) { + CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava(); + if (csj->method() != NULL && + (csj->method()->holder() == C->env()->StringBuffer_klass() || + csj->method()->holder() == C->env()->StringBuilder_klass()) && + csj->method()->name() == ciSymbol::toString_name()) { + for (int o = 0; o < concats.length(); o++) { + if (c == o) continue; + StringConcat* other = concats.at(o); + if (other->end() == csj) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("considering stacked concats"); + } +#endif + + StringConcat* merged = sc->merge(other, arg); + if (merged->validate_control_flow()) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("stacking would succeed"); + } +#endif + if (c < o) { + concats.remove_at(o); + concats.at_put(c, merged); + } else { + concats.remove_at(c); + concats.at_put(o, merged); + } + goto restart; + } else { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("stacking would fail"); + } +#endif + } + } + } + } + } + } + } + + + for (int c = 0; c < concats.length(); c++) { + StringConcat* sc = concats.at(c); + replace_string_concat(sc); + } + + remove_dead_nodes(); +} + +void PhaseStringOpts::record_dead_node(Node* dead) { + dead_worklist.push(dead); +} + +void PhaseStringOpts::remove_dead_nodes() { + // Delete any dead nodes to make things clean enough that escape + // analysis doesn't get unhappy. + while (dead_worklist.size() > 0) { + Node* use = dead_worklist.pop(); + int opc = use->Opcode(); + switch (opc) { + case Op_Region: { + uint i = 1; + for (i = 1; i < use->req(); i++) { + if (use->in(i) != C->top()) { + break; + } + } + if (i >= use->req()) { + for (SimpleDUIterator i(use); i.has_next(); i.next()) { + Node* m = i.get(); + if (m->is_Phi()) { + dead_worklist.push(m); + } + } + C->gvn_replace_by(use, C->top()); + } + break; + } + case Op_AddP: + case Op_CreateEx: { + // Recurisvely clean up references to CreateEx so EA doesn't + // get unhappy about the partially collapsed graph. + for (SimpleDUIterator i(use); i.has_next(); i.next()) { + Node* m = i.get(); + if (m->is_AddP()) { + dead_worklist.push(m); + } + } + C->gvn_replace_by(use, C->top()); + break; + } + case Op_Phi: + if (use->in(0) == C->top()) { + C->gvn_replace_by(use, C->top()); + } + break; + } + } +} + + +bool StringConcat::validate_control_flow() { + // We found all the calls and arguments now lets see if it's + // safe to transform the graph as we would expect. + + // Check to see if this resulted in too many uncommon traps previously + if (Compile::current()->too_many_traps(_begin->jvms()->method(), _begin->jvms()->bci(), + Deoptimization::Reason_intrinsic)) { + return false; + } + + // Walk backwards over the control flow from toString to the + // allocation and make sure all the control flow is ok. This + // means it's either going to be eliminated once the calls are + // removed or it can safely be transformed into an uncommon + // trap. + + int null_check_count = 0; + Unique_Node_List ctrl_path; + + assert(_control.contains(_begin), "missing"); + assert(_control.contains(_end), "missing"); + + // Collect the nodes that we know about and will eliminate into ctrl_path + for (uint i = 0; i < _control.size(); i++) { + // Push the call and it's control projection + Node* n = _control.at(i); + if (n->is_Allocate()) { + AllocateNode* an = n->as_Allocate(); + InitializeNode* init = an->initialization(); + ctrl_path.push(init); + ctrl_path.push(init->as_Multi()->proj_out(0)); + } + if (n->is_Call()) { + CallNode* cn = n->as_Call(); + ctrl_path.push(cn); + ctrl_path.push(cn->proj_out(0)); + ctrl_path.push(cn->proj_out(0)->unique_out()); + ctrl_path.push(cn->proj_out(0)->unique_out()->as_Catch()->proj_out(0)); + } else { + ShouldNotReachHere(); + } + } + + // Skip backwards through the control checking for unexpected contro flow + Node* ptr = _end; + bool fail = false; + while (ptr != _begin) { + if (ptr->is_Call() && ctrl_path.member(ptr)) { + ptr = ptr->in(0); + } else if (ptr->is_CatchProj() && ctrl_path.member(ptr)) { + ptr = ptr->in(0)->in(0)->in(0); + assert(ctrl_path.member(ptr), "should be a known piece of control"); + } else if (ptr->is_IfTrue()) { + IfNode* iff = ptr->in(0)->as_If(); + BoolNode* b = iff->in(1)->isa_Bool(); + Node* cmp = b->in(1); + Node* v1 = cmp->in(1); + Node* v2 = cmp->in(2); + Node* otherproj = iff->proj_out(1 - ptr->as_Proj()->_con); + + // Null check of the return of append which can simply be eliminated + if (b->_test._test == BoolTest::ne && + v2->bottom_type() == TypePtr::NULL_PTR && + v1->is_Proj() && ctrl_path.member(v1->in(0))) { + // NULL check of the return value of the append + null_check_count++; + if (otherproj->outcnt() == 1) { + CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); + if (call != NULL && call->_name != NULL && strcmp(call->_name, "uncommon_trap") == 0) { + ctrl_path.push(call); + } + } + _control.push(ptr); + ptr = ptr->in(0)->in(0); + continue; + } + + // A test which leads to an uncommon trap which should be safe. + // Later this trap will be converted into a trap that restarts + // at the beginning. + if (otherproj->outcnt() == 1) { + CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); + if (call != NULL && call->_name != NULL && strcmp(call->_name, "uncommon_trap") == 0) { + // control flow leads to uct so should be ok + _uncommon_traps.push(call); + ctrl_path.push(call); + ptr = ptr->in(0)->in(0); + continue; + } + } + +#ifndef PRODUCT + // Some unexpected control flow we don't know how to handle. + if (PrintOptimizeStringConcat) { + tty->print_cr("failing with unknown test"); + b->dump(); + cmp->dump(); + v1->dump(); + v2->dump(); + tty->cr(); + } +#endif + break; + } else if (ptr->is_Proj() && ptr->in(0)->is_Initialize()) { + ptr = ptr->in(0)->in(0); + } else if (ptr->is_Region()) { + Node* copy = ptr->as_Region()->is_copy(); + if (copy != NULL) { + ptr = copy; + continue; + } + if (ptr->req() == 3 && + ptr->in(1) != NULL && ptr->in(1)->is_Proj() && + ptr->in(2) != NULL && ptr->in(2)->is_Proj() && + ptr->in(1)->in(0) == ptr->in(2)->in(0) && + ptr->in(1)->in(0) != NULL && ptr->in(1)->in(0)->is_If()) { + // Simple diamond. + // XXX should check for possibly merging stores. simple data merges are ok. + ptr = ptr->in(1)->in(0)->in(0); + continue; + } +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("fusion would fail for region"); + _begin->dump(); + ptr->dump(2); + } +#endif + fail = true; + break; + } else { + // other unknown control + if (!fail) { +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + tty->print_cr("fusion would fail for"); + _begin->dump(); + } +#endif + fail = true; + } +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + ptr->dump(); + } +#endif + ptr = ptr->in(0); + } + } +#ifndef PRODUCT + if (PrintOptimizeStringConcat && fail) { + tty->cr(); + } +#endif + if (fail) return !fail; + + // Validate that all these results produced are contained within + // this cluster of objects. First collect all the results produced + // by calls in the region. + _stringopts->_visited.Clear(); + Node_List worklist; + Node* final_result = _end->proj_out(TypeFunc::Parms); + for (uint i = 0; i < _control.size(); i++) { + CallNode* cnode = _control.at(i)->isa_Call(); + if (cnode != NULL) { + _stringopts->_visited.test_set(cnode->_idx); + } + Node* result = cnode != NULL ? cnode->proj_out(TypeFunc::Parms) : NULL; + if (result != NULL && result != final_result) { + worklist.push(result); + } + } + + Node* last_result = NULL; + while (worklist.size() > 0) { + Node* result = worklist.pop(); + if (_stringopts->_visited.test_set(result->_idx)) + continue; + for (SimpleDUIterator i(result); i.has_next(); i.next()) { + Node *use = i.get(); + if (ctrl_path.member(use)) { + // already checked this + continue; + } + int opc = use->Opcode(); + if (opc == Op_CmpP || opc == Op_Node) { + ctrl_path.push(use); + continue; + } + if (opc == Op_CastPP || opc == Op_CheckCastPP) { + for (SimpleDUIterator j(use); j.has_next(); j.next()) { + worklist.push(j.get()); + } + worklist.push(use->in(1)); + ctrl_path.push(use); + continue; + } +#ifndef PRODUCT + if (PrintOptimizeStringConcat) { + if (result != last_result) { + last_result = result; + tty->print_cr("extra uses for result:"); + last_result->dump(); + } + use->dump(); + } +#endif + fail = true; + break; + } + } + +#ifndef PRODUCT + if (PrintOptimizeStringConcat && !fail) { + ttyLocker ttyl; + tty->cr(); + tty->print("fusion would succeed (%d %d) for ", null_check_count, _uncommon_traps.size()); + _begin->jvms()->dump_spec(tty); tty->cr(); + for (int i = 0; i < num_arguments(); i++) { + argument(i)->dump(); + } + _control.dump(); + tty->cr(); + } +#endif + + return !fail; +} + +Node* PhaseStringOpts::fetch_static_field(GraphKit& kit, ciField* field) { + const TypeKlassPtr* klass_type = TypeKlassPtr::make(field->holder()); + Node* klass_node = __ makecon(klass_type); + BasicType bt = field->layout_type(); + ciType* field_klass = field->type(); + + const Type *type; + if( bt == T_OBJECT ) { + if (!field->type()->is_loaded()) { + type = TypeInstPtr::BOTTOM; + } else if (field->is_constant()) { + // This can happen if the constant oop is non-perm. + ciObject* con = field->constant_value().as_object(); + // Do not "join" in the previous type; it doesn't add value, + // and may yield a vacuous result if the field is of interface type. + type = TypeOopPtr::make_from_constant(con)->isa_oopptr(); + assert(type != NULL, "field singleton type must be consistent"); + } else { + type = TypeOopPtr::make_from_klass(field_klass->as_klass()); + } + } else { + type = Type::get_const_basic_type(bt); + } + + return kit.make_load(NULL, kit.basic_plus_adr(klass_node, field->offset_in_bytes()), + type, T_OBJECT, + C->get_alias_index(klass_type->add_offset(field->offset_in_bytes()))); +} + +Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { + RegionNode *final_merge = new (C, 3) RegionNode(3); + kit.gvn().set_type(final_merge, Type::CONTROL); + Node* final_size = new (C, 3) PhiNode(final_merge, TypeInt::INT); + kit.gvn().set_type(final_size, TypeInt::INT); + + IfNode* iff = kit.create_and_map_if(kit.control(), + __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + Node* is_min = __ IfFalse(iff); + final_merge->init_req(1, is_min); + final_size->init_req(1, __ intcon(11)); + + kit.set_control(__ IfTrue(iff)); + if (kit.stopped()) { + final_merge->init_req(2, C->top()); + final_size->init_req(2, C->top()); + } else { + + // int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); + RegionNode *r = new (C, 3) RegionNode(3); + kit.gvn().set_type(r, Type::CONTROL); + Node *phi = new (C, 3) PhiNode(r, TypeInt::INT); + kit.gvn().set_type(phi, TypeInt::INT); + Node *size = new (C, 3) PhiNode(r, TypeInt::INT); + kit.gvn().set_type(size, TypeInt::INT); + Node* chk = __ CmpI(arg, __ intcon(0)); + Node* p = __ Bool(chk, BoolTest::lt); + IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_FAIR, COUNT_UNKNOWN); + Node* lessthan = __ IfTrue(iff); + Node* greaterequal = __ IfFalse(iff); + r->init_req(1, lessthan); + phi->init_req(1, __ SubI(__ intcon(0), arg)); + size->init_req(1, __ intcon(1)); + r->init_req(2, greaterequal); + phi->init_req(2, arg); + size->init_req(2, __ intcon(0)); + kit.set_control(r); + C->record_for_igvn(r); + C->record_for_igvn(phi); + C->record_for_igvn(size); + + // for (int i=0; ; i++) + // if (x <= sizeTable[i]) + // return i+1; + RegionNode *loop = new (C, 3) RegionNode(3); + loop->init_req(1, kit.control()); + kit.gvn().set_type(loop, Type::CONTROL); + + Node *index = new (C, 3) PhiNode(loop, TypeInt::INT); + index->init_req(1, __ intcon(0)); + kit.gvn().set_type(index, TypeInt::INT); + kit.set_control(loop); + Node* sizeTable = fetch_static_field(kit, size_table_field); + + Node* value = kit.load_array_element(NULL, sizeTable, index, TypeAryPtr::INTS); + C->record_for_igvn(value); + Node* limit = __ CmpI(phi, value); + Node* limitb = __ Bool(limit, BoolTest::le); + IfNode* iff2 = kit.create_and_map_if(kit.control(), limitb, PROB_MIN, COUNT_UNKNOWN); + Node* lessEqual = __ IfTrue(iff2); + Node* greater = __ IfFalse(iff2); + + loop->init_req(2, greater); + index->init_req(2, __ AddI(index, __ intcon(1))); + + kit.set_control(lessEqual); + C->record_for_igvn(loop); + C->record_for_igvn(index); + + final_merge->init_req(2, kit.control()); + final_size->init_req(2, __ AddI(__ AddI(index, size), __ intcon(1))); + } + + kit.set_control(final_merge); + C->record_for_igvn(final_merge); + C->record_for_igvn(final_size); + + return final_size; +} + +void PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* char_array, Node* start, Node* end) { + RegionNode *final_merge = new (C, 4) RegionNode(4); + kit.gvn().set_type(final_merge, Type::CONTROL); + Node *final_mem = PhiNode::make(final_merge, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); + kit.gvn().set_type(final_mem, Type::MEMORY); + + // need to handle Integer.MIN_VALUE specially because negating doesn't make it positive + { + // i == MIN_VALUE + IfNode* iff = kit.create_and_map_if(kit.control(), + __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + + Node* old_mem = kit.memory(char_adr_idx); + + kit.set_control(__ IfFalse(iff)); + if (kit.stopped()) { + // Statically not equal to MIN_VALUE so this path is dead + final_merge->init_req(3, kit.control()); + } else { + copy_string(kit, __ makecon(TypeInstPtr::make(C->env()->the_min_jint_string())), + char_array, start); + final_merge->init_req(3, kit.control()); + final_mem->init_req(3, kit.memory(char_adr_idx)); + } + + kit.set_control(__ IfTrue(iff)); + kit.set_memory(old_mem, char_adr_idx); + } + + + // Simplified version of Integer.getChars + + // int q, r; + // int charPos = index; + Node* charPos = end; + + // char sign = 0; + + Node* i = arg; + Node* sign = __ intcon(0); + + // if (i < 0) { + // sign = '-'; + // i = -i; + // } + { + IfNode* iff = kit.create_and_map_if(kit.control(), + __ Bool(__ CmpI(arg, __ intcon(0)), BoolTest::lt), + PROB_FAIR, COUNT_UNKNOWN); + + RegionNode *merge = new (C, 3) RegionNode(3); + kit.gvn().set_type(merge, Type::CONTROL); + i = new (C, 3) PhiNode(merge, TypeInt::INT); + kit.gvn().set_type(i, TypeInt::INT); + sign = new (C, 3) PhiNode(merge, TypeInt::INT); + kit.gvn().set_type(sign, TypeInt::INT); + + merge->init_req(1, __ IfTrue(iff)); + i->init_req(1, __ SubI(__ intcon(0), arg)); + sign->init_req(1, __ intcon('-')); + merge->init_req(2, __ IfFalse(iff)); + i->init_req(2, arg); + sign->init_req(2, __ intcon(0)); + + kit.set_control(merge); + + C->record_for_igvn(merge); + C->record_for_igvn(i); + C->record_for_igvn(sign); + } + + // for (;;) { + // q = i / 10; + // r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... + // buf [--charPos] = digits [r]; + // i = q; + // if (i == 0) break; + // } + + { + RegionNode *head = new (C, 3) RegionNode(3); + head->init_req(1, kit.control()); + kit.gvn().set_type(head, Type::CONTROL); + Node *i_phi = new (C, 3) PhiNode(head, TypeInt::INT); + i_phi->init_req(1, i); + kit.gvn().set_type(i_phi, TypeInt::INT); + charPos = PhiNode::make(head, charPos); + kit.gvn().set_type(charPos, TypeInt::INT); + Node *mem = PhiNode::make(head, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); + kit.gvn().set_type(mem, Type::MEMORY); + kit.set_control(head); + kit.set_memory(mem, char_adr_idx); + + Node* q = __ DivI(NULL, i_phi, __ intcon(10)); + Node* r = __ SubI(i_phi, __ AddI(__ LShiftI(q, __ intcon(3)), + __ LShiftI(q, __ intcon(1)))); + Node* m1 = __ SubI(charPos, __ intcon(1)); + 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); + + + IfNode* iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + Node* ne = __ IfTrue(iff); + Node* eq = __ IfFalse(iff); + + head->init_req(2, ne); + mem->init_req(2, st); + i_phi->init_req(2, q); + charPos->init_req(2, m1); + + charPos = m1; + + kit.set_control(eq); + kit.set_memory(st, char_adr_idx); + + C->record_for_igvn(head); + C->record_for_igvn(mem); + C->record_for_igvn(i_phi); + C->record_for_igvn(charPos); + } + + { + // if (sign != 0) { + // buf [--charPos] = sign; + // } + IfNode* iff = kit.create_and_map_if(kit.control(), + __ Bool(__ CmpI(sign, __ intcon(0)), BoolTest::ne), + PROB_FAIR, COUNT_UNKNOWN); + + final_merge->init_req(2, __ IfFalse(iff)); + final_mem->init_req(2, kit.memory(char_adr_idx)); + + kit.set_control(__ IfTrue(iff)); + if (kit.stopped()) { + final_merge->init_req(1, C->top()); + final_mem->init_req(1, C->top()); + } 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); + + final_merge->init_req(1, kit.control()); + final_mem->init_req(1, st); + } + + kit.set_control(final_merge); + kit.set_memory(final_mem, char_adr_idx); + + C->record_for_igvn(final_merge); + C->record_for_igvn(final_mem); + } +} + + +Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start) { + Node* string = str; + Node* offset = kit.make_load(NULL, + kit.basic_plus_adr(string, string, java_lang_String::offset_offset_in_bytes()), + TypeInt::INT, T_INT, offset_field_idx); + Node* count = kit.make_load(NULL, + kit.basic_plus_adr(string, string, java_lang_String::count_offset_in_bytes()), + TypeInt::INT, T_INT, count_field_idx); + const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull, + TypeAry::make(TypeInt::CHAR,TypeInt::POS), + ciTypeArrayKlass::make(T_CHAR), true, 0); + Node* value = kit.make_load(NULL, + kit.basic_plus_adr(string, string, java_lang_String::value_offset_in_bytes()), + value_type, T_OBJECT, value_field_idx); + + // copy the contents + if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) { + // For small constant strings just emit individual stores. + // A length of 6 seems like a good space/speed tradeof. + int c = count->get_int(); + int o = offset->get_int(); + const TypeOopPtr* t = kit.gvn().type(value)->isa_oopptr(); + 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); + start = __ AddI(start, __ intcon(1)); + } + } else { + Node* src_ptr = kit.array_element_address(value, offset, T_CHAR); + Node* dst_ptr = kit.array_element_address(char_array, start, T_CHAR); + Node* c = count; + Node* extra = NULL; +#ifdef _LP64 + c = __ ConvI2L(c); + extra = C->top(); +#endif + Node* call = kit.make_runtime_call(GraphKit::RC_LEAF|GraphKit::RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + CAST_FROM_FN_PTR(address, StubRoutines::jshort_disjoint_arraycopy()), + "jshort_disjoint_arraycopy", TypeAryPtr::CHARS, + src_ptr, dst_ptr, c, extra); + start = __ AddI(start, count); + } + return start; +} + + +void PhaseStringOpts::replace_string_concat(StringConcat* sc) { + // Log a little info about the transformation + sc->maybe_log_transform(); + + // pull the JVMState of the allocation into a SafePointNode to serve as + // as a shim for the insertion of the new code. + JVMState* jvms = sc->begin()->jvms()->clone_shallow(C); + uint size = sc->begin()->req(); + SafePointNode* map = new (C, size) SafePointNode(size, jvms); + + // copy the control and memory state from the final call into our + // new starting state. This allows any preceeding tests to feed + // into the new section of code. + for (uint i1 = 0; i1 < TypeFunc::Parms; i1++) { + map->init_req(i1, sc->end()->in(i1)); + } + // blow away old allocation arguments + for (uint i1 = TypeFunc::Parms; i1 < jvms->debug_start(); i1++) { + map->init_req(i1, C->top()); + } + // Copy the rest of the inputs for the JVMState + for (uint i1 = jvms->debug_start(); i1 < sc->begin()->req(); i1++) { + map->init_req(i1, sc->begin()->in(i1)); + } + // Make sure the memory state is a MergeMem for parsing. + if (!map->in(TypeFunc::Memory)->is_MergeMem()) { + map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory))); + } + + jvms->set_map(map); + map->ensure_stack(jvms, jvms->method()->max_stack()); + + + // disconnect all the old StringBuilder calls from the graph + sc->eliminate_unneeded_control(); + + // At this point all the old work has been completely removed from + // the graph and the saved JVMState exists at the point where the + // final toString call used to be. + GraphKit kit(jvms); + + // There may be uncommon traps which are still using the + // intermediate states and these need to be rewritten to point at + // the JVMState at the beginning of the transformation. + sc->convert_uncommon_traps(kit, jvms); + + // Now insert the logic to compute the size of the string followed + // by all the logic to construct array and resulting string. + + Node* null_string = __ makecon(TypeInstPtr::make(C->env()->the_null_string())); + + // Create a region for the overflow checks to merge into. + int args = MAX2(sc->num_arguments(), 1); + RegionNode* overflow = new (C, args) RegionNode(args); + kit.gvn().set_type(overflow, Type::CONTROL); + + // Create a hook node to hold onto the individual sizes since they + // are need for the copying phase. + Node* string_sizes = new (C, args) Node(args); + + Node* length = __ intcon(0); + for (int argi = 0; argi < sc->num_arguments(); argi++) { + Node* arg = sc->argument(argi); + switch (sc->mode(argi)) { + case StringConcat::IntMode: { + Node* string_size = int_stringSize(kit, arg); + + // accumulate total + length = __ AddI(length, string_size); + + // Cache this value for the use by int_toString + string_sizes->init_req(argi, string_size); + break; + } + case StringConcat::StringMode: { + const Type* type = kit.gvn().type(arg); + if (type == TypePtr::NULL_PTR) { + // replace the argument with the null checked version + arg = null_string; + sc->set_argument(argi, arg); + } else if (!type->higher_equal(TypeInstPtr::NOTNULL)) { + // s = s != null ? s : "null"; + // length = length + (s.count - s.offset); + RegionNode *r = new (C, 3) RegionNode(3); + kit.gvn().set_type(r, Type::CONTROL); + Node *phi = new (C, 3) PhiNode(r, type); + kit.gvn().set_type(phi, phi->bottom_type()); + Node* p = __ Bool(__ CmpP(arg, kit.null()), BoolTest::ne); + IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_MIN, COUNT_UNKNOWN); + Node* notnull = __ IfTrue(iff); + Node* isnull = __ IfFalse(iff); + kit.set_control(notnull); // set control for the cast_not_null + r->init_req(1, notnull); + phi->init_req(1, kit.cast_not_null(arg, false)); + r->init_req(2, isnull); + phi->init_req(2, null_string); + kit.set_control(r); + C->record_for_igvn(r); + C->record_for_igvn(phi); + // replace the argument with the null checked version + arg = phi; + sc->set_argument(argi, arg); + } + // Node* offset = kit.make_load(NULL, kit.basic_plus_adr(arg, arg, offset_offset), + // TypeInt::INT, T_INT, offset_field_idx); + Node* count = kit.make_load(NULL, kit.basic_plus_adr(arg, arg, java_lang_String::count_offset_in_bytes()), + TypeInt::INT, T_INT, count_field_idx); + length = __ AddI(length, count); + string_sizes->init_req(argi, NULL); + break; + } + case StringConcat::CharMode: { + // one character only + length = __ AddI(length, __ intcon(1)); + break; + } + default: + ShouldNotReachHere(); + } + if (argi > 0) { + // Check that the sum hasn't overflowed + IfNode* iff = kit.create_and_map_if(kit.control(), + __ Bool(__ CmpI(length, __ intcon(0)), BoolTest::lt), + PROB_MIN, COUNT_UNKNOWN); + kit.set_control(__ IfFalse(iff)); + overflow->set_req(argi, __ IfTrue(iff)); + } + } + + { + // Hook + PreserveJVMState pjvms(&kit); + kit.set_control(overflow); + kit.uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + } + + // length now contains the number of characters needed for the + // char[] so create a new AllocateArray for the char[] + Node* char_array = NULL; + { + PreserveReexecuteState preexecs(&kit); + // The original jvms is for an allocation of either a String or + // StringBuffer so no stack adjustment is necessary for proper + // reexecution. If we deoptimize in the slow path the bytecode + // will be reexecuted and the char[] allocation will be thrown away. + kit.jvms()->set_should_reexecute(true); + char_array = kit.new_array(__ makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_CHAR))), + length, 1); + } + + // Mark the allocation so that zeroing is skipped since the code + // below will overwrite the entire array + AllocateArrayNode* char_alloc = AllocateArrayNode::Ideal_array_allocation(char_array, _gvn); + char_alloc->maybe_set_complete(_gvn); + + // Now copy the string representations into the final char[] + Node* start = __ intcon(0); + for (int argi = 0; argi < sc->num_arguments(); argi++) { + Node* arg = sc->argument(argi); + switch (sc->mode(argi)) { + case StringConcat::IntMode: { + Node* end = __ AddI(start, string_sizes->in(argi)); + // getChars words backwards so pass the ending point as well as the start + int_getChars(kit, arg, char_array, start, end); + start = end; + break; + } + case StringConcat::StringMode: { + start = copy_string(kit, arg, char_array, start); + break; + } + case StringConcat::CharMode: { + __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), + arg, T_CHAR, char_adr_idx); + start = __ AddI(start, __ intcon(1)); + break; + } + default: + ShouldNotReachHere(); + } + } + + // If we're not reusing an existing String allocation then allocate one here. + Node* result = sc->string_alloc(); + if (result == NULL) { + PreserveReexecuteState preexecs(&kit); + // The original jvms is for an allocation of either a String or + // StringBuffer so no stack adjustment is necessary for proper + // reexecution. + kit.jvms()->set_should_reexecute(true); + result = kit.new_instance(__ makecon(TypeKlassPtr::make(C->env()->String_klass()))); + } + + // Intialize the string + kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::offset_offset_in_bytes()), + __ intcon(0), T_INT, offset_field_idx); + kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::count_offset_in_bytes()), + length, T_INT, count_field_idx); + kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::value_offset_in_bytes()), + char_array, T_OBJECT, value_field_idx); + + // hook up the outgoing control and result + kit.replace_call(sc->end(), result); + + // Unhook any hook nodes + string_sizes->disconnect_inputs(NULL); + sc->cleanup(); +} diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/stringopts.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/opto/stringopts.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -0,0 +1,83 @@ +/* + * Copyright 2009 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class StringConcat; + +class PhaseStringOpts : public Phase { + friend class StringConcat; + + private: + PhaseGVN* _gvn; + + // List of dead nodes to clean up aggressively at the end + Unique_Node_List dead_worklist; + + // Memory slices needed for code gen + int char_adr_idx; + int value_field_idx; + int count_field_idx; + int offset_field_idx; + + // Integer.sizeTable - used for int to String conversion + ciField* size_table_field; + + // A set for use by various stages + VectorSet _visited; + + // Collect a list of all SB.toString calls + Node_List collect_toString_calls(); + + // Examine the use of the SB alloc to see if it can be replace with + // a single string construction. + StringConcat* build_candidate(CallStaticJavaNode* call); + + // Replace all the SB calls in concat with an optimization String allocation + void replace_string_concat(StringConcat* concat); + + // Load the value of a static field, performing any constant folding. + Node* fetch_static_field(GraphKit& kit, ciField* field); + + // Compute the number of characters required to represent the int value + Node* int_stringSize(GraphKit& kit, Node* value); + + // Copy the characters representing value into char_array starting at start + void int_getChars(GraphKit& kit, Node* value, Node* char_array, Node* start, Node* end); + + // Copy of the contents of the String str into char_array starting at index start. + Node* copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start); + + // Clean up any leftover nodes + void record_dead_node(Node* node); + void remove_dead_nodes(); + + PhaseGVN* gvn() { return _gvn; } + + enum { + // max length of constant string copy unrolling in copy_string + unroll_string_copy_length = 6 + }; + + public: + PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List* worklist); +}; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/subnode.cpp --- a/src/share/vm/opto/subnode.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/subnode.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 Sun Microsystems, 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 @@ -1244,8 +1244,7 @@ if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; double d = t1->getd(); - if( d < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dcos( d ) ); + return TypeD::make( StubRoutines::intrinsic_cos( d ) ); } //============================================================================= @@ -1256,8 +1255,7 @@ if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; double d = t1->getd(); - if( d < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dsin( d ) ); + return TypeD::make( StubRoutines::intrinsic_sin( d ) ); } //============================================================================= @@ -1268,8 +1266,7 @@ if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; double d = t1->getd(); - if( d < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dtan( d ) ); + return TypeD::make( StubRoutines::intrinsic_tan( d ) ); } //============================================================================= @@ -1280,8 +1277,7 @@ if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; double d = t1->getd(); - if( d < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dlog( d ) ); + return TypeD::make( StubRoutines::intrinsic_log( d ) ); } //============================================================================= @@ -1292,8 +1288,7 @@ if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; double d = t1->getd(); - if( d < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dlog10( d ) ); + return TypeD::make( StubRoutines::intrinsic_log10( d ) ); } //============================================================================= @@ -1304,8 +1299,7 @@ if( t1 == Type::TOP ) return Type::TOP; if( t1->base() != Type::DoubleCon ) return Type::DOUBLE; double d = t1->getd(); - if( d < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dexp( d ) ); + return TypeD::make( StubRoutines::intrinsic_exp( d ) ); } @@ -1323,5 +1317,5 @@ double d2 = t2->getd(); if( d1 < 0.0 ) return Type::DOUBLE; if( d2 < 0.0 ) return Type::DOUBLE; - return TypeD::make( SharedRuntime::dpow( d1, d2 ) ); + return TypeD::make( StubRoutines::intrinsic_pow( d1, d2 ) ); } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/superword.cpp --- a/src/share/vm/opto/superword.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/superword.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -457,10 +457,6 @@ } else if (out->Opcode() == Op_StoreCM && out->in(MemNode::OopStore) == n) { // StoreCM has an input edge used as a precedence edge. // Maybe an issue when oop stores are vectorized. - } else if( out->is_MergeMem() && prev && - prev->Opcode() == Op_StoreCM && out == prev->in(MemNode::OopStore)) { - // Oop store is a MergeMem! This should not happen. Temporarily remove the assertion - // for this case because it could not be superwordized anyway. } else { assert(out == prev || prev == NULL, "no branches off of store slice"); } @@ -477,6 +473,12 @@ // Can s1 and s2 be in a pack with s1 immediately preceding s2 and // s1 aligned at "align" bool SuperWord::stmts_can_pack(Node* s1, Node* s2, int align) { + + // Do not use superword for non-primitives + if((s1->is_Mem() && !is_java_primitive(s1->as_Mem()->memory_type())) || + (s2->is_Mem() && !is_java_primitive(s2->as_Mem()->memory_type()))) + return false; + if (isomorphic(s1, s2)) { if (independent(s1, s2)) { if (!exists_at(s1, 0) && !exists_at(s2, 1)) { @@ -990,8 +992,8 @@ // (5) We know there is no dependence cycle, so there in no other case; // (6) Finally, all memory ops in another single pack should be moved in the same direction. // -// To schedule a load pack: the memory edge of every loads in the pack must be -// the same as the memory edge of the last executed load in the pack +// To schedule a load pack, we use the memory state of either the first or the last load in +// the pack, based on the dependence constraint. void SuperWord::co_locate_pack(Node_List* pk) { if (pk->at(0)->is_Store()) { MemNode* first = executed_first(pk)->as_Mem(); @@ -1076,15 +1078,32 @@ current = my_mem->as_Mem(); } // end while } else if (pk->at(0)->is_Load()) { //load - // all use the memory state that the last executed load uses - LoadNode* last_load = executed_last(pk)->as_Load(); - Node* last_mem = last_load->in(MemNode::Memory); - _igvn.hash_delete(last_mem); - // Give each load same memory state as last + // all loads in the pack should have the same memory state. By default, + // we use the memory state of the last load. However, if any load could + // not be moved down due to the dependence constraint, we use the memory + // state of the first load. + Node* last_mem = executed_last(pk)->in(MemNode::Memory); + Node* first_mem = executed_first(pk)->in(MemNode::Memory); + bool schedule_last = true; + for (uint i = 0; i < pk->size(); i++) { + Node* ld = pk->at(i); + for (Node* current = last_mem; current != ld->in(MemNode::Memory); + current=current->in(MemNode::Memory)) { + assert(current != first_mem, "corrupted memory graph"); + if(current->is_Mem() && !independent(current, ld)){ + schedule_last = false; // a later store depends on this load + break; + } + } + } + + Node* mem_input = schedule_last ? last_mem : first_mem; + _igvn.hash_delete(mem_input); + // Give each load the same memory state for (uint i = 0; i < pk->size(); i++) { LoadNode* ld = pk->at(i)->as_Load(); _igvn.hash_delete(ld); - ld->set_req(MemNode::Memory, last_mem); + ld->set_req(MemNode::Memory, mem_input); _igvn._worklist.push(ld); } } @@ -1902,6 +1921,11 @@ } // Match AddP(base, AddP(ptr, k*iv [+ invariant]), constant) Node* base = adr->in(AddPNode::Base); + //unsafe reference could not be aligned appropriately without runtime checking + if (base == NULL || base->bottom_type() == Type::TOP) { + assert(!valid(), "unsafe access"); + return; + } for (int i = 0; i < 3; i++) { if (!scaled_iv_plus_offset(adr->in(AddPNode::Offset))) { assert(!valid(), "too complex"); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/type.cpp --- a/src/share/vm/opto/type.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/type.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -296,7 +296,7 @@ false, 0, oopDesc::mark_offset_in_bytes()); TypeInstPtr::KLASS = TypeInstPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), false, 0, oopDesc::klass_offset_in_bytes()); - TypeOopPtr::BOTTOM = TypeOopPtr::make(TypePtr::BotPTR, OffsetBot); + TypeOopPtr::BOTTOM = TypeOopPtr::make(TypePtr::BotPTR, OffsetBot, TypeOopPtr::InstanceBot); TypeNarrowOop::NULL_PTR = TypeNarrowOop::make( TypePtr::NULL_PTR ); TypeNarrowOop::BOTTOM = TypeNarrowOop::make( TypeInstPtr::BOTTOM ); @@ -492,8 +492,13 @@ bool Type::interface_vs_oop(const Type *t) const { bool result = false; - const TypeInstPtr* this_inst = this->isa_instptr(); - const TypeInstPtr* t_inst = t->isa_instptr(); + const TypePtr* this_ptr = this->make_ptr(); // In case it is narrow_oop + const TypePtr* t_ptr = t->make_ptr(); + if( this_ptr == NULL || t_ptr == NULL ) + return result; + + const TypeInstPtr* this_inst = this_ptr->isa_instptr(); + const TypeInstPtr* t_inst = t_ptr->isa_instptr(); if( this_inst && this_inst->is_loaded() && t_inst && t_inst->is_loaded() ) { bool this_interface = this_inst->klass()->is_interface(); bool t_interface = t_inst->klass()->is_interface(); @@ -1110,7 +1115,7 @@ //------------------------------widen------------------------------------------ // Only happens for optimistic top-down optimizations. -const Type *TypeInt::widen( const Type *old ) const { +const Type *TypeInt::widen( const Type *old, const Type* limit ) const { // Coming from TOP or such; no widening if( old->base() != Int ) return this; const TypeInt *ot = old->is_int(); @@ -1129,15 +1134,21 @@ // Now widen new guy. // Check for widening too far if (_widen == WidenMax) { - if (min_jint < _lo && _hi < max_jint) { + int max = max_jint; + int min = min_jint; + if (limit->isa_int()) { + max = limit->is_int()->_hi; + min = limit->is_int()->_lo; + } + if (min < _lo && _hi < max) { // If neither endpoint is extremal yet, push out the endpoint // which is closer to its respective limit. if (_lo >= 0 || // easy common case - (juint)(_lo - min_jint) >= (juint)(max_jint - _hi)) { + (juint)(_lo - min) >= (juint)(max - _hi)) { // Try to widen to an unsigned range type of 31 bits: - return make(_lo, max_jint, WidenMax); + return make(_lo, max, WidenMax); } else { - return make(min_jint, _hi, WidenMax); + return make(min, _hi, WidenMax); } } return TypeInt::INT; @@ -1352,7 +1363,7 @@ //------------------------------widen------------------------------------------ // Only happens for optimistic top-down optimizations. -const Type *TypeLong::widen( const Type *old ) const { +const Type *TypeLong::widen( const Type *old, const Type* limit ) const { // Coming from TOP or such; no widening if( old->base() != Long ) return this; const TypeLong *ot = old->is_long(); @@ -1371,18 +1382,24 @@ // Now widen new guy. // Check for widening too far if (_widen == WidenMax) { - if (min_jlong < _lo && _hi < max_jlong) { + jlong max = max_jlong; + jlong min = min_jlong; + if (limit->isa_long()) { + max = limit->is_long()->_hi; + min = limit->is_long()->_lo; + } + if (min < _lo && _hi < max) { // If neither endpoint is extremal yet, push out the endpoint // which is closer to its respective limit. if (_lo >= 0 || // easy common case - (julong)(_lo - min_jlong) >= (julong)(max_jlong - _hi)) { + (julong)(_lo - min) >= (julong)(max - _hi)) { // Try to widen to an unsigned range type of 32/63 bits: - if (_hi < max_juint) + if (max >= max_juint && _hi < max_juint) return make(_lo, max_juint, WidenMax); else - return make(_lo, max_jlong, WidenMax); + return make(_lo, max, WidenMax); } else { - return make(min_jlong, _hi, WidenMax); + return make(min, _hi, WidenMax); } } return TypeLong::LONG; @@ -2236,12 +2253,12 @@ //------------------------------make------------------------------------------- const TypeOopPtr *TypeOopPtr::make(PTR ptr, - int offset) { + int offset, int instance_id) { assert(ptr != Constant, "no constant generic pointers"); ciKlass* k = ciKlassKlass::make(); bool xk = false; ciObject* o = NULL; - return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, InstanceBot))->hashcons(); + return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, instance_id))->hashcons(); } @@ -2249,7 +2266,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); + return make(ptr, _offset, _instance_id); } //-----------------------------cast_to_instance_id---------------------------- @@ -2319,8 +2336,10 @@ if (ptr == Null) return TypePtr::make(AnyPtr, ptr, offset); // else fall through: case TopPTR: - case AnyNull: - return make(ptr, offset); + case AnyNull: { + int instance_id = meet_instance_id(InstanceTop); + return make(ptr, offset, instance_id); + } case BotPTR: case NotNull: return TypePtr::make(AnyPtr, ptr, offset); @@ -2330,7 +2349,8 @@ case OopPtr: { // Meeting to other OopPtrs const TypeOopPtr *tp = t->is_oopptr(); - return make( meet_ptr(tp->ptr()), meet_offset(tp->offset()) ); + int instance_id = meet_instance_id(tp->instance_id()); + return make( meet_ptr(tp->ptr()), meet_offset(tp->offset()), instance_id ); } case InstPtr: // For these, flip the call around to cut down @@ -2410,14 +2430,13 @@ //------------------------------make_from_constant----------------------------- // Make a java pointer from an oop constant -const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o) { - if (o->is_method_data() || o->is_method()) { +const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, bool require_constant) { + if (o->is_method_data() || o->is_method() || o->is_cpcache()) { // Treat much like a typeArray of bytes, like below, but fake the type... - assert(o->has_encoding(), "must be a perm space object"); const Type* etype = (Type*)get_const_basic_type(T_BYTE); const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS); ciKlass *klass = ciTypeArrayKlass::make((BasicType) T_BYTE); - assert(o->has_encoding(), "method data oops should be tenured"); + assert(o->can_be_constant(), "method data oops should be tenured"); const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0); return arr; } else { @@ -2426,8 +2445,9 @@ ciKlass *klass = o->klass(); if (klass->is_instance_klass()) { // Element is an instance - if (!o->has_encoding()) { // not a perm-space constant - // %%% remove this restriction by rewriting non-perm ConPNodes in a later phase + if (require_constant) { + if (!o->can_be_constant()) return NULL; + } else if (!o->should_be_constant()) { return TypeInstPtr::make(TypePtr::NotNull, klass, true, NULL, 0); } return TypeInstPtr::make(o); @@ -2439,8 +2459,9 @@ // We used to pass NotNull in here, asserting that the sub-arrays // are all not-null. This is not true in generally, as code can // slam NULLs down in the subarrays. - if (!o->has_encoding()) { // not a perm-space constant - // %%% remove this restriction by rewriting non-perm ConPNodes in a later phase + if (require_constant) { + if (!o->can_be_constant()) return NULL; + } 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); @@ -2452,8 +2473,9 @@ const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length())); // We used to pass NotNull in here, asserting that the array pointer // is not-null. That was not true in general. - if (!o->has_encoding()) { // not a perm-space constant - // %%% remove this restriction by rewriting non-perm ConPNodes in a later phase + if (require_constant) { + if (!o->can_be_constant()) return NULL; + } 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); @@ -2482,7 +2504,7 @@ ShouldNotReachHere(); } - return (intptr_t)const_oop()->encoding(); + return (intptr_t)const_oop()->constant_encoding(); } @@ -2590,7 +2612,7 @@ //------------------------------add_offset------------------------------------- const TypePtr *TypeOopPtr::add_offset( intptr_t offset ) const { - return make( _ptr, xadd_offset(offset) ); + return make( _ptr, xadd_offset(offset), _instance_id); } //------------------------------meet_instance_id-------------------------------- @@ -2693,6 +2715,7 @@ const TypeInstPtr *TypeInstPtr::xmeet_unloaded(const TypeInstPtr *tinst) const { int off = meet_offset(tinst->offset()); PTR ptr = meet_ptr(tinst->ptr()); + int instance_id = meet_instance_id(tinst->instance_id()); const TypeInstPtr *loaded = is_loaded() ? this : tinst; const TypeInstPtr *unloaded = is_loaded() ? tinst : this; @@ -2713,7 +2736,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() ); } + else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make( ptr, unloaded->klass(), false, NULL, off, instance_id ); } 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; } @@ -2786,7 +2809,8 @@ // then we can subclass in the Java class hierarchy. if (klass()->equals(ciEnv::current()->Object_klass())) { // that is, tp's array type is a subtype of my klass - return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id); + return TypeAryPtr::make(ptr, (ptr == Constant ? tp->const_oop() : NULL), + tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id); } } // The other case cannot happen, since I cannot be a subtype of an array. @@ -2801,7 +2825,7 @@ case OopPtr: { // Meeting to OopPtrs // Found a OopPtr type vs self-InstPtr type - const TypePtr *tp = t->is_oopptr(); + const TypeOopPtr *tp = t->is_oopptr(); int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); switch (tp->ptr()) { @@ -2812,8 +2836,10 @@ (ptr == Constant ? const_oop() : NULL), offset, instance_id); } case NotNull: - case BotPTR: - return TypeOopPtr::make(ptr, offset); + case BotPTR: { + int instance_id = meet_instance_id(tp->instance_id()); + return TypeOopPtr::make(ptr, offset, instance_id); + } default: typerr(t); } } @@ -3259,7 +3285,7 @@ case OopPtr: { // Meeting to OopPtrs // Found a OopPtr type vs self-AryPtr type - const TypePtr *tp = t->is_oopptr(); + const TypeOopPtr *tp = t->is_oopptr(); int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); switch (tp->ptr()) { @@ -3270,8 +3296,10 @@ _ary, _klass, _klass_is_exact, offset, instance_id); } case BotPTR: - case NotNull: - return TypeOopPtr::make(ptr, offset); + case NotNull: { + int instance_id = meet_instance_id(tp->instance_id()); + return TypeOopPtr::make(ptr, offset, instance_id); + } default: ShouldNotReachHere(); } } @@ -3333,14 +3361,19 @@ ciObject* o = const_oop(); if( _ptr == Constant ) { if( tap->const_oop() != NULL && !o->equals(tap->const_oop()) ) { + xk = (klass() == tap->klass()); ptr = NotNull; o = NULL; instance_id = InstanceBot; + } else { + xk = true; } } else if( above_centerline(_ptr) ) { o = tap->const_oop(); + xk = true; + } else { + xk = this->_klass_is_exact; } - xk = true; return TypeAryPtr::make( ptr, o, tary, tap->_klass, xk, off, instance_id ); } case NotNull: @@ -3383,7 +3416,8 @@ // then we can subclass in the Java class hierarchy. if( tp->klass()->equals(ciEnv::current()->Object_klass()) ) { // that is, my array type is a subtype of 'tp' klass - return make( ptr, _ary, _klass, _klass_is_exact, offset, instance_id ); + return make( ptr, (ptr == Constant ? const_oop() : NULL), + _ary, _klass, _klass_is_exact, offset, instance_id ); } } // The other case cannot happen, since t cannot be a subtype of an array. @@ -3934,7 +3968,7 @@ const TypeFunc* tf = C->last_tf(method); // check cache if (tf != NULL) return tf; // The hit rate here is almost 50%. const TypeTuple *domain; - if (method->flags().is_static()) { + if (method->is_static()) { domain = TypeTuple::make_domain(NULL, method->signature()); } else { domain = TypeTuple::make_domain(method->holder(), method->signature()); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/opto/type.hpp --- a/src/share/vm/opto/type.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/opto/type.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -168,7 +168,7 @@ // MEET operation; lower in lattice. const Type *meet( const Type *t ) const; // WIDEN: 'widens' for Ints and other range types - virtual const Type *widen( const Type *old ) const { return this; } + virtual const Type *widen( const Type *old, const Type* limit ) const { return this; } // NARROW: complement for widen, used by pessimistic phases virtual const Type *narrow( const Type *old ) const { return this; } @@ -409,7 +409,7 @@ 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; + 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; @@ -465,7 +465,7 @@ 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; + 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; @@ -711,10 +711,13 @@ return make_from_klass_common(klass, false, false); } // Creates a singleton type given an object. - static const TypeOopPtr* make_from_constant(ciObject* o); + // If the object cannot be rendered as a constant, + // may return a non-singleton type. + // If require_constant, produce a NULL if a singleton is not possible. + static const TypeOopPtr* make_from_constant(ciObject* o, bool require_constant = false); // Make a generic (unclassed) pointer to an oop. - static const TypeOopPtr* make(PTR ptr, int offset); + static const TypeOopPtr* make(PTR ptr, int offset, int instance_id); ciObject* const_oop() const { return _const_oop; } virtual ciKlass* klass() const { return _klass; } @@ -844,9 +847,6 @@ // 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); - // Convenience - static const TypeAryPtr *make(ciObject* o); - // Return a 'ptr' version of this type virtual const Type *cast_to_ptr_type(PTR ptr) const; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jni.cpp --- a/src/share/vm/prims/jni.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jni.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -299,7 +299,8 @@ } } klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, - Handle(), &st, CHECK_NULL); + Handle(), &st, true, + CHECK_NULL); if (TraceClassResolution && k != NULL) { trace_class_resolution(k); @@ -395,11 +396,11 @@ oop mirror = NULL; int slot = 0; - if (reflected->klass() == SystemDictionary::reflect_constructor_klass()) { + if (reflected->klass() == SystemDictionary::reflect_Constructor_klass()) { mirror = java_lang_reflect_Constructor::clazz(reflected); slot = java_lang_reflect_Constructor::slot(reflected); } else { - assert(reflected->klass() == SystemDictionary::reflect_method_klass(), "wrong type"); + assert(reflected->klass() == SystemDictionary::reflect_Method_klass(), "wrong type"); mirror = java_lang_reflect_Method::clazz(reflected); slot = java_lang_reflect_Method::slot(reflected); } @@ -495,7 +496,7 @@ klassOop super = Klass::cast(k)->java_super(); // super2 is the value computed by the compiler's getSuperClass intrinsic: debug_only(klassOop super2 = ( Klass::cast(k)->oop_is_javaArray() - ? SystemDictionary::object_klass() + ? SystemDictionary::Object_klass() : Klass::cast(k)->super() ) ); assert(super == super2, "java_super computation depends on interface, array, other super"); @@ -583,7 +584,7 @@ if (thread->has_pending_exception()) { Handle ex(thread, thread->pending_exception()); thread->clear_pending_exception(); - if (ex->is_a(SystemDictionary::threaddeath_klass())) { + if (ex->is_a(SystemDictionary::ThreadDeath_klass())) { // Don't print anything if we are being killed. } else { jio_fprintf(defaultStream::error_stream(), "Exception "); @@ -592,12 +593,12 @@ jio_fprintf(defaultStream::error_stream(), "in thread \"%s\" ", thread->get_thread_name()); } - if (ex->is_a(SystemDictionary::throwable_klass())) { + if (ex->is_a(SystemDictionary::Throwable_klass())) { JavaValue result(T_VOID); JavaCalls::call_virtual(&result, ex, KlassHandle(THREAD, - SystemDictionary::throwable_klass()), + SystemDictionary::Throwable_klass()), vmSymbolHandles::printStackTrace_name(), vmSymbolHandles::void_method_signature(), THREAD); @@ -2115,7 +2116,7 @@ DT_RETURN_MARK(GetObjectArrayElement, jobject, (const jobject&)ret); objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(array)); if (a->is_within_bounds(index)) { - jobject ret = JNIHandles::make_local(env, a->obj_at(index)); + ret = JNIHandles::make_local(env, a->obj_at(index)); return ret; } else { char buf[jintAsStringSize]; @@ -2149,14 +2150,14 @@ #define DEFINE_NEWSCALARARRAY(Return,Allocator,Result) \ \ - DT_RETURN_MARK_DECL_FOR(Result, New##Result##Array, Return);\ + DT_RETURN_MARK_DECL(New##Result##Array, Return);\ \ JNI_ENTRY(Return, \ jni_New##Result##Array(JNIEnv *env, jsize len)) \ JNIWrapper("New" XSTR(Result) "Array"); \ DTRACE_PROBE2(hotspot_jni, New##Result##Array__entry, env, len);\ Return ret = NULL;\ - DT_RETURN_MARK_FOR(Result, New##Result##Array, Return, (const Return&)ret);\ + DT_RETURN_MARK(New##Result##Array, Return, (const Return&)ret);\ \ oop obj= oopFactory::Allocator(len, CHECK_0); \ ret = (Return) JNIHandles::make_local(env, obj); \ @@ -3230,6 +3231,21 @@ jint result = JNI_ERR; DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result); + // We're about to use Atomic::xchg for synchronization. Some Zero + // platforms use the GCC builtin __sync_lock_test_and_set for this, + // but __sync_lock_test_and_set is not guaranteed to do what we want + // on all architectures. So we check it works before relying on it. +#if defined(ZERO) && defined(ASSERT) + { + jint a = 0xcafebabe; + jint b = Atomic::xchg(0xdeadbeef, &a); + void *c = &a; + void *d = Atomic::xchg_ptr(&b, &c); + assert(a == (jint) 0xdeadbeef && b == (jint) 0xcafebabe, "Atomic::xchg() works"); + assert(c == &b && d == &a, "Atomic::xchg_ptr() works"); + } +#endif // ZERO && ASSERT + // At the moment it's only possible to have one Java VM, // since some of the runtime state is in global variables. @@ -3385,12 +3401,16 @@ thread->set_thread_state(_thread_in_vm); // Must do this before initialize_thread_local_storage thread->record_stack_base_and_size(); + thread->initialize_thread_local_storage(); if (!os::create_attached_thread(thread)) { delete thread; return JNI_ERR; } + // Enable stack overflow checks + thread->create_stack_guard_pages(); + thread->initialize_tlab(); // Crucial that we do not have a safepoint check for this thread, since it has @@ -3436,9 +3456,6 @@ // to regrab the threads_lock thread->set_attached(); - // Enable stack overflow checks - thread->create_stack_guard_pages(); - // Set java thread status. java_lang_Thread::set_thread_status(thread->threadObj(), java_lang_Thread::RUNNABLE); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jniCheck.cpp --- a/src/share/vm/prims/jniCheck.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jniCheck.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-2009 Sun Microsystems, 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 @@ -96,6 +96,7 @@ static const char * fatal_class_not_a_class = "JNI received a class argument that is not a class"; static const char * fatal_class_not_a_throwable_class = "JNI Throw or ThrowNew received a class argument that is not a Throwable or Throwable subclass"; static const char * fatal_wrong_class_or_method = "Wrong object class or methodID passed to JNI call"; +static const char * fatal_non_weak_method = "non-weak methodID passed to JNI call"; static const char * fatal_unknown_array_object = "Unknown array object passed to JNI array operations"; static const char * fatal_object_array_expected = "Object array expected but not received for JNI array operation"; static const char * fatal_non_array = "Non-array passed to JNI array operations"; @@ -291,10 +292,16 @@ methodOop jniCheck::validate_jmethod_id(JavaThread* thr, jmethodID method_id) { ASSERT_OOPS_ALLOWED; + // do the fast jmethodID check first methodOop moop = JNIHandles::checked_resolve_jmethod_id(method_id); if (moop == NULL) { ReportJNIFatalError(thr, fatal_wrong_class_or_method); } + // jmethodIDs are supposed to be weak global handles, but that + // can be expensive so check it last + else if (!JNIHandles::is_weak_global_handle((jobject) method_id)) { + ReportJNIFatalError(thr, fatal_non_weak_method); + } return moop; } @@ -334,7 +341,7 @@ ReportJNIFatalError(thr, fatal_received_null_class); } - if (mirror->klass() != SystemDictionary::class_klass()) { + if (mirror->klass() != SystemDictionary::Class_klass()) { ReportJNIFatalError(thr, fatal_class_not_a_class); } @@ -351,7 +358,7 @@ assert(klass != NULL, "klass argument must have a value"); if (!Klass::cast(klass)->oop_is_instance() || - !instanceKlass::cast(klass)->is_subclass_of(SystemDictionary::throwable_klass())) { + !instanceKlass::cast(klass)->is_subclass_of(SystemDictionary::Throwable_klass())) { ReportJNIFatalError(thr, fatal_class_not_a_throwable_class); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvm.cpp --- a/src/share/vm/prims/jvm.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvm.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -26,6 +26,10 @@ #include "incls/_jvm.cpp.incl" #include +HS_DTRACE_PROBE_DECL1(hotspot, thread__sleep__begin, long long); +HS_DTRACE_PROBE_DECL1(hotspot, thread__sleep__end, int); +HS_DTRACE_PROBE_DECL0(hotspot, thread__yield); + /* NOTE about use of any ctor or function call that can trigger a safepoint/GC: such ctors and calls MUST NOT come between an oop declaration/init and its @@ -80,7 +84,7 @@ while (!vfst.at_end()) { methodOop m = vfst.method(); - if (!vfst.method()->method_holder()->klass_part()->is_subclass_of(SystemDictionary::classloader_klass())&& + if (!vfst.method()->method_holder()->klass_part()->is_subclass_of(SystemDictionary::ClassLoader_klass())&& !vfst.method()->method_holder()->klass_part()->is_subclass_of(access_controller_klass) && !vfst.method()->method_holder()->klass_part()->is_subclass_of(privileged_action_klass)) { break; @@ -257,7 +261,7 @@ Handle value_str = java_lang_String::create_from_platform_dependent_str((value != NULL ? value : ""), CHECK); JavaCalls::call_virtual(&r, props, - KlassHandle(THREAD, SystemDictionary::properties_klass()), + KlassHandle(THREAD, SystemDictionary::Properties_klass()), vmSymbolHandles::put_name(), vmSymbolHandles::object_object_object_signature(), key_str, @@ -495,7 +499,7 @@ guarantee(klass->is_cloneable(), "all arrays are cloneable"); } else { guarantee(obj->is_instance(), "should be instanceOop"); - bool cloneable = klass->is_subtype_of(SystemDictionary::cloneable_klass()); + bool cloneable = klass->is_subtype_of(SystemDictionary::Cloneable_klass()); guarantee(cloneable == klass->is_cloneable(), "incorrect cloneable flag"); } #endif @@ -762,7 +766,11 @@ } // common code for JVM_DefineClass() and JVM_DefineClassWithSource() -static jclass jvm_define_class_common(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source, TRAPS) { +// and JVM_DefineClassWithSourceCond() +static jclass jvm_define_class_common(JNIEnv *env, const char *name, + jobject loader, const jbyte *buf, + jsize len, jobject pd, const char *source, + jboolean verify, TRAPS) { if (source == NULL) source = "__JVM_DefineClass__"; assert(THREAD->is_Java_thread(), "must be a JavaThread"); @@ -803,6 +811,7 @@ Handle protection_domain (THREAD, JNIHandles::resolve(pd)); klassOop k = SystemDictionary::resolve_from_stream(class_name, class_loader, protection_domain, &st, + verify != 0, CHECK_NULL); if (TraceClassResolution && k != NULL) { @@ -816,16 +825,24 @@ JVM_ENTRY(jclass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd)) JVMWrapper2("JVM_DefineClass %s", name); - return jvm_define_class_common(env, name, loader, buf, len, pd, NULL, THREAD); + return jvm_define_class_common(env, name, loader, buf, len, pd, NULL, true, THREAD); JVM_END JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source)) JVMWrapper2("JVM_DefineClassWithSource %s", name); - return jvm_define_class_common(env, name, loader, buf, len, pd, source, THREAD); -JVM_END - + return jvm_define_class_common(env, name, loader, buf, len, pd, source, true, THREAD); +JVM_END + +JVM_ENTRY(jclass, JVM_DefineClassWithSourceCond(JNIEnv *env, const char *name, + jobject loader, const jbyte *buf, + jsize len, jobject pd, + const char *source, jboolean verify)) + JVMWrapper2("JVM_DefineClassWithSourceCond %s", name); + + return jvm_define_class_common(env, name, loader, buf, len, pd, source, verify, THREAD); +JVM_END JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name)) JVMWrapper("JVM_FindLoadedClass"); @@ -895,7 +912,7 @@ // Special handling for primitive objects if (java_lang_Class::is_primitive(mirror)) { // Primitive objects does not have any interfaces - objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(), 0, CHECK_NULL); return (jobjectArray) JNIHandles::make_local(env, r); } @@ -910,7 +927,7 @@ } // Allocate result array - objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), size, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(), size, CHECK_NULL); objArrayHandle result (THREAD, r); // Fill in result if (klass->oop_is_instance()) { @@ -921,8 +938,8 @@ } } else { // All arrays implement java.lang.Cloneable and java.io.Serializable - result->obj_at_put(0, Klass::cast(SystemDictionary::cloneable_klass())->java_mirror()); - result->obj_at_put(1, Klass::cast(SystemDictionary::serializable_klass())->java_mirror()); + result->obj_at_put(0, Klass::cast(SystemDictionary::Cloneable_klass())->java_mirror()); + result->obj_at_put(1, Klass::cast(SystemDictionary::Serializable_klass())->java_mirror()); } return (jobjectArray) JNIHandles::make_local(env, result()); JVM_END @@ -1085,8 +1102,8 @@ pending_exception = Handle(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; - if ( pending_exception->is_a(SystemDictionary::exception_klass()) && - !pending_exception->is_a(SystemDictionary::runtime_exception_klass())) { + if ( pending_exception->is_a(SystemDictionary::Exception_klass()) && + !pending_exception->is_a(SystemDictionary::RuntimeException_klass())) { // Throw a java.security.PrivilegedActionException(Exception e) exception JavaCallArguments args(pending_exception); THROW_ARG_0(vmSymbolHandles::java_security_PrivilegedActionException(), @@ -1177,7 +1194,7 @@ // the resource area must be registered in case of a gc RegisterArrayForGC ragc(thread, local_array); - objArrayOop context = oopFactory::new_objArray(SystemDictionary::protectionDomain_klass(), + objArrayOop context = oopFactory::new_objArray(SystemDictionary::ProtectionDomain_klass(), local_array->length(), CHECK_NULL); objArrayHandle h_context(thread, context); for (int index = 0; index < local_array->length(); index++) { @@ -1238,7 +1255,7 @@ if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || ! Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_instance()) { - oop result = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_NULL); + oop result = oopFactory::new_objArray(SystemDictionary::Class_klass(), 0, CHECK_NULL); return (jobjectArray)JNIHandles::make_local(env, result); } @@ -1246,7 +1263,7 @@ if (k->inner_classes()->length() == 0) { // Neither an inner nor outer class - oop result = oopFactory::new_objArray(SystemDictionary::class_klass(), 0, CHECK_NULL); + oop result = oopFactory::new_objArray(SystemDictionary::Class_klass(), 0, CHECK_NULL); return (jobjectArray)JNIHandles::make_local(env, result); } @@ -1256,7 +1273,7 @@ int length = icls->length(); // Allocate temp. result array - objArrayOop r = oopFactory::new_objArray(SystemDictionary::class_klass(), length/4, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(), length/4, CHECK_NULL); objArrayHandle result (THREAD, r); int members = 0; @@ -1286,7 +1303,7 @@ if (members != length) { // Return array of right length - objArrayOop res = oopFactory::new_objArray(SystemDictionary::class_klass(), members, CHECK_NULL); + objArrayOop res = oopFactory::new_objArray(SystemDictionary::Class_klass(), members, CHECK_NULL); for(int i = 0; i < members; i++) { res->obj_at_put(i, result->obj_at(i)); } @@ -1305,19 +1322,20 @@ return NULL; } - symbolOop simple_name = NULL; + bool inner_is_member = false; klassOop outer_klass = instanceKlass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)) - )->compute_enclosing_class(simple_name, CHECK_NULL); + )->compute_enclosing_class(&inner_is_member, CHECK_NULL); if (outer_klass == NULL) return NULL; // already a top-level class - if (simple_name == NULL) return NULL; // an anonymous class (inside a method) + if (!inner_is_member) return NULL; // an anonymous class (inside a method) return (jclass) JNIHandles::make_local(env, Klass::cast(outer_klass)->java_mirror()); } JVM_END // should be in instanceKlass.cpp, but is here for historical reasons klassOop instanceKlass::compute_enclosing_class_impl(instanceKlassHandle k, - symbolOop& simple_name_result, TRAPS) { + bool* inner_is_member, + TRAPS) { Thread* thread = THREAD; const int inner_class_info_index = inner_class_inner_class_info_offset; const int outer_class_info_index = inner_class_outer_class_info_offset; @@ -1334,8 +1352,7 @@ bool found = false; klassOop ok; instanceKlassHandle outer_klass; - bool inner_is_member = false; - int simple_name_index = 0; + *inner_is_member = false; // Find inner_klass attribute for (int i = 0; i < i_length && !found; i += inner_class_next_offset) { @@ -1351,8 +1368,7 @@ if (found && ooff != 0) { ok = i_cp->klass_at(ooff, CHECK_NULL); outer_klass = instanceKlassHandle(thread, ok); - simple_name_index = noff; - inner_is_member = true; + *inner_is_member = true; } } } @@ -1364,7 +1380,7 @@ if (encl_method_class_idx != 0) { ok = i_cp->klass_at(encl_method_class_idx, CHECK_NULL); outer_klass = instanceKlassHandle(thread, ok); - inner_is_member = false; + *inner_is_member = false; } } @@ -1374,9 +1390,7 @@ // Throws an exception if outer klass has not declared k as an inner klass // We need evidence that each klass knows about the other, or else // the system could allow a spoof of an inner class to gain access rights. - Reflection::check_for_inner_class(outer_klass, k, inner_is_member, CHECK_NULL); - - simple_name_result = (inner_is_member ? i_cp->symbol_at(simple_name_index) : symbolOop(NULL)); + Reflection::check_for_inner_class(outer_klass, k, *inner_is_member, CHECK_NULL); return outer_klass(); } @@ -1460,11 +1474,11 @@ oop mirror = NULL; int slot = 0; - if (reflected->klass() == SystemDictionary::reflect_constructor_klass()) { + if (reflected->klass() == SystemDictionary::reflect_Constructor_klass()) { mirror = java_lang_reflect_Constructor::clazz(reflected); slot = java_lang_reflect_Constructor::slot(reflected); } else { - assert(reflected->klass() == SystemDictionary::reflect_method_klass(), + assert(reflected->klass() == SystemDictionary::reflect_Method_klass(), "wrong type"); mirror = java_lang_reflect_Method::clazz(reflected); slot = java_lang_reflect_Method::slot(reflected); @@ -1520,7 +1534,7 @@ if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_javaArray()) { // Return empty array - oop res = oopFactory::new_objArray(SystemDictionary::reflect_field_klass(), 0, CHECK_NULL); + oop res = oopFactory::new_objArray(SystemDictionary::reflect_Field_klass(), 0, CHECK_NULL); return (jobjectArray) JNIHandles::make_local(env, res); } @@ -1548,13 +1562,13 @@ } else { num_fields = fields_len / instanceKlass::next_offset; - if (k() == SystemDictionary::throwable_klass()) { + if (k() == SystemDictionary::Throwable_klass()) { num_fields--; skip_backtrace = true; } } - objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_field_klass(), num_fields, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_Field_klass(), num_fields, CHECK_NULL); objArrayHandle result (THREAD, r); int out_idx = 0; @@ -1588,7 +1602,7 @@ if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_javaArray()) { // Return empty array - oop res = oopFactory::new_objArray(SystemDictionary::reflect_method_klass(), 0, CHECK_NULL); + oop res = oopFactory::new_objArray(SystemDictionary::reflect_Method_klass(), 0, CHECK_NULL); return (jobjectArray) JNIHandles::make_local(env, res); } @@ -1612,7 +1626,7 @@ } // Allocate result - objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_method_klass(), num_methods, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_Method_klass(), num_methods, CHECK_NULL); objArrayHandle result (THREAD, r); int out_idx = 0; @@ -1640,7 +1654,7 @@ if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(ofClass)) || Klass::cast(java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(ofClass)))->oop_is_javaArray()) { // Return empty array - oop res = oopFactory::new_objArray(SystemDictionary::reflect_constructor_klass(), 0 , CHECK_NULL); + oop res = oopFactory::new_objArray(SystemDictionary::reflect_Constructor_klass(), 0 , CHECK_NULL); return (jobjectArray) JNIHandles::make_local(env, res); } @@ -1664,7 +1678,7 @@ } // Allocate result - objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_constructor_klass(), num_constructors, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::reflect_Constructor_klass(), num_constructors, CHECK_NULL); objArrayHandle result(THREAD, r); int out_idx = 0; @@ -1877,7 +1891,7 @@ symbolHandle klass_name (THREAD, cp->klass_name_at(klass_ref)); symbolHandle member_name(THREAD, cp->uncached_name_ref_at(index)); symbolHandle member_sig (THREAD, cp->uncached_signature_ref_at(index)); - objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::string_klass(), 3, CHECK_NULL); + objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::String_klass(), 3, CHECK_NULL); objArrayHandle dest(THREAD, dest_o); Handle str = java_lang_String::create_from_symbol(klass_name, CHECK_NULL); dest->obj_at_put(0, str()); @@ -2244,10 +2258,8 @@ switch (cp->tag_at(cp_index).value()) { case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_NameAndType: // for invokedynamic return cp->uncached_name_ref_at(cp_index)->as_utf8(); - case JVM_CONSTANT_NameAndType: - // for invokedynamic - return cp->nt_name_ref_at(cp_index)->as_utf8(); default: fatal("JVM_GetCPMethodNameUTF: illegal constant"); } @@ -2264,10 +2276,8 @@ switch (cp->tag_at(cp_index).value()) { case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_NameAndType: // for invokedynamic return cp->uncached_signature_ref_at(cp_index)->as_utf8(); - case JVM_CONSTANT_NameAndType: - // for invokedynamic - return cp->nt_signature_ref_at(cp_index)->as_utf8(); default: fatal("JVM_GetCPMethodSignatureUTF: illegal constant"); } @@ -2569,7 +2579,7 @@ JavaValue result(T_VOID); JavaCalls::call_virtual(&result, obj, - KlassHandle(THREAD, SystemDictionary::thread_klass()), + KlassHandle(THREAD, SystemDictionary::Thread_klass()), vmSymbolHandles::run_method_name(), vmSymbolHandles::void_method_signature(), THREAD); @@ -2667,7 +2677,7 @@ // Fix for 4314342, 4145910, perhaps others: it now doesn't have // any effect on the "liveness" of a thread; see // JVM_IsThreadAlive, below. - if (java_throwable->is_a(SystemDictionary::threaddeath_klass())) { + if (java_throwable->is_a(SystemDictionary::ThreadDeath_klass())) { java_lang_Thread::set_stillborn(java_thread); } THROW_OOP(java_throwable); @@ -2756,6 +2766,7 @@ JVM_ENTRY(void, JVM_Yield(JNIEnv *env, jclass threadClass)) JVMWrapper("JVM_Yield"); if (os::dont_yield()) return; + HS_DTRACE_PROBE0(hotspot, thread__yield); // When ConvertYieldToSleep is off (default), this matches the classic VM use of yield. // Critical for similar threading behaviour if (ConvertYieldToSleep) { @@ -2781,6 +2792,8 @@ // And set new thread state to SLEEPING. JavaThreadSleepState jtss(thread); + HS_DTRACE_PROBE1(hotspot, thread__sleep__begin, millis); + if (millis == 0) { // When ConvertSleepToYield is on, this matches the classic VM implementation of // JVM_Sleep. Critical for similar threading behaviour (Win32) @@ -2801,6 +2814,7 @@ // An asynchronous exception (e.g., ThreadDeathException) could have been thrown on // us while we were sleeping. We do not overwrite those. if (!HAS_PENDING_EXCEPTION) { + HS_DTRACE_PROBE1(hotspot, thread__sleep__end,1); // TODO-FIXME: THROW_MSG returns which means we will not call set_state() // to properly restore the thread state. That's likely wrong. THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted"); @@ -2808,6 +2822,7 @@ } thread->osthread()->set_state(old_state); } + HS_DTRACE_PROBE1(hotspot, thread__sleep__end,0); JVM_END JVM_ENTRY(jobject, JVM_CurrentThread(JNIEnv* env, jclass threadClass)) @@ -3022,7 +3037,7 @@ } // Create result array of type [Ljava/lang/Class; - objArrayOop result = oopFactory::new_objArray(SystemDictionary::class_klass(), depth, CHECK_NULL); + objArrayOop result = oopFactory::new_objArray(SystemDictionary::Class_klass(), depth, CHECK_NULL); // Fill in mirrors corresponding to method holders int index = 0; while (first != NULL) { @@ -4318,7 +4333,7 @@ JvmtiVMObjectAllocEventCollector oam; int num_threads = tle.num_threads(); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::thread_klass(), num_threads, CHECK_NULL); + objArrayOop r = oopFactory::new_objArray(SystemDictionary::Thread_klass(), num_threads, CHECK_NULL); objArrayHandle threads_ah(THREAD, r); for (int i = 0; i < num_threads; i++) { @@ -4352,7 +4367,7 @@ // check if threads is not an array of objects of Thread class klassOop k = objArrayKlass::cast(ah->klass())->element_klass(); - if (k != SystemDictionary::thread_klass()) { + if (k != SystemDictionary::Thread_klass()) { THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); } @@ -4412,7 +4427,7 @@ if (encl_method_class_idx == 0) { return NULL; } - objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::object_klass(), 3, CHECK_NULL); + objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::Object_klass(), 3, CHECK_NULL); objArrayHandle dest(THREAD, dest_o); klassOop enc_k = ik_h->constants()->klass_at(encl_method_class_idx, CHECK_NULL); dest->obj_at_put(0, Klass::cast(enc_k)->java_mirror()); @@ -4526,7 +4541,7 @@ values_h->int_at(0) == java_lang_Thread::NEW, "Invalid threadStatus value"); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), 1, /* only 1 substate */ CHECK_NULL); names_h = objArrayHandle(THREAD, r); @@ -4539,7 +4554,7 @@ values_h->int_at(0) == java_lang_Thread::RUNNABLE, "Invalid threadStatus value"); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), 1, /* only 1 substate */ CHECK_NULL); names_h = objArrayHandle(THREAD, r); @@ -4552,7 +4567,7 @@ values_h->int_at(0) == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER, "Invalid threadStatus value"); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), 1, /* only 1 substate */ CHECK_NULL); names_h = objArrayHandle(THREAD, r); @@ -4565,7 +4580,7 @@ values_h->int_at(0) == java_lang_Thread::IN_OBJECT_WAIT && values_h->int_at(1) == java_lang_Thread::PARKED, "Invalid threadStatus value"); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), 2, /* number of substates */ CHECK_NULL); names_h = objArrayHandle(THREAD, r); @@ -4583,7 +4598,7 @@ values_h->int_at(1) == java_lang_Thread::IN_OBJECT_WAIT_TIMED && values_h->int_at(2) == java_lang_Thread::PARKED_TIMED, "Invalid threadStatus value"); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), 3, /* number of substates */ CHECK_NULL); names_h = objArrayHandle(THREAD, r); @@ -4602,7 +4617,7 @@ assert(values_h->length() == 1 && values_h->int_at(0) == java_lang_Thread::TERMINATED, "Invalid threadStatus value"); - objArrayOop r = oopFactory::new_objArray(SystemDictionary::string_klass(), + objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(), 1, /* only 1 substate */ CHECK_NULL); names_h = objArrayHandle(THREAD, r); @@ -4637,4 +4652,3 @@ #endif // KERNEL } JVM_END - diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvm.h --- a/src/share/vm/prims/jvm.h Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvm.h Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, 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 @@ -417,6 +417,17 @@ const jbyte *buf, jsize len, jobject pd, const char *source); +/* Define a class with a source with conditional verification (added HSX 14) + * -Xverify:all will verify anyway, -Xverify:none will not verify, + * -Xverify:remote (default) will obey this conditional + * i.e. true = should_verify_class + */ +JNIEXPORT jclass JNICALL +JVM_DefineClassWithSourceCond(JNIEnv *env, const char *name, + jobject loader, const jbyte *buf, + jsize len, jobject pd, const char *source, + jboolean verify); + /* Define a class with a source (MLVM) */ JNIEXPORT jclass JNICALL JVM_DefineClassWithCP(JNIEnv *env, const char *name, jobject loader, diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiCodeBlobEvents.cpp --- a/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, 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 @@ -402,7 +402,7 @@ address scopes_data = nm->scopes_data_begin(); for( pcd = nm->scopes_pcs_begin(); pcd < nm->scopes_pcs_end(); ++pcd ) { - ScopeDesc sc0(nm, pcd->scope_decode_offset()); + ScopeDesc sc0(nm, pcd->scope_decode_offset(), pcd->should_reexecute(), pcd->return_oop()); ScopeDesc *sd = &sc0; while( !sd->is_top() ) { sd = sd->sender(); } int bci = sd->bci(); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiEnter.xsl --- a/src/share/vm/prims/jvmtiEnter.xsl Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiEnter.xsl Thu Mar 25 16:54:59 2010 -0700 @@ -773,7 +773,7 @@ } - if (!thread_oop->is_a(SystemDictionary::thread_klass())) { + if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { JVMTI_ERROR_INVALID_THREAD @@ -857,7 +857,7 @@ } - if (!k_mirror->is_a(SystemDictionary::class_klass())) { + if (!k_mirror->is_a(SystemDictionary::Class_klass())) { JVMTI_ERROR_INVALID_CLASS diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiEnv.cpp --- a/src/share/vm/prims/jvmtiEnv.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiEnv.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, 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 @@ -32,15 +32,15 @@ // FIXLATER: hook into JvmtiTrace #define TraceJVMTICalls false -JvmtiEnv::JvmtiEnv() : JvmtiEnvBase() { +JvmtiEnv::JvmtiEnv(jint version) : JvmtiEnvBase(version) { } JvmtiEnv::~JvmtiEnv() { } JvmtiEnv* -JvmtiEnv::create_a_jvmti() { - return new JvmtiEnv(); +JvmtiEnv::create_a_jvmti(jint version) { + return new JvmtiEnv(version); } // VM operation class to copy jni function table at safepoint. @@ -133,7 +133,7 @@ if (thread_oop == NULL) { return JVMTI_ERROR_INVALID_THREAD; } - if (!thread_oop->is_a(SystemDictionary::thread_klass())) { + if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { return JVMTI_ERROR_INVALID_THREAD; } JavaThread* java_thread = java_lang_Thread::thread(thread_oop); @@ -199,7 +199,7 @@ if (k_mirror == NULL) { return JVMTI_ERROR_INVALID_CLASS; } - if (!k_mirror->is_a(SystemDictionary::class_klass())) { + if (!k_mirror->is_a(SystemDictionary::Class_klass())) { return JVMTI_ERROR_INVALID_CLASS; } @@ -266,7 +266,7 @@ oop mirror = JNIHandles::resolve_external_guard(object); NULL_CHECK(mirror, JVMTI_ERROR_INVALID_OBJECT); - if (mirror->klass() == SystemDictionary::class_klass()) { + if (mirror->klass() == SystemDictionary::Class_klass()) { if (!java_lang_Class::is_primitive(mirror)) { mirror = java_lang_Class::as_klassOop(mirror); assert(mirror != NULL, "class for non-primitive mirror must exist"); @@ -327,7 +327,7 @@ if (thread_oop == NULL) { return JVMTI_ERROR_INVALID_THREAD; } - if (!thread_oop->is_a(SystemDictionary::thread_klass())) { + if (!thread_oop->is_a(SystemDictionary::Thread_klass())) { return JVMTI_ERROR_INVALID_THREAD; } java_thread = java_lang_Thread::thread(thread_oop); @@ -411,8 +411,15 @@ if (phase == JVMTI_PHASE_ONLOAD) { Arguments::append_sysclasspath(segment); return JVMTI_ERROR_NONE; - } else { - assert(phase == JVMTI_PHASE_LIVE, "sanity check"); + } else if (use_version_1_0_semantics()) { + // This JvmtiEnv requested version 1.0 semantics and this function + // is only allowed in the ONLOAD phase in version 1.0 so we need to + // return an error here. + return JVMTI_ERROR_WRONG_PHASE; + } else if (phase == JVMTI_PHASE_LIVE) { + // The phase is checked by the wrapper that called this function, + // but this thread could be racing with the thread that is + // terminating the VM so we check one more time. // create the zip entry ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment); @@ -433,6 +440,8 @@ } ClassLoader::add_to_list(zip_entry); return JVMTI_ERROR_NONE; + } else { + return JVMTI_ERROR_WRONG_PHASE; } } /* end AddToBootstrapClassLoaderSearch */ @@ -451,11 +460,12 @@ } } return JVMTI_ERROR_NONE; - } else { + } else if (phase == JVMTI_PHASE_LIVE) { + // The phase is checked by the wrapper that called this function, + // but this thread could be racing with the thread that is + // terminating the VM so we check one more time. HandleMark hm; - assert(phase == JVMTI_PHASE_LIVE, "sanity check"); - // create the zip entry (which will open the zip file and hence // check that the segment is indeed a zip file). ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment); @@ -504,6 +514,8 @@ } return JVMTI_ERROR_NONE; + } else { + return JVMTI_ERROR_WRONG_PHASE; } } /* end AddToSystemClassLoaderSearch */ @@ -580,7 +592,6 @@ break; case JVMTI_VERBOSE_GC: PrintGC = value != 0; - TraceClassUnloading = value != 0; break; case JVMTI_VERBOSE_JNI: PrintJNIResolving = value != 0; @@ -620,7 +631,7 @@ thread_oop = JNIHandles::resolve_external_guard(thread); } - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) { + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { return JVMTI_ERROR_INVALID_THREAD; } @@ -858,7 +869,7 @@ jvmtiError JvmtiEnv::InterruptThread(jthread thread) { oop thread_oop = JNIHandles::resolve_external_guard(thread); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) return JVMTI_ERROR_INVALID_THREAD; JavaThread* current_thread = JavaThread::current(); @@ -895,7 +906,7 @@ } else { thread_oop = JNIHandles::resolve_external_guard(thread); } - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) return JVMTI_ERROR_INVALID_THREAD; Handle thread_obj(current_thread, thread_oop); @@ -1061,7 +1072,7 @@ jvmtiError JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) { oop thread_oop = JNIHandles::resolve_external_guard(thread); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) { + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { return JVMTI_ERROR_INVALID_THREAD; } if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { @@ -2863,6 +2874,14 @@ // is_obsolete_ptr - pre-checked for NULL jvmtiError JvmtiEnv::IsMethodObsolete(methodOop method_oop, jboolean* is_obsolete_ptr) { + if (use_version_1_0_semantics() && + get_capabilities()->can_redefine_classes == 0) { + // This JvmtiEnv requested version 1.0 semantics and this function + // requires the can_redefine_classes capability in version 1.0 so + // we need to return an error here. + return JVMTI_ERROR_MUST_POSSESS_CAPABILITY; + } + if (method_oop == NULL || method_oop->is_obsolete()) { *is_obsolete_ptr = true; } else { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiEnvBase.cpp --- a/src/share/vm/prims/jvmtiEnvBase.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiEnvBase.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -123,7 +123,26 @@ } -JvmtiEnvBase::JvmtiEnvBase() : _env_event_enable() { +bool +JvmtiEnvBase::use_version_1_0_semantics() { + int major, minor, micro; + + JvmtiExport::decode_version_values(_version, &major, &minor, µ); + return major == 1 && minor == 0; // micro version doesn't matter here +} + + +bool +JvmtiEnvBase::use_version_1_1_semantics() { + int major, minor, micro; + + JvmtiExport::decode_version_values(_version, &major, &minor, µ); + return major == 1 && minor == 1; // micro version doesn't matter here +} + + +JvmtiEnvBase::JvmtiEnvBase(jint version) : _env_event_enable() { + _version = version; _env_local_storage = NULL; _tag_map = NULL; _native_method_prefix_count = 0; @@ -508,7 +527,7 @@ JavaThread * JvmtiEnvBase::get_JavaThread(jthread jni_thread) { oop t = JNIHandles::resolve_external_guard(jni_thread); - if (t == NULL || !t->is_a(SystemDictionary::thread_klass())) { + if (t == NULL || !t->is_a(SystemDictionary::Thread_klass())) { return NULL; } // The following returns NULL if the thread has not yet run or is in @@ -1250,7 +1269,7 @@ for (int i = 0; i < _thread_count; ++i) { jthread jt = _thread_list[i]; oop thread_oop = JNIHandles::resolve_external_guard(jt); - if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::thread_klass())) { + if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) { set_result(JVMTI_ERROR_INVALID_THREAD); return; } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiEnvBase.hpp --- a/src/share/vm/prims/jvmtiEnvBase.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiEnvBase.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, 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 @@ -76,6 +76,7 @@ jvmtiEnv _jvmti_external; jint _magic; + jint _version; // version value passed to JNI GetEnv() JvmtiEnvBase* _next; bool _is_retransformable; const void *_env_local_storage; // per env agent allocated data. @@ -91,7 +92,7 @@ int _native_method_prefix_count; protected: - JvmtiEnvBase(); + JvmtiEnvBase(jint version); ~JvmtiEnvBase(); void dispose(); void env_dispose(); @@ -122,6 +123,9 @@ bool is_valid(); + bool use_version_1_0_semantics(); // agent asked for version 1.0 + bool use_version_1_1_semantics(); // agent asked for version 1.1 + bool is_retransformable() { return _is_retransformable; } static ByteSize jvmti_external_offset() { diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiEventController.cpp --- a/src/share/vm/prims/jvmtiEventController.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiEventController.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -82,7 +82,7 @@ THREAD_START_BIT | THREAD_END_BIT | DYNAMIC_CODE_GENERATED_BIT; static const jlong GLOBAL_EVENT_BITS = ~THREAD_FILTERED_EVENT_BITS; - +static const jlong SHOULD_POST_ON_EXCEPTIONS_BITS = EXCEPTION_BITS | METHOD_EXIT_BIT | FRAME_POP_BIT; /////////////////////////////////////////////////////////////// // @@ -511,7 +511,12 @@ leave_interp_only_mode(state); } } + + // update the JavaThread cached value for thread-specific should_post_on_exceptions value + bool should_post_on_exceptions = (any_env_enabled & SHOULD_POST_ON_EXCEPTIONS_BITS) != 0; + state->set_should_post_on_exceptions(should_post_on_exceptions); } + return any_env_enabled; } @@ -615,6 +620,10 @@ // set global truly enabled, that is, any thread in any environment JvmtiEventController::_universal_global_event_enabled.set_bits(any_env_thread_enabled); + + // set global should_post_on_exceptions + JvmtiExport::set_should_post_on_exceptions((any_env_thread_enabled & SHOULD_POST_ON_EXCEPTIONS_BITS) != 0); + } EC_TRACE(("JVMTI [-] # recompute enabled - after %llx", any_env_thread_enabled)); diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiExport.cpp --- a/src/share/vm/prims/jvmtiExport.cpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiExport.cpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 Sun Microsystems, 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 @@ -319,7 +319,27 @@ jint JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { - /* To Do: add version checks */ + // The JVMTI_VERSION_INTERFACE_JVMTI part of the version number + // has already been validated in JNI GetEnv(). + int major, minor, micro; + + // micro version doesn't matter here (yet?) + decode_version_values(version, &major, &minor, µ); + switch (major) { + case 1: + switch (minor) { + case 0: // version 1.0. is recognized + case 1: // version 1.1. is recognized + break; + + default: + return JNI_EVERSION; // unsupported minor version number + } + break; + + default: + return JNI_EVERSION; // unsupported major version number + } if (JvmtiEnv::get_phase() == JVMTI_PHASE_LIVE) { JavaThread* current_thread = (JavaThread*) ThreadLocalStorage::thread(); @@ -328,13 +348,13 @@ __ENTRY(jvmtiEnv*, JvmtiExport::get_jvmti_interface, current_thread) debug_only(VMNativeEntryWrapper __vew;) - JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(); + JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(version); *penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* return JNI_OK; } else if (JvmtiEnv::get_phase() == JVMTI_PHASE_ONLOAD) { // not live, no thread to transition - JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(); + JvmtiEnv *jvmti_env = JvmtiEnv::create_a_jvmti(version); *penv = jvmti_env->jvmti_external(); // actual type is jvmtiEnv* -- not to be confused with JvmtiEnv* return JNI_OK; @@ -345,6 +365,15 @@ } } + +void +JvmtiExport::decode_version_values(jint version, int * major, int * minor, + int * micro) { + *major = (version & JVMTI_VERSION_MASK_MAJOR) >> JVMTI_VERSION_SHIFT_MAJOR; + *minor = (version & JVMTI_VERSION_MASK_MINOR) >> JVMTI_VERSION_SHIFT_MINOR; + *micro = (version & JVMTI_VERSION_MASK_MICRO) >> JVMTI_VERSION_SHIFT_MICRO; +} + void JvmtiExport::enter_primordial_phase() { JvmtiEnvBase::set_phase(JVMTI_PHASE_PRIMORDIAL); } @@ -627,7 +656,7 @@ klassOop k = obj->klass(); // if the object is a java.lang.Class then return the java mirror - if (k == SystemDictionary::class_klass()) { + if (k == SystemDictionary::Class_klass()) { if (!java_lang_Class::is_primitive(obj)) { k = java_lang_Class::as_klassOop(obj); assert(k != NULL, "class for non-primitive mirror must exist"); @@ -657,11 +686,11 @@ jvmtiAddrLocationMap *_map; const void *_compile_info; public: - JvmtiCompiledMethodLoadEventMark(JavaThread *thread, nmethod *nm) + JvmtiCompiledMethodLoadEventMark(JavaThread *thread, nmethod *nm, void* compile_info_ptr = NULL) : JvmtiMethodEventMark(thread,methodHandle(thread, nm->method())) { _code_data = nm->code_begin(); _code_size = nm->code_size(); - _compile_info = NULL; /* no info for our VM. */ + _compile_info = compile_info_ptr; // Set void pointer of compiledMethodLoad Event. Default value is NULL. JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &_map, &_map_length); } ~JvmtiCompiledMethodLoadEventMark() { @@ -848,7 +877,7 @@ bool JvmtiExport::_can_get_source_debug_extension = false; bool JvmtiExport::_can_maintain_original_method_order = false; bool JvmtiExport::_can_post_interpreter_events = false; -bool JvmtiExport::_can_post_exceptions = false; +bool JvmtiExport::_can_post_on_exceptions = false; bool JvmtiExport::_can_post_breakpoint = false; bool JvmtiExport::_can_post_field_access = false; bool JvmtiExport::_can_post_field_modification = false; @@ -879,6 +908,7 @@ bool JvmtiExport::_should_post_object_free = false; bool JvmtiExport::_should_post_resource_exhausted = false; bool JvmtiExport::_should_post_vm_object_alloc = false; +bool JvmtiExport::_should_post_on_exceptions = false; //////////////////////////////////////////////////////////////////////////////////////////////// @@ -1723,6 +1753,46 @@ } } +// Returns a record containing inlining information for the given nmethod +jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) { + jint numstackframes = 0; + jvmtiCompiledMethodLoadInlineRecord* record = (jvmtiCompiledMethodLoadInlineRecord*)NEW_RESOURCE_OBJ(jvmtiCompiledMethodLoadInlineRecord); + record->header.kind = JVMTI_CMLR_INLINE_INFO; + record->header.next = NULL; + record->header.majorinfoversion = JVMTI_CMLR_MAJOR_VERSION_1; + record->header.minorinfoversion = JVMTI_CMLR_MINOR_VERSION_0; + record->numpcs = 0; + for(PcDesc* p = nm->scopes_pcs_begin(); p < nm->scopes_pcs_end(); p++) { + if(p->scope_decode_offset() == DebugInformationRecorder::serialized_null) continue; + record->numpcs++; + } + record->pcinfo = (PCStackInfo*)(NEW_RESOURCE_ARRAY(PCStackInfo, record->numpcs)); + int scope = 0; + for(PcDesc* p = nm->scopes_pcs_begin(); p < nm->scopes_pcs_end(); p++) { + if(p->scope_decode_offset() == DebugInformationRecorder::serialized_null) continue; + void* pc_address = (void*)p->real_pc(nm); + assert(pc_address != NULL, "pc_address must be non-null"); + record->pcinfo[scope].pc = pc_address; + numstackframes=0; + for(ScopeDesc* sd = nm->scope_desc_at(p->real_pc(nm));sd != NULL;sd = sd->sender()) { + numstackframes++; + } + assert(numstackframes != 0, "numstackframes must be nonzero."); + record->pcinfo[scope].methods = (jmethodID *)NEW_RESOURCE_ARRAY(jmethodID, numstackframes); + record->pcinfo[scope].bcis = (jint *)NEW_RESOURCE_ARRAY(jint, numstackframes); + record->pcinfo[scope].numstackframes = numstackframes; + int stackframe = 0; + for(ScopeDesc* sd = nm->scope_desc_at(p->real_pc(nm));sd != NULL;sd = sd->sender()) { + // sd->method() can be NULL for stubs but not for nmethods. To be completely robust, include an assert that we should never see a null sd->method() + assert(!sd->method().is_null(), "sd->method() cannot be null."); + record->pcinfo[scope].methods[stackframe] = sd->method()->jmethod_id(); + record->pcinfo[scope].bcis[stackframe] = sd->bci(); + stackframe++; + } + scope++; + } + return record; +} void JvmtiExport::post_compiled_method_load(nmethod *nm) { // If there are pending CompiledMethodUnload events then these are @@ -1751,7 +1821,11 @@ (nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string())); ResourceMark rm(thread); - JvmtiCompiledMethodLoadEventMark jem(thread, nm); + + // Add inlining information + jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm); + // Pass inlining information through the void pointer + JvmtiCompiledMethodLoadEventMark jem(thread, nm, inlinerecord); JvmtiJavaThreadEventTransition jet(thread); jvmtiEventCompiledMethodLoad callback = env->callbacks()->CompiledMethodLoad; if (callback != NULL) { @@ -1896,7 +1970,7 @@ if (collector != NULL && collector->is_enabled()) { // Don't record classes as these will be notified via the ClassLoad // event. - if (obj->klass() != SystemDictionary::class_klass()) { + if (obj->klass() != SystemDictionary::Class_klass()) { collector->record_allocation(obj); } } diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiExport.hpp --- a/src/share/vm/prims/jvmtiExport.hpp Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiExport.hpp Thu Mar 25 16:54:59 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1998-2010 Sun Microsystems, 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 @@ -66,7 +66,7 @@ JVMTI_SUPPORT_FLAG(can_get_source_debug_extension) JVMTI_SUPPORT_FLAG(can_maintain_original_method_order) JVMTI_SUPPORT_FLAG(can_post_interpreter_events) - JVMTI_SUPPORT_FLAG(can_post_exceptions) + JVMTI_SUPPORT_FLAG(can_post_on_exceptions) JVMTI_SUPPORT_FLAG(can_post_breakpoint) JVMTI_SUPPORT_FLAG(can_post_field_access) JVMTI_SUPPORT_FLAG(can_post_field_modification) @@ -93,6 +93,7 @@ JVMTI_SUPPORT_FLAG(should_post_data_dump) JVMTI_SUPPORT_FLAG(should_post_garbage_collection_start) JVMTI_SUPPORT_FLAG(should_post_garbage_collection_finish) + JVMTI_SUPPORT_FLAG(should_post_on_exceptions) // ------ the below maybe don't have to be (but are for now) // fixed conditions here ------------ @@ -236,6 +237,8 @@ static bool is_jvmti_version(jint version) { return (version & JVMTI_VERSION_MASK) == JVMTI_VERSION_VALUE; } static bool is_jvmdi_version(jint version) { return (version & JVMTI_VERSION_MASK) == JVMDI_VERSION_VALUE; } static jint get_jvmti_interface(JavaVM *jvm, void **penv, jint version); + static void decode_version_values(jint version, int * major, int * minor, + int * micro); // single stepping management methods static void at_single_stepping_point(JavaThread *thread, methodOop method, address location) KERNEL_RETURN; diff -r 39e409a664b3 -r 84043c7507b9 src/share/vm/prims/jvmtiHpp.xsl --- a/src/share/vm/prims/jvmtiHpp.xsl Thu Mar 25 16:27:12 2010 -0700 +++ b/src/share/vm/prims/jvmtiHpp.xsl Thu Mar 25 16:54:59 2010 -0700 @@ -1,6 +1,6 @@