blob: 841dd1f6a8e0ca0f020dc00238c2790c8a752ecd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2017 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.qvtd.compiler.internal.qvts2qvts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.utilities.NameUtil;
import org.eclipse.qvtd.pivot.qvtschedule.DatumConnection;
import org.eclipse.qvtd.pivot.qvtschedule.Region;
import org.eclipse.qvtd.pivot.qvtschedule.ScheduledRegion;
/**
* ScheduleCache provides the immutable caches used during the schedule index allocation.
*/
public abstract class ScheduleCache extends Region2Depth
{
/**
* The overall RootScheduledRegion.
*/
protected final @NonNull ScheduledRegion rootScheduledRegion;
/**
* All transitively callable regions within the rootScheduledRegion (no OperationRegions).
*/
protected final @NonNull List<@NonNull Region> callableRegions;
/**
* Cached list of all incoming connections per-region; excludes recursions.
*/
private final @NonNull Map<@NonNull Region, @NonNull List<@NonNull DatumConnection<?>>> region2incomingConnections = new HashMap<>();
/**
* Cached list of all recursive/looping connections per-region.
*/
private final @NonNull Map<@NonNull Region, @NonNull List<@NonNull DatumConnection<?>>> region2loopingConnections = new HashMap<>();
/**
* Cached list of all outgoing connections per-region; excludes recursions.
*/
private final @NonNull Map<@NonNull Region, @NonNull List<@NonNull DatumConnection<?>>> region2outgoingConnections = new HashMap<>();
/**
* Cached list of all source regions per-connection and whether the source has unserviced content for the connection.
*/
private final @NonNull Map<@NonNull DatumConnection<?>, @NonNull List<@NonNull Region>> connection2sourceRegions = new HashMap<>();
/**
* Cached list of all target regions per-connection and whether the connection has unserviced content for the target.
*/
private final @NonNull Map<@NonNull DatumConnection<?>, @NonNull List<@NonNull Region>> connection2targetRegions = new HashMap<>();
/**
* The regions that have no outgoing passed connections.
*/
private final @NonNull Set<@NonNull Region> unpassedRegions = new HashSet<>();
protected ScheduleCache(@NonNull ScheduledRegion rootScheduledRegion) {
this.rootScheduledRegion = rootScheduledRegion;
this.callableRegions = analyzeRegions(rootScheduledRegion, new ArrayList<>());
Collections.sort(this.callableRegions, NameUtil.NAMEABLE_COMPARATOR);
for (@NonNull Region region : this.callableRegions) {
getRegionDepth(region);
}
//
// Initialize the incoming/looping/outgoing connection analyses of each region
//
for (@NonNull Region region : this.callableRegions) {
analyzeConnections(region);
}
//
// Initialize the source/target of each connection.
// Compute the set of all connections that are not passed.
//
for (@NonNull Region region : this.callableRegions) {
analyzeSourcesAndTargets(region);
}
}
/**
* Initialize the incoming/looping/outgoing connection analyses of each region
*/
private void analyzeConnections(@NonNull Region region) {
List<@NonNull DatumConnection<?>> incomingConnections = new ArrayList<>();
List<@NonNull DatumConnection<?>> loopingConnections = new ArrayList<>();
List<@NonNull DatumConnection<?>> outgoingConnections = new ArrayList<>();
for (@NonNull DatumConnection<?> connection : region.getIncomingConnections()) {
for (@NonNull Region sourceRegion : connection.getSourceRegions()) {
if (region == sourceRegion) {
if (!loopingConnections.contains(connection)) {
loopingConnections.add(connection);
}
}
else {
if (!incomingConnections.contains(connection)) {
incomingConnections.add(connection);
}
}
}
}
for (@NonNull DatumConnection<?> connection : region.getNextConnections()) {
for (@NonNull Region targetRegion : connection.getTargetRegions()) {
if (region == targetRegion) {
assert loopingConnections.contains(connection);
loopingConnections.add(connection);
}
else if (!outgoingConnections.contains(connection)) {
outgoingConnections.add(connection);
}
}
}
if (outgoingConnections.size() > 1) { // Ensure that connection ordering is deterministic
Collections.sort(outgoingConnections, NameUtil.NAMEABLE_COMPARATOR);
}
region2incomingConnections.put(region, incomingConnections);
region2loopingConnections.put(region, loopingConnections);
region2outgoingConnections.put(region, outgoingConnections);
}
private @NonNull List<@NonNull Region> analyzeRegions(@NonNull ScheduledRegion outerScheduledRegion, @NonNull List<@NonNull Region> allCallableRegions) {
for (@NonNull Region region : outerScheduledRegion.getCallableRegions()) {
assert !region.isOperationRegion();
assert !allCallableRegions.contains(region);
allCallableRegions.add(region);
if (region instanceof ScheduledRegion) {
ScheduledRegion innerScheduledRegion = (ScheduledRegion)region;
analyzeRegions(innerScheduledRegion, allCallableRegions);
}
}
return allCallableRegions;
}
/**
* Initialize the source/target content of each connection of each region to empty.
* Compute the set of all connections that are not passed.
* Compute the set of all regions that are blocked by a mandatory dependence.
*/
private void analyzeSourcesAndTargets(@NonNull Region region) {
boolean hasPassedConnection = false;
for (@NonNull DatumConnection<?> connection : getOutgoingConnections(region)) {
if (connection.isPassed()) {
hasPassedConnection = true;
break;
}
}
if (!hasPassedConnection) {
unpassedRegions.add(region);
}
for (@NonNull DatumConnection<?> connection : getIncomingConnections(region)) {
List<@NonNull Region> sourceRegions = connection2sourceRegions.get(connection);
if (sourceRegions == null) {
sourceRegions = new ArrayList<>();
for (@NonNull Region sourceRegion : connection.getSourceRegions()) {
if (!sourceRegions.contains(sourceRegion)) {
sourceRegions.add(sourceRegion);
}
}
connection2sourceRegions.put(connection, sourceRegions);
}
List<@NonNull Region> targetRegions = connection2targetRegions.get(connection);
if (targetRegions == null) {
targetRegions = new ArrayList<>();
for (@NonNull Region targetRegion : connection.getTargetRegions()) {
if (!targetRegions.contains(targetRegion)) {
targetRegions.add(targetRegion);
}
}
connection2targetRegions.put(connection, targetRegions);
}
}
}
protected @NonNull Iterable<? extends @NonNull DatumConnection<?>> getConnections() {
return connection2targetRegions.keySet();
}
protected @NonNull Iterable<@NonNull DatumConnection<?>> getIncomingConnections(@NonNull Region region) {
List<@NonNull DatumConnection<?>> incomingConnections = region2incomingConnections.get(region);
assert incomingConnections != null;
return incomingConnections;
}
protected @NonNull Iterable<@NonNull DatumConnection<?>> getLoopingConnections(@NonNull Region region) {
List<@NonNull DatumConnection<?>> loopingConnections = region2loopingConnections.get(region);
assert loopingConnections != null;
return loopingConnections;
}
protected @NonNull Iterable<@NonNull DatumConnection<?>> getOutgoingConnections(@NonNull Region region) {
List<@NonNull DatumConnection<?>> outgoingConnections = region2outgoingConnections.get(region);
assert outgoingConnections != null;
return outgoingConnections;
}
protected @NonNull Iterable<@NonNull Region> getSourceRegions(@NonNull DatumConnection<?> connection) {
List<@NonNull Region> sourceRegions = connection2sourceRegions.get(connection);
assert sourceRegions != null;
return sourceRegions;
}
protected @NonNull Iterable<@NonNull Region> getTargetRegions(@NonNull DatumConnection<?> connection) {
List<@NonNull Region> targetRegions = connection2targetRegions.get(connection);
assert targetRegions != null;
return targetRegions;
}
protected boolean isPassed(@NonNull Region region) {
return !unpassedRegions.contains(region);
}
}