view graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java @ 9501:bef43373de39

coalesce allocations during escape analysis
author Lukas Stadler <lukas.stadler@jku.at>
date Mon, 29 Apr 2013 14:53:08 +0200
parents b9cf7d0b598e
children 106f0a0acafa
line wrap: on
line source

/*
 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.graal.virtual.phases.ea;

import static com.oracle.graal.virtual.phases.ea.PartialEscapeAnalysisPhase.*;

import java.util.*;

import com.oracle.graal.api.code.*;
import com.oracle.graal.graph.*;
import com.oracle.graal.nodes.*;
import com.oracle.graal.nodes.extended.*;
import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
import com.oracle.graal.nodes.virtual.*;

class BlockState {

    private final IdentityHashMap<VirtualObjectNode, ObjectState> objectStates = new IdentityHashMap<>();
    private final IdentityHashMap<ValueNode, VirtualObjectNode> objectAliases;
    private final IdentityHashMap<ValueNode, ValueNode> scalarAliases;
    final HashMap<ReadCacheEntry, ValueNode> readCache;

    static class ReadCacheEntry {

        public final Object identity;
        public final ValueNode object;

        public ReadCacheEntry(Object identity, ValueNode object) {
            this.identity = identity;
            this.object = object;
        }

        @Override
        public int hashCode() {
            int result = 31 + ((identity == null) ? 0 : identity.hashCode());
            return 31 * result + ((object == null) ? 0 : object.hashCode());
        }

        @Override
        public boolean equals(Object obj) {
            ReadCacheEntry other = (ReadCacheEntry) obj;
            return identity == other.identity && object == other.object;
        }

        @Override
        public String toString() {
            return object + ":" + identity;
        }
    }

    public BlockState() {
        objectAliases = new IdentityHashMap<>();
        scalarAliases = new IdentityHashMap<>();
        readCache = new HashMap<>();
    }

    public BlockState(BlockState other) {
        for (Map.Entry<VirtualObjectNode, ObjectState> entry : other.objectStates.entrySet()) {
            objectStates.put(entry.getKey(), entry.getValue().cloneState());
        }
        objectAliases = new IdentityHashMap<>(other.objectAliases);
        scalarAliases = new IdentityHashMap<>(other.scalarAliases);
        readCache = new HashMap<>(other.readCache);
    }

    public void addReadCache(ValueNode object, Object identity, ValueNode value) {
        ValueNode cacheObject;
        ObjectState obj = getObjectState(object);
        if (obj != null) {
            assert !obj.isVirtual();
            cacheObject = obj.getMaterializedValue();
        } else {
            cacheObject = object;
        }
        readCache.put(new ReadCacheEntry(identity, cacheObject), value);
    }

    public ValueNode getReadCache(ValueNode object, Object identity) {
        ValueNode cacheObject;
        ObjectState obj = getObjectState(object);
        if (obj != null) {
            assert !obj.isVirtual();
            cacheObject = obj.getMaterializedValue();
        } else {
            cacheObject = object;
        }
        ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject));
        obj = getObjectState(cacheValue);
        if (obj != null) {
            assert !obj.isVirtual();
            cacheValue = obj.getMaterializedValue();
        } else {
            cacheValue = getScalarAlias(cacheValue);
        }
        return cacheValue;
    }

    public void killReadCache(Object identity) {
        if (identity == LocationNode.ANY_LOCATION) {
            readCache.clear();
        } else {
            Iterator<Map.Entry<ReadCacheEntry, ValueNode>> iter = readCache.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<ReadCacheEntry, ValueNode> entry = iter.next();
                if (entry.getKey().identity == identity) {
                    iter.remove();
                }
            }
        }
    }

    public ObjectState getObjectState(VirtualObjectNode object) {
        assert objectStates.containsKey(object);
        return objectStates.get(object);
    }

    public ObjectState getObjectStateOptional(VirtualObjectNode object) {
        return objectStates.get(object);
    }

    public ObjectState getObjectState(ValueNode value) {
        VirtualObjectNode object = objectAliases.get(value);
        return object == null ? null : getObjectState(object);
    }

    public BlockState cloneState() {
        return new BlockState(this);
    }

    public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, GraphEffectList materializeEffects) {
        PartialEscapeClosure.METRIC_MATERIALIZATIONS.increment();
        List<AllocatedObjectNode> objects = new ArrayList<>(2);
        List<ValueNode> values = new ArrayList<>(8);
        List<Integer> lockCounts = new ArrayList<>(2);
        List<ValueNode> otherAllocations = new ArrayList<>(2);
        materializeWithCommit(virtual, objects, lockCounts, values, otherAllocations, state);

        materializeEffects.addMaterializationBefore(fixed, objects, lockCounts, values, otherAllocations);
    }

    private void materializeWithCommit(VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<Integer> lockCounts, List<ValueNode> values, List<ValueNode> otherAllocations, EscapeState state) {
        trace("materializing %s", virtual);
        ObjectState obj = getObjectState(virtual);
        if (obj.getLockCount() > 0 && obj.virtual.type().isArray()) {
            throw new BailoutException("array materialized with lock");
        }

        ValueNode[] entries = obj.getEntries();
        ValueNode representation = virtual.getMaterializedRepresentation(entries, obj.getLockCount());
        obj.escape(representation, state);
        if (representation instanceof AllocatedObjectNode) {
            objects.add((AllocatedObjectNode) representation);
            lockCounts.add(obj.getLockCount());
            int pos = values.size();
            while (values.size() < pos + entries.length) {
                values.add(null);
            }
            for (int i = 0; i < entries.length; i++) {
                ObjectState entryObj = getObjectState(entries[i]);
                if (entryObj != null) {
                    if (entryObj.isVirtual()) {
                        materializeWithCommit(entryObj.getVirtualObject(), objects, lockCounts, values, otherAllocations, state);
                    }
                    values.set(pos + i, entryObj.getMaterializedValue());
                } else {
                    values.set(pos + i, entries[i]);
                }
            }
            if (virtual instanceof VirtualInstanceNode) {
                VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
                for (int i = 0; i < entries.length; i++) {
                    readCache.put(new ReadCacheEntry(instance.field(i), representation), values.get(pos + i));
                }
            }
        } else {
            otherAllocations.add(representation);
            assert obj.getLockCount() == 0;
        }
    }

    void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node, NodeBitMap usages) {
        objectAliases.put(node, virtual);
        if (node.isAlive()) {
            for (Node usage : node.usages()) {
                markVirtualUsages(usage, usages);
            }
        }
    }

    private void markVirtualUsages(Node node, NodeBitMap usages) {
        if (!usages.isNew(node)) {
            usages.mark(node);
        }
        if (node instanceof VirtualState) {
            for (Node usage : node.usages()) {
                markVirtualUsages(usage, usages);
            }
        }
    }

    public void addObject(VirtualObjectNode virtual, ObjectState state) {
        objectStates.put(virtual, state);
    }

    public void addScalarAlias(ValueNode alias, ValueNode value) {
        scalarAliases.put(alias, value);
    }

    public ValueNode getScalarAlias(ValueNode alias) {
        ValueNode result = scalarAliases.get(alias);
        return result == null ? alias : result;
    }

    public Iterable<ObjectState> getStates() {
        return objectStates.values();
    }

    public Collection<VirtualObjectNode> getVirtualObjects() {
        return objectAliases.values();
    }

    @Override
    public String toString() {
        return objectStates + " " + readCache;
    }

    public static BlockState meetAliases(List<BlockState> states) {
        BlockState newState = new BlockState();

        newState.objectAliases.putAll(states.get(0).objectAliases);
        for (int i = 1; i < states.size(); i++) {
            BlockState state = states.get(i);
            for (Map.Entry<ValueNode, VirtualObjectNode> entry : states.get(0).objectAliases.entrySet()) {
                if (state.objectAliases.containsKey(entry.getKey())) {
                    assert state.objectAliases.get(entry.getKey()) == entry.getValue();
                } else {
                    newState.objectAliases.remove(entry.getKey());
                }
            }
        }

        newState.scalarAliases.putAll(states.get(0).scalarAliases);
        for (int i = 1; i < states.size(); i++) {
            BlockState state = states.get(i);
            for (Map.Entry<ValueNode, ValueNode> entry : states.get(0).scalarAliases.entrySet()) {
                if (state.scalarAliases.containsKey(entry.getKey())) {
                    assert state.scalarAliases.get(entry.getKey()) == entry.getValue();
                } else {
                    newState.scalarAliases.remove(entry.getKey());
                }
            }
        }

        return newState;
    }

    public Map<ReadCacheEntry, ValueNode> getReadCache() {
        return readCache;
    }

    public boolean equivalentTo(BlockState other) {
        if (this == other) {
            return true;
        }
        boolean objectAliasesEqual = compareMaps(objectAliases, other.objectAliases);
        boolean objectStatesEqual = compareMaps(objectStates, other.objectStates);
        boolean readCacheEqual = compareMapsNoSize(readCache, other.readCache);
        boolean scalarAliasesEqual = scalarAliases.equals(other.scalarAliases);
        return objectAliasesEqual && objectStatesEqual && readCacheEqual && scalarAliasesEqual;
    }

    private static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
        if (left.size() != right.size()) {
            return false;
        }
        return compareMapsNoSize(left, right);
    }

    private static <K, V> boolean compareMapsNoSize(Map<K, V> left, Map<K, V> right) {
        if (left == right) {
            return true;
        }
        for (Map.Entry<K, V> entry : right.entrySet()) {
            K key = entry.getKey();
            V value = entry.getValue();
            assert value != null;
            V otherValue = left.get(key);
            if (otherValue != value && !value.equals(otherValue)) {
                return false;
            }
        }
        return true;
    }

}