blob: bfd627c93d204f6a94d1684dff9840d447f5aad8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 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.partitioner;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.qvtd.compiler.internal.qvtm2qvts.RegionUtil;
import org.eclipse.qvtd.pivot.qvtschedule.Edge;
import org.eclipse.qvtd.pivot.qvtschedule.NavigableEdge;
import org.eclipse.qvtd.pivot.qvtschedule.Node;
import org.eclipse.qvtd.pivot.qvtschedule.Role;
import com.google.common.collect.Sets;
/**
* The SpeculationPartition identifies the nodes and edges required in a speculation micro-mapping
* which creates the speculated trace with predicates solely on constant inputs, loaded inputs
* and acyclic predicated nodes.
*/
//
// FIXME if multiple heads are predicated, we cannot wait for all of them before speculating. Speculation
// may need to consider just loaded nodes.
//
class SpeculationPartition extends AbstractPartition
{
private final @NonNull Set<@NonNull Node> headNodes;
public SpeculationPartition(@NonNull MappingPartitioner partitioner) {
super(partitioner);
this.headNodes = Sets.newHashSet(RegionUtil.getHeadNodes(region));
//
// The realized middle (trace) nodes become speculation nodes.
//
for (@NonNull Node traceNode : partitioner.getTraceNodes()) {
addNode(traceNode, Role.SPECULATION);
}
//
// All old nodes reachable from heads that are not part of cycles are copied to the speculation guard.
// NB. Unreachable loaded nodes are effectively predicates and so are deferred.
//
for (@NonNull Node node : headNodes) {
if (!node.isTrue()) {
addReachableOldAcyclicNodes(node);
}
}
//
// Add the outstanding predicates that can be checked by this partition.
//
resolveTrueNodes();
//
// Ensure that the predecessors of each node are included in the partition.
//
resolvePrecedingNodes();
//
// Ensure that re-used trace classes do not lead to ambiguous mappings.
//
resolveDisambiguations();
//
// Join up the edges.
//
resolveEdges();
}
/**
* Add all old nodes, including node, that have no cyclic dependency and are reachable by to-one navigation from node.
*/
protected void addReachableOldAcyclicNodes(@NonNull Node node) {
if (!hasNode(node) && (node.isHead() || node.isOld() && !partitioner.isCyclic(node))) {
addNode(node, RegionUtil.getNodeRole(node));
for (@NonNull NavigableEdge edge : node.getNavigationEdges()) {
if (edge.isOld()) {
addReachableOldAcyclicNodes(edge.getEdgeTarget());
}
}
}
}
/**
* Return a prioritized hint for the choice of head nodes.
* The override implementation returns null for no hint.
*/
@Override
protected @Nullable Iterable<@NonNull Node> getPreferredHeadNodes() {
return null;
}
@Override
protected @NonNull Iterable<@NonNull Node> getReachabilityRootNodes() {
List<@NonNull Node> rootNodes = new ArrayList<>();
for (@NonNull Node headNode : RegionUtil.getHeadNodes(region)) {
if (!headNode.isTrue()) {
rootNodes.add(headNode);
}
}
for (@NonNull Node leafConstant : partitioner.getLeafConstantNodes()) {
// if (!leafConstant.isTrue()) {
rootNodes.add(leafConstant);
// }
}
return rootNodes;
}
/**
* Return true if edge is available for use by this partition.
* The override implementation returns true for all constant and loaded edges.
*/
@Override
protected boolean isAvailable(@NonNull Edge edge) {
return edge.isConstant() || edge.isLoaded();
}
/**
* Return true if node is available for use by this partition.
* The override implementation returns true for all constant and loaded nodes.
*/
@Override
protected boolean isAvailable(@NonNull Node node) {
return node.isConstant() || node.isLoaded();
}
@Override
protected @Nullable Role resolveEdgeRole(@NonNull Role sourceNodeRole, @NonNull Edge edge, @NonNull Role targetNodeRole) {
Role edgeRole = RegionUtil.getEdgeRole(edge);
if (edgeRole == Role.REALIZED) {
assert !partitioner.hasRealizedEdge(edge);
}
return edgeRole;
}
}