From c4f814598efce30fe1e025cbdc992cece68bf971 Mon Sep 17 00:00:00 2001 From: John R Rose Date: Wed, 21 Oct 2009 23:19:48 -0700 Subject: [PATCH 01/61] 6891770: JSR 292 API needs initial unit tests Backport working mlvm regression test to M3 implementation of JSR 292; requires jtreg 4.1 Reviewed-by: twisti --- jdk/test/java/dyn/MethodHandlesTest.java | 881 +++++++++++++++++++++++ 1 file changed, 881 insertions(+) create mode 100644 jdk/test/java/dyn/MethodHandlesTest.java diff --git a/jdk/test/java/dyn/MethodHandlesTest.java b/jdk/test/java/dyn/MethodHandlesTest.java new file mode 100644 index 00000000000..26142c8c527 --- /dev/null +++ b/jdk/test/java/dyn/MethodHandlesTest.java @@ -0,0 +1,881 @@ +/* + * 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. 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. + */ + +/* @test + * @summary unit tests for java.dyn.MethodHandles + * @compile -XDinvokedynamic MethodHandlesTest.java + * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic jdk.java.dyn.MethodHandlesTest + */ + +package jdk.java.dyn; + +import java.dyn.*; +import java.dyn.MethodHandles.Lookup; +import java.lang.reflect.*; +import java.util.*; +import org.junit.*; +import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; + + +/** + * + * @author jrose + */ +public class MethodHandlesTest { + // How much output? + static int verbosity = 1; + + // Set this true during development if you want to fast-forward to + // a particular new, non-working test. Tests which are known to + // work (or have recently worked) test this flag and return on true. + static boolean CAN_SKIP_WORKING = false; + //static { CAN_SKIP_WORKING = true; } + + // Set true to test more calls. If false, some tests are just + // lookups, without exercising the actual method handle. + static boolean DO_MORE_CALLS = false; + + + @Test + public void testFirst() throws Throwable { + verbosity += 9; try { + // left blank for debugging + } finally { verbosity -= 9; } + } + + static final int MAX_ARG_INCREASE = 3; + + public MethodHandlesTest() { + } + + @Before + public void checkImplementedPlatform() { + boolean platformOK = false; + Properties properties = System.getProperties(); + String vers = properties.getProperty("java.vm.version"); + String name = properties.getProperty("java.vm.name"); + String arch = properties.getProperty("os.arch"); + if (arch.equals("i386") && + (name.contains("Client") || name.contains("Server")) + ) { + platformOK = true; + } else { + System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); + } + assumeTrue(platformOK); + } + + String testName; + int posTests, negTests; + @After + public void printCounts() { + if (verbosity >= 1 && (posTests | negTests) != 0) { + System.out.println(); + if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run"); + if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run"); + } + } + void countTest(boolean positive) { + if (positive) ++posTests; + else ++negTests; + } + void countTest() { countTest(true); } + void startTest(String name) { + if (testName != null) printCounts(); + if (verbosity >= 0) + System.out.println(name); + posTests = negTests = 0; + testName = name; + } + + @BeforeClass + public static void setUpClass() throws Exception { + calledLog.clear(); + calledLog.add(null); + nextArg = 1000000; + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + static List calledLog = new ArrayList(); + static Object logEntry(String name, Object... args) { + return Arrays.asList(name, Arrays.asList(args)); + } + static Object called(String name, Object... args) { + Object entry = logEntry(name, args); + calledLog.add(entry); + return entry; + } + static void assertCalled(String name, Object... args) { + Object expected = logEntry(name, args); + Object actual = calledLog.get(calledLog.size() - 1); + if (expected.equals(actual)) return; + System.out.println("assertCalled "+name+":"); + System.out.println("expected: "+expected); + System.out.println("actual: "+actual); + System.out.println("ex. types: "+getClasses(expected)); + System.out.println("act. types: "+getClasses(actual)); + assertEquals("previous method call types", expected, actual); + assertEquals("previous method call", expected, actual); + } + static void printCalled(MethodHandle target, String name, Object... args) { + if (verbosity >= 2) + System.out.println("calling "+logEntry(name, args)+" on "+target); + } + + static Object castToWrapper(Object value, Class dst) { + Object wrap = null; + if (value instanceof Number) + wrap = castToWrapperOrNull(((Number)value).longValue(), dst); + if (value instanceof Character) + wrap = castToWrapperOrNull((char)(Character)value, dst); + if (wrap != null) return wrap; + return dst.cast(value); + } + + static Object castToWrapperOrNull(long value, Class dst) { + if (dst == int.class || dst == Integer.class) + return (int)(value); + if (dst == long.class || dst == Long.class) + return (long)(value); + if (dst == char.class || dst == Character.class) + return (char)(value); + if (dst == short.class || dst == Short.class) + return (short)(value); + if (dst == float.class || dst == Float.class) + return (float)(value); + if (dst == double.class || dst == Double.class) + return (double)(value); + return null; + } + + static int nextArg; + static Object randomArg(Class param) { + Object wrap = castToWrapperOrNull(nextArg, param); + if (wrap != null) { + nextArg++; + return wrap; + } +// import sun.dyn.util.Wrapper; +// Wrapper wrap = Wrapper.forBasicType(dst); +// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst)) +// wrap = Wrapper.forWrapperType(dst); +// if (wrap != Wrapper.OBJECT) +// return wrap.wrap(nextArg++); + if (param.isInterface() || param.isAssignableFrom(String.class)) + return "#"+(nextArg++); + else + try { + return param.newInstance(); + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + return null; // random class not Object, String, Integer, etc. + } + static Object[] randomArgs(Class... params) { + Object[] args = new Object[params.length]; + for (int i = 0; i < args.length; i++) + args[i] = randomArg(params[i]); + return args; + } + static Object[] randomArgs(int nargs, Class param) { + Object[] args = new Object[nargs]; + for (int i = 0; i < args.length; i++) + args[i] = randomArg(param); + return args; + } + + static T[] array(Class atype, E... a) { + return Arrays.copyOf(a, a.length, atype); + } + static T[] cat(T[] a, T... b) { + int alen = a.length, blen = b.length; + if (blen == 0) return a; + T[] c = Arrays.copyOf(a, alen + blen); + System.arraycopy(b, 0, c, alen, blen); + return c; + } + static Integer[] boxAll(int... vx) { + Integer[] res = new Integer[vx.length]; + for (int i = 0; i < res.length; i++) { + res[i] = vx[i]; + } + return res; + } + static Object getClasses(Object x) { + if (x == null) return x; + if (x instanceof String) return x; // keep the name + if (x instanceof List) { + // recursively report classes of the list elements + Object[] xa = ((List)x).toArray(); + for (int i = 0; i < xa.length; i++) + xa[i] = getClasses(xa[i]); + return Arrays.asList(xa); + } + return x.getClass().getSimpleName(); + } + + static MethodHandle changeArgTypes(MethodHandle target, Class argType) { + return changeArgTypes(target, 0, 999, argType); + } + static MethodHandle changeArgTypes(MethodHandle target, + int beg, int end, Class argType) { + MethodType targetType = target.type(); + end = Math.min(end, targetType.parameterCount()); + ArrayList> argTypes = new ArrayList>(targetType.parameterList()); + Collections.fill(argTypes.subList(beg, end), argType); + MethodType ttype2 = MethodType.make(targetType.returnType(), argTypes); + return MethodHandles.convertArguments(target, ttype2); + } + + // This lookup is good for all members in and under MethodHandlesTest. + static final Lookup PRIVATE = MethodHandles.lookup(); + // This lookup is good for package-private members but not private ones. + static final Lookup PACKAGE = PackageSibling.lookup(); + // This lookup is good only for public members. + static final Lookup PUBLIC = MethodHandles.Lookup.PUBLIC_LOOKUP; + + // Subject methods... + static class Example implements IntExample { + final String name; + public Example() { name = "Example#"+(nextArg++); } + protected Example(String name) { this.name = name; } + protected Example(int x) { this(); called("protected ", this, x); } + @Override public String toString() { return name; } + + public void v0() { called("v0", this); } + void pkg_v0() { called("pkg_v0", this); } + private void pri_v0() { called("pri_v0", this); } + public static void s0() { called("s0"); } + static void pkg_s0() { called("pkg_s0"); } + private static void pri_s0() { called("pri_s0"); } + + public Object v1(Object x) { return called("v1", this, x); } + public Object v2(Object x, Object y) { return called("v2", this, x, y); } + public Object v2(Object x, int y) { return called("v2", this, x, y); } + public Object v2(int x, Object y) { return called("v2", this, x, y); } + public Object v2(int x, int y) { return called("v2", this, x, y); } + public static Object s1(Object x) { return called("s1", x); } + public static Object s2(int x) { return called("s2", x); } + public static Object s3(long x) { return called("s3", x); } + public static Object s4(int x, int y) { return called("s4", x, y); } + public static Object s5(long x, int y) { return called("s5", x, y); } + public static Object s6(int x, long y) { return called("s6", x, y); } + public static Object s7(float x, double y) { return called("s7", x, y); } + } + public static class PubExample extends Example { + } + static class SubExample extends Example { + @Override public void v0() { called("Sub/v0", this); } + @Override void pkg_v0() { called("Sub/pkg_v0", this); } + private SubExample(int x) { called("", this, x); } + public SubExample() { super("SubExample#"+(nextArg++)); } + } + public static interface IntExample { + public void v0(); + static class Impl implements IntExample { + public void v0() { called("Int/v0", this); } + final String name; + public Impl() { name = "Example#"+(nextArg++); } + } + } + + static final Object[][][] ACCESS_CASES = { + { { true, PRIVATE } } // only one test case at present + }; + + static Object[][] accessCases(Class defc, String name) { + return ACCESS_CASES[0]; + } + + @Test + public void testFindStatic() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("findStatic"); + testFindStatic(PubExample.class, void.class, "s0"); + testFindStatic(Example.class, void.class, "s0"); + testFindStatic(Example.class, void.class, "pkg_s0"); + testFindStatic(Example.class, void.class, "pri_s0"); + + testFindStatic(Example.class, Object.class, "s1", Object.class); + testFindStatic(Example.class, Object.class, "s2", int.class); + testFindStatic(Example.class, Object.class, "s3", long.class); + testFindStatic(Example.class, Object.class, "s4", int.class, int.class); + testFindStatic(Example.class, Object.class, "s5", long.class, int.class); + testFindStatic(Example.class, Object.class, "s6", int.class, long.class); + testFindStatic(Example.class, Object.class, "s7", float.class, double.class); + + testFindStatic(false, PRIVATE, Example.class, void.class, "bogus"); + } + + void testFindStatic(Class defc, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testFindStatic((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params); + } + } + void testFindStatic(Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + testFindStatic(true, lookup, defc, ret, name, params); + } + void testFindStatic(boolean positive, Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.findStatic(defc, name, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("findStatic "+lookup+": "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(type, target.type()); + assertTrue(target.toString().contains(name)); // rough check + if (!DO_MORE_CALLS && lookup != PRIVATE) return; + Object[] args = randomArgs(params); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test + public void testFindVirtual() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("findVirtual"); + testFindVirtual(Example.class, void.class, "v0"); + testFindVirtual(Example.class, void.class, "pkg_v0"); + testFindVirtual(Example.class, void.class, "pri_v0"); + testFindVirtual(Example.class, Object.class, "v1", Object.class); + testFindVirtual(Example.class, Object.class, "v2", Object.class, Object.class); + testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class); + testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class); + testFindVirtual(Example.class, Object.class, "v2", int.class, int.class); + testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus"); + // test dispatch + testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/v0"); + testFindVirtual(SubExample.class, Example.class, void.class, "Sub/v0"); + testFindVirtual(SubExample.class, IntExample.class, void.class, "Sub/v0"); + testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/pkg_v0"); + testFindVirtual(SubExample.class, Example.class, void.class, "Sub/pkg_v0"); + testFindVirtual(Example.class, IntExample.class, void.class, "v0"); + testFindVirtual(IntExample.Impl.class, IntExample.class, void.class, "Int/v0"); + } + + void testFindVirtual(Class defc, Class ret, String name, Class... params) throws Throwable { + Class rcvc = defc; + testFindVirtual(rcvc, defc, ret, name, params); + } + void testFindVirtual(Class rcvc, Class defc, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testFindVirtual((Boolean)ac[0], (Lookup)ac[1], rcvc, defc, ret, name, params); + } + } + void testFindVirtual(Lookup lookup, Class rcvc, Class defc, Class ret, String name, Class... params) throws Throwable { + testFindVirtual(true, lookup, rcvc, defc, ret, name, params); + } + void testFindVirtual(boolean positive, Lookup lookup, Class rcvc, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.findVirtual(defc, methodName, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("findVirtual "+lookup+": "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + Class[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); + MethodType typeWithSelf = MethodType.make(ret, paramsWithSelf); + MethodType ttype = target.type(); + ttype = ttype.changeParameterType(0, defc); // FIXME: test this + assertEquals(typeWithSelf, ttype); + assertTrue(target.toString().contains(methodName)); // rough check + if (!DO_MORE_CALLS && lookup != PRIVATE) return; + Object[] argsWithSelf = randomArgs(paramsWithSelf); + if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); + printCalled(target, name, argsWithSelf); + MethodHandles.invoke(target, argsWithSelf); + assertCalled(name, argsWithSelf); + System.out.print(':'); + } + + @Test + public void testFindSpecial() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("findSpecial"); + testFindSpecial(Example.class, void.class, "v0"); + testFindSpecial(Example.class, void.class, "pkg_v0"); + testFindSpecial(false, PRIVATE, Example.class, void.class, "", int.class); + testFindSpecial(false, PRIVATE, Example.class, void.class, "bogus"); + } + + void testFindSpecial(Class defc, Class ret, String name, Class... params) throws Throwable { + testFindSpecial(true, PRIVATE, defc, ret, name, params); + testFindSpecial(false, PACKAGE, defc, ret, name, params); + testFindSpecial(false, PUBLIC, defc, ret, name, params); + } + void testFindSpecial(boolean positive, Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + MethodType type = MethodType.make(ret, params); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.findSpecial(defc, name, type, defc); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("findSpecial "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + Class[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); + MethodType typeWithSelf = MethodType.make(ret, paramsWithSelf); + MethodType ttype = target.type(); + ttype = ttype.changeParameterType(0, defc); // FIXME: test this + assertEquals(typeWithSelf, ttype); + assertTrue(target.toString().contains(name)); // rough check + if (!DO_MORE_CALLS && lookup != PRIVATE) return; + Object[] args = randomArgs(paramsWithSelf); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + assertCalled(name, args); + System.out.print(':'); + } + + @Test + public void testBind() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("bind"); + testBind(Example.class, void.class, "v0"); + testBind(Example.class, void.class, "pkg_v0"); + testBind(Example.class, void.class, "pri_v0"); + testBind(Example.class, Object.class, "v1", Object.class); + testBind(Example.class, Object.class, "v2", Object.class, Object.class); + testBind(Example.class, Object.class, "v2", Object.class, int.class); + testBind(Example.class, Object.class, "v2", int.class, Object.class); + testBind(Example.class, Object.class, "v2", int.class, int.class); + testBind(false, PRIVATE, Example.class, void.class, "bogus"); + testBind(SubExample.class, void.class, "Sub/v0"); + testBind(SubExample.class, void.class, "Sub/pkg_v0"); + testBind(IntExample.Impl.class, void.class, "Int/v0"); + } + + void testBind(Class defc, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testBind((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params); + } + } + + void testBind(boolean positive, Lookup lookup, Class defc, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo + MethodType type = MethodType.make(ret, params); + Object receiver = randomArg(defc); + MethodHandle target = null; + RuntimeException noAccess = null; + try { + target = lookup.bind(receiver, methodName, type); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(type, target.type()); + Object[] args = randomArgs(params); + printCalled(target, name, args); + MethodHandles.invoke(target, args); + Object[] argsWithReceiver = cat(array(Object[].class, receiver), args); + assertCalled(name, argsWithReceiver); + System.out.print(':'); + } + + @Test + public void testUnreflect() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("unreflect"); + testUnreflect(Example.class, true, void.class, "s0"); + testUnreflect(Example.class, true, void.class, "pkg_s0"); + testUnreflect(Example.class, true, void.class, "pri_s0"); + + testUnreflect(Example.class, true, Object.class, "s1", Object.class); + testUnreflect(Example.class, true, Object.class, "s2", int.class); + //testUnreflect(Example.class, true, Object.class, "s3", long.class); + //testUnreflect(Example.class, true, Object.class, "s4", int.class, int.class); + //testUnreflect(Example.class, true, Object.class, "s5", long.class, int.class); + //testUnreflect(Example.class, true, Object.class, "s6", int.class, long.class); + + testUnreflect(Example.class, false, void.class, "v0"); + testUnreflect(Example.class, false, void.class, "pkg_v0"); + testUnreflect(Example.class, false, void.class, "pri_v0"); + testUnreflect(Example.class, false, Object.class, "v1", Object.class); + testUnreflect(Example.class, false, Object.class, "v2", Object.class, Object.class); + testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class); + testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class); + testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class); + } + + void testUnreflect(Class defc, boolean isStatic, Class ret, String name, Class... params) throws Throwable { + for (Object[] ac : accessCases(defc, name)) { + testUnreflect((Boolean)ac[0], (Lookup)ac[1], defc, isStatic, ret, name, params); + } + } + void testUnreflect(boolean positive, Lookup lookup, Class defc, boolean isStatic, Class ret, String name, Class... params) throws Throwable { + countTest(positive); + MethodType type = MethodType.make(ret, params); + Method rmethod = null; + MethodHandle target = null; + RuntimeException noAccess = null; + try { + rmethod = defc.getDeclaredMethod(name, params); + } catch (NoSuchMethodException ex) { + throw new NoAccessException(ex); + } + assertEquals(isStatic, Modifier.isStatic(rmethod.getModifiers())); + try { + target = lookup.unreflect(rmethod); + } catch (NoAccessException ex) { + noAccess = ex; + } + if (verbosity >= 2) + System.out.println("unreflect "+defc+"."+name+"/"+type+" => "+target + +(noAccess == null ? "" : " !! "+noAccess)); + if (positive && noAccess != null) throw noAccess; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + Class[] paramsMaybeWithSelf = params; + if (!isStatic) { + paramsMaybeWithSelf = cat(array(Class[].class, (Class)defc), params); + } + MethodType typeMaybeWithSelf = MethodType.make(ret, paramsMaybeWithSelf); + MethodType ttype = target.type(); + if (!isStatic) + ttype = ttype.changeParameterType(0, defc); // FIXME: test this + assertEquals(typeMaybeWithSelf, ttype); + Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf); + printCalled(target, name, argsMaybeWithSelf); + MethodHandles.invoke(target, argsMaybeWithSelf); + assertCalled(name, argsMaybeWithSelf); + System.out.print(':'); + } + + @Test @Ignore("unimplemented") + public void testUnreflectSpecial() throws Throwable { + Lookup lookup = PRIVATE; // FIXME: test more lookups than this one + startTest("unreflectSpecial"); + Method m = null; + MethodHandle expResult = null; + MethodHandle result = lookup.unreflectSpecial(m, Example.class); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testUnreflectGetter() throws Throwable { + Lookup lookup = PRIVATE; // FIXME: test more lookups than this one + startTest("unreflectGetter"); + Field f = null; + MethodHandle expResult = null; + MethodHandle result = lookup.unreflectGetter(f); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testUnreflectSetter() throws Throwable { + Lookup lookup = PRIVATE; // FIXME: test more lookups than this one + startTest("unreflectSetter"); + Field f = null; + MethodHandle expResult = null; + MethodHandle result = lookup.unreflectSetter(f); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testArrayElementGetter() throws Throwable { + startTest("arrayElementGetter"); + Class arrayClass = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.arrayElementGetter(arrayClass); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + @Test @Ignore("unimplemented") + public void testArrayElementSetter() throws Throwable { + startTest("arrayElementSetter"); + Class arrayClass = null; + MethodHandle expResult = null; + MethodHandle result = MethodHandles.arrayElementSetter(arrayClass); + assertEquals(expResult, result); + fail("The test case is a prototype."); + } + + static class Callee { + static Object id() { return called("id"); } + static Object id(Object x) { return called("id", x); } + static Object id(Object x, Object y) { return called("id", x, y); } + static Object id(Object x, Object y, Object z) { return called("id", x, y, z); } + static Object id(Object... vx) { return called("id", vx); } + static MethodHandle ofType(int n) { + return ofType(Object.class, n); + } + static MethodHandle ofType(Class rtype, int n) { + if (n == -1) + return ofType(MethodType.make(rtype, Object[].class)); + return ofType(MethodType.makeGeneric(n).changeReturnType(rtype)); + } + static MethodHandle ofType(Class rtype, Class... ptypes) { + return ofType(MethodType.make(rtype, ptypes)); + } + static MethodHandle ofType(MethodType type) { + Class rtype = type.returnType(); + String pfx = ""; + if (rtype != Object.class) + pfx = rtype.getSimpleName().substring(0, 1).toLowerCase(); + String name = pfx+"id"; + return PRIVATE.findStatic(Callee.class, name, type); + } + } + + @Test + public void testConvertArguments() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("convertArguments"); + testConvert(Callee.ofType(1), null, "id", int.class); + testConvert(Callee.ofType(1), null, "id", String.class); + testConvert(Callee.ofType(1), null, "id", Integer.class); + testConvert(Callee.ofType(1), null, "id", short.class); + } + + void testConvert(MethodHandle id, Class rtype, String name, Class... params) throws Throwable { + testConvert(true, id, rtype, name, params); + } + + void testConvert(boolean positive, MethodHandle id, Class rtype, String name, Class... params) throws Throwable { + countTest(positive); + MethodType idType = id.type(); + if (rtype == null) rtype = idType.returnType(); + for (int i = 0; i < params.length; i++) { + if (params[i] == null) params[i] = idType.parameterType(i); + } + // simulate the pairwise conversion + MethodType newType = MethodType.make(rtype, params); + Object[] args = randomArgs(newType.parameterArray()); + Object[] convArgs = args.clone(); + for (int i = 0; i < args.length; i++) { + Class src = newType.parameterType(i); + Class dst = idType.parameterType(i); + if (src != dst) + convArgs[i] = castToWrapper(convArgs[i], dst); + } + Object convResult = MethodHandles.invoke(id, convArgs); + { + Class dst = newType.returnType(); + Class src = idType.returnType(); + if (src != dst) + convResult = castToWrapper(convResult, dst); + } + MethodHandle target = null; + RuntimeException error = null; + try { + target = MethodHandles.convertArguments(id, newType); + } catch (RuntimeException ex) { + error = ex; + } + if (verbosity >= 2) + System.out.println("convert "+id+ " to "+newType+" => "+target + +(error == null ? "" : " !! "+error)); + if (positive && error != null) throw error; + assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); + if (!positive) return; // negative test failed as expected + assertEquals(newType, target.type()); + printCalled(target, id.toString(), args); + Object result = MethodHandles.invoke(target, args); + assertCalled(name, convArgs); + assertEquals(convResult, result); + System.out.print(':'); + } + + @Test + public void testInsertArguments() throws Throwable { + if (CAN_SKIP_WORKING) return; + startTest("insertArguments"); + for (int nargs = 0; nargs <= 4; nargs++) { + for (int ins = 0; ins <= 4; ins++) { + if (ins > MAX_ARG_INCREASE) continue; // FIXME Fail_6 + for (int pos = 0; pos <= nargs; pos++) { + testInsertArguments(nargs, pos, ins); + } + } + } + } + + void testInsertArguments(int nargs, int pos, int ins) throws Throwable { + if (pos != 0 || ins != 1) return; // temp. restriction until MHs.insertArguments + countTest(); + MethodHandle target = ValueConversions.varargsArray(nargs + ins); + Object[] args = randomArgs(target.type().parameterArray()); + List resList = Arrays.asList(args); + List argsToPass = new ArrayList(resList); + List argsToInsert = argsToPass.subList(pos, pos + ins); + if (verbosity >= 2) + System.out.println("insert: "+argsToInsert+" into "+target); + MethodHandle target2 = MethodHandles.insertArgument(target, pos, + argsToInsert.get(0)); + argsToInsert.clear(); // remove from argsToInsert + Object res2 = MethodHandles.invoke(target2, argsToPass.toArray()); + Object res2List = Arrays.asList((Object[])res2); + if (verbosity >= 2) + System.out.println("result: "+res2List); + //if (!resList.equals(res2List)) + // System.out.println("*** fail at n/p/i = "+nargs+"/"+pos+"/"+ins+": "+resList+" => "+res2List); + assertEquals(resList, res2List); + } + + private static final String MISSING_ARG = "missingArg"; + static Object targetIfEquals() { + return called("targetIfEquals"); + } + static Object fallbackIfNotEquals() { + return called("fallbackIfNotEquals"); + } + static Object targetIfEquals(Object x) { + assertEquals(x, MISSING_ARG); + return called("targetIfEquals", x); + } + static Object fallbackIfNotEquals(Object x) { + assertFalse(x.toString(), x.equals(MISSING_ARG)); + return called("fallbackIfNotEquals", x); + } + static Object targetIfEquals(Object x, Object y) { + assertEquals(x, y); + return called("targetIfEquals", x, y); + } + static Object fallbackIfNotEquals(Object x, Object y) { + assertFalse(x.toString(), x.equals(y)); + return called("fallbackIfNotEquals", x, y); + } + static Object targetIfEquals(Object x, Object y, Object z) { + assertEquals(x, y); + return called("targetIfEquals", x, y, z); + } + static Object fallbackIfNotEquals(Object x, Object y, Object z) { + assertFalse(x.toString(), x.equals(y)); + return called("fallbackIfNotEquals", x, y, z); + } + +} +// Local abbreviated copy of sun.dyn.util.ValueConversions +class ValueConversions { + private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); + private static final Object[] NO_ARGS_ARRAY = {}; + private static Object[] makeArray(Object... args) { return args; } + private static Object[] array() { return NO_ARGS_ARRAY; } + private static Object[] array(Object a0) + { return makeArray(a0); } + private static Object[] array(Object a0, Object a1) + { return makeArray(a0, a1); } + private static Object[] array(Object a0, Object a1, Object a2) + { return makeArray(a0, a1, a2); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3) + { return makeArray(a0, a1, a2, a3); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4) + { return makeArray(a0, a1, a2, a3, a4); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { return makeArray(a0, a1, a2, a3, a4, a5); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { return makeArray(a0, a1, a2, a3, a4, a5, a6); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + static MethodHandle[] makeArrays() { + ArrayList arrays = new ArrayList(); + MethodHandles.Lookup lookup = IMPL_LOOKUP; + for (;;) { + int nargs = arrays.size(); + MethodType type = MethodType.makeGeneric(nargs).changeReturnType(Object[].class); + String name = "array"; + MethodHandle array = null; + try { + array = lookup.findStatic(ValueConversions.class, name, type); + } catch (NoAccessException ex) { + } + if (array == null) break; + arrays.add(array); + } + assert(arrays.size() == 11); // current number of methods + return arrays.toArray(new MethodHandle[0]); + } + static final MethodHandle[] ARRAYS = makeArrays(); + + /** Return a method handle that takes the indicated number of Object + * arguments and returns an Object array of them, as if for varargs. + */ + public static MethodHandle varargsArray(int nargs) { + if (nargs < ARRAYS.length) + return ARRAYS[nargs]; + // else need to spin bytecode or do something else fancy + throw new UnsupportedOperationException("NYI"); + } +} +// This guy tests access from outside the same package member, but inside +// the package itself. +class PackageSibling { + static Lookup lookup() { + return MethodHandles.lookup(); + } +} + From 2f7d60fa36f1a572aea6175d4806963626e9fa01 Mon Sep 17 00:00:00 2001 From: Paul Hohensee Date: Wed, 28 Oct 2009 16:25:51 -0400 Subject: [PATCH 02/61] 6887571: Increase default heap config sizes Apply modification of existing server heap size ergo to all collectors except CMS. Reviewed-by: jmasa, ysr, xlu --- hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp | 22 +- hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp | 23 +- hotspot/src/cpu/sparc/vm/globals_sparc.hpp | 21 +- hotspot/src/cpu/x86/vm/c1_globals_x86.hpp | 21 +- hotspot/src/cpu/x86/vm/c2_globals_x86.hpp | 17 +- hotspot/src/cpu/x86/vm/globals_x86.hpp | 32 ++- hotspot/src/cpu/zero/vm/globals_zero.hpp | 8 - .../os_cpu/linux_x86/vm/globals_linux_x86.hpp | 8 +- .../solaris_x86/vm/globals_solaris_x86.hpp | 14 +- .../windows_x86/vm/globals_windows_x86.hpp | 5 +- .../psGCAdaptivePolicyCounters.cpp | 2 +- .../src/share/vm/memory/collectorPolicy.cpp | 2 +- hotspot/src/share/vm/runtime/arguments.cpp | 207 ++++++++++-------- hotspot/src/share/vm/runtime/arguments.hpp | 9 +- hotspot/src/share/vm/runtime/globals.cpp | 26 +++ hotspot/src/share/vm/runtime/globals.hpp | 176 +++++++++------ .../share/vm/runtime/globals_extension.hpp | 1 + hotspot/src/share/vm/services/management.cpp | 2 +- 18 files changed, 320 insertions(+), 276 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp index cb2bab6ea93..d47f7ce41d5 100644 --- a/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/c1_globals_sparc.hpp @@ -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(intx, OnStackReplacePercentage, 1400 ); 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 --git a/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp index 92f69be26bb..2df4dbd8ede 100644 --- a/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/c2_globals_sparc.hpp @@ -59,7 +59,6 @@ define_pd_global(intx, FLOATPRESSURE, 52); // C2 on V9 gets to u 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 @@ define_pd_global(bool, OptoScheduling, true); // 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 --git a/hotspot/src/cpu/sparc/vm/globals_sparc.hpp b/hotspot/src/cpu/sparc/vm/globals_sparc.hpp index ff28c96cfc7..e115ef2a9e0 100644 --- a/hotspot/src/cpu/sparc/vm/globals_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/globals_sparc.hpp @@ -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, NeedsDeoptSuspend, true); // register window ma 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 --git a/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp b/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp index 659dcca4732..bbf96cba1f2 100644 --- a/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c1_globals_x86.hpp @@ -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, Tier4BackEdgeThreshold, 100000); 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 --git a/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp index 6b3d7250442..b299e5a5480 100644 --- a/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp +++ b/hotspot/src/cpu/x86/vm/c2_globals_x86.hpp @@ -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, 1000); 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 @@ define_pd_global(intx, FreqInlineSize, 325); #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, InitialCodeCacheSize, 2496*K); // Integral multip 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(intx, ReservedCodeCacheSize, 48*M); 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 --git a/hotspot/src/cpu/x86/vm/globals_x86.hpp b/hotspot/src/cpu/x86/vm/globals_x86.hpp index f5586c1ee56..764e7ef284a 100644 --- a/hotspot/src/cpu/x86/vm/globals_x86.hpp +++ b/hotspot/src/cpu/x86/vm/globals_x86.hpp @@ -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 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap NUL // 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 --git a/hotspot/src/cpu/zero/vm/globals_zero.hpp b/hotspot/src/cpu/zero/vm/globals_zero.hpp index 89cf7077dee..ca61623cade 100644 --- a/hotspot/src/cpu/zero/vm/globals_zero.hpp +++ b/hotspot/src/cpu/zero/vm/globals_zero.hpp @@ -23,10 +23,8 @@ * */ -// // 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); @@ -37,12 +35,6 @@ define_pd_global(bool, ImplicitNullChecks, true); define_pd_global(bool, UncommonNullCast, true); define_pd_global(intx, CodeEntryAlignment, 32); -define_pd_global(uintx, TLABSize, 0); -#ifdef _LP64 -define_pd_global(uintx, NewSize, ScaleForWordSize(2048 * K)); -#else -define_pd_global(uintx, NewSize, ScaleForWordSize(1024 * K)); -#endif // _LP64 define_pd_global(intx, InlineFrequencyCount, 100); define_pd_global(intx, InlineSmallCode, 1000); define_pd_global(intx, PreInflateSpin, 10); diff --git a/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp index a1cb9732ace..708cc3e085c 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp +++ b/hotspot/src/os_cpu/linux_x86/vm/globals_linux_x86.hpp @@ -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 @@ define_pd_global(intx, VMThreadStackSize, 512); #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 --git a/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp index 4b51eef5ee0..4b2749bc193 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/globals_solaris_x86.hpp @@ -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 --git a/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp index 300541e0e96..e5cf1dfe371 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp +++ b/hotspot/src/os_cpu/windows_x86/vm/globals_windows_x86.hpp @@ -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(bool, DontYieldALot, false); 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 --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp index 02c6450a7a0..26345dbc924 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGCAdaptivePolicyCounters.cpp @@ -51,7 +51,7 @@ PSGCAdaptivePolicyCounters::PSGCAdaptivePolicyCounters(const char* name_arg, 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 --git a/hotspot/src/share/vm/memory/collectorPolicy.cpp b/hotspot/src/share/vm/memory/collectorPolicy.cpp index 3f885d9ecba..ebb886fbad8 100644 --- a/hotspot/src/share/vm/memory/collectorPolicy.cpp +++ b/hotspot/src/share/vm/memory/collectorPolicy.cpp @@ -55,7 +55,7 @@ void CollectorPolicy::initialize_flags() { 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 --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index ccd568f3a6d..4c2281a98dc 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -37,7 +37,6 @@ SystemProperty* Arguments::_system_properties = NULL; const char* Arguments::_gc_log_filename = NULL; bool Arguments::_has_profile = false; bool Arguments::_has_alloc_profile = false; -uintx Arguments::_initial_heap_size = 0; uintx Arguments::_min_heap_size = 0; Arguments::Mode Arguments::_mode = _mixed; bool Arguments::_java_compiler = false; @@ -182,6 +181,9 @@ static ObsoleteFlag obsolete_jvm_flags[] = { { "ProcessingToTenuringRatio", JDK_Version::jdk(5), JDK_Version::jdk(7) }, { "MinTrainLength", JDK_Version::jdk(5), JDK_Version::jdk(7) }, { "AppendRatio", JDK_Version::jdk_update(6,10), JDK_Version::jdk(7) }, + { "DefaultMaxRAM", JDK_Version::jdk_update(6,18), JDK_Version::jdk(7) }, + { "DefaultInitialRAMFraction", + JDK_Version::jdk_update(6,18), JDK_Version::jdk(7) }, { NULL, JDK_Version(0), JDK_Version(0) } }; @@ -555,6 +557,10 @@ static bool set_numeric_flag(char* name, char* value, FlagValueOrigin origin) { if (!is_neg && CommandLineFlags::uintxAtPut(name, &uintx_v, origin)) { return true; } + uint64_t uint64_t_v = (uint64_t) v; + if (!is_neg && CommandLineFlags::uint64_tAtPut(name, &uint64_t_v, origin)) { + return true; + } return false; } @@ -947,7 +953,7 @@ static void no_shared_spaces() { // UseParNewGC and not explicitly set ParallelGCThreads we // set it, unless this is a single cpu machine. void Arguments::set_parnew_gc_flags() { - assert(!UseSerialGC && !UseParallelGC && !UseG1GC, + assert(!UseSerialGC && !UseParallelOldGC && !UseParallelGC && !UseG1GC, "control point invariant"); assert(UseParNewGC, "Error"); @@ -960,13 +966,13 @@ void Arguments::set_parnew_gc_flags() { if (ParallelGCThreads == 0) { FLAG_SET_DEFAULT(ParallelGCThreads, Abstract_VM_Version::parallel_worker_threads()); - if (FLAG_IS_DEFAULT(ParallelGCThreads) && ParallelGCThreads == 1) { + if (ParallelGCThreads == 1) { FLAG_SET_DEFAULT(UseParNewGC, false); + FLAG_SET_DEFAULT(ParallelGCThreads, 0); } } - if (!UseParNewGC) { - FLAG_SET_DEFAULT(ParallelGCThreads, 0); - } else { + if (UseParNewGC) { + // CDS doesn't work with ParNew yet no_shared_spaces(); // By default YoungPLABSize and OldPLABSize are set to 4096 and 1024 respectively, @@ -980,7 +986,7 @@ void Arguments::set_parnew_gc_flags() { FLAG_SET_DEFAULT(OldPLABSize, (intx)1024); } - // AlwaysTenure flag should make ParNew to promote all at first collection. + // AlwaysTenure flag should make ParNew promote all at first collection. // See CR 6362902. if (AlwaysTenure) { FLAG_SET_CMDLINE(intx, MaxTenuringThreshold, 0); @@ -1003,7 +1009,7 @@ void Arguments::set_parnew_gc_flags() { // further optimization and tuning efforts, and would almost // certainly gain from analysis of platform and environment. void Arguments::set_cms_and_parnew_gc_flags() { - assert(!UseSerialGC && !UseParallelGC, "Error"); + assert(!UseSerialGC && !UseParallelOldGC && !UseParallelGC, "Error"); assert(UseConcMarkSweepGC, "CMS is expected to be on here"); // If we are using CMS, we prefer to UseParNewGC, @@ -1068,7 +1074,7 @@ void Arguments::set_cms_and_parnew_gc_flags() { } else { FLAG_SET_ERGO(uintx, MaxNewSize, preferred_max_new_size); } - if(PrintGCDetails && Verbose) { + if (PrintGCDetails && Verbose) { // Too early to use gclog_or_tty tty->print_cr("Ergo set MaxNewSize: " SIZE_FORMAT, MaxNewSize); } @@ -1097,15 +1103,15 @@ void Arguments::set_cms_and_parnew_gc_flags() { } else { min_new = NewSize; } - size_t prev_initial_size = initial_heap_size(); - if (prev_initial_size != 0 && prev_initial_size < min_new+OldSize) { - set_initial_heap_size(min_new+OldSize); + size_t prev_initial_size = InitialHeapSize; + if (prev_initial_size != 0 && prev_initial_size < min_new + OldSize) { + FLAG_SET_ERGO(uintx, InitialHeapSize, min_new + OldSize); // Currently minimum size and the initial heap sizes are the same. - set_min_heap_size(initial_heap_size()); + set_min_heap_size(InitialHeapSize); if (PrintGCDetails && Verbose) { warning("Initial heap size increased to " SIZE_FORMAT " M from " SIZE_FORMAT " M; use -XX:NewSize=... for finer control.", - initial_heap_size()/M, prev_initial_size/M); + InitialHeapSize/M, prev_initial_size/M); } } @@ -1114,12 +1120,12 @@ void Arguments::set_cms_and_parnew_gc_flags() { align_size_down(MaxHeapSize, CardTableRS::ct_max_alignment_constraint()); - if(PrintGCDetails && Verbose) { + if (PrintGCDetails && Verbose) { // Too early to use gclog_or_tty tty->print_cr("CMS set min_heap_size: " SIZE_FORMAT " initial_heap_size: " SIZE_FORMAT " max_heap: " SIZE_FORMAT, - min_heap_size(), initial_heap_size(), max_heap); + min_heap_size(), InitialHeapSize, max_heap); } if (max_heap > min_new) { // Unless explicitly requested otherwise, make young gen @@ -1127,7 +1133,7 @@ void Arguments::set_cms_and_parnew_gc_flags() { if (FLAG_IS_DEFAULT(NewSize)) { FLAG_SET_ERGO(uintx, NewSize, MAX2(NewSize, min_new)); FLAG_SET_ERGO(uintx, NewSize, MIN2(preferred_max_new_size, NewSize)); - if(PrintGCDetails && Verbose) { + if (PrintGCDetails && Verbose) { // Too early to use gclog_or_tty tty->print_cr("Ergo set NewSize: " SIZE_FORMAT, NewSize); } @@ -1137,8 +1143,8 @@ void Arguments::set_cms_and_parnew_gc_flags() { // later NewRatio will decide how it grows; see above. if (FLAG_IS_DEFAULT(OldSize)) { if (max_heap > NewSize) { - FLAG_SET_ERGO(uintx, OldSize, MIN2(3*NewSize, max_heap - NewSize)); - if(PrintGCDetails && Verbose) { + FLAG_SET_ERGO(uintx, OldSize, MIN2(3*NewSize, max_heap - NewSize)); + if (PrintGCDetails && Verbose) { // Too early to use gclog_or_tty tty->print_cr("Ergo set OldSize: " SIZE_FORMAT, OldSize); } @@ -1186,7 +1192,7 @@ void Arguments::set_cms_and_parnew_gc_flags() { inline uintx max_heap_for_compressed_oops() { LP64_ONLY(return oopDesc::OopEncodingHeapMax - MaxPermSize - os::vm_page_size()); - NOT_LP64(return DefaultMaxRAM); + NOT_LP64(ShouldNotReachHere(); return 0); } bool Arguments::should_auto_select_low_pause_collector() { @@ -1205,7 +1211,7 @@ bool Arguments::should_auto_select_low_pause_collector() { void Arguments::set_ergonomics_flags() { // Parallel GC is not compatible with sharing. If one specifies - // that they want sharing explicitly, do not set ergonmics flags. + // that they want sharing explicitly, do not set ergonomics flags. if (DumpSharedSpaces || ForceSharedSpaces) { return; } @@ -1271,8 +1277,6 @@ void Arguments::set_parallel_gc_flags() { FLAG_SET_ERGO(uintx, ParallelGCThreads, Abstract_VM_Version::parallel_worker_threads()); - // PS is a server collector, setup the heap sizes accordingly. - set_server_heap_size(); // If InitialSurvivorRatio or MinSurvivorRatio were not specified, but the // SurvivorRatio has been set, reset their default values to SurvivorRatio + // 2. By doing this we make SurvivorRatio also work for Parallel Scavenger. @@ -1302,8 +1306,6 @@ void Arguments::set_parallel_gc_flags() { void Arguments::set_g1_gc_flags() { assert(UseG1GC, "Error"); - // G1 is a server collector, setup the heap sizes accordingly. - set_server_heap_size(); #ifdef COMPILER1 FastTLABRefill = false; #endif @@ -1321,50 +1323,77 @@ void Arguments::set_g1_gc_flags() { } } -void Arguments::set_server_heap_size() { +void Arguments::set_heap_size() { + if (!FLAG_IS_DEFAULT(DefaultMaxRAMFraction)) { + // Deprecated flag + FLAG_SET_CMDLINE(uintx, MaxRAMFraction, DefaultMaxRAMFraction); + } + + const julong phys_mem = + FLAG_IS_DEFAULT(MaxRAM) ? MIN2(os::physical_memory(), (julong)MaxRAM) + : (julong)MaxRAM; + + // If the maximum heap size has not been set with -Xmx, + // then set it as fraction of the size of physical memory, + // respecting the maximum and minimum sizes of the heap. if (FLAG_IS_DEFAULT(MaxHeapSize)) { - const uint64_t reasonable_fraction = - os::physical_memory() / DefaultMaxRAMFraction; - const uint64_t maximum_size = (uint64_t) - (FLAG_IS_DEFAULT(DefaultMaxRAM) && UseCompressedOops ? - MIN2(max_heap_for_compressed_oops(), DefaultMaxRAM) : - DefaultMaxRAM); - size_t reasonable_max = - (size_t) os::allocatable_physical_memory(reasonable_fraction); - if (reasonable_max > maximum_size) { - reasonable_max = maximum_size; + julong reasonable_max = phys_mem / MaxRAMFraction; + + if (phys_mem <= MaxHeapSize * MinRAMFraction) { + // Small physical memory, so use a minimum fraction of it for the heap + reasonable_max = phys_mem / MinRAMFraction; + } else { + // Not-small physical memory, so require a heap at least + // as large as MaxHeapSize + reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize); } + if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) { + // Limit the heap size to ErgoHeapSizeLimit + reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit); + } + if (UseCompressedOops) { + // Limit the heap size to the maximum possible when using compressed oops + reasonable_max = MIN2(reasonable_max, (julong)max_heap_for_compressed_oops()); + } + reasonable_max = os::allocatable_physical_memory(reasonable_max); + + if (!FLAG_IS_DEFAULT(InitialHeapSize)) { + // An initial heap size was specified on the command line, + // so be sure that the maximum size is consistent. Done + // after call to allocatable_physical_memory because that + // method might reduce the allocation size. + reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize); + } + if (PrintGCDetails && Verbose) { // Cannot use gclog_or_tty yet. - tty->print_cr(" Max heap size for server class platform " - SIZE_FORMAT, reasonable_max); + tty->print_cr(" Maximum heap size " SIZE_FORMAT, reasonable_max); } - // If the initial_heap_size has not been set with -Xms, - // then set it as fraction of size of physical memory - // respecting the maximum and minimum sizes of the heap. - if (initial_heap_size() == 0) { - const uint64_t reasonable_initial_fraction = - os::physical_memory() / DefaultInitialRAMFraction; - const size_t reasonable_initial = - (size_t) os::allocatable_physical_memory(reasonable_initial_fraction); - const size_t minimum_size = NewSize + OldSize; - set_initial_heap_size(MAX2(MIN2(reasonable_initial, reasonable_max), - minimum_size)); - // Currently the minimum size and the initial heap sizes are the same. - set_min_heap_size(initial_heap_size()); - if (PrintGCDetails && Verbose) { - // Cannot use gclog_or_tty yet. - tty->print_cr(" Initial heap size for server class platform " - SIZE_FORMAT, initial_heap_size()); - } - } else { - // A minimum size was specified on the command line. Be sure - // that the maximum size is consistent. - if (initial_heap_size() > reasonable_max) { - reasonable_max = initial_heap_size(); - } + FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx)reasonable_max); + } + + // If the initial_heap_size has not been set with InitialHeapSize + // or -Xms, then set it as fraction of the size of physical memory, + // respecting the maximum and minimum sizes of the heap. + if (FLAG_IS_DEFAULT(InitialHeapSize)) { + julong reasonable_initial = phys_mem / InitialRAMFraction; + + reasonable_initial = MAX2(reasonable_initial, (julong)(OldSize + NewSize)); + reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize); + + reasonable_initial = os::allocatable_physical_memory(reasonable_initial); + + if (PrintGCDetails && Verbose) { + // Cannot use gclog_or_tty yet. + tty->print_cr(" Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial); } - FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx) reasonable_max); + FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial); + + // Subsequent ergonomics code may expect min_heap_size to be set + // if InitialHeapSize is. Use whatever the current values are + // for OldSize and NewSize, whether or not they were set on the + // command line. + set_min_heap_size(OldSize + NewSize); } } @@ -1448,7 +1477,7 @@ bool Arguments::verify_percentage(uintx value, const char* name) { return false; } -static void set_serial_gc_flags() { +static void force_serial_gc() { FLAG_SET_DEFAULT(UseSerialGC, true); FLAG_SET_DEFAULT(UseParNewGC, false); FLAG_SET_DEFAULT(UseConcMarkSweepGC, false); @@ -1584,15 +1613,15 @@ bool Arguments::check_vm_args_consistency() { // force sharing off. if (DumpSharedSpaces || ForceSharedSpaces) { jio_fprintf(defaultStream::error_stream(), - "Reverting to Serial GC because of %s \n", + "Reverting to Serial GC because of %s\n", ForceSharedSpaces ? " -Xshare:on" : "-Xshare:dump"); - set_serial_gc_flags(); + force_serial_gc(); FLAG_SET_DEFAULT(SOLARIS_ONLY(UseISM) NOT_SOLARIS(UseLargePages), false); } else { - if (UseSharedSpaces) { + if (UseSharedSpaces && Verbose) { jio_fprintf(defaultStream::error_stream(), "Turning off use of shared archive because of " - "choice of garbage collector or large pages \n"); + "choice of garbage collector or large pages\n"); } no_shared_spaces(); } @@ -1925,8 +1954,8 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, describe_range_error(errcode); return JNI_EINVAL; } - FLAG_SET_CMDLINE(uintx, MaxNewSize, (size_t) long_initial_eden_size); - FLAG_SET_CMDLINE(uintx, NewSize, (size_t) long_initial_eden_size); + FLAG_SET_CMDLINE(uintx, MaxNewSize, (uintx)long_initial_eden_size); + FLAG_SET_CMDLINE(uintx, NewSize, (uintx)long_initial_eden_size); // -Xms } else if (match_option(option, "-Xms", &tail)) { julong long_initial_heap_size = 0; @@ -1937,9 +1966,9 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, describe_range_error(errcode); return JNI_EINVAL; } - set_initial_heap_size((size_t) long_initial_heap_size); + FLAG_SET_CMDLINE(uintx, InitialHeapSize, (uintx)long_initial_heap_size); // Currently the minimum size and the initial heap sizes are the same. - set_min_heap_size(initial_heap_size()); + set_min_heap_size(InitialHeapSize); // -Xmx } else if (match_option(option, "-Xmx", &tail)) { julong long_max_heap_size = 0; @@ -1950,7 +1979,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, describe_range_error(errcode); return JNI_EINVAL; } - FLAG_SET_CMDLINE(uintx, MaxHeapSize, (size_t) long_max_heap_size); + FLAG_SET_CMDLINE(uintx, MaxHeapSize, (uintx)long_max_heap_size); // Xmaxf } else if (match_option(option, "-Xmaxf", &tail)) { int maxf = (int)(atof(tail) * 100); @@ -2196,9 +2225,9 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, if (FLAG_IS_DEFAULT(MaxHeapSize)) { FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); - set_initial_heap_size(MaxHeapSize); + FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); // Currently the minimum size and the initial heap sizes are the same. - set_min_heap_size(initial_heap_size()); + set_min_heap_size(initHeapSize); } if (FLAG_IS_DEFAULT(NewSize)) { // Make the young generation 3/8ths of the total heap. @@ -2676,7 +2705,7 @@ jint Arguments::parse(const JavaVMInitArgs* args) { } #ifdef SERIALGC - set_serial_gc_flags(); + force_serial_gc(); #endif // SERIALGC #ifdef KERNEL no_shared_spaces(); @@ -2690,18 +2719,22 @@ jint Arguments::parse(const JavaVMInitArgs* args) { return JNI_EINVAL; } - if (UseParallelGC || UseParallelOldGC) { - // Set some flags for ParallelGC if needed. - set_parallel_gc_flags(); - } else if (UseConcMarkSweepGC) { - // Set some flags for CMS + if (UseConcMarkSweepGC) { + // Set flags for CMS and ParNew. Check UseConcMarkSweep first + // to ensure that when both UseConcMarkSweepGC and UseParNewGC + // are true, we don't call set_parnew_gc_flags() as well. set_cms_and_parnew_gc_flags(); - } else if (UseParNewGC) { - // Set some flags for ParNew - set_parnew_gc_flags(); - } else if (UseG1GC) { - // Set some flags for garbage-first, if needed. - set_g1_gc_flags(); + } else { + // Set heap size based on available physical memory + set_heap_size(); + // Set per-collector flags + if (UseParallelGC || UseParallelOldGC) { + set_parallel_gc_flags(); + } else if (UseParNewGC) { + set_parnew_gc_flags(); + } else if (UseG1GC) { + set_g1_gc_flags(); + } } #ifdef SERIALGC diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp index c11aef0001a..a68c6cad904 100644 --- a/hotspot/src/share/vm/runtime/arguments.hpp +++ b/hotspot/src/share/vm/runtime/arguments.hpp @@ -254,7 +254,6 @@ class Arguments : AllStatic { static bool _has_profile; static bool _has_alloc_profile; static const char* _gc_log_filename; - static uintx _initial_heap_size; static uintx _min_heap_size; // -Xrun arguments @@ -300,8 +299,8 @@ class Arguments : AllStatic { static void set_g1_gc_flags(); // GC ergonomics static void set_ergonomics_flags(); - // Setup heap size for a server platform - static void set_server_heap_size(); + // Setup heap size + static void set_heap_size(); // Based on automatic selection criteria, should the // low pause collector be used. static bool should_auto_select_low_pause_collector(); @@ -434,9 +433,7 @@ class Arguments : AllStatic { static bool has_profile() { return _has_profile; } static bool has_alloc_profile() { return _has_alloc_profile; } - // -Xms , -Xmx - static uintx initial_heap_size() { return _initial_heap_size; } - static void set_initial_heap_size(uintx v) { _initial_heap_size = v; } + // -Xms, -Xmx static uintx min_heap_size() { return _min_heap_size; } static void set_min_heap_size(uintx v) { _min_heap_size = v; } diff --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp index d4a4b416d4f..b348f98e70a 100644 --- a/hotspot/src/share/vm/runtime/globals.cpp +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -324,6 +324,32 @@ void CommandLineFlagsEx::uintxAtPut(CommandLineFlagWithType flag, uintx value, F faddr->origin = origin; } +bool CommandLineFlags::uint64_tAt(char* name, size_t len, uint64_t* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_uint64_t()) return false; + *value = result->get_uint64_t(); + return true; +} + +bool CommandLineFlags::uint64_tAtPut(char* name, size_t len, uint64_t* value, FlagValueOrigin origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_uint64_t()) return false; + uint64_t old_value = result->get_uint64_t(); + result->set_uint64_t(*value); + *value = old_value; + result->origin = origin; + return true; +} + +void CommandLineFlagsEx::uint64_tAtPut(CommandLineFlagWithType flag, uint64_t value, FlagValueOrigin origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_uint64_t(), "wrong flag type"); + faddr->set_uint64_t(value); + faddr->origin = origin; +} + bool CommandLineFlags::doubleAt(char* name, size_t len, double* value) { Flag* result = Flag::find_flag(name, len); if (result == NULL) return false; diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index df9cb2b778a..b894aac439e 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -47,8 +47,8 @@ define_pd_global(intx, Tier4BackEdgeThreshold, 0); define_pd_global(intx, OnStackReplacePercentage, 0); define_pd_global(bool, ResizeTLAB, false); define_pd_global(intx, FreqInlineSize, 0); +define_pd_global(intx, InlineSmallCode, 0); define_pd_global(intx, NewSizeThreadIncrease, 4*K); -define_pd_global(intx, NewRatio, 4); define_pd_global(intx, InlineClassNatives, true); define_pd_global(intx, InlineUnsafeOps, true); define_pd_global(intx, InitialCodeCacheSize, 160*K); @@ -58,7 +58,7 @@ define_pd_global(intx, CodeCacheMinBlockLength, 1); define_pd_global(uintx,PermSize, ScaleForWordSize(4*M)); define_pd_global(uintx,MaxPermSize, ScaleForWordSize(64*M)); define_pd_global(bool, NeverActAsServerClassMachine, true); -define_pd_global(uintx, DefaultMaxRAM, 1*G); +define_pd_global(uint64_t,MaxRAM, 1ULL*G); #define CI_COMPILER_COUNT 0 #else @@ -113,6 +113,10 @@ struct Flag { uintx get_uintx() const { return *((uintx*) addr); } void set_uintx(uintx value) { *((uintx*) addr) = value; } + bool is_uint64_t() const { return strcmp(type, "uint64_t") == 0; } + uint64_t get_uint64_t() const { return *((uint64_t*) addr); } + void set_uint64_t(uint64_t value) { *((uint64_t*) addr) = value; } + bool is_double() const { return strcmp(type, "double") == 0; } double get_double() const { return *((double*) addr); } void set_double(double value) { *((double*) addr) = value; } @@ -188,6 +192,11 @@ class CommandLineFlags { static bool uintxAtPut(char* name, size_t len, uintx* value, FlagValueOrigin origin); static bool uintxAtPut(char* name, uintx* value, FlagValueOrigin origin) { return uintxAtPut(name, strlen(name), value, origin); } + static bool uint64_tAt(char* name, size_t len, uint64_t* value); + static bool uint64_tAt(char* name, uint64_t* value) { return uint64_tAt(name, strlen(name), value); } + static bool uint64_tAtPut(char* name, size_t len, uint64_t* value, FlagValueOrigin origin); + static bool uint64_tAtPut(char* name, uint64_t* value, FlagValueOrigin origin) { return uint64_tAtPut(name, strlen(name), value, origin); } + static bool doubleAt(char* name, size_t len, double* value); static bool doubleAt(char* name, double* value) { return doubleAt(name, strlen(name), value); } static bool doubleAtPut(char* name, size_t len, double* value, FlagValueOrigin origin); @@ -785,7 +794,7 @@ class CommandLineFlags { product(bool, ProfilerRecordPC, false, \ "Collects tick for each 16 byte interval of compiled code") \ \ - product(bool, ProfileVM, false, \ + product(bool, ProfileVM, false, \ "Profiles ticks that fall within VM (either in the VM Thread " \ "or VM code called through stubs)") \ \ @@ -815,7 +824,7 @@ class CommandLineFlags { \ product(bool, RegisterFinalizersAtInit, true, \ "Register finalizable objects at end of Object. or " \ - "after allocation.") \ + "after allocation") \ \ develop(bool, RegisterReferences, true, \ "Tells whether the VM should register soft/weak/final/phantom " \ @@ -862,14 +871,14 @@ class CommandLineFlags { product(bool, AlwaysLockClassLoader, false, \ "Require the VM to acquire the class loader lock before calling " \ "loadClass() even for class loaders registering " \ - "as parallel capable. Default false. ") \ + "as parallel capable") \ \ product(bool, AllowParallelDefineClass, false, \ "Allow parallel defineClass requests for class loaders " \ - "registering as parallel capable. Default false") \ + "registering as parallel capable") \ \ product(bool, MustCallLoadClassInternal, false, \ - "Call loadClassInternal() rather than loadClass().Default false") \ + "Call loadClassInternal() rather than loadClass()") \ \ product_pd(bool, DontYieldALot, \ "Throw away obvious excess yield calls (for SOLARIS only)") \ @@ -921,9 +930,9 @@ class CommandLineFlags { "(Unstable, Linux-specific)" \ " avoid NPTL-FUTEX hang pthread_cond_timedwait" ) \ \ - product(bool, FilterSpuriousWakeups , true, \ - "Prevent spurious or premature wakeups from object.wait" \ - "(Solaris only)") \ + product(bool, FilterSpuriousWakeups, true, \ + "Prevent spurious or premature wakeups from object.wait " \ + "(Solaris only)") \ \ product(intx, NativeMonitorTimeout, -1, "(Unstable)" ) \ product(intx, NativeMonitorFlags, 0, "(Unstable)" ) \ @@ -973,7 +982,7 @@ class CommandLineFlags { \ product(bool, UseAltSigs, false, \ "Use alternate signals instead of SIGUSR1 & SIGUSR2 for VM " \ - "internal signals. (Solaris only)") \ + "internal signals (Solaris only)") \ \ product(bool, UseSpinning, false, \ "Use spinning in monitor inflation and before entry") \ @@ -1265,12 +1274,12 @@ class CommandLineFlags { "Always tenure objects in eden. (ParallelGC only)") \ \ product(bool, NeverTenure, false, \ - "Never tenure objects in eden, May tenure on overflow" \ - " (ParallelGC only)") \ + "Never tenure objects in eden, May tenure on overflow " \ + "(ParallelGC only)") \ \ product(bool, ScavengeBeforeFullGC, true, \ - "Scavenge youngest generation before each full GC," \ - " used with UseParallelGC") \ + "Scavenge youngest generation before each full GC, " \ + "used with UseParallelGC") \ \ develop(bool, ScavengeWithObjectsInToSpace, false, \ "Allow scavenges to occur when to_space contains objects.") \ @@ -1283,9 +1292,9 @@ class CommandLineFlags { " (effective only when UseConcMarkSweepGC)") \ \ product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, \ - "A System.gc() request invokes a concurrent collection and" \ - " also unloads classes during such a concurrent gc cycle " \ - " (effective only when UseConcMarkSweepGC)") \ + "A System.gc() request invokes a concurrent collection and " \ + "also unloads classes during such a concurrent gc cycle " \ + "(effective only when UseConcMarkSweepGC)") \ \ develop(bool, UseCMSAdaptiveFreeLists, true, \ "Use Adaptive Free Lists in the CMS generation") \ @@ -1340,8 +1349,8 @@ class CommandLineFlags { "Whether we should simulate work queue overflow in ParNew") \ \ notproduct(uintx, ParGCWorkQueueOverflowInterval, 1000, \ - "An `interval' counter that determines how frequently" \ - " we simulate overflow; a smaller number increases frequency") \ + "An `interval' counter that determines how frequently " \ + "we simulate overflow; a smaller number increases frequency") \ \ product(uintx, ParGCDesiredObjsFromOverflowList, 20, \ "The desired number of objects to claim from the overflow list") \ @@ -1354,12 +1363,12 @@ class CommandLineFlags { "It forces all freshly committed pages to be pre-touched.") \ \ product(bool, CMSUseOldDefaults, false, \ - "A flag temporarily introduced to allow reverting to some older" \ - "default settings; older as of 6.0 ") \ + "A flag temporarily introduced to allow reverting to some " \ + "older default settings; older as of 6.0") \ \ product(intx, CMSYoungGenPerWorker, 16*M, \ "The amount of young gen chosen by default per GC worker " \ - "thread available ") \ + "thread available") \ \ product(bool, GCOverheadReporting, false, \ "Enables the GC overhead reporting facility") \ @@ -1380,43 +1389,44 @@ class CommandLineFlags { "automatically adjusted") \ \ product(uintx, CMSIncrementalDutyCycleMin, 0, \ - "Lower bound on the duty cycle when CMSIncrementalPacing is" \ - "enabled (a percentage, 0-100).") \ + "Lower bound on the duty cycle when CMSIncrementalPacing is " \ + "enabled (a percentage, 0-100)") \ \ product(uintx, CMSIncrementalSafetyFactor, 10, \ - "Percentage (0-100) used to add conservatism when computing the" \ - "duty cycle.") \ + "Percentage (0-100) used to add conservatism when computing the " \ + "duty cycle") \ \ product(uintx, CMSIncrementalOffset, 0, \ "Percentage (0-100) by which the CMS incremental mode duty cycle" \ - "is shifted to the right within the period between young GCs") \ + " is shifted to the right within the period between young GCs") \ \ product(uintx, CMSExpAvgFactor, 25, \ - "Percentage (0-100) used to weight the current sample when" \ - "computing exponential averages for CMS statistics.") \ + "Percentage (0-100) used to weight the current sample when " \ + "computing exponential averages for CMS statistics") \ \ product(uintx, CMS_FLSWeight, 50, \ - "Percentage (0-100) used to weight the current sample when" \ - "computing exponentially decating averages for CMS FLS statistics.") \ + "Percentage (0-100) used to weight the current sample when " \ + "computing exponentially decating averages for CMS FLS statistics") \ \ product(uintx, CMS_FLSPadding, 2, \ - "The multiple of deviation from mean to use for buffering" \ + "The multiple of deviation from mean to use for buffering " \ "against volatility in free list demand.") \ \ product(uintx, FLSCoalescePolicy, 2, \ "CMS: Aggression level for coalescing, increasing from 0 to 4") \ \ product(uintx, CMS_SweepWeight, 50, \ - "Percentage (0-100) used to weight the current sample when" \ - "computing exponentially decaying average for inter-sweep duration.") \ + "Percentage (0-100) used to weight the current sample when " \ + "computing exponentially decaying average for inter-sweep " \ + "duration") \ \ product(uintx, CMS_SweepPadding, 2, \ - "The multiple of deviation from mean to use for buffering" \ + "The multiple of deviation from mean to use for buffering " \ "against volatility in inter-sweep duration.") \ \ product(uintx, CMS_SweepTimerThresholdMillis, 10, \ "Skip block flux-rate sampling for an epoch unless inter-sweep " \ - " duration exceeds this threhold in milliseconds") \ + "duration exceeds this threhold in milliseconds") \ \ develop(bool, CMSTraceIncrementalMode, false, \ "Trace CMS incremental mode") \ @@ -1617,35 +1627,36 @@ class CommandLineFlags { \ product(intx, CMSTriggerRatio, 80, \ "Percentage of MinHeapFreeRatio in CMS generation that is " \ - " allocated before a CMS collection cycle commences") \ + "allocated before a CMS collection cycle commences") \ \ product(intx, CMSTriggerPermRatio, 80, \ - "Percentage of MinHeapFreeRatio in the CMS perm generation that" \ - " is allocated before a CMS collection cycle commences, that " \ - " also collects the perm generation") \ + "Percentage of MinHeapFreeRatio in the CMS perm generation that " \ + "is allocated before a CMS collection cycle commences, that " \ + "also collects the perm generation") \ \ product(uintx, CMSBootstrapOccupancy, 50, \ "Percentage CMS generation occupancy at which to " \ - " initiate CMS collection for bootstrapping collection stats") \ + "initiate CMS collection for bootstrapping collection stats") \ \ product(intx, CMSInitiatingOccupancyFraction, -1, \ "Percentage CMS generation occupancy to start a CMS collection " \ - " cycle (A negative value means that CMSTriggerRatio is used)") \ + "cycle. A negative value means that CMSTriggerRatio is used") \ \ product(intx, CMSInitiatingPermOccupancyFraction, -1, \ - "Percentage CMS perm generation occupancy to start a CMScollection"\ - " cycle (A negative value means that CMSTriggerPermRatio is used)")\ + "Percentage CMS perm generation occupancy to start a " \ + "CMScollection cycle. A negative value means that " \ + "CMSTriggerPermRatio is used") \ \ product(bool, UseCMSInitiatingOccupancyOnly, false, \ "Only use occupancy as a crierion for starting a CMS collection") \ \ product(intx, CMSIsTooFullPercentage, 98, \ - "An absolute ceiling above which CMS will always consider the" \ - " perm gen ripe for collection") \ + "An absolute ceiling above which CMS will always consider the " \ + "perm gen ripe for collection") \ \ develop(bool, CMSTestInFreeList, false, \ "Check if the coalesced range is already in the " \ - "free lists as claimed.") \ + "free lists as claimed") \ \ notproduct(bool, CMSVerifyReturnedBytes, false, \ "Check that all the garbage collected was returned to the " \ @@ -1663,8 +1674,8 @@ class CommandLineFlags { "Enforce ScavengeALot/GCALot at all potential safepoints") \ \ product(bool, HandlePromotionFailure, true, \ - "The youngest generation collection does not require" \ - " a guarantee of full promotion of all live objects.") \ + "The youngest generation collection does not require " \ + "a guarantee of full promotion of all live objects.") \ \ notproduct(bool, PromotionFailureALot, false, \ "Use promotion failure handling on every youngest generation " \ @@ -1692,7 +1703,7 @@ class CommandLineFlags { "Ratio of hard spins to calls to yield") \ \ product(uintx, PreserveMarkStackSize, 1024, \ - "Size for stack used in promotion failure handling") \ + "Size for stack used in promotion failure handling") \ \ product_pd(bool, UseTLAB, "Use thread-local object allocation") \ \ @@ -1720,14 +1731,27 @@ class CommandLineFlags { product(bool, AlwaysActAsServerClassMachine, false, \ "Always act like a server-class machine") \ \ - product_pd(uintx, DefaultMaxRAM, \ - "Maximum real memory size for setting server class heap size") \ + product_pd(uint64_t, MaxRAM, \ + "Real memory size (in bytes) used to set maximum heap size") \ + \ + product(uintx, ErgoHeapSizeLimit, 0, \ + "Maximum ergonomically set heap size (in bytes); zero means use " \ + "MaxRAM / MaxRAMFraction") \ + \ + product(uintx, MaxRAMFraction, 4, \ + "Maximum fraction (1/n) of real memory used for maximum heap " \ + "size") \ \ product(uintx, DefaultMaxRAMFraction, 4, \ - "Fraction (1/n) of real memory used for server class max heap") \ + "Maximum fraction (1/n) of real memory used for maximum heap " \ + "size; deprecated: to be renamed to MaxRAMFraction") \ \ - product(uintx, DefaultInitialRAMFraction, 64, \ - "Fraction (1/n) of real memory used for server class initial heap") \ + product(uintx, MinRAMFraction, 2, \ + "Minimum fraction (1/n) of real memory used for maxmimum heap " \ + "size on systems with small physical memory size") \ + \ + product(uintx, InitialRAMFraction, 64, \ + "Fraction (1/n) of real memory used for initial heap size") \ \ product(bool, UseAutoGCSelectPolicy, false, \ "Use automatic collection selection policy") \ @@ -1778,7 +1802,7 @@ class CommandLineFlags { "Number of collections before the adaptive sizing is started") \ \ product(uintx, AdaptiveSizePolicyOutputInterval, 0, \ - "Collecton interval for printing information, zero => never") \ + "Collecton interval for printing information; zero => never") \ \ product(bool, UseAdaptiveSizePolicyFootprintGoal, true, \ "Use adaptive minimum footprint as a goal") \ @@ -1808,7 +1832,8 @@ class CommandLineFlags { "Allowed collection cost difference between generations") \ \ product(uintx, AdaptiveSizePolicyCollectionCostMargin, 50, \ - "If collection costs are within margin, reduce both by full delta") \ + "If collection costs are within margin, reduce both by full " \ + "delta") \ \ product(uintx, YoungGenerationSizeIncrement, 20, \ "Adaptive size percentage change in young generation") \ @@ -2527,7 +2552,7 @@ class CommandLineFlags { \ develop(bool, VerifyCompiledCode, false, \ "Include miscellaneous runtime verifications in nmethod code; " \ - "off by default because it disturbs nmethod size heuristics.") \ + "default off because it disturbs nmethod size heuristics") \ \ \ /* compilation */ \ @@ -2789,20 +2814,28 @@ class CommandLineFlags { "an OS lock") \ \ /* gc parameters */ \ - product(uintx, MaxHeapSize, ScaleForWordSize(64*M), \ - "Default maximum size for object heap (in bytes)") \ + product(uintx, InitialHeapSize, 0, \ + "Initial heap size (in bytes); zero means OldSize + NewSize") \ \ - product_pd(uintx, NewSize, \ - "Default size of new generation (in bytes)") \ + product(uintx, MaxHeapSize, ScaleForWordSize(96*M), \ + "Maximum heap size (in bytes)") \ + \ + product(uintx, OldSize, ScaleForWordSize(4*M), \ + "Initial tenured generation size (in bytes)") \ + \ + product(uintx, NewSize, ScaleForWordSize(4*M), \ + "Initial new generation size (in bytes)") \ \ product(uintx, MaxNewSize, max_uintx, \ - "Maximum size of new generation (in bytes)") \ + "Maximum new generation size (in bytes), max_uintx means set " \ + "ergonomically") \ \ product(uintx, PretenureSizeThreshold, 0, \ - "Max size in bytes of objects allocated in DefNew generation") \ + "Maximum size in bytes of objects allocated in DefNew " \ + "generation; zero means no maximum") \ \ - product_pd(uintx, TLABSize, \ - "Default (or starting) size of TLAB (in bytes)") \ + product(uintx, TLABSize, 0, \ + "Starting TLAB size (in bytes); zero means set ergonomically") \ \ product(uintx, MinTLABSize, 2*K, \ "Minimum allowed TLAB size (in bytes)") \ @@ -2819,10 +2852,10 @@ class CommandLineFlags { product(uintx, TLABWasteIncrement, 4, \ "Increment allowed waste at slow allocation") \ \ - product_pd(intx, SurvivorRatio, \ + product(intx, SurvivorRatio, 8, \ "Ratio of eden/survivor space size") \ \ - product_pd(intx, NewRatio, \ + product(intx, NewRatio, 2, \ "Ratio of new/old generation sizes") \ \ product(uintx, MaxLiveObjectEvacuationRatio, 100, \ @@ -2832,11 +2865,8 @@ class CommandLineFlags { "Additional size added to desired new generation size per " \ "non-daemon thread (in bytes)") \ \ - product(uintx, OldSize, ScaleForWordSize(4096*K), \ - "Default size of tenured generation (in bytes)") \ - \ product_pd(uintx, PermSize, \ - "Default size of permanent generation (in bytes)") \ + "Initial size of permanent generation (in bytes)") \ \ product_pd(uintx, MaxPermSize, \ "Maximum size of permanent generation (in bytes)") \ diff --git a/hotspot/src/share/vm/runtime/globals_extension.hpp b/hotspot/src/share/vm/runtime/globals_extension.hpp index b129555ef83..35844b7d2a4 100644 --- a/hotspot/src/share/vm/runtime/globals_extension.hpp +++ b/hotspot/src/share/vm/runtime/globals_extension.hpp @@ -202,6 +202,7 @@ class CommandLineFlagsEx : CommandLineFlags { static void boolAtPut(CommandLineFlagWithType flag, bool value, FlagValueOrigin origin); static void intxAtPut(CommandLineFlagWithType flag, intx value, FlagValueOrigin origin); static void uintxAtPut(CommandLineFlagWithType flag, uintx value, FlagValueOrigin origin); + static void uint64_tAtPut(CommandLineFlagWithType flag, uint64_t value, FlagValueOrigin origin); static void doubleAtPut(CommandLineFlagWithType flag, double value, FlagValueOrigin origin); static void ccstrAtPut(CommandLineFlagWithType flag, ccstr value, FlagValueOrigin origin); diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp index 669afbaeac8..ee681b17146 100644 --- a/hotspot/src/share/vm/services/management.cpp +++ b/hotspot/src/share/vm/services/management.cpp @@ -790,7 +790,7 @@ JVM_ENTRY(jobject, jmm_GetMemoryUsage(JNIEnv* env, jboolean heap)) assert(!has_undefined_init_size, "Undefined init size"); assert(!has_undefined_max_size, "Undefined max size"); - MemoryUsage usage((heap ? Arguments::initial_heap_size() : total_init), + MemoryUsage usage((heap ? InitialHeapSize : total_init), total_used, total_committed, (heap ? Universe::heap()->max_capacity() : total_max)); From bdd0f44defb2072cac06b6d9bae4f67e2a3ad612 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Thu, 29 Oct 2009 16:57:55 -0700 Subject: [PATCH 03/61] 6896084: VM does not reserve protected page below heap for compressed oops implicit null checks Set narrow_oop_base and narrow_oop_use_implicit_null_checks in Universe::preferred_heap_base(). Reviewed-by: twisti, jcoomes --- hotspot/src/share/vm/memory/universe.cpp | 36 +++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index 5043f9e2599..5a80ac14477 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -744,22 +744,22 @@ static const uint64_t NarrowOopHeapMax = (uint64_t(max_juint) + 1); 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; // Return specified base for the first request. if (!FLAG_IS_DEFAULT(HeapBaseMinAddress) && (mode == UnscaledNarrowOop)) { - return (char*)HeapBaseMinAddress; - } - const size_t total_size = heap_size + HeapBaseMinAddress; - if (total_size <= OopEncodingHeapMax && (mode != HeapBasedNarrowOop)) { + 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); @@ -768,16 +768,38 @@ char* Universe::preferred_heap_base(size_t heap_size, NARROW_OOP_MODE mode) { // 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() { From e3b5580eccfed3851f3414479a39a11376976c59 Mon Sep 17 00:00:00 2001 From: Changpeng Fang Date: Fri, 30 Oct 2009 10:12:52 -0700 Subject: [PATCH 04/61] 6852078: HSX 14/16 in jdk 5.0: api/javax_management api/org_omg jck tests crashes or make tnameserv crash Disable SuperWord optimization for unsafe read/write Reviewed-by: kvn, phh --- hotspot/src/share/vm/opto/superword.cpp | 5 ++ .../test/compiler/6852078/Test6852078.java | 57 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 hotspot/test/compiler/6852078/Test6852078.java diff --git a/hotspot/src/share/vm/opto/superword.cpp b/hotspot/src/share/vm/opto/superword.cpp index bc62f30c3d3..83d359d31f1 100644 --- a/hotspot/src/share/vm/opto/superword.cpp +++ b/hotspot/src/share/vm/opto/superword.cpp @@ -1921,6 +1921,11 @@ SWPointer::SWPointer(MemNode* mem, SuperWord* slp) : } // 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 --git a/hotspot/test/compiler/6852078/Test6852078.java b/hotspot/test/compiler/6852078/Test6852078.java new file mode 100644 index 00000000000..7028ee3b5fa --- /dev/null +++ b/hotspot/test/compiler/6852078/Test6852078.java @@ -0,0 +1,57 @@ +/* + * 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. + * + */ + +/** + * @test + * @bug 6852078 + * @summary Disable SuperWord optimization for unsafe read/write + * + * @run main/othervm Test6852078 + */ + +import java.util.*; +import java.nio.ByteBuffer; +import com.sun.corba.se.impl.encoding.ByteBufferWithInfo; +import com.sun.jndi.toolkit.corba.CorbaUtils; + +public class Test6852078 { + + public Test6852078(String [] args) { + + int capacity = 128; + ByteBuffer bb = ByteBuffer.allocateDirect(capacity); + ByteBufferWithInfo bbwi = new ByteBufferWithInfo( CorbaUtils.getOrb(null, -1, new Hashtable()), bb); + byte[] tmpBuf; + tmpBuf = new byte[bbwi.buflen]; + + for (int i = 0; i < capacity; i++) + tmpBuf[i] = bbwi.byteBuffer.get(i); + } + + public static void main(String [] args) { + for (int i=0; i<2000; i++) { + Test6852078 t = new Test6852078(args); + } + } +} From 903247cf85ef129c17edb08c5b4729f79067c590 Mon Sep 17 00:00:00 2001 From: John R Rose Date: Fri, 30 Oct 2009 16:22:59 -0700 Subject: [PATCH 05/61] 6858164: invokedynamic code needs some cleanup (post-6655638) Fix several crashers, remove needless paths for boxed-style bootstrap method call, refactor & simplify APIs for rewriter constantPoolOop, remove sun.dyn.CallSiteImpl Reviewed-by: kvn --- .../sparc/vm/templateInterpreter_sparc.cpp | 3 +- .../cpu/x86/vm/templateInterpreter_x86_32.cpp | 83 +------------------ .../cpu/x86/vm/templateInterpreter_x86_64.cpp | 3 +- .../src/cpu/x86/vm/templateTable_x86_32.cpp | 56 +------------ hotspot/src/share/vm/ci/ciEnv.cpp | 4 +- .../src/share/vm/classfile/javaClasses.cpp | 24 +++--- .../src/share/vm/classfile/javaClasses.hpp | 4 +- .../share/vm/classfile/systemDictionary.cpp | 43 ++++++++-- .../share/vm/classfile/systemDictionary.hpp | 1 - hotspot/src/share/vm/classfile/verifier.cpp | 13 +-- hotspot/src/share/vm/classfile/vmSymbols.hpp | 3 +- hotspot/src/share/vm/includeDB_core | 1 + .../share/vm/interpreter/bytecodeTracer.cpp | 7 +- .../src/share/vm/interpreter/interpreter.cpp | 14 ++++ .../vm/interpreter/interpreterRuntime.cpp | 69 ++------------- .../vm/interpreter/interpreterRuntime.hpp | 1 - .../src/share/vm/interpreter/linkResolver.cpp | 5 +- hotspot/src/share/vm/interpreter/rewriter.cpp | 14 +--- hotspot/src/share/vm/interpreter/rewriter.hpp | 11 ++- .../vm/interpreter/templateInterpreter.cpp | 32 ------- .../vm/interpreter/templateInterpreter.hpp | 4 - .../templateInterpreterGenerator.hpp | 5 +- hotspot/src/share/vm/oops/constantPoolOop.cpp | 39 +++++++-- hotspot/src/share/vm/oops/constantPoolOop.hpp | 27 ++---- hotspot/src/share/vm/oops/cpCacheOop.cpp | 33 +++++--- hotspot/src/share/vm/oops/cpCacheOop.hpp | 47 ++++++++--- hotspot/src/share/vm/oops/generateOopMap.cpp | 22 +++-- .../src/share/vm/oops/instanceKlassKlass.cpp | 10 +++ hotspot/src/share/vm/prims/jvm.cpp | 8 +- hotspot/src/share/vm/prims/methodHandles.cpp | 8 +- 30 files changed, 222 insertions(+), 372 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp index b83ed82cf13..ada795d7ea1 100644 --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp @@ -150,8 +150,7 @@ address TemplateInterpreterGenerator::generate_StackOverflowError_handler() { } -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 --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp index b78f0e2a066..620c0b2def0 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp @@ -155,15 +155,8 @@ address TemplateInterpreterGenerator::generate_continuation_for(TosState state) } -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(); @@ -216,46 +209,6 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, __ 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); @@ -263,32 +216,6 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, } __ 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())); @@ -301,14 +228,6 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, __ 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; diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp index 4f0c3c9f779..cd9d08915ca 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp @@ -166,8 +166,7 @@ address TemplateInterpreterGenerator::generate_continuation_for(TosState state) 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 diff --git a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp index 50ae3190953..ff78f933d56 100644 --- a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp @@ -2890,9 +2890,6 @@ void TemplateTable::count_calls(Register method, Register temp) { 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 @@ void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) 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 @@ void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) // 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(); @@ -3154,53 +3147,10 @@ void TemplateTable::invokedynamic(int byte_no) { } 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 --git a/hotspot/src/share/vm/ci/ciEnv.cpp b/hotspot/src/share/vm/ci/ciEnv.cpp index b0a17b35c2e..0a6de348d43 100644 --- a/hotspot/src/share/vm/ci/ciEnv.cpp +++ b/hotspot/src/share/vm/ci/ciEnv.cpp @@ -690,10 +690,8 @@ ciMethod* ciEnv::get_method_by_index_impl(ciInstanceKlass* 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(); diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index cc0db7a173b..cb71bbdd73d 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -2430,15 +2430,15 @@ oop java_dyn_MethodTypeForm::erasedType(oop mtform) { } -// Support for sun_dyn_CallSiteImpl +// Support for java_dyn_CallSite -int sun_dyn_CallSiteImpl::_type_offset; -int sun_dyn_CallSiteImpl::_target_offset; -int sun_dyn_CallSiteImpl::_vmmethod_offset; +int java_dyn_CallSite::_type_offset; +int java_dyn_CallSite::_target_offset; +int java_dyn_CallSite::_vmmethod_offset; -void sun_dyn_CallSiteImpl::compute_offsets() { +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); @@ -2446,23 +2446,23 @@ void sun_dyn_CallSiteImpl::compute_offsets() { } } -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); } @@ -2811,7 +2811,7 @@ void JavaClasses::compute_offsets() { 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 --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index 048fba8d4b0..b486670fc04 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -1061,9 +1061,9 @@ class java_dyn_MethodTypeForm: AllStatic { }; -// 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: diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index 5598c9fcfb3..05d0c85e85f 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -1973,7 +1973,7 @@ void SystemDictionary::initialize_preloaded_classes(TRAPS) { WKID indy_group_end = WK_KLASS_ENUM_NAME(Dynamic_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. @@ -2404,7 +2404,7 @@ Handle SystemDictionary::make_dynamic_call_site(KlassHandle caller, 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); @@ -2413,17 +2413,19 @@ Handle SystemDictionary::make_dynamic_call_site(KlassHandle caller, 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(); assert(call_site_oop->is_oop() - /*&& sun_dyn_CallSiteImpl::is_instance(call_site_oop)*/, "must be sane"); - sun_dyn_CallSiteImpl::set_vmmethod(call_site_oop, mh_invdyn()); + /*&& 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; } @@ -2436,9 +2438,17 @@ Handle SystemDictionary::find_bootstrap_method(KlassHandle caller, 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())); @@ -2452,9 +2462,18 @@ Handle SystemDictionary::find_bootstrap_method(KlassHandle caller, 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: @@ -2463,6 +2482,14 @@ Handle SystemDictionary::find_bootstrap_method(KlassHandle caller, 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(); } diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp index b7c82033628..18367ded29e 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -144,7 +144,6 @@ class SymbolPropertyTable; 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 */ \ \ diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp index dd947d19cfb..c944b36f437 100644 --- a/hotspot/src/share/vm/classfile/verifier.cpp +++ b/hotspot/src/share/vm/classfile/verifier.cpp @@ -1903,17 +1903,8 @@ void ClassVerifier::verify_invoke_instructions( 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 --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index 04bb9369205..bb9f3076a96 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -233,10 +233,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;") \ diff --git a/hotspot/src/share/vm/includeDB_core b/hotspot/src/share/vm/includeDB_core index e96a5e9be1a..1a5f3ee3073 100644 --- a/hotspot/src/share/vm/includeDB_core +++ b/hotspot/src/share/vm/includeDB_core @@ -1291,6 +1291,7 @@ cpCacheOop.cpp jvmtiRedefineClassesTrace.hpp 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 diff --git a/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp b/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp index 79cee762daa..a3d21b9b3bc 100644 --- a/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp +++ b/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp @@ -282,18 +282,21 @@ void BytecodePrinter::print_field_or_method(int i, outputStream* st) { 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 --git a/hotspot/src/share/vm/interpreter/interpreter.cpp b/hotspot/src/share/vm/interpreter/interpreter.cpp index b4f1007bc6c..f068e0d98b7 100644 --- a/hotspot/src/share/vm/interpreter/interpreter.cpp +++ b/hotspot/src/share/vm/interpreter/interpreter.cpp @@ -314,6 +314,20 @@ address AbstractInterpreter::deopt_continue_after_entry(methodOop method, addres 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()->entry_at(index)->set_parameter_size(callee_parameters); + } + break; + } + case Bytecodes::_ldc : type = constant_pool_type( method, *(bcp+1) ); break; diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp index c8e19944de7..6d5623f9c04 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -681,7 +681,7 @@ IRT_ENTRY(void, InterpreterRuntime::resolve_invoke(JavaThread* thread, Bytecodes 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 @@ IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) { 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 @@ IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) { 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 @@ IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) { // 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 diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp index 76b9e9c2191..b0a616308e4 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp @@ -91,7 +91,6 @@ class InterpreterRuntime: AllStatic { // 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 --git a/hotspot/src/share/vm/interpreter/linkResolver.cpp b/hotspot/src/share/vm/interpreter/linkResolver.cpp index c9a2c13c5b1..60b391a77a1 100644 --- a/hotspot/src/share/vm/interpreter/linkResolver.cpp +++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp @@ -1015,11 +1015,8 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle po // 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 --git a/hotspot/src/share/vm/interpreter/rewriter.cpp b/hotspot/src/share/vm/interpreter/rewriter.cpp index c70c0c0356f..bfca8559fbe 100644 --- a/hotspot/src/share/vm/interpreter/rewriter.cpp +++ b/hotspot/src/share/vm/interpreter/rewriter.cpp @@ -48,16 +48,6 @@ void Rewriter::compute_index_maps() { } -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 @@ void Rewriter::rewrite_invokedynamic(address bcp, int offset, int delete_me) { 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 @@ void Rewriter::rewrite_invokedynamic(address bcp, int offset, int delete_me) { // 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. } diff --git a/hotspot/src/share/vm/interpreter/rewriter.hpp b/hotspot/src/share/vm/interpreter/rewriter.hpp index 2546b57ef37..4bfe86dd7b3 100644 --- a/hotspot/src/share/vm/interpreter/rewriter.hpp +++ b/hotspot/src/share/vm/interpreter/rewriter.hpp @@ -43,13 +43,18 @@ class Rewriter: public StackObj { 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); @@ -65,4 +70,8 @@ class Rewriter: public StackObj { public: // Driver routine: static void rewrite(instanceKlassHandle klass, TRAPS); + + enum { + _secondary_entry_tag = nth_bit(30) + }; }; diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp index 9f12f44e3a5..8e48dce07b2 100644 --- a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp @@ -178,14 +178,12 @@ EntryPoint TemplateInterpreter::_trace_code; #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 @@ void TemplateInterpreterGenerator::generate_all() { } } - 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 @@ void TemplateInterpreterGenerator::generate_all() { 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"); @@ -547,18 +527,6 @@ address TemplateInterpreter::return_entry(TosState state, int length) { } -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 --git a/hotspot/src/share/vm/interpreter/templateInterpreter.hpp b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp index 7de665b882f..b3eed1c3a4e 100644 --- a/hotspot/src/share/vm/interpreter/templateInterpreter.hpp +++ b/hotspot/src/share/vm/interpreter/templateInterpreter.hpp @@ -110,14 +110,12 @@ class TemplateInterpreter: public AbstractInterpreter { #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 @@ class TemplateInterpreter: public AbstractInterpreter { // 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 --git a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp index 9d5b694049e..676e762725d 100644 --- a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp +++ b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp @@ -51,10 +51,7 @@ class TemplateInterpreterGenerator: public AbstractInterpreterGenerator { 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 --git a/hotspot/src/share/vm/oops/constantPoolOop.cpp b/hotspot/src/share/vm/oops/constantPoolOop.cpp index af72333b0b0..6481f01fed2 100644 --- a/hotspot/src/share/vm/oops/constantPoolOop.cpp +++ b/hotspot/src/share/vm/oops/constantPoolOop.cpp @@ -262,25 +262,48 @@ symbolOop constantPoolOopDesc::impl_signature_ref_at(int which, bool uncached) { 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 --git a/hotspot/src/share/vm/oops/constantPoolOop.hpp b/hotspot/src/share/vm/oops/constantPoolOop.hpp index 72bec650014..ce9fb9a64ad 100644 --- a/hotspot/src/share/vm/oops/constantPoolOop.hpp +++ b/hotspot/src/share/vm/oops/constantPoolOop.hpp @@ -342,12 +342,14 @@ class constantPoolOopDesc : public oopDesc { } // 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 +363,6 @@ class constantPoolOopDesc : public oopDesc { // 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 +425,7 @@ class constantPoolOopDesc : public oopDesc { 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 --git a/hotspot/src/share/vm/oops/cpCacheOop.cpp b/hotspot/src/share/vm/oops/cpCacheOop.cpp index 6f549afbe38..36380c88903 100644 --- a/hotspot/src/share/vm/oops/cpCacheOop.cpp +++ b/hotspot/src/share/vm/oops/cpCacheOop.cpp @@ -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_interface_call(methodHandle method, int index) 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 ConstantPoolCacheEntry::verify(outputStream* st) const { 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 --git a/hotspot/src/share/vm/oops/cpCacheOop.hpp b/hotspot/src/share/vm/oops/cpCacheOop.hpp index ec25b87352b..6fb7a635e56 100644 --- a/hotspot/src/share/vm/oops/cpCacheOop.hpp +++ b/hotspot/src/share/vm/oops/cpCacheOop.hpp @@ -154,7 +154,8 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { }; // 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 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { // 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 @@ class constantPoolCacheOopDesc: public oopDesc { ConstantPoolCacheEntry* base() const { return (ConstantPoolCacheEntry*)((address)this + in_bytes(base_offset())); } friend class constantPoolCacheKlass; + friend class ConstantPoolCacheEntry; public: // Initialization @@ -329,7 +332,8 @@ class constantPoolCacheOopDesc: public oopDesc { // 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 @@ class constantPoolCacheOopDesc: public oopDesc { // 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 @@ class constantPoolCacheOopDesc: public oopDesc { // 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 --git a/hotspot/src/share/vm/oops/generateOopMap.cpp b/hotspot/src/share/vm/oops/generateOopMap.cpp index eb533a81e6f..9d5ad3b7baf 100644 --- a/hotspot/src/share/vm/oops/generateOopMap.cpp +++ b/hotspot/src/share/vm/oops/generateOopMap.cpp @@ -1556,13 +1556,13 @@ void GenerateOopMap::interp1(BytecodeStream *itr) { case Bytecodes::_getfield: do_field(true, false, itr->get_index_big(), itr->bci()); break; case Bytecodes::_putfield: do_field(false, false, itr->get_index_big(), itr->bci()); break; - 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::_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; + 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(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; case Bytecodes::_checkcast: do_checkcast(); break; case Bytecodes::_arraylength: case Bytecodes::_instanceof: pp(rCTS, vCTS); break; @@ -1900,11 +1900,9 @@ void GenerateOopMap::do_field(int is_get, int is_static, int idx, int bci) { } 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 --git a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp index 18a6d7addf8..a56751ab387 100644 --- a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp @@ -317,6 +317,11 @@ void instanceKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { 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 @@ void instanceKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { 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); } diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index 36c6507ee72..14407030c12 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -2257,10 +2257,8 @@ JVM_ENTRY(const char*, JVM_GetCPMethodNameUTF(JNIEnv *env, jclass cls, jint cp_i 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"); } @@ -2277,10 +2275,8 @@ JVM_ENTRY(const char*, JVM_GetCPMethodSignatureUTF(JNIEnv *env, jclass cls, jint 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"); } diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp index b5910f21d37..453ac3c0448 100644 --- a/hotspot/src/share/vm/prims/methodHandles.cpp +++ b/hotspot/src/share/vm/prims/methodHandles.cpp @@ -2347,9 +2347,9 @@ JVM_END JVM_ENTRY(void, MH_linkCallSite(JNIEnv *env, jobject igcls, jobject site_jh, jobject target_jh)) { // No special action required, yet. oop site_oop = JNIHandles::resolve(site_jh); - if (site_oop == NULL || site_oop->klass() != SystemDictionary::CallSiteImpl_klass()) + if (site_oop == NULL || site_oop->klass() != SystemDictionary::CallSite_klass()) THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "call site"); - sun_dyn_CallSiteImpl::set_target(site_oop, JNIHandles::resolve(target_jh)); + java_dyn_CallSite::set_target(site_oop, JNIHandles::resolve(target_jh)); } JVM_END @@ -2365,6 +2365,7 @@ JVM_END #define OBJ LANG"Object;" #define CLS LANG"Class;" #define STRG LANG"String;" +#define CST JDYN"CallSite;" #define MT JDYN"MethodType;" #define MH JDYN"MethodHandle;" #define MHI IDYN"MethodHandleImpl;" @@ -2372,7 +2373,6 @@ JVM_END #define AMH IDYN"AdapterMethodHandle;" #define BMH IDYN"BoundMethodHandle;" #define DMH IDYN"DirectMethodHandle;" -#define CSTI IDYN"CallSiteImpl;" #define CC (char*) /*cast a literal from (const char*)*/ #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) @@ -2398,7 +2398,7 @@ static JNINativeMethod methods[] = { // More entry points specifically for EnableInvokeDynamic. static JNINativeMethod methods2[] = { - {CC"linkCallSite", CC"("CSTI MH")V", FN_PTR(MH_linkCallSite)} + {CC"linkCallSite", CC"("CST MH")V", FN_PTR(MH_linkCallSite)} }; From 7eea7dcfe4b525900a7fd0def50983ae3c842909 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Mon, 2 Nov 2009 11:17:55 +0100 Subject: [PATCH 06/61] 6769124: various 64-bit fixes for c1 Reviewed-by: never --- .../cpu/sparc/vm/c1_LIRAssembler_sparc.cpp | 23 ++++--- .../cpu/sparc/vm/c1_LIRGenerator_sparc.cpp | 12 ++-- hotspot/src/cpu/x86/vm/assembler_x86.cpp | 25 ++++++- hotspot/src/cpu/x86/vm/assembler_x86.hpp | 4 ++ .../src/cpu/x86/vm/c1_LIRAssembler_x86.cpp | 27 ++++++-- .../src/cpu/x86/vm/c1_LIRGenerator_x86.cpp | 15 +++- hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 2 + hotspot/src/share/vm/c1/c1_GraphBuilder.cpp | 2 +- hotspot/src/share/vm/c1/c1_LIRGenerator.cpp | 35 +++++++++- hotspot/src/share/vm/c1/c1_LinearScan.cpp | 11 ++- hotspot/src/share/vm/runtime/arguments.cpp | 6 ++ .../6769124/TestArrayCopy6769124.java | 53 ++++++++++++++ .../compiler/6769124/TestDeoptInt6769124.java | 56 +++++++++++++++ .../6769124/TestUnalignedLoad6769124.java | 69 +++++++++++++++++++ 14 files changed, 309 insertions(+), 31 deletions(-) create mode 100644 hotspot/test/compiler/6769124/TestArrayCopy6769124.java create mode 100644 hotspot/test/compiler/6769124/TestDeoptInt6769124.java create mode 100644 hotspot/test/compiler/6769124/TestUnalignedLoad6769124.java diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp index 2583f7cee23..2b054b43e65 100644 --- a/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp @@ -189,14 +189,17 @@ void LIR_Assembler::osr_entry() { 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 @@ void LIR_Assembler::osr_entry() { } #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)); } } @@ -953,9 +956,11 @@ int LIR_Assembler::load(Register base, int offset, LIR_Opr to_reg, BasicType typ } 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 +981,8 @@ int LIR_Assembler::load(Register base, int offset, LIR_Opr to_reg, BasicType typ 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()); } @@ -2200,6 +2205,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { 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 +2214,7 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { } __ 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 { diff --git a/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp b/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp index 2a69ade2156..87b47124352 100644 --- a/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/c1_LIRGenerator_sparc.cpp @@ -144,17 +144,17 @@ LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index, 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 @@ LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index, } } 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; } diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp index 07509493038..5db2ae35955 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp @@ -2251,6 +2251,7 @@ void Assembler::popf() { 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 @@ void Assembler::popl(Address dst) { emit_byte(0x8F); emit_operand(rax, dst); } +#endif void Assembler::prefetch_prefix(Address src) { prefix(src); @@ -2428,6 +2430,7 @@ void Assembler::pushf() { 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 @@ void Assembler::pushl(Address src) { 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::align(int modulus) { } 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::cmpxchgptr(Register reg, Address adr) { } 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)); + } } diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp index 2d61a3cf0aa..fdec30180b1 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp @@ -1244,7 +1244,9 @@ private: 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 @@ private: // 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); diff --git a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp index f8cdb23ee82..2fae5406861 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp @@ -301,22 +301,25 @@ void LIR_Assembler::osr_entry() { 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); } } @@ -785,7 +788,13 @@ void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmi 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 +1127,14 @@ void LIR_Assembler::stack2stack(LIR_Opr src, LIR_Opr dest, BasicType type) { __ 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()) { @@ -3136,8 +3151,10 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) { #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); diff --git a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp index 2e2c1364717..f98bfaa8ea3 100644 --- a/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_LIRGenerator_x86.cpp @@ -755,8 +755,19 @@ void LIRGenerator::do_CompareAndSwap(Intrinsic* x, ValueType* type) { } 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. diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index ae8efb86939..5965fd3ead8 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -255,6 +255,8 @@ void VM_Version::get_processor_features() { 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 --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp index caa99ded618..f567d6e120d 100644 --- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp @@ -365,7 +365,7 @@ void BlockListBuilder::make_loop_header(BlockBegin* block) { 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"); } } diff --git a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp index 8eb667dda29..a393028792d 100644 --- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp @@ -1855,12 +1855,26 @@ void LIRGenerator::do_UnsafeGetRaw(UnsafeGetRaw* x) { 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 @@ void LIRGenerator::do_UnsafePutRaw(UnsafePutRaw* x) { 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); diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp index bab43e2ad21..ab049832121 100644 --- a/hotspot/src/share/vm/c1/c1_LinearScan.cpp +++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp @@ -2464,6 +2464,10 @@ int LinearScan::append_scope_value_for_constant(LIR_Opr opr, GrowableArrayappend(&_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 @@ int LinearScan::append_scope_value_for_constant(LIR_Opr opr, GrowableArrayappend(new ConstantIntValue(c->as_jint_lo_bits())); scope_values->append(new ConstantIntValue(c->as_jint_hi_bits())); } - +#endif return 2; } @@ -2503,17 +2507,18 @@ int LinearScan::append_scope_value_for_operand(LIR_Opr opr, GrowableArrayis_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 --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index ccd568f3a6d..cc3bc2dca98 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -1234,9 +1234,11 @@ void Arguments::set_ergonomics_flags() { // Check that UseCompressedOops can be set with the max heap size allocated // by ergonomics. if (MaxHeapSize <= max_heap_for_compressed_oops()) { +#ifndef COMPILER1 if (FLAG_IS_DEFAULT(UseCompressedOops) && !UseG1GC) { FLAG_SET_ERGO(bool, UseCompressedOops, true); } +#endif #ifdef _WIN64 if (UseLargePages && UseCompressedOops) { // Cannot allocate guard pages for implicit checks in indexed addressing @@ -2675,6 +2677,10 @@ jint Arguments::parse(const JavaVMInitArgs* args) { } } +#if defined(_LP64) && defined(COMPILER1) + UseCompressedOops = false; +#endif + #ifdef SERIALGC set_serial_gc_flags(); #endif // SERIALGC diff --git a/hotspot/test/compiler/6769124/TestArrayCopy6769124.java b/hotspot/test/compiler/6769124/TestArrayCopy6769124.java new file mode 100644 index 00000000000..aa9d628cb1f --- /dev/null +++ b/hotspot/test/compiler/6769124/TestArrayCopy6769124.java @@ -0,0 +1,53 @@ +/* + * 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. + * + */ + +/** + * @test + * @bug 6769124 + * @summary arraycopy may crash the VM with c1 on 64 bit + */ + +public class TestArrayCopy6769124 { + + public static void main(String[] args) { + + int k = 1 << 31; + + + for(int j = 0; j <1000000; j++) { + int i = -1; + while(i < 10) { + i++; + } + + int m = k * i; + + int[] O1 = new int[20]; + int[] O2 = new int[20]; + + System.arraycopy(O1, i, O2, i, 1); //will crash on amd64 + System.arraycopy(O1, m, O2, m, 1); //will crash on sparcv9 + } + } +} diff --git a/hotspot/test/compiler/6769124/TestDeoptInt6769124.java b/hotspot/test/compiler/6769124/TestDeoptInt6769124.java new file mode 100644 index 00000000000..42bea10c637 --- /dev/null +++ b/hotspot/test/compiler/6769124/TestDeoptInt6769124.java @@ -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. + * + */ + +/** + * @test + * @bug 6769124 + * @summary int value might not be correctly decoded on deopt with c1 on 64 bit + * + * @run main/othervm -Xcomp -XX:CompileOnly=TestDeoptInt6769124.m TestDeoptInt6769124 + */ + +public class TestDeoptInt6769124 { + + static class A { + volatile int vl; + A(int v) { + vl = v; + } + } + + static void m(int b) { + A a = new A(10); + int c; + c = b + a.vl; //accessing volatile field of class not loaded at compile time forces a deopt + if(c != 20) { + System.out.println("a (= " + a.vl + ") + b (= " + b + ") = c (= " + c + ") != 20"); + throw new InternalError(); + } + } + + public static void main(String[] args) { + m(10); + } + +} diff --git a/hotspot/test/compiler/6769124/TestUnalignedLoad6769124.java b/hotspot/test/compiler/6769124/TestUnalignedLoad6769124.java new file mode 100644 index 00000000000..3a73047e15d --- /dev/null +++ b/hotspot/test/compiler/6769124/TestUnalignedLoad6769124.java @@ -0,0 +1,69 @@ +/* + * 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. + * + */ + +/** + * @test + * @bug 6769124 + * @summary unaligned load may fail with c1 on 64 bit + */ + +public class TestUnalignedLoad6769124 { + + static long l1v = 0x200000003L; + static long l2v = 0x400000005L; + static double d1v = Double.MAX_VALUE; + static double d2v = Double.MIN_VALUE; + + public static void main(String[] args) { + long l1 = l1v; + double d1 = d1v; + long l2 = l2v; + double d2 = d2v; + + // Run long enough to induce an OSR + for (int i = 0; i < 10000000; i++) { + } + boolean error = false; + + if (l1 != l1v) { + System.out.println(l1 + " != " + l1v); + error = true; + } + if (l2 != l2v) { + System.out.println(l2 + " != " + l2v); + error = true; + } + if (d1 != d1v) { + System.out.println(d1 + " != " + d1v); + error = true; + } + if (d2 != d2v) { + System.out.println(d2 + " != " + d2v); + error = true; + } + if (error) { + throw new InternalError(); + } + } +} From e38fa6385f8c8bb62a5d3d2be1598254616e82fa Mon Sep 17 00:00:00 2001 From: Paul Hohensee Date: Wed, 4 Nov 2009 16:49:23 -0500 Subject: [PATCH 07/61] 6898160: Need serviceability support for new vm argument type 'uint64_t' Add serviceability support for uint64_t. Flags of unknown type assert in debug builds and are ignored in product builds. Reviewed-by: never, xlu, mchung, dcubed --- hotspot/src/share/vm/runtime/globals.cpp | 9 +++-- .../src/share/vm/services/attachListener.cpp | 35 +++++++++++++++---- hotspot/src/share/vm/services/management.cpp | 35 +++++++++++++------ 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp index b348f98e70a..71fec552482 100644 --- a/hotspot/src/share/vm/runtime/globals.cpp +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -69,9 +69,10 @@ bool Flag::is_external() const { void Flag::print_on(outputStream* st) { st->print("%5s %-35s %c= ", type, name, (origin != DEFAULT ? ':' : ' ')); - if (is_bool()) st->print("%-16s", get_bool() ? "true" : "false"); - if (is_intx()) st->print("%-16ld", get_intx()); - if (is_uintx()) st->print("%-16lu", get_uintx()); + if (is_bool()) st->print("%-16s", get_bool() ? "true" : "false"); + if (is_intx()) st->print("%-16ld", get_intx()); + if (is_uintx()) st->print("%-16lu", get_uintx()); + if (is_uint64_t()) st->print("%-16lu", get_uint64_t()); if (is_ccstr()) { const char* cp = get_ccstr(); if (cp != NULL) { @@ -100,6 +101,8 @@ void Flag::print_as_flag(outputStream* st) { st->print("-XX:%s=" INTX_FORMAT, name, get_intx()); } else if (is_uintx()) { st->print("-XX:%s=" UINTX_FORMAT, name, get_uintx()); + } else if (is_uint64_t()) { + st->print("-XX:%s=" UINT64_FORMAT, name, get_uint64_t()); } else if (is_ccstr()) { st->print("-XX:%s=", name); const char* cp = get_ccstr(); diff --git a/hotspot/src/share/vm/services/attachListener.cpp b/hotspot/src/share/vm/services/attachListener.cpp index 007de28da37..2de9c102212 100644 --- a/hotspot/src/share/vm/services/attachListener.cpp +++ b/hotspot/src/share/vm/services/attachListener.cpp @@ -207,7 +207,7 @@ static jint set_bool_flag(const char* name, AttachOperation* op, outputStream* o int tmp; int n = sscanf(arg1, "%d", &tmp); if (n != 1) { - out->print_cr("flag value has to be boolean (1 or 0)"); + out->print_cr("flag value must be a boolean (1 or 0)"); return JNI_ERR; } value = (tmp != 0); @@ -226,11 +226,11 @@ static jint set_intx_flag(const char* name, AttachOperation* op, outputStream* o if ((arg1 = op->arg(1)) != NULL) { int n = sscanf(arg1, INTX_FORMAT, &value); if (n != 1) { - out->print_cr("flag value has to be integer"); + out->print_cr("flag value must be an integer"); return JNI_ERR; } } - bool res = CommandLineFlags::intxAtPut((char*)name, &value, ATTACH_ON_DEMAND); + bool res = CommandLineFlags::intxAtPut((char*)name, &value, ATTACH_ON_DEMAND); if (! res) { out->print_cr("setting flag %s failed", name); } @@ -245,11 +245,30 @@ static jint set_uintx_flag(const char* name, AttachOperation* op, outputStream* if ((arg1 = op->arg(1)) != NULL) { int n = sscanf(arg1, UINTX_FORMAT, &value); if (n != 1) { - out->print_cr("flag value has to be integer"); + out->print_cr("flag value must be an unsigned integer"); return JNI_ERR; } } - bool res = CommandLineFlags::uintxAtPut((char*)name, &value, ATTACH_ON_DEMAND); + bool res = CommandLineFlags::uintxAtPut((char*)name, &value, ATTACH_ON_DEMAND); + if (! res) { + out->print_cr("setting flag %s failed", name); + } + + return res? JNI_OK : JNI_ERR; +} + +// set a uint64_t global flag using value from AttachOperation +static jint set_uint64_t_flag(const char* name, AttachOperation* op, outputStream* out) { + uint64_t value; + const char* arg1; + if ((arg1 = op->arg(1)) != NULL) { + int n = sscanf(arg1, UINT64_FORMAT, &value); + if (n != 1) { + out->print_cr("flag value must be an unsigned 64-bit integer"); + return JNI_ERR; + } + } + bool res = CommandLineFlags::uint64_tAtPut((char*)name, &value, ATTACH_ON_DEMAND); if (! res) { out->print_cr("setting flag %s failed", name); } @@ -261,10 +280,10 @@ static jint set_uintx_flag(const char* name, AttachOperation* op, outputStream* static jint set_ccstr_flag(const char* name, AttachOperation* op, outputStream* out) { const char* value; if ((value = op->arg(1)) == NULL) { - out->print_cr("flag value has to be a string"); + out->print_cr("flag value must be a string"); return JNI_ERR; } - bool res = CommandLineFlags::ccstrAtPut((char*)name, &value, ATTACH_ON_DEMAND); + bool res = CommandLineFlags::ccstrAtPut((char*)name, &value, ATTACH_ON_DEMAND); if (res) { FREE_C_HEAP_ARRAY(char, value); } else { @@ -291,6 +310,8 @@ static jint set_flag(AttachOperation* op, outputStream* out) { return set_intx_flag(name, op, out); } else if (f->is_uintx()) { return set_uintx_flag(name, op, out); + } else if (f->is_uint64_t()) { + return set_uint64_t_flag(name, op, out); } else if (f->is_ccstr()) { return set_ccstr_flag(name, op, out); } else { diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp index ee681b17146..d88af72c09b 100644 --- a/hotspot/src/share/vm/services/management.cpp +++ b/hotspot/src/share/vm/services/management.cpp @@ -1507,16 +1507,17 @@ JVM_ENTRY(jobjectArray, jmm_GetVMGlobalNames(JNIEnv *env)) return (jobjectArray)JNIHandles::make_local(env, flags_ah()); JVM_END -// utility function used by jmm_GetVMGlobals -void add_global_entry(JNIEnv* env, Handle name, jmmVMGlobal *global, Flag *flag, TRAPS) { +// Utility function used by jmm_GetVMGlobals. Returns false if flag type +// can't be determined, true otherwise. If false is returned, then *global +// will be incomplete and invalid. +bool add_global_entry(JNIEnv* env, Handle name, jmmVMGlobal *global, Flag *flag, TRAPS) { Handle flag_name; if (name() == NULL) { - flag_name = java_lang_String::create_from_str(flag->name, CHECK); + flag_name = java_lang_String::create_from_str(flag->name, CHECK_false); } else { flag_name = name; } global->name = (jstring)JNIHandles::make_local(env, flag_name()); - global->type = JMM_VMGLOBAL_TYPE_UNKNOWN; if (flag->is_bool()) { global->value.z = flag->get_bool() ? JNI_TRUE : JNI_FALSE; @@ -1527,10 +1528,17 @@ void add_global_entry(JNIEnv* env, Handle name, jmmVMGlobal *global, Flag *flag, } else if (flag->is_uintx()) { global->value.j = (jlong)flag->get_uintx(); global->type = JMM_VMGLOBAL_TYPE_JLONG; + } else if (flag->is_uint64_t()) { + global->value.j = (jlong)flag->get_uint64_t(); + global->type = JMM_VMGLOBAL_TYPE_JLONG; } else if (flag->is_ccstr()) { - Handle str = java_lang_String::create_from_str(flag->get_ccstr(), CHECK); + Handle str = java_lang_String::create_from_str(flag->get_ccstr(), CHECK_false); global->value.l = (jobject)JNIHandles::make_local(env, str()); global->type = JMM_VMGLOBAL_TYPE_JSTRING; + } else { + global->type = JMM_VMGLOBAL_TYPE_UNKNOWN; + assert(false, "Unsupported VMGlobal Type"); + return false; } global->writeable = flag->is_writeable(); @@ -1557,6 +1565,8 @@ void add_global_entry(JNIEnv* env, Handle name, jmmVMGlobal *global, Flag *flag, default: global->origin = JMM_VMGLOBAL_ORIGIN_OTHER; } + + return true; } // Fill globals array of count length with jmmVMGlobal entries @@ -1599,8 +1609,8 @@ JVM_ENTRY(jint, jmm_GetVMGlobals(JNIEnv *env, Handle sh(THREAD, s); char* str = java_lang_String::as_utf8_string(s); Flag* flag = Flag::find_flag(str, strlen(str)); - if (flag != NULL) { - add_global_entry(env, sh, &globals[i], flag, THREAD); + if (flag != NULL && + add_global_entry(env, sh, &globals[i], flag, THREAD)) { num_entries++; } else { globals[i].name = NULL; @@ -1617,8 +1627,8 @@ JVM_ENTRY(jint, jmm_GetVMGlobals(JNIEnv *env, for (int i = 0; i < nFlags && num_entries < count; i++) { Flag* flag = &Flag::flags[i]; // Exclude the locked (diagnostic, experimental) flags - if (flag->is_unlocked() || flag->is_unlocker()) { - add_global_entry(env, null_h, &globals[num_entries], flag, THREAD); + if ((flag->is_unlocked() || flag->is_unlocker()) && + add_global_entry(env, null_h, &globals[num_entries], flag, THREAD)) { num_entries++; } } @@ -1650,11 +1660,14 @@ JVM_ENTRY(void, jmm_SetVMGlobal(JNIEnv *env, jstring flag_name, jvalue new_value bool bvalue = (new_value.z == JNI_TRUE ? true : false); succeed = CommandLineFlags::boolAtPut(name, &bvalue, MANAGEMENT); } else if (flag->is_intx()) { - intx ivalue = new_value.j; + intx ivalue = (intx)new_value.j; succeed = CommandLineFlags::intxAtPut(name, &ivalue, MANAGEMENT); } else if (flag->is_uintx()) { - uintx uvalue = new_value.j; + uintx uvalue = (uintx)new_value.j; succeed = CommandLineFlags::uintxAtPut(name, &uvalue, MANAGEMENT); + } else if (flag->is_uint64_t()) { + uint64_t uvalue = (uint64_t)new_value.j; + succeed = CommandLineFlags::uint64_tAtPut(name, &uvalue, MANAGEMENT); } else if (flag->is_ccstr()) { oop str = JNIHandles::resolve_external_guard(new_value.l); if (str == NULL) { From 9c1321eed341cfb92e2ac87cd3502b7422fb7198 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Wed, 4 Nov 2009 14:16:20 -0800 Subject: [PATCH 08/61] 6896370: CTW fails share/vm/opto/matcher.cpp:1475 "duplicating node that's already been matched" Move DecodeN code outside the memory nodes only code. Reviewed-by: never --- hotspot/src/share/vm/opto/matcher.cpp | 82 +++++++-------------------- 1 file changed, 19 insertions(+), 63 deletions(-) diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp index 57d2c5e72ae..c753a996388 100644 --- a/hotspot/src/share/vm/opto/matcher.cpp +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -1832,67 +1832,23 @@ void Matcher::find_shared( Node *n ) { 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 +1869,15 @@ void Matcher::find_shared( Node *n ) { continue; // for(int i = ...) } - // Clone addressing expressions as they are "free" in most 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)); - } + 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 ) { // 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. From c5744bd138f7efd65c963ba587878df1f222dd96 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Wed, 4 Nov 2009 14:43:50 -0800 Subject: [PATCH 09/61] 6896352: CTW fails hotspot/src/share/vm/opto/escape.cpp:1155 Always call C->get_alias_index(phase->type(address)) during parsing. Reviewed-by: never --- hotspot/src/share/vm/opto/escape.cpp | 5 +++-- hotspot/src/share/vm/opto/memnode.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index b22f4814a57..e57dad63a5f 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -537,8 +537,9 @@ bool ConnectionGraph::split_AddP(Node *addp, Node *base, PhaseGVN *igvn) { } 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 diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index b80f18c2e7d..02c15af7232 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -255,6 +255,13 @@ Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) { 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()) From f473d94b9c81995777945eae947fff417d5f1dac Mon Sep 17 00:00:00 2001 From: John Cuthbertson Date: Fri, 6 Nov 2009 11:10:05 -0800 Subject: [PATCH 10/61] 6895788: G1: SATB and update buffer allocation code allocates too much space The type in the NEW_C_HEAP_ARRRY and FREE_C_HEAP_ARRAY calls in the buffer allocation code was changed from void* to char as the size argument had already been mulitipled by the byte size of an object pointer. Reviewed-by: ysr, tonyp --- hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp index 060743dd38a..a83f42a6a08 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/ptrQueue.cpp @@ -107,7 +107,7 @@ void** PtrQueueSet::allocate_buffer() { res[0] = NULL; return res; } else { - return NEW_C_HEAP_ARRAY(void*, _sz); + return (void**) NEW_C_HEAP_ARRAY(char, _sz); } } @@ -127,7 +127,8 @@ void PtrQueueSet::reduce_free_list() { 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); + FREE_C_HEAP_ARRAY(char, head); + _buf_free_list_sz --; n--; } } From bedf9084362662f2da00ca352d08599ea6489a6a Mon Sep 17 00:00:00 2001 From: Jon Masamitsu Date: Tue, 10 Nov 2009 11:32:48 -0800 Subject: [PATCH 11/61] 6898857: [Regression] -XX:NewRatio with -XX:+UseConcMarkSweepGC causes fatal error Use CollectorPolicy information instead of MaxNewSize Reviewed-by: ysr, jcoomes --- .../concurrentMarkSweep/concurrentMarkSweepGeneration.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index c3d30c348b2..20bbd7abafd 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -709,7 +709,8 @@ CMSCollector::CMSCollector(ConcurrentMarkSweepGeneration* cmsGen, // Support for parallelizing survivor space rescan if (CMSParallelRemarkEnabled && CMSParallelSurvivorRemarkEnabled) { - size_t max_plab_samples = MaxNewSize/((SurvivorRatio+2)*MinTLABSize); + size_t max_plab_samples = cp->max_gen0_size()/ + ((SurvivorRatio+2)*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); From 47748afed526cca653a7a2a382ae7ed418d9dc6c Mon Sep 17 00:00:00 2001 From: Karen Kinnear Date: Wed, 11 Nov 2009 15:49:38 -0500 Subject: [PATCH 12/61] 6893504: LinkageError for bootstrap duplicate class definitions Reviewed-by: kamg, xlu --- .../share/vm/classfile/systemDictionary.cpp | 30 ++++++++++++++----- .../share/vm/classfile/systemDictionary.hpp | 1 + 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index 5598c9fcfb3..10167809f34 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -99,6 +99,15 @@ bool SystemDictionary::is_parallelCapable(Handle class_loader) { 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 @@ -724,13 +733,13 @@ klassOop SystemDictionary::resolve_instance_class_or_null(symbolHandle class_nam // 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 @@ -1483,14 +1492,17 @@ void SystemDictionary::define_instance_class(instanceKlassHandle k, TRAPS) { } // 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. @@ -1511,9 +1523,11 @@ instanceKlassHandle SystemDictionary::find_or_define_instance_class(symbolHandle { 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 @@ -1529,7 +1543,7 @@ instanceKlassHandle SystemDictionary::find_or_define_instance_class(symbolHandle // 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(); diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp index b7c82033628..60d03f2117c 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -578,6 +578,7 @@ private: 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); From 1fb2423873a9ca24a8544ae167da1ccffc18a3a9 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Wed, 11 Nov 2009 23:39:17 -0800 Subject: [PATCH 13/61] 6892079: live value must not be garbage failure after fix for 6854812 Reviewed-by: kvn --- hotspot/src/share/vm/opto/parse1.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/parse1.cpp b/hotspot/src/share/vm/opto/parse1.cpp index 9633c5e270b..9845ac708ac 100644 --- a/hotspot/src/share/vm/opto/parse1.cpp +++ b/hotspot/src/share/vm/opto/parse1.cpp @@ -231,12 +231,13 @@ void Parse::load_interpreter_state(Node* osr_buf) { // Use the raw liveness computation to make sure that unexpected // values don't propagate into the OSR frame. - MethodLivenessResult live_locals = method()->raw_liveness_at_bci(osr_bci()); + MethodLivenessResult live_locals = method()->liveness_at_bci(osr_bci()); if (!live_locals.is_valid()) { // Degenerate or breakpointed method. C->record_method_not_compilable("OSR in empty or breakpointed method"); return; } + MethodLivenessResult raw_live_locals = method()->raw_liveness_at_bci(osr_bci()); // Extract the needed locals from the interpreter frame. Node *locals_addr = basic_plus_adr(osr_buf, osr_buf, (max_locals-1)*wordSize); @@ -316,6 +317,10 @@ void Parse::load_interpreter_state(Node* osr_buf) { continue; } } + if (type->basic_type() == T_ADDRESS && !raw_live_locals.at(index)) { + // Skip type check for dead address locals + continue; + } set_local(index, check_interpreter_type(l, type, bad_type_exit)); } From 9db2092b1b07043f267b3664dad707f4ad5f839e Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Thu, 12 Nov 2009 09:24:21 -0800 Subject: [PATCH 14/61] 6892658: C2 should optimize some stringbuilder patterns Reviewed-by: kvn, twisti --- hotspot/src/share/vm/ci/ciEnv.cpp | 23 + hotspot/src/share/vm/ci/ciEnv.hpp | 18 + hotspot/src/share/vm/ci/ciInstanceKlass.cpp | 14 + hotspot/src/share/vm/ci/ciInstanceKlass.hpp | 1 + hotspot/src/share/vm/ci/ciObjectFactory.cpp | 9 + .../share/vm/classfile/systemDictionary.hpp | 1 + hotspot/src/share/vm/classfile/vmSymbols.cpp | 6 + hotspot/src/share/vm/classfile/vmSymbols.hpp | 52 +- hotspot/src/share/vm/includeDB_compiler2 | 25 + hotspot/src/share/vm/includeDB_core | 1 + hotspot/src/share/vm/memory/universe.cpp | 7 + hotspot/src/share/vm/memory/universe.hpp | 4 + hotspot/src/share/vm/opto/c2_globals.cpp | 2 +- hotspot/src/share/vm/opto/c2_globals.hpp | 10 +- hotspot/src/share/vm/opto/callGenerator.cpp | 191 ++- hotspot/src/share/vm/opto/callGenerator.hpp | 12 +- hotspot/src/share/vm/opto/callnode.cpp | 78 + hotspot/src/share/vm/opto/callnode.hpp | 23 + hotspot/src/share/vm/opto/compile.cpp | 73 + hotspot/src/share/vm/opto/compile.hpp | 15 + hotspot/src/share/vm/opto/doCall.cpp | 61 +- hotspot/src/share/vm/opto/graphKit.cpp | 75 +- hotspot/src/share/vm/opto/graphKit.hpp | 39 +- hotspot/src/share/vm/opto/macro.cpp | 38 +- hotspot/src/share/vm/opto/memnode.cpp | 21 + hotspot/src/share/vm/opto/node.hpp | 43 +- hotspot/src/share/vm/opto/parseHelper.cpp | 8 + hotspot/src/share/vm/opto/phase.hpp | 1 + hotspot/src/share/vm/opto/phaseX.hpp | 6 +- hotspot/src/share/vm/opto/stringopts.cpp | 1395 +++++++++++++++++ hotspot/src/share/vm/opto/stringopts.hpp | 83 + hotspot/src/share/vm/opto/type.hpp | 3 - hotspot/src/share/vm/runtime/globals.cpp | 6 +- .../share/vm/runtime/globals_extension.hpp | 5 +- .../src/share/vm/utilities/growableArray.hpp | 11 + 35 files changed, 2245 insertions(+), 115 deletions(-) create mode 100644 hotspot/src/share/vm/opto/stringopts.cpp create mode 100644 hotspot/src/share/vm/opto/stringopts.hpp diff --git a/hotspot/src/share/vm/ci/ciEnv.cpp b/hotspot/src/share/vm/ci/ciEnv.cpp index 0a6de348d43..5d7df11f59f 100644 --- a/hotspot/src/share/vm/ci/ciEnv.cpp +++ b/hotspot/src/share/vm/ci/ciEnv.cpp @@ -46,6 +46,9 @@ ciInstanceKlass* ciEnv::_Throwable; ciInstanceKlass* ciEnv::_Thread; ciInstanceKlass* ciEnv::_OutOfMemoryError; ciInstanceKlass* ciEnv::_String; +ciInstanceKlass* ciEnv::_StringBuffer; +ciInstanceKlass* ciEnv::_StringBuilder; +ciInstanceKlass* ciEnv::_Integer; ciSymbol* ciEnv::_unloaded_cisymbol = NULL; ciInstanceKlass* ciEnv::_unloaded_ciinstance_klass = NULL; @@ -110,6 +113,8 @@ ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) { _ArrayIndexOutOfBoundsException_instance = NULL; _ArrayStoreException_instance = NULL; _ClassCastException_instance = NULL; + _the_null_string = NULL; + _the_min_jint_string = NULL; } ciEnv::ciEnv(Arena* arena) { @@ -163,6 +168,8 @@ ciEnv::ciEnv(Arena* arena) { _ArrayIndexOutOfBoundsException_instance = NULL; _ArrayStoreException_instance = NULL; _ClassCastException_instance = NULL; + _the_null_string = NULL; + _the_min_jint_string = NULL; } ciEnv::~ciEnv() { @@ -248,6 +255,22 @@ ciInstance* ciEnv::ClassCastException_instance() { 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) { diff --git a/hotspot/src/share/vm/ci/ciEnv.hpp b/hotspot/src/share/vm/ci/ciEnv.hpp index e855dbf9e4f..493600e020c 100644 --- a/hotspot/src/share/vm/ci/ciEnv.hpp +++ b/hotspot/src/share/vm/ci/ciEnv.hpp @@ -82,6 +82,9 @@ private: static ciInstanceKlass* _Thread; static ciInstanceKlass* _OutOfMemoryError; static ciInstanceKlass* _String; + static ciInstanceKlass* _StringBuffer; + static ciInstanceKlass* _StringBuilder; + static ciInstanceKlass* _Integer; static ciSymbol* _unloaded_cisymbol; static ciInstanceKlass* _unloaded_ciinstance_klass; @@ -97,6 +100,9 @@ private: 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, @@ -310,6 +316,15 @@ public: ciInstanceKlass* String_klass() { return _String; } + ciInstanceKlass* StringBuilder_klass() { + return _StringBuilder; + } + ciInstanceKlass* StringBuffer_klass() { + return _StringBuffer; + } + ciInstanceKlass* Integer_klass() { + return _Integer; + } ciInstance* NullPointerException_instance() { assert(_NullPointerException_instance != NULL, "initialization problem"); return _NullPointerException_instance; @@ -324,6 +339,9 @@ public: ciInstance* ArrayStoreException_instance(); ciInstance* ClassCastException_instance(); + ciInstance* the_null_string(); + ciInstance* the_min_jint_string(); + static ciSymbol* unloaded_cisymbol() { return _unloaded_cisymbol; } diff --git a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp index 1053727a93f..ac14e71d591 100644 --- a/hotspot/src/share/vm/ci/ciInstanceKlass.cpp +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.cpp @@ -340,6 +340,20 @@ ciField* ciInstanceKlass::get_field_by_offset(int field_offset, bool is_static) return field; } +// ------------------------------------------------------------------ +// 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. diff --git a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp index a60020adc34..007a2ab8dba 100644 --- a/hotspot/src/share/vm/ci/ciInstanceKlass.hpp +++ b/hotspot/src/share/vm/ci/ciInstanceKlass.hpp @@ -148,6 +148,7 @@ public: 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(); diff --git a/hotspot/src/share/vm/ci/ciObjectFactory.cpp b/hotspot/src/share/vm/ci/ciObjectFactory.cpp index 6fb4edc4d9c..f05abb21f1c 100644 --- a/hotspot/src/share/vm/ci/ciObjectFactory.cpp +++ b/hotspot/src/share/vm/ci/ciObjectFactory.cpp @@ -168,6 +168,15 @@ void ciObjectFactory::init_shared_objects() { ciEnv::_String = get(SystemDictionary::string_klass()) ->as_instance_klass(); + ciEnv::_StringBuffer = + get(SystemDictionary::stringBuffer_klass()) + ->as_instance_klass(); + ciEnv::_StringBuilder = + get(SystemDictionary::StringBuilder_klass()) + ->as_instance_klass(); + ciEnv::_Integer = + get(SystemDictionary::int_klass()) + ->as_instance_klass(); for (int len = -1; len != _ci_objects->length(); ) { len = _ci_objects->length(); diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp index 18367ded29e..1b1a4a7d574 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -150,6 +150,7 @@ class SymbolPropertyTable; template(vector_klass, java_util_Vector, Pre) \ template(hashtable_klass, java_util_Hashtable, 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) \ diff --git a/hotspot/src/share/vm/classfile/vmSymbols.cpp b/hotspot/src/share/vm/classfile/vmSymbols.cpp index c805af344e7..cc96bee955b 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.cpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.cpp @@ -303,6 +303,11 @@ inline bool match_F_R(jshort flags) { 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; @@ -361,6 +366,7 @@ const char* vmIntrinsics::short_name_as_C_string(vmIntrinsics::ID id, char* buf, const char* sname = vmSymbols::name_for(signature_for(id)); const char* fname = ""; switch (flags_for(id)) { + case F_Y: fname = "synchronized "; break; case F_RN: fname = "native "; break; case F_SN: fname = "native static "; break; case F_S: fname = "static "; break; diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index bb9f3076a96..aed7b874fdf 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -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") \ @@ -334,6 +335,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") \ @@ -415,6 +417,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) \ \ @@ -814,10 +823,34 @@ /*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) */ \ \ @@ -945,11 +978,12 @@ class vmIntrinsics: AllStatic { 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 }; public: diff --git a/hotspot/src/share/vm/includeDB_compiler2 b/hotspot/src/share/vm/includeDB_compiler2 index 6ba7bfaf867..abe70d1f73e 100644 --- a/hotspot/src/share/vm/includeDB_compiler2 +++ b/hotspot/src/share/vm/includeDB_compiler2 @@ -149,6 +149,7 @@ c2compiler.cpp runtime.hpp c2compiler.hpp abstractCompiler.hpp callGenerator.cpp addnode.hpp +callGenerator.cpp bcEscapeAnalyzer.hpp callGenerator.cpp callGenerator.hpp callGenerator.cpp callnode.hpp callGenerator.cpp cfgnode.hpp @@ -321,6 +322,7 @@ compile.cpp phaseX.hpp 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 @@ -476,12 +478,16 @@ graphKit.cpp rootnode.hpp 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 +496,10 @@ idealKit.cpp cfgnode.hpp 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 @@ -641,6 +650,7 @@ macro.cpp addnode.hpp 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 @@ -993,6 +1003,21 @@ split_if.cpp callnode.hpp 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 --git a/hotspot/src/share/vm/includeDB_core b/hotspot/src/share/vm/includeDB_core index 1a5f3ee3073..1943a4a859b 100644 --- a/hotspot/src/share/vm/includeDB_core +++ b/hotspot/src/share/vm/includeDB_core @@ -570,6 +570,7 @@ ciEnv.hpp debugInfoRec.hpp ciEnv.hpp dependencies.hpp ciEnv.hpp exceptionHandlerTable.hpp ciEnv.hpp oopMap.hpp +ciEnv.hpp systemDictionary.hpp ciEnv.hpp thread.hpp ciExceptionHandler.cpp ciExceptionHandler.hpp diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index 5a80ac14477..5a4bd323abd 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -67,6 +67,8 @@ typeArrayOop Universe::_the_empty_int_array = NULL; 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 @@ void Universe::oops_do(OopClosure* f, bool do_all) { 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); @@ -289,6 +293,9 @@ void Universe::genesis(TRAPS) { 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) == diff --git a/hotspot/src/share/vm/memory/universe.hpp b/hotspot/src/share/vm/memory/universe.hpp index b22a1eba6c9..97044e161d4 100644 --- a/hotspot/src/share/vm/memory/universe.hpp +++ b/hotspot/src/share/vm/memory/universe.hpp @@ -169,6 +169,8 @@ class Universe: AllStatic { 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 @@ class Universe: AllStatic { 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 --git a/hotspot/src/share/vm/opto/c2_globals.cpp b/hotspot/src/share/vm/opto/c2_globals.cpp index 5715b24ba57..40594bf940e 100644 --- a/hotspot/src/share/vm/opto/c2_globals.cpp +++ b/hotspot/src/share/vm/opto/c2_globals.cpp @@ -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 --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index 091ad4a9bb4..8c650eeaf10 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -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, " \ @@ -394,6 +394,12 @@ product(bool, UseOptoBiasInlining, true, \ "Generate biased locking code in C2 ideal graph") \ \ + experimental(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 +419,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 --git a/hotspot/src/share/vm/opto/callGenerator.cpp b/hotspot/src/share/vm/opto/callGenerator.cpp index 8ce7c0ce57d..8ddc84715aa 100644 --- a/hotspot/src/share/vm/opto/callGenerator.cpp +++ b/hotspot/src/share/vm/opto/callGenerator.cpp @@ -98,12 +98,21 @@ JVMState* ParseGenerator::generate(JVMState* jvms) { //---------------------------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) { @@ -129,9 +138,10 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) { call->set_optimized_virtual(true); } kit.set_arguments_for_java_call(call); - kit.set_edges_for_java_call(call); - Node* ret = kit.set_results_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(); } @@ -238,9 +248,9 @@ CallGenerator* CallGenerator::for_osr(ciMethod* m, int osr_bci) { 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_virtual_call(ciMethod* m, int vtable_index) { @@ -248,6 +258,108 @@ CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) { 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 +427,7 @@ JVMState* WarmCallGenerator::generate(JVMState* jvms) { } 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() { diff --git a/hotspot/src/share/vm/opto/callGenerator.hpp b/hotspot/src/share/vm/opto/callGenerator.hpp index bbd47ca4aab..70ec9d3ffb6 100644 --- a/hotspot/src/share/vm/opto/callGenerator.hpp +++ b/hotspot/src/share/vm/opto/callGenerator.hpp @@ -57,6 +57,13 @@ class CallGenerator : public ResourceObj { // 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,12 @@ class CallGenerator : public ResourceObj { 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_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, diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp index 5c69109b8fe..a5902713111 100644 --- a/hotspot/src/share/vm/opto/callnode.cpp +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -693,6 +693,84 @@ Node *CallNode::result_cast() { } +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 --git a/hotspot/src/share/vm/opto/callnode.hpp b/hotspot/src/share/vm/opto/callnode.hpp index ac886f3ba99..a9649a7ccd5 100644 --- a/hotspot/src/share/vm/opto/callnode.hpp +++ b/hotspot/src/share/vm/opto/callnode.hpp @@ -470,6 +470,23 @@ public: #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 @@ public: // 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 @@ public: #endif }; + //------------------------------CallJavaNode----------------------------------- // Make a static or dynamic subroutine call node using Java calling // convention. (The "Java" calling convention is the compiler's calling diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index ecffd2f4e6a..02bcadb387b 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -224,6 +224,32 @@ bool Compile::valid_bundle_info(const Node *n) { } +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. @@ -554,6 +580,28 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr 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. @@ -820,6 +868,7 @@ void Compile::Init(int aliaslevel) { _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 @@ -2240,6 +2289,30 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) { 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 diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp index 4bd1900fc70..450ff269f81 100644 --- a/hotspot/src/share/vm/opto/compile.hpp +++ b/hotspot/src/share/vm/opto/compile.hpp @@ -149,6 +149,7 @@ class Compile : public Phase { 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. @@ -219,6 +220,9 @@ class Compile : public Phase { 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 +302,8 @@ class Compile : public Phase { 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; } @@ -475,6 +481,7 @@ class Compile : public Phase { // 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 +502,11 @@ class Compile : public Phase { 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 +514,9 @@ class Compile : public Phase { 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 --git a/hotspot/src/share/vm/opto/doCall.cpp b/hotspot/src/share/vm/opto/doCall.cpp index 5104e648aa6..fff6eedda11 100644 --- a/hotspot/src/share/vm/opto/doCall.cpp +++ b/hotspot/src/share/vm/opto/doCall.cpp @@ -128,6 +128,12 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, 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) { @@ -225,10 +231,63 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, } 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) { diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp index b63aae489ff..1b5eb54422f 100644 --- a/hotspot/src/share/vm/opto/graphKit.cpp +++ b/hotspot/src/share/vm/opto/graphKit.cpp @@ -1351,8 +1351,8 @@ void GraphKit::set_all_memory(Node* newmem) { } //------------------------------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); } @@ -1573,7 +1573,7 @@ void GraphKit::set_arguments_for_java_call(CallJavaNode* call) { //---------------------------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() ); @@ -1595,13 +1595,13 @@ void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw) { // 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. @@ -1614,8 +1614,15 @@ Node* GraphKit::set_results_for_java_call(CallJavaNode* call) { // 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; } @@ -1678,6 +1685,59 @@ void GraphKit::set_predefined_output_for_runtime_call(Node* call, } } + +// 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()); + } 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 @@ -3459,4 +3519,3 @@ void GraphKit::g1_write_barrier_post(Node* oop_store, sync_kit(ideal); } #undef __ - diff --git a/hotspot/src/share/vm/opto/graphKit.hpp b/hotspot/src/share/vm/opto/graphKit.hpp index b127789b5f3..8135aca2d39 100644 --- a/hotspot/src/share/vm/opto/graphKit.hpp +++ b/hotspot/src/share/vm/opto/graphKit.hpp @@ -279,6 +279,34 @@ class GraphKit : public Phase { } 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 +428,7 @@ class GraphKit : public Phase { 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 +571,12 @@ class GraphKit : public Phase { // 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 +587,11 @@ class GraphKit : public Phase { 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 diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index e2421a7f3d3..6bff3539c30 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -912,15 +912,29 @@ bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { 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 +1653,18 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { } // 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 --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index 02c15af7232..fb14ebff8c1 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -1503,6 +1503,8 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const { } } } 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() || @@ -1510,6 +1512,25 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const { 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 diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp index bad1607058e..1fe4950e913 100644 --- a/hotspot/src/share/vm/opto/node.hpp +++ b/hotspot/src/share/vm/opto/node.hpp @@ -661,18 +661,25 @@ public: 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) @@ -1249,6 +1256,24 @@ Node* Node::last_out(DUIterator_Last& i) const { #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 +1315,12 @@ class Node_List : public Node_Array { 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 --git a/hotspot/src/share/vm/opto/parseHelper.cpp b/hotspot/src/share/vm/opto/parseHelper.cpp index 6b3f432eae7..ab7883fd8ff 100644 --- a/hotspot/src/share/vm/opto/parseHelper.cpp +++ b/hotspot/src/share/vm/opto/parseHelper.cpp @@ -221,6 +221,14 @@ void Parse::do_new() { // 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 diff --git a/hotspot/src/share/vm/opto/phase.hpp b/hotspot/src/share/vm/opto/phase.hpp index d0d54e1d900..9df5500fd5e 100644 --- a/hotspot/src/share/vm/opto/phase.hpp +++ b/hotspot/src/share/vm/opto/phase.hpp @@ -44,6 +44,7 @@ public: 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 diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp index b9391ba1d97..33ff56f0ee4 100644 --- a/hotspot/src/share/vm/opto/phaseX.hpp +++ b/hotspot/src/share/vm/opto/phaseX.hpp @@ -345,7 +345,11 @@ public: 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 --git a/hotspot/src/share/vm/opto/stringopts.cpp b/hotspot/src/share/vm/opto/stringopts.cpp new file mode 100644 index 00000000000..192d31f8257 --- /dev/null +++ b/hotspot/src/share/vm/opto/stringopts.cpp @@ -0,0 +1,1395 @@ +/* + * 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/_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(kit.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->join(TypeInstPtr::NOTNULL)); + 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); + r->init_req(1, notnull); + phi->init_req(1, arg); + 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 --git a/hotspot/src/share/vm/opto/stringopts.hpp b/hotspot/src/share/vm/opto/stringopts.hpp new file mode 100644 index 00000000000..417e08a9185 --- /dev/null +++ b/hotspot/src/share/vm/opto/stringopts.hpp @@ -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 --git a/hotspot/src/share/vm/opto/type.hpp b/hotspot/src/share/vm/opto/type.hpp index 9b11f9ca595..03f81532c41 100644 --- a/hotspot/src/share/vm/opto/type.hpp +++ b/hotspot/src/share/vm/opto/type.hpp @@ -847,9 +847,6 @@ public: // 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 --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp index 71fec552482..03e068dea91 100644 --- a/hotspot/src/share/vm/runtime/globals.cpp +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -46,7 +46,8 @@ bool Flag::is_unlocker() const { bool Flag::is_unlocked() const { if (strcmp(kind, "{diagnostic}") == 0) { return UnlockDiagnosticVMOptions; - } else if (strcmp(kind, "{experimental}") == 0) { + } else if (strcmp(kind, "{experimental}") == 0 || + strcmp(kind, "{C2 experimental}") == 0) { return UnlockExperimentalVMOptions; } else { return true; @@ -169,6 +170,7 @@ void Flag::print_as_flag(outputStream* st) { #define C2_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 product}", DEFAULT }, #define C2_PD_PRODUCT_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C2 pd product}", DEFAULT }, #define C2_DIAGNOSTIC_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 diagnostic}", DEFAULT }, +#define C2_EXPERIMENTAL_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 experimental}", DEFAULT }, #ifdef PRODUCT #define C2_DEVELOP_FLAG_STRUCT(type, name, value, doc) /* flag is constant */ #define C2_PD_DEVELOP_FLAG_STRUCT(type, name, doc) /* flag is constant */ @@ -190,7 +192,7 @@ static Flag flagTable[] = { C1_FLAGS(C1_DEVELOP_FLAG_STRUCT, C1_PD_DEVELOP_FLAG_STRUCT, C1_PRODUCT_FLAG_STRUCT, C1_PD_PRODUCT_FLAG_STRUCT, C1_NOTPRODUCT_FLAG_STRUCT) #endif #ifdef COMPILER2 - C2_FLAGS(C2_DEVELOP_FLAG_STRUCT, C2_PD_DEVELOP_FLAG_STRUCT, C2_PRODUCT_FLAG_STRUCT, C2_PD_PRODUCT_FLAG_STRUCT, C2_DIAGNOSTIC_FLAG_STRUCT, C2_NOTPRODUCT_FLAG_STRUCT) + C2_FLAGS(C2_DEVELOP_FLAG_STRUCT, C2_PD_DEVELOP_FLAG_STRUCT, C2_PRODUCT_FLAG_STRUCT, C2_PD_PRODUCT_FLAG_STRUCT, C2_DIAGNOSTIC_FLAG_STRUCT, C2_EXPERIMENTAL_FLAG_STRUCT, C2_NOTPRODUCT_FLAG_STRUCT) #endif {0, NULL, NULL} }; diff --git a/hotspot/src/share/vm/runtime/globals_extension.hpp b/hotspot/src/share/vm/runtime/globals_extension.hpp index 35844b7d2a4..f32cab1cc7b 100644 --- a/hotspot/src/share/vm/runtime/globals_extension.hpp +++ b/hotspot/src/share/vm/runtime/globals_extension.hpp @@ -64,6 +64,7 @@ #define C2_PRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), #define C2_PD_PRODUCT_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), #define C2_DIAGNOSTIC_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), +#define C2_EXPERIMENTAL_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), #ifdef PRODUCT #define C2_DEVELOP_FLAG_MEMBER(type, name, value, doc) /* flag is constant */ #define C2_PD_DEVELOP_FLAG_MEMBER(type, name, doc) /* flag is constant */ @@ -84,7 +85,7 @@ typedef enum { C1_FLAGS(C1_DEVELOP_FLAG_MEMBER, C1_PD_DEVELOP_FLAG_MEMBER, C1_PRODUCT_FLAG_MEMBER, C1_PD_PRODUCT_FLAG_MEMBER, C1_NOTPRODUCT_FLAG_MEMBER) #endif #ifdef COMPILER2 - C2_FLAGS(C2_DEVELOP_FLAG_MEMBER, C2_PD_DEVELOP_FLAG_MEMBER, C2_PRODUCT_FLAG_MEMBER, C2_PD_PRODUCT_FLAG_MEMBER, C2_DIAGNOSTIC_FLAG_MEMBER, C2_NOTPRODUCT_FLAG_MEMBER) + C2_FLAGS(C2_DEVELOP_FLAG_MEMBER, C2_PD_DEVELOP_FLAG_MEMBER, C2_PRODUCT_FLAG_MEMBER, C2_PD_PRODUCT_FLAG_MEMBER, C2_DIAGNOSTIC_FLAG_MEMBER, C2_EXPERIMENTAL_FLAG_MEMBER, C2_NOTPRODUCT_FLAG_MEMBER) #endif NUM_CommandLineFlag } CommandLineFlag; @@ -130,6 +131,7 @@ typedef enum { #define C2_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), #define C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), #define C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), +#define C2_EXPERIMENTAL_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), #ifdef PRODUCT #define C2_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) /* flag is constant */ #define C2_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) /* flag is constant */ @@ -181,6 +183,7 @@ typedef enum { C2_PRODUCT_FLAG_MEMBER_WITH_TYPE, C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE, C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE, + C2_EXPERIMENTAL_FLAG_MEMBER_WITH_TYPE, C2_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE) #endif NUM_CommandLineFlagWithType diff --git a/hotspot/src/share/vm/utilities/growableArray.hpp b/hotspot/src/share/vm/utilities/growableArray.hpp index 77ce93c1396..c5236466617 100644 --- a/hotspot/src/share/vm/utilities/growableArray.hpp +++ b/hotspot/src/share/vm/utilities/growableArray.hpp @@ -278,6 +278,17 @@ template class GrowableArray : public GenericGrowableArray { _len--; } + // inserts the given element before the element at index i + void insert_before(const int idx, const E& elem) { + check_nesting(); + if (_len == _max) grow(_len); + for (int j = _len - 1; j >= idx; j--) { + _data[j + 1] = _data[j]; + } + _len++; + _data[idx] = elem; + } + void appendAll(const GrowableArray* l) { for (int i = 0; i < l->_len; i++) { raw_at_put_grow(_len, l->_data[i], 0); From 241f0f0cd87507c6f80f107a8cf4b51fbd994f62 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Fri, 13 Nov 2009 11:55:26 -0800 Subject: [PATCH 15/61] 6898948: G1: forensic instrumentation for out-of-bounds recent_avg_pause_time_ratio() Added instrumentation and (temporary) assert in non-product mode; clipped the value when found out-of-bounds in product mode. Fix of original issue will follow collection of data from this instrumentation. Reviewed-by: jcoomes, tonyp --- .../g1/g1CollectorPolicy.cpp | 27 +++++++++++++++-- hotspot/src/share/vm/utilities/numberSeq.cpp | 30 +++++++++++++++++++ hotspot/src/share/vm/utilities/numberSeq.hpp | 10 +++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 20e8fba5dea..70f6809acf3 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -1516,8 +1516,31 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) { (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; - // using 1.01 to account for floating point inaccuracies - assert(recent_avg_pause_time_ratio() < 1.01, "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()); + // TEMPORARY: In debug mode, terminate the JVM, so nightly testing explicitly + // flags the sighting by failing the test. + assert(false, "Debugging data for CR 6898948 has been dumped above"); +#else // PRODUCT + // Clip ratio between 0.0 and 1.0 + 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; + } +#endif // PRODUCT + } } if (G1PolicyVerbose > 1) { diff --git a/hotspot/src/share/vm/utilities/numberSeq.cpp b/hotspot/src/share/vm/utilities/numberSeq.cpp index 7cd06b28c78..0c152cfdeb1 100644 --- a/hotspot/src/share/vm/utilities/numberSeq.cpp +++ b/hotspot/src/share/vm/utilities/numberSeq.cpp @@ -241,3 +241,33 @@ double TruncatedSeq::predict_next() const { return b0 + b1 * num; } + + +// Printing/Debugging Support + +void AbsSeq::dump() { dump_on(gclog_or_tty); } + +void AbsSeq::dump_on(outputStream* s) { + s->print_cr("\t _num = %d, _sum = %7.3f, _sum_of_squares = %7.3f", + _num, _sum, _sum_of_squares); + s->print_cr("\t _davg = %7.3f, _dvariance = %7.3f, _alpha = %7.3f", + _davg, _dvariance, _alpha); +} + +void NumberSeq::dump_on(outputStream* s) { + AbsSeq::dump_on(s); + s->print_cr("\t\t _last = %7.3f, _maximum = %7.3f"); +} + +void TruncatedSeq::dump_on(outputStream* s) { + AbsSeq::dump_on(s); + s->print_cr("\t\t _length = %d, _next = %d", _length, _next); + for (int i = 0; i < _length; i++) { + if (i%5 == 0) { + s->cr(); + s->print("\t"); + } + s->print("\t[%d]=%7.3f", i, _sequence[i]); + } + s->print_cr(""); +} diff --git a/hotspot/src/share/vm/utilities/numberSeq.hpp b/hotspot/src/share/vm/utilities/numberSeq.hpp index 4366c8bf1e0..eb40b149b13 100644 --- a/hotspot/src/share/vm/utilities/numberSeq.hpp +++ b/hotspot/src/share/vm/utilities/numberSeq.hpp @@ -74,6 +74,10 @@ public: double davg() const; // decaying average double dvariance() const; // decaying variance double dsd() const; // decaying "standard deviation" + + // Debugging/Printing + virtual void dump(); + virtual void dump_on(outputStream* s); }; class NumberSeq: public AbsSeq { @@ -91,6 +95,9 @@ public: virtual void add(double val); virtual double maximum() const { return _maximum; } virtual double last() const { return _last; } + + // Debugging/Printing + virtual void dump_on(outputStream* s); }; class TruncatedSeq: public AbsSeq { @@ -114,4 +121,7 @@ public: double oldest() const; // the oldest valid value in the sequence double predict_next() const; // prediction based on linear regression + + // Debugging/Printing + virtual void dump_on(outputStream* s); }; From e6547d1b204d811f7e43534c66a0a53074623676 Mon Sep 17 00:00:00 2001 From: Christian Thalinger Date: Thu, 19 Nov 2009 03:41:29 -0800 Subject: [PATCH 16/61] 6902000: use ShouldNotReachHere() for btos/ctos/stos in TemplateInterpreterGenerator::set_short_entry_points Set_entry_point is only ever used with the tos states of bytecode templates in templateTable.cpp and none of those use the subword tos states like btos, ctos and stos. Reviewed-by: kvn --- hotspot/src/share/vm/interpreter/templateInterpreter.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp index 8e48dce07b2..e617623506b 100644 --- a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp @@ -465,9 +465,11 @@ void TemplateInterpreterGenerator::set_wide_entry_point(Template* t, address& we 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; From c54bb4236d35a807ea2dc3688c61250488838c30 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Thu, 19 Nov 2009 10:19:19 -0800 Subject: [PATCH 17/61] 6902701: G1: protect debugging code related to 6898948 with a debug flag Protected stats dump with a new develop flag; other than for the dump, reconciled product and non-product behaviour in face of the error. Reviewed-by: tonyp --- .../vm/gc_implementation/g1/g1CollectorPolicy.cpp | 11 +++++------ .../src/share/vm/gc_implementation/g1/g1_globals.hpp | 4 ++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 70f6809acf3..9b72a114dc2 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -1528,18 +1528,17 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) { _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()); - // TEMPORARY: In debug mode, terminate the JVM, so nightly testing explicitly - // flags the sighting by failing the test. - assert(false, "Debugging data for CR 6898948 has been dumped above"); -#else // PRODUCT - // Clip ratio between 0.0 and 1.0 + // 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; } -#endif // PRODUCT } } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp index c941c8755d6..b55dc0f5bf1 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp @@ -242,6 +242,10 @@ 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") \ \ From b5af9f340887711cb163bad8dce55df5397a1fb9 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Thu, 19 Nov 2009 13:43:25 -0800 Subject: [PATCH 18/61] 6902303: G1: ScavengeALot should cause an incremental, rather than a full, collection ScavengeALot now causes an incremental (but possibly partially young, in the G1 sense) collection. Some such collections may be abandoned on account of MMU specs. Band-aided a native leak associated with abandoned pauses, as well as an MMU tracker overflow related to frequent scavenge events in the face of a large MMU denominator interval; the latter is protected by a product flag that defaults to false. Reviewed-by: tonyp --- .../gc_implementation/g1/g1CollectedHeap.cpp | 57 +++++++++++++------ .../g1/g1CollectorPolicy.cpp | 13 +++-- .../vm/gc_implementation/g1/g1MMUTracker.cpp | 20 +++++-- .../vm/gc_implementation/g1/g1MMUTracker.hpp | 5 +- .../vm/gc_implementation/g1/g1_globals.hpp | 3 + .../gc_implementation/g1/vm_operations_g1.cpp | 2 +- .../gc_implementation/g1/vm_operations_g1.hpp | 5 +- hotspot/src/share/vm/memory/sharedHeap.hpp | 4 -- 8 files changed, 73 insertions(+), 36 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 550b028ea60..1f428a85841 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -1732,13 +1732,6 @@ size_t G1CollectedHeap::unsafe_max_alloc() { 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"); @@ -1755,17 +1748,31 @@ void G1CollectedHeap::collect_as_vm_thread(GCCause::Cause cause) { } } +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"); -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(); + 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); + } } } @@ -2649,8 +2656,6 @@ G1CollectedHeap::do_collection_pause_at_safepoint() { 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. @@ -2802,6 +2807,22 @@ G1CollectedHeap::do_collection_pause_at_safepoint() { _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()); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 9b72a114dc2..93a15f6eb69 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -2847,8 +2847,15 @@ choose_collection_set() { 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); @@ -2994,7 +3001,3 @@ record_collection_pause_end(bool abandoned) { G1CollectorPolicy::record_collection_pause_end(abandoned); assert(assertMarkedBytesDataOK(), "Marked regions not OK at pause end."); } - -// Local Variables: *** -// c-indentation-style: gnu *** -// End: *** diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp index b0d0df45dd9..492adaef6fd 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp @@ -86,12 +86,22 @@ void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) { // 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 ||) G1ForgetfulMMUTracker, + "array full, currently we can't recover unless +G1ForgetfulMMUTracker"); + // 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 G1ForgetfulMMUTracker 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); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.hpp index 0eff2c3867a..1030454a741 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.hpp @@ -99,7 +99,10 @@ private: // 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 +G1ForgetfulMMUTracker, thus + // potentially violating MMU specs for some time thereafter. G1MMUTrackerQueueElem _array[QueueLength]; int _head_index; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp index b55dc0f5bf1..ae669f6f3aa 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp @@ -256,6 +256,9 @@ "If non-0 is the size of the G1 survivor space, " \ "otherwise SurvivorRatio is used to determine the size") \ \ + product(bool, G1ForgetfulMMUTracker, false, \ + "If the MMU tracker's memory is full, forget the oldest entry") \ + \ product(uintx, G1HeapRegionSize, 0, \ "Size of the G1 regions.") \ \ diff --git a/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp b/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp index e59dbe483d2..2137efa4e83 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp @@ -42,7 +42,7 @@ void VM_G1CollectFull::doit() { 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 --git a/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp b/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp index 6cf0605ec8c..95dda3844b7 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp @@ -68,8 +68,9 @@ class VM_G1CollectForAllocation: public VM_GC_Operation { 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 --git a/hotspot/src/share/vm/memory/sharedHeap.hpp b/hotspot/src/share/vm/memory/sharedHeap.hpp index 0bf863fb133..6094387241b 100644 --- a/hotspot/src/share/vm/memory/sharedHeap.hpp +++ b/hotspot/src/share/vm/memory/sharedHeap.hpp @@ -224,10 +224,6 @@ public: 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. From 39a98bab1de1a17c8411f6279e1db8586aed2d72 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Thu, 19 Nov 2009 14:32:23 -0800 Subject: [PATCH 19/61] 6902036: WorldWind asserts on escape.cpp:1153: assert(addr->is_AddP(),"AddP required") Remove the assert. Reviewed-by: twisti --- hotspot/src/share/vm/opto/escape.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index e57dad63a5f..478a96a7537 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -1150,7 +1150,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } 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; From 9cf0c891d94c7d6ec8d6f4310beb1ab914da93be Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Fri, 20 Nov 2009 14:47:01 -0500 Subject: [PATCH 20/61] 6815790: G1: Missing MemoryPoolMXBeans with -XX:+UseG1GC It introduces the necessary memory pools for G1. Reviewed-by: mchung, ysr --- .../gc_implementation/g1/g1CollectedHeap.cpp | 11 +- .../vm/gc_implementation/includeDB_gc_g1 | 11 + .../src/share/vm/services/g1MemoryPool.cpp | 153 ++++++++++++ .../src/share/vm/services/g1MemoryPool.hpp | 226 ++++++++++++++++++ .../src/share/vm/services/memoryManager.cpp | 8 + .../src/share/vm/services/memoryManager.hpp | 24 +- .../src/share/vm/services/memoryService.cpp | 75 +++++- .../src/share/vm/services/memoryService.hpp | 9 + 8 files changed, 513 insertions(+), 4 deletions(-) create mode 100644 hotspot/src/share/vm/services/g1MemoryPool.cpp create mode 100644 hotspot/src/share/vm/services/g1MemoryPool.hpp diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 1f428a85841..f9cce8b4e6a 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -928,6 +928,8 @@ void G1CollectedHeap::do_collection(bool full, bool clear_all_soft_refs, 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(); g1_policy()->record_full_collection_start(); @@ -1001,6 +1003,8 @@ void G1CollectedHeap::do_collection(bool full, bool clear_all_soft_refs, 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:"); @@ -2645,6 +2649,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint() { } { + ResourceMark rm; + char verbose_str[128]; sprintf(verbose_str, "GC pause "); if (g1_policy()->in_young_gc_mode()) { @@ -2663,7 +2669,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint() { 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"); @@ -2859,6 +2866,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint() { 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:"); diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 b/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 index 63bfbce76f9..32da661dd60 100644 --- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 @@ -222,6 +222,15 @@ g1MarkSweep.hpp oop.hpp 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 @@ -303,6 +312,8 @@ heapRegionSeq.inline.hpp heapRegionSeq.hpp klass.hpp g1OopClosures.hpp +memoryService.cpp g1MemoryPool.hpp + ptrQueue.cpp allocation.hpp ptrQueue.cpp allocation.inline.hpp ptrQueue.cpp mutex.hpp diff --git a/hotspot/src/share/vm/services/g1MemoryPool.cpp b/hotspot/src/share/vm/services/g1MemoryPool.cpp new file mode 100644 index 00000000000..1a1c337a036 --- /dev/null +++ b/hotspot/src/share/vm/services/g1MemoryPool.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 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/_g1MemoryPool.cpp.incl" + +G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h, + const char* name, + size_t init_size, + size_t max_size, + bool support_usage_threshold) : + _g1h(g1h), CollectedMemoryPool(name, + MemoryPool::Heap, + init_size, + max_size, + support_usage_threshold) { + assert(UseG1GC, "sanity"); +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::eden_space_committed(G1CollectedHeap* g1h) { + return eden_space_used(g1h); +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::eden_space_used(G1CollectedHeap* g1h) { + size_t young_list_length = g1h->young_list_length(); + size_t eden_used = young_list_length * HeapRegion::GrainBytes; + size_t survivor_used = survivor_space_used(g1h); + eden_used = subtract_up_to_zero(eden_used, survivor_used); + return eden_used; +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::eden_space_max(G1CollectedHeap* g1h) { + return eden_space_committed(g1h); +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::survivor_space_committed(G1CollectedHeap* g1h) { + return survivor_space_used(g1h); +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::survivor_space_used(G1CollectedHeap* g1h) { + size_t survivor_num = g1h->g1_policy()->recorded_survivor_regions(); + size_t survivor_used = survivor_num * HeapRegion::GrainBytes; + return survivor_used; +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::survivor_space_max(G1CollectedHeap* g1h) { + return survivor_space_committed(g1h); +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::old_space_committed(G1CollectedHeap* g1h) { + size_t committed = overall_committed(g1h); + size_t eden_committed = eden_space_committed(g1h); + size_t survivor_committed = survivor_space_committed(g1h); + committed = subtract_up_to_zero(committed, eden_committed); + committed = subtract_up_to_zero(committed, survivor_committed); + return committed; +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::old_space_used(G1CollectedHeap* g1h) { + size_t used = overall_used(g1h); + size_t eden_used = eden_space_used(g1h); + size_t survivor_used = survivor_space_used(g1h); + used = subtract_up_to_zero(used, eden_used); + used = subtract_up_to_zero(used, survivor_used); + return used; +} + +// See the comment at the top of g1MemoryPool.hpp +size_t G1MemoryPoolSuper::old_space_max(G1CollectedHeap* g1h) { + size_t max = g1h->g1_reserved_obj_bytes(); + size_t eden_max = eden_space_max(g1h); + size_t survivor_max = survivor_space_max(g1h); + max = subtract_up_to_zero(max, eden_max); + max = subtract_up_to_zero(max, survivor_max); + return max; +} + +G1EdenPool::G1EdenPool(G1CollectedHeap* g1h) : + G1MemoryPoolSuper(g1h, + "G1 Eden", + eden_space_committed(g1h), /* init_size */ + eden_space_max(g1h), /* max_size */ + false /* support_usage_threshold */) { +} + +MemoryUsage G1EdenPool::get_memory_usage() { + size_t maxSize = max_size(); + size_t used = used_in_bytes(); + size_t committed = eden_space_committed(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +G1SurvivorPool::G1SurvivorPool(G1CollectedHeap* g1h) : + G1MemoryPoolSuper(g1h, + "G1 Survivor", + survivor_space_committed(g1h), /* init_size */ + survivor_space_max(g1h), /* max_size */ + false /* support_usage_threshold */) { +} + +MemoryUsage G1SurvivorPool::get_memory_usage() { + size_t maxSize = max_size(); + size_t used = used_in_bytes(); + size_t committed = survivor_space_committed(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} + +G1OldGenPool::G1OldGenPool(G1CollectedHeap* g1h) : + G1MemoryPoolSuper(g1h, + "G1 Old Gen", + old_space_committed(g1h), /* init_size */ + old_space_max(g1h), /* max_size */ + true /* support_usage_threshold */) { +} + +MemoryUsage G1OldGenPool::get_memory_usage() { + size_t maxSize = max_size(); + size_t used = used_in_bytes(); + size_t committed = old_space_committed(); + + return MemoryUsage(initial_size(), used, committed, maxSize); +} diff --git a/hotspot/src/share/vm/services/g1MemoryPool.hpp b/hotspot/src/share/vm/services/g1MemoryPool.hpp new file mode 100644 index 00000000000..73c12adfb9c --- /dev/null +++ b/hotspot/src/share/vm/services/g1MemoryPool.hpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) 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. + * + */ + +class G1CollectedHeap; + +// This file contains the three classes that represent the memory +// pools of the G1 spaces: G1EdenPool, G1SurvivorPool, and +// G1OldGenPool. In G1, unlike our other GCs, we do not have a +// physical space for each of those spaces. Instead, we allocate +// regions for all three spaces out of a single pool of regions (that +// pool basically covers the entire heap). As a result, the eden, +// survivor, and old gen are considered logical spaces in G1, as each +// is a set of non-contiguous regions. This is also reflected in the +// way we map them to memory pools here. The easiest way to have done +// this would have been to map the entire G1 heap to a single memory +// pool. However, it's helpful to show how large the eden and survivor +// get, as this does affect the performance and behavior of G1. Which +// is why we introduce the three memory pools implemented here. +// +// The above approach inroduces a couple of challenging issues in the +// implementation of the three memory pools: +// +// 1) The used space calculation for a pool is not necessarily +// independent of the others. We can easily get from G1 the overall +// used space in the entire heap, the number of regions in the young +// generation (includes both eden and survivors), and the number of +// survivor regions. So, from that we calculate: +// +// survivor_used = survivor_num * region_size +// eden_used = young_region_num * region_size - survivor_used +// old_gen_used = overall_used - eden_used - survivor_used +// +// Note that survivor_used and eden_used are upper bounds. To get the +// actual value we would have to iterate over the regions and add up +// ->used(). But that'd be expensive. So, we'll accept some lack of +// accuracy for those two. But, we have to be careful when calculating +// old_gen_used, in case we subtract from overall_used more then the +// actual number and our result goes negative. +// +// 2) Calculating the used space is straightforward, as described +// above. However, how do we calculate the committed space, given that +// we allocate space for the eden, survivor, and old gen out of the +// same pool of regions? One way to do this is to use the used value +// as also the committed value for the eden and survivor spaces and +// then calculate the old gen committed space as follows: +// +// old_gen_committed = overall_committed - eden_committed - survivor_committed +// +// Maybe a better way to do that would be to calculate used for eden +// and survivor as a sum of ->used() over their regions and then +// calculate committed as region_num * region_size (i.e., what we use +// to calculate the used space now). This is something to consider +// in the future. +// +// 3) Another decision that is again not straightforward is what is +// the max size that each memory pool can grow to. Right now, we set +// that the committed size for the eden and the survivors and +// calculate the old gen max as follows (basically, it's a similar +// pattern to what we use for the committed space, as described +// above): +// +// old_gen_max = overall_max - eden_max - survivor_max +// +// 4) Now, there is a very subtle issue with all the above. The +// framework will call get_memory_usage() on the three pools +// asynchronously. As a result, each call might get a different value +// for, say, survivor_num which will yield inconsistent values for +// eden_used, survivor_used, and old_gen_used (as survivor_num is used +// in the calculation of all three). This would normally be +// ok. However, it's possible that this might cause the sum of +// eden_used, survivor_used, and old_gen_used to go over the max heap +// size and this seems to sometimes cause JConsole (and maybe other +// clients) to get confused. There's not a really an easy / clean +// solution to this problem, due to the asynchrounous nature of the +// framework. + + +// This class is shared by the three G1 memory pool classes +// (G1EdenPool, G1SurvivorPool, G1OldGenPool). Given that the way we +// calculate used / committed bytes for these three pools is related +// (see comment above), we put the calculations in this class so that +// we can easily share them among the subclasses. +class G1MemoryPoolSuper : public CollectedMemoryPool { +private: + G1CollectedHeap* _g1h; + + // It returns x - y if x > y, 0 otherwise. + // As described in the comment above, some of the inputs to the + // calculations we have to do are obtained concurrently and hence + // may be inconsistent with each other. So, this provides a + // defensive way of performing the subtraction and avoids the value + // going negative (which would mean a very large result, given that + // the parameter are size_t). + static size_t subtract_up_to_zero(size_t x, size_t y) { + if (x > y) { + return x - y; + } else { + return 0; + } + } + +protected: + // Would only be called from subclasses. + G1MemoryPoolSuper(G1CollectedHeap* g1h, + const char* name, + size_t init_size, + size_t max_size, + bool support_usage_threshold); + + // The reason why all the code is in static methods is so that it + // can be safely called from the constructors of the subclasses. + + static size_t overall_committed(G1CollectedHeap* g1h) { + return g1h->capacity(); + } + static size_t overall_used(G1CollectedHeap* g1h) { + return g1h->used_unlocked(); + } + + static size_t eden_space_committed(G1CollectedHeap* g1h); + static size_t eden_space_used(G1CollectedHeap* g1h); + static size_t eden_space_max(G1CollectedHeap* g1h); + + static size_t survivor_space_committed(G1CollectedHeap* g1h); + static size_t survivor_space_used(G1CollectedHeap* g1h); + static size_t survivor_space_max(G1CollectedHeap* g1h); + + static size_t old_space_committed(G1CollectedHeap* g1h); + static size_t old_space_used(G1CollectedHeap* g1h); + static size_t old_space_max(G1CollectedHeap* g1h); + + // The non-static versions are included for convenience. + + size_t eden_space_committed() { + return eden_space_committed(_g1h); + } + size_t eden_space_used() { + return eden_space_used(_g1h); + } + size_t eden_space_max() { + return eden_space_max(_g1h); + } + + size_t survivor_space_committed() { + return survivor_space_committed(_g1h); + } + size_t survivor_space_used() { + return survivor_space_used(_g1h); + } + size_t survivor_space_max() { + return survivor_space_max(_g1h); + } + + size_t old_space_committed() { + return old_space_committed(_g1h); + } + size_t old_space_used() { + return old_space_used(_g1h); + } + size_t old_space_max() { + return old_space_max(_g1h); + } +}; + +// Memory pool that represents the G1 eden. +class G1EdenPool : public G1MemoryPoolSuper { +public: + G1EdenPool(G1CollectedHeap* g1h); + + size_t used_in_bytes() { + return eden_space_used(); + } + size_t max_size() { + return eden_space_max(); + } + MemoryUsage get_memory_usage(); +}; + +// Memory pool that represents the G1 survivor. +class G1SurvivorPool : public G1MemoryPoolSuper { +public: + G1SurvivorPool(G1CollectedHeap* g1h); + + size_t used_in_bytes() { + return survivor_space_used(); + } + size_t max_size() { + return survivor_space_max(); + } + MemoryUsage get_memory_usage(); +}; + +// Memory pool that represents the G1 old gen. +class G1OldGenPool : public G1MemoryPoolSuper { +public: + G1OldGenPool(G1CollectedHeap* g1h); + + size_t used_in_bytes() { + return old_space_used(); + } + size_t max_size() { + return old_space_max(); + } + MemoryUsage get_memory_usage(); +}; diff --git a/hotspot/src/share/vm/services/memoryManager.cpp b/hotspot/src/share/vm/services/memoryManager.cpp index 7b7905c5ab7..f92ba14c184 100644 --- a/hotspot/src/share/vm/services/memoryManager.cpp +++ b/hotspot/src/share/vm/services/memoryManager.cpp @@ -72,6 +72,14 @@ GCMemoryManager* MemoryManager::get_psMarkSweep_memory_manager() { return (GCMemoryManager*) new PSMarkSweepMemoryManager(); } +GCMemoryManager* MemoryManager::get_g1YoungGen_memory_manager() { + return (GCMemoryManager*) new G1YoungGenMemoryManager(); +} + +GCMemoryManager* MemoryManager::get_g1OldGen_memory_manager() { + return (GCMemoryManager*) new G1OldGenMemoryManager(); +} + instanceOop MemoryManager::get_memory_manager_instance(TRAPS) { // Must do an acquire so as to force ordering of subsequent // loads from anything _memory_mgr_obj points to or implies. diff --git a/hotspot/src/share/vm/services/memoryManager.hpp b/hotspot/src/share/vm/services/memoryManager.hpp index 4efc955eb82..955ca8139ce 100644 --- a/hotspot/src/share/vm/services/memoryManager.hpp +++ b/hotspot/src/share/vm/services/memoryManager.hpp @@ -54,7 +54,9 @@ public: ParNew, ConcurrentMarkSweep, PSScavenge, - PSMarkSweep + PSMarkSweep, + G1YoungGen, + G1OldGen }; MemoryManager(); @@ -85,6 +87,8 @@ public: static GCMemoryManager* get_cms_memory_manager(); static GCMemoryManager* get_psScavenge_memory_manager(); static GCMemoryManager* get_psMarkSweep_memory_manager(); + static GCMemoryManager* get_g1YoungGen_memory_manager(); + static GCMemoryManager* get_g1OldGen_memory_manager(); }; @@ -231,3 +235,21 @@ public: MemoryManager::Name kind() { return MemoryManager::PSMarkSweep; } const char* name() { return "PS MarkSweep"; } }; + +class G1YoungGenMemoryManager : public GCMemoryManager { +private: +public: + G1YoungGenMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::G1YoungGen; } + const char* name() { return "G1 Young Generation"; } +}; + +class G1OldGenMemoryManager : public GCMemoryManager { +private: +public: + G1OldGenMemoryManager() : GCMemoryManager() {} + + MemoryManager::Name kind() { return MemoryManager::G1OldGen; } + const char* name() { return "G1 Old Generation"; } +}; diff --git a/hotspot/src/share/vm/services/memoryService.cpp b/hotspot/src/share/vm/services/memoryService.cpp index 1d243828c77..9941a6f0ff4 100644 --- a/hotspot/src/share/vm/services/memoryService.cpp +++ b/hotspot/src/share/vm/services/memoryService.cpp @@ -60,8 +60,8 @@ void MemoryService::set_universe_heap(CollectedHeap* heap) { break; } case CollectedHeap::G1CollectedHeap : { - G1CollectedHeap::g1_unimplemented(); - return; + add_g1_heap_info(G1CollectedHeap::heap()); + break; } #endif // SERIALGC default: { @@ -164,6 +164,19 @@ void MemoryService::add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap) add_psOld_memory_pool(heap->old_gen(), _major_gc_manager); add_psPerm_memory_pool(heap->perm_gen(), _major_gc_manager); } + +void MemoryService::add_g1_heap_info(G1CollectedHeap* g1h) { + assert(UseG1GC, "sanity"); + + _minor_gc_manager = MemoryManager::get_g1YoungGen_memory_manager(); + _major_gc_manager = MemoryManager::get_g1OldGen_memory_manager(); + _managers_list->append(_minor_gc_manager); + _managers_list->append(_major_gc_manager); + + add_g1YoungGen_memory_pool(g1h, _major_gc_manager, _minor_gc_manager); + add_g1OldGen_memory_pool(g1h, _major_gc_manager); + add_g1PermGen_memory_pool(g1h, _major_gc_manager); +} #endif // SERIALGC MemoryPool* MemoryService::add_gen(Generation* gen, @@ -384,6 +397,64 @@ void MemoryService::add_psPerm_memory_pool(PSPermGen* gen, MemoryManager* mgr) { mgr->add_pool(perm_gen); _pools_list->append(perm_gen); } + +void MemoryService::add_g1YoungGen_memory_pool(G1CollectedHeap* g1h, + MemoryManager* major_mgr, + MemoryManager* minor_mgr) { + assert(major_mgr != NULL && minor_mgr != NULL, "should have two managers"); + + G1EdenPool* eden = new G1EdenPool(g1h); + G1SurvivorPool* survivor = new G1SurvivorPool(g1h); + + major_mgr->add_pool(eden); + major_mgr->add_pool(survivor); + minor_mgr->add_pool(eden); + minor_mgr->add_pool(survivor); + _pools_list->append(eden); + _pools_list->append(survivor); +} + +void MemoryService::add_g1OldGen_memory_pool(G1CollectedHeap* g1h, + MemoryManager* mgr) { + assert(mgr != NULL, "should have one manager"); + + G1OldGenPool* old_gen = new G1OldGenPool(g1h); + mgr->add_pool(old_gen); + _pools_list->append(old_gen); +} + +void MemoryService::add_g1PermGen_memory_pool(G1CollectedHeap* g1h, + MemoryManager* mgr) { + assert(mgr != NULL, "should have one manager"); + + CompactingPermGenGen* perm_gen = (CompactingPermGenGen*) g1h->perm_gen(); + PermanentGenerationSpec* spec = perm_gen->spec(); + size_t max_size = spec->max_size() - spec->read_only_size() + - spec->read_write_size(); + MemoryPool* pool = add_space(perm_gen->unshared_space(), + "G1 Perm Gen", + false, /* is_heap */ + max_size, + true /* support_usage_threshold */); + mgr->add_pool(pool); + + // in case we support CDS in G1 + if (UseSharedSpaces) { + pool = add_space(perm_gen->ro_space(), + "G1 Perm Gen [shared-ro]", + false, /* is_heap */ + spec->read_only_size(), + true /* support_usage_threshold */); + mgr->add_pool(pool); + + pool = add_space(perm_gen->rw_space(), + "G1 Perm Gen [shared-rw]", + false, /* is_heap */ + spec->read_write_size(), + true /* support_usage_threshold */); + mgr->add_pool(pool); + } +} #endif // SERIALGC void MemoryService::add_code_heap_memory_pool(CodeHeap* heap) { diff --git a/hotspot/src/share/vm/services/memoryService.hpp b/hotspot/src/share/vm/services/memoryService.hpp index 52b76a72937..44823ae426f 100644 --- a/hotspot/src/share/vm/services/memoryService.hpp +++ b/hotspot/src/share/vm/services/memoryService.hpp @@ -40,6 +40,7 @@ class GenCollectedHeap; class ParallelScavengeHeap; class CompactingPermGenGen; class CMSPermGenGen; +class G1CollectedHeap; // VM Monitoring and Management Support @@ -88,6 +89,13 @@ private: static void add_psPerm_memory_pool(PSPermGen* perm, MemoryManager* mgr); + static void add_g1YoungGen_memory_pool(G1CollectedHeap* g1h, + MemoryManager* major_mgr, + MemoryManager* minor_mgr); + static void add_g1OldGen_memory_pool(G1CollectedHeap* g1h, + MemoryManager* mgr); + static void add_g1PermGen_memory_pool(G1CollectedHeap* g1h, + MemoryManager* mgr); static MemoryPool* add_space(ContiguousSpace* space, const char* name, @@ -111,6 +119,7 @@ private: static void add_gen_collected_heap_info(GenCollectedHeap* heap); static void add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap); + static void add_g1_heap_info(G1CollectedHeap* g1h); public: static void set_universe_heap(CollectedHeap* heap); From 4aba621c221a0ceef94454b96f5ee86a0c4c4c42 Mon Sep 17 00:00:00 2001 From: Paul Hohensee Date: Fri, 20 Nov 2009 16:22:38 -0500 Subject: [PATCH 21/61] 6900899: vm fails to start when -Xmx value is less than OldSize + NewSize Set minimum heap size to min(OldSize + NewSize, MaxHeapSize) in Arguments::set_heap_size(). Reviewed-by: kvn, ysr, tonyp --- hotspot/src/share/vm/runtime/arguments.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index c57d61b259f..ae82b8def40 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -1378,9 +1378,15 @@ void Arguments::set_heap_size() { // or -Xms, then set it as fraction of the size of physical memory, // respecting the maximum and minimum sizes of the heap. if (FLAG_IS_DEFAULT(InitialHeapSize)) { + julong reasonable_minimum = (julong)(OldSize + NewSize); + + reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize); + + reasonable_minimum = os::allocatable_physical_memory(reasonable_minimum); + julong reasonable_initial = phys_mem / InitialRAMFraction; - reasonable_initial = MAX2(reasonable_initial, (julong)(OldSize + NewSize)); + reasonable_initial = MAX2(reasonable_initial, reasonable_minimum); reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize); reasonable_initial = os::allocatable_physical_memory(reasonable_initial); @@ -1388,14 +1394,10 @@ void Arguments::set_heap_size() { if (PrintGCDetails && Verbose) { // Cannot use gclog_or_tty yet. tty->print_cr(" Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial); + tty->print_cr(" Minimum heap size " SIZE_FORMAT, (uintx)reasonable_minimum); } FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial); - - // Subsequent ergonomics code may expect min_heap_size to be set - // if InitialHeapSize is. Use whatever the current values are - // for OldSize and NewSize, whether or not they were set on the - // command line. - set_min_heap_size(OldSize + NewSize); + set_min_heap_size((uintx)reasonable_minimum); } } From 5a239d996b7285c5f863b072f7ca374e9a9fde6e Mon Sep 17 00:00:00 2001 From: John Cuthbertson Date: Tue, 24 Nov 2009 15:19:30 -0800 Subject: [PATCH 22/61] 6899058: G1: Internal error in ptrQueue.cpp:201 in nightly tests Fixes a race on the dirty card queue completed buffer list between worker thread(s) performing a flush of a deferred store barrier (enqueueing a newly completed buffer) and worker thread(s) in the RSet updating code claiming completed buffers. Removed the routine that removes elements from the completed update buffer queue using a CAS. Reviewed-by: ysr, tonyp --- .../gc_implementation/g1/dirtyCardQueue.cpp | 35 +++---------------- .../gc_implementation/g1/dirtyCardQueue.hpp | 9 ++--- 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp index 7372a4a787c..afe4990fc67 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp @@ -155,7 +155,7 @@ bool DirtyCardQueueSet::mut_process_buffer(void** buf) { } DirtyCardQueueSet::CompletedBufferNode* -DirtyCardQueueSet::get_completed_buffer_lock(int stop_at) { +DirtyCardQueueSet::get_completed_buffer(int stop_at) { CompletedBufferNode* nd = NULL; MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); @@ -175,28 +175,6 @@ DirtyCardQueueSet::get_completed_buffer_lock(int stop_at) { 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) { @@ -222,15 +200,10 @@ apply_closure_to_completed_buffer_helper(int worker_i, 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"); + CompletedBufferNode* 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; diff --git a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp index 7a6f3f27bbd..f986d21af61 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp @@ -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 @@ -120,12 +120,13 @@ public: // 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); - CompletedBufferNode* get_completed_buffer_CAS(); - CompletedBufferNode* get_completed_buffer_lock(int stop_at); + CompletedBufferNode* get_completed_buffer(int stop_at); + // Applies the current closure to all completed buffers, // non-consumptively. void apply_closure_to_all_completed_buffers(); From b409d164779c63d3e9c083043c10e40387dba235 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Wed, 25 Nov 2009 08:37:04 -0800 Subject: [PATCH 23/61] 6888880: JKernel VM to inject the sun.jkernel.DownloadManager as a boot classloader hook Call sun.jkernel.DownloadManager.setBootClassLoaderHook during the kernel VM initialization Reviewed-by: alanb, coleenp, acorn --- hotspot/src/share/vm/classfile/vmSymbols.hpp | 1 + hotspot/src/share/vm/runtime/thread.cpp | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index bb9f3076a96..fdc8786e551 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -104,6 +104,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") \ diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index 38e3ac438b3..5b2b90dedcb 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -884,6 +884,22 @@ static void call_initializeSystemClass(TRAPS) { vmSymbolHandles::void_method_signature(), CHECK); } +#ifdef KERNEL +static void set_jkernel_boot_classloader_hook(TRAPS) { + klassOop k = SystemDictionary::sun_jkernel_DownloadManager_klass(); + instanceKlassHandle klass (THREAD, k); + + if (k == NULL) { + // sun.jkernel.DownloadManager may not present in the JDK; just return + return; + } + + JavaValue result(T_VOID); + JavaCalls::call_static(&result, klass, vmSymbolHandles::setBootClassLoaderHook_name(), + vmSymbolHandles::void_method_signature(), CHECK); +} +#endif // KERNEL + static void reset_vm_info_property(TRAPS) { // the vm info string ResourceMark rm(THREAD); @@ -3102,6 +3118,12 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION)); } +#ifdef KERNEL + if (JDK_Version::is_gte_jdk17x_version()) { + set_jkernel_boot_classloader_hook(THREAD); + } +#endif // KERNEL + #ifndef SERIALGC // Support for ConcurrentMarkSweep. This should be cleaned up // and better encapsulated. The ugly nested if test would go away From a3b6bcb4a8c4915e79ad6942836f6778f2ce0f23 Mon Sep 17 00:00:00 2001 From: Changpeng Fang Date: Wed, 25 Nov 2009 12:09:02 -0800 Subject: [PATCH 24/61] 6904191: OptimizeStringConcat should be product instead of experimental Make OptimizeStringConcat a product VM option(contributed by never) Reviewed-by: never --- hotspot/src/share/vm/opto/c2_globals.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index 8c650eeaf10..b68b66342cb 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -394,7 +394,7 @@ product(bool, UseOptoBiasInlining, true, \ "Generate biased locking code in C2 ideal graph") \ \ - experimental(bool, OptimizeStringConcat, false, \ + product(bool, OptimizeStringConcat, false, \ "Optimize the construction of Strings by StringBuilder") \ \ notproduct(bool, PrintOptimizeStringConcat, false, \ From 63cc2211edc386eb4e83b5940d95a38bb45abcd3 Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Fri, 27 Nov 2009 07:56:58 -0800 Subject: [PATCH 25/61] 6896043: first round of zero fixes Reviewed-by: kvn --- .../src/cpu/zero/vm/cppInterpreter_zero.cpp | 14 +++++++++ hotspot/src/cpu/zero/vm/frame_zero.cpp | 31 +++++-------------- hotspot/src/cpu/zero/vm/frame_zero.hpp | 5 +-- hotspot/src/cpu/zero/vm/globals_zero.hpp | 1 - .../src/cpu/zero/vm/sharedRuntime_zero.cpp | 9 +++++- hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp | 4 +-- .../vm/interpreter/bytecodeInterpreter.cpp | 13 ++------ hotspot/src/share/vm/prims/jni.cpp | 15 +++++++++ .../vm/prims/jvmtiManageCapabilities.cpp | 2 ++ hotspot/src/share/vm/runtime/os.hpp | 19 +++++------- 10 files changed, 59 insertions(+), 54 deletions(-) diff --git a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp index 8c99fbf4556..389269c5b54 100644 --- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp +++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp @@ -204,6 +204,20 @@ void CppInterpreter::native_entry(methodOop method, intptr_t UNUSED, TRAPS) { 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; diff --git a/hotspot/src/cpu/zero/vm/frame_zero.cpp b/hotspot/src/cpu/zero/vm/frame_zero.cpp index 1b3cafdc589..323912e1cb3 100644 --- a/hotspot/src/cpu/zero/vm/frame_zero.cpp +++ b/hotspot/src/cpu/zero/vm/frame_zero.cpp @@ -36,11 +36,8 @@ bool frame::is_interpreted_frame() const { return zeroframe()->is_interpreter_frame(); } -bool frame::is_fake_stub_frame() const { - return zeroframe()->is_fake_stub_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(), @@ -50,15 +47,10 @@ frame frame::sender_for_entry_frame(RegisterMap *map) const { return frame(sender_sp(), sp() + 1); } -frame frame::sender_for_interpreter_frame(RegisterMap *map) const { - return frame(sender_sp(), sp() + 1); -} - -frame frame::sender_for_compiled_frame(RegisterMap *map) const { - return frame(sender_sp(), sp() + 1); -} - -frame frame::sender_for_fake_stub_frame(RegisterMap *map) const { +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); } @@ -69,17 +61,8 @@ frame frame::sender(RegisterMap* map) const { if (is_entry_frame()) return sender_for_entry_frame(map); - - if (is_interpreted_frame()) - return sender_for_interpreter_frame(map); - - if (is_compiled_frame()) - return sender_for_compiled_frame(map); - - if (is_fake_stub_frame()) - return sender_for_fake_stub_frame(map); - - ShouldNotReachHere(); + else + return sender_for_nonentry_frame(map); } #ifdef CC_INTERP diff --git a/hotspot/src/cpu/zero/vm/frame_zero.hpp b/hotspot/src/cpu/zero/vm/frame_zero.hpp index 81b6314571d..84d248fe0c4 100644 --- a/hotspot/src/cpu/zero/vm/frame_zero.hpp +++ b/hotspot/src/cpu/zero/vm/frame_zero.hpp @@ -65,10 +65,7 @@ } public: - bool is_fake_stub_frame() const; - - public: - frame sender_for_fake_stub_frame(RegisterMap* map) const; + frame sender_for_nonentry_frame(RegisterMap* map) const; public: void zero_print_on_error(int index, diff --git a/hotspot/src/cpu/zero/vm/globals_zero.hpp b/hotspot/src/cpu/zero/vm/globals_zero.hpp index ca61623cade..e8a32f1d47a 100644 --- a/hotspot/src/cpu/zero/vm/globals_zero.hpp +++ b/hotspot/src/cpu/zero/vm/globals_zero.hpp @@ -36,7 +36,6 @@ define_pd_global(bool, UncommonNullCast, true); define_pd_global(intx, CodeEntryAlignment, 32); define_pd_global(intx, InlineFrequencyCount, 100); -define_pd_global(intx, InlineSmallCode, 1000); define_pd_global(intx, PreInflateSpin, 10); define_pd_global(intx, StackYellowPages, 2); diff --git a/hotspot/src/cpu/zero/vm/sharedRuntime_zero.cpp b/hotspot/src/cpu/zero/vm/sharedRuntime_zero.cpp index 7bb4614980f..5adb87aef70 100644 --- a/hotspot/src/cpu/zero/vm/sharedRuntime_zero.cpp +++ b/hotspot/src/cpu/zero/vm/sharedRuntime_zero.cpp @@ -1,6 +1,6 @@ /* * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. - * Copyright 2007, 2008 Red Hat, Inc. + * 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 @@ -61,7 +61,14 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, 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, diff --git a/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp b/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp index 3337219a370..2473ebf7ae6 100644 --- a/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp +++ b/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp @@ -1,6 +1,6 @@ /* * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. - * Copyright 2008 Red Hat, Inc. + * 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 @@ -41,7 +41,7 @@ // | ... | class SharkFrame : public ZeroFrame { - friend class SharkFunction; + friend class SharkStack; private: SharkFrame() : ZeroFrame() { diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp index ac46f4ab5e1..1f0adb487ed 100644 --- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp @@ -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 @@ run: 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); diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp index 20484da07a9..9ea39067772 100644 --- a/hotspot/src/share/vm/prims/jni.cpp +++ b/hotspot/src/share/vm/prims/jni.cpp @@ -3231,6 +3231,21 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v 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 == 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. diff --git a/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp index 7651a67b0fc..82ddb4adf0d 100644 --- a/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp +++ b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp @@ -115,8 +115,10 @@ jvmtiCapabilities JvmtiManageCapabilities::init_onload_capabilities() { jvmtiCapabilities jc; memset(&jc, 0, sizeof(jc)); +#ifndef CC_INTERP jc.can_pop_frame = 1; jc.can_force_early_return = 1; +#endif // !CC_INTERP jc.can_get_source_debug_extension = 1; jc.can_access_local_variables = 1; jc.can_maintain_original_method_order = 1; diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index 41408583200..a3299c70a16 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -294,19 +294,16 @@ class os: AllStatic { } static bool is_memory_serialize_page(JavaThread *thread, address addr) { - address thr_addr; if (UseMembar) return false; - // Calculate thread specific address + // Previously this function calculated the exact address of this + // thread's serialize page, and checked if the faulting address + // was equal. However, some platforms mask off faulting addresses + // to the page size, so now we just check that the address is + // within the page. This makes the thread argument unnecessary, + // but we retain the NULL check to preserve existing behaviour. if (thread == NULL) return false; - // TODO-FIXME: some platforms mask off faulting addresses to the base pagesize. - // Instead of using a test for equality we should probably use something - // of the form: - // return ((_mem_serialize_page ^ addr) & -pagesize) == 0 - // - thr_addr = (address)(((uintptr_t)thread >> - get_serialize_page_shift_count()) & - get_serialize_page_mask()) + (uintptr_t)_mem_serialize_page; - return (thr_addr == addr); + address page = (address) _mem_serialize_page; + return addr >= page && addr < (page + os::vm_page_size()); } static void block_on_serialize_page_trap(); From 10232cb341e23967255de13ec160079eba5fa0b2 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Tue, 1 Dec 2009 22:29:02 -0500 Subject: [PATCH 26/61] 6822370: ReentrantReadWriteLock: threads hung when there are no threads holding onto the lock (Netra x4450) This day one bug is caused by missing memory barriers in various Parker::park() paths that can result in lost wakeups and hangs. Reviewed-by: dice, acorn --- hotspot/src/os/linux/vm/os_linux.cpp | 3 +++ hotspot/src/os/solaris/vm/os_solaris.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index 281e81c264f..6aa3ebc0a76 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -4683,6 +4683,7 @@ void Parker::park(bool isAbsolute, jlong time) { // Return immediately if a permit is available. if (_counter > 0) { _counter = 0 ; + OrderAccess::fence(); return ; } @@ -4725,6 +4726,7 @@ void Parker::park(bool isAbsolute, jlong time) { _counter = 0; status = pthread_mutex_unlock(_mutex); assert (status == 0, "invariant") ; + OrderAccess::fence(); return; } @@ -4765,6 +4767,7 @@ void Parker::park(bool isAbsolute, jlong time) { jt->java_suspend_self(); } + OrderAccess::fence(); } void Parker::unpark() { diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp index 13abbf9d4b9..b85bcef8cdd 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.cpp +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp @@ -5803,6 +5803,7 @@ void Parker::park(bool isAbsolute, jlong time) { // Return immediately if a permit is available. if (_counter > 0) { _counter = 0 ; + OrderAccess::fence(); return ; } @@ -5846,6 +5847,7 @@ void Parker::park(bool isAbsolute, jlong time) { _counter = 0; status = os::Solaris::mutex_unlock(_mutex); assert (status == 0, "invariant") ; + OrderAccess::fence(); return; } @@ -5892,6 +5894,7 @@ void Parker::park(bool isAbsolute, jlong time) { jt->java_suspend_self(); } + OrderAccess::fence(); } void Parker::unpark() { From 8a9580b1a18e455c3994367a360e12264dc9c2c4 Mon Sep 17 00:00:00 2001 From: Changpeng Fang Date: Wed, 2 Dec 2009 13:29:00 -0800 Subject: [PATCH 27/61] 6901572: JVM 1.6.16 crash on loops: assert(has_node(i),"") Skip the secondary induction variable handling if it is dead Reviewed-by: never, kvn --- hotspot/src/share/vm/opto/loopnode.cpp | 3 +- hotspot/test/compiler/6901572/Test.java | 55 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 hotspot/test/compiler/6901572/Test.java diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp index a1d87225203..883fa75c722 100644 --- a/hotspot/src/share/vm/opto/loopnode.cpp +++ b/hotspot/src/share/vm/opto/loopnode.cpp @@ -1279,7 +1279,8 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) { // 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 diff --git a/hotspot/test/compiler/6901572/Test.java b/hotspot/test/compiler/6901572/Test.java new file mode 100644 index 00000000000..1139161b1cf --- /dev/null +++ b/hotspot/test/compiler/6901572/Test.java @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/** + * @test + * @bug 6901572 + * @summary JVM 1.6.16 crash on loops: assert(has_node(i),"") + * + * @run main/othervm Test + */ + + +public class Test { + + public static void main(String[] args) { + for (int i = 0; i < 2; i++) + NestedLoop(); + } + + public static long NestedLoop() { + final int n = 50; + long startTime = System.currentTimeMillis(); + int x = 0; + for(int a = 0; a < n; a++) + for(int b = 0; b < n; b++) + for(int c = 0; c < n; c++) + for(int d = 0; d < n; d++) + for(int e = 0; e < n; e++) + for(int f = 0; f < n; f++) + x++; + long stopTime = System.currentTimeMillis(); + + return stopTime - startTime; + } +} From d875a8e0f168c10adaf147e2e1fff0ba50672618 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Thu, 3 Dec 2009 15:01:57 -0800 Subject: [PATCH 28/61] 6906727: UseCompressedOops: some card-marking fixes related to object arrays Introduced a new write_ref_array(HeapWords* start, size_t count) method that does the requisite MemRegion range calculation so (some of the) clients of the erstwhile write_ref_array(MemRegion mr) do not need to worry. This removed all external uses of array_size(), which was also simplified and made private. Asserts were added to catch other possible issues. Further, less essential, fixes stemming from this investigation are deferred to CR 6904516 (to follow shortly in hs17). Reviewed-by: kvn, coleenp, jmasa --- .../src/share/vm/classfile/javaClasses.cpp | 3 +- hotspot/src/share/vm/includeDB_core | 2 +- hotspot/src/share/vm/memory/barrierSet.cpp | 9 +--- hotspot/src/share/vm/memory/barrierSet.hpp | 13 +++--- .../src/share/vm/memory/barrierSet.inline.hpp | 30 +++++++++++++ .../src/share/vm/memory/cardTableModRefBS.cpp | 4 ++ hotspot/src/share/vm/oops/objArrayKlass.cpp | 6 +-- hotspot/src/share/vm/oops/objArrayOop.hpp | 43 +++++++++++-------- 8 files changed, 74 insertions(+), 36 deletions(-) diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index cc0db7a173b..a5cef1683b3 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -1124,8 +1124,7 @@ class BacktraceBuilder: public StackObj { if (_dirty && _methods != NULL) { 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()); _dirty = false; } } diff --git a/hotspot/src/share/vm/includeDB_core b/hotspot/src/share/vm/includeDB_core index e96a5e9be1a..a8bc76aad0b 100644 --- a/hotspot/src/share/vm/includeDB_core +++ b/hotspot/src/share/vm/includeDB_core @@ -289,7 +289,7 @@ attachListener.hpp allocation.hpp 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 diff --git a/hotspot/src/share/vm/memory/barrierSet.cpp b/hotspot/src/share/vm/memory/barrierSet.cpp index 07ff7a0fc21..8cd80b7c437 100644 --- a/hotspot/src/share/vm/memory/barrierSet.cpp +++ b/hotspot/src/share/vm/memory/barrierSet.cpp @@ -41,11 +41,6 @@ void BarrierSet::static_write_ref_array_pre(HeapWord* start, size_t count) { // 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 --git a/hotspot/src/share/vm/memory/barrierSet.hpp b/hotspot/src/share/vm/memory/barrierSet.hpp index 0fc6a006140..c624f2506e2 100644 --- a/hotspot/src/share/vm/memory/barrierSet.hpp +++ b/hotspot/src/share/vm/memory/barrierSet.hpp @@ -121,17 +121,20 @@ public: 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) {} + // Below MemRegion mr is expected to be HeapWord-aligned 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 --git a/hotspot/src/share/vm/memory/barrierSet.inline.hpp b/hotspot/src/share/vm/memory/barrierSet.inline.hpp index edcb551bdcd..ddc398348ca 100644 --- a/hotspot/src/share/vm/memory/barrierSet.inline.hpp +++ b/hotspot/src/share/vm/memory/barrierSet.inline.hpp @@ -43,6 +43,8 @@ void BarrierSet::write_ref_field(void* field, oop new_val) { } void BarrierSet::write_ref_array(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" ); if (kind() == CardTableModRef) { ((CardTableModRefBS*)this)->inline_write_ref_array(mr); } else { @@ -50,6 +52,34 @@ void BarrierSet::write_ref_array(MemRegion 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. In the event that 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, much like the pre-barrier one above, 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 --git a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp index c036a355514..2deb7da83f0 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp @@ -511,6 +511,8 @@ void CardTableModRefBS::mod_oop_in_space_iterate(Space* sp, } 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::dirty_MemRegion(MemRegion mr) { } 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); diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp index 212126490cd..68556a5a0cf 100644 --- a/hotspot/src/share/vm/oops/objArrayKlass.cpp +++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp @@ -127,16 +127,14 @@ template void objArrayKlass::do_copy(arrayOop s, T* src, // 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, diff --git a/hotspot/src/share/vm/oops/objArrayOop.hpp b/hotspot/src/share/vm/oops/objArrayOop.hpp index 626f398a6be..a00eb7c1a9b 100644 --- a/hotspot/src/share/vm/oops/objArrayOop.hpp +++ b/hotspot/src/share/vm/oops/objArrayOop.hpp @@ -37,6 +37,32 @@ class objArrayOopDesc : public arrayOopDesc { 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 = (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,29 +93,12 @@ class objArrayOopDesc : public arrayOopDesc { // 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; - } - } - // special iterators for index ranges, returns size of object #define ObjArrayOop_OOP_ITERATE_DECL(OopClosureType, nv_suffix) \ int oop_iterate_range(OopClosureType* blk, int start, int end); From 8a125d9913ec5c7f3647e87f3befed3b58b1f3e0 Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Fri, 4 Dec 2009 07:44:35 -0500 Subject: [PATCH 29/61] 6880903: G1: G1 reports incorrect Runtime.maxMemory() G1 reports committed memory instead of reserved memory from the Runtime.maxMemory() method Reviewed-by: ysr, jmasa --- .../gc_implementation/g1/g1CollectedHeap.cpp | 2 +- .../gc_implementation/g1/g1CollectedHeap.hpp | 2 +- .../src/share/vm/services/g1MemoryPool.cpp | 29 ++++++++++--------- .../src/share/vm/services/g1MemoryPool.hpp | 3 ++ 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index f9cce8b4e6a..0acb1761096 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -2130,7 +2130,7 @@ size_t G1CollectedHeap::large_typearray_limit() { } size_t G1CollectedHeap::max_capacity() const { - return _g1_committed.byte_size(); + return g1_reserved_obj_bytes(); } jlong G1CollectedHeap::millis_since_last_gc() { diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 5e07828267f..3c071beed9a 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -692,7 +692,7 @@ public: // 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 diff --git a/hotspot/src/share/vm/services/g1MemoryPool.cpp b/hotspot/src/share/vm/services/g1MemoryPool.cpp index 1a1c337a036..9a124da7a5a 100644 --- a/hotspot/src/share/vm/services/g1MemoryPool.cpp +++ b/hotspot/src/share/vm/services/g1MemoryPool.cpp @@ -96,7 +96,7 @@ size_t G1MemoryPoolSuper::old_space_used(G1CollectedHeap* g1h) { // See the comment at the top of g1MemoryPool.hpp size_t G1MemoryPoolSuper::old_space_max(G1CollectedHeap* g1h) { - size_t max = g1h->g1_reserved_obj_bytes(); + size_t max = overall_max(g1h); size_t eden_max = eden_space_max(g1h); size_t survivor_max = survivor_space_max(g1h); max = subtract_up_to_zero(max, eden_max); @@ -113,11 +113,12 @@ G1EdenPool::G1EdenPool(G1CollectedHeap* g1h) : } MemoryUsage G1EdenPool::get_memory_usage() { - size_t maxSize = max_size(); - size_t used = used_in_bytes(); - size_t committed = eden_space_committed(); + size_t initial_sz = initial_size(); + size_t max_sz = max_size(); + size_t used = used_in_bytes(); + size_t committed = eden_space_committed(); - return MemoryUsage(initial_size(), used, committed, maxSize); + return MemoryUsage(initial_sz, used, committed, max_sz); } G1SurvivorPool::G1SurvivorPool(G1CollectedHeap* g1h) : @@ -129,11 +130,12 @@ G1SurvivorPool::G1SurvivorPool(G1CollectedHeap* g1h) : } MemoryUsage G1SurvivorPool::get_memory_usage() { - size_t maxSize = max_size(); - size_t used = used_in_bytes(); - size_t committed = survivor_space_committed(); + size_t initial_sz = initial_size(); + size_t max_sz = max_size(); + size_t used = used_in_bytes(); + size_t committed = survivor_space_committed(); - return MemoryUsage(initial_size(), used, committed, maxSize); + return MemoryUsage(initial_sz, used, committed, max_sz); } G1OldGenPool::G1OldGenPool(G1CollectedHeap* g1h) : @@ -145,9 +147,10 @@ G1OldGenPool::G1OldGenPool(G1CollectedHeap* g1h) : } MemoryUsage G1OldGenPool::get_memory_usage() { - size_t maxSize = max_size(); - size_t used = used_in_bytes(); - size_t committed = old_space_committed(); + size_t initial_sz = initial_size(); + size_t max_sz = max_size(); + size_t used = used_in_bytes(); + size_t committed = old_space_committed(); - return MemoryUsage(initial_size(), used, committed, maxSize); + return MemoryUsage(initial_sz, used, committed, max_sz); } diff --git a/hotspot/src/share/vm/services/g1MemoryPool.hpp b/hotspot/src/share/vm/services/g1MemoryPool.hpp index 73c12adfb9c..12b3662a1c7 100644 --- a/hotspot/src/share/vm/services/g1MemoryPool.hpp +++ b/hotspot/src/share/vm/services/g1MemoryPool.hpp @@ -137,6 +137,9 @@ protected: static size_t overall_used(G1CollectedHeap* g1h) { return g1h->used_unlocked(); } + static size_t overall_max(G1CollectedHeap* g1h) { + return g1h->g1_reserved_obj_bytes(); + } static size_t eden_space_committed(G1CollectedHeap* g1h); static size_t eden_space_used(G1CollectedHeap* g1h); From 911a80e5d516bb78426997b6533ee7272e62a0f2 Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Fri, 4 Dec 2009 07:44:41 -0500 Subject: [PATCH 30/61] 6906565: G1: deal with compilation warning in g1MemoryPool.hpp Size_t max_size() hides size_t max_size() const. Reviewed-by: jmasa, ysr --- .../src/share/vm/services/g1MemoryPool.cpp | 6 +-- .../src/share/vm/services/g1MemoryPool.hpp | 54 ++++--------------- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/hotspot/src/share/vm/services/g1MemoryPool.cpp b/hotspot/src/share/vm/services/g1MemoryPool.cpp index 9a124da7a5a..6d19adfcdc8 100644 --- a/hotspot/src/share/vm/services/g1MemoryPool.cpp +++ b/hotspot/src/share/vm/services/g1MemoryPool.cpp @@ -116,7 +116,7 @@ MemoryUsage G1EdenPool::get_memory_usage() { size_t initial_sz = initial_size(); size_t max_sz = max_size(); size_t used = used_in_bytes(); - size_t committed = eden_space_committed(); + size_t committed = eden_space_committed(_g1h); return MemoryUsage(initial_sz, used, committed, max_sz); } @@ -133,7 +133,7 @@ MemoryUsage G1SurvivorPool::get_memory_usage() { size_t initial_sz = initial_size(); size_t max_sz = max_size(); size_t used = used_in_bytes(); - size_t committed = survivor_space_committed(); + size_t committed = survivor_space_committed(_g1h); return MemoryUsage(initial_sz, used, committed, max_sz); } @@ -150,7 +150,7 @@ MemoryUsage G1OldGenPool::get_memory_usage() { size_t initial_sz = initial_size(); size_t max_sz = max_size(); size_t used = used_in_bytes(); - size_t committed = old_space_committed(); + size_t committed = old_space_committed(_g1h); return MemoryUsage(initial_sz, used, committed, max_sz); } diff --git a/hotspot/src/share/vm/services/g1MemoryPool.hpp b/hotspot/src/share/vm/services/g1MemoryPool.hpp index 12b3662a1c7..cfc61e5f1ac 100644 --- a/hotspot/src/share/vm/services/g1MemoryPool.hpp +++ b/hotspot/src/share/vm/services/g1MemoryPool.hpp @@ -103,8 +103,6 @@ class G1CollectedHeap; // we can easily share them among the subclasses. class G1MemoryPoolSuper : public CollectedMemoryPool { private: - G1CollectedHeap* _g1h; - // It returns x - y if x > y, 0 otherwise. // As described in the comment above, some of the inputs to the // calculations we have to do are obtained concurrently and hence @@ -121,6 +119,8 @@ private: } protected: + G1CollectedHeap* _g1h; + // Would only be called from subclasses. G1MemoryPoolSuper(G1CollectedHeap* g1h, const char* name, @@ -152,38 +152,6 @@ protected: static size_t old_space_committed(G1CollectedHeap* g1h); static size_t old_space_used(G1CollectedHeap* g1h); static size_t old_space_max(G1CollectedHeap* g1h); - - // The non-static versions are included for convenience. - - size_t eden_space_committed() { - return eden_space_committed(_g1h); - } - size_t eden_space_used() { - return eden_space_used(_g1h); - } - size_t eden_space_max() { - return eden_space_max(_g1h); - } - - size_t survivor_space_committed() { - return survivor_space_committed(_g1h); - } - size_t survivor_space_used() { - return survivor_space_used(_g1h); - } - size_t survivor_space_max() { - return survivor_space_max(_g1h); - } - - size_t old_space_committed() { - return old_space_committed(_g1h); - } - size_t old_space_used() { - return old_space_used(_g1h); - } - size_t old_space_max() { - return old_space_max(_g1h); - } }; // Memory pool that represents the G1 eden. @@ -192,10 +160,10 @@ public: G1EdenPool(G1CollectedHeap* g1h); size_t used_in_bytes() { - return eden_space_used(); + return eden_space_used(_g1h); } - size_t max_size() { - return eden_space_max(); + size_t max_size() const { + return eden_space_max(_g1h); } MemoryUsage get_memory_usage(); }; @@ -206,10 +174,10 @@ public: G1SurvivorPool(G1CollectedHeap* g1h); size_t used_in_bytes() { - return survivor_space_used(); + return survivor_space_used(_g1h); } - size_t max_size() { - return survivor_space_max(); + size_t max_size() const { + return survivor_space_max(_g1h); } MemoryUsage get_memory_usage(); }; @@ -220,10 +188,10 @@ public: G1OldGenPool(G1CollectedHeap* g1h); size_t used_in_bytes() { - return old_space_used(); + return old_space_used(_g1h); } - size_t max_size() { - return old_space_max(); + size_t max_size() const { + return old_space_max(_g1h); } MemoryUsage get_memory_usage(); }; From 203cd9408aa46b3406583aa8258d78d7a1f05efe Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Mon, 7 Dec 2009 14:22:34 -0500 Subject: [PATCH 31/61] 6904967: G1: some CollectionUsageThreshold tests fail Ensure that max and committed are non-zero (currently: at least as large as the region size). Reviewed-by: iveresov, mchung --- hotspot/src/share/vm/services/g1MemoryPool.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/services/g1MemoryPool.cpp b/hotspot/src/share/vm/services/g1MemoryPool.cpp index 6d19adfcdc8..7b73f7c79ba 100644 --- a/hotspot/src/share/vm/services/g1MemoryPool.cpp +++ b/hotspot/src/share/vm/services/g1MemoryPool.cpp @@ -40,7 +40,7 @@ G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h, // See the comment at the top of g1MemoryPool.hpp size_t G1MemoryPoolSuper::eden_space_committed(G1CollectedHeap* g1h) { - return eden_space_used(g1h); + return MAX2(eden_space_used(g1h), (size_t) HeapRegion::GrainBytes); } // See the comment at the top of g1MemoryPool.hpp @@ -54,12 +54,14 @@ size_t G1MemoryPoolSuper::eden_space_used(G1CollectedHeap* g1h) { // See the comment at the top of g1MemoryPool.hpp size_t G1MemoryPoolSuper::eden_space_max(G1CollectedHeap* g1h) { + // This should ensure that it returns a value no smaller than the + // region size. Currently, eden_space_committed() guarantees that. return eden_space_committed(g1h); } // See the comment at the top of g1MemoryPool.hpp size_t G1MemoryPoolSuper::survivor_space_committed(G1CollectedHeap* g1h) { - return survivor_space_used(g1h); + return MAX2(survivor_space_used(g1h), (size_t) HeapRegion::GrainBytes); } // See the comment at the top of g1MemoryPool.hpp @@ -71,6 +73,8 @@ size_t G1MemoryPoolSuper::survivor_space_used(G1CollectedHeap* g1h) { // See the comment at the top of g1MemoryPool.hpp size_t G1MemoryPoolSuper::survivor_space_max(G1CollectedHeap* g1h) { + // This should ensure that it returns a value no smaller than the + // region size. Currently, survivor_space_committed() guarantees that. return survivor_space_committed(g1h); } @@ -81,6 +85,7 @@ size_t G1MemoryPoolSuper::old_space_committed(G1CollectedHeap* g1h) { size_t survivor_committed = survivor_space_committed(g1h); committed = subtract_up_to_zero(committed, eden_committed); committed = subtract_up_to_zero(committed, survivor_committed); + committed = MAX2(committed, (size_t) HeapRegion::GrainBytes); return committed; } @@ -101,6 +106,7 @@ size_t G1MemoryPoolSuper::old_space_max(G1CollectedHeap* g1h) { size_t survivor_max = survivor_space_max(g1h); max = subtract_up_to_zero(max, eden_max); max = subtract_up_to_zero(max, survivor_max); + max = MAX2(max, (size_t) HeapRegion::GrainBytes); return max; } From d090b4fe29044f548d8716223c71b08f11160bf1 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Tue, 8 Dec 2009 15:12:17 -0800 Subject: [PATCH 32/61] 6908208: UseCompressedOops: array_size() returns incorrect size for MAX_INT object array following 6906727 In array_size() cast to an unsigned to avoid overflow of intermediate value. Reviewed-by: kvn, tonyp, jmasa, jcoomes, coleenp --- hotspot/src/share/vm/oops/objArrayOop.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/oops/objArrayOop.hpp b/hotspot/src/share/vm/oops/objArrayOop.hpp index a00eb7c1a9b..1c1764a8751 100644 --- a/hotspot/src/share/vm/oops/objArrayOop.hpp +++ b/hotspot/src/share/vm/oops/objArrayOop.hpp @@ -58,7 +58,7 @@ private: old_res = align_size_up(length, OopsPerHeapWord)/OopsPerHeapWord; } #endif // ASSERT - int res = (length + OopsPerHeapWord - 1)/OopsPerHeapWord; + int res = ((uint)length + OopsPerHeapWord - 1)/OopsPerHeapWord; assert(res == old_res, "Inconsistency between old and new."); return res; } @@ -96,7 +96,11 @@ private: static int object_size(int length) { // This returns the object size in HeapWords. - return align_object_size(header_size() + array_size(length)); + 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 From dfbb0bf3e28873ee5e15fa9d577de4b9ab96ab49 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Tue, 8 Dec 2009 16:27:21 -0800 Subject: [PATCH 33/61] 6908167: jbb2005, OptimizeStringConcat causes assert in EA Reviewed-by: kvn --- hotspot/src/share/vm/opto/graphKit.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp index 1b5eb54422f..3a42be98b60 100644 --- a/hotspot/src/share/vm/opto/graphKit.cpp +++ b/hotspot/src/share/vm/opto/graphKit.cpp @@ -1714,6 +1714,11 @@ void GraphKit::replace_call(CallNode* call, Node* result) { 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); From 9f5ca0249d160f721386eb410bdad7f04c86498e Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Wed, 9 Dec 2009 16:40:45 -0800 Subject: [PATCH 34/61] 6895383: JCK test throws NPE for method compiled with Escape Analysis Add missing checks for MemBar nodes in EA. Reviewed-by: never --- hotspot/src/share/vm/opto/compile.cpp | 1 + hotspot/src/share/vm/opto/escape.cpp | 548 +++++++++++++++--------- hotspot/src/share/vm/opto/escape.hpp | 5 + hotspot/src/share/vm/opto/lcm.cpp | 5 +- hotspot/src/share/vm/opto/macro.cpp | 27 ++ hotspot/src/share/vm/opto/memnode.cpp | 66 ++- hotspot/src/share/vm/opto/memnode.hpp | 8 +- hotspot/src/share/vm/opto/node.hpp | 5 +- hotspot/src/share/vm/opto/parse3.cpp | 6 +- hotspot/test/compiler/6895383/Test.java | 51 +++ 10 files changed, 510 insertions(+), 212 deletions(-) create mode 100644 hotspot/test/compiler/6895383/Test.java diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index 02bcadb387b..f5d5387e9d3 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -1852,6 +1852,7 @@ void Compile::dump_asm(int *pcs, uint pc_limit) { !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 diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index 478a96a7537..ad624ee0042 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -779,6 +779,13 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra } 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)); @@ -808,7 +815,6 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra return result; } - // // Convert the types of unescaped object to instance types where possible, // propagate the new type information through the graph, and update memory @@ -900,7 +906,6 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra // 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(); @@ -1025,7 +1030,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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); } } @@ -1035,10 +1040,12 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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() || @@ -1053,8 +1060,10 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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(); @@ -1069,8 +1078,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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 { @@ -1082,33 +1090,25 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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) { + // Load/store to instance's field memnode_worklist.append_if_missing(use); - } else if (use->is_Initialize()) { + } else if (use->is_MemBar()) { 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_AddP() && use->outcnt() > 0) { // No dead nodes Node* addp2 = find_second_addp(use, n); if (addp2 != NULL) { @@ -1121,6 +1121,29 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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 } } @@ -1138,13 +1161,11 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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 { @@ -1181,31 +1202,48 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) // 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. - uint nslices = nmm->req(); + // 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(); for (uint i = Compile::AliasIdxRaw+1; i < nslices; i++) { Node* mem = nmm->in(i); Node* cur = NULL; @@ -1259,41 +1297,6 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } 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 @@ -1381,8 +1384,20 @@ bool ConnectionGraph::compute_escape() { 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); @@ -1423,12 +1438,13 @@ bool ConnectionGraph::compute_escape() { } } - 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); @@ -1438,98 +1454,9 @@ bool ConnectionGraph::compute_escape() { 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); } } } @@ -1646,6 +1573,150 @@ bool ConnectionGraph::compute_escape() { 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()) { @@ -1657,6 +1728,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha 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. @@ -1667,9 +1739,23 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha 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()) { // @@ -1706,9 +1792,10 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha 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::ArgEscape) { bool global_escapes = false; bool fields_escapes = false; @@ -1942,20 +2029,23 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) 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; } @@ -2088,18 +2178,27 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) } 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: @@ -2160,6 +2259,15 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) } 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); @@ -2174,6 +2282,7 @@ void ConnectionGraph::record_for_escape_analysis(Node *n, PhaseTransform *phase) 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. @@ -2211,6 +2320,7 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { 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 { @@ -2250,7 +2360,6 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { #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); @@ -2302,13 +2411,19 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { } 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: @@ -2320,6 +2435,7 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { } #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 { @@ -2354,14 +2470,38 @@ void ConnectionGraph::build_connection_graph(Node *n, PhaseTransform *phase) { } 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 --git a/hotspot/src/share/vm/opto/escape.hpp b/hotspot/src/share/vm/opto/escape.hpp index 1ce0cc9cf29..4a5b960fa3e 100644 --- a/hotspot/src/share/vm/opto/escape.hpp +++ b/hotspot/src/share/vm/opto/escape.hpp @@ -210,6 +210,8 @@ private: 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. @@ -315,6 +317,9 @@ private: // 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 --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp index 31de55a5435..0afe80c8f92 100644 --- a/hotspot/src/share/vm/opto/lcm.cpp +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -616,8 +616,9 @@ bool Block::schedule_local(PhaseCFG *cfg, Matcher &matcher, int *ready_cnt, Vect 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 --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index 6bff3539c30..2fdc335b918 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -316,6 +316,21 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me 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 @@ void PhaseMacroExpand::process_users_of_allocation(AllocateNode *alloc) { 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); diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index fb14ebff8c1..4698add456b 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -123,6 +123,13 @@ Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypePtr *t_adr, } 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); } @@ -537,6 +544,15 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) { } 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); @@ -2454,6 +2470,31 @@ Node *ClearArrayNode::Ideal(PhaseGVN *phase, bool can_reshape){ 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, @@ -2627,7 +2668,30 @@ MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { // 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 --git a/hotspot/src/share/vm/opto/memnode.hpp b/hotspot/src/share/vm/opto/memnode.hpp index a71df7d406a..cd0e60d971e 100644 --- a/hotspot/src/share/vm/opto/memnode.hpp +++ b/hotspot/src/share/vm/opto/memnode.hpp @@ -717,7 +717,10 @@ public: //------------------------------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 @@ -743,6 +746,9 @@ public: 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------------------------------------- diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp index 1fe4950e913..92da96b40a2 100644 --- a/hotspot/src/share/vm/opto/node.hpp +++ b/hotspot/src/share/vm/opto/node.hpp @@ -47,6 +47,7 @@ class CallStaticJavaNode; class CatchNode; class CatchProjNode; class CheckCastPPNode; +class ClearArrayNode; class CmpNode; class CodeBuffer; class ConstraintCastNode; @@ -599,8 +600,9 @@ public: 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 @@ -698,6 +700,7 @@ public: 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) diff --git a/hotspot/src/share/vm/opto/parse3.cpp b/hotspot/src/share/vm/opto/parse3.cpp index 7125cb5d619..83a3927a5ee 100644 --- a/hotspot/src/share/vm/opto/parse3.cpp +++ b/hotspot/src/share/vm/opto/parse3.cpp @@ -240,19 +240,19 @@ void Parse::do_put_xxx(const TypePtr* obj_type, Node* obj, ciField* field, bool // 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); } } } diff --git a/hotspot/test/compiler/6895383/Test.java b/hotspot/test/compiler/6895383/Test.java new file mode 100644 index 00000000000..719ba31134a --- /dev/null +++ b/hotspot/test/compiler/6895383/Test.java @@ -0,0 +1,51 @@ +/* + * 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. + * + */ + +/** + * @test + * @bug 6895383 + * @summary JCK test throws NPE for method compiled with Escape Analysis + * + * @run main/othervm -Xcomp Test + */ + +public class Test { + public static void main(String argv[]) { + Test test = new Test(); + test.testRemove1_IndexOutOfBounds(); + test.testAddAll1_IndexOutOfBoundsException(); + } + + public void testRemove1_IndexOutOfBounds() { + CopyOnWriteArrayList c = new CopyOnWriteArrayList(); + } + + public void testAddAll1_IndexOutOfBoundsException() { + try { + CopyOnWriteArrayList c = new CopyOnWriteArrayList(); + c.addAll(-1, new LinkedList()); // should throw IndexOutOfBoundsException + } catch (IndexOutOfBoundsException e) { + } + } +} From 00f583219fef7064cf99e3670d0277e457530517 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Wed, 9 Dec 2009 19:50:14 -0800 Subject: [PATCH 35/61] 6896727: nsk/logging/LoggingPermission/LoggingPermission/logperm002 fails with G1, EscapeAnalisys Move instance store's memory users to corresponding memory slices when updating its memory edge. Reviewed-by: never --- hotspot/src/share/vm/opto/escape.cpp | 137 ++++++++++++++++++++++-- hotspot/src/share/vm/opto/escape.hpp | 3 +- hotspot/test/compiler/6896727/Test.java | 48 +++++++++ 3 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 hotspot/test/compiler/6896727/Test.java diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index ad624ee0042..b560151bf83 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -543,6 +543,7 @@ bool ConnectionGraph::split_AddP(Node *addp, Node *base, PhaseGVN *igvn) { 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'. @@ -618,9 +619,14 @@ PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, Gro 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; } @@ -710,6 +716,81 @@ static Node *step_through_mergemem(MergeMemNode *mmem, int alias_idx, const Type return mem; } +// +// 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. @@ -775,6 +856,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra 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; @@ -907,10 +989,12 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) { GrowableArray memnode_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. @@ -986,6 +1070,8 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) // - 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(); @@ -1182,6 +1268,10 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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; } @@ -1249,6 +1339,8 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) 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) { @@ -1267,7 +1359,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } 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); @@ -1283,11 +1375,11 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } // 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; @@ -1325,19 +1417,48 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) } // 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) { diff --git a/hotspot/src/share/vm/opto/escape.hpp b/hotspot/src/share/vm/opto/escape.hpp index 4a5b960fa3e..576043beb45 100644 --- a/hotspot/src/share/vm/opto/escape.hpp +++ b/hotspot/src/share/vm/opto/escape.hpp @@ -291,7 +291,7 @@ private: 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 @@ -300,7 +300,6 @@ private: // 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]; diff --git a/hotspot/test/compiler/6896727/Test.java b/hotspot/test/compiler/6896727/Test.java new file mode 100644 index 00000000000..a9aef1d2b3d --- /dev/null +++ b/hotspot/test/compiler/6896727/Test.java @@ -0,0 +1,48 @@ +/* + * 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. + * + */ + +/* + * @test + * @bug 6896727 + * @summary nsk/logging/LoggingPermission/LoggingPermission/logperm002 fails with G1, EscapeAnalisys w/o COOPs + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -Xcomp -XX:+DoEscapeAnalysis -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC Test + */ + +public class Test { + + final static String testString = "abracadabra"; + public static void main(String args[]) { + String params[][] = { + {"control", testString} + }; + for (int i=0; i Date: Wed, 9 Dec 2009 23:51:38 -0800 Subject: [PATCH 36/61] 6908215: G1: SEGV with G1PolicyVerbose=2 debug flag Change CollectionSetChooser::printSortedHeapRegions to handle null entries in _markedRegions growable array. Reviewed-by: jmasa, tonyp, iveresov --- .../vm/gc_implementation/g1/collectionSetChooser.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp b/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp index 3000f010b17..96760517637 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp @@ -351,9 +351,16 @@ void 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"); } From 82e58a7b133e6fdfe50c79dfb810781e7da5d18e Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 10 Dec 2009 20:35:31 -0800 Subject: [PATCH 37/61] 6909538: Clarify meaning of "element" in javax.lang.model.element API Reviewed-by: ahe --- .../classes/javax/lang/model/element/package-info.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/langtools/src/share/classes/javax/lang/model/element/package-info.java b/langtools/src/share/classes/javax/lang/model/element/package-info.java index 26ea184c432..7729f505578 100644 --- a/langtools/src/share/classes/javax/lang/model/element/package-info.java +++ b/langtools/src/share/classes/javax/lang/model/element/package-info.java @@ -26,6 +26,16 @@ /** * Interfaces used to model elements of the Java programming language. * + * The term "element" in this package is used to refer to program + * elements, the declared entities that make up a program. Elements + * include classes, interfaces, methods, constructors, and fields. + * The interfaces in this package do not model the structure of a + * program inside a method body; for example there is no + * representation of a {@code for} loop or {@code try}-{@code finally} + * block. However, the interfaces can model some structures only + * appearing inside method bodies, such as local variables and + * anonymous classes. + * *

When used in the context of annotation processing, an accurate * model of the element being represented must be returned. As this * is a language model, the source code provides the fiducial From 4f656a451eb20d743f73c0d37a63e63e9ca24c40 Mon Sep 17 00:00:00 2001 From: Yumin Qi Date: Fri, 11 Dec 2009 11:09:49 -0800 Subject: [PATCH 38/61] 6361589: Print out stack trace for target thread of GC crash If GC crashed with java thread involved, print out the java stack trace in error report Reviewed-by: never, ysr, coleenp, dholmes --- hotspot/src/share/vm/runtime/frame.cpp | 16 ++++++++++++--- hotspot/src/share/vm/runtime/globals.hpp | 3 +++ hotspot/src/share/vm/runtime/thread.cpp | 24 ++++++++++++++++++++++ hotspot/src/share/vm/runtime/thread.hpp | 14 ++++++++++++- hotspot/src/share/vm/runtime/vmStructs.cpp | 1 + hotspot/src/share/vm/runtime/vmThread.cpp | 4 ++-- hotspot/src/share/vm/runtime/vmThread.hpp | 4 +--- hotspot/src/share/vm/utilities/vmError.cpp | 17 +++++++++++++++ 8 files changed, 74 insertions(+), 9 deletions(-) diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index 4ce96408ce4..6f87873bfd4 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -1190,9 +1190,19 @@ void frame::oops_entry_do(OopClosure* f, const RegisterMap* map) { void frame::oops_do_internal(OopClosure* f, CodeBlobClosure* cf, RegisterMap* map, bool use_interpreter_oop_map_cache) { - if (is_interpreted_frame()) { oops_interpreted_do(f, map, use_interpreter_oop_map_cache); - } else if (is_entry_frame()) { oops_entry_do (f, map); - } else if (CodeCache::contains(pc())) { oops_code_blob_do (f, cf, map); +#ifndef PRODUCT + // simulate GC crash here to dump java thread in error report + if (CrashGCForDumpingJavaThread) { + char *t = NULL; + *t = 'c'; + } +#endif + if (is_interpreted_frame()) { + oops_interpreted_do(f, map, use_interpreter_oop_map_cache); + } else if (is_entry_frame()) { + oops_entry_do(f, map); + } else if (CodeCache::contains(pc())) { + oops_code_blob_do(f, cf, map); } else { ShouldNotReachHere(); } diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index b894aac439e..62dceadc215 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -2554,6 +2554,9 @@ class CommandLineFlags { "Include miscellaneous runtime verifications in nmethod code; " \ "default off because it disturbs nmethod size heuristics") \ \ + notproduct(bool, CrashGCForDumpingJavaThread, false, \ + "Manually make GC thread crash then dump java stack trace; " \ + "Test only") \ \ /* compilation */ \ product(bool, UseCompiler, true, \ diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index 5b2b90dedcb..858c09f0480 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -991,6 +991,7 @@ void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name, bool // uniquely named instances should derive from this. NamedThread::NamedThread() : Thread() { _name = NULL; + _processed_thread = NULL; } NamedThread::~NamedThread() { @@ -2333,6 +2334,27 @@ void JavaThread::gc_prologue() { frames_do(frame_gc_prologue); } +// If the caller is a NamedThread, then remember, in the current scope, +// the given JavaThread in its _processed_thread field. +class RememberProcessedThread: public StackObj { + NamedThread* _cur_thr; +public: + RememberProcessedThread(JavaThread* jthr) { + Thread* thread = Thread::current(); + if (thread->is_Named_thread()) { + _cur_thr = (NamedThread *)thread; + _cur_thr->set_processed_thread(jthr); + } else { + _cur_thr = NULL; + } + } + + ~RememberProcessedThread() { + if (_cur_thr) { + _cur_thr->set_processed_thread(NULL); + } + } +}; void JavaThread::oops_do(OopClosure* f, CodeBlobClosure* cf) { // Flush deferred store-barriers, if any, associated with @@ -2349,6 +2371,8 @@ void JavaThread::oops_do(OopClosure* f, CodeBlobClosure* cf) { (has_last_Java_frame() && java_call_counter() > 0), "wrong java_sp info!"); if (has_last_Java_frame()) { + // Record JavaThread to GC thread + RememberProcessedThread rpt(this); // Traverse the privileged stack if (_privileged_stack_top != NULL) { diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp index cb4d6168a82..e0ea0be5588 100644 --- a/hotspot/src/share/vm/runtime/thread.hpp +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -48,7 +48,12 @@ class IdealGraphPrinter; // Class hierarchy // - Thread -// - VMThread +// - NamedThread +// - VMThread +// - ConcurrentGCThread +// - WorkerThread +// - GangWorker +// - GCTaskThread // - JavaThread // - WatcherThread @@ -249,6 +254,7 @@ class Thread: public ThreadShadow { virtual bool is_GC_task_thread() const { return false; } virtual bool is_Watcher_thread() const { return false; } virtual bool is_ConcurrentGC_thread() const { return false; } + virtual bool is_Named_thread() const { return false; } virtual char* name() const { return (char*)"Unknown thread"; } @@ -568,12 +574,18 @@ class NamedThread: public Thread { }; private: char* _name; + // log JavaThread being processed by oops_do + JavaThread* _processed_thread; + public: NamedThread(); ~NamedThread(); // May only be called once per thread. void set_name(const char* format, ...); + virtual bool is_Named_thread() const { return true; } virtual char* name() const { return _name == NULL ? (char*)"Unknown Thread" : _name; } + JavaThread *processed_thread() { return _processed_thread; } + void set_processed_thread(JavaThread *thread) { _processed_thread = thread; } }; // Worker threads are named and have an id of an assigned work. diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index bbff61b478b..3e21d0d4b05 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -666,6 +666,7 @@ static inline uint64_t cast_uint64_t(size_t x) nonstatic_field(Thread, _current_pending_monitor_is_from_java, bool) \ nonstatic_field(Thread, _current_waiting_monitor, ObjectMonitor*) \ nonstatic_field(NamedThread, _name, char*) \ + nonstatic_field(NamedThread, _processed_thread, JavaThread*) \ nonstatic_field(JavaThread, _next, JavaThread*) \ nonstatic_field(JavaThread, _threadObj, oop) \ nonstatic_field(JavaThread, _anchor, JavaFrameAnchor) \ diff --git a/hotspot/src/share/vm/runtime/vmThread.cpp b/hotspot/src/share/vm/runtime/vmThread.cpp index b54f625158e..26dad9a689c 100644 --- a/hotspot/src/share/vm/runtime/vmThread.cpp +++ b/hotspot/src/share/vm/runtime/vmThread.cpp @@ -204,8 +204,8 @@ void VMThread::create() { } -VMThread::VMThread() : Thread() { - // nothing to do +VMThread::VMThread() : NamedThread() { + set_name("VM Thread"); } void VMThread::destroy() { diff --git a/hotspot/src/share/vm/runtime/vmThread.hpp b/hotspot/src/share/vm/runtime/vmThread.hpp index 7acf84a70c7..daa705138ae 100644 --- a/hotspot/src/share/vm/runtime/vmThread.hpp +++ b/hotspot/src/share/vm/runtime/vmThread.hpp @@ -83,7 +83,7 @@ class VMOperationQueue : public CHeapObj { // like scavenge, garbage_collect etc. // -class VMThread: public Thread { +class VMThread: public NamedThread { private: static ThreadPriority _current_priority; @@ -101,8 +101,6 @@ class VMThread: public Thread { bool is_VM_thread() const { return true; } bool is_GC_thread() const { return true; } - char* name() const { return (char*)"VM Thread"; } - // The ever running loop for the VMThread void loop(); diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp index 2a2ddf83ab1..b758d8e9fa5 100644 --- a/hotspot/src/share/vm/utilities/vmError.cpp +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -502,6 +502,23 @@ void VMError::report(outputStream* st) { #endif // ZERO } + STEP(135, "(printing target Java thread stack)" ) + + // printing Java thread stack trace if it is involved in GC crash + if (_verbose && (_thread->is_Named_thread())) { + JavaThread* jt = ((NamedThread *)_thread)->processed_thread(); + if (jt != NULL) { + st->print_cr("JavaThread " PTR_FORMAT " (nid = " UINTX_FORMAT ") was being processed", jt, jt->osthread()->thread_id()); + if (jt->has_last_Java_frame()) { + st->print_cr("Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)"); + for(StackFrameStream sfs(jt); !sfs.is_done(); sfs.next()) { + sfs.current()->print_on_error(st, buf, sizeof(buf), true); + st->cr(); + } + } + } + } + STEP(140, "(printing VM operation)" ) if (_verbose && _thread && _thread->is_VM_thread()) { From 75f1feee86b80ef4ffa2b69340129072d1158c6d Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Fri, 11 Dec 2009 14:26:27 -0800 Subject: [PATCH 39/61] 6906175: bridge JSR199 and JSR 203 APIs Reviewed-by: darcy, alanb --- langtools/make/build.properties | 19 +- langtools/make/build.xml | 6 +- .../sun/tools/javac/file/BaseFileObject.java | 11 +- .../tools/javac/file/JavacFileManager.java | 302 +--------- .../com/sun/tools/javac/file/Paths.java | 2 +- .../tools/javac/nio/JavacPathFileManager.java | 543 ++++++++++++++++++ .../sun/tools/javac/nio/PathFileManager.java | 125 ++++ .../sun/tools/javac/nio/PathFileObject.java | 319 ++++++++++ .../sun/tools/javac/util/BaseFileManager.java | 355 ++++++++++++ .../CloseableURLClassLoader.java | 7 +- .../javax/tools/StandardJavaFileManager.java | 1 - .../javac/nio/compileTest/CompileTest.java | 165 ++++++ .../javac/nio/compileTest/HelloPathWorld.java | 28 + 13 files changed, 1570 insertions(+), 313 deletions(-) create mode 100644 langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java create mode 100644 langtools/src/share/classes/com/sun/tools/javac/nio/PathFileManager.java create mode 100644 langtools/src/share/classes/com/sun/tools/javac/nio/PathFileObject.java create mode 100644 langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java rename langtools/src/share/classes/com/sun/tools/javac/{file => util}/CloseableURLClassLoader.java (96%) create mode 100644 langtools/test/tools/javac/nio/compileTest/CompileTest.java create mode 100644 langtools/test/tools/javac/nio/compileTest/HelloPathWorld.java diff --git a/langtools/make/build.properties b/langtools/make/build.properties index da7a65a6561..75dc6c97b32 100644 --- a/langtools/make/build.properties +++ b/langtools/make/build.properties @@ -149,11 +149,26 @@ apt.tests = \ # # The following files require the import JDK to be available -require.import.jdk.files = +require.import.jdk.files = \ + com/sun/tools/javac/nio/*.java # The following files in the import jdk source directory are required # in order to compile the files defined in ${require.import.jdk.files} -import.jdk.stub.files = +# +# For NIO, the list of stub files is defined by the contents of the primary +# API packages, together with such types that may be required in order to +# compile the stubs. Some of these dependencies would go away if the stub +# generator were to be improved -- e.g. by removing unnecessary imports. +# +import.jdk.stub.files = \ + java/io/File.java \ + java/nio/file/**.java \ + java/nio/file/attribute/**.java \ + java/nio/file/spi/**.java \ + java/nio/channels/AsynchronousChannel.java \ + java/nio/channels/AsynchronousFileChannel.java \ + java/nio/channels/CompletionHandler.java \ + java/nio/channels/SeekableByteChannel.java # The following value is used by the main jtreg target. # An empty value means all tests diff --git a/langtools/make/build.xml b/langtools/make/build.xml index fc6e0f41f1f..7081f4dff6d 100644 --- a/langtools/make/build.xml +++ b/langtools/make/build.xml @@ -98,7 +98,7 @@ import.jdk should be unset, or set to jdk home (to use rt.jar) or to jdk repo (to use src/share/classes). Based on the value, if any, set up default values for javac's sourcepath, - classpath and bootclasspath. Note: the default values are overridden + classpath and bootclasspath. Note: the default values are overridden in the build-bootstrap-classes macro. --> - - + + diff --git a/langtools/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java b/langtools/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java index 6435e05a04c..9666f1cee7e 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java +++ b/langtools/src/share/classes/com/sun/tools/javac/file/BaseFileObject.java @@ -39,6 +39,8 @@ import javax.tools.JavaFileObject; import static javax.tools.JavaFileObject.Kind.*; +import com.sun.tools.javac.util.BaseFileManager; + /** *

This is NOT part of any API supported by Sun Microsystems. * If you write code that depends on this, you do so at your own risk. @@ -74,14 +76,7 @@ public abstract class BaseFileObject implements JavaFileObject { protected abstract String inferBinaryName(Iterable path); protected static JavaFileObject.Kind getKind(String filename) { - if (filename.endsWith(CLASS.extension)) - return CLASS; - else if (filename.endsWith(SOURCE.extension)) - return SOURCE; - else if (filename.endsWith(HTML.extension)) - return HTML; - else - return OTHER; + return BaseFileManager.getKind(filename); } protected static String removeExtension(String fileName) { diff --git a/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java b/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java index 23f169b5392..661f89eb717 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java +++ b/langtools/src/share/classes/com/sun/tools/javac/file/JavacFileManager.java @@ -26,29 +26,16 @@ package com.sun.tools.javac.file; import java.io.ByteArrayOutputStream; -import java.io.Closeable; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStreamWriter; -import java.lang.ref.SoftReference; -import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.net.URLClassLoader; -import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.channels.FileChannel; import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; -import java.nio.charset.CodingErrorAction; -import java.nio.charset.IllegalCharsetNameException; -import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -66,18 +53,13 @@ import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; -import com.sun.tools.javac.code.Source; import com.sun.tools.javac.file.RelativePath.RelativeFile; import com.sun.tools.javac.file.RelativePath.RelativeDirectory; -import com.sun.tools.javac.main.JavacOption; import com.sun.tools.javac.main.OptionName; -import com.sun.tools.javac.main.RecognizedOptions; +import com.sun.tools.javac.util.BaseFileManager; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; -import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Options; import static javax.tools.StandardLocation.*; import static com.sun.tools.javac.main.OptionName.*; @@ -91,7 +73,7 @@ import static com.sun.tools.javac.main.OptionName.*; * This code and its internal interfaces are subject to change or * deletion without notice. */ -public class JavacFileManager implements StandardJavaFileManager { +public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager { boolean useZipFileIndex; @@ -102,17 +84,10 @@ public class JavacFileManager implements StandardJavaFileManager { return buffer.toString().toCharArray(); } - /** - * The log to be used for error reporting. - */ - protected Log log; - /** Encapsulates knowledge of paths */ private Paths paths; - private Options options; - private FSInfo fsInfo; private final File uninited = new File("U N I N I T E D"); @@ -134,12 +109,6 @@ public class JavacFileManager implements StandardJavaFileManager { protected boolean mmappedIO; protected boolean ignoreSymbolFile; - protected String classLoaderClass; - - /** - * User provided charset (through javax.tools). - */ - protected Charset charset; /** * Register a Context.Factory to create a JavacFileManager. @@ -157,18 +126,18 @@ public class JavacFileManager implements StandardJavaFileManager { * it as the JavaFileManager for that context. */ public JavacFileManager(Context context, boolean register, Charset charset) { + super(charset); if (register) context.put(JavaFileManager.class, this); - byteBufferCache = new ByteBufferCache(); - this.charset = charset; setContext(context); } /** * Set the context for JavacFileManager. */ + @Override public void setContext(Context context) { - log = Log.instance(context); + super.setContext(context); if (paths == null) { paths = Paths.instance(context); } else { @@ -177,14 +146,12 @@ public class JavacFileManager implements StandardJavaFileManager { paths.setContext(context); } - options = Options.instance(context); fsInfo = FSInfo.instance(context); useZipFileIndex = System.getProperty("useJavaUtilZip") == null;// TODO: options.get("useJavaUtilZip") == null; mmappedIO = options.get("mmappedIO") != null; ignoreSymbolFile = options.get("ignore.symbol.file") != null; - classLoaderClass = options.get("procloader"); } public JavaFileObject getFileForInput(String name) { @@ -214,17 +181,6 @@ public class JavacFileManager implements StandardJavaFileManager { return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names))); } - protected JavaFileObject.Kind getKind(String extension) { - if (extension.equals(JavaFileObject.Kind.CLASS.extension)) - return JavaFileObject.Kind.CLASS; - else if (extension.equals(JavaFileObject.Kind.SOURCE.extension)) - return JavaFileObject.Kind.SOURCE; - else if (extension.equals(JavaFileObject.Kind.HTML.extension)) - return JavaFileObject.Kind.HTML; - else - return JavaFileObject.Kind.OTHER; - } - private static boolean isValidName(String name) { // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ), // but the set of keywords depends on the source level, and we don't want @@ -359,9 +315,7 @@ public class JavacFileManager implements StandardJavaFileManager { } private boolean isValidFile(String s, Set fileKinds) { - int lastDot = s.lastIndexOf("."); - String extn = (lastDot == -1 ? s : s.substring(lastDot)); - JavaFileObject.Kind kind = getKind(extn); + JavaFileObject.Kind kind = getKind(s); return fileKinds.contains(kind); } @@ -564,18 +518,6 @@ public class JavacFileManager implements StandardJavaFileManager { } } - CharBuffer getCachedContent(JavaFileObject file) { - SoftReference r = contentCache.get(file); - return (r == null ? null : r.get()); - } - - void cache(JavaFileObject file, CharBuffer cb) { - contentCache.put(file, new SoftReference(cb)); - } - - private final Map> contentCache - = new HashMap>(); - private String defaultEncodingName; private String getDefaultEncodingName() { if (defaultEncodingName == null) { @@ -585,161 +527,6 @@ public class JavacFileManager implements StandardJavaFileManager { return defaultEncodingName; } - protected String getEncodingName() { - String encName = options.get(OptionName.ENCODING); - if (encName == null) - return getDefaultEncodingName(); - else - return encName; - } - - protected Source getSource() { - String sourceName = options.get(OptionName.SOURCE); - Source source = null; - if (sourceName != null) - source = Source.lookup(sourceName); - return (source != null ? source : Source.DEFAULT); - } - - /** - * Make a byte buffer from an input stream. - */ - ByteBuffer makeByteBuffer(InputStream in) - throws IOException { - int limit = in.available(); - if (mmappedIO && in instanceof FileInputStream) { - // Experimental memory mapped I/O - FileInputStream fin = (FileInputStream)in; - return fin.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, limit); - } - if (limit < 1024) limit = 1024; - ByteBuffer result = byteBufferCache.get(limit); - int position = 0; - while (in.available() != 0) { - if (position >= limit) - // expand buffer - result = ByteBuffer. - allocate(limit <<= 1). - put((ByteBuffer)result.flip()); - int count = in.read(result.array(), - position, - limit - position); - if (count < 0) break; - result.position(position += count); - } - return (ByteBuffer)result.flip(); - } - - void recycleByteBuffer(ByteBuffer bb) { - byteBufferCache.put(bb); - } - - /** - * A single-element cache of direct byte buffers. - */ - private static class ByteBufferCache { - private ByteBuffer cached; - ByteBuffer get(int capacity) { - if (capacity < 20480) capacity = 20480; - ByteBuffer result = - (cached != null && cached.capacity() >= capacity) - ? (ByteBuffer)cached.clear() - : ByteBuffer.allocate(capacity + capacity>>1); - cached = null; - return result; - } - void put(ByteBuffer x) { - cached = x; - } - } - - private final ByteBufferCache byteBufferCache; - - CharsetDecoder getDecoder(String encodingName, boolean ignoreEncodingErrors) { - Charset cs = (this.charset == null) - ? Charset.forName(encodingName) - : this.charset; - CharsetDecoder decoder = cs.newDecoder(); - - CodingErrorAction action; - if (ignoreEncodingErrors) - action = CodingErrorAction.REPLACE; - else - action = CodingErrorAction.REPORT; - - return decoder - .onMalformedInput(action) - .onUnmappableCharacter(action); - } - - /** - * Decode a ByteBuffer into a CharBuffer. - */ - CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) { - String encodingName = getEncodingName(); - CharsetDecoder decoder; - try { - decoder = getDecoder(encodingName, ignoreEncodingErrors); - } catch (IllegalCharsetNameException e) { - log.error("unsupported.encoding", encodingName); - return (CharBuffer)CharBuffer.allocate(1).flip(); - } catch (UnsupportedCharsetException e) { - log.error("unsupported.encoding", encodingName); - return (CharBuffer)CharBuffer.allocate(1).flip(); - } - - // slightly overestimate the buffer size to avoid reallocation. - float factor = - decoder.averageCharsPerByte() * 0.8f + - decoder.maxCharsPerByte() * 0.2f; - CharBuffer dest = CharBuffer. - allocate(10 + (int)(inbuf.remaining()*factor)); - - while (true) { - CoderResult result = decoder.decode(inbuf, dest, true); - dest.flip(); - - if (result.isUnderflow()) { // done reading - // make sure there is at least one extra character - if (dest.limit() == dest.capacity()) { - dest = CharBuffer.allocate(dest.capacity()+1).put(dest); - dest.flip(); - } - return dest; - } else if (result.isOverflow()) { // buffer too small; expand - int newCapacity = - 10 + dest.capacity() + - (int)(inbuf.remaining()*decoder.maxCharsPerByte()); - dest = CharBuffer.allocate(newCapacity).put(dest); - } else if (result.isMalformed() || result.isUnmappable()) { - // bad character in input - - // report coding error (warn only pre 1.5) - if (!getSource().allowEncodingErrors()) { - log.error(new SimpleDiagnosticPosition(dest.limit()), - "illegal.char.for.encoding", - charset == null ? encodingName : charset.name()); - } else { - log.warning(new SimpleDiagnosticPosition(dest.limit()), - "illegal.char.for.encoding", - charset == null ? encodingName : charset.name()); - } - - // skip past the coding error - inbuf.position(inbuf.position() + result.length()); - - // undo the flip() to prepare the output buffer - // for more translation - dest.position(dest.limit()); - dest.limit(dest.capacity()); - dest.put((char)0xfffd); // backward compatible - } else { - throw new AssertionError(result); - } - } - // unreached - } - public ClassLoader getClassLoader(Location location) { nullCheck(location); Iterable path = getLocation(location); @@ -754,39 +541,7 @@ public class JavacFileManager implements StandardJavaFileManager { } } - URL[] urls = lb.toArray(new URL[lb.size()]); - ClassLoader thisClassLoader = getClass().getClassLoader(); - - // Bug: 6558476 - // Ideally, ClassLoader should be Closeable, but before JDK7 it is not. - // On older versions, try the following, to get a closeable classloader. - - // 1: Allow client to specify the class to use via hidden option - if (classLoaderClass != null) { - try { - Class loader = - Class.forName(classLoaderClass).asSubclass(ClassLoader.class); - Class[] constrArgTypes = { URL[].class, ClassLoader.class }; - Constructor constr = loader.getConstructor(constrArgTypes); - return constr.newInstance(new Object[] { urls, thisClassLoader }); - } catch (Throwable t) { - // ignore errors loading user-provided class loader, fall through - } - } - - // 2: If URLClassLoader implements Closeable, use that. - if (Closeable.class.isAssignableFrom(URLClassLoader.class)) - return new URLClassLoader(urls, thisClassLoader); - - // 3: Try using private reflection-based CloseableURLClassLoader - try { - return new CloseableURLClassLoader(urls, thisClassLoader); - } catch (Throwable t) { - // ignore errors loading workaround class loader, fall through - } - - // 4: If all else fails, use plain old standard URLClassLoader - return new URLClassLoader(urls, thisClassLoader); + return getClassLoader(lb.toArray(new URL[lb.size()])); } public Iterable list(Location location, @@ -836,38 +591,6 @@ public class JavacFileManager implements StandardJavaFileManager { return a.equals(b); } - public boolean handleOption(String current, Iterator remaining) { - for (JavacOption o: javacFileManagerOptions) { - if (o.matches(current)) { - if (o.hasArg()) { - if (remaining.hasNext()) { - if (!o.process(options, current, remaining.next())) - return true; - } - } else { - if (!o.process(options, current)) - return true; - } - // operand missing, or process returned false - throw new IllegalArgumentException(current); - } - } - - return false; - } - // where - private static JavacOption[] javacFileManagerOptions = - RecognizedOptions.getJavacFileManagerOptions( - new RecognizedOptions.GrumpyHelper()); - - public int isSupportedOption(String option) { - for (JavacOption o : javacFileManagerOptions) { - if (o.matches(option)) - return o.hasArg() ? 1 : 0; - } - return -1; - } - public boolean hasLocation(Location location) { return getLocation(location) != null; } @@ -1115,15 +838,4 @@ public class JavacFileManager implements StandardJavaFileManager { } throw new IllegalArgumentException("Invalid relative path: " + file); } - - private static T nullCheck(T o) { - o.getClass(); // null check - return o; - } - - private static Iterable nullCheck(Iterable it) { - for (T t : it) - t.getClass(); // null check - return it; - } } diff --git a/langtools/src/share/classes/com/sun/tools/javac/file/Paths.java b/langtools/src/share/classes/com/sun/tools/javac/file/Paths.java index 6457b8107a5..c4dc9bb2301 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/file/Paths.java +++ b/langtools/src/share/classes/com/sun/tools/javac/file/Paths.java @@ -66,7 +66,7 @@ public class Paths { * @param context the context * @return the Paths instance for this context */ - static Paths instance(Context context) { + public static Paths instance(Context context) { Paths instance = context.get(pathsKey); if (instance == null) instance = new Paths(context); diff --git a/langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java b/langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java new file mode 100644 index 00000000000..6202b37d3b9 --- /dev/null +++ b/langtools/src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java @@ -0,0 +1,543 @@ +/* + * 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. 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. + */ + +package com.sun.tools.javac.nio; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.Attributes; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import javax.lang.model.SourceVersion; +import javax.tools.FileObject; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.StandardLocation; + +import static java.nio.file.FileVisitOption.*; +import static javax.tools.StandardLocation.*; + +import com.sun.tools.javac.file.Paths; +import com.sun.tools.javac.util.BaseFileManager; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; + +import static com.sun.tools.javac.main.OptionName.*; + + +// NOTE the imports carefully for this compilation unit. +// +// Path: java.nio.file.Path -- the new NIO type for which this file manager exists +// +// Paths: com.sun.tools.javac.file.Paths -- legacy javac type for handling path options +// The other Paths (java.nio.file.Paths) is not used + +// NOTE this and related classes depend on new API in JDK 7. +// This requires special handling while bootstrapping the JDK build, +// when these classes might not yet have been compiled. To workaround +// this, the build arranges to make stubs of these classes available +// when compiling this and related classes. The set of stub files +// is specified in make/build.properties. + +/** + * Implementation of PathFileManager: a JavaFileManager based on the use + * of java.nio.file.Path. + * + *

Just as a Path is somewhat analagous to a File, so too is this + * JavacPathFileManager analogous to JavacFileManager, as it relates to the + * support of FileObjects based on File objects (i.e. just RegularFileObject, + * not ZipFileObject and its variants.) + * + *

The default values for the standard locations supported by this file + * manager are the same as the default values provided by JavacFileManager -- + * i.e. as determined by the javac.file.Paths class. To override these values, + * call {@link #setLocation}. + * + *

To reduce confusion with Path objects, the locations such as "class path", + * "source path", etc, are generically referred to here as "search paths". + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class JavacPathFileManager extends BaseFileManager implements PathFileManager { + protected FileSystem defaultFileSystem; + + /** + * Create a JavacPathFileManager using a given context, optionally registering + * it as the JavaFileManager for that context. + */ + public JavacPathFileManager(Context context, boolean register, Charset charset) { + super(charset); + if (register) + context.put(JavaFileManager.class, this); + pathsForLocation = new HashMap(); + fileSystems = new HashMap(); + setContext(context); + } + + /** + * Set the context for JavacPathFileManager. + */ + @Override + protected void setContext(Context context) { + super.setContext(context); + searchPaths = Paths.instance(context); + } + + @Override + public FileSystem getDefaultFileSystem() { + if (defaultFileSystem == null) + defaultFileSystem = FileSystems.getDefault(); + return defaultFileSystem; + } + + @Override + public void setDefaultFileSystem(FileSystem fs) { + defaultFileSystem = fs; + } + + @Override + public void flush() throws IOException { + contentCache.clear(); + } + + @Override + public void close() throws IOException { + for (FileSystem fs: fileSystems.values()) + fs.close(); + } + + @Override + public ClassLoader getClassLoader(Location location) { + nullCheck(location); + Iterable path = getLocation(location); + if (path == null) + return null; + ListBuffer lb = new ListBuffer(); + for (Path p: path) { + try { + lb.append(p.toUri().toURL()); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + } + + return getClassLoader(lb.toArray(new URL[lb.size()])); + } + + // + + public boolean hasLocation(Location location) { + return (getLocation(location) != null); + } + + public Iterable getLocation(Location location) { + nullCheck(location); + lazyInitSearchPaths(); + PathsForLocation path = pathsForLocation.get(location); + if (path == null && !pathsForLocation.containsKey(location)) { + setDefaultForLocation(location); + path = pathsForLocation.get(location); + } + return path; + } + + private Path getOutputLocation(Location location) { + Iterable paths = getLocation(location); + return (paths == null ? null : paths.iterator().next()); + } + + public void setLocation(Location location, Iterable searchPath) + throws IOException + { + nullCheck(location); + lazyInitSearchPaths(); + if (searchPath == null) { + setDefaultForLocation(location); + } else { + if (location.isOutputLocation()) + checkOutputPath(searchPath); + PathsForLocation pl = new PathsForLocation(); + for (Path p: searchPath) + pl.add(p); // TODO -Xlint:path warn if path not found + pathsForLocation.put(location, pl); + } + } + + private void checkOutputPath(Iterable searchPath) throws IOException { + Iterator pathIter = searchPath.iterator(); + if (!pathIter.hasNext()) + throw new IllegalArgumentException("empty path for directory"); + Path path = pathIter.next(); + if (pathIter.hasNext()) + throw new IllegalArgumentException("path too long for directory"); + if (!path.exists()) + throw new FileNotFoundException(path + ": does not exist"); + else if (!isDirectory(path)) + throw new IOException(path + ": not a directory"); + } + + private void setDefaultForLocation(Location locn) { + Collection files = null; + if (locn instanceof StandardLocation) { + switch ((StandardLocation) locn) { + case CLASS_PATH: + files = searchPaths.userClassPath(); + break; + case PLATFORM_CLASS_PATH: + files = searchPaths.bootClassPath(); + break; + case SOURCE_PATH: + files = searchPaths.sourcePath(); + break; + case CLASS_OUTPUT: { + String arg = options.get(D); + files = (arg == null ? null : Collections.singleton(new File(arg))); + break; + } + case SOURCE_OUTPUT: { + String arg = options.get(S); + files = (arg == null ? null : Collections.singleton(new File(arg))); + break; + } + } + } + + PathsForLocation pl = new PathsForLocation(); + if (files != null) { + for (File f: files) + pl.add(f.toPath()); + } + pathsForLocation.put(locn, pl); + } + + private void lazyInitSearchPaths() { + if (!inited) { + setDefaultForLocation(PLATFORM_CLASS_PATH); + setDefaultForLocation(CLASS_PATH); + setDefaultForLocation(SOURCE_PATH); + inited = true; + } + } + // where + private boolean inited = false; + + private Map pathsForLocation; + private Paths searchPaths; + + private static class PathsForLocation extends LinkedHashSet { + private static final long serialVersionUID = 6788510222394486733L; + } + + // + + // + + @Override + public Path getPath(FileObject fo) { + nullCheck(fo); + if (!(fo instanceof PathFileObject)) + throw new IllegalArgumentException(); + return ((PathFileObject) fo).getPath(); + } + + @Override + public boolean isSameFile(FileObject a, FileObject b) { + nullCheck(a); + nullCheck(b); + if (!(a instanceof PathFileObject)) + throw new IllegalArgumentException("Not supported: " + a); + if (!(b instanceof PathFileObject)) + throw new IllegalArgumentException("Not supported: " + b); + return ((PathFileObject) a).isSameFile((PathFileObject) b); + } + + @Override + public Iterable list(Location location, + String packageName, Set kinds, boolean recurse) + throws IOException { + // validatePackageName(packageName); + nullCheck(packageName); + nullCheck(kinds); + + Iterable paths = getLocation(location); + if (paths == null) + return List.nil(); + ListBuffer results = new ListBuffer(); + + for (Path path : paths) + list(path, packageName, kinds, recurse, results); + + return results.toList(); + } + + private void list(Path path, String packageName, final Set kinds, + boolean recurse, final ListBuffer results) + throws IOException { + if (!path.exists()) + return; + + final Path pathDir; + if (isDirectory(path)) + pathDir = path; + else { + FileSystem fs = getFileSystem(path); + if (fs == null) + return; + pathDir = fs.getRootDirectories().iterator().next(); + } + String sep = path.getFileSystem().getSeparator(); + Path packageDir = packageName.isEmpty() ? pathDir + : pathDir.resolve(packageName.replace(".", sep)); + if (!packageDir.exists()) + return; + +/* Alternate impl of list, superceded by use of Files.walkFileTree */ +// Deque queue = new LinkedList(); +// queue.add(packageDir); +// +// Path dir; +// while ((dir = queue.poll()) != null) { +// DirectoryStream ds = dir.newDirectoryStream(); +// try { +// for (Path p: ds) { +// String name = p.getName().toString(); +// if (isDirectory(p)) { +// if (recurse && SourceVersion.isIdentifier(name)) { +// queue.add(p); +// } +// } else { +// if (kinds.contains(getKind(name))) { +// JavaFileObject fe = +// PathFileObject.createDirectoryPathFileObject(this, p, pathDir); +// results.append(fe); +// } +// } +// } +// } finally { +// ds.close(); +// } +// } + int maxDepth = (recurse ? Integer.MAX_VALUE : 1); + Set opts = EnumSet.of(DETECT_CYCLES, FOLLOW_LINKS); + Files.walkFileTree(packageDir, opts, maxDepth, + new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir) { + if (SourceVersion.isIdentifier(dir.getName().toString())) // JSR 292? + return FileVisitResult.CONTINUE; + else + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + if (attrs.isRegularFile() && kinds.contains(getKind(file.getName().toString()))) { + JavaFileObject fe = + PathFileObject.createDirectoryPathFileObject( + JavacPathFileManager.this, file, pathDir); + results.append(fe); + } + return FileVisitResult.CONTINUE; + } + }); + } + + @Override + public Iterable getJavaFileObjectsFromPaths( + Iterable paths) { + ArrayList result; + if (paths instanceof Collection) + result = new ArrayList(((Collection)paths).size()); + else + result = new ArrayList(); + for (Path p: paths) + result.add(PathFileObject.createSimplePathFileObject(this, nullCheck(p))); + return result; + } + + @Override + public Iterable getJavaFileObjects(Path... paths) { + return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths))); + } + + @Override + public JavaFileObject getJavaFileForInput(Location location, + String className, Kind kind) throws IOException { + return getFileForInput(location, getRelativePath(className, kind)); + } + + @Override + public FileObject getFileForInput(Location location, + String packageName, String relativeName) throws IOException { + return getFileForInput(location, getRelativePath(packageName, relativeName)); + } + + private JavaFileObject getFileForInput(Location location, String relativePath) + throws IOException { + for (Path p: getLocation(location)) { + if (isDirectory(p)) { + Path f = resolve(p, relativePath); + if (f.exists()) + return PathFileObject.createDirectoryPathFileObject(this, f, p); + } else { + FileSystem fs = getFileSystem(p); + if (fs != null) { + Path file = getPath(fs, relativePath); + if (file.exists()) + return PathFileObject.createJarPathFileObject(this, file); + } + } + } + return null; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, + String className, Kind kind, FileObject sibling) throws IOException { + return getFileForOutput(location, getRelativePath(className, kind), sibling); + } + + @Override + public FileObject getFileForOutput(Location location, String packageName, + String relativeName, FileObject sibling) + throws IOException { + return getFileForOutput(location, getRelativePath(packageName, relativeName), sibling); + } + + private JavaFileObject getFileForOutput(Location location, + String relativePath, FileObject sibling) { + Path dir = getOutputLocation(location); + if (dir == null) { + if (location == CLASS_OUTPUT) { + Path siblingDir = null; + if (sibling != null && sibling instanceof PathFileObject) { + siblingDir = ((PathFileObject) sibling).getPath().getParent(); + } + return PathFileObject.createSiblingPathFileObject(this, + siblingDir.resolve(getBaseName(relativePath)), + relativePath); + } else if (location == SOURCE_OUTPUT) { + dir = getOutputLocation(CLASS_OUTPUT); + } + } + + Path file; + if (dir != null) { + file = resolve(dir, relativePath); + return PathFileObject.createDirectoryPathFileObject(this, file, dir); + } else { + file = getPath(getDefaultFileSystem(), relativePath); + return PathFileObject.createSimplePathFileObject(this, file); + } + + } + + @Override + public String inferBinaryName(Location location, JavaFileObject fo) { + nullCheck(fo); + // Need to match the path semantics of list(location, ...) + Iterable paths = getLocation(location); + if (paths == null) { + return null; + } + + if (!(fo instanceof PathFileObject)) + throw new IllegalArgumentException(fo.getClass().getName()); + + return ((PathFileObject) fo).inferBinaryName(paths); + } + + private FileSystem getFileSystem(Path p) throws IOException { + FileSystem fs = fileSystems.get(p); + if (fs == null) { + fs = FileSystems.newFileSystem(p, Collections.emptyMap(), null); + fileSystems.put(p, fs); + } + return fs; + } + + private Map fileSystems; + + // + + // + + private static String getRelativePath(String className, Kind kind) { + return className.replace(".", "/") + kind.extension; + } + + private static String getRelativePath(String packageName, String relativeName) { + return packageName.replace(".", "/") + relativeName; + } + + private static String getBaseName(String relativePath) { + int lastSep = relativePath.lastIndexOf("/"); + return relativePath.substring(lastSep + 1); // safe if "/" not found + } + + private static boolean isDirectory(Path path) throws IOException { + BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path); + return attrs.isDirectory(); + } + + private static Path getPath(FileSystem fs, String relativePath) { + return fs.getPath(relativePath.replace("/", fs.getSeparator())); + } + + private static Path resolve(Path base, String relativePath) { + FileSystem fs = base.getFileSystem(); + Path rp = fs.getPath(relativePath.replace("/", fs.getSeparator())); + return base.resolve(rp); + } + + // + +} diff --git a/langtools/src/share/classes/com/sun/tools/javac/nio/PathFileManager.java b/langtools/src/share/classes/com/sun/tools/javac/nio/PathFileManager.java new file mode 100644 index 00000000000..0b55e6859ba --- /dev/null +++ b/langtools/src/share/classes/com/sun/tools/javac/nio/PathFileManager.java @@ -0,0 +1,125 @@ +/* + * 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. 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. + */ + +package com.sun.tools.javac.nio; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import javax.tools.FileObject; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; + +/** + * File manager based on {@linkplain File java.nio.file.Path}. + * + * Eventually, this should be moved to javax.tools. + * Also, JavaCompiler might reasonably provide a method getPathFileManager, + * similar to {@link javax.tools.JavaCompiler#getStandardFileManager + * getStandardFileManager}. However, would need to be handled carefully + * as another forward reference from langtools to jdk. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public interface PathFileManager extends JavaFileManager { + /** + * Get the default file system used to create paths. If no value has been + * set, the default file system is {@link FileSystems#getDefault}. + */ + FileSystem getDefaultFileSystem(); + + /** + * Set the default file system used to create paths. + * @param fs the default file system used to create any new paths. + */ + void setDefaultFileSystem(FileSystem fs); + + /** + * Get file objects representing the given files. + * + * @param paths a list of paths + * @return a list of file objects + * @throws IllegalArgumentException if the list of paths includes + * a directory + */ + Iterable getJavaFileObjectsFromPaths( + Iterable paths); + + /** + * Get file objects representing the given paths. + * Convenience method equivalent to: + * + *

+     *     getJavaFileObjectsFromPaths({@linkplain java.util.Arrays#asList Arrays.asList}(paths))
+     * 
+ * + * @param paths an array of paths + * @return a list of file objects + * @throws IllegalArgumentException if the array of files includes + * a directory + * @throws NullPointerException if the given array contains null + * elements + */ + Iterable getJavaFileObjects(Path... paths); + + /** + * Return the Path for a file object that has been obtained from this + * file manager. + * + * @param fo A file object that has been obtained from this file manager. + * @return The underlying Path object. + * @throws IllegalArgumentException is the file object was not obtained from + * from this file manager. + */ + Path getPath(FileObject fo); + + /** + * Get the search path associated with the given location. + * + * @param location a location + * @return a list of paths or {@code null} if this location has no + * associated search path + * @see #setLocation + */ + Iterable getLocation(Location location); + + /** + * Associate the given search path with the given location. Any + * previous value will be discarded. + * + * @param location a location + * @param searchPath a list of files, if {@code null} use the default + * search path for this location + * @see #getLocation + * @throws IllegalArgumentException if location is an output + * location and searchpath does not contain exactly one element + * @throws IOException if location is an output location and searchpath + * does not represent an existing directory + */ + void setLocation(Location location, Iterable searchPath) throws IOException; +} diff --git a/langtools/src/share/classes/com/sun/tools/javac/nio/PathFileObject.java b/langtools/src/share/classes/com/sun/tools/javac/nio/PathFileObject.java new file mode 100644 index 00000000000..80bdb2edb74 --- /dev/null +++ b/langtools/src/share/classes/com/sun/tools/javac/nio/PathFileObject.java @@ -0,0 +1,319 @@ +/* + * 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. 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. + */ + +package com.sun.tools.javac.nio; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.Attributes; +import java.nio.file.attribute.BasicFileAttributes; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.JavaFileObject; + +import com.sun.tools.javac.util.BaseFileManager; + + +/** + * Implementation of JavaFileObject using java.nio.file API. + * + *

PathFileObjects are, for the most part, straightforward wrappers around + * Path objects. The primary complexity is the support for "inferBinaryName". + * This is left as an abstract method, implemented by each of a number of + * different factory methods, which compute the binary name based on + * information available at the time the file object is created. + * + *

This is NOT part of any API supported by Sun Microsystems. If + * you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +abstract class PathFileObject implements JavaFileObject { + private JavacPathFileManager fileManager; + private Path path; + + /** + * Create a PathFileObject within a directory, such that the binary name + * can be inferred from the relationship to the parent directory. + */ + static PathFileObject createDirectoryPathFileObject(JavacPathFileManager fileManager, + final Path path, final Path dir) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + return toBinaryName(dir.relativize(path)); + } + }; + } + + /** + * Create a PathFileObject in a file system such as a jar file, such that + * the binary name can be inferred from its position within the filesystem. + */ + static PathFileObject createJarPathFileObject(JavacPathFileManager fileManager, + final Path path) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + return toBinaryName(path); + } + }; + } + + /** + * Create a PathFileObject whose binary name can be inferred from the + * relative path to a sibling. + */ + static PathFileObject createSiblingPathFileObject(JavacPathFileManager fileManager, + final Path path, final String relativePath) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + return toBinaryName(relativePath, "/"); + } + }; + } + + /** + * Create a PathFileObject whose binary name might be inferred from its + * position on a search path. + */ + static PathFileObject createSimplePathFileObject(JavacPathFileManager fileManager, + final Path path) { + return new PathFileObject(fileManager, path) { + @Override + String inferBinaryName(Iterable paths) { + Path absPath = path.toAbsolutePath(); + for (Path p: paths) { + Path ap = p.toAbsolutePath(); + if (absPath.startsWith(ap)) { + try { + Path rp = ap.relativize(absPath); + if (rp != null) // maybe null if absPath same as ap + return toBinaryName(rp); + } catch (IllegalArgumentException e) { + // ignore this p if cannot relativize path to p + } + } + } + return null; + } + }; + } + + protected PathFileObject(JavacPathFileManager fileManager, Path path) { + fileManager.getClass(); // null check + path.getClass(); // null check + this.fileManager = fileManager; + this.path = path; + } + + abstract String inferBinaryName(Iterable paths); + + /** + * Return the Path for this object. + * @return the Path for this object. + */ + Path getPath() { + return path; + } + + @Override + public Kind getKind() { + return BaseFileManager.getKind(path.getName().toString()); + } + + @Override + public boolean isNameCompatible(String simpleName, Kind kind) { + simpleName.getClass(); + // null check + if (kind == Kind.OTHER && getKind() != kind) { + return false; + } + String sn = simpleName + kind.extension; + String pn = path.getName().toString(); + if (pn.equals(sn)) { + return true; + } + if (pn.equalsIgnoreCase(sn)) { + try { + // allow for Windows + return path.toRealPath(false).getName().toString().equals(sn); + } catch (IOException e) { + } + } + return false; + } + + @Override + public NestingKind getNestingKind() { + return null; + } + + @Override + public Modifier getAccessLevel() { + return null; + } + + @Override + public URI toUri() { + return path.toUri(); + } + + @Override + public String getName() { + return path.toString(); + } + + @Override + public InputStream openInputStream() throws IOException { + return path.newInputStream(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + ensureParentDirectoriesExist(); + return path.newOutputStream(); + } + + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + CharsetDecoder decoder = fileManager.getDecoder(fileManager.getEncodingName(), ignoreEncodingErrors); + return new InputStreamReader(openInputStream(), decoder); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + CharBuffer cb = fileManager.getCachedContent(this); + if (cb == null) { + InputStream in = openInputStream(); + try { + ByteBuffer bb = fileManager.makeByteBuffer(in); + JavaFileObject prev = fileManager.log.useSource(this); + try { + cb = fileManager.decode(bb, ignoreEncodingErrors); + } finally { + fileManager.log.useSource(prev); + } + fileManager.recycleByteBuffer(bb); + if (!ignoreEncodingErrors) { + fileManager.cache(this, cb); + } + } finally { + in.close(); + } + } + return cb; + } + + @Override + public Writer openWriter() throws IOException { + ensureParentDirectoriesExist(); + return new OutputStreamWriter(path.newOutputStream(), fileManager.getEncodingName()); + } + + @Override + public long getLastModified() { + try { + BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path); + return attrs.lastModifiedTime().toMillis(); + } catch (IOException e) { + return -1; + } + } + + @Override + public boolean delete() { + try { + path.delete(); + return true; + } catch (IOException e) { + return false; + } + } + + public boolean isSameFile(PathFileObject other) { + try { + return path.isSameFile(other.path); + } catch (IOException e) { + return false; + } + } + + @Override + public boolean equals(Object other) { + return (other instanceof PathFileObject && path.equals(((PathFileObject) other).path)); + } + + @Override + public int hashCode() { + return path.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + path + "]"; + } + + private void ensureParentDirectoriesExist() throws IOException { + Path parent = path.getParent(); + if (parent != null) + Files.createDirectories(parent); + } + + private long size() { + try { + BasicFileAttributes attrs = Attributes.readBasicFileAttributes(path); + return attrs.size(); + } catch (IOException e) { + return -1; + } + } + + protected static String toBinaryName(Path relativePath) { + return toBinaryName(relativePath.toString(), + relativePath.getFileSystem().getSeparator()); + } + + protected static String toBinaryName(String relativePath, String sep) { + return removeExtension(relativePath).replaceAll(sep, "."); + } + + protected static String removeExtension(String fileName) { + int lastDot = fileName.lastIndexOf("."); + return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); + } +} diff --git a/langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java b/langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java new file mode 100644 index 00000000000..bc91c470f7a --- /dev/null +++ b/langtools/src/share/classes/com/sun/tools/javac/util/BaseFileManager.java @@ -0,0 +1,355 @@ +/* + * 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. 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. + */ + +package com.sun.tools.javac.util; + +import com.sun.tools.javac.code.Source; +import com.sun.tools.javac.main.JavacOption; +import com.sun.tools.javac.main.OptionName; +import com.sun.tools.javac.main.RecognizedOptions; +import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.lang.ref.SoftReference; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; + +/** + * Utility methods for building a filemanager. + * There are no references here to file-system specific objects such as + * java.io.File or java.nio.file.Path. + */ +public class BaseFileManager { + protected BaseFileManager(Charset charset) { + this.charset = charset; + byteBufferCache = new ByteBufferCache(); + } + + /** + * Set the context for JavacPathFileManager. + */ + protected void setContext(Context context) { + log = Log.instance(context); + options = Options.instance(context); + classLoaderClass = options.get("procloader"); + } + + /** + * The log to be used for error reporting. + */ + public Log log; + + /** + * User provided charset (through javax.tools). + */ + protected Charset charset; + + protected Options options; + + protected String classLoaderClass; + + protected Source getSource() { + String sourceName = options.get(OptionName.SOURCE); + Source source = null; + if (sourceName != null) + source = Source.lookup(sourceName); + return (source != null ? source : Source.DEFAULT); + } + + protected ClassLoader getClassLoader(URL[] urls) { + ClassLoader thisClassLoader = getClass().getClassLoader(); + + // Bug: 6558476 + // Ideally, ClassLoader should be Closeable, but before JDK7 it is not. + // On older versions, try the following, to get a closeable classloader. + + // 1: Allow client to specify the class to use via hidden option + if (classLoaderClass != null) { + try { + Class loader = + Class.forName(classLoaderClass).asSubclass(ClassLoader.class); + Class[] constrArgTypes = { URL[].class, ClassLoader.class }; + Constructor constr = loader.getConstructor(constrArgTypes); + return constr.newInstance(new Object[] { urls, thisClassLoader }); + } catch (Throwable t) { + // ignore errors loading user-provided class loader, fall through + } + } + + // 2: If URLClassLoader implements Closeable, use that. + if (Closeable.class.isAssignableFrom(URLClassLoader.class)) + return new URLClassLoader(urls, thisClassLoader); + + // 3: Try using private reflection-based CloseableURLClassLoader + try { + return new CloseableURLClassLoader(urls, thisClassLoader); + } catch (Throwable t) { + // ignore errors loading workaround class loader, fall through + } + + // 4: If all else fails, use plain old standard URLClassLoader + return new URLClassLoader(urls, thisClassLoader); + } + + // + public boolean handleOption(String current, Iterator remaining) { + for (JavacOption o: javacFileManagerOptions) { + if (o.matches(current)) { + if (o.hasArg()) { + if (remaining.hasNext()) { + if (!o.process(options, current, remaining.next())) + return true; + } + } else { + if (!o.process(options, current)) + return true; + } + // operand missing, or process returned false + throw new IllegalArgumentException(current); + } + } + + return false; + } + // where + private static JavacOption[] javacFileManagerOptions = + RecognizedOptions.getJavacFileManagerOptions( + new RecognizedOptions.GrumpyHelper()); + + public int isSupportedOption(String option) { + for (JavacOption o : javacFileManagerOptions) { + if (o.matches(option)) + return o.hasArg() ? 1 : 0; + } + return -1; + } + // + + // + private String defaultEncodingName; + private String getDefaultEncodingName() { + if (defaultEncodingName == null) { + defaultEncodingName = + new OutputStreamWriter(new ByteArrayOutputStream()).getEncoding(); + } + return defaultEncodingName; + } + + public String getEncodingName() { + String encName = options.get(OptionName.ENCODING); + if (encName == null) + return getDefaultEncodingName(); + else + return encName; + } + + public CharBuffer decode(ByteBuffer inbuf, boolean ignoreEncodingErrors) { + String encodingName = getEncodingName(); + CharsetDecoder decoder; + try { + decoder = getDecoder(encodingName, ignoreEncodingErrors); + } catch (IllegalCharsetNameException e) { + log.error("unsupported.encoding", encodingName); + return (CharBuffer)CharBuffer.allocate(1).flip(); + } catch (UnsupportedCharsetException e) { + log.error("unsupported.encoding", encodingName); + return (CharBuffer)CharBuffer.allocate(1).flip(); + } + + // slightly overestimate the buffer size to avoid reallocation. + float factor = + decoder.averageCharsPerByte() * 0.8f + + decoder.maxCharsPerByte() * 0.2f; + CharBuffer dest = CharBuffer. + allocate(10 + (int)(inbuf.remaining()*factor)); + + while (true) { + CoderResult result = decoder.decode(inbuf, dest, true); + dest.flip(); + + if (result.isUnderflow()) { // done reading + // make sure there is at least one extra character + if (dest.limit() == dest.capacity()) { + dest = CharBuffer.allocate(dest.capacity()+1).put(dest); + dest.flip(); + } + return dest; + } else if (result.isOverflow()) { // buffer too small; expand + int newCapacity = + 10 + dest.capacity() + + (int)(inbuf.remaining()*decoder.maxCharsPerByte()); + dest = CharBuffer.allocate(newCapacity).put(dest); + } else if (result.isMalformed() || result.isUnmappable()) { + // bad character in input + + // report coding error (warn only pre 1.5) + if (!getSource().allowEncodingErrors()) { + log.error(new SimpleDiagnosticPosition(dest.limit()), + "illegal.char.for.encoding", + charset == null ? encodingName : charset.name()); + } else { + log.warning(new SimpleDiagnosticPosition(dest.limit()), + "illegal.char.for.encoding", + charset == null ? encodingName : charset.name()); + } + + // skip past the coding error + inbuf.position(inbuf.position() + result.length()); + + // undo the flip() to prepare the output buffer + // for more translation + dest.position(dest.limit()); + dest.limit(dest.capacity()); + dest.put((char)0xfffd); // backward compatible + } else { + throw new AssertionError(result); + } + } + // unreached + } + + public CharsetDecoder getDecoder(String encodingName, boolean ignoreEncodingErrors) { + Charset cs = (this.charset == null) + ? Charset.forName(encodingName) + : this.charset; + CharsetDecoder decoder = cs.newDecoder(); + + CodingErrorAction action; + if (ignoreEncodingErrors) + action = CodingErrorAction.REPLACE; + else + action = CodingErrorAction.REPORT; + + return decoder + .onMalformedInput(action) + .onUnmappableCharacter(action); + } + // + + // + /** + * Make a byte buffer from an input stream. + */ + public ByteBuffer makeByteBuffer(InputStream in) + throws IOException { + int limit = in.available(); + if (limit < 1024) limit = 1024; + ByteBuffer result = byteBufferCache.get(limit); + int position = 0; + while (in.available() != 0) { + if (position >= limit) + // expand buffer + result = ByteBuffer. + allocate(limit <<= 1). + put((ByteBuffer)result.flip()); + int count = in.read(result.array(), + position, + limit - position); + if (count < 0) break; + result.position(position += count); + } + return (ByteBuffer)result.flip(); + } + + public void recycleByteBuffer(ByteBuffer bb) { + byteBufferCache.put(bb); + } + + /** + * A single-element cache of direct byte buffers. + */ + private static class ByteBufferCache { + private ByteBuffer cached; + ByteBuffer get(int capacity) { + if (capacity < 20480) capacity = 20480; + ByteBuffer result = + (cached != null && cached.capacity() >= capacity) + ? (ByteBuffer)cached.clear() + : ByteBuffer.allocate(capacity + capacity>>1); + cached = null; + return result; + } + void put(ByteBuffer x) { + cached = x; + } + } + + private final ByteBufferCache byteBufferCache; + // + + // + public CharBuffer getCachedContent(JavaFileObject file) { + SoftReference r = contentCache.get(file); + return (r == null ? null : r.get()); + } + + public void cache(JavaFileObject file, CharBuffer cb) { + contentCache.put(file, new SoftReference(cb)); + } + + protected final Map> contentCache + = new HashMap>(); + // + + public static Kind getKind(String name) { + if (name.endsWith(Kind.CLASS.extension)) + return Kind.CLASS; + else if (name.endsWith(Kind.SOURCE.extension)) + return Kind.SOURCE; + else if (name.endsWith(Kind.HTML.extension)) + return Kind.HTML; + else + return Kind.OTHER; + } + + protected static T nullCheck(T o) { + o.getClass(); // null check + return o; + } + + protected static Collection nullCheck(Collection it) { + for (T t : it) + t.getClass(); // null check + return it; + } +} diff --git a/langtools/src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java b/langtools/src/share/classes/com/sun/tools/javac/util/CloseableURLClassLoader.java similarity index 96% rename from langtools/src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java rename to langtools/src/share/classes/com/sun/tools/javac/util/CloseableURLClassLoader.java index bcba74bef9f..48c1e6f0dfd 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/file/CloseableURLClassLoader.java +++ b/langtools/src/share/classes/com/sun/tools/javac/util/CloseableURLClassLoader.java @@ -23,7 +23,7 @@ * have any questions. */ -package com.sun.tools.javac.file; +package com.sun.tools.javac.util; import java.io.Closeable; import java.io.IOException; @@ -45,9 +45,9 @@ import java.util.jar.JarFile; * This code and its internal interfaces are subject to change or * deletion without notice. */ -class CloseableURLClassLoader +public class CloseableURLClassLoader extends URLClassLoader implements Closeable { - CloseableURLClassLoader(URL[] urls, ClassLoader parent) throws Error { + public CloseableURLClassLoader(URL[] urls, ClassLoader parent) throws Error { super(urls, parent); try { getLoaders(); //proactive check that URLClassLoader is as expected @@ -63,6 +63,7 @@ class CloseableURLClassLoader * @throws java.io.IOException if the jar files cannot be found for any * reson, or if closing the jar file itself causes an IOException. */ + @Override public void close() throws IOException { try { for (Object l: getLoaders()) { diff --git a/langtools/src/share/classes/javax/tools/StandardJavaFileManager.java b/langtools/src/share/classes/javax/tools/StandardJavaFileManager.java index 116cf8c55f4..028c8d8f509 100644 --- a/langtools/src/share/classes/javax/tools/StandardJavaFileManager.java +++ b/langtools/src/share/classes/javax/tools/StandardJavaFileManager.java @@ -28,7 +28,6 @@ package javax.tools; import java.io.File; import java.io.IOException; import java.util.*; -import java.util.concurrent.*; /** * File manager based on {@linkplain File java.io.File}. A common way diff --git a/langtools/test/tools/javac/nio/compileTest/CompileTest.java b/langtools/test/tools/javac/nio/compileTest/CompileTest.java new file mode 100644 index 00000000000..72df1693a8a --- /dev/null +++ b/langtools/test/tools/javac/nio/compileTest/CompileTest.java @@ -0,0 +1,165 @@ +/* + * 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. + */ + +/** + * @test + * @compile HelloPathWorld.java + * @run main CompileTest + */ + +import java.io.*; +import java.nio.file.*; +import java.util.*; +import java.util.jar.*; +import javax.tools.*; + +import com.sun.tools.javac.nio.*; +import com.sun.tools.javac.util.Context; +import java.nio.file.spi.FileSystemProvider; + + +public class CompileTest { + public static void main(String[] args) throws Exception { + new CompileTest().run(); + } + + public void run() throws Exception { + File rtDir = new File("rt.dir"); + File javaHome = new File(System.getProperty("java.home")); + if (javaHome.getName().equals("jre")) + javaHome = javaHome.getParentFile(); + File rtJar = new File(new File(new File(javaHome, "jre"), "lib"), "rt.jar"); + expand(rtJar, rtDir); + + String[] rtDir_opts = { + "-bootclasspath", rtDir.toString(), + "-classpath", "", + "-sourcepath", "", + "-extdirs", "" + }; + test(rtDir_opts, "HelloPathWorld"); + + if (isJarFileSystemAvailable()) { + String[] rtJar_opts = { + "-bootclasspath", rtJar.toString(), + "-classpath", "", + "-sourcepath", "", + "-extdirs", "" + }; + test(rtJar_opts, "HelloPathWorld"); + + String[] default_opts = { }; + test(default_opts, "HelloPathWorld"); + + // finally, a non-trivial program + test(default_opts, "CompileTest"); + } else + System.err.println("jar file system not available: test skipped"); + } + + void test(String[] opts, String className) throws Exception { + count++; + System.err.println("Test " + count + " " + Arrays.asList(opts) + " " + className); + Path testSrcDir = Paths.get(System.getProperty("test.src")); + Path testClassesDir = Paths.get(System.getProperty("test.classes")); + Path classes = Paths.get("classes." + count); + classes.createDirectory(); + + Context ctx = new Context(); + PathFileManager fm = new JavacPathFileManager(ctx, true, null); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + List options = new ArrayList(); + options.addAll(Arrays.asList(opts)); + options.addAll(Arrays.asList( + "-verbose", "-XDverboseCompilePolicy", + "-d", classes.toString() + )); + Iterable compilationUnits = + fm.getJavaFileObjects(testSrcDir.resolve(className + ".java")); + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + JavaCompiler.CompilationTask t = + compiler.getTask(out, fm, null, options, null, compilationUnits); + boolean ok = t.call(); + System.err.println(sw.toString()); + if (!ok) { + throw new Exception("compilation failed"); + } + + File expect = new File("classes." + count + "/" + className + ".class"); + if (!expect.exists()) + throw new Exception("expected file not found: " + expect); + long expectedSize = new File(testClassesDir.toString(), className + ".class").length(); + long actualSize = expect.length(); + if (expectedSize != actualSize) + throw new Exception("wrong size found: " + actualSize + "; expected: " + expectedSize); + } + + boolean isJarFileSystemAvailable() { + boolean result = false; + for (FileSystemProvider fsp: FileSystemProvider.installedProviders()) { + String scheme = fsp.getScheme(); + System.err.println("Provider: " + scheme + " " + fsp); + if (scheme.equalsIgnoreCase("jar") || scheme.equalsIgnoreCase("zip")) + result = true; + } + return result; + } + + void expand(File jar, File dir) throws IOException { + JarFile jarFile = new JarFile(jar); + try { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry je = entries.nextElement(); + if (!je.isDirectory()) { + copy(jarFile.getInputStream(je), new File(dir, je.getName())); + } + } + } finally { + jarFile.close(); + } + } + + void copy(InputStream in, File dest) throws IOException { + dest.getParentFile().mkdirs(); + OutputStream out = new BufferedOutputStream(new FileOutputStream(dest)); + try { + byte[] data = new byte[8192]; + int n; + while ((n = in.read(data, 0, data.length)) > 0) + out.write(data, 0, n); + } finally { + out.close(); + in.close(); + } + } + + void error(String message) { + System.err.println("Error: " + message); + errors++; + } + + int errors; + int count; +} diff --git a/langtools/test/tools/javac/nio/compileTest/HelloPathWorld.java b/langtools/test/tools/javac/nio/compileTest/HelloPathWorld.java new file mode 100644 index 00000000000..a65e21a9afa --- /dev/null +++ b/langtools/test/tools/javac/nio/compileTest/HelloPathWorld.java @@ -0,0 +1,28 @@ +/* + * 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 HelloPathWorld { + public static void main(String... args) { + System.out.println("Hello World!"); + } +} From 8d1a5a5750d32a7e95040697a0e01d4fb05b5b14 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Sat, 12 Dec 2009 09:28:40 -0800 Subject: [PATCH 40/61] 6907575: [classfile] add support for classfile dependency analysis Reviewed-by: ksrini --- .../com/sun/tools/classfile/Dependencies.java | 718 ++++++++++++++++++ .../com/sun/tools/classfile/Dependency.java | 90 +++ .../tools/javap/classfile/deps/GetDeps.java | 211 +++++ .../tools/javap/classfile/deps/T6907575.java | 71 ++ .../tools/javap/classfile/deps/T6907575.out | 8 + .../test/tools/javap/classfile/deps/p/C1.java | 37 + 6 files changed, 1135 insertions(+) create mode 100644 langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java create mode 100644 langtools/src/share/classes/com/sun/tools/classfile/Dependency.java create mode 100644 langtools/test/tools/javap/classfile/deps/GetDeps.java create mode 100644 langtools/test/tools/javap/classfile/deps/T6907575.java create mode 100644 langtools/test/tools/javap/classfile/deps/T6907575.out create mode 100644 langtools/test/tools/javap/classfile/deps/p/C1.java diff --git a/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java b/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java new file mode 100644 index 00000000000..4daa6ac8d15 --- /dev/null +++ b/langtools/src/share/classes/com/sun/tools/classfile/Dependencies.java @@ -0,0 +1,718 @@ +/* + * 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. 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. + */ +package com.sun.tools.classfile; + +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import com.sun.tools.classfile.Dependency.Finder; +import com.sun.tools.classfile.Dependency.Filter; +import com.sun.tools.classfile.Dependency.Location; +import com.sun.tools.classfile.Type.ArrayType; +import com.sun.tools.classfile.Type.ClassSigType; +import com.sun.tools.classfile.Type.ClassType; +import com.sun.tools.classfile.Type.MethodType; +import com.sun.tools.classfile.Type.SimpleType; +import com.sun.tools.classfile.Type.TypeParamType; +import com.sun.tools.classfile.Type.WildcardType; + +import static com.sun.tools.classfile.ConstantPool.*; + +/** + * A framework for determining {@link Dependency dependencies} between class files. + * + * A {@link Dependency.Finder finder} is used to identify the dependencies of + * individual classes. Some finders may return subtypes of {@code Dependency} to + * further characterize the type of dependency, such as a dependency on a + * method within a class. + * + * A {@link Dependency.Filter filter} may be used to restrict the set of + * dependencies found by a finder. + * + * Dependencies that are found may be passed to a {@link Dependencies.Recorder + * recorder} so that the dependencies can be stored in a custom data structure. + */ +public class Dependencies { + /** + * Thrown when a class file cannot be found. + */ + public static class ClassFileNotFoundException extends Exception { + private static final long serialVersionUID = 3632265927794475048L; + + public ClassFileNotFoundException(String className) { + super(className); + this.className = className; + } + + public ClassFileNotFoundException(String className, Throwable cause) { + this(className); + initCause(cause); + } + + public final String className; + } + + /** + * Thrown when an exception is found processing a class file. + */ + public static class ClassFileError extends Error { + private static final long serialVersionUID = 4111110813961313203L; + + public ClassFileError(Throwable cause) { + initCause(cause); + } + } + + /** + * Service provider interface to locate and read class files. + */ + public interface ClassFileReader { + /** + * Get the ClassFile object for a specified class. + * @param className the name of the class to be returned. + * @return the ClassFile for the given class + * @throws Dependencies#ClassFileNotFoundException if the classfile cannot be + * found + */ + public ClassFile getClassFile(String className) + throws ClassFileNotFoundException; + } + + /** + * Service provide interface to handle results. + */ + public interface Recorder { + /** + * Record a dependency that has been found. + * @param d + */ + public void addDependency(Dependency d); + } + + /** + * Get the default finder used to locate the dependencies for a class. + * @return the default finder + */ + public static Finder getDefaultFinder() { + return new APIDependencyFinder(AccessFlags.ACC_PRIVATE); + } + + /** + * Get a finder used to locate the API dependencies for a class. + * These include the superclass, superinterfaces, and classes referenced in + * the declarations of fields and methods. The fields and methods that + * are checked can be limited according to a specified access. + * The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC}, + * {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE}, + * {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for + * package private access. Members with greater than or equal accessibility + * to that specified will be searched for dependencies. + * @param access the access of members to be checked + * @return an API finder + */ + public static Finder getAPIFinder(int access) { + return new APIDependencyFinder(access); + } + + /** + * Get the finder used to locate the dependencies for a class. + * @return the finder + */ + public Finder getFinder() { + if (finder == null) + finder = getDefaultFinder(); + return finder; + } + + /** + * Set the finder used to locate the dependencies for a class. + * @param f the finder + */ + public void setFinder(Finder f) { + f.getClass(); // null check + finder = f; + } + + /** + * Get the default filter used to determine included when searching + * the transitive closure of all the dependencies. + * Unless overridden, the default filter accepts all dependencies. + * @return the default filter. + */ + public static Filter getDefaultFilter() { + return DefaultFilter.instance(); + } + + /** + * Get a filter which uses a regular expression on the target's class name + * to determine if a dependency is of interest. + * @param pattern the pattern used to match the target's class name + * @return a filter for matching the target class name with a regular expression + */ + public static Filter getRegexFilter(Pattern pattern) { + return new TargetRegexFilter(pattern); + } + + /** + * Get a filter which checks the package of a target's class name + * to determine if a dependency is of interest. The filter checks if the + * package of the target's class matches any of a set of given package + * names. The match may optionally match subpackages of the given names as well. + * @param packageNames the package names used to match the target's class name + * @param matchSubpackages whether or not to match subpackages as well + * @return a filter for checking the target package name against a list of package names + */ + public static Filter getPackageFilter(Set packageNames, boolean matchSubpackages) { + return new TargetPackageFilter(packageNames, matchSubpackages); + } + + /** + * Get the filter used to determine the dependencies included when searching + * the transitive closure of all the dependencies. + * Unless overridden, the default filter accepts all dependencies. + * @return the filter + */ + public Filter getFilter() { + if (filter == null) + filter = getDefaultFilter(); + return filter; + } + + /** + * Set the filter used to determine the dependencies included when searching + * the transitive closure of all the dependencies. + * @param f the filter + */ + public void setFilter(Filter f) { + f.getClass(); // null check + filter = f; + } + + /** + * Find the dependencies of a class, using the current + * {@link Dependencies#getFinder finder} and + * {@link Dependencies#getFilter filter}. + * The search may optionally include the transitive closure of all the + * filtered dependencies, by also searching in the classes named in those + * dependencies. + * @param classFinder a finder to locate class files + * @param rootClassNames the names of the root classes from which to begin + * searching + * @param transitiveClosure whether or not to also search those classes + * named in any filtered dependencies that are found. + * @return the set of dependencies that were found + * @throws ClassFileNotFoundException if a required class file cannot be found + * @throws ClassFileError if an error occurs while processing a class file, + * such as an error in the internal class file structure. + */ + public Set findAllDependencies( + ClassFileReader classFinder, Set rootClassNames, + boolean transitiveClosure) + throws ClassFileNotFoundException { + final Set results = new HashSet(); + Recorder r = new Recorder() { + public void addDependency(Dependency d) { + results.add(d); + } + }; + findAllDependencies(classFinder, rootClassNames, transitiveClosure, r); + return results; + } + + + + /** + * Find the dependencies of a class, using the current + * {@link Dependencies#getFinder finder} and + * {@link Dependencies#getFilter filter}. + * The search may optionally include the transitive closure of all the + * filtered dependencies, by also searching in the classes named in those + * dependencies. + * @param classFinder a finder to locate class files + * @param rootClassNames the names of the root classes from which to begin + * searching + * @param transitiveClosure whether or not to also search those classes + * named in any filtered dependencies that are found. + * @param recorder a recorder for handling the results + * @throws ClassFileNotFoundException if a required class file cannot be found + * @throws ClassFileError if an error occurs while processing a class file, + * such as an error in the internal class file structure. + */ + public void findAllDependencies( + ClassFileReader classFinder, Set rootClassNames, + boolean transitiveClosure, Recorder recorder) + throws ClassFileNotFoundException { + Set doneClasses = new HashSet(); + + getFinder(); // ensure initialized + getFilter(); // ensure initialized + + // Work queue of names of classfiles to be searched. + // Entries will be unique, and for classes that do not yet have + // dependencies in the results map. + Deque deque = new LinkedList(rootClassNames); + + String className; + while ((className = deque.poll()) != null) { + assert (!doneClasses.contains(className)); + doneClasses.add(className); + + ClassFile cf = classFinder.getClassFile(className); + + // The following code just applies the filter to the dependencies + // followed for the transitive closure. + for (Dependency d: finder.findDependencies(cf)) { + recorder.addDependency(d); + if (transitiveClosure && filter.accepts(d)) { + String cn = d.getTarget().getClassName(); + if (!doneClasses.contains(cn)) + deque.add(cn); + } + } + } + } + + private Filter filter; + private Finder finder; + + /** + * A location identifying a class. + */ + static class SimpleLocation implements Location { + public SimpleLocation(String className) { + this.className = className; + } + + /** + * Get the name of the class being depended on. This name will be used to + * locate the class file for transitive dependency analysis. + * @return the name of the class being depended on + */ + public String getClassName() { + return className; + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof SimpleLocation)) + return false; + return (className.equals(((SimpleLocation) other).className)); + } + + @Override + public int hashCode() { + return className.hashCode(); + } + + @Override + public String toString() { + return className; + } + + private String className; + } + + /** + * A dependency of one class on another. + */ + static class SimpleDependency implements Dependency { + public SimpleDependency(Location origin, Location target) { + this.origin = origin; + this.target = target; + } + + public Location getOrigin() { + return origin; + } + + public Location getTarget() { + return target; + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof SimpleDependency)) + return false; + SimpleDependency o = (SimpleDependency) other; + return (origin.equals(o.origin) && target.equals(o.target)); + } + + @Override + public int hashCode() { + return origin.hashCode() * 31 + target.hashCode(); + } + + @Override + public String toString() { + return origin + ":" + target; + } + + private Location origin; + private Location target; + } + + + /** + * This class accepts all dependencies. + */ + static class DefaultFilter implements Filter { + private static DefaultFilter instance; + + static DefaultFilter instance() { + if (instance == null) + instance = new DefaultFilter(); + return instance; + } + + public boolean accepts(Dependency dependency) { + return true; + } + } + + /** + * This class accepts those dependencies whose target's class name matches a + * regular expression. + */ + static class TargetRegexFilter implements Filter { + TargetRegexFilter(Pattern pattern) { + this.pattern = pattern; + } + + public boolean accepts(Dependency dependency) { + return pattern.matcher(dependency.getTarget().getClassName()).matches(); + } + + Pattern pattern; + } + + /** + * This class accepts those dependencies whose class name is in a given + * package. + */ + static class TargetPackageFilter implements Filter { + TargetPackageFilter(Set packageNames, boolean matchSubpackages) { + for (String pn: packageNames) { + if (pn.length() == 0) // implies null check as well + throw new IllegalArgumentException(); + } + this.packageNames = packageNames; + this.matchSubpackages = matchSubpackages; + } + + public boolean accepts(Dependency dependency) { + String cn = dependency.getTarget().getClassName(); + int lastSep = cn.lastIndexOf("/"); + String pn = (lastSep == -1 ? "" : cn.substring(0, lastSep)); + if (packageNames.contains(pn)) + return true; + + if (matchSubpackages) { + for (String n: packageNames) { + if (pn.startsWith(n + ".")) + return true; + } + } + + return false; + } + + Set packageNames; + boolean matchSubpackages; + } + + + + /** + * This class identifies class names directly or indirectly in the constant pool. + */ + static class ClassDependencyFinder extends BasicDependencyFinder { + public Iterable findDependencies(ClassFile classfile) { + Visitor v = new Visitor(classfile); + for (CPInfo cpInfo: classfile.constant_pool.entries()) { + v.scan(cpInfo); + } + return v.deps; + } + } + + /** + * This class identifies class names in the signatures of classes, fields, + * and methods in a class. + */ + static class APIDependencyFinder extends BasicDependencyFinder { + APIDependencyFinder(int access) { + switch (access) { + case AccessFlags.ACC_PUBLIC: + case AccessFlags.ACC_PROTECTED: + case AccessFlags.ACC_PRIVATE: + case 0: + showAccess = access; + break; + default: + throw new IllegalArgumentException("invalid access 0x" + + Integer.toHexString(access)); + } + } + + public Iterable findDependencies(ClassFile classfile) { + try { + Visitor v = new Visitor(classfile); + v.addClass(classfile.super_class); + v.addClasses(classfile.interfaces); + // inner classes? + for (Field f : classfile.fields) { + if (checkAccess(f.access_flags)) + v.scan(f.descriptor, f.attributes); + } + for (Method m : classfile.methods) { + if (checkAccess(m.access_flags)) { + v.scan(m.descriptor, m.attributes); + Exceptions_attribute e = + (Exceptions_attribute) m.attributes.get(Attribute.Exceptions); + if (e != null) + v.addClasses(e.exception_index_table); + } + } + return v.deps; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + boolean checkAccess(AccessFlags flags) { + // code copied from javap.Options.checkAccess + boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC); + boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED); + boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE); + boolean isPackage = !(isPublic || isProtected || isPrivate); + + if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage)) + return false; + else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage)) + return false; + else if ((showAccess == 0) && (isPrivate)) + return false; + else + return true; + } + + private int showAccess; + } + + static abstract class BasicDependencyFinder implements Finder { + private Map locations = new HashMap(); + + Location getLocation(String className) { + Location l = locations.get(className); + if (l == null) + locations.put(className, l = new SimpleLocation(className)); + return l; + } + + class Visitor implements ConstantPool.Visitor, Type.Visitor { + private ConstantPool constant_pool; + private Set deps; + private Location origin; + + Visitor(ClassFile classFile) { + try { + constant_pool = classFile.constant_pool; + origin = getLocation(classFile.getName()); + deps = new HashSet(); + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + void scan(Descriptor d, Attributes attrs) { + try { + scan(new Signature(d.index).getType(constant_pool)); + Signature_attribute sa = (Signature_attribute) attrs.get(Attribute.Signature); + if (sa != null) + scan(new Signature(sa.signature_index).getType(constant_pool)); + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + void scan(CPInfo cpInfo) { + cpInfo.accept(this, null); + } + + void scan(Type t) { + t.accept(this, null); + } + + void addClass(int index) throws ConstantPoolException { + if (index != 0) { + String name = constant_pool.getClassInfo(index).getBaseName(); + if (name != null) + addDependency(name); + } + } + + void addClasses(int[] indices) throws ConstantPoolException { + for (int i: indices) + addClass(i); + } + + private void addDependency(String name) { + deps.add(new SimpleDependency(origin, getLocation(name))); + } + + // ConstantPool.Visitor methods + + public Void visitClass(CONSTANT_Class_info info, Void p) { + try { + if (info.getName().startsWith("[")) + new Signature(info.name_index).getType(constant_pool).accept(this, null); + else + addDependency(info.getBaseName()); + return null; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + public Void visitDouble(CONSTANT_Double_info info, Void p) { + return null; + } + + public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) { + return visitRef(info, p); + } + + public Void visitFloat(CONSTANT_Float_info info, Void p) { + return null; + } + + public Void visitInteger(CONSTANT_Integer_info info, Void p) { + return null; + } + + public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) { + return visitRef(info, p); + } + + public Void visitLong(CONSTANT_Long_info info, Void p) { + return null; + } + + public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) { + try { + new Signature(info.type_index).getType(constant_pool).accept(this, null); + return null; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + public Void visitMethodref(CONSTANT_Methodref_info info, Void p) { + return visitRef(info, p); + } + + public Void visitString(CONSTANT_String_info info, Void p) { + return null; + } + + public Void visitUtf8(CONSTANT_Utf8_info info, Void p) { + return null; + } + + private Void visitRef(CPRefInfo info, Void p) { + try { + visitClass(info.getClassInfo(), p); + return null; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + // Type.Visitor methods + + private void findDependencies(Type t) { + if (t != null) + t.accept(this, null); + } + + private void findDependencies(List ts) { + if (ts != null) { + for (Type t: ts) + t.accept(this, null); + } + } + + public Void visitSimpleType(SimpleType type, Void p) { + return null; + } + + public Void visitArrayType(ArrayType type, Void p) { + findDependencies(type.elemType); + return null; + } + + public Void visitMethodType(MethodType type, Void p) { + findDependencies(type.paramTypes); + findDependencies(type.returnType); + findDependencies(type.throwsTypes); + return null; + } + + public Void visitClassSigType(ClassSigType type, Void p) { + findDependencies(type.superclassType); + findDependencies(type.superinterfaceTypes); + return null; + } + + public Void visitClassType(ClassType type, Void p) { + findDependencies(type.outerType); + addDependency(type.name); + findDependencies(type.typeArgs); + return null; + } + + public Void visitTypeParamType(TypeParamType type, Void p) { + findDependencies(type.classBound); + findDependencies(type.interfaceBounds); + return null; + } + + public Void visitWildcardType(WildcardType type, Void p) { + findDependencies(type.boundType); + return null; + } + } + } +} diff --git a/langtools/src/share/classes/com/sun/tools/classfile/Dependency.java b/langtools/src/share/classes/com/sun/tools/classfile/Dependency.java new file mode 100644 index 00000000000..b145f8081b9 --- /dev/null +++ b/langtools/src/share/classes/com/sun/tools/classfile/Dependency.java @@ -0,0 +1,90 @@ +/* + * 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. 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. + */ + +package com.sun.tools.classfile; + + +/** + * A directed relationship between two {@link Dependency.Location Location}s. + * Subtypes of {@code Dependency} may provide additional detail about the dependency. + * + * @see Dependency.Finder + * @see Dependency.Filter + * @see Dependencies + */ +public interface Dependency { + /** + * A filter used to select dependencies of interest, and to discard others. + */ + public interface Filter { + /** + * Return true if the dependency is of interest. + * @param dependency the dependency to be considered + * @return true if and only if the dependency is of interest. + */ + boolean accepts(Dependency dependency); + } + + /** + * An interface for finding the immediate dependencies of a given class file. + */ + public interface Finder { + /** + * Find the immediate dependencies of a given class file. + * @param classfile the class file to be examined + * @return the set of dependencies located in the given class file. + */ + public Iterable findDependencies(ClassFile classfile); + } + + + /** + * A location somewhere within a class. Subtypes of {@code Location} + * may be used to provide additional detail about the location. + */ + public interface Location { + /** + * Get the name of the class containing the location. + * This name will be used to locate the class file for transitive + * dependency analysis. + * @return the name of the class containing the location. + */ + String getClassName(); + } + + + /** + * Get the location that has the dependency. + * @return the location that has the dependency. + */ + Location getOrigin(); + + /** + * Get the location that is being depended upon. + * @return the location that is being depended upon. + */ + Location getTarget(); +} + diff --git a/langtools/test/tools/javap/classfile/deps/GetDeps.java b/langtools/test/tools/javap/classfile/deps/GetDeps.java new file mode 100644 index 00000000000..f5db9770ce6 --- /dev/null +++ b/langtools/test/tools/javap/classfile/deps/GetDeps.java @@ -0,0 +1,211 @@ +/* + * 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. 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. + */ + +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; +import javax.tools.*; + +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.Dependencies.*; +import com.sun.tools.classfile.Dependency.Location; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.util.Context; + +/** + * Demo utility for using the classfile dependency analysis API framework. + * + * Usage: + * getdeps [options] classes + * where options include: + * -classpath path where to find classes to analyze + * -p package-name restrict analysis to classes in this package + * (may be given multiple times) + * -r regex restrict analysis to packages matching pattern + * (-p and -r are exclusive) + * -rev invert the dependencies in the output + * -t transitive closure of dependencies + */ +public class GetDeps { + public static void main(String... args) throws Exception { + new GetDeps().run(args); + } + + void run(String... args) throws IOException, ClassFileNotFoundException { + PrintWriter pw = new PrintWriter(System.out); + try { + run(pw, args); + } finally { + pw.flush(); + } + } + + void run(PrintWriter out, String... args) throws IOException, ClassFileNotFoundException { + decodeArgs(args); + + final StandardJavaFileManager fm = new JavacFileManager(new Context(), false, null); + if (classpath != null) + fm.setLocation(StandardLocation.CLASS_PATH, classpath); + + ClassFileReader reader = new ClassFileReader(fm); + + Dependencies d = new Dependencies(); + + if (regex != null) + d.setFilter(Dependencies.getRegexFilter(Pattern.compile(regex))); + + if (packageNames.size() > 0) + d.setFilter(Dependencies.getPackageFilter(packageNames, false)); + + SortedRecorder r = new SortedRecorder(reverse); + + d.findAllDependencies(reader, rootClassNames, transitiveClosure, r); + + SortedMap> deps = r.getMap(); + for (Map.Entry> e: deps.entrySet()) { + out.println(e.getKey()); + for (Dependency dep: e.getValue()) { + out.println(" " + dep.getTarget()); + } + } + } + + void decodeArgs(String... args) { + rootClassNames = new TreeSet(); + packageNames = new TreeSet(); + + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg.equals("-classpath") && (i + 1 < args.length)) + classpath = getPathFiles(args[++i]); + else if (arg.equals("-p") && (i + 1 < args.length)) + packageNames.add(args[++i]); + else if (arg.equals("-r") && (i + 1 < args.length)) + regex = args[++i]; + else if (arg.equals("-rev")) + reverse = true; + else if (arg.equals("-t")) + transitiveClosure = true; + else if (arg.startsWith("-")) + throw new Error(arg); + else { + for ( ; i < args.length; i++) + rootClassNames.add(args[i]); + } + } + } + + List getPathFiles(String path) { + List files = new ArrayList(); + for (String p: path.split(File.pathSeparator)) { + if (p.length() > 0) + files.add(new File(p)); + } + return files; + } + + boolean transitiveClosure; + List classpath; + Set rootClassNames; + Set packageNames; + String regex; + boolean reverse; + + + static class ClassFileReader implements Dependencies.ClassFileReader { + private JavaFileManager fm; + + ClassFileReader(JavaFileManager fm) { + this.fm = fm; + } + + @Override + public ClassFile getClassFile(String className) throws ClassFileNotFoundException { + try { + JavaFileObject fo = fm.getJavaFileForInput( + StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS); + if (fo == null) + fo = fm.getJavaFileForInput( + StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS); + if (fo == null) + throw new ClassFileNotFoundException(className); + InputStream in = fo.openInputStream(); + try { + return ClassFile.read(in); + } finally { + in.close(); + } + } catch (ConstantPoolException e) { + throw new ClassFileNotFoundException(className, e); + } catch (IOException e) { + throw new ClassFileNotFoundException(className, e); + } + } + }; + + static class SortedRecorder implements Recorder { + public SortedRecorder(boolean reverse) { + this.reverse = reverse; + } + + public void addDependency(Dependency d) { + Location o = (reverse ? d.getTarget() : d.getOrigin()); + SortedSet odeps = map.get(o); + if (odeps == null) { + Comparator c = (reverse ? originComparator : targetComparator); + map.put(o, odeps = new TreeSet(c)); + } + odeps.add(d); + } + + public SortedMap> getMap() { + return map; + } + + private Comparator originComparator = new Comparator() { + public int compare(Dependency o1, Dependency o2) { + return o1.getTarget().toString().compareTo(o2.getOrigin().toString()); + } + }; + + private Comparator targetComparator = new Comparator() { + public int compare(Dependency o1, Dependency o2) { + return o1.getTarget().toString().compareTo(o2.getTarget().toString()); + } + }; + + private Comparator locationComparator = new Comparator() { + public int compare(Location o1, Location o2) { + return o1.toString().compareTo(o2.toString()); + } + }; + + private final SortedMap> map = + new TreeMap>(locationComparator); + + boolean reverse; + } + +} diff --git a/langtools/test/tools/javap/classfile/deps/T6907575.java b/langtools/test/tools/javap/classfile/deps/T6907575.java new file mode 100644 index 00000000000..23983ec7928 --- /dev/null +++ b/langtools/test/tools/javap/classfile/deps/T6907575.java @@ -0,0 +1,71 @@ +/* + * 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. 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. + */ + +/* + * @test + * @bug 6907575 + * @build GetDeps p.C1 + * @run main T6907575 + */ + +import java.io.*; + +public class T6907575 { + public static void main(String... args) throws Exception { + new T6907575().run(); + } + + void run() throws Exception { + String testSrc = System.getProperty("test.src"); + String testClasses = System.getProperty("test.classes"); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + GetDeps gd = new GetDeps(); + gd.run(pw, "-classpath", testClasses, "-t", "-p", "p", "p/C1"); + pw.close(); + System.out.println(sw); + + String ref = readFile(new File(testSrc, "T6907575.out")); + diff(sw.toString().replaceAll("[\r\n]+", "\n"), ref); + } + + void diff(String actual, String ref) throws Exception { + System.out.println("EXPECT:>>>" + ref + "<<<"); + System.out.println("ACTUAL:>>>" + actual + "<<<"); + if (!actual.equals(ref)) + throw new Exception("output not as expected"); + } + + String readFile(File f) throws IOException { + Reader r = new FileReader(f); + char[] buf = new char[(int) f.length()]; + int offset = 0; + int n; + while (offset < buf.length && (n = r.read(buf, offset, buf.length - offset)) != -1) + offset += n; + return new String(buf, 0, offset); + } +} diff --git a/langtools/test/tools/javap/classfile/deps/T6907575.out b/langtools/test/tools/javap/classfile/deps/T6907575.out new file mode 100644 index 00000000000..f1315914eb1 --- /dev/null +++ b/langtools/test/tools/javap/classfile/deps/T6907575.out @@ -0,0 +1,8 @@ +p/C1 + java/lang/Object + p/C2 +p/C2 + java/lang/Object + p/C3 +p/C3 + java/lang/Object diff --git a/langtools/test/tools/javap/classfile/deps/p/C1.java b/langtools/test/tools/javap/classfile/deps/p/C1.java new file mode 100644 index 00000000000..71f253e128b --- /dev/null +++ b/langtools/test/tools/javap/classfile/deps/p/C1.java @@ -0,0 +1,37 @@ +/* + * 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. 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. + */ + +package p; + +public class C1 { + C2 c2; +} + +class C2 { + C3 c3; +} + +class C3 { +} From 8dd1b6ace1b426723b80eee9328f25da8f3a805e Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Mon, 14 Dec 2009 09:51:09 -0700 Subject: [PATCH 41/61] 6648438: 4/4 src/share/vm/prims/jvmtiEnv.cpp:457 assert(phase == JVMTI_PHASE_LIVE,"sanity check") Return error on invalid JVMTI_PHASE instead of asserting. Reviewed-by: dholmes, ohair --- hotspot/src/share/vm/prims/jvmtiEnv.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/prims/jvmtiEnv.cpp b/hotspot/src/share/vm/prims/jvmtiEnv.cpp index a19c48972c8..77d63520d95 100644 --- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp +++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp @@ -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 @@ -408,8 +408,10 @@ JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) { if (phase == JVMTI_PHASE_ONLOAD) { Arguments::append_sysclasspath(segment); return JVMTI_ERROR_NONE; - } else { - assert(phase == JVMTI_PHASE_LIVE, "sanity check"); + } 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); @@ -430,6 +432,8 @@ JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) { } ClassLoader::add_to_list(zip_entry); return JVMTI_ERROR_NONE; + } else { + return JVMTI_ERROR_WRONG_PHASE; } } /* end AddToBootstrapClassLoaderSearch */ @@ -448,11 +452,12 @@ JvmtiEnv::AddToSystemClassLoaderSearch(const char* segment) { } } 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); @@ -501,6 +506,8 @@ JvmtiEnv::AddToSystemClassLoaderSearch(const char* segment) { } return JVMTI_ERROR_NONE; + } else { + return JVMTI_ERROR_WRONG_PHASE; } } /* end AddToSystemClassLoaderSearch */ From ce7894453911dc2fa08a9b460562c614cb2ab4b7 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Mon, 14 Dec 2009 10:05:36 -0700 Subject: [PATCH 42/61] 6849968: 3/2 JVMTI tests fails on jdk5.0 with hs14 If a JVMTI agent asks for version 1.0, then it should get version 1.0 semantics. Reviewed-by: dholmes, ohair --- hotspot/src/share/vm/prims/jvmtiEnv.cpp | 19 +++++++++-- hotspot/src/share/vm/prims/jvmtiEnvBase.cpp | 23 ++++++++++++-- hotspot/src/share/vm/prims/jvmtiEnvBase.hpp | 8 +++-- hotspot/src/share/vm/prims/jvmtiExport.cpp | 35 +++++++++++++++++++-- hotspot/src/share/vm/prims/jvmtiExport.hpp | 4 ++- hotspot/src/share/vm/prims/jvmtiHpp.xsl | 6 ++-- 6 files changed, 81 insertions(+), 14 deletions(-) diff --git a/hotspot/src/share/vm/prims/jvmtiEnv.cpp b/hotspot/src/share/vm/prims/jvmtiEnv.cpp index 77d63520d95..3de748f216e 100644 --- a/hotspot/src/share/vm/prims/jvmtiEnv.cpp +++ b/hotspot/src/share/vm/prims/jvmtiEnv.cpp @@ -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. @@ -408,6 +408,11 @@ JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) { if (phase == JVMTI_PHASE_ONLOAD) { Arguments::append_sysclasspath(segment); return JVMTI_ERROR_NONE; + } 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 @@ -2857,6 +2862,14 @@ JvmtiEnv::IsMethodSynthetic(methodOop method_oop, jboolean* is_synthetic_ptr) { // 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 --git a/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp b/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp index 3152e91c3a5..f7a37a90a66 100644 --- a/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp +++ b/hotspot/src/share/vm/prims/jvmtiEnvBase.cpp @@ -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 @@ -94,7 +94,26 @@ JvmtiEnvBase::initialize() { } -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; diff --git a/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp b/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp index 477725ffec5..d2f3de552b8 100644 --- a/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp +++ b/hotspot/src/share/vm/prims/jvmtiEnvBase.hpp @@ -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 @@ class JvmtiEnvBase : public CHeapObj { 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 @@ class JvmtiEnvBase : public CHeapObj { int _native_method_prefix_count; protected: - JvmtiEnvBase(); + JvmtiEnvBase(jint version); ~JvmtiEnvBase(); void dispose(); void env_dispose(); @@ -122,6 +123,9 @@ class JvmtiEnvBase : public CHeapObj { bool is_valid() { return _magic == JVMTI_MAGIC; } + 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 --git a/hotspot/src/share/vm/prims/jvmtiExport.cpp b/hotspot/src/share/vm/prims/jvmtiExport.cpp index e51e5b3d467..cbdd7234ab8 100644 --- a/hotspot/src/share/vm/prims/jvmtiExport.cpp +++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp @@ -319,7 +319,27 @@ address JvmtiExport::get_field_modification_count_addr() { 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 @@ JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { __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 @@ JvmtiExport::get_jvmti_interface(JavaVM *jvm, void **penv, jint version) { } } + +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); } diff --git a/hotspot/src/share/vm/prims/jvmtiExport.hpp b/hotspot/src/share/vm/prims/jvmtiExport.hpp index 54a9416f425..20214aecf9c 100644 --- a/hotspot/src/share/vm/prims/jvmtiExport.hpp +++ b/hotspot/src/share/vm/prims/jvmtiExport.hpp @@ -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 @@ -236,6 +236,8 @@ class JvmtiExport : public AllStatic { 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 --git a/hotspot/src/share/vm/prims/jvmtiHpp.xsl b/hotspot/src/share/vm/prims/jvmtiHpp.xsl index 3b3b23e90f6..e5dd49ffc6c 100644 --- a/hotspot/src/share/vm/prims/jvmtiHpp.xsl +++ b/hotspot/src/share/vm/prims/jvmtiHpp.xsl @@ -1,6 +1,6 @@