--- old/src/share/vm/opto/cfgnode.cpp Thu Jul 30 13:22:14 2009 +++ new/src/share/vm/opto/cfgnode.cpp Thu Jul 30 13:22:14 2009 @@ -1792,15 +1792,12 @@ if (UseCompressedOops && can_reshape && progress == NULL) { bool may_push = true; bool has_decodeN = false; - Node* in_decodeN = NULL; for (uint i=1; iis_DecodeN() && ii->bottom_type() == bottom_type()) { - // Note: in_decodeN is used only to define the type of new phi. - // Find a non dead path otherwise phi type will be wrong. + // Do optimization if a non dead path exist. if (ii->in(1)->bottom_type() != Type::TOP) { has_decodeN = true; - in_decodeN = ii->in(1); } } else if (!ii->is_Phi()) { may_push = false; @@ -1809,7 +1806,9 @@ if (has_decodeN && may_push) { PhaseIterGVN *igvn = phase->is_IterGVN(); - PhiNode *new_phi = PhiNode::make_blank(in(0), in_decodeN); + // Make narrow type for new phi. + const Type* narrow_t = TypeNarrowOop::make(this->bottom_type()->is_ptr()); + PhiNode* new_phi = new (phase->C, r->req()) PhiNode(r, narrow_t); uint orig_cnt = req(); for (uint i=1; ias_Phi() == this) { new_ii = new_phi; } else { - new_ii = new (phase->C, 2) EncodePNode(ii, in_decodeN->bottom_type()); + new_ii = new (phase->C, 2) EncodePNode(ii, narrow_t); igvn->register_new_node_with_optimizer(new_ii); } } --- /dev/null Thu Jul 30 13:22:15 2009 +++ new/test/compiler/6865031/Test.java Thu Jul 30 13:22:15 2009 @@ -0,0 +1,650 @@ +/* + * Copyright 2009 Goldman Sachs International. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along 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 6865031 + * @summary Application gives bad result (throws bad exception) with compressed oops + * @run main/othervm -XX:+UseCompressedOops -XX:HeapBaseMinAddress=32g -XX:-LoopUnswitching -XX:CompileCommand=inline,AbstractMemoryEfficientList.equals Test hello goodbye + */ + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +interface MyList { + public int size(); + public Object set(final int index, final Object element); + public Object get(final int index); +} + +abstract class AbstractMemoryEfficientList implements MyList { + abstract public int size(); + abstract public Object get(final int index); + abstract public Object set(final int index, final Object element); + + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof MyList)) { + return false; + } + + final MyList that = (MyList) o; + if (this.size() != that.size()) { + return false; + } + + for (int i = 0; i < this.size(); i++) { + try { + if (!((this.get(i)).equals(that.get(i)))) { + return false; + } + } catch (IndexOutOfBoundsException e) { + System.out.println("THROWING RT EXC"); + System.out.println("concurrent modification of this:" + this.getClass() + ":" + System.identityHashCode(this) + "; that:" + that.getClass() + ":" + System.identityHashCode(that) + "; i:" + i); + e.printStackTrace(); + System.exit(97); + throw new RuntimeException("concurrent modification of this:" + this.getClass() + ":" + System.identityHashCode(this) + "; that:" + that.getClass() + ":" + System.identityHashCode(that) + "; i:" + i, e); + } + } + return true; + } + + public int hashCode() { + int hashCode = 1; + for (int i = 0; i < this.size(); i++) { + Object obj = this.get(i); + hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); + } + return hashCode; + } +} + +final class SingletonList extends AbstractMemoryEfficientList { + private Object element1; + + SingletonList(final Object obj1) { + super(); + this.element1 = obj1; + } + + public int size() { + return 1; + } + + public Object get(final int index) { + if (index == 0) { + return this.element1; + } else { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); + } + } + + public Object set(final int index, final Object element) { + if (index == 0) { + final Object previousElement = this.element1; + this.element1 = element; + return previousElement; + } else { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); + } + } +} + +final class DoubletonList extends AbstractMemoryEfficientList { + private Object element1; + private Object element2; + + DoubletonList(final Object obj1, final Object obj2) { + this.element1 = obj1; + this.element2 = obj2; + } + + public int size() { + return 2; + } + + public Object get(final int index) { + switch (index) { + case 0 : return this.element1; + case 1 : return this.element2; + default: throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); + } + } + + public Object set(final int index, final Object element) { + switch (index) { + case 0 : + { + final Object previousElement = this.element1; + this.element1 = element; + return previousElement; + } + case 1 : + { + final Object previousElement = this.element2; + this.element2 = element; + return previousElement; + } + default : throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size()); + } + } +} + +class WeakPool { + protected static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int MAXIMUM_CAPACITY = 1 << 30; + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + protected Entry[] table; + + private int size; + protected int threshold; + private final float loadFactor; + private final ReferenceQueue queue = new ReferenceQueue(); + + public WeakPool() + { + this.loadFactor = DEFAULT_LOAD_FACTOR; + threshold = DEFAULT_INITIAL_CAPACITY; + table = new Entry[DEFAULT_INITIAL_CAPACITY]; + } + + /** + * Check for equality of non-null reference x and possibly-null y. By + * default uses Object.equals. + */ + private boolean eq(Object x, Object y) + { + return x == y || x.equals(y); + } + + /** + * Return index for hash code h. + */ + private int indexFor(int h, int length) + { + return h & length - 1; + } + + /** + * Expunge stale entries from the table. + */ + private void expungeStaleEntries() + { + Object r; + while ((r = queue.poll()) != null) + { + Entry e = (Entry) r; + int h = e.hash; + int i = indexFor(h, table.length); + + // System.out.println("EXPUNGING " + h); + Entry prev = table[i]; + Entry p = prev; + while (p != null) + { + Entry next = p.next; + if (p == e) + { + if (prev == e) + { + table[i] = next; + } + else + { + prev.next = next; + } + e.next = null; // Help GC + size--; + break; + } + prev = p; + p = next; + } + } + } + + /** + * Return the table after first expunging stale entries + */ + private Entry[] getTable() + { + expungeStaleEntries(); + return table; + } + + /** + * Returns the number of key-value mappings in this map. + * This result is a snapshot, and may not reflect unprocessed + * entries that will be removed before next attempted access + * because they are no longer referenced. + */ + public int size() + { + if (size == 0) + { + return 0; + } + expungeStaleEntries(); + return size; + } + + /** + * Returns true if this map contains no key-value mappings. + * This result is a snapshot, and may not reflect unprocessed + * entries that will be removed before next attempted access + * because they are no longer referenced. + */ + public boolean isEmpty() + { + return size() == 0; + } + + /** + * Returns the value stored in the pool that equals the requested key + * or null if the map contains no mapping for + * this key (or the key is null) + * + * @param key the key whose equals value is to be returned. + * @return the object that is equal the specified key, or + * null if key is null or no object in the pool equals the key. + */ + public V get(V key) + { + if (key == null) + { + return null; + } + int h = key.hashCode(); + Entry[] tab = getTable(); + int index = indexFor(h, tab.length); + Entry e = tab[index]; + while (e != null) + { + V candidate = e.get(); + if (e.hash == h && eq(key, candidate)) + { + return candidate; + } + e = e.next; + } + return null; + } + + /** + * Returns the entry associated with the specified key in the HashMap. + * Returns null if the HashMap contains no mapping for this key. + */ + Entry getEntry(Object key) + { + int h = key.hashCode(); + Entry[] tab = getTable(); + int index = indexFor(h, tab.length); + Entry e = tab[index]; + while (e != null && !(e.hash == h && eq(key, e.get()))) + { + e = e.next; + } + return e; + } + + /** + * Places the object into the pool. If the object is null, nothing happens. + * If an equal object already exists, it is not replaced. + * + * @param key the object to put into the pool. key may be null. + * @return the object in the pool that is equal to the key, or the newly placed key if no such object existed when put was called + */ + public V put(V key) + { + if (key == null) + { + return null; + } + int h = key.hashCode(); + Entry[] tab = getTable(); + int i = indexFor(h, tab.length); + + for (Entry e = tab[i]; e != null; e = e.next) + { + V candidate = e.get(); + if (h == e.hash && eq(key, candidate)) + { + return candidate; + } + } + + tab[i] = new Entry(key, queue, h, tab[i]); + + if (++size >= threshold) + { + resize(tab.length * 2); + } + + // System.out.println("Added " + key + " to pool"); + return key; + } + + /** + * Rehashes the contents of this map into a new array with a + * larger capacity. This method is called automatically when the + * number of keys in this map reaches its threshold. + *

+ * If current capacity is MAXIMUM_CAPACITY, this method does not + * resize the map, but but sets threshold to Integer.MAX_VALUE. + * This has the effect of preventing future calls. + * + * @param newCapacity the new capacity, MUST be a power of two; + * must be greater than current capacity unless current + * capacity is MAXIMUM_CAPACITY (in which case value + * is irrelevant). + */ + void resize(int newCapacity) + { + Entry[] oldTable = getTable(); + int oldCapacity = oldTable.length; + if (oldCapacity == MAXIMUM_CAPACITY) + { + threshold = Integer.MAX_VALUE; + return; + } + + Entry[] newTable = new Entry[newCapacity]; + transfer(oldTable, newTable); + table = newTable; + + /* + * If ignoring null elements and processing ref queue caused massive + * shrinkage, then restore old table. This should be rare, but avoids + * unbounded expansion of garbage-filled tables. + */ + if (size >= threshold / 2) + { + threshold = (int) (newCapacity * loadFactor); + } + else + { + expungeStaleEntries(); + transfer(newTable, oldTable); + table = oldTable; + } + } + + /** + * Transfer all entries from src to dest tables + */ + private void transfer(Entry[] src, Entry[] dest) + { + for (int j = 0; j < src.length; ++j) + { + Entry e = src[j]; + src[j] = null; + while (e != null) + { + Entry next = e.next; + Object key = e.get(); + if (key == null) + { + e.next = null; // Help GC + size--; + } + else + { + int i = indexFor(e.hash, dest.length); + e.next = dest[i]; + dest[i] = e; + } + e = next; + } + } + } + + /** + * Removes the object in the pool that equals the key. + * + * @param key + * @return previous value associated with specified key, or null + * if there was no mapping for key or the key is null. + */ + public V removeFromPool(V key) + { + if (key == null) + { + return null; + } + int h = key.hashCode(); + Entry[] tab = getTable(); + int i = indexFor(h, tab.length); + Entry prev = tab[i]; + Entry e = prev; + + while (e != null) + { + Entry next = e.next; + V candidate = e.get(); + if (h == e.hash && eq(key, candidate)) + { + size--; + if (prev == e) + { + tab[i] = next; + } + else + { + prev.next = next; + } + return candidate; + } + prev = e; + e = next; + } + + return null; + } + + /** + * Removes all mappings from this map. + */ + public void clear() + { + // clear out ref queue. We don't need to expunge entries + // since table is getting cleared. + while (queue.poll() != null) + { + // nop + } + + table = new Entry[DEFAULT_INITIAL_CAPACITY]; + threshold = DEFAULT_INITIAL_CAPACITY; + size = 0; + + // Allocation of array may have caused GC, which may have caused + // additional entries to go stale. Removing these entries from the + // reference queue will make them eligible for reclamation. + while (queue.poll() != null) + { + // nop + } + } + + /** + * The entries in this hash table extend WeakReference, using its main ref + * field as the key. + */ + protected static class Entry + extends WeakReference + { + private final int hash; + private Entry next; + + /** + * Create new entry. + */ + Entry(final V key, final ReferenceQueue queue, final int hash, final Entry next) + { + super(key, queue); + this.hash = hash; + this.next = next; + } + + public V getKey() + { + return super.get(); + } + + public boolean equals(Object o) + { + if (!(o instanceof WeakPool.Entry)) + { + return false; + } + WeakPool.Entry that = (WeakPool.Entry) o; + V k1 = this.getKey(); + V k2 = that.getKey(); + return (k1==k2 || k1.equals(k2)); + } + + public int hashCode() + { + return this.hash; + } + + public String toString() + { + return String.valueOf(this.getKey()); + } + } +} + +final class MultiSynonymKey { + private List keys; + + public MultiSynonymKey() { + keys = new ArrayList(); + } + + public MultiSynonymKey(MyList... arg) { + keys = Arrays.asList(arg); + } + + public List getKeys() { + return keys; + } + + public int hashCode() { + return this.getKeys().hashCode(); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof MultiSynonymKey)) { + return false; + } + + MultiSynonymKey that = (MultiSynonymKey) obj; + return this.getKeys().equals(that.getKeys()); + } + + public String toString() { + return this.getClass().getName() + this.getKeys().toString(); + } +} + +public class Test extends Thread { + static public Test test; + static private byte[] arg1; + static private byte[] arg2; + static public WeakPool wp; + public volatile MultiSynonymKey ml1; + public volatile MultiSynonymKey ml2; + private volatile MultiSynonymKey ml3; + + public void run() { + int count=0; + while (true) { + try { + Thread.sleep(10); + } catch (Exception e) {} + synchronized (wp) { + ml2 = new MultiSynonymKey(new DoubletonList(new String(arg1), new String(arg2))); + wp.put(ml2); + ml3 = new MultiSynonymKey(new DoubletonList(new String(arg1), new String(arg2))); + } + try { + Thread.sleep(10); + } catch (Exception e) {} + synchronized (wp) { + ml1 = new MultiSynonymKey(new SingletonList(new String(arg1))); + wp.put(ml1); + ml3 = new MultiSynonymKey(new SingletonList(new String(arg1))); + } + if (count++==100) + System.exit(95); + } + } + + public static void main(String[] args) throws Exception { + wp = new WeakPool(); + test = new Test(); + + test.arg1 = args[0].getBytes(); + test.arg2 = args[1].getBytes(); + + test.ml1 = new MultiSynonymKey(new SingletonList(new String(test.arg1))); + test.ml2 = new MultiSynonymKey(new DoubletonList(new String(test.arg1), new String(test.arg2))); + test.ml3 = new MultiSynonymKey(new DoubletonList(new String(test.arg1), new String(test.arg2))); + + wp.put(test.ml1); + wp.put(test.ml2); + + test.setDaemon(true); + test.start(); + + int counter = 0; + while (true) { + synchronized (wp) { + MultiSynonymKey foo = test.ml3; + + if (wp.put(foo) == foo) { + // System.out.println("foo " + counter); + // System.out.println(foo); + } + } + counter++; + } + } + + private boolean eq(Object x, Object y) { + return x == y || x.equals(y); + } +}